Repository navigation
A promoted map must load on a running robot without a nav restart - #120
Merged
Merged
Conversation
map_server reads its map once, at startup. A promoted revision was pulled and installed by the agent and then ignored until someone restarted nav; health, reading the floor's symlink, meanwhile reported the new revision (mote-01, 2026-09-21). map_reloader (mote_bringup), started by nav2_launch.py with localisation, hears map/installed from the agent, asks the task server's task/idle whether the lane is free, calls map_server/load_map with the revision's own map.yaml, re-seeds AMCL on /initialpose from the last amcl_pose, and reports map/serving. A mission in flight defers the load to its terminal state; a load in progress makes the task server refuse new missions as busy; a failed load leaves the old map served and says why. The task server reloads the floor's zones when the served revision changes. Health's map.revision is now what map_server serves, with map.installed (the symlink) and map.error beside it. AMCL's first_map_only is pinned false. Tests: reloader unit tests against fake map_server/lane/AMCL; the real task server and reloader deferring a load across a held Nav2 goal; agent health between install and load; an e2e promote -> pull -> load over a real broker; and a sim acceptance (pixi run sim-map-reload-test). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M93DeThw4ndhxsg4Dho6XP
MJohnson459
pushed a commit
that referenced
this pull request
Sep 29, 2026
#120 added a 330-word 'Fleet: loading a promoted map' section to the old file. Its narrative already lives in docs/fleet/README.md §11; the rules it carried become bullets under Map registry. The 'next bringup' line and the DDS total (~27) are corrected, and a short 'Editing this file' rule says what belongs here so the next feature adds bullets, not a section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Ly1jZnCmZq3aaa2iKM9Zz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A promoted map loads on a running robot without a nav restart
map_serverreads its map once, at startup. On mote-01 (2026-09-21) a promoted revision was pulled and installed, and Nav2 kept serving the old one untilpixi run robotwas restarted by hand. Health read the floor's symlink, so the fleet showed the new revision throughout.Where the reload lives
In a new node,
map_reloader(mote_bringup/map_reloader.py; the ROS-free half is inmap_reload.py).nav2_launch.pystarts it only withlocalisation:=true, because under SLAM there is no saved map to swap. The agent stays a bridge and a reporter: it publishes a latchedmap/installed(a statement of what it put on disk) and never calls Nav2.map_server/load_mapwithmaps/<rev>/map.yaml, not the symlink, so the served file is the one reported. It then re-publishes the lastamcl_poseon/initialposeand publishes a latchedmap/serving. AMCL re-initialises its filter on a new map; the sim log showsReceived a 117 X 117 mapfollowed by the carried pose. The revision shares the floor frame, so the old estimate is still correct. The node readsamcl_poserather than TF so it holds no TransformListener.task/idleservice (std_srvs/Trigger) and waits until the mission reaches a terminal state. While a load runs,map/serving.loadingmakes the task server refuse new missions asbusy(recoverable). The refusal is bounded at 30 s, so a reloader that dies mid-load cannot hold the lane.zones.yamlwhen the served revision changes, so a promoted zone edit also needs no restart.map/serving.errorand health'smap.error, and the node does not retry the same revision until the agent installs it again.first_map_only: falseis now pinned innav2_params.yaml.(restart nav to load it).Health contract
map.revisionis now whatmap_serverserves. Two nullable fields are added without a version bump:map.installed: the symlink.map.error: why the last load failed.With no reloader running,
revisionequalsinstalled. Updated inhealth.schema.json,control-plane.mdandprotocol.health.Cost
One process and one DDS participant, taking the robot stack to about 27 of 33 (not measured with
dds-check).Known limits
site use-map <rev>still needs a nav restart; it tells nobody.map_reloaderalone respawns while a deferred load is pending, it reads the symlink at startup and reports the installed revision althoughmap_serverstill serves the old one.map_serveroffers no way to ask what it is serving.Verification
mote_bringup/test/test_map_reloader.py: 11 tests against a fakemap_server, lane and AMCL. Covers load and pose carry, deferral, no task server, failed load and no retry, another floor, andmap_serverabsent.mote_tasks/test/test_map_reload_lane.py: the real task server and the real reloader. A promotion during a held Nav2 goal does not load until the mission succeeds, then loads and brings the new zones. An idle robot loads at once and agototo the new zone succeeds. A mission sent mid-load is refused asbusy.mote_fleet/test/test_agent.py: health names the old revision between install and load and the new one after; a failed load is reported;map/installedis published.mote_fleet/test/test_e2e_map_registry.py, new sibling test: real broker, fleet server,fleetctl promote, agent and reloader. The lane is held busy across the promotion, so MQTT health showsinstallednew andrevisionold; releasing it loads the new map and health follows.pixi run sim-map-reload-test(new): mote_world with a second revision that has a 4×4 px obstacle drawn in, promoted whilesim-navis up and idle.🤖 Generated with Claude Code