Behavior:Line Following
| Line Following | |
|---|---|
| Type | Behavior (Algorithm) |
| Requires Capabilities | Capability:Line Sensing, Capability:Differential Drive |
| Enables Activities | Activity:Line Following |
| Difficulty | Beginner |
| Implementations | SimpleBot:Line Following Implementation |
| Status | Fully Documented |
Line Following is a fundamental robotic behavior that enables a robot to autonomously track and follow a visible line on the ground. This behavior interprets data from line sensors (typically infrared reflectance sensors) to continuously adjust the robot's steering and maintain alignment with the line path.
Overview
Line-following behavior is one of the most common introductory robotics tasks, yet it scales from simple bang-bang control to sophisticated PID-based systems. The core principle remains constant: detect position relative to the line, and adjust motor speeds to correct any deviation.
The behavior operates in a continuous sense-think-act loop:
- Sense: Read reflectance values from line sensors
- Think: Determine position relative to the line (on line, left of line, right of line, or lost)
- Act: Adjust motor speeds to steer back toward the line
Line-following is typically used with high-contrast lines (black tape on white surface or vice versa), though the algorithms can be adapted for other visual markers.
Algorithm Variants by Sensor Count
The complexity and performance of line-following behavior depends heavily on the number of sensors used. Each configuration has distinct trade-offs.
One-Sensor Algorithm
The simplest line-following approach uses a single sensor positioned at the robot's front center.
Algorithm:
loop forever:
if sensor detects line:
drive forward
else:
rotate in place (searching)
wait until line is found again
Pseudocode:
while true:
if read_sensor() == LINE_DETECTED:
set_motors(FORWARD_SPEED, FORWARD_SPEED)
else:
// Lost the line - search by rotating
set_motors(TURN_SPEED, -TURN_SPEED) // Spin in place
Pros:
- Simplest hardware and code
- Minimal cost
- Good for teaching basic concepts
Cons:
- Jerky, inefficient motion (constant stop-and-turn)
- Cannot anticipate curves
- Very slow on anything but straight lines
- No way to determine which direction to correct
Two-Sensor Algorithm
Using two sensors positioned on either side of the line enables the robot to detect which direction it's drifting and make appropriate corrections. This is the approach used by SimpleBot.
Algorithm:
loop forever:
read left_sensor and right_sensor
if both sensors on white (off line):
drive straight - robot is centered on line
else if left_sensor on line (black):
turn right - robot is drifting left
else if right_sensor on line (black):
turn left - robot is drifting right
else if both sensors on line (black):
intersection or completely lost
execute special handling
Pseudocode:
const BASE_SPEED = 150
const TURN_SPEED = 100
while true:
left = read_left_sensor()
right = read_right_sensor()
if left == WHITE and right == WHITE:
// Centered on line - drive straight
set_motors(BASE_SPEED, BASE_SPEED)
else if left == BLACK and right == WHITE:
// Drifting left - turn right
set_motors(BASE_SPEED, TURN_SPEED)
else if left == WHITE and right == BLACK:
// Drifting right - turn left
set_motors(TURN_SPEED, BASE_SPEED)
else if left == BLACK and right == BLACK:
// Both on line - intersection or lost
// Option 1: Stop and signal
set_motors(0, 0)
// Option 2: Drive straight through
set_motors(BASE_SPEED, BASE_SPEED)
// Option 3: Execute turn decision
make_intersection_decision()
Pros:
- Simple to understand and implement
- Directional correction (knows which way to turn)
- Smooth following on gentle curves
- Can detect intersections
- Good cost-to-performance ratio
Cons:
- Fixed correction amount (not proportional)
- Struggles with sharp curves
- Sensor spacing critical to performance
- Line width must match sensor spacing
Three-Sensor Algorithm
Three sensors (left, center, right) provide better tracking by distinguishing between centered, slightly off, and significantly off positions.
Algorithm:
loop forever:
read left_sensor, center_sensor, right_sensor
if center_sensor on line:
drive fast and straight - perfectly centered
else if left_sensor on line:
gentle turn right
else if right_sensor on line:
gentle turn left
else if no sensors on line:
execute lost-line recovery
Pseudocode:
const FAST_SPEED = 200
const NORMAL_SPEED = 150
const SLOW_SPEED = 100
while true:
left = read_left_sensor()
center = read_center_sensor()
right = read_right_sensor()
if center == BLACK:
// Perfectly centered - drive fast
set_motors(FAST_SPEED, FAST_SPEED)
else if left == BLACK:
// Slightly left - gentle right turn
set_motors(NORMAL_SPEED, SLOW_SPEED)
else if right == BLACK:
// Slightly right - gentle left turn
set_motors(SLOW_SPEED, NORMAL_SPEED)
else:
// Lost line - slow search
set_motors(SLOW_SPEED, -SLOW_SPEED)
Pros:
- Better detection of centered position
- Can optimize speed (drive faster when centered)
- More graceful corrections
- Better lost-line detection
Cons:
- More hardware complexity
- Still uses discrete corrections
- Cannot handle very sharp curves without slowing significantly
Multi-Sensor Array (5-8 Sensors)
High-performance line following uses an array of sensors to calculate a continuous "line position" value, enabling proportional control.
Algorithm:
loop forever:
read all sensors (s0, s1, s2, s3, s4, s5, s6, s7)
calculate line_position using weighted average:
line_position = sum(sensor_value[i] * position_weight[i]) / sum(sensor_value[i])
calculate error = line_position - desired_position (0 = center)
apply PID control:
correction = Kp * error + Ki * integral_error + Kd * derivative_error
adjust motors:
left_motor = BASE_SPEED + correction
right_motor = BASE_SPEED - correction
Pseudocode:
const BASE_SPEED = 180
const Kp = 0.8 // Proportional gain
const Ki = 0.01 // Integral gain
const Kd = 2.0 // Derivative gain
integral_error = 0
last_error = 0
while true:
// Read sensor array (0 = white, 1000 = black)
sensors = [read_sensor(0), read_sensor(1), ..., read_sensor(7)]
// Calculate weighted line position (-3.5 to +3.5 for 8 sensors)
weighted_sum = 0
total_activation = 0
for i in 0 to 7:
weight = i - 3.5 // Position weights: -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5
weighted_sum += sensors[i] * weight
total_activation += sensors[i]
if total_activation > 0:
line_position = weighted_sum / total_activation
else:
// Lost line - use last known position
line_position = last_line_position
// PID control
error = line_position // Target is 0 (center)
integral_error += error
derivative_error = error - last_error
correction = Kp * error + Ki * integral_error + Kd * derivative_error
// Apply correction
left_speed = BASE_SPEED + correction
right_speed = BASE_SPEED - correction
set_motors(left_speed, right_speed)
last_error = error
last_line_position = line_position
Pros:
- Smooth, precise tracking
- Proportional corrections (gentle curves vs. sharp turns)
- High speed capability
- Excellent on complex paths
- Can implement look-ahead strategies
Cons:
- Significant hardware cost
- Complex algorithm and tuning
- Requires PID understanding
- Overkill for simple tasks
Tuning Parameters
Successful line-following requires careful tuning of several parameters:
Base Speed
The forward speed when driving straight or making corrections.
- Too slow: Robot is stable but inefficient; wastes time
- Too fast: Robot overshoots corrections and oscillates
- Optimal: As fast as possible while maintaining stable tracking
Start with low speeds (50-100 motor units) and gradually increase until oscillation appears, then reduce by 20%.
Turn Aggression
How sharply the robot corrects when off-line.
- Too gentle: Robot drifts off line before correction takes effect
- Too aggressive: Robot oscillates or zigzags
- Optimal: Smooth sinusoidal path that stays on line
For differential corrections, the turn speed should be 50-70% of base speed.
Sensor Spacing
Physical distance between sensors affects behavior.
- Narrow spacing: More sensitive to small deviations; better on gentle curves; struggles on sharp turns
- Wide spacing: Less sensitive; handles sharp turns; may miss gentle curves
- Optimal: Spacing slightly wider than line width
For a 20mm wide line, sensors should be 25-30mm apart.
Sensor Threshold
The reflectance value that distinguishes line from background.
- Calibrate in actual operating environment
- Use midpoint between average white and average black readings
- Implement hysteresis to prevent flickering at boundary
// Calibration procedure white_value = read_sensor_on_white_surface() black_value = read_sensor_on_black_line() threshold = (white_value + black_value) / 2
Common Challenges and Solutions
Lost Line
Problem: Robot loses the line completely (all sensors read white or all read black).
Solutions:
- Memory-based recovery: Remember last known direction and turn that way
if line_lost():
if last_correction == TURNING_LEFT:
set_motors(-SEARCH_SPEED, SEARCH_SPEED) // Continue left
else:
set_motors(SEARCH_SPEED, -SEARCH_SPEED) // Continue right
- Spiral search: Gradually expand search radius
- Timeout and stop: If line not found within N seconds, halt and signal error
Sharp Turns
Problem: Robot cannot turn sharply enough to stay on line.
Solutions:
- Detect sharp turn: Multiple consecutive corrections in same direction
- Reduce speed: Slow down when sharp turn detected
consecutive_turns_right += 1
if consecutive_turns_right > 5:
base_speed = SLOW_SPEED // Sharp curve detected
- Aggressive correction: Increase turn speed or even stop one wheel
Intersections
Problem: Multiple possible paths (T-junction, cross, etc.).
Solutions:
- Detection: Both sensors (or multiple sensors) detect line simultaneously
- Pre-programmed decisions: Follow sequence of turns (left, right, straight, right...)
intersection_sequence = [LEFT, STRAIGHT, RIGHT, LEFT]
current_intersection = 0
if detect_intersection():
execute_turn(intersection_sequence[current_intersection])
current_intersection += 1
- Marker-based navigation: Use additional sensors to detect colored markers
- Default behavior: Always go straight through intersections
Line Width Variations
Problem: Line width changes along path.
Solutions:
- Calibration: Test on actual track
- Adaptive thresholds: Continuously recalibrate based on recent readings
- Edge detection: Track line edge rather than center (more consistent)
Advanced Techniques
PID Control
Proportional-Integral-Derivative control provides smooth, optimized tracking:
- Proportional (P): Correction proportional to current error (distance from line)
- Integral (I): Corrects for persistent bias (e.g., one motor slightly faster)
- Derivative (D): Dampens oscillation by responding to rate of change
Start with P-only control (set I and D to zero), then add D to reduce oscillation, and finally add small I if steady-state error exists.
Look-Ahead
With sensor arrays, use forward sensors to anticipate curves:
if sensors[0] or sensors[1] active:
// Sharp left turn coming
reduce_speed()
increase_left_correction()
Sophisticated intersection handling:
- Count intersections to determine position on known map
- Use timing to ensure robot fully enters intersection before turning
- Implement "intersection memory" to avoid counting same intersection twice
Path Memory
Record successful paths and replay:
// Record mode
path = []
while running:
sensor_state = read_sensors()
motor_command = calculate_correction(sensor_state)
path.append(motor_command)
execute(motor_command)
// Replay mode
for command in path:
execute(command)
delay(LOOP_TIME)
SimpleBot Implementation
SimpleBot uses the two-sensor algorithm with basic bang-bang control. The sensors are positioned 25mm apart, straddling a 20mm wide black line on a white surface.
The implementation uses:
- Reflectance sensors with analog output
- Threshold-based line detection
- Four-state control logic (straight, left, right, lost)
- Simple lost-line recovery (continue last turn direction)
For complete implementation details including circuit diagrams, calibration procedures, and code, see SimpleBot:Line Following Implementation.
See Also
- Capability:Line Sensing - Hardware and sensor technology
- Capability:Differential Drive - Motor control fundamentals
- Activity:Line Following - Using line following for navigation tasks
- SimpleBot:Line Following Implementation - Complete working example
- Behavior:PID Control - Advanced smooth control technique