Following a line
cx to rotation, the angle as a look-ahead, speed on the straights.
Do this lesson in the simulatorFollowing 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 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())
Watch the robot take the two bends. On the diagonal the line sits off-centre all the way along, and the controller holds it there: that is the same steady error a P controller always has, and it is fine as long as the line stays in view.
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 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 the whole 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).