We started with a new approach to line following. Initially we attempted to use 5 IR sensors but we found there was too much overlap in the reflection of the IR beams, causing inaccurate readings. We switched to 3 sensors, but still had problems. Firstly we added fairings to block the overlap. Then we switched to following a white line on a black background (luckily the kitchen table was black!). These changes resulted in setup the could reliably detect the line. Now we needed so code....
We started by creating our own algorithm which gave each of the line sensors a number which is what we used to to set the motor steering angle to. However we found that we kept overshooting the sharp turns, so we added a small code snippet to make it reverse when we overshot the end of the line. This worked but was not very reliable.
We had read previously (See book review on "Robotics at Home with Raspberry Pi Pico") about using a PID algorithm. However it sounded complicated to code, so was asked Bing Co-Pilot "write the code in CircuitPython to do line following using PID algorithm with 3 digital line sensors, driving 2 motors". We modified this to use out own motor setup functions and watched it wiggle down the line. After a bit of tuning, success! We had a pretty reliable line following robot.
import time
import board
import pulseio
from digitalio import DigitalInOut, Direction, Pull
# Initialize the motors
left_motor = pulseio.PWMOut(board.D9, frequency=50)
right_motor = pulseio.PWMOut(board.D10, frequency=50)
# Initialize the line sensors
line_sensors = [DigitalInOut(board.D2), DigitalInOut(board.D3), DigitalInOut(board.D4)]
for sensor in line_sensors:
sensor.direction = Direction.INPUT
sensor.pull = Pull.UP
# PID constants
Kp = 1.0
Ki = 0.0
Kd = 0.0
# PID variables
integral = 0.0
previous_error = 0.0
while True:
# Read the line sensors
line_readings = [not sensor.value for sensor in line_sensors]
# Calculate the error
error = line_readings[0] - line_readings[2]
# Calculate the PID terms
proportional = error
integral += error
derivative = error - previous_error
# Calculate the motor speeds
left_speed = 0.5 + Kp*proportional + Ki*integral + Kd*derivative
right_speed = 0.5 - Kp*proportional - Ki*integral - Kd*derivative
# Set the motor speeds
left_motor.duty_cycle = int(left_speed * 65535)
right_motor.duty_cycle = int(right_speed * 65535)
# Update the previous error
previous_error = error
# Wait for a short period of time
time.sleep(0.01)