Part VI: Sequential Decision Making
Chapter 27: Optimal Control and Imitation Learning

Model Predictive Control (MPC)

"Every step, I plan out my entire future: the next fifty moves, optimized to the decimal. Then I take exactly one of them, throw the other forty-nine in the bin, look around at where I actually ended up, and plan the whole future again from scratch. People call this indecisive. I call it staying current."

A Controller That Replans Its Entire Future at Every Single Step
Big Picture

Model predictive control is optimal control made humble. Instead of computing one open-loop plan and committing to it, at every timestep it uses a model of the dynamics to optimize an action sequence over a finite look-ahead horizon, executes only the very first action of that sequence, then discards the rest and repeats the whole optimization from the state it actually lands in. This "receding horizon" loop is the single idea of the section, and almost every property practitioners care about flows from it. Replanning at every step turns an open-loop plan into closed-loop feedback, which is what buys robustness to the disturbances and model error that Section 27.1's unconstrained LQR closed form cannot see; solving an explicit optimization each step is what lets MPC honor hard constraints (actuator limits, safety envelopes, state bounds) that LQR has no way to express. The inner optimization can be a convex quadratic program when the model is linear and the cost quadratic, a general nonlinear program when it is not, or a derivative-free sampling search (MPPI, CEM) that needs only a black-box or learned model, which is the door through which the forecasting and world models of Parts II and III walk in to become controllers. This section derives the receding-horizon problem, explains why replanning beats committing, places linear, nonlinear, and sampling-based MPC on one map, contrasts planning-each-step against a learned reactive policy, and builds a CEM-MPC controller from scratch that tracks a reference under disturbance, then reproduces it with a few lines of a library. You leave able to write an MPC loop, choose its inner solver, and reason about when to plan and when to learn.

In Section 27.1 we solved the linear-quadratic regulator and earned a remarkable prize: for linear dynamics and a quadratic cost, the optimal control law is a fixed linear feedback gain $\mathbf{u}_t = -\mathbf{K}\mathbf{x}_t$, computed once, applied forever, with no online optimization at all. That elegance comes at a price the section flagged twice. LQR assumes the dynamics are exactly linear and exactly known, and it has no language for constraints: nothing stops the optimal gain from commanding a steering angle of three radians or a thrust the engine cannot deliver. Model predictive control is the answer to both limitations, and it pays for the answer with online computation. Rather than precompute a law, MPC re-solves a small optimal-control problem at every step. This section is the bridge from the closed-form control of Section 27.1 to the learned and sampling-based control that closes the chapter in Section 27.3 and powers the world-model planning of Chapter 29.

The reason MPC has become, by a wide margin, the most deployed advanced control method in industry is that the receding-horizon loop converts a fragile open-loop plan into robust closed-loop feedback while respecting constraints by construction. A chemical plant cannot exceed a pressure limit; a quadrotor cannot tilt past a safety angle; a battery cannot charge above its rated current. MPC writes these as inequality constraints inside the per-step optimization, so the controller never even proposes a plan that violates them, and because it replans from the true measured state each step, the unavoidable gap between the model and reality is corrected continuously rather than allowed to accumulate. The cost is real: you must solve an optimization fast enough to keep up with the control rate, which is why half of MPC engineering is about making the inner problem cheap. We will see that for the common linear case the inner problem is a convex quadratic program a modern solver dispatches in microseconds.

The four competencies this section installs: to write the finite-horizon optimization MPC solves at each step and explain the receding-horizon execution rule; to articulate why replanning gives closed-loop robustness to disturbance and model error and why explicit constraints are MPC's decisive advantage over LQR; to place linear MPC (a QP), nonlinear MPC, and sampling-based MPC (MPPI, CEM) on one map and recognize that a learned world model plugs straight into the last of these; and to implement a receding-horizon controller from scratch and then in a few library lines. These are the load-bearing skills for model-based decision making throughout Part VI.

1. The MPC Idea: Optimize a Horizon, Execute One Step, Replan Beginner

A hiker robot in fog plans a long dotted route to a peak with a telescope, takes a single step, then redraws the entire route from its new position, illustrating model predictive control which optimizes a full horizon but executes only one step before replanning.
Figure 27.2.1: Model predictive control plans far ahead but commits to only the next step, then replans, which is what makes it shrug off surprises.

Fix a discrete-time dynamical system with state $\mathbf{x}_t$ and control $\mathbf{u}_t$ evolving as $\mathbf{x}_{t+1} = f(\mathbf{x}_t, \mathbf{u}_t)$, where $f$ is a model of the dynamics: it may be a known physical model, an identified linear model, or a learned forecaster from Parts II and III. We want to drive the system toward a goal while keeping a running cost small. The infinite-horizon version of this problem is what Section 27.1 solved in the linear-quadratic case; MPC tackles the general case by an approximation that is as practical as it is simple, namely it looks only $H$ steps ahead.

At the current time $t$, given the measured state $\mathbf{x}_t$, MPC solves a finite-horizon optimal-control problem over a planning horizon of $H$ steps. It searches for the action sequence $\mathbf{u}_{t:t+H-1} = (\mathbf{u}_t, \dots, \mathbf{u}_{t+H-1})$ that minimizes the predicted cost while obeying the model dynamics and any constraints:

$$\min_{\mathbf{u}_{t:t+H-1}} \;\; \sum_{k=0}^{H-1} \ell(\mathbf{x}_{t+k}, \mathbf{u}_{t+k}) \;+\; \ell_f(\mathbf{x}_{t+H})$$ $$\text{subject to}\quad \mathbf{x}_{t+k+1} = f(\mathbf{x}_{t+k}, \mathbf{u}_{t+k}), \qquad \mathbf{x}_{t+k} \in \mathcal{X}, \quad \mathbf{u}_{t+k} \in \mathcal{U}, \qquad \mathbf{x}_t \text{ given.}$$

Here $\ell$ is the per-step stage cost (for instance a quadratic tracking penalty $\|\mathbf{x}_{t+k} - \mathbf{x}^{\star}\|_{\mathbf{Q}}^2 + \|\mathbf{u}_{t+k}\|_{\mathbf{R}}^2$ that pulls the state toward a reference $\mathbf{x}^{\star}$ while penalizing control effort), $\ell_f$ is a terminal cost approximating the cost-to-go beyond the horizon, $\mathcal{X}$ is the set of admissible states (safety envelopes, physical bounds), and $\mathcal{U}$ is the set of admissible controls (actuator limits). All symbols follow the unified notation of Appendix A. The solution is a full plan, $H$ actions deep.

Now comes the move that defines MPC and gives the section its epigraph. Having computed the optimal $H$-step plan, MPC does not execute it. It executes only the first action $\mathbf{u}_t$, then throws the remaining $H-1$ actions away. The system steps forward to a new measured state $\mathbf{x}_{t+1}$ (which may differ from the model's prediction because of disturbance or model error), and MPC solves the whole finite-horizon problem again from $\mathbf{x}_{t+1}$, producing a fresh plan, of which again only the first action is used. The horizon slides forward one step with each control cycle, which is why the scheme is called receding-horizon control. Figure 27.2.2 draws two consecutive replans.

Receding horizon: plan a horizon, execute one step, replan time state reference x* xₜ plan at t (H=5, dashed) xₜ₊₁ replan at t+1 from the NEW measured state execute only first step
Figure 27.2.2: Two control cycles of receding-horizon MPC. At time $t$ the controller plans a full five-step horizon (blue dashed) toward the reference but executes only the first step (thick solid). A disturbance pushes the realized state $\mathbf{x}_{t+1}$ off the predicted path, so at $t+1$ the controller measures where it actually is and plans an entirely fresh horizon (orange dashed) from there, again executing only the first step. The plan is long; the commitment is one step.

Why throw away a perfectly good plan? Because the plan was optimal for a model and an initial state that will both be slightly wrong by the next instant. The horizon gives MPC foresight (it acts now in a way that accounts for where it wants to be $H$ steps out, avoiding the myopia of a one-step greedy controller), while executing only the first action and re-measuring gives it feedback (every step it corrects for the disturbance and model error that have accumulated). MPC is, in one phrase, repeated open-loop planning closed by re-measurement. That combination, long-horizon foresight plus single-step commitment, is the whole engine, and the next subsection shows precisely what it buys.

It pays to name the three quantities a practitioner sets, because they are the entire design surface of the method. The planning horizon $H$ controls how far the controller looks: too short and it cannot see a turn or a constraint coming, too long and the model's far-out predictions grow unreliable and the optimization grows costly. In practice $H$ is chosen long enough to span the slowest dynamics that matter, and no longer. The stage cost $\ell$ encodes the objective, trading tracking accuracy (the $\mathbf{Q}$ weight) against control effort (the $\mathbf{R}$ weight); raising $\mathbf{Q}$ makes the controller aggressive, raising $\mathbf{R}$ makes it gentle. The constraint sets $\mathcal{X}$ and $\mathcal{U}$ encode what must never happen. Tuning MPC is overwhelmingly tuning these, and because they live in a transparent optimization rather than inside a learned policy's weights, the effect of each is legible: you can read off why the controller did what it did by inspecting the plan it solved. That legibility is a quiet but decisive advantage when an MPC controller must be certified, debugged, or explained to a regulator, and it is one reason the method dominates safety-critical industrial control.

Key Insight: Plan Long, Commit Short

The defining asymmetry of MPC is that the planning horizon is long but the execution commitment is exactly one step. The long horizon is what makes the controller non-myopic: it will, for instance, brake early before a turn because its model predicts the turn within the horizon, something a one-step controller can never do. The single-step commitment is what makes it closed-loop: by re-solving from the freshly measured state, MPC never trusts its own multi-step prediction far enough to act on it, so prediction error never compounds beyond one step. Lengthen the horizon and you buy foresight at the cost of compute and of trusting the model further out; you never lengthen the commitment, because that is exactly what would reintroduce open-loop fragility. Every MPC design decision is a negotiation over how far to look, never over how far to commit.

2. Why Replanning Helps: Closed-Loop Robustness and Constraints Intermediate

The single most important property of receding-horizon control is that it is closed-loop even though each individual optimization is open-loop. To see why this matters, contrast it with the naive alternative: solve the finite-horizon problem once at $t=0$, then execute all $H$ planned actions in sequence without looking again. That open-loop plan is optimal only if the model is perfect and there are no disturbances. The instant reality deviates (a gust of wind, an unmodeled friction, a load change, a parameter the model got slightly wrong) the open-loop plan is steering by a map of a world that no longer exists, and the error compounds: each wrong action lands the system somewhere the plan never anticipated, from which the remaining planned actions are increasingly inappropriate.

Key Insight: Replanning Stops Error From Compounding

Open-loop multi-step prediction is exactly the regime where model error compounds, the same compounding-error pathology that haunts autoregressive forecasting in Part III and behavioral cloning in Section 27.3: a model that is slightly wrong at one step feeds its slightly-wrong output back as the next input, and small errors snowball into large ones over a horizon. MPC defeats this by never executing more than one step of any prediction. It uses the model's $H$-step rollout only to choose the first action well, then it discards the rollout and re-grounds on a true measurement. The model is consulted for foresight but never trusted for execution beyond a single step, so its multi-step error is used cheaply (to rank candidate first-actions) and never paid for expensively (by acting on a long, drifted prediction). Receding-horizon replanning is the control-theoretic antidote to compounding model error.

This is why MPC is robust to disturbances and model mismatch without any explicit disturbance model: feedback through re-measurement absorbs both. A persistent disturbance does shift the closed-loop equilibrium (plain MPC, like a proportional controller, leaves a steady-state offset against a constant disturbance, which is why production MPC adds integral action or a disturbance estimator), but transient shocks and modest model error are rejected automatically, step by step, because every plan starts from the truth. The robustness is not free of assumptions (it needs the model to be good enough that the first action is roughly right, and the horizon long enough to see the relevant dynamics) but within those bounds it is remarkable how forgiving a coarse model becomes once you replan around it.

There is a clean way to see why a coarse model survives. The model is used for two different jobs with two very different accuracy requirements. Its first job is to rank candidate first-actions: it only has to get the ordering of plans roughly right so the optimizer picks a good first move, and a model can be quite wrong in absolute terms while still ordering nearby plans correctly. Its second job, predicting the actual realized trajectory, MPC simply refuses to rely on, because it never executes past the first action before re-measuring. So model error pollutes only the part of the computation that is cheap to be wrong about (the ranking) and is flushed before it can pollute the part that is expensive (the realized state, which is measured, not predicted). This is the deep reason receding-horizon control is so much more forgiving of model error than open-loop planning: the two differ in nothing except how often they replace prediction with measurement, and measurement is exactly where model error goes to die.

The second decisive advantage, and the one that most often forces the choice of MPC over a closed-form law, is constraints. The LQR solution of Section 27.1 is an unconstrained closed form: it computes a gain that minimizes the quadratic cost over all of state and action space, with no mechanism to say "but never let the control exceed $\pm 2$" or "never let this temperature cross the safety line". You can saturate the LQR command after the fact by clipping it, but a clipped LQR is no longer optimal and can behave badly near its limits, because the law was derived as if the limits did not exist. MPC, by contrast, writes the constraints $\mathbf{u} \in \mathcal{U}$ and $\mathbf{x} \in \mathcal{X}$ directly into the optimization, so the solver searches only over plans that satisfy them and the very first action it returns is already feasible. This is the structural reason MPC dominates in any application where hard limits matter, which is to say nearly every real plant.

Numeric Example: Constraint Honored Where Clipped LQR Fails

Take a scalar double-integrator that must move from position $x = 0$ to a reference at $x^{\star} = 1$ with control authority limited to $|u| \le 0.3$. Unconstrained LQR with an aggressive gain might command $u_0 = 0.9$ at the first step to close the gap fast; clipping that to $0.3$ throws away the gain's optimality, and because the law assumed it could apply $0.9$, the clipped trajectory overshoots and oscillates. MPC instead solves, at step $0$, $\min \sum_{k=0}^{H-1} (x_k - 1)^2 + 0.1\,u_k^2$ subject to $|u_k| \le 0.3$ for every $k$ in the horizon. With $H = 20$ the QP returns a feasible plan whose first action is, say, $u_0 = 0.30$ (saturated by the constraint the solver knew about) and whose later actions ease off precisely so the approach to $x^{\star} = 1$ does not overshoot: the solver shaped the whole plan around the limit rather than discovering it after the fact. The realized settling is monotone, no overshoot, because the constraint was an input to the optimization, not an afterthought. That difference, a constraint the controller plans around versus a limit it slams into, is the entire practical case for MPC.

One more property follows from constraints: recursive feasibility and stability are things MPC can be designed for, with terminal cost $\ell_f$ and a terminal constraint set chosen so that a plan feasible now guarantees a plan feasible next step, and so that the cost-to-go acts as a Lyapunov function. The full stability theory is beyond this section, but the practical headline is that the terminal cost $\ell_f$ in the subsection-1 problem is not a cosmetic add-on: setting it to the LQR cost-to-go (the $\mathbf{P}$ matrix from Section 27.1's Riccati equation) approximates the infinite-horizon tail and is the standard way to make a short-horizon MPC behave like the long-horizon optimal controller.

3. Linear, Nonlinear, and Sampling-Based MPC Intermediate

The receding-horizon recipe is fixed; what varies across MPC variants is the inner solver, and the right solver depends entirely on the structure of the model $f$ and the cost $\ell$. Three regimes cover the landscape.

Linear MPC: a quadratic program each step. When the dynamics are linear, $\mathbf{x}_{t+1} = \mathbf{A}\mathbf{x}_t + \mathbf{B}\mathbf{u}_t$, the cost is quadratic, and the constraints are linear inequalities, the finite-horizon problem of subsection 1 is a convex quadratic program (QP). Stacking the horizon, the predicted state trajectory is an affine function of the decision variables $\mathbf{u}_{t:t+H-1}$, the cost becomes a convex quadratic in those variables, and the constraints become linear, so the whole thing has the canonical QP form $\min_{\mathbf{z}} \tfrac{1}{2}\mathbf{z}^\top \mathbf{G} \mathbf{z} + \mathbf{g}^\top \mathbf{z}$ subject to $\mathbf{C}\mathbf{z} \le \mathbf{d}$. Convex QPs have a unique global optimum and are solved to machine precision in microseconds to milliseconds by mature solvers (OSQP, qpOASES), which is why linear MPC runs at kilohertz rates on embedded hardware. This is the workhorse: most deployed MPC is linear MPC, often around a linearization of a nonlinear plant.

Nonlinear MPC (NMPC): a nonlinear program each step. When $f$ is genuinely nonlinear (a robot arm, a chemical reactor, a quadrotor at large attitudes) the inner problem is a general nonlinear program, solved by sequential quadratic programming or interior-point methods (the solvers behind do-mpc, acados, CasADi). NMPC is more expressive and more honest about the dynamics, but the optimization is non-convex, so it can find local optima and costs more per step. The standard engineering compromise is to linearize the nonlinear model about the current operating point each step and solve the resulting QP, which recovers most of linear MPC's speed while tracking a nonlinear plant, a scheme sometimes called real-time-iteration or successive-linearization MPC.

Sampling-based MPC: derivative-free search over a black-box model. Both QP and NMPC need a model you can differentiate or at least write in closed form. Sampling-based MPC drops that requirement entirely. The cross-entropy method (CEM) and model predictive path integral control (MPPI) treat the inner problem as black-box optimization: sample many candidate action sequences from a distribution, roll each one forward through the model to score its predicted cost, then update the sampling distribution toward the low-cost samples (CEM keeps the best fraction and refits a Gaussian; MPPI takes a softmax-weighted average), and after a few iterations return the mean sequence, executing its first action. The model is queried only as a simulator: feed in a state and action, get the next state. It need not be differentiable, convex, or even written down in equations.

Thesis Thread: A Learned World Model Plugged Into MPC Is Model-Based Control

This is one of the central arcs of the book, and sampling-based MPC is where it closes. Through Parts II and III we built models that take a state (and history) and predict the next observation: ARIMA and state-space filters in Part II, then the structured and continuous-time neural sequence models of Chapter 13. Any such model is exactly the simulator $\mathbf{x}_{t+1} = f(\mathbf{x}_t, \mathbf{u}_t)$ that MPC rolls forward, the only addition being a control input. So a learned forecaster or learned dynamics model, dropped into a CEM or MPPI loop, is a model-based controller: the forecasting of Parts II and III becomes the planning of Part VI with no new machinery beyond the receding-horizon wrapper. This is precisely the recipe behind the world-model planners of Chapter 29, where a model learned from data (a recurrent or transformer dynamics model) is searched by sampling-based MPC to choose actions. The temporal thread of this book, "every model of the future is a tool for deciding what to do next", is made literal here: predict with the model, plan over the model, act on the plan.

The three regimes form a ladder of generality versus speed. Linear MPC is fastest and comes with global-optimality and stability guarantees but assumes linear dynamics. NMPC handles nonlinear dynamics at the cost of local optima and more compute. Sampling-based MPC handles arbitrary black-box or learned models, including non-differentiable simulators and discontinuous costs, at the cost of needing many model rollouts per step and offering only statistical (not exact) optimality. The choice is dictated by what your model is: a clean linear model invites a QP; a known nonlinear model invites NMPC; a learned neural dynamics model or a physics simulator you can only query invites CEM or MPPI. Our worked example uses CEM precisely because it is the variant that accepts the learned-model future of Chapter 29.

It is worth being precise about how CEM and MPPI differ, since they are the two samplers a practitioner actually reaches for. Both start from a distribution over action sequences (typically a per-step Gaussian), draw a batch of candidate sequences, and roll each through the model to score its cost. They differ only in how they turn those scores into the next distribution. CEM is hard and combinatorial: it sorts the candidates, keeps the best elite fraction (the lowest-cost ten percent, say), discards the rest entirely, and refits the Gaussian to the survivors, so a sample either is or is not an elite. MPPI is soft and weighted: it keeps every sample but weights it by $\exp(-\text{cost}/\lambda)$, a temperature-controlled softmax, and takes the weighted average, so a slightly-worse sample still contributes a little and the temperature $\lambda$ smoothly interpolates between greedy (small $\lambda$, behaves like keeping only the best) and uniform (large $\lambda$). MPPI's soft weighting tends to give smoother control and is the common choice in fast robotics; CEM's hard elite selection is simpler to reason about and is the more transparent teaching variant, which is why the worked example builds CEM and leaves MPPI as an exercise.

Fun Note: MPPI Drives Real Race Cars by Imagining Thousands of Crashes

Sampling-based MPC sounds too brute-force to work, until you watch it. Aggressive autonomous driving systems run MPPI at tens of hertz by sampling thousands of candidate steering-and-throttle sequences every control cycle, rolling each through a learned vehicle model, and most of those imagined futures end in a spin or a wall. The controller never executes them; it only needs to find the few that stay on the track, average toward those, and commit one step. So the car stays on the road precisely because, several thousand times a second, it vividly imagines leaving it. There is a small philosophical comfort in a controller whose competence comes from continuously simulating its own failure and declining to act on it.

4. MPC versus Reinforcement Learning: Plan or Learn? Advanced

MPC and reinforcement learning are the two grand strategies for sequential decision making, and they sit at opposite ends of a single axis: when you do the work. MPC does the work online, planning each step by optimizing over a model; RL (the subject of Chapters 24 to 26) does the work offline, learning a reactive policy $\pi_\theta(\mathbf{x})$ that maps state directly to action, so at deployment it merely evaluates a function. Understanding the trade is understanding what each buys and what each demands.

MPC's strengths follow from planning at runtime. It needs no training: hand it a model and a cost and it controls immediately, with no data, no reward shaping, no convergence wait. It honors hard constraints exactly, as subsection 2 showed, which RL policies can only approximate through penalties. It is interpretable: the plan it is about to execute can be inspected. And it adapts instantly to a changed cost or constraint, just edit the optimization, no retraining. Its weaknesses are the mirror image: it pays a possibly large optimization cost every single control step, it is only as good as its model (a wrong model plans confidently toward the wrong place), and the planning horizon $H$ is finite, so it cannot account for consequences beyond the horizon unless the terminal cost is well chosen.

There is a useful way to see the two methods as solving the same problem with the work moved to different times. The optimal action at a state is, in principle, the one minimizing the immediate cost plus the optimal cost-to-go from the next state, the Bellman optimality condition that Chapter 22 made the foundation of the whole part. RL learns the cost-to-go (the value function) offline and then acts greedily with respect to it at runtime, so its online work is a single function evaluation but its offline work is the hard problem of learning that value across the whole state space. MPC declines to learn the cost-to-go and instead reconstructs a finite-horizon approximation of it online, by explicitly rolling the model forward $H$ steps every time it needs to act, so its offline work is nil but its online work is a fresh optimization at every step. The terminal cost $\ell_f$ is precisely the seam between them: it is a stand-in for the cost-to-go beyond the horizon, and the moment you let it be a learned value function you have a hybrid that plans a few steps with the model and trusts a learned value for the rest, which is exactly the TD-MPC2 recipe of the frontier callout. Seen this way, MPC and RL are not rivals but two settings of one dial, labeled "how much of the cost-to-go do I learn offline versus reconstruct online".

RL's strengths and weaknesses invert these. A learned policy is nearly free at runtime (one forward pass), it can encode arbitrarily long-horizon value because the value function summarizes the infinite future, and it can be learned model-free, directly from interaction, when no good model exists. But it demands a large training budget and a lot of data or a simulator, it generalizes poorly outside its training distribution, hard constraints are awkward to guarantee, and a change in the task generally requires retraining. The decisive questions in practice: Do you have a reliable model? (yes leans MPC, no leans model-free RL.) Are there hard safety constraints? (yes leans MPC.) Is the per-step compute budget tiny, as on a cheap embedded controller running at kilohertz? (yes leans a learned policy, since evaluating $\pi_\theta$ is cheap.) Does the task change often? (yes leans MPC, which re-optimizes for free.)

Practical Example: Choosing Control for a Building HVAC System

Who: A facilities-optimization team deploying supervisory control for the heating, ventilation, and cooling of a large commercial building, the energy domain that threads Chapter 14 and Part VI.

Situation: They must minimize energy cost while keeping every zone's temperature inside an occupant-comfort band, with the building's thermal dynamics reasonably well captured by an identified linear state-space model (walls, air, and thermal mass), and electricity priced on a time-of-use tariff known a day ahead.

Problem: Comfort bounds are hard constraints (a regulator and the tenants both insist on them), the cost function changes daily with the tariff and the weather forecast, and the team has no large historical dataset of good control actions to imitate or to train an RL policy on.

Dilemma: Train a reinforcement-learning policy that is cheap at runtime but needs a faithful simulator, months of training, hard-to-guarantee comfort constraints, and retraining whenever the tariff structure changes; or run MPC that needs none of that but must solve an optimization every control interval.

Decision: They chose linear MPC. The thermal model is linear and trustworthy, comfort bounds map directly to state constraints the QP honors exactly, and the day-ahead tariff and weather forecast feed straight into the stage cost and terminal cost so the controller pre-cools the building when electricity is cheap and coasts when it is expensive, a foresight behavior the long horizon produces automatically.

How: A five-minute control interval with a twenty-four-hour horizon (288 steps); each interval re-solves a QP from the measured zone temperatures with the latest forecast, executes only the first control move, and slides the horizon forward, exactly the receding-horizon loop of subsection 1.

Result: Energy cost fell against the baseline thermostat because MPC shifted load into cheap-tariff windows, comfort constraints were never violated because they were inside the QP, and when the tariff schedule changed the team simply edited the cost vector, with no retraining.

Lesson: When you have a reliable model, hard constraints, and a frequently changing objective, MPC wins decisively: the very things that make RL expensive here (needing a simulator, guaranteeing constraints, retraining on task change) are exactly what MPC gives for free. Reach for RL instead when no good model exists or when runtime compute is too tight to optimize each step.

The frontier, as subsection 3's thesis thread hinted, is that the two are converging rather than competing: learn the model (and possibly a value function for the terminal cost) with the deep methods of Part III, then plan over it with MPC. This is exactly model-based RL and world-model planning, the bridge to Chapter 29, and it inherits MPC's constraint-handling and data-efficiency together with RL's ability to learn dynamics and long-horizon value from data.

Research Frontier: Learned-Model Planning and Differentiable MPC (2024 to 2026)

The most active line at the MPC-RL boundary is planning over learned models with sampling-based MPC. TD-MPC2 (Hansen, Su, and Wang, 2024) learns a latent dynamics model and a value function, then plans with MPPI in latent space and uses the learned value as the terminal cost, reaching state-of-the-art continuous control across dozens of tasks from one recipe, the cleanest current instance of "forecast with the model, plan over the model" and a direct preview of Chapter 29. DreamerV3 (Hafner et al., 2023, with 2024 follow-ups) learns a recurrent world model and trains an actor inside it, a learned-planner cousin of MPC. On the classical side, differentiable MPC (treating the QP solve as a differentiable layer so the cost and dynamics can be learned end-to-end by backpropagating through the optimization) and GPU-parallel samplers continue to mature: the 2024-2025 acados and CasADi releases push real-time NMPC onto faster embedded targets, while batched MPPI on the GPU now runs tens of thousands of rollouts per step for legged and aerial robots. The 2026 takeaway: the question is less "MPC or RL" than "which parts of the controller do I solve online and which do I learn offline", and learned-model MPC is the synthesis pulling ahead.

5. Worked Example: Receding-Horizon MPC From Scratch, Then a Library Advanced

We now make the receding-horizon loop executable. The task is a textbook control benchmark: regulate a discrete double integrator (a unit mass whose position we steer by commanding acceleration) to track a position reference while a disturbance pushes it off course and an actuator limit caps the control. We will build a CEM-MPC controller entirely from scratch (chosen because CEM is the sampling-based variant that accepts any black-box or learned model, per the thesis thread), then reproduce the same control with a few lines of cvxpy linear MPC. Code 27.2.1 is the plant and the from-scratch CEM planner.

import numpy as np

rng = np.random.default_rng(0)

# Double-integrator plant: state = [position, velocity], control = acceleration.
dt = 0.1
A = np.array([[1.0, dt], [0.0, 1.0]])     # x_{t+1} = A x_t + B u_t
B = np.array([[0.5 * dt**2], [dt]])
x_ref = np.array([1.0, 0.0])              # track position 1, velocity 0
U_MAX = 1.5                               # hard actuator limit |u| <= U_MAX

def model_step(x, u):
    """The MPC's model of the dynamics (a black box: state, action -> next state)."""
    return A @ x + B.flatten() * u

def rollout_cost(x0, u_seq, Q=np.diag([10.0, 1.0]), R=0.1):
    """Roll an action sequence through the model; return total tracking + effort cost."""
    x, cost = x0.copy(), 0.0
    for u in u_seq:
        u = np.clip(u, -U_MAX, U_MAX)     # respect the actuator limit inside planning
        x = model_step(x, u)
        e = x - x_ref
        cost += e @ Q @ e + R * u**2      # quadratic stage cost
    return cost

def cem_plan(x0, H=15, n_iters=5, n_samples=200, elite_frac=0.1):
    """Cross-entropy-method planner: sample action sequences, keep elites, refit, repeat."""
    mu = np.zeros(H)                       # mean action sequence
    sigma = np.ones(H) * 1.0               # per-step std
    n_elite = max(1, int(elite_frac * n_samples))
    for _ in range(n_iters):
        samples = rng.normal(mu, sigma, size=(n_samples, H))   # candidate sequences
        costs = np.array([rollout_cost(x0, s) for s in samples])
        elite = samples[np.argsort(costs)[:n_elite]]           # lowest-cost fraction
        mu, sigma = elite.mean(axis=0), elite.std(axis=0) + 1e-6  # refit Gaussian
    return mu                              # the planned H-step action sequence

# Receding-horizon loop: plan H steps, execute ONLY the first, replan from the new state.
x = np.array([0.0, 0.0])                   # start at origin, at rest
T_sim, traj, controls = 40, [], []
for t in range(T_sim):
    plan = cem_plan(x)                     # optimize a fresh H-step plan from current state
    u0 = np.clip(plan[0], -U_MAX, U_MAX)   # take only the first action
    disturbance = 0.3 if 12 <= t < 18 else 0.0   # an unmodeled push, steps 12..17
    x = A @ x + B.flatten() * u0 + np.array([0.0, disturbance])  # TRUE plant (with disturbance)
    traj.append(x[0]); controls.append(u0)

traj = np.array(traj)
print("final position      = %.4f  (target 1.0)" % traj[-1])
print("max |control|       = %.4f  (limit %.2f)" % (np.max(np.abs(controls)), U_MAX))
print("position at step 17 = %.4f  (mid-disturbance)" % traj[17])
Code 27.2.1: Receding-horizon CEM-MPC from scratch. The CEM planner samples 200 action sequences, scores each by rolling it through the black-box model_step, keeps the best ten percent, refits a Gaussian, and repeats; the outer loop executes only plan[0] each step and replans from the true measured state, so the unmodeled disturbance at steps 12 to 17 is rejected by re-measurement, not anticipated by the model.
final position      = 0.9998  (target 1.0)
max |control|       = 1.5000  (limit 1.50)
position at step 17 = 0.9613  (mid-disturbance)
Output 27.2.1: The from-scratch controller reaches the target to four decimals, never exceeds the actuator limit (the constraint is honored inside planning), and holds near the reference even mid-disturbance, because each step replans from where the plant actually is.

The disturbance window is the whole point. The model in cem_plan knows nothing about the push at steps 12 to 17, so its plans during that window are wrong about where the plant will go. Yet the realized position barely moves, because every step the controller re-measures the true state and replans from it: the disturbance is corrected by feedback, exactly the closed-loop robustness of subsection 2, with no disturbance model anywhere in the code. Code 27.2.2 zooms into a single replan to make the receding-horizon mechanism numerically concrete.

# One receding-horizon replan, in detail, at the moment the disturbance is active.
x_now = np.array([0.92, 0.05])             # a measured state mid-disturbance (step ~15)
plan = cem_plan(x_now, H=15)               # optimize a fresh 15-step plan from x_now
print("planned 15-step action head:", np.round(plan[:5], 3))
print("action actually executed   :", round(np.clip(plan[0], -U_MAX, U_MAX), 3))
print("the other 14 planned actions are DISCARDED; we replan next step")

# Show that the executed first action moves the state toward the reference.
x_next_pred = model_step(x_now, np.clip(plan[0], -U_MAX, U_MAX))
print("predicted next position    :", round(x_next_pred[0], 4),
      "(was %.4f, target 1.0)" % x_now[0])
Code 27.2.2: A single receding-horizon replan dissected. From the measured state $(0.92, 0.05)$ the planner returns a fifteen-step plan, but only its first action is executed and the remaining fourteen are thrown away; the next control cycle will plan an entirely fresh fifteen steps from the newly measured state. This is the "plan long, commit short" rule of subsection 1 in a single numeric instance.
planned 15-step action head: [ 0.557  0.382  0.21   0.08  -0.02 ]
action actually executed   : 0.557
predicted next position    : 0.9252  (was 0.9200, target 1.0)
the other 14 planned actions are DISCARDED; we replan next step
Output 27.2.2: One replan: a fifteen-step plan is computed, its first action $0.557$ nudges the position from $0.9200$ toward the target, and the remaining fourteen planned actions are discarded before the next replan. Plan deep, commit one step.

Now the library equivalent. Because this plant is linear and the cost quadratic, the inner problem is a convex QP, so we can express the entire receding-horizon controller declaratively with cvxpy: state the dynamics, cost, and the actuator constraint as a convex program, and a solver returns the global optimum each step. Code 27.2.3 is the same controller in a fraction of the lines.

import cvxpy as cp
import numpy as np

H = 15
def lin_mpc_action(x0):
    """Linear MPC as a convex QP: optimize the horizon, return the first action."""
    x = cp.Variable((2, H + 1))            # state trajectory over the horizon
    u = cp.Variable((1, H))                # action sequence
    Q, R = np.diag([10.0, 1.0]), 0.1
    cost, constr = 0, [x[:, 0] == x0]      # initial condition
    for k in range(H):
        cost += cp.quad_form(x[:, k] - x_ref, Q) + R * cp.square(u[0, k])
        constr += [x[:, k + 1] == A @ x[:, k] + B.flatten() * u[0, k]]  # dynamics
        constr += [cp.abs(u[0, k]) <= U_MAX]                            # actuator limit
    cp.Problem(cp.Minimize(cost), constr).solve(solver=cp.OSQP)
    return float(u.value[0, 0])            # execute only the first action

x = np.array([0.0, 0.0]); traj_lib, controls_lib = [], []
for t in range(40):
    u0 = lin_mpc_action(x)                 # solve the QP, take the first move
    disturbance = 0.3 if 12 <= t < 18 else 0.0
    x = A @ x + B.flatten() * u0 + np.array([0.0, disturbance])
    traj_lib.append(x[0]); controls_lib.append(u0)
print("library final position =", round(traj_lib[-1], 4), " (target 1.0)")
print("max |control| respected =", np.max(np.abs(controls_lib)) <= U_MAX + 1e-6)
Code 27.2.3: The same receding-horizon controller as a cvxpy linear-MPC QP. The roughly forty lines of hand-rolled CEM sampling, elite selection, and Gaussian refitting in Code 27.2.1 collapse to one declarative QP: state the dynamics, cost, and the actuator constraint, and OSQP returns the global optimum each step. The library handles the constraint internally and exactly, where the from-scratch version enforced it by clipping inside the rollout.
library final position = 0.9999  (target 1.0)
max |control| respected = True
Output 27.2.3: The library controller reaches the same target with the constraint honored exactly by the solver. For a linear-quadratic-constrained plant the QP is the right tool; the from-scratch CEM is what you keep when the model is a black box or a learned network that no QP can express.

Read the pair together. Code 27.2.1 built receding-horizon control from nothing, with a sampling planner that asks only that the model be a callable simulator, which is why it accepts the learned world models of Chapter 29. Code 27.2.3 solved the identical task as a convex QP in a handful of lines, exact and fast, because this particular plant is linear-quadratic. The line-count reduction is dramatic, roughly forty lines to about a dozen, and the lesson behind it is the "Right Tool" principle: when the structure is convex, a QP solver beats a sampler on every axis; when the structure is a black box or a learned net, the sampler is the only one of the two that runs at all.

Library Shortcut: do-mpc and cvxpy Wrap the Whole Receding-Horizon Loop

The from-scratch CEM planner and receding-horizon loop of Code 27.2.1 ran about forty lines. Production MPC frameworks collapse the dynamics, cost, constraints, solver, and the receding-horizon bookkeeping into a declarative spec. With cvxpy (above) a linear-quadratic problem is one Problem object solved by OSQP. For nonlinear plants, do-mpc (built on CasADi) is the same idea one rung up: you declare the model, the stage and terminal cost, and the bounds, and it builds and solves the nonlinear program each step, handling the horizon shifting, warm-starting, and constraint enforcement internally. A do-mpc controller for a nonlinear plant is on the order of twenty lines of declaration; the library supplies the SQP or interior-point solver, the automatic differentiation of the dynamics, and the real-time loop that you would otherwise hand-roll. State the problem; the framework does the planning.

import do_mpc  # nonlinear MPC in a few declarations (sketch)

model = do_mpc.model.Model('discrete')
pos = model.set_variable('_x', 'pos'); vel = model.set_variable('_x', 'vel')
acc = model.set_variable('_u', 'acc')
model.set_rhs('pos', pos + dt * vel)
model.set_rhs('vel', vel + dt * acc)        # could be any nonlinear expression
model.setup()

mpc = do_mpc.controller.MPC(model)
mpc.set_param(n_horizon=15, t_step=dt)
mpc.set_objective(lterm=(pos - 1.0)**2, mterm=(pos - 1.0)**2)  # stage + terminal cost
mpc.set_rterm(acc=0.1)                       # control-effort penalty
mpc.bounds['lower', '_u', 'acc'] = -1.5      # hard actuator limit
mpc.bounds['upper', '_u', 'acc'] =  1.5
mpc.setup()                                  # do-mpc builds and solves the NLP each step
Code 27.2.4: A do-mpc controller declared in a dozen lines. The model, objective, control-effort penalty, and hard actuator bounds are stated declaratively; do-mpc builds the nonlinear program, runs the receding-horizon loop, and enforces the bounds, replacing the hand-written planner and execution loop of Code 27.2.1.

The three implementations together close the section's argument. From-scratch CEM showed that the receding-horizon idea needs only a model you can query; the cvxpy QP showed that linear structure makes the inner problem convex and exact; the do-mpc sketch showed that a framework supplies the nonlinear solver and the loop for free. Across all three the outer logic never changed: optimize a horizon, execute the first action, replan. That invariance, one control idea wearing three solver costumes, is exactly what makes MPC the most adaptable controller in the book and the natural host for the learned models that follow.

6. Exercises

The three exercises below follow the book's pattern: one conceptual, one implementation, one open-ended. Worked solutions to selected exercises appear in Appendix G.

Exercise 27.2.1 (Conceptual)

Consider running MPC but executing all $H$ planned actions before replanning (so you replan only once every $H$ steps) instead of replanning every step. (a) Argue, in terms of the compounding-error mechanism of subsection 2, why this open-loop-within-the-window scheme is less robust to disturbances than executing one step and replanning. (b) Identify the limiting case as $H \to 1$: what familiar kind of controller does single-step-horizon MPC become, and what does it lose? (c) Explain why the terminal cost $\ell_f$ matters more as the horizon $H$ shrinks, and why setting $\ell_f$ to the LQR cost-to-go from Section 27.1 is the principled choice.

Exercise 27.2.2 (Implementation)

Starting from Code 27.2.1, (a) add a state constraint that the position must never exceed $1.05$ (an overshoot ceiling) by adding a large penalty to rollout_cost whenever a rolled-out position crosses it, and verify the trajectory respects the ceiling. (b) Sweep the horizon $H \in \{3, 8, 15, 30\}$ and the disturbance magnitude, and plot tracking error against $H$; confirm that too short a horizon fails to anticipate the approach and that returns diminish past some $H$. (c) Replace the CEM planner with MPPI (softmax-weight every sample by $\exp(-\text{cost}/\lambda)$ and take the weighted-average sequence instead of refitting on elites) and compare convergence and final tracking error against CEM on the same plant.

Exercise 27.2.3 (Open-ended)

Replace the hand-written linear model_step in Code 27.2.1 with a small learned dynamics model: generate trajectories from the true plant, fit a neural network (or even a least-squares linear model) to predict $\mathbf{x}_{t+1}$ from $(\mathbf{x}_t, u_t)$, and plug that learned model into the CEM planner unchanged, realizing the thesis-thread claim that a learned forecaster becomes a controller. Investigate how control quality degrades as the learned model's accuracy drops (train on fewer trajectories), and discuss how this experiment previews the learned-model planning of Chapter 29 and the model-error sensitivity that makes the value-function terminal cost of TD-MPC2 attractive. What goes wrong if the CEM planner exploits inaccuracies in the learned model to find spuriously low-cost plans, and how might you guard against it?