How line-following robots work
Light sensors, thresholds, and the step from wobbly on/off steering to smooth proportional control, with a diagram and sample code.
Following a black line on a white floor is the classic first real robotics task. It looks simple, yet it touches almost everything that makes robotics interesting: sensors that give noisy readings, motors that never behave quite the same, and code that has to make a decision many times a second. It is also the basis of "rescue" style challenges, where robots follow a line through a course with gaps, turns and obstacles.
What the sensor actually sees
Most line followers use a reflected-light sensor: a small light source pointing at the floor, and a detector that measures how much light bounces back. White surfaces reflect a lot of light; black tape reflects very little. The sensor turns that into a number.
The range of that number depends on the hardware. An Arduino's analog input reads values from 0 to 1023. The micro:bit's built-in light level reading in MakeCode runs from 0 to 255, though for line following you would normally use a dedicated reflectance sensor on a buggy rather than the LED display.
The first job, before writing any steering code, is to print the readings over white, over black, and over the edge between them. Students are usually surprised how much the numbers change with the height of the sensor, the classroom lighting and the type of tape.
Step one: the threshold
Take the average of your white and black readings. That is the threshold. If white reads around 800 and black around 200, the threshold is about 500. Anything above it is "mostly white", anything below is "mostly black".
With one sensor, the robot follows the edge of the line. If it sees white, it steers toward the line; if it sees black, it steers away. The result is a zig-zag that wobbles along the edge.
// on/off edge following, one sensor (Arduino-style)
int reading = analogRead(A0);
if (reading > THRESHOLD) {
drive(60, 20); // on white: curve right, toward the line
} else {
drive(20, 60); // on black: curve left, back to the edge
}
This works, and it is a great first success. But it is slow, and on sharp corners it often loses the line completely.
Step two: proportional steering
The improvement is to steer more when the robot is far from the edge and less when it is close. Instead of a yes/no decision, you calculate an error: how far the reading is from the threshold.
int error = analogRead(A0) - THRESHOLD;
int turn = error * KP; // KP is a small number you tune
drive(BASE + turn, BASE - turn);
This is proportional control, the "P" in the PID controllers used throughout engineering. The robot now glides along the edge with much less wobble. The tuning is done by experiment:
- Start with a slow base speed and a small KP. The robot will drift and may lose the line on corners.
- Increase KP gradually until the robot follows well but starts to oscillate.
- Back KP off a little from the point where oscillation starts.
- Only then raise the base speed, and expect to retune.
Older students can add the "D" (derivative) term, which reacts to how quickly the error is changing and damps the oscillation. The "I" (integral) term is rarely needed for school line followers.
Two sensors, three sensors, more
Two sensors, one either side of the line, let the robot follow the middle: if the left sensor sees black, turn left; if the right one does, turn right. Arrays of five or more sensors let the robot estimate exactly where the line sits under it, which makes sharp turns and junctions much easier to handle.
Common problems
- It worked yesterday. Lighting changed, or the battery is lower. Recalibrate at the start of every session.
- It spins on the spot. The motors may be wired in opposite directions, or the turn value is far too big.
- It overshoots corners. It is going too fast for how often the code checks the sensor. Slow down, or remove delays from the loop.
Line following is worth doing well because the same ideas (read, compare, correct, repeat) come back in almost every robot that moves.
Gaps, junctions and obstacles
Rescue-style courses add complications on purpose. A gap in the line means the robot briefly sees only white; a good approach is to keep driving straight for a short distance before deciding the line is really lost. Junctions, where lines cross or split, are easier with two or more sensors, because the robot can notice when both sides see black at once. Obstacles on the line usually need a distance sensor and a short pre-planned detour that ends with the robot searching for the line again.
Each of these is best tackled separately. Build a small test track for each problem, get the behaviour working there, and only then combine everything on a full course.
Sources: MakeCode light level reference; Arduino analog input guide.