Behavior:Line Following

From Bespoke Robot Society
Jump to navigation Jump to search
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:

  1. Sense: Read reflectance values from line sensors
  2. Think: Determine position relative to the line (on line, left of line, right of line, or lost)
  3. 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()

Intersection Navigation

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