Projects & Applications
Project: A Line-Following Robot
The classic first robot, built properly: an IR sensor array, a PD controller, calibration, and the failure cases that separate a demo from a robot that works.
beginner · 14 min read
Lesson content
A line follower is the smallest robot that is genuinely a robot: it senses, it decides, and it acts, in a loop, and it fails in instructive ways. Every idea in the fundamentals path shows up in about fifty lines of code.
What you need
A differential-drive base
Two driven wheels and a castor. Motor driver, battery, microcontroller.
An IR reflectance array
Five to eight sensors in a row, mounted 3–8 mm above the floor, across the direction of travel.
A line
Black electrical tape on a light floor. About 19 mm wide, comfortably wider than the sensor spacing.
A control loop
Read, compute the error, set two wheel speeds, repeat at 50–100 Hz.
The first real decision is how to turn several sensor readings into one number. A single sensor gives you "on the line or off it", which is enough for a jerky bang-bang follower. An array gives you how far off, which is what makes smooth control possible.
The math
Weight each sensor by its position, weight that by how much line it sees, and take the average. The result is where the line's centre is, in sensor units.
position = Σ(wᵢ · sᵢ) ÷ Σ(sᵢ)
error = position − centre
- sᵢ
- calibrated reading of sensor i, 0 for bare floor and 1000 for line
- wᵢ
- that sensor's position, e.g. 0, 1000, 2000, 3000, 4000 for five sensors
- centre
- the middle position, which is 2000 for five sensors
With real numbers
Readings (0, 200, 900, 700, 0) across five sensors:
numerator = 0·0 + 1000·200 + 2000·900 + 3000·700 + 4000·0 = 4 100 000
denominator = 0 + 200 + 900 + 700 + 0 = 1800
position = 2278, so error = +278. The line sits a little right of centre.
Notice this gives a continuous value even though the sensors are discrete: with the line between two sensors, both read partially and the weighted average lands between them. That interpolation is what makes smooth steering possible.
BASE_SPEED = 120 # out of 255KP, KD = 0.35, 4.0last_error = 0def loop(): global last_error s = read_calibrated() # five values, 0..1000 total = sum(s) if total < 200: # line lost, see below recover(last_error) return position = sum(i * 1000 * v for i, v in enumerate(s)) / total error = position - 2000 derivative = error - last_error correction = KP * error + KD * derivative last_error = error left = clamp(BASE_SPEED + correction, -255, 255) right = clamp(BASE_SPEED - correction, -255, 255) set_motors(left, right)if total < 200: # line lost, see below
recover(last_error)Detect the lost line by total reflectance, not by any single sensor. A sharp corner will lose it, and this is the branch that saves the run.
derivative = error - last_errorThe derivative is the change in error per cycle. It works because the loop period is fixed, which is why it must be.
left = clamp(BASE_SPEED + correction, -255, 255)Add to one wheel and subtract from the other. That is the whole of differential steering.
Why PD, and not full PID?
Because there is no steady-state offset to remove.
When the robot is centred on a straight line, the error is genuinely zero. So P alone reaches the target. The problem that P has on a gravity-loaded arm simply does not exist here.
What a line follower does suffer from is oscillation. Proportional control overshoots, the robot weaves across the line, and at speed it weaves itself right off.
That is exactly what D fixes, pushing back as the error collapses.
Adding I usually makes things worse. On a long curve the integral builds up, and when the curve ends the robot is still turning.
PD, almost always.
Calibrate, or nothing else matters
Raw infrared readings vary enormously from one sensor to the next, with battery voltage, with the light in the room, and with how high the array sits above the floor.
An uncalibrated array will report a line where there is none, or miss one entirely under a bright window.
The standard routine takes twenty seconds. Sweep the array across the line and back. Record the minimum and maximum each sensor saw. Then map every future reading onto 0–1000 between those two numbers.
Run it at the start of every session.
And when the robot behaves differently in a different room, this is why.
Tuning, in order
1 · Calibrate
Sweep the array over the line and back. Nothing below is meaningful without this.
2 · Set a slow base speed
Half of what you eventually want. Fast tuning hides problems until the robot is off the table.
3 · Raise KP until it weaves
Then halve it. If it cannot follow a gentle curve at all, KP is too low.
4 · Raise KD until the weaving stops
Too much and the steering becomes twitchy and noise-sensitive.
5 · Raise the base speed
Re-check on the tightest corner. Speed and gains interact, so going faster usually needs more KD.
A demo that works on a gentle oval and a robot that completes a real course differ entirely in how they handle the awkward cases. Four are worth planning for.
| Case | What goes wrong | Fix |
|---|---|---|
| Sharp corner | The line leaves the array entirely between samples | Detect zero total reflectance; turn hard in the direction of the last known error |
| Gap in the line | Same symptom, different cause | Drive straight for a short timeout before deciding it is lost |
| Crossing / T junction | Every sensor reads the line; the weighted average says centre | Detect an all-on pattern and apply a rule: go straight, or turn by policy |
| Ambient light change | Calibration no longer matches | Use the sensor's own LED and read with it on and off, subtracting the difference |
Example
Speed is not the same as speed through corners
The classic competition mistake: raise the base speed until the robot flies down the straights, then watch it fly off every corner.
The better approach is speed scheduling. Reduce the base speed in proportion to how large the error is.
Now the robot slows down exactly when the line is curving away, and speeds up when it is nicely centred.
One extra line does it:speed = BASE * (1 - min(abs(error)/2000, 0.6))
That single line is often worth more than any amount of gain tuning. And it is the same thing you do when driving into a bend.
Mounting the sensor array too high.
Reflectance falls off quickly with distance. 3–8 mm is the useful range; at 20 mm the contrast between tape and floor nearly vanishes.
Using a variable loop period.
The derivative divides by time. If the loop runs at 100 Hz sometimes and 40 Hz others, KD is effectively random. Fix the period.
Tuning with a low battery.
Motor speed depends on voltage, so gains tuned at 7.4 V are wrong at 6.8 V. Tune on a fresh charge and check again when low.
Key takeaway
- A weighted average across a sensor array turns discrete sensors into a continuous line position.
- PD, not PID, because there is no steady-state offset and integral action hurts on curves.
- Calibrate every session; uncalibrated readings make every other parameter meaningless.
- Tune KP until it weaves, then KD until it stops, then raise the speed and re-check.
- Handle the lost line, gaps and junctions explicitly, because that is what separates a demo from a working robot.
Finished this lesson?
Guest progress is saved on this device.