7. Moving to a certain position

Where the robot is, and how to send it somewhere

The problem. drive() and spin() are open-loop: they run the wheels for a time and never check where the robot ended up. On the floor, a wheel slips or a turn comes out at 85 degrees, and nothing corrects it, which is why your step 6 robot does not end exactly where the simulator’s did, and the challenge square does not quite close. goto() is closed-loop: the robot reads its own position all the way and keeps correcting until it is there.

Where am I: the frame

robot.position() gives where the robot is, as (x, y) in millimetres, measured by the lighthouse (step 2). The origin is the top-left corner of the map, x grows to the right and y grows down (Figure 1). So “300 mm up the map” is (x, y - 300).

(0, 0) x y (x, y - 300) (x, y) 300 mm up
Figure 1: The map’s frame, in millimetres: the origin is the top-left corner and y grows down, so going up the map takes y down.

Coordinates belong to the controller you talk to. Each controller places its map on a site. Your own simulator uses the default site, a 2 x 2 m field; the room’s real floor uses the instructor’s site, with a staging area along the top and other numbers. So:

  • Relative moves work everywhere. A target computed from robot.position(), like (x, y - 300), means the same thing in your simulator and on the floor. Every script on this page does that.
  • Absolute targets do not travel. A fixed goto(1000, 1200) must be read off the map of the controller you are connected to (hover over the console map to see the coordinates), not off your simulator’s map.

Going there: goto() and arrived()

  • robot.goto(x, y) gives the robot a target. The robot drives there by itself and stops once it is within threshold mm of it (60 by default). goto() returns at once: the robot is still on its way.
  • robot.arrived() is True once it is there. Wait for it in a loop, instead of a time.sleep() that guesses how long the trip takes.
  • academy.distance(a, b) is the distance in mm between two (x, y) points, to check how close it got.
Important

goto() returns immediately: the robot drives there by itself. Use arrived() to wait.

Save example.py next to academy.py and run it against your simulator, python example.py.

example.py
import time

import academy

academy.connect("http://localhost:8000")  # your simulator
robot = academy.bot("000000")  # its first robot

x, y = robot.position()  # in mm: x grows to the right, y grows DOWN the map
print("Start:", x, y)

for target in [(x, y - 300), (x + 300, y - 300), (x, y)]:  # up, right, back
    robot.led(0, 0, 255)  # blue while it drives
    robot.goto(*target)
    while not robot.arrived():
        time.sleep(0.5)
    robot.led(0, 255, 0)  # green: there
    off = academy.distance(robot.position(), target)
    print("At", robot.position(), f"{off:.0f} mm from {target}")

You should see: robot 000000 goes up, right and back to where it started, blue while it drives and green at each target, and the terminal prints how many mm from each target it stopped.

Core there and back

Save exercise.py (below), fill in its two TODOs, and run it against your simulator. When it works there, run it on your real robot: change BASE_URL and LABEL as in step 6.

You should see: the robot turns red, drives 300 mm up the map, comes back and turns green.

exercise.py
"""Step 7: moving to a certain position.

Building on example.py: turn the robot red, send it 300 mm up the map, bring it
back and turn it green.
You should see: on the map, the robot turns red, drives up, comes back, turns green.
"""

import time

import academy

BASE_URL = "http://localhost:8000"  # your simulator; or the instructor's controller, as in step 6
LABEL = "000000"  # the simulator's first robot; or your real robot's label, as in step 6

academy.connect(BASE_URL)
robot = academy.bot(LABEL)

# 1. Turn it red this time.
# TODO

x, y = robot.position()
print("Start:", x, y)

# 2. Send it 300 mm up the map. Instead of a fixed sleep, wait until it is there.
robot.goto(x, y - 300)
while not robot.arrived():
    time.sleep(0.5)

# 3. Bring it back to (x, y), wait the same way, then turn it green.
# TODO

print("Back at", robot.position())

goto() only gives the robot its target, then returns at once. The while not robot.arrived() loop is what makes your script wait until the robot is there before it sends the next order.

  • cannot unpack non-iterable NoneType: position() returned None, so the lighthouse cannot see the robot. Call the instructor.
  • The robot never arrives: it is circling close to the target. Stop the script with Ctrl+C, check that the floor around the target is free, and run it again.
solution.py
"""Step 7: moving to a certain position.

Building on example.py: turn the robot red, send it 300 mm up the map, bring it
back and turn it green.
You should see: on the map, the robot turns red, drives up, comes back, turns green.
"""

import time

import academy

BASE_URL = "http://localhost:8000"  # your simulator; or the instructor's controller, as in step 6
LABEL = "000000"  # the simulator's first robot; or your real robot's label, as in step 6

academy.connect(BASE_URL)
robot = academy.bot(LABEL)

# 1. Turn it red this time.
robot.led(255, 0, 0)

x, y = robot.position()
print("Start:", x, y)

# 2. Send it 300 mm up the map. Instead of a fixed sleep, wait until it is there.
robot.goto(x, y - 300)
while not robot.arrived():
    time.sleep(0.5)

# 3. Bring it back to (x, y), wait the same way, then turn it green.
robot.goto(x, y)
while not robot.arrived():
    time.sleep(0.5)
robot.led(0, 255, 0)

print("Back at", robot.position())