Skip to content

Line tracking hands on

Goal:

Make the robot follow a black line by adjusting its wheel speeds based on the position of the line under 4 optocoupler sensors using closed-loop PID controller.

Assumptions and Setup

  • Sensors: 4 digital or analog reflectance sensors mounted left to right:

    • S0 (far left), S1 (left-center), S2 (right-center), S3 (far right)
  • PWM outputs: pwm_left, pwm_right to control wheel speeds

  • Desired state: Robot is centered on the line → small or zero error

  • Goal: Adjust motor speeds based on sensor input to correct the direction

Step-by-Step Explanation

1. Sensor Inputs

sensors = [S0, S1, S2, S3]  # values from your reflectance sensors

Each sensor returns a value:

  • If digital, it’s 0 (line detected) or 1 (no line).

You can use either — same principle applies.

2. Weights for Sensor Positions

weights = [-3, -1, 1, 3]

Each sensor has a "position score":

  • Far left = -3
  • Left-center = -1
  • Right-center = +1
  • Far right = +3

This gives a numeric way to calculate where the center of the detected line is.

3. Weighted Average to Get Line Position

numerator = 0
denominator = 0
for i in range(4):
    numerator += sensors[i] * weights[i]
    denominator += sensors[i]

if denominator > 0:
    position = numerator / denominator
else:
    position = 0  # Fallback if no line is detected

This gives you the center of mass of the line reading.

  • Example: if only the middle sensors detect the line:
    sensors = [0, 1, 1, 0] → position = (1×(-1) + 1×(1)) / 2 = 0

  • If the robot is off to the left:
    sensors = [1, 1, 0, 0] → position = (-3 + -1)/2 = -2


4. Calculate Error

error = 0 - position

You're trying to stay centered, so the desired position is 0.
The error tells you how far off-center the line is, and in which direction.

Looping It in Real Time

Here's how this looks in a real control loop (say, using PID):

Step 1: Define a Line Position “Error”

You need a way to convert 4 sensor readings into a position error.

Option A: Weighted Sum Method (for analog or binary sensors)

Assign positions like this:

# For example:
# Sensor positions: [-3, -1, +1, +3]
# Reflects sensor intensity or digital ON/OFF
weights = [-3, -1, 1, 3]
sensors = [S0, S1, S2, S3]

numerator = 0
denominator = 0
for i in range(4):
    numerator += sensors[i] * weights[i]
    denominator += sensors[i]

if denominator > 0:
    position = numerator / denominator  # e.g., -1.5 (left of center)
else:
    position = 0  # Line lost

error = 0 - position  # Desired position is 0 (centered)

Step 2: Control Signal = correction

Let’s now define the control response using different controllers.


P Controller

correction = Kp * error

Adjust left/right motor speed like this:

left_speed = base_speed - correction 
right_speed = base_speed + correction

PI Controller

Adds an integral term to remove bias or steady drift.

integral += error 
correction = Kp * error + Ki * integral

PD Controller

Adds derivative term to reduce overshoot and improve stability.

derivative = error - previous_error 
correction = Kp * error + Kd * derivative 

previous_error = error

⚙️ PID Controller

Full version:

integral += error 
derivative = error - previous_error 
correction = Kp * error + Ki * integral + Kd * derivative 
previous_error = error

Motor Speed Update

Make sure to clamp the motor speeds to valid PWM limits (e.g., 0 to 65535 for RP2040 PWM):

def clamp(value, min_val, max_val):
    return max(min_val, min(value, max_val))

left_pwm = clamp(base_speed - correction, 0, max_pwm)
right_pwm = clamp(base_speed + correction, 0, max_pwm)

set_pwm(left_motor, left_pwm)
set_pwm(right_motor, right_pwm)

Tuning Tips

  • Start with P controller, set Kp = 1.0, adjust until it tracks the line with minimal wiggle.

  • Add D if it overshoots or oscillates.

  • Add I if it slowly drifts off the line over time or turns unevenly.