Control Loops Outside the Lab · 03
Integrator Windup and Actuator Limits
Why accumulated error delays recovery after saturation, and how conditional integration and back-calculation change the response.
A proportional controller can settle with a permanent error. A heater needs power to replace heat lost from a room. If its output is proportional to temperature error alone, with no baseline term, then zero error means zero power. The room has to stay a little cold to keep the heater running.
Integral control provides the missing sustained output. It accumulates error over time, so even a small persistent error continues changing the command.
error = target - measured
requested = kp * error + ki * integral
integral += error * dt
At equilibrium the error can be zero while the integral supplies the required output. That is useful, but it introduces state that must remain meaningful when the actuator reaches a limit.
The request is not the output
Suppose the target is 70 and the actuator is temporarily limited to 55. In the simple plant below, output approaches the applied action, so the target is unreachable while that limit is active.
The controller continues seeing positive error. Its integral grows, and its requested action rises above 55. The plant still receives 55.
- Process output
- Target
- Requested action
- Applied action
- Unapplied request
The middle plot separates requested action from applied action. The bottom plot shows the integral itself. The shaded interval marks when the temporary capacity limit is in place; it does not imply that every controller is saturated throughout that interval.
Release the constraint after the integral has grown. The controller can now apply more output, but it carries the integral accumulated under the old limit. It may continue pushing upward even after reaching the target. Negative error then has to reduce the integral before the command comes down.
That delayed recovery is integrator windup. The integral is not inherently bad state, and a nonzero value at equilibrium is normal. The problem is allowing it to grow without regard to whether the requested correction can be applied.
Stop adding error in the wrong direction
Conditional integration is a small change to the update rule:
const requested = kp * error + ki * integral;
const applied = Math.min(maximum, Math.max(minimum, requested));
const pushingIntoLimit =
(requested >= maximum && error > 0) ||
(requested <= minimum && error < 0);
if (!pushingIntoLimit) integral += error * dt;
This assumes positive gains. It stops adding error when doing so would push an already limited command farther into saturation. It still allows error of the opposite sign to unwind the integral.
Back-calculation uses the difference between requested and applied action:
integral += (error + backCalcGain * (applied - requested)) * dt
Here the integral stores accumulated error, and the requested action includes ki * integral. The units and useful range of backCalcGain follow that convention. Implementations that store the integral’s output contribution directly use a differently scaled equation.
The extra feedback pulls the request toward an action the actuator can deliver. Its gain is another tuning parameter; turning it up indefinitely is not a general solution. MathWorks compares clamping and back-calculation in a constrained PID example.
- No anti-windup
- Conditional integration
- Back-calculation
- Target
The three controllers have the same plant, gains, and capacity limit. Only the integral update differs. With the default parameters, both anti-windup methods reduce the overshoot. They do not guarantee zero overshoot for every gain or release time.
Set integral gain to zero as a useful check. The controller is now proportional-only and can settle below the target after release. That offset is expected.
Keep the applied action observable
Software has actuator limits too: maximum replica counts, downstream connection budgets, rate limits, and finite worker pools. An adaptive controller should know what was actually applied, not just what it requested.
That does not make every growing queue an instance of windup. The controller must have accumulating internal state that continues influencing later commands. A queue may simply contain real work that remains to be done.
For a controller that does integrate, log the error, integral, requested action, applied action, and active limits together. On a mode change or manual takeover, decide how to transfer the integral. Resetting it blindly can create a discontinuity; preserving it blindly can carry an obsolete command into the new operating mode.
The actuator limit belongs in the control design. Clamping the final output protects the actuator, but by itself it does nothing to keep the controller’s internal state consistent with that output.