Mapping · University · about 35 min
The forward model: given a map and a pose, what would the sensor read?
[1 mark]What does this program print?
CELL = 5.0
occupied_row = 6
y = 2.0
r = 0.0
while int((y + r) / CELL) < occupied_row:
r += 1.0
surface = 33.0
print(r, surface - y)
28.0 31.0
The caster stops on entering row 6 at y = 30, so it predicts 28 cm, but the surface inside the cell is 31 cm away. That shortfall is the half-cell bias.
[1 mark]A ray caster's predictions are consistently a few centimetres shorter than the real readings. Why?
[1 mark]A particle filter has 300 particles and uses 2 beams each, at 10 Hz, and each ray cast marches 150 steps. How many grid lookups is that per second?
[1 mark]What does a likelihood field precompute, and what does it save?
[1 mark]A planner uses a ray cast to ask whether a straight route is safe. How should that caster treat unknown cells?
[1 mark]Why can using two of the eight beams improve the estimate as well as the speed?
Standing at (100, 50), build a map from the scans. Then ray cast from a pose 20 cm behind the robot and print predicted:. Drive back 20 cm, measure, and print measured: and error:.
from bugbot import * import math connect() CELL, W = 5.0, 40 L_OCC, L_FREE, L_MAX, FAR = 0.85, -0.4, 8.0, 170.0 START_X, START_Y = 100.0, 50.0 grid = [0.0] * (W * W)
The hint students can ask for: Build a small map standing still, then march a ray out of it from a pose the robot is not at yet: one step at a time until a cell is occupied, and the distance you have marched is the prediction. Then drive back 20 cm and see whether the map was telling the truth.
from bugbot import *
import math
connect()
CELL, W = 5.0, 40
L_OCC, L_FREE, L_MAX = 0.85, -0.4, 8.0
FAR = 170.0
START_X, START_Y = 100.0, 50.0
grid = [0.0] * (W * W)
def bump(x, y, amount):
c, r = int(x / CELL), int(y / CELL)
if 0 <= c < W and 0 <= r < W:
i = r * W + c
grid[i] = max(-L_MAX, min(L_MAX, grid[i] + amount))
def integrate():
px, py = position()
x, y = START_X + px, START_Y + py
h = heading()
for a, d in scan():
th = math.radians(h + a)
sx, sy = math.sin(th), math.cos(th)
r = 0.0
while r < min(d, FAR) - CELL:
bump(x + r * sx, y + r * sy, L_FREE)
r += CELL / 2
if d < FAR:
bump(x + d * sx, y + d * sy, L_OCC)
for i in range(25):
integrate()
wait(0.1)
# the forward model: march out of the map until a cell is occupied
def cast(x, y, h, limit=250.0):
th = math.radians(h)
sx, sy = math.sin(th), math.cos(th)
r = 0.0
while r < limit:
c, row = int((x + r * sx) / CELL), int((y + r * sy) / CELL)
if not (0 <= c < W and 0 <= row < W):
return limit
if grid[row * W + c] > 1.0:
return r
r += 1.0
return limit
predicted = cast(START_X, START_Y - 20.0, 0.0)
print("predicted:", round(predicted, 1))
backward(50, distance=20)
wait(0.3)
readings = []
for i in range(8):
readings.append(distance())
wait(0.1)
measured = sum(readings) / len(readings)
print("measured:", round(measured, 1))
print("error:", round(abs(predicted - measured), 1))
Any program that meets the task's checks is marked correct in the simulator; this is one way, not the only way.