explainer
Homogeneous transformations: map sensor coordinates into the world
Combine rotation and translation in one matrix. Follow a sensor-to-robot-to-world frame chain, distinguish points from displacements, and calculate the inverse.
What you will learn
- Build a planar homogeneous matrix from a frame's orientation and origin.
- Compose two coordinate mappings in the correct order.
- Explain why translation affects points but leaves free displacements unchanged.
- Calculate a rigid transform's inverse and check a coordinate round trip.
Before you start
A sensor reading gives a location relative to the sensor. A robot's map usually needs that location in world coordinates. A homogeneous transformation combines the sensor's orientation and position into one matrix calculation.
This lesson follows one synthetic planar example through three frames. You will map a point into the world, recover its sensor coordinates, and check why a displacement needs different treatment.
Name the source and destination frames
Use T_AB to map B-coordinates into A-coordinates. The second label names the source; the first names the destination. All coordinate vectors are columns, even when we write their entries horizontally to save space.
For a point with coordinates p_B, the ordinary two-coordinate formula is:
p_A = R_AB p_B + t_AB
The columns of R_AB describe B's unit axes in A-coordinates. The translation t_AB locates B's origin in A-coordinates. These two quantities specify the pose of frame B relative to frame A.
Read the coordinate-frame lesson if the same point having different coordinate pairs is unfamiliar. Here the mapping changes how we describe a point. We will identify a separate example of physically moving a point when we discuss operation order.
Use the same length unit for p_B and t_AB. Our diagram uses meters and right-handed planar axes: world x points right, world y points up, and positive angles turn counterclockwise. Modern Robotics uses the same destination–source subscript convention.
Add one coordinate to include translation
A two-by-two matrix alone sends the origin to the origin. It cannot add an arbitrary translation. Append a dimensionless 1 to a point, and add a column for the translation:
T_AB =
[cos θ, −sin θ, t_x;
sin θ, cos θ, t_y;
0, 0, 1]
[x_A, y_A, 1]ᵀ = T_AB [x_B, y_B, 1]ᵀ
Semicolons separate matrix rows. The first row calculates x_A = cos θ x_B − sin θ y_B + t_x. The second calculates y_A, and the last row keeps the appended coordinate equal to 1.
The extra entry lets matrix multiplication carry both operations. It does not add a physical spatial dimension. This normalized homogeneous representation describes a two-dimensional point with three numbers.
Work through a sensor-to-world chain
Name the frames W for world, R for robot, and S for sensor. The robot's origin is (2, 1) meters in W, and its axes turn 90° counterclockwise from W. The sensor's origin is (1, 0) meters in R, with axes parallel to R.
Using the quarter-turn matrix from rotation matrices, the two mappings are:
T_WR = [0, −1, 2; 1, 0, 1; 0, 0, 1]
T_RS = [1, 0, 1; 0, 1, 0; 0, 0, 1]
Let the sensor report point p_S = (1, 1) meters. T_RS first adds the sensor's offset, giving p_R = (2, 1). T_WR then rotates that pair to (−1, 2) and adds (2, 1), giving p_W = (1, 3).
Combine those steps with the rightmost mapping acting first:
T_WS = T_WR T_RS
T_WS = [0, −1, 2; 1, 0, 2; 0, 0, 1]
T_WS [1, 1, 1]ᵀ = [1, 3, 1]ᵀ
The adjacent R labels match. T_RS delivers robot coordinates, which T_WR accepts. This gives a useful check before doing any arithmetic.
The combined translation is t_WS = R_WR t_RS + t_WR. In numbers, it is (0, 1) + (2, 1) = (2, 2). The sensor offset starts in robot coordinates, so it must rotate into world coordinates before the translations can add.
Move the frames and inspect the result
The experiment begins with the worked example. Its three matrix tables show the two individual mappings and their product. Compare the direct world result with the result calculated through R.
Choose Aligned axes. The sensor point reaches world coordinates (4, 2), because both rotations become identities. Choose Sensor turned 90° to add a quarter-turn at the sensor; the world point becomes (1, 1).
The sensor components stay (1, 1) so you can isolate the effect of changing frames. Sliders change the robot's world pose and the sensor's pose within the robot. The sensor's robot y-offset stays zero in this bounded model.
The diagram uses equal horizontal and vertical scales. Every frame has a marked origin and two one-meter axis arrows; solid arrows show positive x, and dashed arrows show positive y. The numeric results remain readable when frames overlap.
This describes ideal geometry at one instant. A real sensor mount needs calibration, and a moving robot needs a pose estimate at the reading's time. Matrix multiplication carries the values supplied to it; it cannot correct an inaccurate offset or timestamp.
Give a displacement a final zero
A point has a location relative to an origin. A free displacement describes the difference between two points. Translating the coordinate origin changes both point coordinates equally, so their difference cancels the translation.
Use a final 0 for that displacement:
[d_A, 0]ᵀ = T_AB [d_B, 0]ᵀ
d_A = R_AB d_B
For the default chain, a sensor displacement d_S = (1, 1) maps to d_W = (−1, 1). Its length stays √2 meters. Giving those same components a final 1 produced the point (1, 3), because that calculation included the sensor origin at (2, 2).
Choose Displacement in the widget, then change either origin. The world components stay unchanged. Change an angle and the displacement turns; the amber arrow uses the world origin as a convenient drawing location.
Kris Hauser's coordinate-transformation chapter explains the final 1 and 0 convention. The displacement here is a geometric vector at one instant. Computing a point's velocity relative to moving frames requires accounting for those frames' motion too.
Return to the source frame
To recover p_B, subtract t_AB and apply the inverse rotation. A proper rotation satisfies R⁻¹ = Rᵀ, so:
p_B = R_ABᵀ(p_A − t_AB)
T_BA = T_AB⁻¹ = [R_ABᵀ, −R_ABᵀt_AB; 0, 1]
The inverse translation is −Rᵀt. Negating t alone would leave its components in the wrong frame. Transposing the entire homogeneous matrix also fails in general: it moves the translation into the bottom row.
For our combined T_WS, R is a quarter-turn and t = (2, 2). The inverse uses a −90° rotation and translation (−2, 2):
T_SW = [0, 1, −2; −1, 0, 2; 0, 0, 1]
T_SW [1, 3, 1]ᵀ = [1, 1, 1]ᵀ
The widget's inverse result performs this round trip. Also check T_SW T_WS = I₃. The transpose and inverse lesson explains why the rotation block can use a transpose while the full transform needs the extra translation calculation.
Check why operation order matters
Consider a separate task: physically move point (1, 0) in a fixed frame. Let Q rotate 90° about that frame's origin, and let D translate by (2, 0) along its x-axis.
- D Q rotates first: (1, 0) becomes (0, 1), then (2, 1).
- Q D translates first: (1, 0) becomes (3, 0), then (0, 3).
The two results differ. In a frame chain, labels impose a further requirement: the output frame of one mapping must match the next mapping's input frame. Reversing T_WR T_RS breaks that coordinate path, even though the two three-by-three arrays can still multiply.
Keep frame labels attached to data in code and notes. They act like units: compatible matrix sizes alone do not establish that a calculation has the intended meaning.
Extend the rigid transform to three dimensions
The same block structure works in space. R becomes three-by-three, t has three entries, and the homogeneous matrix becomes four-by-four:
T =
[r₁₁, r₁₂, r₁₃, t_x;
r₂₁, r₂₂, r₂₃, t_y;
r₃₁, r₃₂, r₃₃, t_z;
0, 0, 0, 1]
A point uses (x, y, z, 1). A displacement uses (d_x, d_y, d_z, 0). Composition and the inverse formula carry over with the larger blocks.
For a proper rigid transform, RᵀR = I and det(R) = +1. The bottom row must be (0, 0, 0, 1). The rotation preserves lengths and angles, and the full mapping preserves distances between points.
These matrices form SE(3); the planar versions form SE(2). A general homogeneous matrix can also encode scaling, shear, or projective effects. The fixed bottom row alone does not prove rigidity: the rotation block must satisfy its constraints.
Lie groups and Lie algebras explains what this group structure gives us: valid compositions, inverses, and a tangent space for local motions. To build a finite pose change from a constant body motion, continue with exponential and logarithm maps.
A twist combines angular and linear motion. Expressing the same twist in another frame also accounts for the shift between frame origins. The adjoint transformation lesson derives that conversion and checks the velocity of a shared physical point.
This lesson uses only proper rigid transforms. Its angle controls construct valid planar rotations. For matrices estimated from measured data, check the rotation constraints with an appropriate numerical tolerance. Cornell's kinematics notes develop the spatial block form and coordinate convention.
A spatial pose can also store its orientation as a unit quaternion, alongside a translation vector. Converting the quaternion to R gives the rotation block above. The quaternion's component order and the pose's source and destination frames still need explicit labels.
Check the chain in Python
This example needs only Python's standard library. The chosen quarter-turn matrices contain integers, so the example can verify the results exactly. The inverse helper assumes a valid planar rigid matrix with bottom row (0, 0, 1).
def multiply(a, b):
return [
[sum(x * y for x, y in zip(row, column))
for column in zip(*b)]
for row in a
]
def apply(matrix, vector):
return tuple(sum(a * b for a, b in zip(row, vector))
for row in matrix)
def inverse_rigid_2d(t):
r00, r01, tx = t[0]
r10, r11, ty = t[1]
return [
[r00, r10, -(r00 * tx + r10 * ty)],
[r01, r11, -(r01 * tx + r11 * ty)],
[0, 0, 1],
]
t_wr = [[0, -1, 2], [1, 0, 1], [0, 0, 1]]
t_rs = [[1, 0, 1], [0, 1, 0], [0, 0, 1]]
t_ws = multiply(t_wr, t_rs)
t_sw = inverse_rigid_2d(t_ws)
point_s = (1, 1, 1)
point_r = apply(t_rs, point_s)
point_w = apply(t_ws, point_s)
assert point_w == apply(t_wr, point_r)
assert multiply(t_sw, t_ws) == [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
print("T_WS:", t_ws)
print("Point in R:", point_r)
print("Point in W:", point_w)
print("Displacement in W:", apply(t_ws, (1, 1, 0)))
print("Back in S:", apply(t_sw, point_w))
turn = [[0, -1, 0], [1, 0, 0], [0, 0, 1]]
shift = [[1, 0, 2], [0, 1, 0], [0, 0, 1]]
print("Rotate then translate:", apply(multiply(shift, turn), (1, 0, 1)))
print("Translate then rotate:", apply(multiply(turn, shift), (1, 0, 1)))
Expected output:
T_WS: [[0, -1, 2], [1, 0, 2], [0, 0, 1]]
Point in R: (2, 1, 1)
Point in W: (1, 3, 1)
Displacement in W: (-1, 1, 0)
Back in S: (1, 1, 1)
Rotate then translate: (2, 1, 1)
Translate then rotate: (0, 3, 1)
Try it yourself
Exercise 1. Keep the default robot and sensor poses. The sensor now reports point (2, 0) and displacement (2, 0). Find both world-coordinate results and explain the difference.
Show solution: include translation only for the point
The combined rotation sends (2, 0) to (0, 2). Add the sensor origin (2, 2) to obtain the point p_W = (2, 4).
The displacement becomes d_W = (0, 2). Their homogeneous results are (2, 4, 1) and (0, 2, 0). The final zero removes the translation term from the displacement calculation.
Exercise 2. T_AB has a 90° rotation and translation (3, −1). Find its inverse translation. Then map p_B = (1, 2) into A and back into B.
Show solution: express the inverse offset in B
The inverse rotation turns −90°. Applying it to t = (3, −1) gives Rᵀt = (−1, −3), so the inverse translation is (1, 3).
Forward mapping rotates (1, 2) to (−2, 1), then adds (3, −1), giving p_A = (1, 0). The inverse rotates (1, 0) to (0, −1), then adds (1, 3), recovering p_B = (1, 2).
Sources and further study
- Modern Robotics: Homogeneous Transformation Matrices. Kevin Lynch and Frank Park explain frame mappings, composition, and SE(3).
- Kris Hauser: Coordinate Transformations. Rigid-transform inverses, point and displacement representations, and frame-label checks.
- Cornell Robot Planning and Autonomy Lab: Kinematics. Pages 63–64 develop homogeneous rigid transformations and the final-coordinate convention.