Following a line
cx to rotation, the angle as a look-ahead, speed on the straights.
Do this lesson in the simulatorPrint it: worksheet (PDF) · answers (PDF) · as a web page
Guide: Line following robots explained
Following a line is the servoing from Module 4 pointed at the mat: the line's cx is the error, rotation is the correction. The difference is that a line bends, so the loop never gets to relax.
One step
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
def follow_step(speed=60, gain=0.4):
# one tick of line following: steer from where the line crosses the picture
# [cx, angle] of the line ahead, or [] if none
seen = line()
if not seen:
return False
cx, angle = seen
# pixels left (-) or right (+) of centre
error = cx - 160
# forward, sideways, rotation: -100 to 100 each, until the next command
drive(speed, 0, error * gain)
return True
# camera: the line detector
set_cv("line")
# do this 30 times (tick counts from 0)
for tick in range(30):
follow_step()
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
# all motors off
stop()
print("at", position(), "line now", line())
follow_step does one tick and says whether it saw the line. The gain is in rotation per pixel: 0.4 means a line 50 pixels off centre (about 5 cm, at the scan row) gives rotation 20.
The whole line
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
def follow_step(speed=60, gain=0.4):
# [cx, angle] of the line ahead, or [] if none
seen = line()
if not seen:
return False
cx, angle = seen
# forward, sideways, rotation: -100 to 100 each, until the next command
drive(speed, 0, (cx - 160) * gain)
return True
# camera: the line detector
set_cv("line")
while position()[1] < 85:
if not follow_step(60, 0.4):
# no line: creep on, it is probably a gap
forward(30)
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
# all motors off
stop()
print("end:", position())
position() counts from where the robot started, not from the corner of the mat. This robot starts 5 cm up the mat, so position()[1] < 85 stops it 90 cm up, inside the green zone. The end print, (-6.9, 85.7), means 6.9 cm to the left of the start and 85.7 cm further up: the last piece of line is 5 cm left of where the first piece began.
Watch the robot take the bends. The line jogs right and then back left, and at each bend it swings well off-centre before the robot has turned: 59 pixels at the first bend, 85 at the worst. A P controller only turns once there is an error, so it always lags a bend a little. That is fine as long as the line stays in view, and here it drops out of view for just one tick, at the very end. The run is done in 10.2 seconds.
Using the angle
cx says where the line is; angle says where it is going. Feeding both in lets the robot start turning before the line has drifted off-centre:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# camera: the line detector
set_cv("line")
while position()[1] < 85:
# [cx, angle] of the line ahead, or [] if none
seen = line()
if seen:
cx, angle = seen
# forward, sideways, rotation: -100 to 100 each, until the next command
drive(70, 0, (cx - 160) * 0.3 + angle * 1.5)
else:
# drive forward at 30 (keeps going until the next command)
forward(30)
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
# all motors off
stop()
print("end:", position())
Faster and smoother round the bends: the run is done in 7.9 seconds instead of 10.2, the line is never more than 58 pixels off centre, and it never leaves the view. The angle term is the beginning of a derivative controller: it reacts to where the error is heading, not just where it is.
Speed on the straights
A line follower that is fast on the straights and slow in the bends beats one at a fixed speed. The angle tells you which you are on: small angle, speed up.
Task: follow the line
Follow the line to the green zone, staying within 6 cm of it for at least 85% of the run.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# drive forward at 60 for 90 cm, then stop
forward(60, distance=90)
Challenges
- Make the speed depend on the angle: 80 when the line is straight, 40 in a bend.
- Print the largest
cxerror you saw during the run. - Follow the line backwards from the end to the start (turn round first).