Contents
  1. What MPC Is
  2. System Model
  3. The Feedback Loop
  4. What Makes MPC Powerful
  5. Key Terms
  6. MPC vs Machine Learning
  7. What You Can Do Now
← All posts

Model Predictive Control: Optimisation Over a Time Horizon

MPC plans over a rolling time horizon, optimises at each step, and executes only the first action. It handles constraints, works with linear and non-linear models, and requires real-time optimisation to close the loop.

What MPC Is

Model Predictive Control is a feedback control strategy that uses a model of the system to plan ahead. At every time step:

  1. Measure the current system state xx
  2. Optimise a sequence of control inputs uu over a future time horizon
  3. Apply only the first action from that sequence
  4. Repeat at the next time step with updated measurements

This receding-horizon approach means MPC is always looking forward but acting cautiously. It commits to one step at a time. The result is a controller that handles constraints naturally and anticipates future states.

MPC optimises future system trajectories through predictions using a dynamical model of the system. MPC does not improve itself through data like ML.

System Model

The general system:

x˙=f(x,u),y=g(x)\dot{x} = f(x, u), \quad y = g(x)
  • State xx: system state
  • Input uu: control input
  • Output yy: system output (what you measure)

The model can be linear or non-linear. The linear case:

y=Ax+Buy = Ax + Bu

where AA and BB are matrices. AA governs how the state evolves on its own; BB maps control input to state change.

The Feedback Loop

System → y → [compare to target] → MPC → u → System

The MPC block receives the measured output yy, compares it to the target (e.g. y=10y^* = 10), computes the optimal control input uu over the time horizon, and feeds uu back into the system. This closes the loop.

The optimisation happens at every time step, only for the next value, not the entire future trajectory all at once: “we optimise over the time horizon at step 1.”

What Makes MPC Powerful

  • Constraint handling: MPC natively enforces input and output constraints (e.g. actuator limits, safety bounds) as part of the optimisation
  • Linear and non-linear: works with both model types
  • Look-ahead: planning over a horizon allows anticipation of future disturbances
  • Complexity: computationally expensive compared to simpler controllers like PID; the optimisation must solve in real time at every step

Key Terms

Linear Quadratic Regulator (LQR): a special case of MPC for linear systems with a quadratic cost. Has a closed-form solution.

Kalman Filter: used inside MPC for state estimation when the full state xx cannot be measured directly.

System Identification: the process of finding a model of the system from input-output data. Often the hardest step in deploying MPC.

Real-time optimisation: the core computational requirement. The solve must complete within one time step of the physical system.

MPC vs Machine Learning

MPC uses a fixed dynamical model and optimises trajectories. It does not learn from new data. ML learns patterns from data and generalises to new inputs. The two are complementary: ML can be used for system identification (learning the model), and MPC uses that model for control.

What You Can Do Now

The code below runs a simple 1D MPC loop: a scalar system with a target setpoint, optimising a sequence of control inputs over a short horizon at each step.

import numpy as np
from itertools import product

# System: x_{t+1} = A*x + B*u  (scalar, A=1 means no natural decay)
A, B = 1.0, 0.5
target = 10.0
horizon = 3           # look-ahead steps
u_candidates = np.linspace(-3, 3, 13)   # discrete control options

def simulate_horizon(x0, u_seq):
    """Simulate system over the horizon, return total cost."""
    x, cost = x0, 0.0
    for u in u_seq:
        x = A * x + B * u
        cost += (x - target)**2 + 0.1 * u**2   # state error + control effort
    return cost

x = 0.0   # initial state
print(f"{'Step':>4}  {'State':>8}  {'Control':>8}  {'Error':>8}")
for step in range(15):
    # Optimise over all combinations of u over the horizon (brute force, small horizon)
    best_cost, best_u0 = np.inf, 0.0
    for u_seq in product(u_candidates, repeat=horizon):
        cost = simulate_horizon(x, u_seq)
        if cost < best_cost:
            best_cost, best_u0 = cost, u_seq[0]

    x = A * x + B * best_u0     # apply only the first action
    print(f"{step+1:>4}  {x:>8.3f}  {best_u0:>8.3f}  {abs(x-target):>8.3f}")

Increase horizon to see the controller anticipate further ahead (at higher compute cost). Adjust the 0.1 * u**2 weight to trade off control effort against tracking accuracy. A larger weight makes the controller more conservative.

← All posts