Click & WhirrAll lessons

Perception · Lesson p1

The camera under the line

This is the written walk-through of the lesson: the idea, stage by stage, plus the deeper asides. The interactive version, where you write real Python in the browser and drive a simulated robot, lives on the lesson page.

Since lesson 02 you have been handed line_offset, one honest number telling you how far off the line you sit. Nothing in the world produces that number. Something has to look at the floor and decide. Here is the raw look: a strip of brightness readings across the ground ahead of you, dark where the line is and pale where it is not. Rebuild the number from it, and the proportional law you already tuned will drive this course on your reconstruction.

The number was always a picture

Every lesson since 02 has opened with the same quiet gift: line_offset, a single number telling you exactly how far off the line you are sitting, and in which direction. You tuned a gain against it. You damped it. You never once asked where it came from.

It came from here. Press Run and look at the new panel in the upper half of the simulator, just under the OFF-CENTER gauge. That is a strip of floor just ahead of the robot, sampled left to right: pale where the mat is bare, dark where the line is. Watch it slide as the robot drives. When the robot sits square on the line the dark patch is centered; when it drifts right, the dark patch slides left. There was never a number in that strip. There was only ever brightness.

The robot is not steering yet, because find_line currently claims the line is always dead ahead, so it cruises straight off the first curve. Your job for the rest of this lesson is to turn that strip back into the number you have been trusting all along.

Turn the row back into an offset

robot.sensors.camera() hands you a tuple of brightness readings, one per cell, running left to right across the strip. 0.0 is bare floor and 1.0 is the middle of the line. The cells at the edge of the dark patch come back part-lit, somewhere in between, because the line does not stop cleanly on a cell boundary.

Two questions, in that order. First: which cells are actually looking at line rather than floor? That is what threshold is for. Compare each brightness against it and skip the ones that fail.

Second: where is the middle of the ones that pass? Each cell sits at a known position u across the strip, running from +half_width at cell 0 (your left) to -half_width at the last one. Average those positions, and weight each by how bright its cell was, so a fully dark cell counts for a whole vote and a half-lit edge cell counts for half. That weighted average is your offset, in px, in exactly the units and the sign lesson 02 used.

Drive lesson 02 on your own reading

Look at what follow_camera does with your answer. It multiplies it by kp and splits it across the wheels, which is the same two-line proportional law you wrote and tuned in lesson 02b, unchanged, right down to kp=2.4 and base=26.

That is the point of driving the same course. Those constants were tuned against the real sensor, so if they still glide, your reconstruction is not merely close, it is close enough to be the same signal as far as the controller can tell. The orange caret above the strip marks where the line truly is. Your number and that caret should agree to about a pixel, the whole way home.

Go deeper: Why the blurry cells are the useful ones

There is a shortcut that looks reasonable and is not: just find the single darkest cell and report its position. It works, roughly. It will even get you to the flag. But its answer can only ever land on a cell center, so it steps in jumps instead of moving smoothly, and the controller faithfully turns every one of those jumps into a twitch of the wheels. Run it and the wobble meter tells on you.

Weighting by brightness is what buys back the space between the cells. A cell that is 40 percent dark is telling you the line covers 40 percent of it, and that fraction is real information about where the edge falls inside that cell. Add up enough of those fractions and the answer lands between the cells, finer than the strip that produced it. Sub-pixel accuracy, from a camera with no extra pixels.

This is the whole trick behind a surprising amount of real machine vision. Edge detectors, blob centroids, and camera calibration targets all lean on the same idea: the soft, blurry, in-between values at a boundary are not noise to be thresholded away, they are the most precise part of the measurement.

Ready to build it? The interactive lesson is where you write the code and watch the robot run.