Coordinate frames and sign conventions
The section that causes the most bugs and tolerates the least ambiguity. Fix the conventions, then write code.
This is the important one. In projects that combine vision and motion, more than half of the strange problems end up being "a sign was flipped" or "two people meant different things by the same frame". So the conventions are written down, fixed, and copied verbatim.
Four frames
World W the stage, right-handed, mm
├── Machine M each axis's mechanical zero (the origin after homing)
├── Part P a datum on the part or carrier, usually defined by fiducials
└── Image I pixels, origin top-left, u right, v down
└── Camera C optical centre at the origin, Z along the optical axis
Conventions — copy these, do not improvise
| Item | Convention |
|---|---|
| Handedness | Right-handed. X right, Y away from the operator, Z up |
| Positive rotation | Counter-clockwise seen looking down the positive axis toward the origin |
| Image v axis | Down is positive (every camera SDK does this; do not fight it) |
| Angles | rad internally, degrees only in the UI |
| Lengths | mm internally; pixel size quoted in µm but converted on input |
| Transform notation | T_A_B maps points from B into A: p_A = T_A_B · p_B |
| Composition | T_A_C = T_A_B · T_B_C — subscripts meet in the middle, so a wrong chain is visible at a glance |
The trap in "v points down"
In the image v points down; in the world Y points away. Those directions are opposite, so the 2D similarity transform from image to world always carries a sign flip:
[x_W] [ s·cosθ s·sinθ ] [ u ] [tx]
[y_W] = [ s·sinθ -s·cosθ ] [ v ] + [ty]
↑
this minus sign is not a typo
Miss it and the calibration still "converges" with a small-looking residual, but Y motion is globally inverted. The symptom is: X tracks fine, Y drifts further away the more you move. When you see that, come back to this sign before you start suspecting the camera.
Hand-eye: two configurations
| Configuration | Camera mounting | What you solve for | Typical use |
|---|---|---|---|
| Eye-in-hand | On the moving member | T_Tool_Cam |
Camera rides the Z axis to find focus |
| Eye-to-hand | Fixed to the frame | T_World_Cam |
Fixed overhead station |
The two have different solve formulations, and picking the wrong one does not raise an error — it hands you a small residual and a completely wrong result. The wizard asks you first for a reason; do not click past it.
The cheap way to check a sign
Do not re-derive the algebra. Run an experiment:
- Move X by
+10 mm; - See which way, and how far in pixels, a marker moved in the image;
- Convert that pixel displacement back to mm with your transform and check it says
+10.
If it does not, a sign is flipped or an axis mapping is wrong. On the virtual rig this takes a minute. On real hardware it takes half an hour and risks a crash.
Naming rule
Variables carry their frame. Bare x or pos is not allowed:
var pTargetW = new Point2d(120.5, 80.0); // world, mm
var pTargetI = new Point2d(1204.0, 768.0); // image, px
var tWorldCam = calib.Get<Transform2d>("T_World_Cam");
var pMeasuredW = tWorldCam * pMeasuredI; // subscripts visibly line upIt reads as fussy, and it is. It also moves "a sign got flipped" from a machine crash to a code review comment.