Searching
Spin until it is in view; find and approach as functions; list comprehensions.
Do this lesson in the simulatorThe camera sees 120 degrees. The world is 360. Most of the time the thing you want is not in the picture, and a robot that only reacts to what it sees just sits there. This lesson is about looking for things.
Nothing in view
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# camera: the tag detector
set_cv("apriltag")
print(apriltags())
An empty list. Marker 5 is somewhere on the mat, but not in the 120 degrees in front of the robot.
Spin until you see it
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# camera: the tag detector
set_cv("apriltag")
# until a tag is in view
while not apriltags():
# spin clockwise on the spot at 40
turn_right(40)
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
# all motors off
stop()
# pause 0.3 s (the robot keeps doing what it was told)
wait(0.3)
# every tag in view: [id, cx, cy, distance], nearest first
tags = apriltags()
print("found", tags[0][0], "after turning to", round(heading()))
while not apriltags() reads as "while the list is empty". Each time round, spin a little and look again. The moment the tag enters the picture the loop ends. Then settle, because the robot coasts and the tag may have slid to the edge of the view, or out of it.
Spin the right way
Spinning always to the right takes up to a full turn. If you have a clue, use it: the last place you saw it, or the side the distance grid says is open. A search that uses clues is called heuristic. Without a clue, right is as good as left.
Find, then approach
Searching and servoing are two functions, and every camera program in this course is those two in a row:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
def bearing_of(cx):
return (cx - 160) * 120 / 320
def find(tag_id):
# again and again, for ever
while True:
tags = [t for t in apriltags() if t[0] == tag_id]
if tags:
# leave the loop
break
# spin clockwise on the spot at 40
turn_right(40)
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
# all motors off
stop()
# pause 0.3 s (the robot keeps doing what it was told)
wait(0.3)
def approach(tag_id, stop_at):
# again and again, for ever
while True:
tags = [t for t in apriltags() if t[0] == tag_id]
if not tags:
# lost it: turn slowly until it is back
turn_right(30)
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
# back to the top of the loop
continue
cx, dist = tags[0][1], tags[0][3]
if dist <= stop_at:
# leave the loop
break
speed = max(20, min(70, (dist - stop_at) * 3))
# forward, sideways, rotation: -100 to 100 each, until the next command
drive(speed, 0, bearing_of(cx) * 3)
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
# all motors off
stop()
# camera: the tag detector
set_cv("apriltag")
find(5)
print("found 5")
approach(5, 14)
print("at", position(), "facing", round(heading()))
Two new things. [t for t in apriltags() if t[0] == tag_id] keeps only the tags with the id you want: a list comprehension, and one of the most useful lines in Python. And approach handles losing the tag by turning slowly instead of stopping.
Task: hide and seek
Find marker 5, print found 5, then drive up and stop about 14 cm in front of it.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# camera: the tag detector
set_cv("apriltag")
print(apriltags())
Challenges
- Search left instead of right. Which is quicker for this marker?
- Print how many degrees the search turned through.
- Make
findgive up and returnFalseafter a full turn with nothing seen, and print a message instead of spinning forever.