PID control: present, past and future error
P responds to the present error, I to the accumulated error and D to the rate at which the error changes. The effect of each term and the tuning order.
u = Kp·e + Ki·∫e dt + Kd·de/dt
Role of each term
| Term | Responds to | Raising it |
|---|---|---|
| P | The error right now | Faster, but more overshoot and oscillation. Steady-state error remains |
| I | Accumulated error | Removes steady-state error; too much gives slow swinging |
| D | Rate of change of error | Suppresses overshoot (a brake); amplifies noise |
Code
float pid_step(pid_t *p, float target, float meas, float dt) {
float e = target - meas;
p->integ += p->ki * e * dt;
p->integ = clamp(p->integ, -p->imax, p->imax); // anti-windup
float deriv = -(meas - p->prev_meas) / dt; // derivative of the measurement (no kick when the target jumps)
p->prev_meas = meas;
return clamp(p->kp * e + p->integ + p->kd * deriv, -p->umax, p->umax);
}
Tuning order (the field method)
- With I and D at 0, raise P until the response is fast but just short of oscillating
- Lower P a little and raise I to remove the steady-state error
- If the overshoot is large, add a little D (with a low-pass filter in front of D)
- Apply a disturbance (push by hand, add weight) and check the recovery
Try changing the gains in the lab's PID 3D simulation and see the effect of each term for yourself.
In a picture: response for different gains
What the three terms do
| Term | Looks at | Raise it and | Overdo it and |
|---|---|---|---|
| P | The error right now | The response gets faster | Ringing, divergence |
| I | Accumulated error | The remaining error disappears | Overshoot, slow swinging |
| D | How fast the error is changing | Ringing decreases | It shakes on noise |
With P alone, a little error remains near the target. When the error is 0 the output is 0 too, but output is needed to overcome the load. I removes this remaining error over time.
3D labRun what you have read so far→Code as actually used
typedef struct { float kp, ki, kd, integ, prev_meas, out_min, out_max; } pid_t;
float pid_update(pid_t *p, float target, float meas, float dt) {
float err = target - meas;
float d = -(meas - p->prev_meas) / dt; // differentiate the measurement (no kick on target jumps)
p->prev_meas = meas;
float out = p->kp * err + p->integ + p->kd * d;
if (out > p->out_max) out = p->out_max; // output limit
else if (out < p->out_min) out = p->out_min;
else p->integ += p->ki * err * dt; // integrate only when not saturated
return out;
}
There are two differences from the textbook equation.
- Integral windup prevention: if the integral keeps accumulating while the output is stuck at its limit, then even after the error reverses the output is pushed to one side for a long time until the accumulated value unwinds. Stop integrating while saturated.
- Derivative on measurement: differentiating the error makes the derivative term spike the moment the target changes suddenly. Differentiating only the measurement avoids this kick.
Order for tuning by hand
- With I and D at 0, raise P. When it starts to ring, bring it down to about half.
- Raise I a little at a time until the remaining error disappears within a reasonable time.
- If the overshoot bothers you, add a little D. If it shakes, reduce it or add a low-pass filter to the derivative.
It is quicker to change one thing at a time, in big steps of double or half, and find the trend first.
The period must be constant
If dt jitters, the response changes even with the same gains. Run the control loop from a timer interrupt at a fixed period, not from the main loop. Choose a period at least 10 times faster than the system responds. Tens of μs for a current loop, 1 ms for a speed loop and about 100 ms for temperature control are common values.
Problems that come up in practice
- The sign is connected the wrong way round: positive feedback that pushes harder the larger the error gets
- Sensor noise amplified by the D term, making the motor audible
- Differentiating a sensor value that updates more slowly than the control period
- Mixed units changing what the gains mean (rad and deg, seconds and ms)