DEV Community

Cover image for Taming Overshoot Without Losing Disturbance Rejection: A Look at Two-Degree-of-Freedom PI Control
NovaSolver
NovaSolver

Posted on Originally published at novasolver.jp

Taming Overshoot Without Losing Disturbance Rejection: A Look at Two-Degree-of-Freedom PI Control

The tuning trap every process control engineer hits

Ask any process control engineer to describe classic single-loop PI tuning, and you'll hear a familiar complaint: turn up the gain to reject disturbances faster, and the setpoint response starts to overshoot and ring. Turn the gain down to keep the response smooth, and a load disturbance takes forever to settle out. The two goals point in opposite directions because a standard PI controller has exactly one set of knobs, K_c and T_i, doing two jobs at once — chasing the setpoint and rejecting whatever the process throws at it.

Two-degree-of-freedom (2-DoF) control breaks that coupling. Instead of feeding the raw setpoint directly into the PI error calculation, you route it through a reference pre-filter first. The feedback controller still handles disturbance rejection exactly as before, but the setpoint the controller sees is smoothed on its way in. You get to tune aggressiveness and smoothness as two separate problems instead of one compromise, which is a genuinely different design space than anything a single-loop PI structure can offer.

Where the extra degree of freedom actually lives

A standard 1-DoF PI loop computes error as e(t) = r(t) − y(t), where r is the setpoint and y is the process variable. Every parameter you tune, K_c and T_i, shapes both the setpoint response and the disturbance response simultaneously, because both responses pass through the same error signal. There's no way to push one without dragging the other along with it.

A 2-DoF structure inserts a first-order pre-filter ahead of the setpoint:

r_filtered(t) = (1 / (tau_F * s + 1)) * r(t)
e(t) = r_filtered(t) - y(t)
u(t) = K_c * (e(t) + (1/T_i) * integral(e(t) dt))
Enter fullscreen mode Exit fullscreen mode

tau_F, the pre-filter time constant, only touches how quickly a setpoint change reaches the controller. It has no effect on how the loop responds to a disturbance d entering downstream of the controller, because a load disturbance never passes through the pre-filter at all. That's the whole trick: K_c and T_i can now be pushed hard for tight disturbance rejection, and tau_F alone absorbs the resulting setpoint overshoot.

Worked example: a flow loop with a sluggish valve

Take a flow control loop where a tuning study already gave you K_c = 2.5 and T_i = 8 s for good disturbance rejection — say, a downstream valve disturbance step d of 15% of span. With those aggressive settings and no pre-filter (tau_F = 0, equivalent to 1-DoF), the setpoint response overshoots badly. Simulating the step response gives:

  • 1-DoF overshoot: roughly 42% for a setpoint step
  • Setpoint settling time: around 38 s, dragged out by the ringing

That's not acceptable for an operator watching a trend screen — a 15% setpoint change shouldn't swing the flow past 21% before settling. But dropping K_c to fix the overshoot would also gut the disturbance response, pushing peak disturbance deviation from a tolerable 6% of span up toward 11-12%.

Instead, add a pre-filter with tau_F = 6 s while leaving K_c = 2.5 and T_i = 8 s untouched. Because the filter only slows the setpoint path, re-running the same step now gives:

  • 2-DoF overshoot: down to roughly 8%
  • Setpoint settling time: about 22 s, faster than before despite the added filter, because the overshoot-driven ringing disappears
  • Peak disturbance deviation: unchanged at 6%, since the disturbance step d never touches the filter

The comparison between 1-DoF overshoot and 2-DoF overshoot for the identical K_c/T_i pair is the entire argument for the 2-DoF structure in one number: you removed 34 percentage points of overshoot for zero cost in disturbance performance. That's a trade you can't make with a single-loop PI controller no matter how you juggle the two gains against each other.

Where people get this wrong

The most common mistake is treating tau_F as a second gain to retune the whole loop. It isn't. If you find yourself increasing tau_F past the point where the setpoint response looks sluggish and boring, you've probably got K_c set too aggressively for the process and are trying to mask it with filtering instead of fixing the root cause. A good rule of thumb: pick K_c and T_i first, purely for disturbance rejection performance, and only then dial in tau_F to bring the setpoint overshoot down to whatever the application requires — often near-zero for operator-facing setpoint changes, more relaxed for cascade slave loops where a little overshoot barely matters to the outer loop.

The second mistake is forgetting that tau_F does nothing for load disturbances that enter partway through the process rather than right at the plant input. If your real disturbance enters downstream of a large time constant, the peak disturbance deviation predicted by a simple two-block model can be optimistic. Always validate against the actual disturbance entry point in your process, not just the textbook block diagram.

A third, subtler issue: very large tau_F values can make the loop feel sluggish enough that operators start manually walking the setpoint in small steps to avoid the perceived lag, which defeats the purpose of a controller in the first place. There's a practical ceiling on tau_F even when the math says a larger value would reduce overshoot further, and that ceiling is usually set by operator tolerance rather than by stability margins.

How this generalizes beyond flow loops

The same 2-DoF structure shows up under different names across the process industries, and it's worth recognizing it in the wild. A setpoint ramp limiter, common on temperature loops where a fast setpoint jump would otherwise thermally shock equipment, is a crude relative of the pre-filter: instead of a first-order lag it uses a hard rate limit, but the intent is identical, decouple how fast the setpoint moves from how aggressively the feedback controller rejects load disturbances. The pre-filter approach modeled here is smoother and easier to reason about analytically, since a first-order lag has a single time constant to tune rather than a rate limit that behaves differently depending on step size.

It's also worth noting what 2-DoF control does not fix. If the process itself is nonlinear, if valve characteristics change significantly between low and high flow, tuning K_c, T_i, and tau_F at one operating point won't necessarily hold at another. The pre-filter helps with the setpoint-versus-disturbance tradeoff at a fixed operating point; it doesn't compensate for gain scheduling needs across a wide operating range. For loops that span a wide flow range, expect to revisit K_c periodically even after the pre-filter is dialed in, since the underlying process gain the controller is fighting against may not be constant.

Finally, 2-DoF thinking extends naturally to cascade control architectures. A master loop's output becomes the slave loop's setpoint, and a pre-filter on the master's setpoint path shapes how aggressively the master pushes new setpoints down to the slave, independent of how tightly the slave rejects its own local disturbances. Once you've internalized the pre-filter concept on a single loop, applying it to a cascade structure is a natural next step rather than a new idea to learn from scratch.

Try it yourself

The interactions between K_c, T_i, tau_F, and a disturbance step d are easier to feel than to compute by hand, especially once you start hunting for the tau_F value that gets 2-DoF overshoot down to a target number without dragging out setpoint settling time. Try the Two-Degree-of-Freedom Control Simulator here to run your own step responses side by side and see the 1-DoF versus 2-DoF comparison directly. If you're chasing gain and phase margin more broadly, the anti-windup tool is a natural next stop for loops that saturate during large setpoint changes.

Top comments (0)