← Lesson diary · Process Identification and Control

Tank Control Lab

Process Identification and Control · Lesson 2

Tank Control Lab

The storage tank of Lesson 2, controlled twice at the same time: a classical PID on the left, the constrained MPC of the lecture on the right. Same tank, same demand, same pump limit — the only difference is that MPC solves an optimization problem over a prediction window at every sampling time. The simulation runs on its own; every parameter below can be changed while it runs.

Plant

06:00
PID
55.0 m³
u = 0.00 m³/min
within limits
MPC
55.0 m³
u = 0.00 m³/min
within limits
Stored volume xkm³
PID MPC MPC prediction over the window reference xref limits xmin, xmax
Pump flow ukm³/min
Demand dk — measured and forecastm³/min

Score so far

0 min simulated
Controller Mean |x − xref| Lowest level Highest level Time outside limits Worst excursion Water pumped
PID — — — — — —
MPC — — — — — —

The two controllers pump almost the same total water, and they must: over a run that starts and ends near the reference, mass balance fixes the total at whatever the demand took out. What separates them is when they pump it.

KKT certificate: —

The MPC problem has stayed feasible so far: every level limit it was asked to respect was still reachable with the pump it has.

The problem solved at every step k

Exactly the formulation of the lecture, with the absolute time k and the relative time i inside the window.

minu Σi=0..N-1 [ a·(xi+1|k − xref)² + b·ui|k² ]
s.t. xi+1|k = xi|k + ui|k·T − di|k·T (dynamics)
   0 ≤ ui|k ≤ umax (pump)
   xmin ≤ xi|k ≤ xmax (level limits)
   x0|k = xk (measurement)

Only u0|k is applied and the rest of the sequence is discarded, then the whole problem is rebuilt one minute later — the receding horizon of Lesson 1. Sampling time T = 1 min, tank capacity 100 m³, limits 20–90 m³.

A quadratic program with inequalities has no closed-form solution, because which constraints are active is not known in advance — that is the content of the complementarity conditions. So the page solves it in two phases. First a projected Newton method, with the pump limits enforced exactly by projection and the level band as a heavily penalized soft constraint, gives a solution in fewer than ten iterations and tells us which constraints are active. Then the KKT system of that active set is solved exactly, once, and the result is checked: every constraint satisfied, every multiplier with the right sign. When the check passes, the applied control is provably optimal.

What to notice

  • Anticipation. Around 07:30 the demand becomes larger than the pump can supply. The MPC starts filling the tank before the peak arrives, because the peak is already inside its prediction window. The PID has no error yet, so it does nothing — a feedback controller can only react to what has already happened.
  • The bill at the end. With the settings the page opens with, the PID spends about 35 minutes below the 20 m³ limit and touches 8 m³; the MPC never leaves the band and never goes under 27 m³. Same tank, same pump, same demand.
  • How far ahead you look. Set N to 6 steps: the peak is outside the window, the MPC becomes myopic and fails almost like the PID. Lower N gradually instead and you find the threshold — 36 steps here — below which the anticipation starts too late and the level goes under the limit anyway. The window has to be longer than the time the plant needs to react.
  • How much the forecast is worth. Untick "MPC knows the demand forecast": the controller then assumes the present demand will persist, and the anticipation disappears even with a long window. Producing that forecast from data is what the identification part of the course is about.
  • Same water, different timing. Look at the last column of the table: the two controllers end up pumping almost the same total volume. They have to — over a run that starts and ends near the reference, mass balance fixes the total at whatever the demand took out, and no controller can create or destroy water. MPC does not win by pumping more, it wins by pumping sooner, moving the same litres to the hours when they are still useful.
  • The price of control. Only the ratio b/a matters, and it has to be large before anything happens: the level error is in cubic metres (tens of them) while the flow is about one cubic metre per minute, so their squares differ by three orders of magnitude and b must reach roughly a thousand times a to weigh on the decision. Around b = 1000 the pump starts saving effort; at b = 5000 it becomes so reluctant that it lets the tank go under the limit for ten minutes. Weight tuning is a matter of units before it is a matter of taste.
  • When a constraint actually does something. At the reference the page starts with, unticking "MPC enforces the level limits" changes nothing: the tracking cost alone already keeps the tank inside the band, so the constraint is inactive and carries no information. Set xref to 35 m³ instead — now the optimum would like to sit near the floor, the constraint becomes active, and switching it off sends the level several metres deeper. A constraint matters only when the optimum wants to cross it.
  • When the tank cannot be saved. With a short window, or in the "unforecast spike" scenario, the MPC also ends up below the limit. That is not a bug of the solver: see the note on feasibility.

Feasibility, and what happens when it is lost

An MPC algorithm is recursively feasible when a problem that admits a solution at one sampling time keeps admitting one at every future time. This tank shows how that property can be lost. Take 07:45 with a short window: the level is 36 m³, the demand is 2.35 m³/min against a pump that gives at most 1.20, so the tank keeps emptying at 1.15 m³/min whatever the controller does, and the peak still has 25 minutes to run. The level will reach 7 m³, and no admissible sequence of inputs can respect the 20 m³ limit. The feasible set is empty, and no solver can do anything about it. With the default window the same controller sees the end of the peak already at 07:00 and fills the tank while it still can.

Watch the limit unreachable badge appear on the MPC readout: it lights up while the constraint is impossible, usually a few minutes before the level actually crosses the limit.

Two ways out, both standard. Give the problem a longer window and a terminal condition, so the controller commits early enough to keep a solution available — this is how recursive feasibility is proved. Or accept that the constraint may become impossible and write it as a soft constraint, with a slack variable that is heavily penalized in the objective: the problem stays feasible and the controller minimizes the violation instead of failing.

This page takes the second road: the level band is written as a soft constraint, penalized so heavily that the controller respects it to within centimetres whenever it can, and minimizes the violation when it cannot. Nothing saturates the pump by hand — the optimizer arrives at maximum flow on its own, because that is the best it can do.