Navigation · Theory · M9
M9-T3 — Frames, Timing and Simulation
A surprising share of navigation failures are not about planning at all: they are about where a coordinate frame is, and what time it is.
The planner can be perfect and the robot can still stop because a transform is half a second old, or drive towards a door that is not there because the map frame jumped. Frame and clock problems look like navigation bugs and are found with different tools. This lesson lists the ones that cost the most time, and how simulation can find them before a robot does.
Learning objectives
After this lesson, you should be able to:
- Use the map, odom and base_link frames correctly and say which node publishes each transform.
- Explain how a mixed simulated and wall clock, a transform tolerance that is too tight, or a jump in map to odom shows up as a navigation failure.
- Estimate the effect of latency and odometry drift on position, and set a transform tolerance sensibly.
- Plan a simulation test that finds frame, clock and configuration errors before hardware, and say what simulation cannot show.
The intuition
Every question a robot asks is “where is X, relative to me, now?”. The answer needs two things: a chain of transforms that connects X’s frame to the robot’s, and a timestamp at which that chain is valid. A tree of frames provides the first; a clock provides the second. If either is wrong, a perfectly good algorithm gets a wrong answer and still obeys it.
The convention (REP 105) is a chain map → odom → base_link. The odom frame is smooth but drifts: wheel odometry is continuous, and its error grows. The map frame is globally consistent but is corrected in jumps when localization improves its estimate. The transform map → odom is the correction, published by localization (AMCL or SLAM); odom → base_link is published by odometry; sensor frames hang off base_link through the robot description. Planning is done in map, the local controller reacts in odom, so the controller is not startled by a localization correction.
The mathematics
A transform lookup
A lookup asks for the transform from frame A to frame B at time t. The transform buffer holds a history of each link. A lookup succeeds when each link in the chain has data at, or around, t; it fails with an extrapolation or timeout error when it does not. A transform tolerance is how much older than “now” the data may be and still be accepted.
Where the time goes
| Source | Typical effect | Consequence |
|---|---|---|
| Sensor and driver latency | 10 to 100 ms | The scan describes where the robot was |
| Transform publication | One update period of the publisher | map → odom may be seconds old if localization is slow |
| Controller period | 1 / frequency | The command is held for the whole period |
| Command chain | Smoother, monitor, driver, motor | Command latency τ: 100 to 400 ms is common on networks |
| Wireless link | Tens to hundreds of ms, with spikes | Stale data and bursts |
Frames and what moves them
| Frame | Behaviour | Used for |
|---|---|---|
| map | Fixed to the world; jumps when localization corrects | Goals, global plans, the static map |
| odom | Continuous, drifts without bound | Local costmap, local control |
| base_link | Moves with the robot | Footprint, sensor placement |
| sensor frames | Fixed to the robot by calibration | Sensor data into the costmaps |
Simulated time
A simulator publishes its own clock on a topic (/clock); a node with the parameter use_sim_time set to true reads that instead of the computer’s clock. It must be true for every node of the system, because a message stamped in simulated time and a node comparing it with wall time disagree by decades. The simulator can also run slower or faster than real time (a real-time factor), so anything that measures time must use the same clock.
What a test in simulation can find
- Configuration contradictions: clocks, frames, footprints, plugin names, velocity limits.
- Wiring errors: topics that do not connect, remaps, lifecycle nodes that never activate.
- Behaviour logic: recoveries, retries, goals that cannot succeed.
- Regression: a change that makes a scenario fail that passed yesterday.
It cannot find sensor noise you did not model, floors that slip, wireless links that stall, or people who do not behave as scripted.
A worked example
Two clocks. A simulation has run for 125.4 s. Every simulated message is stamped with times near 125 s. One node (the navigator) is launched with use_sim_time false and reads the computer clock, 1 760 000 000 s. It asks for the transform from map to base_link at its “now”. The buffer’s newest data is at 125.4 s, so the lookup is about 1.76 × 109 s in the future: it times out. In the log you see “timed out waiting for transform”, though every transform is being published. The fix is one parameter, set in one place for the whole launch (project M9-L4, experiment D).
Latency becomes distance. A robot drives at 0.5 m/s with 0.2 s of delay between a scan and the command that uses it:
With a 0.10 m delay error and a 0.12 m clearance requirement, almost the whole margin is consumed by latency; the costmap is describing where the obstacle was relative to where the robot was. This is why transform tolerance and controller frequency are safety parameters.
A jump in map to odom. After 30 m, wheel odometry has drifted 1%: 0.30 m. When localization corrects it, map → odom changes by 0.30 m within one update. A global path in map is now 0.30 m away from the robot in the controller’s frame: a step of 0.30 m in cross-track error, which a pure-pursuit controller answers with a sudden turn (compare incident 3 of lab M1-L1). Planning in map and controlling in odom is the standard remedy: the controller follows the path as the local costmap sees it and absorbs the correction smoothly, and localization is configured so that corrections are small and frequent, not large and rare.
A compute budget. A controller at 20 Hz has 50 ms per cycle. If one cycle costs 60 ms of computer time, the achieved rate in real time is 1 / 0.060 = 16.7 Hz, not 20: the command is held longer than designed and the lookahead margin shrinks. A simulation running at real-time factor 1 shows this; a simulation running at half speed hides it, and the first real-robot trial reveals it.
Engineering implications and limitations
The five questions
When navigation fails with transform errors, ask in order: is there a clock (/clock) and does every node use it? Is every link of the chain being published, and by whom? How old is the newest data of each link? Do the frame names in the parameter files match the robot description (base_link versus base_footprint)? Does the transform tolerance cover the actual latency, without hiding a broken publisher?
Do not hide a fault with tolerance
Increasing the transform tolerance until the errors stop is tempting. It makes the robot act on data that is seconds old. Find out why the data is late first.
One launch, one clock
Set use_sim_time from a single launch argument that reaches every node, including the ones you wrote. A single node that forgets is hard to see in the logs and easy to find with ros2 param get.
Simulation as a gate
Run a fixed set of scenarios in simulation on every change to the parameters or the behaviour tree: the narrowest door, a blocked corridor, a goal that cannot succeed, a restart in the middle of a mission. Treat a failure as you would a failing unit test. Then run the physical trial for the things simulation cannot show (lesson M9-T4 on evaluation).
Parameter names change
The parameters mentioned here (use_sim_time, transform_tolerance, global_frame, robot_base_frame) are stable names in Nav2 at the time of writing, but defaults and where they appear can change between releases. Check the documentation for your distribution.
Common misconceptions
“Transform errors mean TF is broken.”
They usually mean a clock, a name or a publisher is wrong. TF is doing exactly what it was asked to do.
“If it works in simulation it will work on the robot.”
Simulation finds configuration and logic errors. It does not model your sensors, floor, wireless link or people, and its timing can be kinder than real time.
“Increase the transform tolerance until the warnings stop.”
That hides late data instead of fixing it, and the robot then acts on a stale picture of the world.
“Planning and control should both use the map frame.”
Using odom for local control isolates the controller from localization jumps; the global plan is in map, and the two are connected through the transform.
Engineering takeaways
- The frame chain is map → odom → base_link; localization publishes the first link, odometry the second.
- A lookup needs data at the requested time: mixed clocks or late publishers break it, with errors that look like navigation faults.
- Latency becomes distance: v × delay. Compare it with your clearance.
- Set use_sim_time on every node from one place; do not widen tolerances to hide late data.
- Simulation finds configuration, wiring and logic faults; it cannot replace the real sensors, floor and link.
References and further reading
The standards for coordinate frames and the Nav2 setup documentation.
- ROS Enhancement Proposal REP 105, “Coordinate Frames for Mobile Platforms”, ros.org/reps/rep-0105.html.
- Nav2 documentation, “Setting Up Transformations” and “Getting Started”, docs.nav2.org (choose your distribution).
- S. Macenski, T. Foote, B. Gerkey, C. Lalancette and W. Woodall, “Robot Operating System 2: Design, architecture, and uses in the wild”, Science Robotics 7(66), 2022.
- T. Foote, “tf: The transform library”, IEEE Conference on Technologies for Practical Robot Applications, 2013.
Knowledge check
Three conceptual questions. Write an answer, then reveal the explanation. Your answers stay in your browser.
Question 1
One Nav2 node is started with use_sim_time false in a simulation. What do you expect to see, and why does it look like a transform problem?
Explanation
Timeouts or extrapolation errors on transform lookups. The node asks for transforms at wall-clock times that do not exist in the simulated-time buffer, so lookups fail though the transforms are published. The fix is use_sim_time true on that node.
Question 2
A robot moves at 0.8 m/s and there is 0.25 s of delay between a scan and the command computed from it. How far does it move in that time, and what does this mean for a 0.15 m clearance requirement?
Explanation
0.8 × 0.25 = 0.20 m, larger than the 0.15 m clearance. The robot reacts to where an obstacle was; speed must come down, the delay must shrink, or the monitor must stop the robot earlier.
Question 3
Why is the local costmap placed in the odom frame while the global costmap is in the map frame?
Explanation
odom is continuous, so the local controller is not disturbed when localization corrects the estimate; map is globally consistent, so the global plan and the static map belong there. The transform map → odom connects them.
What’s next
Next: how to show that a navigation system works, with enough runs to believe the answer.
Related content
Nothing below is required to finish this lesson.
- BeforeM9-T2: Tuning a Navigation Stack
Tune inflation, lookahead, tolerances and costmap resolution with a method that changes one thing at a time.
- LabM9-L4: Bring Up Nav2 in Simulation
Run Nav2 on a simulated robot on your own machine, send a goal, and diagnose what you see.
Get notified when the next lesson and new labs are released.
One email per new lesson or lab.