RobotForge
Published·~15 min

PID: the 80% of industrial control

The oldest controller in the book and still the most used. How it works, how to tune it, and when it's the wrong tool.

by RobotForge
#control#pid#fundamentals

More than 80% of deployed industrial controllers are still PID. It's not because engineers are lazy — it's because PID is genuinely the right answer for a surprising number of problems. Here's the mental model, the math, and the tuning procedure that gets you a working controller in an hour.

The scene

You have a system with a setpoint (target) and a measurement. You want the output (a motor command, heater power, valve position) to drive the measurement to the setpoint. PID does that with three ingredients.

The equation

e(t) = setpoint - measurement         # the error

u(t) = Kp · e(t)                      # P: proportional
     + Ki · ∫ e(τ) dτ                 # I: integral
     + Kd · de/dt                     # D: derivative

Three gains. Three terms. That's the entire controller.

setpoint r(t) Σ + e(t) K_p · e K_i · ∫e K_d · ė plant u(t) y(t) measurement feedback
PID block diagram: error drives three parallel paths (P, I, D), summed into the control output u(t), which enters the plant. The measurement feedback closes the loop.

What each term actually does

P (proportional): fixes the now

"The bigger the error, the harder I push." A P-only controller is a linear spring: it pulls harder when further from the target. Downside: it always leaves a small steady-state error, because if the error goes to zero so does the control output — and the moment gravity or friction pushes back, you need some output to hold position.

I (integral): fixes the past

"I've been off for a while — keep increasing my push." The integral accumulates error over time. It eliminates steady-state error by ratcheting up command until the error actually goes to zero.

Downside: integral windup. If the actuator saturates and the error lingers, the integral balloons; when the error finally clears, the controller overshoots massively. Fix: cap the integral (integral clamping) or stop accumulating while the actuator is saturated (conditional integration).

D (derivative): fixes the future

"The error is decreasing fast — ease off before we overshoot." D is a damping term. It looks at the slope of the error and counteracts fast changes.

Downside: D amplifies noise. The derivative of a noisy signal is a much noisier signal. Always filter the measurement before differentiating, or compute the derivative of the measurement rather than the error (derivative-on-measurement).

A minimal Python implementation

class PID:
    def __init__(self, kp, ki, kd, integral_limit=10.0):
        self.kp, self.ki, self.kd = kp, ki, kd
        self.integral_limit = integral_limit
        self.integral = 0.0
        self.prev_err = 0.0

    def step(self, setpoint, measurement, dt):
        err = setpoint - measurement
        self.integral = max(-self.integral_limit,
                            min(self.integral_limit,
                                self.integral + err * dt))
        deriv = (err - self.prev_err) / dt
        self.prev_err = err
        return self.kp * err + self.ki * self.integral + self.kd * deriv

That's a working PID. Twenty lines. In production you'd add output saturation, derivative-on-measurement, and anti-windup — but this gets you a running controller.

Tuning: the Ziegler-Nichols crank

The classic tuning method. Not the best tuning, but the fastest way to a working controller.

  1. Set Ki = 0, Kd = 0.
  2. Increase Kp until the output oscillates continuously with constant amplitude. Call that gain Ku and the oscillation period Tu.
  3. Set Kp = 0.6·Ku, Ki = 1.2·Ku/Tu, Kd = 0.075·Ku·Tu.

Done. Your controller is now stable, with modest overshoot and reasonable response time. You can squeeze more performance later.

When PID is the wrong tool

  • Multi-input, multi-output systems. A quadrotor has three coupled rotational axes. You can stack three independent PIDs (and this is how quadcopters fly), but performance is limited. Consider LQR or MPC.
  • Strong nonlinearities. A robot arm where gravity torque depends on configuration. PID works per joint, but compensating gravity explicitly via computed-torque control does better.
  • Hard constraints. PID can't natively respect "joint must stay under 180°" or "motor current must stay under 5A." That's MPC's home turf.
  • Very fast transients with known dynamics. Feedforward (not feedback) is the right tool when you know what's coming.

When PID is exactly the right tool

  • Single-input, single-output. Motor speed. Heater temperature. Pressure regulator.
  • Plant dynamics are roughly linear near the operating point.
  • You don't need to squeeze the last 10% of performance.
  • You need the controller to be trivially debuggable by anyone.

Real-robot gotchas

  • Discrete time. All real PID controllers run at a fixed rate (100 Hz, 1 kHz). The math above is continuous. Your derivative term is actually a finite difference, and that's where noise lives. Filter.
  • Sample rate. Control loop at 10× the bandwidth of the thing you're controlling, minimum. 100 Hz for mechanical systems. 1 kHz+ for direct-drive motors.
  • Actuator saturation. Every motor has a max torque. Your controller output has to respect it, and your integral has to not wind up while it does.
  • Units, always units. Kp has units of "output per error." If error is in radians and output is in volts, Kp is V/rad. Nothing will tell you when you mix them.

Exercise

Simulate a cart with mass 1 kg, Force = PID output, target position = 1.0 m. Start with Kp = 1, Ki = 0, Kd = 0 and watch the cart oscillate. Crank Kp until it oscillates at constant amplitude. Record Ku and Tu. Apply Ziegler-Nichols. Compare with PID-free (open-loop) behavior.

This exercise in a Jupyter notebook takes 30 minutes and teaches you more about control than a week of reading. Next lesson: LQR, the principled generalization of PID for multi-state systems.

Comments

    Sign in to post a comment.