Reading the World
Write control(obs) that reads the rover's goal_bearing each step and steers toward the goal pad, turning the differential-drive wheels so the rover reaches the goal within tolerance instead of driving blind.
Try this first — before any explanation.
Make the rover reach the green pad. The starter defines control(obs) and slams both motors forward at 0.6 — it runs, but it ignores every sensor. The rover starts facing +x while the pad sits up and to the LEFT, so it drives straight past and never arrives. Driving blind is not control; reading obs['goal_bearing'] and steering on it is the lesson.
Drive the differential-drive rover to the green pad. Read obs['goal_bearing'] and steer the two wheels — the starter ignores it and drives straight past.
Reading the World
Drive the differential-drive rover to the green pad. Read obs['goal_bearing'] and steer the two wheels — the starter ignores it and drives straight past.
The idea, built visually.
The rover is handed a fresh dict of sensor readings every single step, and the one that matters here is goal_bearing: a single angle that says which way to turn. Positive means the goal is off to your left; negative, to your right; zero means dead ahead. Drive straight and you're solving the wrong problem — you reach where you were already pointed, not where the goal is.
A differential-drive rover steers by making one wheel spin faster than the other. Spin the right wheel faster and the rover swings left. So feed the bearing straight into the wheels: the bigger the angle, the harder the turn, and as the angle melts to zero the wheels even out and the rover rolls in. One sensor, two wheels, and the gap closes itself.
▣ Stage animation: A rover sits facing +x with a goal pad up-and-left; a bright bearing arrow swings from the nose to the pad and a +0.79 label glows. The naive run shoots straight off-screen and the pad stays untouched. Then a turn term proportional to the arrow scales the two wheel bars apart — right wheel longer, rover arcs left — the arrow shrinks toward 0, the bars equalize, and the rover noses onto the pad as the distance readout ticks under 0.2 m.
Build it up, step by step.
Step A (read it): inside control(obs), pull bearing = obs['goal_bearing'] and dist = obs['goal_dist']. Step B (steer): build a turn term proportional to bearing, e.g. turn = 1.2 * bearing, then set left = base - turn and right = base + turn so a positive (left) bearing speeds the right wheel and swings the rover toward the goal. Step C (arrive): make base shrink with dist (e.g. min(0.6, 0.25 + 0.5*dist)) so the rover eases onto the pad instead of skidding past, clamp both wheels to [-1.0, 1.0], and return (left, right).
How the Bench grades your run.
PASS WHEN PASS when the rover reaches within 0.2 m of the goal pad inside the 12 s run by steering on obs['goal_bearing'].
- FAIL: closest approach never neared the pad — you're driving on a fixed command. Read obs['goal_bearing'] and turn toward it.
- FAIL: the rover curved the WRONG way — check your sign. Positive goal_bearing means the goal is to your LEFT, so the RIGHT wheel must spin faster (right = base + turn).
- FAIL: you reached the pad's neighborhood but slid past — ease the forward speed as obs['goal_dist'] shrinks so you settle on the pad instead of overshooting.
Bring back what you've already mastered.
- From the SENSE/PERCEIVE/DECIDE/ACT loop: name which stage reading obs['goal_bearing'] is, and which stage returning (left, right) is.
- Given a differential-drive rover, state which wheel must spin faster to turn LEFT, and why right = base + turn produces a left turn for positive bearing.
- Predict: if goal_bearing is negative, which way does the rover need to turn, and which wheel speeds up?
What you must demonstrate to advance.
Produce a control(obs) that steers on goal_bearing and drives the rover to within 0.2 m of the goal pad inside 12 s, returning clamped (left, right) duties (L2 DECIDE/ACT on real rover physics).
How this feeds your build.
This reactive controller is the first live control loop of the capstone autonomy stack: the same control(obs) signature carries forward into the PID heading lock and the FSM navigator, and later gets profiled toward the metal in firmware.