Skip to content

Add Build Map objective for hangar_sim - #931

Draft
bkanator wants to merge 2 commits into
mainfrom
feat/18066-build-map-objective
Draft

Add Build Map objective for hangar_sim#931
bkanator wants to merge 2 commits into
mainfrom
feat/18066-build-map-objective

Conversation

@bkanator

@bkanator bkanator commented Sep 10, 2026

Copy link
Copy Markdown

[written by AI]

needs: moveit_pro/#22585

Why the integration tests are red, and why that is expected. The needs: line above is working: CI resolves it (MOVEIT_PRO_PR_FROM_BODY: 22585) and tries to pull moveit-pro-ci:pr-22585-jazzy-amd64-.... That image does not exist yet, because moveit_pro/#22585 is still a draft, and a draft runs a light CI plan that builds no PR image. So the failure is not found on the image pull, not a test failure and not a missing declaration — lab_sim fails identically, which is the tell that nothing in this diff caused it.

It clears when #22585 produces an image: marking that PR ready for review does it, or any push to it now that it carries the ci:full label. Per the cross-repo guidance in .claude/rules/git-workflow.md, an example_ws PR being red on its own until its paired moveit_pro PR lands is the documented expected state.

Motivation

Closes #18066 (epic #16487).

Epic #16487 promised operators could build maps through the MoveIt Pro Desktop App — teleoperate to explore, click save when done. That never shipped. What ships instead is a single checked-in hangar_map.pgm that a developer generated once through a CLI, and localization_launch.py tells the reader to "Run SLAM first (slam:=True)" with no Objective to do it from.

This is the operator-facing half: the Objective. It depends on the CallSaveMapService Behavior added in moveit_pro/#22585, which must merge and reach the base image first.

Brief description

Adds src/hangar_sim/objectives/build_map.xml, a runnable "Build Map" Objective: teleoperate to explore the hangar while slam_toolbox builds the occupancy grid, then save it on the operator's confirmation. Objectives are auto-discovered from that folder.

It also opts this configuration into the new SLAM Behaviors package: moveit_pro::behaviors::SlamBehaviorsLoader in config/config.yaml, and <exec_depend>moveit_pro_slam_behavior</exec_depend> in package.xml. Those two lines are what the split in moveit_pro/#22585 buys — CallSaveMapService lives outside the shared Behavior library, so only configurations that actually build maps load it or depend on slam_toolbox. A manipulation-only configuration adds neither line and is unaffected.

It assumes slam_toolbox is already running in mapping mode, which robot_drivers_to_persist_sim.launch.py starts with slam:=True (it includes nav2_bringup's slam_launch.py). The slam_toolbox block already in params/nav2_params.yaml supplies mode: mapping, and hangar_sim already declares <exec_depend>slam_toolbox</exec_depend>.

On the tree shape: Sequence, not Parallel

The issue suggests "likely uses a Parallel node with success_count=1 to handle teleop + user interaction concurrency". I used a plain Sequence instead, because the concurrency the Parallel would provide already exists inside the teleop subtree:

DoTeleoperateAction carries enable_user_interaction and user_interaction_prompt on its own action goal, and holds that prompt open for as long as teleoperation is active. So the operator drives and confirms against one node: Continue ends teleop with SUCCESS and the Sequence proceeds to save; Abort ends it with FAILURE and no map is written.

Building the same thing with Parallel would need a separate "wait for a button" Behavior, and no such Behavior exists — the user-interaction Behaviors are GetPoseFromUser, GetPointsFromUser, GetRegionFromUser, and GetTextFromUser, all of which demand a value the workflow does not need. The Sequence reaches the same operator experience with nothing new added. The tradeoff: the button reads Continue rather than Save Map, so the prompt text carries the meaning.

Teleop mode, and the controller question

initial_teleop_mode="1" (JOINT_JOG), not 3. Mode 3 is MOVE_TO_WAYPOINT (TeleoperationMode.msg), which replays a pre-recorded joint state — useless for exploring space no waypoint covers, and on this config its branch passes the three base joints to a planned, collision-checked base motion whose planning scene has no hangar in it yet, because the map is what we are building. JOINT_JOG is what drives the base here: the manipulator group carries linear_x_joint, linear_y_joint and rotational_yaw_joint (picknik_ur.srdf), joint_jog.yaml maps that group to joint_velocity_controller, and that controller's command_joints are platform_velocity_controller/<joint> reference interfaces.

No SwitchController prologue. I drafted one, on the reasoning that the navigate Objectives hand the wheels to platform_velocity_controller_nav2 and never hand them back, so a later jog would command a controller whose downstream was inactive. It turned out to be unnecessary: SwitchController defaults both automatic_activation and automatic_deactivation to true (switch_controller.cpp:228,234), and the former exists specifically to activate downstream chained controllers, so the subtree's own jog switch both activates platform_velocity_controller and drops whatever held the wheel interfaces. Recording it because "why is there no controller setup here, when every neighbouring Objective has one" is a fair reviewer question.

Map quality: two limitations the operator should know about

Neither is introduced by this PR and neither is fixed here, but both shape what this Objective produces, and the second changed the operator prompt:

  1. use_scan_matching: false and do_loop_closing: false in the slam_toolbox block of params/nav2_params.yaml. With both off the map is the scan integrated along raw odometry, so a hangar-scale circuit will smear where the path re-crosses itself — and CallSaveMapService returns SUCCESS for a smeared map exactly as for a good one. These were disabled deliberately (commit c72308b0), so flipping them is a tuning decision, not a drive-by fix; #19530 is where realistic SLAM params belong.
  2. Mapping sees the front lidar only, and cannot see more. slam_toolbox.scan_topic is /scan_front_filtered while AMCL consumes /scan_merged. That is not a tuning choice: dual_laser_merger lives in localization_launch.py, which robot_drivers_to_persist_sim.launch.py:243 includes only under condition=IfCondition(PythonExpression(["not ", slam])). With slam:=True the merger never starts and /scan_merged does not exist. Since each lidar covers ~230°, the rear arc is never ray-traced, so the saved map keeps unknown regions that are physically free — and AMCL then matches a 360° scan against it. The principled fix is to move the merger out of the not slam branch and point scan_topic at /scan_merged; that touches the localization launch path, so it is deliberately not in this PR. Until it lands, the operator prompt tells the operator to turn in place so every surface passes the front lidar.

Map name

map_name="hangar_map" is a literal on the Behavior, matching how the neighbouring navigation Objectives hardcode their configuration. slam_toolbox resolves a relative name against its own working directory, so running this does not overwrite maps/hangar_map.pgm in the source tree; promoting a new map into the config package stays a deliberate step. The Objective comment says so, because that is exactly the surprise worth pre-empting.

How it was tested

pre-commit run --files src/hangar_sim/objectives/build_map.xml clean, including prettier and the Objective-favorites validator. XML parses.

Manual verification still required — this Objective has not been run. It needs the paired Behavior in the base image, plus exclusive simulator access that was not available on this host. Specifically unverified:

  • that JOINT_JOG lands the operator on a panel that actually drives the base on this config, and that DoTeleoperateAction reports joint_velocity_controller in its controllers output so the chain comes up;
  • that 30 s covers the save (slam_toolbox shells out to map_saver_cli and sleeps 1 s, so the budget is dominated by process start and DDS discovery rather than the raster);
  • the end-to-end run: explore, confirm, and a map file actually appearing — and where it appears.

Worth pairing with a rebuild of hangar_map on the 811-beam lidar, which is what #19530 wants a home for — but that is follow-up, not this PR.

Documentation follow-up

documentation-bot found no stale page — no public doc describes map building at all today — but flagged that an operator who sees Build Map in the Objectives panel has nothing to read. The natural home is a ### Build Map section on src/docs/docs/how_to/mobile_navigation/nav2_mobile_navigation/nav2_mobile_navigation.mdx in the moveit_pro repo, covering the slam:=True prerequisite and the step of copying the saved map into the configuration package.

I deliberately have not written it yet: the Objective has not been run once, and two of its parameters are explicitly unverified above. Documenting the workflow before it has been exercised would publish guidance nobody has followed. Once this is verified, that section should land in the same pass.

Release notes

  • Major Feature: Added the Build Map Objective to the hangar simulation configuration. Teleoperate the robot to explore the environment while mapping runs, then confirm to save the occupancy grid map, without leaving the MoveIt Pro Desktop App. Requires the runtime to be launched with slam:=True.

Claude agent checks

  • code-reviewer
    • Reviewed as part of the paired moveit_pro change
  • documentation-bot
    • No stale docs; the Build Map new-doc suggestion is recorded under "Documentation follow-up" above
  • SKIPPED licensing-privacy-bot
    • No dependency, vendored source, or data flow changes in this repo
  • SKIPPED platform-architect-bot
    • Objective XML only; no backend, C++, ROS, REST, or build/CI code
  • roboticist-bot
    • Found initial_teleop_mode="3" was MOVE_TO_WAYPOINT; corrected to 1 (JOINT_JOG). Its controller-prologue finding was investigated and not applied - see above
  • SKIPPED frontend-noah-bot
    • No frontend files changed
  • security-auditor
    • Reviewed this Objective alongside the Behavior; the shipped map_name here is relative and was not a finding
  • compatibility-bot
    • No semver impact and no contract drift; a new Objective is purely additive
  • SKIPPED sonar-bot
    • No C, C++, Python, JavaScript, or TypeScript in this diff
  • SKIPPED test-runner
    • No buildable or testable code in this diff. Validation is the manual run named above, which is still outstanding

Epic #16487 promised operators could build maps through the MoveIt Pro
Desktop App. What shipped instead was a single checked-in map a developer
generated once through a CLI, and localization_launch.py pointed readers at
slam:=True with no Objective to drive it from.

Teleoperate to explore while slam_toolbox builds the grid, then save it on
the operator's confirmation. Teleop mode 1 (JOINT_JOG) is what drives the
base here, and the response timeout accounts for slam_toolbox shelling out
to map_saver_cli.

Refs #18066

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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

@github-actions

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

  • lab_sim
    • jazzy: no report produced — see run logs
  • hangar_sim
    • jazzy: no report produced — see run logs

CallSaveMapService ships in moveit_pro_slam_behavior rather than in the
shared Behavior library, so a configuration has to opt in. Only
configurations that build maps then depend on slam_toolbox.

Refs #18066

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

  • lab_sim
    • jazzy: no report produced — see run logs
  • hangar_sim
    • jazzy: no report produced — see run logs

@bkanator bkanator added this to the 10.2.0 milestone Sep 11, 2026
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.

1 participant