>
section 6 of 128 min read

6. Compensator Design

We have a plant. Its raw open-loop transfer function does not give us the closed-loop performance we want. We add a compensator Gc(s)G_c(s) in series with the plant in the forward path:

plaintext
ref ──→[+]──→[Compensator G_c]──→[Plant G_p]──→ output
       ↑                                           │
       └───────────────────────────────────────────┘
                    (sensor)

The compensator's job: shape the open-loop transfer function so that the closed-loop response meets our specs (rise time, settling time, overshoot, steady-state error, gain and phase margins). Several standard styles.

6.1 Lead compensator

A lead compensator has a zero closer to the origin than its pole:

Gc(s)=K1+ατs1+τs,α>1G_c(s) = K \cdot \frac{1 + \alpha \tau s}{1 + \tau s}, \quad \alpha > 1

Or, equivalently, Gc(s)=K(s+1/(ατ))/(s+1/τ)G_c(s) = K (s + 1/(\alpha\tau)) / (s + 1/\tau). The zero is at 1/(ατ)-1/(\alpha\tau), the pole at 1/τ-1/\tau (so the pole is farther left than the zero, in s-plane terms).

Why "lead": the compensator adds a positive (advancing) phase shift, peaking between the zero and pole frequencies, reaching a maximum:

ϕmax=arcsin(α1α+1)\phi_\text{max} = \arcsin\left(\frac{\alpha - 1}{\alpha + 1}\right)

Centered at the geometric mean frequency ωmax=1/(τα)\omega_\text{max} = 1/(\tau \sqrt{\alpha}).

Designed correctly, lead compensators add phase margin at the gain crossover frequency, improving stability and speeding up the transient response.

Cruise-control intuition. A lead compensator anticipates. "I see the speed is dropping fast, even if the absolute error is small. Let me preemptively add throttle." In algebraic terms it differentiates the error (the zero of the compensator is roughly a derivative term), giving a heads-up to the controller. Same idea as the D term in PID.

6.2 Lag compensator

A lag compensator has a pole closer to the origin than its zero:

Gc(s)=K1+τs1+βτs,β>1G_c(s) = K \cdot \frac{1 + \tau s}{1 + \beta \tau s}, \quad \beta > 1

Why "lag": the compensator adds a small negative phase across part of the frequency range. More importantly, it raises the low-frequency gain by a factor of β\beta, improving steady-state error tracking.

Lag is the right choice when you want better steady-state accuracy without sacrificing transient response. The lag compensator's pole-zero pair is placed far below the gain crossover frequency, so the phase lag is mostly gone by the time you reach the loop's stability-critical frequencies. The DC gain boost remains.

Cruise-control intuition. A lag compensator behaves like an integrator that has been bandlimited. It says, "I keep accumulating long-term error and slowly correct for it, but I won't react to fast transients." Useful when the plant has a slow drift that needs to be canceled (engine power loss as it heats up) but you don't want to amplify high-frequency noise from the speedometer.

6.3 Lag-lead compensator

A combination of both, with the lead at higher frequencies (improving phase margin and transient speed) and the lag at lower frequencies (improving steady-state error). Lag-lead is the most flexible classical compensator and the basis of much industrial controller design. In transfer function form, it has two poles and two zeros, designed jointly.

6.4 PID controllers: the world's workhorse

Far and away the most popular controller in industry: the proportional-integral-derivative controller. In the time domain:

u(t)=Kpe(t)+Ki0te(τ)dτ+Kdde(t)dtu(t) = K_p e(t) + K_i \int_0^t e(\tau) \, d\tau + K_d \frac{de(t)}{dt}

In the Laplace domain:

Gc(s)=Kp+Kis+Kds=Kds2+Kps+KisG_c(s) = K_p + \frac{K_i}{s} + K_d s = \frac{K_d s^2 + K_p s + K_i}{s}

Three terms, three jobs:

  • KpK_p (proportional): acts on present error. Larger KpK_p → faster response, more aggressive correction. Too large KpK_p → oscillation, instability.
  • KiK_i (integral): integrates past error. Eliminates steady-state error. Too large KiK_i → wind-up, overshoot.
  • KdK_d (derivative): anticipates future error from the trend. Adds damping. Too large KdK_d → noisy, jittery response (because differentiating amplifies sensor noise).

Cruise-control PID, in plain English:

  • P: "I am 5 mph below target, so I press the throttle 5 units."
  • I: "I have been below target for 10 seconds straight, so I ramp up the throttle even more until the error is exactly zero."
  • D: "The speed is rising fast right now, so I ease off so I don't overshoot."

PID has all the desirable traits of a control law:

  • Type-1 behavior: the integrator forces zero steady-state error to step inputs.
  • Phase lead at high frequencies: the derivative term provides damping.
  • Adjustable: three knobs cover most plants.
  • Robust: works passably even when the model is wrong.
  • Easy to implement: a microcontroller can compute it in nanoseconds.

6.5 Where you find PID

A non-exhaustive list:

  • Quadcopter pitch / roll / yaw stabilization: three independent PID loops running at 500 Hz to 4 kHz.
  • 3D printer hotend temperature: PID with a thermistor as sensor and a heater MOSFET as actuator.
  • Cruise control in cars.
  • Process plants (temperature, pressure, flow rates, level): PID is the standard, in 99% of industrial loops.
  • Hard-disk drive head positioning: PID with sub-microsecond loop times.
  • Op-amp servo loops in voltage regulators.
  • Power-electronic buck and boost converter voltage loops.
  • Electric vehicle motor controllers (current loop, speed loop, position loop).
  • HVAC controllers in commercial buildings.
  • Camera autofocus.

PID is the most-deployed control algorithm in human history, by orders of magnitude.

6.6 PID tuning

Ziegler-Nichols closed-loop method: increase KpK_p until the system oscillates with sustained amplitude. Measure the period TuT_u of oscillation and the gain at oscillation KuK_u. Compute:

ControllerKpK_pKiK_iKdK_d
P0.5Ku0.5 K_u
PI0.45Ku0.45 K_u1.2Kp/Tu1.2 K_p / T_u
PID0.6Ku0.6 K_u2Kp/Tu2 K_p / T_uKpTu/8K_p T_u / 8

Crude but a starting point. Most textbooks describe this method; in practice, you almost never push a real plant to instability on purpose. Open-loop step-response based methods (such as Cohen-Coon) are gentler.

Modern methods:

  • Auto-tuning: many industrial PID products (Honeywell, Yokogawa, Allen-Bradley, Siemens, Emerson) include automatic tuners that perturb the plant gently and infer parameters.
  • Model-based design: when you have a good plant model, you can compute optimal PID gains from specifications via HH_\infty synthesis or LQG.
  • Adaptive PID: gains change in real-time as the plant changes (gain scheduling for nonlinear plants).
  • Reinforcement learning: emerging approach, especially for plants that are too complex to model.

6.7 Practical PID issues

A clean continuous-time PID doesn't survive contact with reality. Real implementations need:

  • Anti-windup: when the actuator saturates (throttle pegged), the integrator keeps integrating, accumulating a huge value. When the error reverses, the actuator stays saturated until the integrator unwinds, causing massive overshoot. Cure: clamp the integrator, or stop integrating when the actuator is saturated, or back-calculate.
  • Derivative kick: a step change in the reference produces a huge spike in derivative. Cure: take the derivative of the output (negated), not the error.
  • Filtered derivative: a pure KdsK_d s amplifies high-frequency noise to infinity. Cure: replace with Kds/(1+s/N)K_d s / (1 + s/N) for some filter constant.
  • Bumpless transfer: when switching between manual and automatic, the controller's state must be preserved so the output doesn't jump.

These details are the difference between a textbook PID and a production-grade PID. Embedded firmware libraries (e.g., for FOC motor control or 3D printer firmware like Marlin) implement them carefully.

Hardware-security tie-in. Anti-windup is a safety measure against fault-injection attacks. If an attacker can inject a brief disturbance that saturates the actuator, an unbounded integrator becomes a runaway. Many embedded PID implementations have buggy anti-windup, and this is a real attack surface in IoT motor drivers and industrial controllers.

A clean PID implementation in Python:

python
class PID:
    def __init__(self, Kp, Ki, Kd, dt, u_min=-np.inf, u_max=np.inf):
        self.Kp, self.Ki, self.Kd = Kp, Ki, Kd
        self.dt = dt
        self.u_min, self.u_max = u_min, u_max
        self.integral = 0.0
        self.prev_error = 0.0
 
    def update(self, ref, measurement):
        error = ref - measurement
        # Proportional term
        P = self.Kp * error
        # Integral term, with anti-windup via clamping
        self.integral += error * self.dt
        I = self.Ki * self.integral
        # Derivative on measurement (avoid derivative kick)
        D = -self.Kd * (measurement - self.prev_measurement) / self.dt \
            if hasattr(self, 'prev_measurement') else 0
        self.prev_measurement = measurement
        # Sum and saturate
        u = P + I + D
        u_sat = np.clip(u, self.u_min, self.u_max)
        # Back-calculate the integrator to undo wind-up
        if u != u_sat:
            self.integral -= (u - u_sat) / self.Ki * 0.5
        return u_sat

This is the kernel of every motor controller, every servo amplifier, every flight controller you will write.