Geometry, Mapping, and 3D/4D State › Coordinate Frames and Camera Geometry
World Models HandbookAll chapters Geometry, Mapping, and 3D/4D State › Coordinate Frames and Camera GeometryOne section · Sections in this chapter2 / 9 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
This section covers the vocabulary that everything else depends on: frames of reference, rigid transforms, and the pinhole camera model that connects 3D points to pixel observations. The material is classical, but it deserves careful treatment, because many practical failures in 3D perception and learned world modeling trace back to frame bookkeeping done carelessly: a rotation applied in the wrong direction, a translation expressed in the wrong frame, a depth confused with a range.
Frames, Rigid Transforms, and Homogeneous Coordinates
A frame is a coordinate system attached to something: the world (allocentric), the robot's body (egocentric), a camera, an object. Every geometric statement is implicitly relative to a frame, and much of practical 3D robotics is bookkeeping about which frame each quantity lives in. The phrase "the chair is at position " is incomplete on its own; it only becomes a claim about the world once we say whose coordinates those are. In a world model, this incompleteness is dangerous, because learned networks can silently internalize whatever frame dominated their training data, and then fail unpredictably when the deployment frame convention differs.
Rigid transforms move points between frames. A rigid transform is a rotation followed by a translation; it preserves distances and angles. Physically, this is exactly the transformation relating any two frames that are bolted to rigid bodies, which is why it suffices for cameras mounted on robots, hands holding objects, and sensors on vehicles. We write the transform from world frame to camera frame as:
where is a rotation matrix (, ) and is a translation vector. The two constraints defining have physical meaning: orthogonality says the camera's axes stay perpendicular, and unit determinant says the axes keep a right-handed orientation, which excludes reflections. The subscript convention reads naturally as "the rotation taking points expressed in into points expressed in ." The set of all such transforms forms the group , and composing them is just matrix-style chaining: walking from the camera frame to the body frame to the world frame composes two transforms into one. This composition property is what makes transform graphs workable in practice. A robot typically maintains a tree of frames, camera-to-gripper, gripper-to-wrist, wrist-to-base, base-to-world, and any point can be moved between any two frames by composing the transforms along the connecting path.
The awkward part of this notation is that rotations and translations do not combine into a single matrix multiplication on 3-vectors. Composition becomes messy: , and inverting means remembering that the translation must be rotated back before being negated. Homogeneous coordinates fix all of this. We append a 1 to every point, making it a 4-vector, and write the transform as one matrix:
Now composition is ordinary matrix multiplication, , and inversion is (mostly) a structured transpose-like operation: for a rigid transform, , which needs only the already-available rather than a general matrix inverse. When the transform is a pure frame-to-frame map like , the inverse is the reverse map ; software can obtain it with the structured rigid-transform inverse or cache it as an implementation choice. Homogeneous coordinates also let us treat points at infinity uniformly, which matters because projection is about to send finite 3D points onto a 2D plane where direction, not distance, survives. A direction is represented by a homogeneous vector whose final coordinate is zero. Under projection, a family of parallel 3D lines meets at a vanishing point, while the directions contained in a plane form a horizon line. The projective framework handles these ideal elements with the same algebra as ordinary points rather than as special cases.
Camera parameters split into two groups. Extrinsic parameters describe a camera's pose relative to another frame. For a rigidly mounted camera, the camera-to-body mount transform is a fixed calibration and part of the agent's body schema. The world-to-camera pose used above is instead , so it changes as the body pose changes. Intrinsic parameters describe how the camera maps its own frame to pixels: focal length, principal point, lens distortion. For a stable imaging configuration, intrinsics are usually treated as fixed and estimated offline by calibration, for example by imaging a known checkerboard pattern from many angles and solving for the parameters that best explain the observed line curvature and corner positions. They must be updated or estimated online when zoom, focus, cropping, stabilization, resolution, temperature, or other image-formation changes alter the effective camera model. Keeping these quantities separate matters because a world model must predict the evolving body or world-relative pose while the rigid mount calibration and intrinsics normally stay fixed within that stable configuration. When a learned model implicitly entangles them, it will fail to transfer across cameras, or worse, it will fail to transfer across a zoom or a lens swap on the same robot.
The Pinhole Camera
The pinhole model is the workhorse idealization of a camera. Light passes through a single point (the optical center). The physical sensor lies behind that center and forms an inverted image. The conventional computer-vision model used below instead intersects rays with a virtual image plane in front of the center; the image-coordinate convention absorbs the physical inversion and gives the positive-sign projection equations. Lens distortion is a common geometric departure that calibration can estimate, but real image formation also includes finite-aperture blur, aberrations, motion blur, rolling shutter, sampling, exposure, and other effects that may need explicit models for the task at hand. An ideal visible 3D point in camera coordinates , with positive optical-axis depth , projects to pixel coordinates:
The equations have a direct physical reading. Dividing by is the perspective effect: for fixed lateral coordinates and , increasing positive depth moves the projection toward the principal point. The focal lengths scale normalized ray coordinates and , the tangents of the off-axis ray angles, into pixels. Treating pixel offset as directly proportional to angle is a small-angle approximation. A longer focal length means a given lateral offset at depth produces a larger pixel displacement. The principal point accounts for the fact that the optical axis does not usually pierce the sensor exactly at its center. In matrix form, stacking the whole pipeline from world to pixel:
This compact expression is worth internalizing because it separates cleanly: the matrix does the world-to-camera rigid motion, the calibration matrix does the camera-to-pixel mapping, and the result is a single linear operation in homogeneous coordinates. Homogeneous coordinates isolate this linear projective transform from the nonlinear perspective division that follows; real cameras add lens distortion and other nonidealities outside the displayed model.
The tilde and the carry real meaning: is a homogeneous 3-vector, and means equality up to multiplication by any nonzero scale. Converting that homogeneous image vector to ordinary pixel coordinates then requires dividing by its third coordinate. That division is the nonlinear heart of perspective, and it is where information dies. Up to that division, the mapping from world point to image point is linear, which is why much of multi-view geometry can be developed with linear algebra, and why the division step is exactly where reconstruction difficulty concentrates.
Look at what the projection discards. The pixel depends only on the direction of , not its length. The camera-frame points and land on exactly the same pixel because both and match. The perspective division by has erased the overall scale of the camera-frame point. Every observation is a ray from the optical center, and the observation alone cannot tell you where along the ray the world is. Formally, the two pixel coordinates provide two scalar constraints on a three-dimensional point, leaving a one-parameter family of locations along the ray. This single fact drives most of the rest of the chapter: recovering depth requires either extra information (a second viewpoint, a depth sensor, a learned prior) or extra assumptions. Whenever a system appears to get depth from a single image for free, one of these three is hiding somewhere, and it is worth finding out which.
To see the collapse concretely, the next cell places several points along a single viewing ray at different depths, projects each one, and also compares axial depth against Euclidean range for off-axis points.
import numpy as np
## Points along one viewing ray through the optical center: direction fixed, depth varies.
depths = np.array([1.0, 2.0, 4.0, 8.0])
ray_dir = np.array([0.5, 0.3, 1.0])
points_on_ray = depths[:, None] * ray_dir[None, :]
K_demo = np.array([[200.0, 0.0, 160.0], [0.0, 200.0, 120.0], [0.0, 0.0, 1.0]])
uv_ray = (K_demo @ (points_on_ray / points_on_ray[:, 2:]).T).T[:, :2]
## Axial depth vs Euclidean range for off-axis points at fixed depth Z = 3 m.
Z_fixed = 3.0
lateral = np.linspace(0.0, 3.0, 40)
euclid_range = np.sqrt(lateral**2 + Z_fixed**2)
pixel_u = 200.0 * lateral / Z_fixed + 160.0

Also note that is depth along the optical axis, not Euclidean distance to the point. Conflating the two is a common source of subtle bugs. For a point well off-axis, the Euclidean range can be substantially larger than the axial depth, and a stereo algorithm, a lidar, and a learned depth network may each report a slightly different one of these quantities by convention. When fusing sensors or comparing predictions against ground truth, always check which convention each source uses.
Depth and Its Disambiguation
Given the ray ambiguity above, how do agents recover depth? There are three broad strategies, and they differ in what they assume and what they guarantee. It is worth being explicit about the source of depth information in each case, because that source determines the failure modes.
- Stereo and multi-view geometry: two cameras with a known baseline see the same point from two positions. The point's depth is the only value consistent with both observations. Geometrically, each pixel defines a ray. For a correct correspondence from compatible calibrated pinhole cameras, noise-free rays meet at the scene point in a nondegenerate configuration. For a rectified pair with parallel optical axes and a horizontal baseline , horizontal disparity and focal length in pixels obey . Arbitrary calibrated viewpoints instead require triangulation from their projection matrices. The rectified-pair inverse relationship has important operational consequences. A larger baseline improves first-order depth precision when focal length in pixels and disparity uncertainty are controlled. More pixels across the same field of view can also help when optical quality, calibration, correspondence quality, and disparity uncertainty in pixel units do not degrade. Resolution alone cannot recover information lost to blur or matching error. Distant points have tiny disparity and are hard to localize, which is why stereo depth maps are accurate nearby and noisy in the distance.
The inverse relationship is easiest to trust when plotted: the next cells evaluate the stereo equation over a depth range for two baselines.
import numpy as np
f_px = 200.0
depth_grid = np.linspace(1.0, 30.0, 200)
## First-order depth-error estimate from fixed half-pixel disparity uncertainty,
## using differential error propagation for Z = fB/d.
disp_err_px = 0.5
depth_err_B05 = depth_grid**2 * disp_err_px / (f_px * 0.3)
depth_err_B08 = depth_grid**2 * disp_err_px / (f_px * 0.8)
There is also an engineering trade: a wide baseline improves far-field depth but worsens the correspondence problem for near objects, which appear at very different positions in the two images and may not be visible to both cameras at all. Finally, stereo requires that the same surface point be identifiable in both views, which fails on textureless surfaces, specular highlights, and occluded regions. This is the correspondence problem. Once calibration, baseline, resolution, and synchronization are adequate for the operating regime, correspondence quality is a major remaining source of stereo error; whether it dominates depends on that regime.
- Active sensing: depth cameras may use structured light or optical time of flight, lidar times the return of laser light, and radar estimates range from radio-frequency propagation delay, beat frequency, or phase. A time-of-flight lidar sends a pulse and measures the return delay; a structured-light camera projects a known pattern and infers depth after decoding or matching the pattern in the camera image. These sensors replace unconstrained passive-image correspondence with controlled pattern decoding, propagation-time, beat-frequency, or phase estimation. In return, they introduce cost, weight, range, interference, and material-dependent failure modes. They also return range rather than depth in many cases, so converting to a camera-frame depth map requires care with conventions. For a world model, active sensors change the learning problem: when depth is measured rather than inferred, the model's depth predictions can be trained against direct metric measurements rather than only against multi-view consistency. That is a substantially stronger supervision signal, although it still contains calibration error, measurement noise, missing returns, and sensor-specific failure modes.
- Learned monocular depth: a network predicts depth from a single image by exploiting learned priors about the world: sizes of familiar objects, ground-plane regularities, shading and texture gradients. The network is effectively performing inference over the missing dimension using statistical knowledge of what scenes usually look like. This is powerful but is fundamentally prior-driven, and we must be precise about what it guarantees. Where the prior holds, predictions can be good; where it is violated, an unusual object, an unfamiliar scale, an adversarial texture, an ordinary point-estimate output can be confidently wrong without signaling the problem. Uncertainty-aware models can expose predictive uncertainty, confidence, or invalid regions, but those signals are not guaranteed to remain calibrated under distribution shift.
A single calibrated image does not determine a scene merely up to one global scale: each observed pixel defines a ray with its own unknown depth. Perspective makes that per-point ambiguity immediate because any positive multiple of a camera-frame point has the same ratios and . Monocular depth networks therefore produce depth only to the extent that learned scene priors or additional constraints couple those otherwise independent ray depths. For a world model feeding a planner, this distinction is not academic: "the door is 0.9 m wide" and "the door looks like the doors that were 0.9 m wide in training" are different claims with different failure modes. The second can fail when object sizes or camera configurations differ from training, without the image alone exposing the error. A single global scale ambiguity arises in calibrated monocular multi-view reconstruction under the usual rigid-scene and known-calibration assumptions: scaling the reconstructed scene and camera translations together leaves the images unchanged. In that setting, one known metric anchor, such as a known object size or an inertial or wheel-odometry scale, can fix the global factor. It still cannot resolve arbitrary per-pixel depths in an otherwise unconstrained single image.
Triangulation generalizes stereo to arbitrary camera arrangements. Given two projection matrices and a pixel correspondence, the 3D point can be estimated from the two projection constraints. With noisy pixels the rays generally fail to meet exactly, but different estimators optimize different objectives: the linear DLT code below minimizes an algebraic homogeneous residual, while closest-ray and nonlinear reprojection-error methods solve different geometric objectives. Let's make the DLT construction concrete with code.
We define a pinhole camera, project a small cube of 3D points from two poses, and inspect the parallax between the two images.
import numpy as np
def look_at(camera_pos, target, up=np.array([0.0, 0.0, 1.0])):
"""Build a world-to-camera rotation, choosing a fallback up axis if needed."""
forward = target - camera_pos
forward_norm = np.linalg.norm(forward)
if forward_norm < 1e-12:
raise ValueError("camera_pos and target must be distinct")
forward = forward / forward_norm
right = np.cross(up, forward)
right_norm = np.linalg.norm(right)
if right_norm < 1e-12:
# Choose the coordinate axis least aligned with forward. This preserves
# a valid frame when the requested up vector is collinear with view.
fallback = np.eye(3)[np.argmin(np.abs(forward))]
right = np.cross(fallback, forward)
right_norm = np.linalg.norm(right)
right = right / right_norm
up_cam = np.cross(forward, right)
return np.stack(
[right, up_cam, forward], axis=0
) # camera axes in world coords
def project(points_world, R, t, K):
"""Project visible world points; camera-frame optical depth must be positive."""
points_cam = (R @ points_world.T).T + t
z = points_cam[:, 2]
if np.any(z <= 0):
raise ValueError(
"Pinhole projection requires positive camera-frame depth Z."
)
uv = (K @ (points_cam / z[:, None]).T).T
return uv[:, :2], z
## Intrinsics: 320x240 image, focal length 200 px, principal point at the center
K = np.array([[200.0, 0.0, 160.0], [0.0, 200.0, 120.0], [0.0, 0.0, 1.0]])
camera_cx = K[0, 2]
camera_cy = K[1, 2]
## A cube of 3D points near the origin
corners = np.array(
[[x, y, z] for x in (-0.4, 0.4) for y in (-0.4, 0.4) for z in (0.1, 0.9)],
dtype=float,
)
## A rectified stereo pair: parallel optical axes and a lateral 0.8 m baseline.
## This holds camera orientation fixed so disparity isolates inverse depth.
pos1 = np.array([-0.4, 0.0, -2.5])
pos2 = np.array([0.4, 0.0, -2.5])
R1, R2 = np.eye(3), np.eye(3)
t1, t2 = -R1 @ pos1, -R2 @ pos2
uv1, z1 = project(corners, R1, t1, K)
uv2, z2 = project(corners, R2, t2, K)Cube corner depths from camera 1: 2.600 to 3.400 m Mean pixel shift between the two views: 54.3 px
Every corner shifts between the two views, and the shift is larger for nearer points. That shift is exactly the signal stereo reconstruction exploits. Note also the translation trick used in the code: the world-to-camera translation is , where is the camera's position in world coordinates, which is the standard way to convert a camera pose into the transform the projection equations need. Now let's invert the projection: given noisy pixel correspondences in both views and known calibration, recover each point's depth by linear triangulation. For each view with projection matrix , a world point must satisfy and , where is row . Each such equation states that the observed pixel lies on the ray predicted by the projection, which, after clearing the perspective division, becomes a homogeneous linear equation in the unknown 3D point. Two views give four equations; the homogeneous least-squares solution is the right singular vector associated with the smallest singular value.
def projection_matrix(R, t, K):
return K @ np.hstack([R, t.reshape(3, 1)])
def triangulate(uv_a, uv_b, P_a, P_b):
"""Linear triangulation: one 3D point per pair of pixel correspondences."""
points = []
for (u_a, v_a), (u_b, v_b) in zip(uv_a, uv_b):
A = np.vstack(
[
u_a * P_a[2] - P_a[0],
v_a * P_a[2] - P_a[1],
u_b * P_b[2] - P_b[0],
v_b * P_b[2] - P_b[1],
]
)
_, _, Vt = np.linalg.svd(A)
points.append(Vt[-1][:3] / Vt[-1][3])
return np.array(points)
P1, P2 = projection_matrix(R1, t1, K), projection_matrix(R2, t2, K)
## Realistic pixel noise, then triangulate and compare against true depth
rng = np.random.default_rng(0)
uv1_noisy = uv1 + rng.normal(0, 0.5, uv1.shape)
uv2_noisy = uv2 + rng.normal(0, 0.5, uv2.shape)
X_est = triangulate(uv1_noisy, uv2_noisy, P1, P2)
depth_est = ((R1 @ X_est.T).T + t1)[:, 2]
err_cm = np.abs(depth_est - z1) * 100Mean triangulated depth error: 2.34 cm at 3.0 m mean depth Worst corner error: 6.96 cm
With the seeded half-pixel noise, two rectified views 0.8 m apart produce about 2.3 cm mean depth error and about 7 cm worst-corner error in this synthetic setup. The calculation uses no learned object-category prior, but it still assumes calibrated camera models, known poses, correct correspondences, a compatible scene across the observations, and the DLT noise objective. This is the quantitative backbone behind "multi-view consistency": agreement across viewpoints is strong evidence of correct structure when calibration, correspondences, noise sources, and the scene model are sufficiently independent and valid. Shared calibration, synchronization, correspondence, or rolling-shutter errors can instead make several views agree on the same wrong structure. We will return to this when we discuss why novel-view consistency is such a valuable training signal for world models. The same arithmetic also reveals the weakness: the errors here grow roughly with the square of depth and inversely with baseline, so the same pipeline at 25 m with a 0.8 m baseline would be far less precise, which is why outdoor systems demand longer baselines or active sensors.
