Skip to content

turtlebot3: run the simulator, the map and the pose in the same place - #76

Open
bburda wants to merge 2 commits into
mainfrom
fix/headless-sim-data
Open

turtlebot3: run the simulator, the map and the pose in the same place#76
bburda wants to merge 2 commits into
mainfrom
fix/headless-sim-data

Conversation

@bburda

@bburda bburda commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Description

Three settings had drifted apart in the headless turtlebot3 profile, and together they stopped the
demo from navigating at all. Each one hid the next, so they had to be found in order.

The simulator was never given the world. gz_args was passed as separate tokens, and launch
joins those with no separator, so gz started with one glued argument:

gz sim -r-s-v2/opt/ros/jazzy/share/turtlebot3_gazebo/worlds/turtlebot3_world.world

No sensor data reached ROS, so AMCL never published the map to odom transform, the global costmap
could not activate, planner_server sat in Activating for 68 seconds and the lifecycle manager
gave up on the whole stack:

[global_costmap]: Failed to activate global_costmap because transform from base_link to map
  did not become available before timeout
[lifecycle_manager_navigation]: Failed to bring up all requested nodes. Aborting bringup.

The map was in the wrong place. turtlebot3_world.yaml carried origin: [-1.76, -2.42] while
the map.pgm it points at ships in turtlebot3_navigation2 with origin: [-10.0, -10.0]. The map
landed about eight metres from the robot, so the spawn point at (-2.0, -0.5) sat outside the map
rectangle. This one only bites once the pose below is right: Nav2 plans from the estimated pose, and
the old configuration estimated (0, 0), which was still inside the old bounds. With the pose
corrected and the origin still wrong, every plan is refused before it starts:

[planner_server]: GridBased plugin failed to plan from (-2.00, -0.50) to (-1.00, -0.50):
  "Start Coordinates of(-2.000000, -0.500000) was outside bounds"

AMCL started somewhere the robot is not. It was configured at (0, 0) while the robot spawns
at (-2.0, -0.5). Headless has no RViz to set the pose by hand, so the covariance stayed high and
the robot could not follow a path.

Related Issue

closes #75

Testing

New tests/smoke_test_navigation.sh runs in the existing turtlebot job, on the stack that job
already starts, so it costs no extra build. Measured against a running demo, 10 passed and 0 failed:

bt-navigator, planner-server, controller-server, amcl   all reach active
lifecycle manager                                        brings up every node
anomaly detector                                         registered, so an empty fault list means something
robot                                                    inside the global costmap
sensor origin                                            inside the map
two goals, the second back to the spawn point            accepted, then completed
localization                                             not wrong at error severity

Each fix was then reverted on its own, to check the test fails when the bug comes back:

gz_args back to separate tokens   5 FAIL  lifecycle x2, bringup, goal rejected, goal not completed
map origin back to -1.76,-2.42    3 FAIL  robot outside costmap, sensor outside map, goal failed
initial_pose back to 0,0          2 FAIL  both goals stop completing

The localization check is a guard against AMCL losing the robot altogether, not the check that pins
the initial pose: under that last mutation the goals fail while it still passes. It keys on error
severity on purpose, because a healthy drive touches a covariance of 0.307 against the detector's
warn threshold of 0.300, and failing on that would turn the demo doing its job into a red build.

The second goal is there because the goal checker's xy_goal_tolerance is 0.25 m: with one goal the
robot ends up standing on it, and a second run against the same container reported success without
moving.

The existing tests/smoke_test_turtlebot3.sh still passes 49 of 49 on the same stack, including
after the shared smoke library gained request timeouts.

One thing outside this branch: the spawn point stays configurable through x_pose and y_pose
while the AMCL pose is a fixed value in the parameter file, so overriding the spawn recreates the
mismatch. That coupling predates this change and is not addressed here.

Benchmark baselines for this demo are no longer comparable with older runs, because the simulator
and the navigation stack now actually run.

With all three in place the demo raises its navigation faults for real for the first time: a goal
sent while the stack was misconfigured produced NAVIGATION_GOAL_ABORTED from
/bridge/anomaly_detector/goal_status through the detector itself, not through injection.

Checklist

  • Tested locally
  • README updated (if needed)

The headless profile passed gz_args as separate tokens, and launch joins those
with no separator, so the simulator was started with one glued argument
"-r-s-v2<world>" and never ran the world. No sensor data reached ROS, the global
costmap never got its transform, planner_server hung in Activating, and the
lifecycle manager aborted the whole navigation bringup. Keeping the separator
inside the string is the form ros_gz_sim expects.

The map yaml carried origin [-1.76, -2.42] while the map.pgm it points at ships
with [-10.0, -10.0], so the map sat about eight metres away from the robot. The
spawn point then fell outside the global costmap and every plan was refused with
"Start Coordinates ... was outside bounds".

AMCL was configured to start at (0, 0) while the robot spawns at (-2.0, -0.5).
Headless has no RViz to correct that by hand, so the covariance stayed high and
the robot could not follow a path.

Add tests/smoke_test_navigation.sh, which asserts the lifecycle nodes reach
active, the robot is inside the costmap, a goal completes and localization stays
certain. It runs in the existing turtlebot job, on the stack that job already
starts. The other smoke test deliberately does not navigate, which is how all
three of these could ship together unnoticed.
The localization check passed whenever the fault list could not be read, so an
unreachable gateway looked like healthy localization, and it read the whole fault
list rather than this run's, which matters because the default profile confirms
on one event and never heals: one uncertain moment at startup would have failed
every later run against the same container. It now distinguishes "no fault" from
"could not read", takes a baseline before driving, and requires the detector to
be registered so an empty list means something.

It also failed on a healthy stack. AMCL's spread widens while the robot drives
and touches 0.307 against a warn threshold of 0.300, so the check now keys on the
error severity above a covariance of 1.0. With the initial pose deliberately put
back to (0, 0) the goals stop completing while this check still passes, so the
goals are what pin that value and the comment now says so.

Drive two goals instead of one, the second back to the spawn point. A single goal
left the robot standing on it, and the goal checker's xy_goal_tolerance is 0.25 m,
so a second run against the same container reported success without moving.

Bound the waiting. Every request in the shared smoke library ran without a
timeout, so a wedged gateway could hold a job until the six hour default. Requests
now time out, the lifecycle and goal budgets are halved, and the turtlebot job
carries a timeout of its own.
@bburda bburda self-assigned this Sep 9, 2026
status="running"
elapsed=0
while [ "$elapsed" -lt "$GOAL_TIMEOUT" ]; do
status=$(curl -s -m 20 \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

smoke_lib.sh:8 has set -euo pipefail and this assignment has no || true, so one curl timeout or connection reset ends the whole script (reproduced with an unreachable port: rc 7, "exited abnormally") instead of the goal being recorded as failed. lifecycle_state at 78-81 already captures the body first with || true; same pattern here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

turtlebot3 headless: no simulation data reaches ROS, Nav2 bringup aborts

2 participants