Seeing with a Camera, bonus step
Bonus: Where is the camera?
The camera got bumped. Recover its pose from robot motions: marker pixels plus known 3-D points, solved with a DLT.
Builds on Linear systems: calibrate a camera, Least squares: fitting noisy data and Frames & homogeneous transforms, from the free Foundations.
Write and run this step in the simulator with ProThe camera got bumped
Someone knocked the camera stand. The camera has turned by a few degrees and shifted a few centimetres, which is enough to miss every grasp, so camera.pose is switched off. You'll measure the pose again using the robot itself, because its encoders know exactly where the gripper is.
A magenta marker now sits on the grasp point, so arm.ee_position() is its 3-D position . The starter shows it to the camera at 16 places spread out in 3-D and finds its pixel at each.
This is eye-to-hand calibration: the camera stays put and the robot carries a marker to positions it knows, so finding the camera's pose (its extrinsics) comes down to matching known 3-D points with pixels. Classic hand–eye calibration solves instead. It pairs robot motions with the camera motions they cause to find a fixed transform , such as where a camera sits on the gripper, without knowing where the target is. Here the marker is exactly on the grasp point, so the known points are enough.
The direct linear transform
is known, so undo it first: . What's left is the world-to-camera transform , with where . Writing for the rows of , each point gives two equations that are linear in 's 12 entries:
Stack them into (); the best is the last row of np.linalg.svd(A)[2]. Then tidy up:
- Scale so the rows of have length 1. Negate it if the points come out behind the camera ().
- Noise leaves slightly non-orthogonal: with
U, S, Vt = np.linalg.svd(R_cw), useU @ Vt. - Invert: , .
Then polish
DLT is exact on perfect data but sensitive to noise when the points fill only part of the view. So the starter's refine() finishes with a few Gauss–Newton steps on the reprojection error. That's the standard recipe: a linear method for the first guess, nonlinear least squares for the last millimetres.
Your task
Implement calibrate(points3d, pixels) so it returns the 4 × 4 T_world_cam. The grader checks it on 12 perfect correspondences (it must be exact), then compares the refined pose_estimate with the real pose: within 3° and 2 cm. The bump changes on every run.