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_rightto 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
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
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
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
Adjust left/right motor speed like this:
PI Controller
Adds an integral term to remove bias or steady drift.
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.