Skip to main content
onepocketRobotics

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.

A sensor array reads where the line is, and the difference drives a steering correction.

What you need

  1. A differential-drive base

    Two driven wheels and a castor. Motor driver, battery, microcontroller.

  2. An IR reflectance array

    Five to eight sensors in a row, mounted 3–8 mm above the floor, across the direction of travel.

  3. A line

    Black electrical tape on a light floor. About 19 mm wide, comfortably wider than the sensor spacing.

  4. 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.

line_follower.py
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_error

The 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. 1 · Calibrate

    Sweep the array over the line and back. Nothing below is meaningful without this.

  2. 2 · Set a slow base speed

    Half of what you eventually want. Fast tuning hides problems until the robot is off the table.

  3. 3 · Raise KP until it weaves

    Then halve it. If it cannot follow a gentle curve at all, KP is too low.

  4. 4 · Raise KD until the weaving stops

    Too much and the steering becomes twitchy and noise-sensitive.

  5. 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.

The cases that break naive line followers
CaseWhat goes wrongFix
Sharp cornerThe line leaves the array entirely between samplesDetect zero total reflectance; turn hard in the direction of the last known error
Gap in the lineSame symptom, different causeDrive straight for a short timeout before deciding it is lost
Crossing / T junctionEvery sensor reads the line; the weighted average says centreDetect an all-on pattern and apply a rule: go straight, or turn by policy
Ambient light changeCalibration no longer matchesUse 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.

Project: A Line-Following Robot | OnepocketRobotics