Chapter 6: Line Following — Sensors to Algorithms

Line following is one of the oldest autonomous robot behaviours, and one of the most instructive. It demands fast sensing, smooth control, and graceful recovery from losing the line — all within a tight feedback loop. Despite its apparent simplicity, a well-tuned line follower requires careful attention to sensor calibration, centroid computation, and PID gain selection.

6.1 The IR Reflectance Sensor Array

The TCRT5000 is an infrared emitter-detector pair in a single package. It emits IR light downward; the detector measures how much is reflected back. A dark surface (black tape or black paint on white floor) reflects little IR; a light surface reflects a lot. The analogue output voltage is roughly proportional to reflectance.

The 5-element array used on this platform has sensors spaced 25 mm apart. With the array mounted 10 mm above the floor, the detection footprint of each element is approximately 10–15 mm in diameter. The 5-element array therefore covers a 100 mm span across the line.

Digital vs. analogue readings. Many TCRT5000 breakout boards provide both an analogue output (0–3.3 V, read via ADC) and a digital output (high/low based on a potentiometer threshold). Analogue readings allow interpolation between sensors; digital readings are faster to process but lose sub-element resolution. This chapter uses analogue values.

6.2 Calibration

Before computing a position, each sensor must be calibrated to map its raw ADC value to a normalised reflectance in $[0, 1]$:

def calibrate(sensors, n_samples=200):
    mins = [float('inf')] * len(sensors)
    maxs = [float('-inf')] * len(sensors)
    for _ in range(n_samples):
        values = sensors.read()
        for i, v in enumerate(values):
            mins[i] = min(mins[i], v)
            maxs[i] = max(maxs[i], v)
    return mins, maxs

Drive the robot slowly across and along the line during calibration so that each sensor sees both the line (minimum) and the floor (maximum). After calibration, readings are normalised: $r_i = (v_i - min_i) / (max_i - min_i)$

6.3 Line Position Estimation: The Weighted Centroid

Given 5 normalised reflectance values $r_0, \ldots, r_4$, the line position as a fraction of the array width is the weighted centroid of the inverted readings (dark = high weight):

\[p = \frac{\sum_{i=0}^{4} (1 - r_i) \cdot x_i}{\sum_{i=0}^{4} (1 - r_i)}\]

where $x_i$ is the physical position of sensor $i$ in millimetres (e.g., $x = {-50, -25, 0, 25, 50}$ mm for 5 sensors).

This gives a continuous position estimate in $[-50, +50]$ mm. The error fed to the PID controller is simply $p$ (when the line is centred under the array, $p = 0$).

Example: sensors read $[0.1, 0.2, 0.9, 0.3, 0.1]$ (line under sensor 2, slightly right). Inverted: $[0.9, 0.8, 0.1, 0.7, 0.9]$. Centroid: $(0.9 \times -50 + 0.8 \times -25 + 0.1 \times 0 + 0.7 \times 25 + 0.9 \times 50) / (0.9+0.8+0.1+0.7+0.9) = (-45 - 20 + 0 + 17.5 + 45) / 3.4 = -2.5/3.4 \approx -0.74$ mm — essentially centred, with a slight leftward bias.

6.4 PID Line Controller

The line-following controller uses PID on the centroid position error to generate a steering correction (angular velocity command):

pid = PIDController(Kp=0.03, Ki=0.0, Kd=0.008)
while True:
    position = read_line_position()   # mm, negative = line left
    correction = pid.update(position, setpoint=0.0, dt=dt)
    drive.move(linear_mps=0.25, angular_radps=correction)
    time.sleep(dt)

Typical gains for a robot travelling at 0.25 m/s on a 20 mm wide black line: $K_p = 0.03$ rad/s per mm, $K_d = 0.008$ rad/s·s/mm. Start with $K_i = 0$; add a small value only if the robot has a persistent straight-line drift.

6.5 Line-Loss Recovery

When all sensors read near zero (the line is absent — perhaps the robot has overshot a sharp corner), the robot must recover. A simple state machine:

  1. FOLLOWING: centroid is valid → PID control.
  2. LOST: no sensor sees the line → record last known direction of error; spin slowly in that direction.
  3. SEARCHING: if LOST for > 2 s → widen search: alternate left/right sweeps.

The last known error direction is crucial: if the line disappeared to the left, the robot should turn left to find it again.

6.6 Handling Intersections and Junctions

At a T or + intersection, multiple sensors are fully on the line. The centroid computation becomes unreliable (the denominator is very large, but the weighted sum may happen to be near zero). Detect intersections by thresholding the total activated sensor count: if four or more sensors are active, an intersection has been reached.

At an intersection, the robot can stop and apply a rule (e.g., always follow the straight-ahead path, or turn based on a programmed sequence) before resuming line following.

6.7 Integration with Odometry

Line following and odometry can work together. While following a line, the odometry module continues to accumulate pose estimates. When the robot reaches a known landmark on the line (e.g., a cross-tape marker), the odometry can be reset or corrected to the known position. This hybrid approach gives the accuracy of line following in structured areas with the flexibility of odometry in unstructured ones.


Navigation: