Nav2’s velocity smoother uses its last command in OPEN_LOOP and an odometry estimate in CLOSED_LOOP; compare the age and credibility of those inputs before changing acceleration limits. In our AI-assisted standalone Python model, adding 0.3 seconds of feedback delay changed a synthetic ramp to 0.6 m/s from 1.2 to 4.5 seconds. That illustrates repeated use of an older velocity estimate, not measured Nav2 or robot performance. The official documentation requires high-rate, low-latency odometry relative to the smoothing cycle.
Suppose a mobile robot accelerates away from a shelf with a heavier payload. If actual velocity lags the command, trustworthy odometry can expose the difference. But wheel-based odometry may itself misrepresent base motion during slip; changing feedback mode does not create an independent physical measurement. If odometry also arrives late or is heavily filtered, the smoother may react to where the robot was rather than where it is. Ask which input best describes velocity when the next smoothed command is computed.
Compare feedback against the actual smoothing cycle
Nav2 allows smoothing_frequency to exceed the upstream command rate, interpolating between updates. Compare odometry timing with that smoothing period. A nominal publication rate is insufficient if transport or estimation delay makes samples too old for the cycle that uses them.
| Situation | Better starting mode | Evidence needed before keeping it |
|---|---|---|
| Commands closely track measured motion; odometry is sparse or delayed | OPEN_LOOP |
A trustworthy physical reference supports command tracking across acceleration, braking and turns; delayed odometry alone cannot validate it. |
| Load or surface makes actual speed depart from commands; odometry is prompt | CLOSED_LOOP |
Odometry age stays small relative to the smoother period, and tracking improves without oscillation. |
| Odometry is noisy, delayed or in the wrong frame | Neither mode is validated yet | Repair the estimate or timing first; compare both modes only with a trustworthy measurement. |
For a controlled comparison, keep route, planner, command rate, velocity limits, acceleration limits and payload fixed. Log the incoming planner command, smoother output, odometry estimate and physical reference if one is available. Compare sample age, velocity tracking, overshoot, stopping behavior and timeout transitions. The table offers a test plan, not benchmark results; no robot was run for this article. If the two modes look identical in an easy straight-line run, add a representative turn or load change before deciding that feedback has no effect.
The Nav2 odometry setup material is useful when the estimate itself needs attention. A filtered odom stream can be smoother to plot while still being late; inspect timestamps and frame conventions rather than judging the curve by eye. For broader navigation architecture, the global planner and local controller guide explains where command generation occurs. This page addresses the narrower decision made after a velocity command reaches the smoother.
Trace the velocity estimate before changing acceleration limits
To isolate feedback age, we executed an AI-assisted, standalone Python behavioral model on October 4, 2026. It follows the one-axis constraint branch and feedback selection in Nav2 Jazzy source revision 645abd9. We ran Python 3.12.14 on a CPU; we did not compile or run Nav2’s C++, a ROS node, or a robot.
The constructed plant adopts each output as its speed at the next tick. This deliberately assumes perfect command tracking. Open loop takes the previous output; fresh closed loop takes the current plant state; delayed closed loop takes the state three ticks earlier. Each tick represents 0.1 synthetic seconds, so the added feedback delay is 0.3 seconds. The model starts with a zero-speed history, has no odometry averaging, and treats the target as continuously available.
All three runs use a 0.6 m/s target, 10 Hz updates, +0.5 m/s² acceleration, −1.0 m/s² deceleration, and velocity bounds of −0.4 to +0.6 m/s. These are experiment inputs, not recommended Nav2 settings. We disable cross-axis scaling and set deadband to zero.
| Feedback in the model | Output at 1.2 s | Output first reaches 0.6 m/s | Separate braking run: output first reaches zero |
|---|---|---|---|
| Previous command | 0.600 m/s | 1.2 s | 0.6 s |
| Current synthetic speed | 0.600 m/s | 1.2 s | 0.6 s |
| Synthetic speed delayed three ticks | 0.150 m/s | 4.5 s | 2.1 s |
The braking runs start separately at 0.6 m/s with a zero target. Every time in the table is a logical simulation time, not measured robot response or program runtime.
The delayed estimate repeatedly gives the limiter an older starting speed. During acceleration, it therefore adds the permitted increment to that older value. The difference is more than a one-time 0.3-second shift: feedback participates in every subsequent calculation. Under perfect tracking, fresh feedback and the previous command produced identical traces. Removing only the artificial delay reproduced that control trace; this verifies the model’s dependency on feedback age, not a repair to any deployed robot.
We also checked positive and negative velocity caps and a direction reversal. An input target of 10 m/s produced the same trace as 0.6 m/s, while −10 m/s stopped at the configured −0.4 m/s limit. With initial velocity +0.3 m/s and target −0.3 m/s, the first four outputs were approximately +0.20, +0.10, 0, and −0.05 m/s. That exercises the source’s distinction between slowing through zero and accelerating in the new direction.
Run this Python snippet without external packages to reproduce the three table rows. It prints the output at 1.2 seconds, time to the positive target, and time to zero in the separate braking run:
# Standalone one-axis Python model, not Nav2 or ROS execution.
from math import isclose
def step(current, target):
target = min(0.6, max(-0.4, target))
accelerating = abs(target) >= abs(current) and current * target >= 0
limit = (0.5 if accelerating else 1.0) / 10
return current + min(limit, max(-limit, target - current))
def run(mode, delay=0, target=0.6, initial=0.0):
state = [initial]
outputs = []
for tick in range(60):
estimate = state[-1] if mode == "open" else state[max(0, tick-delay)]
output = step(estimate, target)
outputs.append(output)
state.append(output) # ideal command-following plant assumption
return outputs
def reach(values, target):
return next((i+1)/10 for i, v in enumerate(values)
if isclose(v, target, abs_tol=1e-12))
for name, mode, delay in [("open", "open", 0),
("fresh", "closed", 0),
("delayed", "closed", 3)]:
ramp = run(mode, delay)
brake = run(mode, delay, target=0, initial=0.6)
print(name, round(ramp[11], 3), reach(ramp, 0.6), reach(brake, 0))
assert run("open") == run("closed", 0)
assert run("open", target=10) == run("open", target=0.6)
assert isclose(run("open", target=-10)[-1], -0.4)
For an actual mode comparison, retain the velocity estimate used at each smoother tick alongside its timestamp. Check that changing feedback age, limits, or command history does not silently change the comparison. This model does not include estimator averaging, wheel slip, motor lag, callback scheduling, command timeout, or physical stopping distance. It provides a trace to reason about; the choice of feedback mode still needs the robot’s measurements.
Keep limits and stale-command behavior in the comparison
Changing feedback while leaving limits inconsistent can produce a misleading result. Record the complete velocity, acceleration, deadband, timeout and odometry parameter set from the official page. Check axes and signs against the robot’s kinematics. If the acceleration limits differ between runs, do not attribute the resulting difference to feedback mode.
The documented timeout sends a zero command and then stops publication. Test a missing upstream command under controlled conditions and verify what the downstream controller and hardware actually do. That output behavior does not certify a stopping distance. Similarly, stale odometry is a data and control fault, not evidence that closed loop is automatically safer.
Avoid copying numeric defaults from the Rolling documentation into a different Nav2 distribution. Confirm the parameters and message type on the version deployed to the robot, then compare traces under that version. If closed loop improves tracking with fresh odometry, keep the measurements that justify it. If it hunts or lags, investigate odometry delay and estimator behavior before adjusting the feedback mode again. The decision should be supported by command, output and measured-motion timelines, not a smoother-looking plot alone.