Step 2
Least squares: fitting noisy data
Three noisy points fit exactly, noise and all. Twelve points and least squares average the noise away.
A noisy camera
The overhead camera looks straight down and reports pixels : column and row. For points at ball height, table position and pixel are linked by a 2×3 matrix of six unknowns, , so calibrating means finding from pads whose positions you know. The camera moves every run.
This time the marker between the fingers is partly hidden, so the camera finds it only to within about 2.5 pixels (roughly 6 mm). Reading again without moving gives the same answer: it's the same picture. The balls' centres are still found exactly.
With three pads, solve_affine from step 1 (included below) fits the three noisy readings exactly, noise and all. Near those pads the map is fine; away from them the error grows. The starter calibrates on three pads: run it a few times and it misses a ball in about a third of the runs, while reporting a residual of zero. A perfect fit to noisy data is a warning sign.
More equations than unknowns
Twelve pads give 24 equations for 6 unknowns. With noise, no satisfies them all, so find the best compromise. For pad , the residual is how far the map's prediction lands from the pad:
Least squares chooses the that makes the sum of squared residuals, (each residual's squared length, summed), as small as possible. With the matrix of rows and the column of the pads' x-values, the best first row of is
and gives the second row. is transposed (rows become columns). np.linalg.lstsq(M, points, rcond=None) solves both at once, more accurately than inverting . Its first result is 3×2: transpose it (.T) to get .
With three pads, least squares gives the exact fit again. With twelve, their errors partly cancel, and because the pads surround the balls, the map holds everywhere a ball can be.
Seeing with a Camera, step 5 fits a camera's pose to many points the same way.
Your task
- Write
fit_affine(pixels, points)for any number of pairs (pixelsandpointsare both ). Return the 2×3 matrix . - Read the marker on all 12 pads and fit with
fit_affine. The starter prints the RMS (root-mean-square) residual and draws each residual at its pad, 5 times life size. - Put all four balls in the cup.