Kinematics & Motion
Coordinate Frames and Transforms
Every robot carries a dozen coordinate frames. Learn what a frame is, how to name them without getting confused and the one rule that makes chaining transforms safe.
intermediate · 13 min read
Lesson content
"The cup is 40 cm away."
Away from what? The camera? The gripper? The base of the arm? The corner of the room?
A position means nothing on its own. It only means something next to a frame of reference, and a robot carries a lot of those. One for every link. One for every sensor. One for the map. One for the world.
Keeping them straight is not tedious admin around the real work. It is most of what a robotics stack does all day.
Definition
Coordinate frame
An origin and three axes at right angles, fixed to something solid. Coordinates only mean anything next to one.
Formal definition
A right-handed Cartesian coordinate system attached to a rigid body, given by an origin point and three perpendicular unit axis vectors.
Like giving directions in a town you do not know
Someone stops you and asks where the station is.
"Two streets east" only helps if they know which way east is. "Right behind you" only helps if you can see which way they are facing. "Next to the cathedral" helps anyone who can find the cathedral.
All three answers point at the same station. Each one is measured from something different.
A robot has this problem every second of its life, and it has to be exact about it.
Why not just pick one frame and use it for everything?
Because every part of the robot naturally measures in its own terms.
A camera reports a detection in pixels, then in metres from the lens, because that is genuinely all it can see. A joint encoder reports an angle about its own shaft. A LiDAR reports ranges from its own centre. The map does not move at all, because it is nailed to the building.
Each frame is the natural home for one kind of measurement. The transforms between them are how the parts of the robot talk to each other.
Here is the part worth remembering. Trying to avoid frames does not remove a single conversion. It only hides them inside code where nobody can check them.
The notation is what trips people up, so it is worth pinning down once. A transform is written with the target frame above and the source frame below: ᴬT_B, said out loud as "B written in A's numbers".
The math
The raised label is the frame you end up in. The lower label is the frame you started in. Multiplying chains them together, and the labels in the middle have to cancel.
ᴬP = ᴬT_B · ᴮP
ᴬT_C = ᴬT_B · ᴮT_C
ᴮT_A = (ᴬT_B)⁻¹
- ᴬT_B
- rewrites a point's numbers from frame B into frame A
- ᴮP
- a point, measured in B
- the cancelling B
- the two middle labels must match, or the multiplication means nothing
With real numbers
A camera on a robot in a building:
ᴹᵃᵖP_cup = ᴹᵃᵖT_base · ᴮᵃˢᵉT_cam · ᶜᵃᵐP_cup.
Read it from the right: the cup in camera numbers, rewritten in base numbers, rewritten in map numbers. cam cancels, base cancels and what is left is exactly the label you wanted.
If the labels do not cancel, you have multiplied in the wrong order. That one check catches nearly every frame bug before it ever reaches the robot.
Frames are arranged as a tree. Every frame has exactly one parent and the tree has a single root. That is not housekeeping for its own sake. It guarantees there is exactly one route between any two frames, so a lookup can only ever have one answer.
| Frame | Fixed to | Does it move? | What it is for |
|---|---|---|---|
| map | The building | Never | Globally correct, but it jumps when the robot works out where it really is |
| odom | Wherever the robot started | Never | Smooth and continuous, but it drifts as the day goes on |
| base_link | The robot's body | Yes | The robot's own reference point |
| base_footprint | The ground under base_link | Yes | Flat-world navigation happens in this one |
| laser / lidar_link | The scanner | With the robot | Where scans are measured from |
| camera_link | The camera housing | With the robot | x forward, the ROS convention |
| camera_optical_frame | The camera sensor itself | With the robot | z forward, the vision convention |
Two of these describe a camera that has only one lens. That is not a mistake, and the example further down explains why.
Why keep both map and odom, when both say where the robot is?
Because the two have opposite strengths and a robot needs both.
odom to base_link comes from the wheels and the IMU. It is smooth, continuous and fast, so a controller can safely take its derivative. It also drifts. After ten minutes it can be metres out.
map to odom is the correction that localisation publishes. It makes the total answer globally right. It also jumps, every time the localiser changes its mind.
Now picture a speed controller reading a pose that jumps 30 cm in a single tick. It sees an enormous velocity and commands something violent.
So the stack keeps the two apart on purpose. Controllers read odom. Planners and goals use map. The localiser quietly adjusts the link between them.
Once that split makes sense, most of ROS navigation stops feeling mysterious.
Example
The optical frame trap
There are two competing conventions in robotics and your robot uses both at once.
ROS says x forward, y left, z up.
Computer vision says z forward, x right, y down.
That is why a camera in ROS gets two frames. camera_link follows the ROS convention, camera_optical_frame follows the vision convention and a fixed 90° rotation sits between them.
Publish a detection in the wrong one and your object turns up rotated by 90°, usually somewhere below the floor.
This is one of the most common and most baffling bugs in robot perception. It is also completely avoidable. Name the frame explicitly, every single time.
- Right-handed frame
Point your right hand's fingers along x, curl them towards y and your thumb points along z.
Every frame in robotics is right-handed. A left-handed one mirrors everything and produces errors that are very hard to spot.
- Static transform
A transform that never changes, such as a sensor bolted to a chassis.
Published once and latched on /tf_static. It has to be latched, or any node that starts later never hears about it.
- Dynamic transform
A transform that changes as the robot moves, such as a joint or the robot's place in the map.
Published continuously with a timestamp, so anyone reading it can ask for the transform at the moment their data was taken.
- Frame timestamp
The moment a transform was true.
On a moving robot, transforming a measurement with the current transform instead of the measurement's own timestamp puts the answer in the wrong place.
- Tool centre point frame
A frame at the tip of the gripper rather than at the flange it bolts to.
So that motion commands refer to the place where the work actually happens.
Reading ᴬT_B as "transform from A to B".
It rewrites numbers from B into A. Fix that reading once and most frame bugs disappear with it.
Giving one frame two parents.
The structure has to be a tree. Two parents makes the route between two frames ambiguous, and tf will complain about it, quite rightly.
Looking up a transform at the current time for data that is already a moment old.
Use the data's own timestamp. On a robot moving at 1 m/s, being 100 ms late puts the answer 10 cm away from the truth.
Key takeaway
- A position is meaningless without a frame, and a robot carries a dozen of them, one per link and sensor.
- ᴬT_B rewrites a point's numbers from B into A. The raised label is where you end up.
- Chain transforms by multiplying so the inner frame labels cancel. If they do not cancel, the order is wrong.
- Frames form a tree with one parent each, so every lookup has exactly one answer.
- map is globally right and jumps. odom is smooth and drifts. Controllers use odom, planners use map.
- Always transform with the data's own timestamp, and always name the frame out loud.
Finished this lesson?
Guest progress is saved on this device.