Skip to content

fix(hangar_sim): make robot_state_publisher the sole owner of the ridgeback_base_link transform (backport to v9.4) - #866

Closed
griswaldbrooks wants to merge 10 commits into
mainfrom
backport/756-hangar-sim-base-link-name-v9.4
Closed

fix(hangar_sim): make robot_state_publisher the sole owner of the ridgeback_base_link transform (backport to v9.4)#866
griswaldbrooks wants to merge 10 commits into
mainfrom
backport/756-hangar-sim-base-link-name-v9.4

Conversation

@griswaldbrooks

Copy link
Copy Markdown

Intent

Backport moveit_pro_example_ws config changes paired with two MuJoCo fixes being backported to moveit_pro v9.4 (maintainer decision).

  1. base_link_name config for hangar_sim (pairs with moveit_pro PR 20258, backported to v9.4 as moveit_pro#21734, in CI as of this writing): cherry-picked merged main-branch PR Fix: Make robot_state_publisher the sole owner of the ridgeback_base_link transform in hangar_sim #756 (merge commit 59d74ff, branch fix/18174-tf-single-owner-base-link) content-faithfully onto v9.4. Config has NO EFFECT until moveit_pro#21734 lands on v9.4 - stated plainly in the PR body; merge/deploy should wait on that landing first.

  2. Investigated whether an example_ws config change pairs with moveit_pro PR 21443 (site frames via mj_world), whose body references moveit_pro_example_ws#790. Verdict: NO backport needed. Add fuse odometry localization for the hangar_sim mobile base #790 is a large unmerged v10.0-only feature (fuse-odometry + AMCL stack) not present on v9.4; the one relevant commit (removing a mj_world-parented base_gt ground-truth site) touches a site that doesn't exist on v9.4's hangar_sim.

This branch (backport/756-hangar-sim-base-link-name-v9.4) already has an OPEN, CORRECTLY-BASED draft PR: #864 (base v9.4, milestone 9.4.2). This exact content already went through a full no-mistakes pipeline run (review, test, document, lint) on a differently-named branch (fm/exws-backport-baselink-config-v94) that reached identical validated content at this same commit (bb8a034) - that prior run's review/test/document gates were already resolved by captain decision: (a) doc-amcl-fabricated finding fixed to reflect v9.4's actual no-AMCL state, (b) 3 findings reproducing already-merged-upstream PR 756 design accepted as out-of-scope, (c) test failure against the not-yet-backported v9.4 image confirmed as expected/documented dependency behavior, (d) doc divergence note kept but reworded to be self-contained. That prior run's own PR step incorrectly opened a duplicate PR (#865) against main instead of reusing #864, because it ran from a branch with no existing PR - it has been closed with a pointer comment to #864, and this correctly-named branch was fast-forwarded to the same validated commit and pushed to keep #864 up to date.

PIPELINE NOTE: this is a release-branch backport (base v9.4, not main). If the pipeline offers/attempts a rebase-onto-main step, skip/decline it. Since this branch already has an open PR (#864, correct base v9.4), the PR step should recognize and reuse it rather than opening a new one - if it opens another duplicate against main anyway, close it with a pointer comment to #864 and verify #864's branch head/base/milestone via the API, same recovery as before.

What Changed

  • Single owner for the ridgeback_base_link transform (hangar_sim). The MuJoCo hardware plugin no longer broadcasts a competing parent for that frame: odom_publish_tf: false and base_link_name: ridgeback_base_link (stopping the lidar TF fill-in chain at the base instead of walking to the MJCF worldbody) in picknik_ur_mujoco_ros2_control.xacro, plus publish_tf: false for fuse's state estimator. robot_state_publisher now owns the live edge via the virtual-rail joint chain, while /odom messages keep flowing for Nav2. The misspelled, silently-ignored odom_zero_z param is replaced with the real odom_planar, and the world -> virtual_rail_link_1 origin gains the -0.048 m z offset that matches the MJCF base_platform_rotation anchor. Note: base_link_name is not yet a recognized parameter on v9.4 — this config has no effect until the paired moveit_pro backport (moveit_pro#21734) lands on v9.4, so merge/deploy should wait on that.
  • Launch and test follow-on. robot_drivers_to_persist_sim.launch.py re-anchors the static TF from mj_world -> world to odom -> world (REP-105 ordering now that odom is no longer created by a MuJoCo broadcast) and drops the _check_fuse_publish_odom OpaqueFunction guard, which enforced the now-obsolete "publish_odom must be false when use_fuse" rule. A new test_base_link_has_single_tf_parent integration test samples /tf and asserts virtual_rail_link_2 is the sole parent of ridgeback_base_link, and checks /odom pose z is zeroed to pin odom_planar's effect. The nav2/whole-body architecture doc is updated to match the new TF ownership and joint-state path.
  • Build/CI carry-alongs. Dockerfile removes the pre-existing ubuntu UID/GID 1000 user on Ubuntu 24.04 before creating the workspace user (tolerating a non-zero userdel exit), and the workspace integration test workflow is bumped to moveit_pro_ci v0.9.1, which adds the synthetic host NIC the licensing fingerprint requires from MoveIt Pro 9.4.2 onward.

Risk Assessment

✅ Low: Config/doc/test-only backport whose code files are byte-identical to the already-merged upstream PR #756 (modulo documented v9.4 adaptations), both prior-round fixes are correctly applied and verified against the real launch code, and the only new observation is a dormant non-default-path comment inaccuracy.

Testing

Ran the hangar_sim TF-ownership integration test against a live, licensed MoveIt Pro backend with MuJoCo on GPU/EGL in three configurations, plus a paired docker build of both Dockerfiles and a paired use_fuse launch. The change does what it claims on a v9.4 image carrying the paired core backport: three competing publishers of ridgeback_base_link collapse to robot_state_publisher alone, /odom pose z goes from 0.048 m to exactly 0, the mobile base still executes its 2 m move objective, and the obsolete fuse launch guard no longer aborts the nav stack. The Dockerfile fix was reproduced as a real build failure before and a clean build after. The only failing run is the expected one on the published v9.4 image, where base_platform remains a second parent because moveit_pro#21734 is not in that core yet — confirming the PR body's dependency rather than contradicting it. Evidence is a rendered before/after TF-ownership graph, the raw TF edge dumps, and the pytest and docker build transcripts; all test containers, images and temp dirs were removed and the worktree is back to a clean state with submodules deinitialized.

  • Evidence: TF ownership of ridgeback_base_link, before vs after (rendered graph) (local file: /home/griswald/.no-mistakes/evidence/01M0E09QSBRPE4Z4GSNABTF6S9/tf-ownership-before-after.png)
Evidence: Integration test transcript — all three image/config scenarios

=== 1) BASE COMMIT config on the v9.4 + moveit_pro#21734 image -> FAILS === E AssertionError: Expected robot_state_publisher's virtual_rail_link_2 as the sole TF parent of ridgeback_base_link, saw parents: ['base_platform', 'odom', 'virtual_rail_link_2'] === 2) THIS BRANCH on the v9.4 + moveit_pro#21734 image -> PASSES === PASSED objectives_integration_test.py::test_base_link_has_single_tf_parent (12.7s) PASSED objectives_integration_test.py::test_all_objectives[.../solution_move_forward_2m.xml] (12.9s) === 2 passed, 66 deselected, 1 warning in 35.91s === === 3) THIS BRANCH on the PUBLISHED v9.4 image (no base_link_name in core) -> FAILS, as documented === E AssertionError: ... saw parents: ['base_platform', 'virtual_rail_link_2']

hangar_sim TF-ownership integration test — pytest transcripts
Test: src/hangar_sim/test/objectives_integration_test.py::test_base_link_has_single_tf_parent
Harness: full MoveIt Pro backend (drivers + agent + bridge) for MOVEIT_CONFIG_PACKAGE=hangar_sim,
         MuJoCo sim on GPU/EGL, licensed, CycloneDDS on lo.

=== 1) BASE COMMIT config (6c51424) on the v9.4 + moveit_pro#21734 image -> FAILS ===
collecting ... collected 68 items / 67 deselected / 1 selected
E       AssertionError: Expected robot_state_publisher's virtual_rail_link_2 as the sole TF parent of ridgeback_base_link, saw parents: ['base_platform', 'odom', 'virtual_rail_link_2']
E         + {'odom', 'virtual_rail_link_2', 'base_platform'}
================= 1 failed, 67 deselected, 1 warning in 32.13s =================

=== 2) THIS BRANCH (1469de9) on the v9.4 + moveit_pro#21734 image -> PASSES ===
PASSED [ 50%]  PASSED  objectives_integration_test.py::test_base_link_has_single_tf_parent (12.7s)
PASSED [100%]  PASSED  objectives_integration_test.py::test_all_objectives[/tmp/cinstall/hangar_sim/share/hangar_sim/objectives/solution_move_forward_2m.xml] (12.9s)
================= 2 passed, 66 deselected, 1 warning in 35.91s =================

    (second selector above is the base-motion regression: the mobile base still drives 2 m
     with robot_state_publisher as sole TF owner and the new -0.048 m virtual-rail offset)

=== 3) THIS BRANCH on the PUBLISHED v9.4 image (core has no base_link_name) -> FAILS, as documented ===
collecting ... collected 68 items / 67 deselected / 1 selected
E       AssertionError: Expected robot_state_publisher's virtual_rail_link_2 as the sole TF parent of ridgeback_base_link, saw parents: ['base_platform', 'virtual_rail_link_2']
E         + {'virtual_rail_link_2', 'base_platform'}
================= 1 failed, 67 deselected, 1 warning in 22.18s =================

    Expected: the config depends on moveit_pro#21734. Without it MuJoCo still walks the lidar
    fill-in chain to base_platform, so 2 parents remain. odom_publish_tf already takes effect
    (the 'odom' parent is gone), which is why scenario 3 shows 2 parents and not 3.
Evidence: Dockerfile Ubuntu 24.04 UID/GID 1000 collision — build before vs after

=== BASE COMMIT Dockerfile (no userdel) -> build FAILS at user creation === #7 0.136 groupadd: GID '1000' already exists #7 ERROR: ... did not complete successfully: exit code: 4 === THIS BRANCH Dockerfile -> ubuntu user removed, user creation succeeds === #6 [base 2/9] RUN if id -u ubuntu > /dev/null 2>&1; then userdel -r ubuntu || true; fi #6 DONE 0.2s #8 DONE 11.9s

Dockerfile: Ubuntu 24.04 UID/GID 1000 collision — docker build --target base, same base image
Base image: picknikciuser/moveit-studio:v9.4-jazzy-amd64-cuda13.2-cudnn9
Args: USERNAME=moveit-pro-user USER_UID=1000 USER_GID=1000 (the host user's ids)

=== BASE COMMIT Dockerfile (6c51424, no userdel) -> build FAILS at user creation ===
#7 0.136 groupadd: GID '1000' already exists
#7 ERROR: process "/bin/bash -o pipefail -c groupadd --gid $USER_GID ${USERNAME} &&     useradd --uid $USER_UID --gid $USER_GID --shell /bin/bash --create-home ${USERNAME} &&     apt-get update &&     apt-get install -q -y --no-install-recommends sudo &&     echo ${USERNAME} ALL=\\(root\\) NOPASSWD:ALL > /etc/sudoers.d/${USERNAME} &&     chmod 0440 /etc/sudoers.d/${USERNAME} &&     cp -r /etc/skel/. /home/${USERNAME} &&     mkdir -p       /home/${USERNAME}/.ccache       /home/${USERNAME}/.config       /home/${USERNAME}/.ignition       /home/${USERNAME}/.colcon       /home/${USERNAME}/.ros &&     chown -R $USER_UID:$USER_GID /home/${USERNAME} /opt/overlay_ws/" did not complete successfully: exit code: 4

=== THIS BRANCH Dockerfile (1469de9) -> the ubuntu user is removed, user creation succeeds ===
#6 [base 2/9] RUN if id -u ubuntu > /dev/null 2>&1; then userdel -r ubuntu || true; fi
#6 0.112 userdel: ubuntu mail spool (/var/mail/ubuntu) not found
#6 DONE 0.2s
#8 0.154 useradd: warning: the home directory /home/moveit-pro-user already exists.
#8 0.154 useradd: Not copying any file from skel directory into it.
#8 DONE 11.9s

Note: the branch build later stops in the rosdep step for an unrelated reason — this sandbox
has only the submodules hangar_sim needs checked out, so keys for lab_sim/lunar_sim/dual_arm_sim
etc. do not resolve. That step is past the change under test and is not affected by it.
Evidence: use_fuse:=true launch guard removal, before vs after
hangar_sim nav launch with use_fuse:=true and the shipped publish_odom: true
Command: ros2 launch hangar_sim robot_drivers_to_persist_sim.launch.py use_fuse:=true
Image: picknikciuser/moveit-studio (v9.4 + moveit_pro#21734)

=== BASE COMMIT (6c51424): the OpaqueFunction guard shuts the launch down ===
[INFO] [launch.user]: use_fuse:=true but MuJoCo is still configured to publish odom -> ridgeback_base_link. When fuse is enabled, fuse must be the sole publisher of that TF edge. Set 'publish_odom: false' in config.yaml under hardware.robot_description.urdf_params, then rebuild hangar_sim.
launch exit code: 1 (immediate Shutdown)

=== THIS BRANCH (1469de9): guard removed, stack stays up ===
no guard message emitted; launch still running when the 45 s harness timeout killed it (exit 124)

Why the guard is obsolete: fuse no longer owns odom -> ridgeback_base_link (publish_tf: false
in config/fuse/fuse.yaml), and MuJoCo never broadcasts that edge either (odom_publish_tf: false),
so publish_odom: true and use_fuse:=true are no longer in conflict.

Not exercised: fuse's own state estimator node — ros-jazzy-fuse_optimizers in this image aborts
on an unrelated diagnostic_updater symbol. use_fuse defaults to false, so CI does not launch it.
Evidence: Raw live TF edge dump — base commit (3 parents, /odom |z| = 0.048)
{
  "dynamic": [
    {
      "parent": "axle_link",
      "child": "front_rocker_link",
      "count": 389
    },
    {
      "parent": "base_link_inertia",
      "child": "shoulder_link",
      "count": 389
    },
    {
      "parent": "base_platform",
      "child": "ridgeback_base_link",
      "count": 2400
    },
    {
      "parent": "base_platform_rotation",
      "child": "base_platform",
      "count": 2400
    },
    {
      "parent": "forearm_link",
      "child": "wrist_1_link",
      "count": 389
    },
    {
      "parent": "front_rocker_link",
      "child": "front_left_wheel_link",
      "count": 389
    },
    {
      "parent": "front_rocker_link",
      "child": "front_right_wheel_link",
      "count": 389
    },
    {
      "parent": "lidar_front_mount",
      "child": "lidar_front",
      "count": 1200
    },
    {
      "parent": "lidar_front_mount",
      "child": "lidar_front_ROS",
      "count": 1200
    },
    {
      "parent": "lidar_rear_mount",
      "child": "lidar_rear",
      "count": 1200
    },
    {
      "parent": "lidar_rear_mount",
      "child": "lidar_rear_ROS",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "attachment_site",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "base_platform_rotation",
      "count": 2400
    },
    {
      "parent": "mj_world",
      "child": "collision_box_1",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "collision_box_2",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "collision_box_3",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "collision_box_4",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "collision_box_5",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "collision_box_6",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "collision_box_7",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "collision_box_8",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "imu_site",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "scene_camera_optical_frame",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "suction_cup",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "tcp_fts_sensor",
      "count": 1200
    },
    {
      "parent": "mj_world",
      "child": "wrist_camera_optical_frame",
      "count": 1200
    },
    {
      "parent": "odom",
      "child": "ridgeback_base_link",
      "count": 3001
    },
    {
      "parent": "rear_rocker_link",
      "child": "rear_left_wheel_link",
      "count": 389
    },
    {
      "parent": "rear_rocker_link",
      "child": "rear_right_wheel_link",
      "count": 389
    },
    {
      "parent": "ridgeback_base_link",
      "child": "lidar_front_mount",
      "count": 1200
    },
    {
      "parent": "ridgeback_base_link",
      "child": "lidar_rear_mount",
      "count": 1200
    },
    {
      "parent": "shoulder_link",
      "child": "upper_arm_link",
      "count": 389
    },
    {
      "parent": "upper_arm_link",
      "child": "forearm_link",
      "count": 389
    },
    {
      "parent": "virtual_rail_link_1",
      "child": "virtual_rail_link_2",
      "count": 389
    },
    {
      "parent": "virtual_rail_link_2",
      "child": "ridgeback_base_link",
      "count": 389
    },
    {
      "parent": "world",
      "child": "virtual_rail_link_1",
      "count": 389
    },
    {
      "parent": "wrist_1_link",
      "child": "wrist_2_link",
      "count": 389
    },
    {
      "parent": "wrist_2_link",
      "child": "wrist_3_link",
      "count": 389
    }
  ],
  "static": [
    {
      "parent": "axle_link",
      "child": "rear_rocker_link",
      "count": 1
    },
    {
      "parent": "base_link",
      "child": "base",
      "count": 1
    },
    {
      "parent": "base_link",
      "child": "base_link_inertia",
      "count": 1
    },
    {
      "parent": "chassis_link",
      "child": "axle_link",
      "count": 1
    },
    {
      "parent": "chassis_link",
      "child": "front_cover_link",
      "count": 1
    },
    {
      "parent": "chassis_link",
      "child": "front_lights_link",
      "count": 1
    },
    {
      "parent": "chassis_link",
      "child": "imu_link",
      "count": 1
    },
    {
      "parent": "chassis_link",
      "child": "left_side_cover_link",
      "count": 1
    },
    {
      "parent": "chassis_link",
      "child": "rear_cover_link",
      "count": 1
    },
    {
      "parent": "chassis_link",
      "child": "rear_lights_link",
      "count": 1
    },
    {
      "parent": "chassis_link",
      "child": "right_side_cover_link",
      "count": 1
    },
    {
      "parent": "chassis_link",
      "child": "riser_link",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_002",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_002_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_004",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_005",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_006",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_007",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_008",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_009",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_010",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_011",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_012",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_013",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_014",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Cube_015",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_002",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_003",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_004",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_005",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_006",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_007",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_008",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_009",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_010",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_011",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_Plane_012",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Box_A7_73",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Box_A8",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Box_C7_77",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Pillar2_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Pillar3_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Pillar4_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Pillar5_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Pillar6_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Pillar7_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Pillar8_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Pillar_10_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Room_426_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Room_426_002",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Room_426_003",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_Room_426_004",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_WallBack_396_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_WallBack_396_002",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_WallFront_382_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_WallFront_382_002",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_WallSide16_002",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_SM_WallSide4_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_001",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_002",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_003",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_004",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_005",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_006",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_007",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_008",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_009",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_010",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_011",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_012",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_013",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_014",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "collision_supports_015",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "visual_mesh_00",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "visual_mesh_01",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "visual_mesh_02",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "visual_mesh_03",
      "count": 1
    },
    {
      "parent": "collision_SM_Floor_376",
      "child": "visual_mesh_04",
      "count": 1
    },
    {
      "parent": "flange",
      "child": "tool0",
      "count": 1
    },
    {
      "parent": "forearm_link",
      "child": "forearm_pinch_link",
      "count": 1
    },
    {
      "parent": "ham_assem_link",
      "child": "ur_mount_point",
      "count": 1
    },
    {
      "parent": "map",
      "child": "odom",
      "count": 1
    },
    {
      "parent": "mj_world",
      "child": "map",
      "count": 1
    },
    {
      "parent": "mj_world",
      "child": "world",
      "count": 1
    },
    {
      "parent": "ridgeback_base_link",
      "child": "chassis_link",
      "count": 1
    },
    {
      "parent": "ridgeback_base_link",
      "child": "ham_assem_link",
      "count": 1
    },
    {
      "parent": "riser_link",
      "child": "mid_mount",
      "count": 1
    },
    {
      "parent": "riser_link",
      "child": "top_link",
      "count": 1
    },
    {
      "parent": "tool0",
      "child": "vacuum_base",
      "count": 1
    },
    {
      "parent": "ur_mount_point",
      "child": "base_link",
      "count": 1
    },
    {
      "parent": "vacuum_base",
      "child": "collision_vacuum_base",
      "count": 1
    },
    {
      "parent": "vacuum_base",
      "child": "collision_vacuum_suction_cups",
      "count": 1
    },
    {
      "parent": "vacuum_base",
      "child": "grasp_link",
      "count": 1
    },
    {
      "parent": "world",
      "child": "collision_SM_Floor_376",
      "count": 1
    },
    {
      "parent": "wrist_3_link",
      "child": "flange",
      "count": 1
    },
    {
      "parent": "wrist_3_link",
      "child": "ft_frame",
      "count": 1
    },
    {
      "parent": "wrist_3_link",
      "child": "wrist_3_pinch_link",
      "count": 1
    }
  ],
  "odom_samples": 3002,
  "odom_max_abs_z": 0.048
}
- Evidence: Raw live TF edge dump — this branch (1 parent, /odom |z| = 0.0) (local file: /home/griswald/.no-mistakes/evidence/01M0E09QSBRPE4Z4GSNABTF6S9/tf-edges-after.json) - Outcome: ⚠️ 2 infos across 1 run (47m35s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

⏭️ **Rebase** - skipped
  • ⚠️ .github/workflows/ci.yaml - merge conflict rebasing onto origin/main
⚠️ **Review** - 1 info
  • ⚠️ Dockerfile:28 - Dockerfile:28 — the fallback userdel -r ubuntu || userdel ubuntu fails the build in exactly the case it was added to survive. userdel -r exits 12 when it removes the account entry but cannot remove the home directory or mail spool; the account is already gone at that point, so the fallback userdel ubuntu exits 6 ("user 'ubuntu' does not exist") and the RUN layer aborts. The fallback only helps in the narrow case where -r failed before touching /etc/passwd, which is not the common failure. Use userdel -r ubuntu || true — if UID 1000 really is still occupied, the later useradd --uid $USER_UID (Dockerfile:39) fails loudly with a clear message. Separately, this line is not part of the PR Fix: Make robot_state_publisher the sole owner of the ridgeback_base_link transform in hangar_sim #756 content the intent describes as content-faithfully cherry-picked; it modifies commit 4fe52e6 (PR [v9.4] Fix Ubuntu 24.04 user ID collision #782), already merged on v9.4, so reverting it to the upstream form is also a valid resolution. Choosing between fix and revert is the author's call.
  • ℹ️ src/hangar_sim/docs/NAV2_AND_WHOLE_BODY_PLANNING_ARCHITECTURE.md:813 - src/hangar_sim/docs/NAV2_AND_WHOLE_BODY_PLANNING_ARCHITECTURE.md:813-818 — the static_tf_map_to_odom excerpt omits the condition=IfCondition(PythonExpression(["not ", slam])) that the real node carries (robot_drivers_to_persist_sim.launch.py:278), and its comment says "only used when neither SLAM nor AMCL is publishing it" while the same doc (line 848 note) correctly states AMCL is never launched on v9.4. A reader copying this snippet gets an unconditional map->odom static that fights slam_toolbox under slam:=True. Add the condition to the snippet and drop the AMCL clause from its comment.
  • ℹ️ src/hangar_sim/config/fuse/fuse.yaml:95 - src/hangar_sim/config/fuse/fuse.yaml:95 — with publish_tf: false, fuse's only remaining output is the odom_filtered topic (fuse.yaml:85), and nothing subscribes to it: nav2_params.yaml:48/435 read /odom and odom, and no launch file or script references odom_filtered. Combined with the removal of _check_fuse_publish_odom, use_fuse:=true now has no observable effect on the running system. This is the documented consequence of making robot_state_publisher the sole owner of the base transform (upstream PR Fix: Make robot_state_publisher the sole owner of the ridgeback_base_link transform in hangar_sim #756 design), not a defect introduced here — noting it so the dead launch argument is a conscious carry-forward rather than a surprise.

🔧 Fix: Fix userdel fallback; add slam condition to TF doc snippet
1 info still open:

  • ℹ️ src/hangar_sim/launch/sim/robot_drivers_to_persist_sim.launch.py:285 - src/hangar_sim/launch/sim/robot_drivers_to_persist_sim.launch.py:285 — the new comment says a live map->odom correction "shifts the whole robot subtree", but world is not just the robot's root: ur5e_ridgeback.xacro:38 attaches the hangar via <xacro:hangar_urdf parent="world"/>, and hangar_urdf.xacro:5-9 fixes collision_SM_Floor_376 (the floor + structure collision geometry) directly to that parent. Reparenting world under odom therefore also puts MoveIt's entire static collision environment under the correction. Concrete path: launch with slam:=True -> static_tf_map_to_odom is skipped (line 278) and slam_toolbox owns map->odom -> any nonzero correction translates the hangar floor/structure relative to map and to the MuJoCo physics, which stay anchored at mj_world via static_tf_world_to_map (line 261). Dormant on every path exercised here: slam defaults to "False" (line 130), nothing in the workspace passes slam:=True, and with ground-truth odometry the correction is ~0 anyway. Under the old mj_world->world link the environment was rigid regardless of localization. Noting it because the comment's "robot subtree" wording understates the scope; this is upstream PR Fix: Make robot_state_publisher the sole owner of the ridgeback_base_link transform in hangar_sim #756's design, cherry-picked faithfully, so no change is required for this backport.
⚠️ **Test** - 2 infos
  • ℹ️ src/hangar_sim/description/picknik_ur_mujoco_ros2_control.xacro:41 - Confirmed live: with this branch's config on the PUBLISHED v9.4 image (core without moveit_pro#21734), test_base_link_has_single_tf_parent still fails — MuJoCo's lidar fill-in chain keeps publishing base_platform -> ridgeback_base_link, leaving 2 parents. The odom_publish_tf half of the change does take effect (the 'odom' parent is gone), so the config is partially live but does not achieve single ownership. This matches the PR body's stated dependency and the prior run's captain decision; it is merge/deploy ordering context, not a defect in the change.
  • ℹ️ src/hangar_sim/test/objectives_integration_test.py:337 - Two pre-existing, unrelated instabilities showed up while running the hangar_sim backend and could make this integration test flaky in CI: (1) nav2's planner_server fails to load with 'nav2_navfn_planner/NavfnPlanner ... does not exist' on Jazzy (the ROS-distro nav2 params problem that open PR Fix: Select hangar_sim nav2 params by ROS distro so Jazzy navigation starts #863 fixes), and (2) one run out of six had the vacuum_gripper controller spawner die with exit 1, tearing the whole launch down as a critical process; an immediate retry passed. Neither is caused by this change — the TF assertion itself was deterministic across every completed run.
  • pytest objectives_integration_test.py -k test_base_link_has_single_tf_parent against the base-commit hangar_sim install on the v9.4 + moveit_pro#21734 image (expected FAIL: 3 TF parents)
  • pytest objectives_integration_test.py -k "single_tf_parent or solution_move_forward_2m" against this branch on the v9.4 + moveit_pro#21734 image (2 passed — single TF parent plus the base-motion regression)
  • pytest objectives_integration_test.py -k test_base_link_has_single_tf_parent against this branch on the published picknikciuser/moveit-studio:v9.4-jazzy-amd64-cuda13.2-cudnn9 image (expected FAIL: 2 TF parents, the documented core dependency)
  • Live TF census script bringing up generate_robot_drivers_launch_description() + generate_agent_launch_description() for hangar_sim, recording every /tf and /tf_static edge plus /odom pose z for 20 s, run once per commit
  • docker build --target base --build-arg USER_UID=1000 --build-arg USER_GID=1000 with the base-commit Dockerfile (fails: groupadd: GID '1000' already exists) and with the branch Dockerfile (userdel step succeeds, user creation succeeds) on the same v9.4 base image
  • ros2 launch hangar_sim robot_drivers_to_persist_sim.launch.py use_fuse:=true on both commits (base commit: guard message + exit 1; branch: still running at the 45 s harness timeout)
  • colcon build --packages-up-to hangar_sim in both the backported and published v9.4 containers after rosdep install over hangar_sim's dependency set
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

marty-mcfly-bot Bot and others added 10 commits July 14, 2026 20:50
Co-authored-by: marty-mcfly-bot[bot] <marty-mcfly-bot[bot]@users.noreply.github.com>
…v9.4

[v9.4] Fix Ubuntu 24.04 user ID collision
The workspace integration test runs MoveIt Pro inside a bridge-networked
container on an ephemeral runner -- no TPM, and no interface the licensing
fingerprint will accept, since a container veth is rejected by design.

Through 9.4.1 that was fine: the fingerprint hashed eth0's MAC and took the
veth's. From 9.4.2 (moveit_pro#21596) it resolves a TPM endorsement key, else a
permanent hardware NIC, and fails closed with neither -- so this job would stop
activating a license the moment it pulls a 9.4.2 image.

v0.9.1 is one commit on top of the pinned v0.9.0, adding only the step that
builds a synthetic sysfs tree with a fixed MAC and points MOVEIT_HOST_SYSFS at
it. No input signature change. The MAC is fixed so the runner fleet presents one
identity rather than each ephemeral runner consuming its own activation.

v10.0 already pins v0.9.1; this brings the 9.4 line in line ahead of 9.4.2.
….9.1-v9.4

Ci: Bump moveit_pro_ci to v0.9.1 for the license hardware identity
…r-base-link

Fix: Make robot_state_publisher the sole owner of the ridgeback_base_link transform in hangar_sim
@griswaldbrooks

Copy link
Copy Markdown
Author

This is a duplicate opened automatically by the no-mistakes validation pipeline against main. The correct, already-open PR for this v9.4 backport is #864 (base v9.4), which already reflects this same branch head. Closing this one.

@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7f28766c-6a7b-499b-b057-8a9b603634e7

📥 Commits

Reviewing files that changed from the base of the PR and between db62616 and 40e81ae.

📒 Files selected for processing (9)
  • .github/workflows/ci.yaml
  • Dockerfile
  • src/hangar_sim/config/config.yaml
  • src/hangar_sim/config/fuse/fuse.yaml
  • src/hangar_sim/description/picknik_ur_mujoco_ros2_control.xacro
  • src/hangar_sim/description/ur5e_ridgeback.xacro
  • src/hangar_sim/docs/NAV2_AND_WHOLE_BODY_PLANNING_ARCHITECTURE.md
  • src/hangar_sim/launch/sim/robot_drivers_to_persist_sim.launch.py
  • src/hangar_sim/test/objectives_integration_test.py

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved simulation odometry and transform handling to prevent conflicting transform publishers.
    • Ensured the robot base frame has a single, consistent parent.
    • Aligned the simulated robot base and floor-contact frame for more accurate positioning.
    • Preserved odometry messages while maintaining planar motion with zero vertical displacement.
  • Documentation

    • Updated navigation, planning, startup, transform-tree, and troubleshooting guidance for simulation and hardware workflows.
  • Tests

    • Added integration coverage for odometry availability and transform consistency.

Walkthrough

The simulation now keeps odometry messages while assigning live TF ownership to robot_state_publisher. Launch files, configuration, documentation, and integration tests reflect this model. CI uses moveit_pro_ci v0.9.1, and the Dockerfile removes the default Ubuntu user before workspace-user creation.

Changes

Simulation transform and odometry flow

Layer / File(s) Summary
Configure simulation TF ownership
src/hangar_sim/config/*.yaml, src/hangar_sim/description/*.xacro
MuJoCo and fuse retain odometry messages while disabling competing odometry TF output. The virtual rail origin is aligned with the simulation base.
Update simulation launch transforms
src/hangar_sim/launch/sim/robot_drivers_to_persist_sim.launch.py
The fuse/MuJoCo odometry validation is removed. The static odom→world transform replaces mj_world→world.
Align architecture documentation
src/hangar_sim/docs/NAV2_AND_WHOLE_BODY_PLANNING_ARCHITECTURE.md
The documentation separates simulation virtual-joint feedback from the hardware odometry bridge and defines conditional localization and TF ownership.
Validate TF and odometry output
src/hangar_sim/test/objectives_integration_test.py
The integration test checks the single ridgeback_base_link TF parent, odometry reception, and planar Z values.

Integration CI workflow

Layer / File(s) Summary
Pin integration workflow
.github/workflows/ci.yaml
The integration job uses moveit_pro_ci v0.9.1 and documents the synthetic host NIC requirement.

Container workspace user setup

Layer / File(s) Summary
Prepare workspace user creation
Dockerfile
The image build removes the Ubuntu default user and home directory before creating the configured workspace user.

Possibly related PRs

Suggested reviewers: bkanator

✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch backport/756-hangar-sim-base-link-name-v9.4

Comment @coderabbitai help to get the list of available commands.

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.

2 participants