Projection Math
At runtime, point-to-pixel maps a LiDAR point into image pixels with a single 3×4 matrix :
On the feature-branch calibrator, is not a free 12-DoF DLT matrix. It is composed from a known camera matrix and a rigid LiDAR→camera transform:
where sends a point from the LiDAR frame into the OpenCV camera frame, and maps that 3D camera point into the runtime image that classifiers actually see.
Code: compose_projection in calib_geometry.py; application in state_manager.cpp::transform_points.
Frame axes
Section titled “Frame axes”LiDAR points are stored as forward, left, up. OpenCV camera coordinates are right, down, forward. The extrinsic solve works in OpenCV axes; lidar_to_opencv_axes() is the fixed axis change used when seeding an initial .
Runtime vs physical
Section titled “Runtime KKK vs physical KKK”Intrinsics YAML stores the physical (pre-remap) camera matrix and distortion. SeeCam capture undistorts with getOptimalNewCameraMatrix(..., alpha=0), then the right camera rotates the image 180°:
The extrinsic solver therefore builds
and writes so that projecting into the already-rotated right image needs no extra pixel remap at solve time. apply_runtime_pixel_transform encodes the same 180° map if you start from a physical-pixel .
If you calibrate against undistorted-but-not-rotated right frames, or feed distorted frames into the extrinsic scripts, the resulting will look systematically wrong even when is fine.
Reprojection error
Section titled “Reprojection error”For a LiDAR point on the board (or a cone), should match the corresponding image feature. The feature-branch write gate uses mean and p95 error between projected LiDAR pattern corners and detected chessboard corners (extrinsics math).
Prod click-DLT estimates directly from clicked pairs without forcing the factorization; see legacy DLT.
