PID Control

The PID controller is used in over 90 % of industrial loops. Learn what P, I and D each do, tuning with Ziegler–Nichols and practical rules, anti-windup, derivative filtering and a digital implementation.

A PID controller computes its output from three terms of the error: Proportional (the present error), Integral (the accumulated past error) and Derivative (the predicted future error from its rate of change).

It is simple enough to implement in a few lines of firmware yet powerful enough to control temperatures, pressures, flows, motor speeds, drones and chemical reactors.

An analogy

Parking a car: P is steering harder the further you are from the spot, I is correcting a persistent drift (a sloping road), and D is easing off as you approach quickly so you don't overshoot.

What each term does

Each term addresses a different weakness.

Tuning methods

Ziegler–Nichols closed-loop: raise P-only gain until sustained oscillation (K_u, period T_u), then apply the table. It gives fast but aggressive (≈ 25–50 % overshoot) tuning. Refinements (Tyreus–Luyben, SIMC/lambda tuning, auto-tuners using relay feedback) give more robust results. Manual tuning: set P for acceptable response, add I to remove offset, add D if overshoot needs damping.

Integral windup

When the actuator saturates (valve fully open, heater at 100 %), the integral keeps growing, causing large overshoot once the error reverses. Anti-windup stops integrating when saturated (clamping) or feeds back the saturation error (back-calculation).

Practical refinements

These are standard features of industrial PID implementations.

Cascade and gain scheduling

Motor drives nest PI current, speed and position loops. Non-linear processes (pH, aircraft across speeds) use gain scheduling — different PID gains at different operating points. When PID isn't enough (multivariable coupling, constraints, long delays), model predictive control takes over.

Formulas

Worked example: Ziegler–Nichols from a critical-gain test

Given: Plant K/(s(s + 1)(s + 5)) → K_u = 30; Oscillation ω = 2.236 rad/s → T_u = 2.81 s

Find: ZN PID gains.

Answer: K_p = 18, K_i = 12.8, K_d = 6.3

Worked example: One step of a digital PID

Given: K_p = 2, K_i = 0.5, K_d = 0.1, T_s = 0.1 s; e = 2.0 now, e_prev = 1.5, integral so far = 3.0

Find: Controller output.

Answer: u = 6.1

Digital PID with anti-windup and filtered derivative (C)

typedef struct {
    float kp, ki, kd;      // gains
    float ts;              // sample time (s)
    float tau;             // derivative filter time constant (s)
    float out_min, out_max;
    float integ, d_state, prev_meas;
} pid_t;

float pid_update(pid_t *c, float setpoint, float meas) {
    float e = setpoint - meas;
    float p = c->kp * e;

    /* derivative on measurement (no setpoint kick), low-pass filtered */
    float alpha = c->tau / (c->tau + c->ts);
    c->d_state = alpha * c->d_state - (1.0f - alpha) * c->kd * (meas - c->prev_meas) / c->ts;
    c->prev_meas = meas;

    float u = p + c->integ + c->ki * e * c->ts + c->d_state;

    /* conditional integration (clamping anti-windup) */
    if (u > c->out_max)      u = c->out_max;
    else if (u < c->out_min) u = c->out_min;
    else                     c->integ += c->ki * e * c->ts;

    return u;
}

Call pid_update() at a fixed rate from a timer interrupt or RTOS task. The integrator only accumulates while the output is not saturated, and the derivative acts on the filtered measurement.

Common mistakes

Applications

Key takeaways

Going further