Localisation · University · about 50 min
No idea where it starts. Work it out, then drive somewhere on purpose.
[1 mark]What does this program print?
import math SIGMA = 3.5 particles = [40.0, 42.0, 44.0, 150.0, 152.0] measured = 157.0 w = [math.exp(-(((200 - y) - measured) ** 2) / (2 * SIGMA * SIGMA)) + 1e-12 for y in particles] total = sum(w) weights = [v / total for v in w] print(round(sum(v * y for v, y in zip(weights, particles)), 1))
42.2
The reading 157 means y is about 43, so the three particles near 40 to 44 share the weight and the two near 150 predict about 49 and get essentially none. The weighted mean is 42.2.
[1 mark]The depth sensor faces the far wall, so its readings pin down y. The robot turns ninety degrees to the left. What does a cloud weighed against the new readings pin down?
[1 mark]In phase one the robot faces the far wall, and the cloud finds y but stays a stripe across the mat. What cuts the stripe down to a spot?
[1 mark]A project filter weighs its x cloud against every reading, including those taken during the ninety degree turn towards the left wall. It ends 58 cm from the robot with a spread of 5 cm. Why?
[1 mark]During the run the estimate suddenly jumps by 60 cm. What has most likely happened?
[1 mark]In phase two the spread starts climbing steadily. What does that mean?
[1 mark]Which defence specifically lets the filter recover if the robot is picked up and moved mid-run?
Work out where the robot woke up, print it as found:, and then drive into the green corner. position() is not allowed anywhere.
from bugbot import * import math, random connect() DT, N, SIGMA = 0.1, 400, 3.5 particles = [random.uniform(0, 200) for i in range(N)]
The hint students can ask for: The robot wakes up somewhere on the mat and does not know where. The depth sensor only measures the wall it is pointing at, so one cloud can only find one axis: localise y facing the far wall, drive to the right y, then turn to face the left wall and localise x the same way. Print the y you worked out before you set off.
from bugbot import *
import math
import random
connect()
DT = 0.1
N = 400
SIGMA = 3.5
V_MAX = 20.0
def localise(predict, ticks=25, jitter=0.8):
"""A cloud over the whole mat, weighed against what the sensor would read at each place."""
particles = [random.uniform(0, 200) for i in range(N)]
for t in range(ticks):
measured = distance()
w = []
for v in particles:
d = predict(v) - measured
w.append(math.exp(-d * d / (2 * SIGMA * SIGMA)) + 1e-12)
total = sum(w)
weights = [v / total for v in w]
step = 1.0 / N
r = random.uniform(0, step)
c = weights[0]
i = 0
fresh = []
for m in range(N):
u = r + m * step
while u > c and i < N - 1:
i += 1
c += weights[i]
fresh.append(particles[i] + random.gauss(0, jitter))
particles = fresh
wait(DT)
return sum(particles) / N
# facing the far wall: the reading is 200 minus y, and driving forward increases y
found_y = localise(lambda y: 200 - y)
print("found:", round(found_y, 1))
y = found_y
for tick in range(500):
error = 50.0 - y
if abs(error) < 4:
break
drive(100 * max(-12.0, min(12.0, 0.6 * error)) / V_MAX, 0, 0)
y += flow()[1] * DT
wait(DT)
if tick % 5 == 0:
y = 0.7 * y + 0.3 * (200 - distance())
stop()
wait(0.4)
# face the left wall: now the reading IS x, and driving forward reduces it
turn_left(45, angle=90)
wait(0.5)
x = localise(lambda v: v)
for tick in range(500):
error = x - 50.0 # too far right means drive forward
if abs(error) < 4:
break
drive(100 * max(-12.0, min(12.0, 0.6 * error)) / V_MAX, 0, 0)
x -= flow()[1] * DT
wait(DT)
if tick % 5 == 0:
x = 0.7 * x + 0.3 * distance()
stop()
print("in the corner near", round(x, 1), round(y, 1))
Any program that meets the task's checks is marked correct in the simulator; this is one way, not the only way.