← Control Loops Outside the Lab

Control Loops Outside the Lab · 03

Integrator Windup and Actuator Limits

A loader overshoots after a throughput limit is lifted. Follow the correction that accumulated behind the cap, then compare two ways to control it.

Consider a bulk loader configured to process 70,000 records per second. During maintenance, an admission limit holds it to 55,000. Records are available to process, and the controller keeps increasing its requested rate to close the gap. The limit holds, so the measured rate stays low.

Then maintenance ends. The admission limit is lifted, but the controller does not simply settle at 70,000 records per second. It has accumulated a large correction during the restriction and can keep pushing past the target, putting a sudden burst of work into the next stage.

This is a useful failure to understand when building adaptive ingestion, concurrency control, or any other feedback loop that remembers past error. Removing the bottleneck can expose a second problem inside the controller.

Why keep a history of rate error?

A rate controller needs to turn the difference between target and measured throughput into an admission command. A purely proportional rule might calculate that command as kp * error.

Without a baseline term, this has a problem even before any limit is reached. When measured throughput reaches the target, the error becomes zero and so does the admission command. But the loader still needs to admit records to maintain that throughput.

For a simplified process whose measured rate eventually equals its admission command, equilibrium requires:

measured = kp * (target - measured)

With a gain of one, that rule settles at half the target. A larger gain reduces the offset, but also makes corrections more aggressive. A fixed baseline can supply the expected rate, although its value may need to change with operating conditions.

Integral control provides another way to supply a sustained command. It accumulates error over time, so even a small persistent shortfall continues adjusting the request:

error = target - measured
requested = kp * error + ki * integral
integral += error * dt

When measured throughput reaches the target, the integral stops changing. Its existing value still supplies a positive admission command. The controller has learned a sustained correction from the earlier error.

That memory needs to account for admission limits. If a request cannot be applied, continuing to integrate the shortfall can build a command that has little connection to the rate the service can currently deliver.

Watch what happens behind a throughput cap

Once the loader reaches its maintenance cap, a larger request changes nothing about the applied rate. The controller still sees positive error, however, and its integral continues to grow.

The simulation makes that mismatch visible in three aligned plots: the process response, requested versus applied action, and the integral. It uses a generic scale, with a target of 70 and a temporary action limit of 55. For the loader example, one unit can represent a thousand records per second. The process gradually approaches the applied action; this isolates the controller behavior rather than modelling the throughput curve of a particular database.

  • Process output
  • Target
  • Requested action
  • Applied action
  • Unapplied request
Presets

Watch the requested action separate from the applied action while the limit is active. Then press Release constraint, or let the configured duration expire. The process can now reach the target, but the old integral keeps the request high. The output may have to pass the target and produce negative error before that accumulated correction comes down.

Repeat with a longer Constraint duration and compare the overshoot after release. The release changes capacity immediately; it does not erase the controller’s history. The shaded interval marks when the temporary limit exists, including moments when the command may still be below it.

This is integrator windup. The integral has grown while its requested correction could not be applied. A nonzero integral is normal; growth disconnected from the actuator’s available range is the problem.

For a data service, excess output after recovery might mean consuming a shared connection budget or sending a burst downstream. Those effects depend on the service, but they explain why “the restriction has been removed” is an incomplete recovery criterion.

Let the controller know what was applied

One remedy is conditional integration. Stop accumulating error when doing so would push an already limited command farther into saturation, but allow error of the opposite sign to unwind it.

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 adds a small amount of logic at the point where requested and applied action are both known.

Back-calculation uses their difference as feedback instead:

integral += (error + backCalcGain * (applied - requested)) * dt

When the request exceeds the available action, the extra term pulls the integral down. The gain controls how quickly that correction happens. Its scale depends on how the integral is stored: these equations store accumulated error and multiply it by ki to produce an output contribution.

MathWorks compares these two approaches in a constrained PID example.

Recovery is part of the design

Suppose the loader must resume useful throughput promptly after maintenance without flooding the next stage. Lowering integral gain may soften the burst, but also makes the controller slower to correct ordinary, persistent errors. Anti-windup addresses the unavailable action directly.

The comparison below gives three controllers the same gains, process response, target, and temporary limit. Only the integral update changes. This is a way to compare recovery policies without accidentally giving one controller an easier workload.

  • No anti-windup
  • Conditional integration
  • Back-calculation
  • Target
Presets

Follow the integral traces through release, then compare the process responses. With the default parameters, conditional integration and back-calculation both reduce overshoot. Adjust Back-calc gain to see the effect on that controller alone; increasing it indefinitely is not a general recipe for better behavior.

Setting Integral gain to zero provides another useful comparison. The controllers become proportional-only and can settle below the target. Avoiding windup by removing integral action also removes the mechanism that corrected the persistent offset.

A heater reaches the same limit

The loader’s admission command has a physical counterpart in a heater’s power setting. Maintaining a room at its target temperature requires sustained power to replace heat lost through the walls, just as maintaining throughput requires a sustained admission rate. Zero temperature error does not mean the heater’s required output is zero.

If the heater is too weak to reach the requested temperature, the error stays positive while the power is already at its maximum. An unprotected integral keeps growing. When conditions change and the target becomes reachable, that stored correction can keep the heater at high power after it is needed.

The same anti-windup rules apply: account for the output actually available, and allow accumulated correction to come down when it pushes beyond that output. The equations describe this interaction whether the command is power or an admission rate.

Keep the controller’s memory observable

For an adaptive service, record the target, measured value, integral, requested action, applied action, and active limits together. A chart of the measured rate alone cannot show the request growing behind a cap.

The applied action should reflect the limit the system actually enforces. A client-side maximum may be higher than a temporary server quota, and knowing only the client’s request leaves the controller blind to that difference.

Mode changes deserve the same attention. On a manual takeover or a new operating target, decide how to transfer the integral. Preserving it can carry an obsolete command forward; resetting it can abruptly remove an output the process still needs.

A queue of real records is not itself integrator windup: that work still needs processing. Windup is accumulated controller state influencing later commands. Keeping those two kinds of backlog separate makes it easier to decide what should be drained, what should be limited, and what should be recalculated when capacity returns.