Losing the line

Gaps: carry on, then search with a state machine.

6.3Seeing moreRobot club20 min

Do this lesson in the simulator

Lines have gaps: a join in the tape, a scuff, a junction. A follower that stops the moment line() is empty is stuck at the first one. This lesson is what to do when the line is gone.

Keep going first

Most gaps are short. The cheapest fix is to carry straight on for a while before deciding the line has really gone:

# 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")
lost = 0
# do this 120 times (tick counts from 0)
for tick in range(120):
    if follow_step():
        lost = 0
    else:
        lost += 1
        # drive forward at 40 (keeps going until the next command)
        forward(40)
    # pause 0.1 s (the robot keeps doing what it was told)
    wait(0.1)
# all motors off
stop()
print("at", position(), "ticks without a line at the end:", lost)

Run this in the simulator

On this mat the line starts again 22 cm to the right after the gap. Ten centimetres ahead the camera sees about 17 cm either side, so carrying straight on never brings it into the scan row and line() stays empty: the robot ends at (-3.1, 91.5), well left of the green zone, after 97 ticks without a line. The counter tells you how long it has been gone.

From above: the robot carries straight on past the gap, and the new piece of line stays outside what the camera can seegoalends heregrey: whatthe cameracan see22 cmneverin view
Carrying straight on, the camera sees a strip about 17 cm either side of the robot's path. The line starts again 22 cm to the right, outside it, so the robot passes by and ends left of the goal after 97 ticks without a line.

Then search

After enough ticks without a line, switch to a search: creep forward while sweeping side to side, and go back to following the moment the line reappears. That is a state machine from Module 5:

# 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")
lost = 0
state = "follow"
while position()[1] < 85:
    if state == "follow":
        if follow_step(60, 0.4):
            lost = 0
        else:
            lost += 1
            # drive forward at 40 (keeps going until the next command)
            forward(40)
            if lost > 8:
                state = "search"
                sweep = 0
                print("lost the line at", position())
    elif state == "search":
        if line():
            state = "follow"
            print("found it at", position())
        else:
            sweep += 1
            # creep forward, sliding right then left
            drive(25, 40 if (sweep // 15) % 2 == 0 else -40, 0)
    # pause 0.1 s (the robot keeps doing what it was told)
    wait(0.1)
# all motors off
stop()

Run this in the simulator

The sweep slides right for 15 ticks, left for 15, and so on, while still creeping forward. Here it loses the line at (-0.4, 29.3) and finds it again at (5.1, 35.6): sliding 5 cm to the right is enough to bring the new piece into the edge of the view. As in lesson 6.2, position() counts from where the robot started, so the loop ends 85 cm up from the start, in the green zone.

From above: the robot loses the line at the gap, slides right while creeping forward, and finds the new piece at the edge of its viewgoallostfoundscan row
The search slides right while it creeps on. By the time the robot is 5 cm right of where it started, the end of the scan row touches the start of the new piece, and the robot goes back to following it.

Because the robot strafes instead of turning, it keeps its heading, so when the line is found again it is already pointing the right way.

Choosing the first direction

Sweeping right first is a guess. A better guess is the last angle you saw: a line that was heading right when it vanished probably continues to the right. Store it before it is gone. On this mat the last reading before the gap is [163, 56.0]: the far scan row, 25 cm ahead, has already caught the start of the next piece, off to the right.

Task: find the line again

Follow the line through the gap to the green zone, staying within 6 cm of it for at least 60% of the run.

# 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):
        # leave the loop
        break
    # pause 0.1 s (the robot keeps doing what it was told)
    wait(0.1)
# all motors off
stop()

Challenges

  1. Sweep in the direction of the last angle you saw.
  2. Count how many ticks the search took and print it.
  3. Make the sweep widen each time: 10 ticks, then 20, then 30.