Compensator Design: Lead, Lag & Loop Shaping
Designing controllers in the frequency domain. Learn gain and phase margins as design specs, the lead compensator (adds phase, speeds up), the lag compensator (raises low-frequency gain, reduces error), lead-lag, loop-shaping rules, bandwidth vs crossover, and a complete lead design computed in Python — the classical techniques behind servo and aerospace control.
When a plain gain cannot give both accuracy and stability, you add dynamics to the controller: a compensator. Lead networks add phase near crossover to improve damping and speed; lag networks add low-frequency gain to reduce steady-state error without disturbing stability.
PID is itself a lead-lag compensator (PI ≈ lag, PD ≈ lead). Understanding loop shaping explains why PID works and how to go beyond it.
An analogy
A lead compensator is like steering a car by looking further ahead: acting on where things are going (phase advance) prevents weaving. A lag compensator is like patient persistence: it slowly removes the remaining error without rushing.
Why compensate?
Raising the gain improves accuracy and speed but reduces phase margin until the loop oscillates. A compensator reshapes the open-loop frequency response L(jω) = C(jω)G(jω) so that you get high gain where you need accuracy and enough phase where the loop crosses 0 dB.
Lead compensation
A lead network has its zero below its pole, adding positive phase (up to ~60° per stage) between them. Placed at the crossover, it increases phase margin (less overshoot) and usually crossover frequency (faster). Downside: it raises high-frequency gain, amplifying sensor noise and saturating actuators.
Lag compensation
A lag network adds gain at low frequency (by β) while leaving the crossover region nearly unchanged if its zero is placed a decade below ω_c. It reduces steady-state error by β without hurting stability, at the cost of a slow 'tail' in the response. PI control is the limit β → ∞ (a pure integrator).
Loop shaping and robustness
Loop shaping specifies |L(jω)|: large at low frequency (disturbance rejection, tracking), crossing 0 dB with about −20 dB/decade slope (good phase margin), and rolling off fast at high frequency (noise, unmodelled dynamics). The sensitivity function S = 1/(1 + L) shows disturbance amplification; its peak M_s ≤ 2 (6 dB) is a common robustness target. H∞ and μ-synthesis formalise these ideas for multivariable systems.
Formulas
- Lead compensator:
C(s) = (Ts + 1)/(αTs + 1), α < 1 - Maximum phase lead:
sin φ_max = (1 − α)/(1 + α) ⇔ α = (1 − sin φ)/(1 + sin φ) - Frequency of max phase:
ω_m = 1/(T√α); gain there = 10·log₁₀(1/α) dB - Lag compensator:
C(s) = β(Ts + 1)/(βTs + 1), β > 1; zero ≈ ω_c/10 - PM ↔ damping (approx.):
ζ ≈ PM/100 (PM in degrees, PM < 70°) - Loop shaping:
|L| ≫ 1 at low ω (tracking), |L| ≪ 1 at high ω (noise), slope ≈ −20 dB/dec at ω_c
Worked example: Lead ratio
Given: Need 50° of phase from one stage
Find: α and gain at ω_m.
- α = (1 − sin 50°)/(1 + sin 50°)
- gain = 10·log(1/α)
Answer: α = 0.132, +8.8 dB at ω_m
Worked example: Max phase of α = 0.1
Given: α = 0.1
Find: φ_max.
- sin⁻¹(0.9/1.1)
Answer: 54.9°
Worked example: Lag for accuracy
Given: Loop has K_v = 2 and good PM at ω_c = 1 rad/s; need K_v = 20
Find: Lag network.
- β = 10
- Zero at ω_c/10 = 0.1 → T = 10 s; pole at 0.01 rad/s
Answer: C(s) = 10(10s + 1)/(100s + 1)
Designing a lead compensator numerically
import math, cmath
# Lead compensator for G(s) = K / (s (s + 1)), goal: Kv = 10, phase margin >= 45 deg
K = 10.0 # from Kv = lim s*G = K = 10
def G(w):
s = 1j * w
return K / (s * (s + 1))
def margins(L):
lo, hi = 1e-3, 1e3 # find gain crossover |L| = 1 by bisection (log)
for _ in range(200):
mid = math.sqrt(lo * hi)
if abs(L(mid)) > 1: lo = mid
else: hi = mid
wc = math.sqrt(lo * hi)
pm = 180 + math.degrees(cmath.phase(L(wc)))
return wc, pm
wc, pm = margins(G)
print(f"uncompensated: wc = {wc:.2f} rad/s, PM = {pm:.1f} deg")
phi = math.radians(45 - pm + 12) # extra phase needed + 12 deg safety
alpha = (1 - math.sin(phi)) / (1 + math.sin(phi))
# put the max phase at the new crossover, where |G| = sqrt(alpha)
lo, hi = 1e-3, 1e3
for _ in range(200):
mid = math.sqrt(lo * hi)
if abs(G(mid)) > math.sqrt(alpha): lo = mid
else: hi = mid
wm = math.sqrt(lo * hi)
T = 1 / (wm * math.sqrt(alpha))
C = lambda w: (1 + 1j * w * T) / (1 + 1j * w * alpha * T)
wc2, pm2 = margins(lambda w: C(w) * G(w))
print(f"lead: alpha = {alpha:.3f}, T = {T:.3f} s -> C(s) = ({T:.3f}s + 1)/({alpha*T:.4f}s + 1)")
print(f"compensated: wc = {wc2:.2f} rad/s, PM = {pm2:.1f} deg")
# uncompensated: wc = 3.08 rad/s, PM = 18.0 deg
# lead: alpha = 0.227, T = 0.464 s -> C(s) = (0.464s + 1)/(0.1053s + 1)
# compensated: wc = 4.53 rad/s, PM = 51.5 degThe textbook procedure (set gain for accuracy, measure PM, add the missing phase plus a safety margin, place ω_m where the compensated crossover will be) in about 30 lines. The result: phase margin from 18° (≈ 60% overshoot) to 51.5° (≈ 20%) and a faster loop.
Common mistakes
- Wrong: Using lead where the sensor is very noisy. — Right: Add filtering or limit high-frequency gain.
- Wrong: Placing the lag zero near crossover. — Right: Keep it a decade below ω_c.
- Wrong: Asking one lead stage for > 60°. — Right: Use two stages or reconsider the design.
Applications
- Servo drives: Lead networks in position loops.
- Aircraft & missiles: Classical loop shaping of autopilots.
- Power supplies: Type II/III compensators in DC-DC converters.
- Disk drives: Notch + lead for track following.
Key takeaways
- Lead: phase & speed; lag: accuracy.
- α = (1 − sin φ)/(1 + sin φ); ω_m = 1/(T√α).
- Shape |L|: high at low ω, −20 dB/dec at ω_c, low at high ω.
- PID ≈ lead-lag.
Going further
- Nichols chart: Open-loop gain vs phase with closed-loop contours.
- QFT: Quantitative feedback theory for uncertain plants.
- H∞ loop shaping: McFarlane–Glover robust design.