LUNAROPS · OPERATIONAL UPLINK 100% UPTIME 1,247d POSTS 893 JEFF.MOON@LUNAROPS.DEV UTC --:--:--

PID Control from First Principles

control-systemspidautomationfeedbackengineeringrobotics

PID is the feedback loop that runs an embarrassing fraction of industrial reality. Your home thermostat is PID. The cruise control on a car is PID. A chemical plant holding a reactor at temperature is a stack of nested PIDs. A 3D printer’s nozzle and bed temperatures are PIDs. The autopilot on a small aircraft is mostly PIDs in cascade. The reason this one algorithm dominates a century of control engineering is that it is the simplest controller that works on the broadest class of real systems, requires no model of the plant it controls, and can be tuned by hand on hardware you do not fully understand. Its three terms — proportional, integral, derivative — each correspond to an intuition that maps cleanly onto how a thoughtful human would manually drive a system to a setpoint, and once you internalize what each one is doing you can read the behavior of almost any real-world plant by watching how it responds to disturbances. This post walks PID from first principles: what each term is, why the math is the way it is, how to tune one without a textbook, the standard failure modes (windup, derivative kick, deadtime) that bite every practitioner, and the honest gap between the textbook controller and the one that actually keeps a real plant working.


What a Controller Is For

A control system always has the same three pieces. There is a plant — the physical thing you are trying to control, a heater, a motor, an aircraft, a tank of fluid. There is a measurement — a sensor that tells you the current state of the plant, the temperature, the position, the altitude. And there is an actuator — the lever you can pull on the plant, a power level into the heater, a current through the motor, a deflection of a control surface. Your job, as the controller, is to convert the measurement into the actuator command in some way that drives the plant to the state you want.

The thing you want is the setpoint — say, “I want the oven at 200 °C.” The current state of the plant is the process variable — “right now the oven is at 158 °C.” The difference between them is the error:

   error e(t) = setpoint - process_variable

A controller is any function that takes the history of the error and produces an actuator command. The simplest possible controller is a bang-bang controller: if the error is positive, turn the heater fully on; if negative, fully off. This works surprisingly well for slow plants like home thermostats, but it overshoots, oscillates around the setpoint, and wears mechanical actuators out fast. The next level up — a continuously variable response that uses the size of the error to decide how hard to drive the actuator — is proportional control, the P in PID.

The whole game of “controller design” is finding a function of the error that drives the plant to setpoint quickly, holds it there steadily, and does not blow up or oscillate when the plant gets bumped. PID is the family of controllers built from three specific terms that, individually and together, address those three goals.


The Three Terms, From Intuition

Imagine you are driving a car and trying to hold 60 mph on cruise control. The car loses a little speed on a hill and you have to step on the accelerator. There are three things you might pay attention to as you decide how hard to press.

Proportional (P): how big is the error right now? If you are 5 mph slow, press harder than if you are 1 mph slow. The control output is just a constant times the current error: more error, more push. This is the brain-stem response to “we are not where we want to be” and it gets you most of the way there fast.

Integral (I): how long have you been off target? If you have been 1 mph slow for thirty seconds, the proportional response alone clearly is not enough — there is some persistent drag (a long hill, an underinflated tire) that the proportional term cannot overcome by itself. The integral term accumulates the error over time and pushes harder the longer the error persists. It exists specifically to eliminate steady-state error: the constant offset that proportional control alone leaves behind when there is a constant disturbance.

Derivative (D): how fast is the error changing? If you are 1 mph slow but accelerating rapidly toward setpoint already, you should back off — the system has momentum and pushing harder will overshoot. If you are 1 mph slow but the gap is widening, you should push harder. The derivative term is the “what is going to happen next” sensor: it dampens overshoot and oscillation by responding to the rate of change of the error rather than its magnitude.

Mathematically, the textbook PID controller adds these three terms:

   u(t) = Kp * e(t)  +  Ki * integral_0^t e(tau) d(tau)  +  Kd * de(t)/dt

   where:
     u(t)  = controller output (actuator command)
     e(t)  = error = setpoint - process_variable
     Kp    = proportional gain
     Ki    = integral gain
     Kd    = derivative gain

The three gains are the dials you tune. Each one decides how loudly the corresponding intuition speaks. Pick them well and the loop converges fast and settles cleanly; pick them poorly and it overshoots, oscillates, drifts, or refuses to track. The whole art of using a PID controller is choosing those three numbers for a plant whose dynamics you may not fully understand.

   PID CONTROL LOOP

                                       disturbance
                                            │
                                            ▼
   setpoint ──┐                         ┌──────┐
              │      ┌─────────┐         │      │
              ├──> Σ ─> PID    ─actuator─>│plant ├──> process variable
              │   ─    │ K_p, K_i, K_d │  │      │            │
              │   ▲    └─────────┘         └──────┘            │
              │   │                                            │
              │   └──── sensor (process variable) ◄────────────┘
              │
              setpoint feeds back into the summing junction

How Each Term Behaves Alone

A useful exercise in understanding PID is to imagine each term running by itself.

P alone is intuitive and fast but leaves a residual offset. If your plant has any kind of constant disturbance — a heat loss to the room, a load on the motor, gravity on a robot arm — pure proportional control needs a non-zero output just to hold steady against it. But “non-zero output” requires “non-zero error” because the only way the P term generates output is from error. So a P controller settles at a slightly wrong place where the proportional output exactly cancels the disturbance. The bigger the gain, the smaller the offset — but bigger gains also cause overshoot and eventually oscillation. There is no value of Kp that has both zero offset and stable behavior on a real plant with disturbances. This is why P alone is rarely used in production.

I alone is the term most people misunderstand. The integrator’s whole job is to push harder the longer the error persists, accumulating the error over time. Used alone, it produces output even when the instantaneous error is small as long as the time-integral of error is non-zero, and it eventually drives the steady-state error to exactly zero. But pure integral control is slow — it has no instantaneous response to a sudden disturbance — and it has a phase lag that makes the loop almost always oscillatory on its own. I-alone control is rare; integral is almost always combined with proportional.

D alone does not work as a controller at all because differentiation of a constant error gives zero output. The derivative term has no DC response. Its job is purely to anticipate — to start backing off when the system is racing toward setpoint, before the proportional or integral terms would have noticed. D is always a partner, never a solo act.

The combinations everyone actually uses:

  • PI: proportional plus integral. The right default for most slow-ish plants without aggressive dynamics — temperature control, flow control, level control. P does the heavy lifting; I cleans up the residual offset. This is the controller for processes that change slowly and where overshoot does not matter much.

  • PD: proportional plus derivative. Used for fast plants where overshoot is the main concern and steady-state error is acceptable — motion control, servo positioning at scale. D dampens; P pushes.

  • PID: all three. The full controller for plants where you need fast response, zero steady-state error, and damped overshoot all at once. Most temperature control, all motion control, all flight control, all autopilots.

Which combination you need depends on the plant. There is no universal best controller; there is the right tool for the dynamics in front of you.


Tuning Without a Textbook

In an undergraduate controls course, you derive the plant transfer function, do root-locus analysis, and pick gains analytically. In real life you almost never know the plant transfer function, the plant is nonlinear and changes with operating point, and you tune the loop by hand on hardware. There are a couple of methods that actually work.

Ziegler-Nichols is the classic. Set the I and D gains to zero. Slowly increase the P gain until the loop just begins to oscillate at a constant amplitude (not growing, not shrinking) — call the gain at that point Ku (the “ultimate gain”) and the period of the oscillation Pu. The rule of thumb says set:

   Kp = 0.6 * Ku
   Ki = 1.2 * Ku / Pu
   Kd = 0.075 * Ku * Pu

This produces a reasonable starting point that is intentionally aggressive — it gives “quarter-amplitude damping,” meaning each overshoot is one-fourth the previous one, decaying quickly. You then back off as needed for the noise tolerance and overshoot budget of your application. Ziegler-Nichols works because it characterizes the plant at the edge of instability and infers the safe parameters from that one experiment. It is not the optimum tuning, but it gets you in the right zip code on plants you cannot model.

Manual loop shaping is the practitioner’s method. Start with all gains at zero. Slowly bring up Kp until the loop responds reasonably to a setpoint change but does not overshoot too aggressively. Add a small amount of Ki to eliminate the steady-state offset you can now see clearly. If the loop overshoots, increase Kd until the overshoot is acceptable. Iterate. This is the PID tuning loop you see in sous-vide controllers — slow plants, slow tuning, but it converges to a controller that holds setpoint cleanly.

Auto-tuning routines built into industrial controllers usually do a variant of Ziegler-Nichols or a “relay autotune” (apply a square-wave excitation and measure the response) and produce reasonable defaults. They are not as good as a careful hand-tune by someone who understands the plant, but they save days of work and are correct often enough.

Method Time to tune Quality Requires plant knowledge When to use
Ziegler-Nichols Minutes per loop Decent starting point None First-pass on a new plant
Manual loop shaping Hours per loop Excellent if patient Some intuition Production loops you care about
Auto-tune (built-in) Minutes Fair None Many loops, similar plants
Model-based design Days Best in theory Yes (transfer function) Aerospace, expensive systems
“Same as last time” Seconds Variable Lots Replacing identical hardware

The Failure Modes That Bite Everyone

Every practitioner discovers the same handful of pathologies. Knowing them up front saves a week of debugging on each one.

Integral windup. If the actuator saturates — the heater is at 100%, the motor is at full current — the loop cannot reduce the error any faster regardless of how badly the controller asks it to. The integral term keeps accumulating, the controller output keeps growing, and when the error finally crosses zero the integrator has accumulated a huge value that takes a long time to bleed off. The result is dramatic overshoot after the saturation clears. The fix is anti-windup: clamp the integrator’s accumulator to a sane range, stop integrating when the actuator is saturated (called “conditional integration”), or back-calculate the integrator from the actual actuator output. Every production PID implementation has some form of anti-windup. Hand-written ones often do not, and the bug shows up the first time the plant gets bumped hard enough to saturate.

Derivative kick. If you apply a step change to the setpoint, the error suddenly jumps, and the derivative of that jump is an impulse — a huge spike in the D term that briefly slams the actuator. The fix is to compute the derivative on the process variable, not on the error: the plant changes continuously even when the setpoint jumps, and using de_pv/dt instead of de/dt avoids the kick while preserving the derivative term’s damping role. Every modern PID controller does this. The textbook formulation that derivates the error is mostly pedagogical.

Derivative on noisy signals. The D term amplifies high-frequency content. If your sensor is noisy, pure derivative gain turns the noise into actuator chatter that wears out hardware and confuses the rest of the loop. The fix is a low-pass filter on the derivative term — usually a first-order filter with a time constant proportional to Kd. This trades a little phase lag for orders of magnitude less noise sensitivity. Production implementations always filter D.

Deadtime. PID assumes the actuator’s effect on the process variable shows up immediately (well, at the speed of the plant). Many real plants have deadtime — a delay between commanding the actuator and seeing any response, because the heated fluid has to travel down a pipe, or the chemical reaction has lag. PID handles deadtime poorly: if the delay is more than 20% of the dominant time constant, the loop becomes hard to stabilize. The fix is either to detune the loop until the delay is no longer dominant (slow but works), or to use a Smith predictor that explicitly models the deadtime and compensates for it. Pure PID is the wrong tool for deadtime-dominated plants.

Operating-point nonlinearity. A plant’s dynamics may change with the operating point — a valve is more responsive when partially open than when nearly closed, a motor has different inertia at different loads. A single set of PID gains that works well at one operating point may be aggressive or sluggish at another. The fix is gain scheduling: store multiple tuning sets and switch between them based on the current operating point. Industrial controllers ship with this; toy implementations rarely do.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Practical PID with anti-windup and derivative-on-PV

double pid_step(pid_t *pid, double sp, double pv, double dt) {
    double error = sp - pv;

    // Proportional
    double p = pid->Kp * error;

    // Integral, with anti-windup clamp on accumulator
    pid->integ += error * dt;
    if (pid->integ > pid->integ_max) pid->integ = pid->integ_max;
    if (pid->integ < pid->integ_min) pid->integ = pid->integ_min;
    double i = pid->Ki * pid->integ;

    // Derivative on PROCESS VARIABLE (avoid setpoint kick),
    // with first-order low-pass filter
    double dpv = (pv - pid->pv_prev) / dt;
    pid->dpv_filt = pid->alpha * dpv + (1.0 - pid->alpha) * pid->dpv_filt;
    double d = -pid->Kd * pid->dpv_filt;     // note minus sign
    pid->pv_prev = pv;

    double u = p + i + d;

    // Output saturation; back-calculate to limit integral runaway further
    if (u > pid->u_max) u = pid->u_max;
    if (u < pid->u_min) u = pid->u_min;

    return u;
}

That is roughly what a real PID looks like. The textbook math is in the equations; the survival skills are in the anti-windup, the derivative on PV, the filtered D, and the output saturation. Hand-rolled PIDs that omit these terms misbehave the first time a real disturbance happens.


Where PID Falls Short

PID is the right tool for a remarkable fraction of real plants, but it is not universal. The cases where it falls down are worth knowing because they shape what you reach for instead.

Multi-input, multi-output plants where actuators affect multiple process variables (a robot arm, an aircraft, a chemical reactor with intertwined temperature and pressure). PID is a single-input single-output controller. You can build cascades — outer loops driving setpoints into inner loops — and that handles many cases (the cascade structure used in espresso brewing temperature), but truly coupled MIMO systems need state-space controllers (LQR, MPC) that PID cannot match.

Plants with significant constraints. A motor has current and torque limits. An aircraft has angle-of-attack limits. PID has no internal awareness of constraints — it just sees the actuator saturate and tries to deal with it via anti-windup. Model Predictive Control (MPC) solves an explicit optimization at every step that respects constraints by construction; it is the right tool for plants where the actuator limits, state limits, or both are operational concerns rather than rare edge cases.

Plants with significant deadtime as covered above. The Smith predictor extends PID to handle this, but at that point you have a model-based controller, and you might as well use a model predictive controller.

Plants where the dynamics change dramatically. Robot arms whose effective inertia depends on the payload they are carrying, aircraft whose dynamics depend on speed and altitude — these need adaptive or scheduled control. PID gain scheduling handles modest variation; severe variation needs adaptive control.

Plants that need to react to disturbances they have not yet experienced. PID is reactive — it responds to error. If you know a disturbance is coming (the next train load on the power grid, a control input the flight computer is about to make), feedforward can pre-emptively counter it. Most production controllers combine feedforward with PID feedback — PID handles what you do not know, feedforward handles what you do.

The encouraging summary is that all of these are extensions or alternatives to PID, not replacements. PID is the floor on which more sophisticated control sits. Knowing it well makes the more advanced techniques legible. The reason every undergraduate controls class teaches PID first is that it really is the right starting point for almost everything in the field. The reason the same class then spends a semester on Laplace transforms, root locus, and state-space is that real plants beyond a certain complexity outgrow it.

You see the same control-loop pattern at scale in domains that do not call it PID — the feedback loop in active noise cancellation racing the wave equation, the resonance compensation in Klipper’s input shaping, the temperature-and-flow regulation in any thermal process. The math underneath is always the same family. Once you learn to read a plant’s step response, you can predict roughly what a controller on top of it will do, and the rest is tuning.


Verdict

PID is the controller that dominates a century of industrial practice because it is the simplest function of the error that addresses the three goals every loop must hit: get to setpoint fast (P), eliminate steady-state offset against persistent disturbances (I), and not overshoot (D). Each term maps onto a recognizable human-driving intuition — how big is the error, how long have we been off, where is it going — and once you internalize what each does, you can read the behavior of any real plant by watching its response and predict roughly how a PID on top will behave. The textbook version of the controller is a starting point; the version that runs in production carries anti-windup to keep the integrator sane through actuator saturation, derivative-on-process-variable to avoid setpoint kick, a low-pass filter on the derivative term to live with noisy sensors, output saturation that interacts cleanly with the windup logic, and often gain scheduling for plants whose dynamics shift with operating point. Tuning is mostly Ziegler-Nichols for a first pass, manual loop shaping for the loops you really care about, and auto-tune routines for the many that just need to be reasonable. The places PID falls short — MIMO plants, plants with hard constraints, plants with significant deadtime, plants whose dynamics change dramatically — are where MPC, state-space, and feedforward earn their seats. But for the enormous fraction of real-world control that is single-input, single-output, single-operating-point, modest-deadtime stuff — thermostats, motor servos, flow loops, autopilots, ovens, chillers, the temperature loops on every coffee machine and 3D printer in this house — PID is not a stepping stone to something better. It is the right tool, and learning to use it well is the single most leveraged skill in applied control.


Sources

Comments