Control is how a robot makes reality match intention — how it holds a speed, a heading, or a position despite friction, slope, and imperfect motors. The workhorse idea is feedback: measure the error between what you want and what you have, and act to shrink it. This lesson builds the PID controller from that single idea, one term at a time, and shows you how to tune it without the maths becoming a wall. Once you understand PID you can make a robot drive straight, hold a distance, or balance.
- Explain the difference between open-loop and closed-loop control
- Describe what the P, I and D terms each contribute
- Implement a basic PID controller in code
- Tune a PID controller and recognise overshoot and oscillation
Open loop vs closed loop
There are two ways to make a robot do something. Open loop means you send a command and hope: 'run the motor at 70% and it should go about the right speed.' It is simple but blind — if the robot hits a slope or the battery sags, it slows and nothing corrects it. Closed loop means you *measure the result* and adjust: read the actual speed, compare it to the target, and change the motor command to close the gap. The measured difference between what you want (the setpoint) and what you have (the measurement) is the error, and control is the art of driving that error to zero. Almost everything a robot does well — holding a speed, a distance, a heading — is closed-loop.
The core loop of all feedback control: error = setpoint − measurement; then choose an action that reduces the error; then measure again. PID is just a good, general recipe for choosing that action.
P: react to the error now
The proportional term is the obvious one: push in proportion to how big the error is. Far from target, push hard; close to target, push gently. In code the correction is simply a gain Kp times the error. Proportional control alone gets you surprisingly far — a line follower that steers harder the further off the line it is works on P alone. But P has two weaknesses. If Kp is too small the robot responds sluggishly; too large and it overshoots and oscillates around the target. And P alone often settles just short of the target, leaving a small permanent steady-state error, because as the error shrinks so does the push, until it balances against friction before reaching zero.
I and D: kill the offset, damp the wobble
The other two terms fix P's weaknesses. The integral term accumulates the error over time, so a small error that persists keeps adding up until the controller pushes enough to eliminate it — this is what removes the steady-state offset that P leaves behind. Its danger is *windup*: if the error stays large for a while (say the robot is blocked), the integral grows huge and overshoots wildly once freed, so real controllers cap it. The derivative term looks at how fast the error is changing and pushes against rapid change, acting like a shock absorber that damps overshoot and oscillation. It is sensitive to noise, so it is often used gently. Put together — P reacts to the present error, I to the accumulated past, D to the predicted future — and you have PID, the most widely used controller in all of engineering.
class PID:
def __init__(self, kp, ki, kd):
self.kp, self.ki, self.kd = kp, ki, kd
self.integral = 0.0
self.prev_error = 0.0
def update(self, setpoint, measurement, dt):
error = setpoint - measurement
self.integral += error * dt
self.integral = max(-100, min(100, self.integral)) # anti-windup clamp
derivative = (error - self.prev_error) / dt
self.prev_error = error
return self.kp * error + self.ki * self.integral + self.kd * derivative
# hold a target speed of 1.0
pid = PID(kp=2.0, ki=0.5, kd=0.1)
command = pid.update(setpoint=1.0, measurement=0.7, dt=0.02)Tuning without the maths
You rarely calculate PID gains; you tune them, and there is a reliable recipe. Start with I and D at zero. Raise Kp until the robot responds briskly and just begins to overshoot and oscillate a little — that is your ceiling for P. Then add a little Kd to damp that overshoot so the response settles cleanly instead of ringing. Finally add a small Ki to erase any remaining steady-state offset — the robot settling just short of target. Change one gain at a time and watch the behaviour: sluggish means more P; overshoot and wobble means more D (or less P); never quite reaching the target means more I. This 'watch and adjust one thing' loop is how PID is tuned in practice, and it works without any equations.
- Too slow to respond → increase Kp.
- Overshoots and oscillates → increase Kd, or reduce Kp.
- Settles just short of the target → increase Ki.
- Oscillates slowly and grows → too much Ki (windup); reduce it and clamp.
Where you will use it
PID shows up everywhere on a robot. Wheel speed: hold each wheel at a target speed using its encoder, so the robot drives straight regardless of slope or battery — the closed-loop fix for the 'curves when going straight' problem from the motors lesson. Heading: use the IMU or the wheel difference to hold or reach a target heading. Line following: treat the line-sensor offset as the error and steer to zero it. Distance holding: keep a set distance from a wall using the ultrasonic reading. The same small controller, pointed at different errors, produces all of these behaviours — which is why learning PID once pays off across every robot you build.