Module 1·55 min
PID Fundamentals
PID Control
PID controllers are used in the vast majority of real-world control systems, including joint controllers on robot arms and legs. Understanding them deeply — not just the formula — separates good engineers from cargo-cult tuners.
The Control Problem
You have a system with an output (position, velocity, temperature). You want to drive it to a setpoint (desired value). The error is the difference:
e(t) = r(t) - y(t)
Where r(t) is the setpoint and y(t) is the measured output.
PID Law
u(t) = Kp·e(t) + Ki·∫e(t)dt + Kd·(de/dt)
Each term has a distinct role:
| Term | What it does | Analogy |
|---|---|---|
| Kp × e | Pushes toward setpoint proportionally to error | Springs — stiffer with higher Kp |
| Ki × ∫e | Eliminates steady-state error | Memory — accumulates past mistakes |
| Kd × ė | Damps oscillation | Damper — resists fast changes |
Discrete Implementation
Controllers run on digital hardware at a fixed loop rate. The discrete form:
class PIDController:
def __init__(self, kp: float, ki: float, kd: float, dt: float):
self.kp = kp
self.ki = ki
self.kd = kd
self.dt = dt
self._integral = 0.0
self._prev_error = 0.0
def update(self, setpoint: float, measurement: float) -> float:
error = setpoint - measurement
# Proportional
p = self.kp * error
# Integral (with anti-windup clamp)
self._integral += error * self.dt
self._integral = max(-50.0, min(50.0, self._integral))
i = self.ki * self._integral
# Derivative (on measurement, not error — avoids derivative kick)
d = self.kd * (measurement - self._prev_error) / self.dt
self._prev_error = measurement
return p + i - d # Note: -d because we differentiate measurement
# Usage at 1 kHz loop rate
controller = PIDController(kp=10.0, ki=0.1, kd=0.05, dt=0.001)
command = controller.update(setpoint=1.0, measurement=current_pos)
Tuning Heuristic (Ziegler-Nichols Simplified)
- Set Ki = Kd = 0
- Increase Kp until the system oscillates at the stability limit
- Record this critical gain Ku and oscillation period Tu
- Set: Kp = 0.6·Ku, Ki = 2·Kp/Tu, Kd = Kp·Tu/8
This is a starting point — most real systems need empirical refinement from here.