Best for
- Any autonomous mobile-robot navigation task: bringing up Nav2, tuning
- The trigger phrases in the description: 'navigation', 'nav2', 'costmap',
- A robot receives a goal and never moves, or moves erratically — start here
robium-ai/robium/archive/nav2/variants/2026-08-02/A/skill/SKILL.md
Nav2 mobile-robot navigation for ROS 2: bringup, behavior trees, costmaps, planner/controller servers, localization (AMCL, slam_toolbox), waypoint following, and tuning. Use when: 'navigation', 'nav2', 'costmap', 'path planning', 'robot won't move to goal', 'localization', 'SLAM', 'AMCL', 'waypoint', or any autonomous mobile robot task. Load after architect selects the ROS nav stack; pairs with ros2 (foundation), gazebo (sim), and visualization (debugging). Not for: manipulation (lerobot) or gen
Decision brief
The nav-vertical core tool skill for robium: bringup, the BT Navigator and behavior trees, costmap layers, the planner/controller/smoother servers, localization (AMCL and slamtoolbox), waypoint following, and tuning a running stack. Nav2 config in this skill targets ROS 2 Jazzy…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/robium-ai/robium --skill "archive/nav2/variants/2026-08-02/A/skill"Inspect the Agent Skill "nav2" from https://github.com/robium-ai/robium/blob/e94a788531ebbbce97693d713ae5fb4ae64155ed/archive/nav2/variants/2026-08-02/A/skill/SKILL.md at commit e94a788531ebbbce97693d713ae5fb4ae64155ed. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
1. Confirm the ROS 2 substrate is ready. A sourced Jazzy workspace with Nav2 installed (sudo apt install ros-jazzy-navigation2 ros-jazzy-nav2-bringup — re-verify the package name against docs.nav2.org's install page before running it) — see the ros2 skill if the workspace itself…
Bringup with an existing map. Pass a saved map YAML and leave slam false — Nav2 launches nav2mapserver + nav2amcl for localization against that static map. Set the robot's initial pose (RViz "2D Pose Estimate" or publish to /initialpose) immediately after launch; AMCL does not p…
Any autonomous mobile-robot navigation task: bringing up Nav2, tuning
Delegation posture: embed + links. The navigation-specific concepts
Jazzy, not Lyrical, until Nav2 ships Lyrical binaries. See the intro
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 92/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 8 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
The nav-vertical core tool skill for robium: bringup, the BT Navigator and
behavior trees, costmap layers, the planner/controller/smoother servers,
localization (AMCL and slam_toolbox), waypoint following, and tuning a
running stack. Nav2 config in this skill targets ROS 2 Jazzy Jalisco
(LTS, supported to May 2029) rather than Lyrical Luth, the current
overall LTS the ros2 skill defaults to — Nav2 has not yet shipped binary
packages for Lyrical (tracked in ros-navigation/navigation2#6123 as of
2026-07). Gazebo Harmonic is Jazzy's paired simulator. Re-check that gap
before starting a new project, and treat picking the distro itself as
architect's call, not this skill's — load this skill once architect has
routed you to the navigation vertical.
references/common-failures.md), not in application logic.ros2. This skill's
only TF responsibility is verifying the map→odom→base_link chain exists
and is current before tuning anything else; teaching TF2 itself stays in
ros2 — see that skill's interfaces-and-qos and debugging references.gazebo skill.visualization skill.lerobot. Nav2 is mobile-base
navigation only.environments.architect (routes here).navigation2 GitHub repo, not re-typed from memory. See References.examples/nav2-params-diffdrive.yaml is adapted from
nav2_bringup's own nav2_params.yaml (jazzy branch) — begin a new robot
there, verify it navigates, then change exactly one subsystem (footprint,
controller plugin, costmap layer) before touching the next. Changing
costmap, controller, and planner parameters simultaneously makes a
regression impossible to bisect.use_sim_time must be consistent across every node, every time. In
simulation, every Nav2 node, the map/odom TF broadcasters, and the sim
clock source must all agree on use_sim_time: true (or all agree on
false on real hardware) — one node left on the wrong value produces TF
extrapolation errors and rejected goals that look like a planning bug but
are a clock mismatch. Set it once, globally, in the params file passed to
every node (see examples/nav2-params-diffdrive.yaml and
examples/bringup-launch-snippet.py), not per-node.map→odom until it has an initial pose (from
RViz's "2D Pose Estimate" or the /initialpose topic). A robot that
"won't move" is, more often than a bad planner or controller parameter, a
broken or incomplete TF chain — check this first with ros2 run tf2_ros tf2_echo map base_link every time, before touching costmap or controller
tuning. See references/common-failures.md.navigation2 GitHub repo before repeating a claim in
a real project — every example in this skill is marked status: unverified for exactly this reason, and each reference states how its
claims were checked on 2026-07-10.1. Confirm the ROS 2 substrate is ready. A sourced Jazzy workspace with
Nav2 installed (sudo apt install ros-jazzy-navigation2 ros-jazzy-nav2-bringup
— re-verify the package name against docs.nav2.org's install page before
running it) — see the ros2 skill if the workspace itself isn't set up yet.
2. Bring up Nav2 with the example config. Copy
examples/nav2-params-diffdrive.yaml and examples/bringup-launch-snippet.py
into your project, keeping the params filename the launch snippet expects
(or updating both together — see Customization), then:
ros2 launch ./bringup-launch-snippet.py map:=/path/to/your_map.yaml use_sim_time:=true
3. Verify before tuning. Confirm every managed node is active
(ros2 lifecycle get /controller_server etc.) and the TF chain is complete
(ros2 run tf2_ros tf2_echo map base_link) — see
references/common-failures.md if either check fails.
4. Send a goal through RViz2's "Nav2 Goal" tool, or programmatically — see the "send goals programmatically" usage pattern below.
Bringup with an existing map. Pass a saved map YAML and leave slam
false — Nav2 launches nav2_map_server + nav2_amcl for localization
against that static map. Set the robot's initial pose (RViz "2D Pose
Estimate" or publish to /initialpose) immediately after launch; AMCL does
not publish map→odom until it has one. See
examples/bringup-launch-snippet.py (map:= argument) and
references/nav2-architecture.md's localization section.
SLAM-then-navigate. Pick exactly ONE of two combinations — on Jazzy
(observed 2026-07-10, nav-trial) nav2_bringup's slam:=True ALREADY
launches its own online_sync_launch.py, so also including slam_toolbox's
online_async_launch.py alongside spawns two slam_toolbox nodes whose
lifecycle managers fight and every goal fails (0/9: "Unable to start
transition 1 from current state inactive", "Timed out while waiting for
action server to acknowledge goal request for compute_path_to_pose").
Either (a) bring up with slam:=True and NO extra slam include, or (b)
launch navigation_launch.py plus your own online_async_launch.py — never
both. Either way pass no map:= argument; a SLAM node publishes /map and
the map→odom transform in place of nav2_map_server/nav2_amcl.
slam_toolbox's Jazzy online_async already ships base_frame: base_footprint and scan_topic: /scan — only use_sim_time: true needs
adding, and any guidance to edit base_frame is stale for Jazzy. Drive the
robot to explore, then save the resulting map with nav2_map_server's
map_saver_cli (see
map_saver's params in examples/nav2-params-diffdrive.yaml) once mapping
is done, so the next run can go back to AMCL-on-a-fixed-map. Mind the frame
convention: the SLAM map frame's origin is the robot's mapping start
pose, not the world/sim origin — goals written in world coordinates are
silently offset by the spawn pose, and the saved map inherits the same
origin (convert goal_map = goal_world − start_pose, or pick goals off the
live map in a viewer). Verified 2026-07-11 (nav-trial). See
references/nav2-architecture.md.
Launch Nav2 servers directly (without nav2_bringup's launch). Three
Jazzy-verified reasons a project outgrows bringup_launch.py (all hit in
one real build, 2026-07-11 nav-trial): slam:=True starts its own
synchronous slam_toolbox, so also launching online_async_launch.py
alongside yields two SLAM nodes; navigation_launch.py hard-codes its
lifecycle-manager params, so bond_timeout can't be adjusted; and a params
file containing $(find-pkg-share ...) substitutions reaches the nodes as
literal strings. When launching servers as plain Nodes yourself: wrap the
params file in launch_ros.parameter_descriptions.ParameterFile(path, allow_substs=True), replicate navigation_launch.py's remappings (the
cmd_vel → cmd_vel_nav → smoother chain), and list every server in your
own lifecycle manager — set bond_timeout: 0.0 on it (the only way to
change it, since navigation_launch.py hard-codes the value; see the
Docker-stall gotcha). To override a single param the ParameterFile gets
wrong (e.g. TB3 burger.yaml's relative yaml_filename: "map.yaml", which
makes map_server's LoadMap fail), append a plain dict AFTER the
ParameterFile in the node's parameters= list — later entries win:
parameters=[ParameterFile(params_yaml, allow_substs=True), {'yaml_filename': abs_map_path}]. yaml_filename fix observed 2026-07-10 (nav-trial).
Send goals programmatically. Use nav2_simple_commander's
BasicNavigator Python class rather than hand-rolling NavigateToPose
action clients: goToPose() / goThroughPoses() for single/multi-pose
goals, followWaypoints() for a waypoint list, and non-blocking
isTaskComplete()/getResult() polling for feedback in a single-threaded
script. See references/nav2-architecture.md's commander-API section for a
minimal snippet shape.
Tune for a new robot footprint/speed. Start from
examples/nav2-params-diffdrive.yaml's local_costmap/global_costmap
robot_radius (switch to an explicit footprint polygon for a non-circular
base), then the controller's velocity/acceleration limits and
velocity_smoother's max_velocity/max_accel/max_decel — change these
before touching planner or BT internals, since a wrong footprint or speed
limit makes every downstream navigation attempt look broken. See
references/tuning-guide.md.
Author a custom global planner plugin.
A global-planner plugin subclasses nav2_core::GlobalPlanner and
implements configure(parent, name, tf, costmap_ros) / cleanup() /
activate() / deactivate() / createPlan(...) — the costmap_ros
argument handed to configure() is how the plugin reaches the same
costmap references/nav2-architecture.md's planner-server section
already documents. Registration needs exactly two pieces, not three:
PLUGINLIB_EXPORT_CLASS(<YourClass>, nav2_core::GlobalPlanner) in the
.cpp, and the CMakeLists.txt's
pluginlib_export_plugin_description_file(nav2_core <plugin>.xml)
call, which installs the plugin-description XML and writes the
ament-index marker pluginlib's ClassLoader queries at runtime — a
package.xml <export> tag for the same XML is a ROS 1 carryover
some tutorials still ship and is not load-bearing in ROS 2, per
search-synthesis of ament_cmake/pluginlib docs (re-verify on next docs
pass). Treat the exact configure()/createPlan() parameter list as
a conceptual pattern, not a pinned signature — confirmed 2026-08-02
against the rolling-branch navigation2_tutorials repo (no
Jazzy/Humble/Iron branch exists there to check directly); re-verify
against this skill's target distro (Jazzy Jalisco) before writing a
plugin from it.
Author a custom costmap layer plugin.
A costmap layer plugin subclasses nav2_costmap_2d::Layer and
overrides onInitialize() (parameter declaration + one-time state),
updateBounds(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y) (grows the costmap's dirty-bounds window), and
updateCosts(master_grid, min_i, min_j, max_i, max_j) (writes cost
values into that window) — plus reset(), onFootprintChanged(), and
isClearable(). Registration mirrors the planner-plugin pattern's
load-bearing half only (see the custom-global-planner-plugin pattern
above): PLUGINLIB_EXPORT_CLASS(<YourClass>, nav2_costmap_2d::Layer)
in the .cpp, paired with CMakeLists.txt's
pluginlib_export_plugin_description_file(nav2_costmap_2d <layer>.xml) — the macro's first argument is nav2_costmap_2d here,
not nav2_core as in the planner case; it is pluginlib's
resource-index key, not a string a params file's plugins: list
names directly. Cross-references references/nav2-architecture.md's
Costmap 2D section (which names the shared layer types — StaticLayer,
ObstacleLayer, VoxelLayer, InflationLayer — but not how to author a
new one). Confirmed 2026-08-02 against the rolling-branch
navigation2_tutorials repo; the Layer virtual-method set has been
stable across recent distros per search-synthesis, but re-verify
before absorbing a parameter list verbatim.
The controller plugin's TwistStamped return type is not the /cmd_vel wire format.
nav2_core::Controller::computeVelocityCommands() — the method
controller_server calls every control cycle to get a command from the
plugin — is declared returning geometry_msgs::msg::TwistStamped
unconditionally; every controller plugin (e.g.
nav2_pure_pursuit_controller) implements it that way regardless of
what topic type ends up on the wire. Do not read this as explaining or
confirming the Platform gotchas cmd-vel-twiststamped note: a plugin
returning TwistStamped from computeVelocityCommands() does not
imply /cmd_vel is published as TwistStamped — that inference is
not valid. controller_server receives the plugin's TwistStamped
either way and is free to convert/republish it as a plain Twist on
/cmd_vel; the wire format is a separate runtime choice,
enable_stamped_cmd_vel (see the cmd-vel-twiststamped gotcha above),
decoupled from the plugin API. Confirmed 2026-08-02 against the
rolling-branch navigation2_tutorials repo's
nav2_pure_pursuit_controller header, cross-checked against the
upstream nav2_core::Controller base-class header; re-verify against
this skill's target distro (Jazzy Jalisco) before treating the exact
parameter list as pinned.
architect and
ros2 both reference it) — don't silently "upgrade" a nav2 project to
Lyrical without re-checking ros-navigation/navigation2#6123 first./initialpose published will sit idle —
no error, just no map→odom transform and a costmap that never
activates. This looks identical to a hung launch; check for a missing
initial pose before debugging anything else. Between bringup and the first
goal, global_costmap and AMCL spam transform/pose warnings every
~0.5–2 s ("Timed out waiting for transform from base_link to map ...
Invalid frame ID map", "AMCL cannot publish a pose ... Please set the
initial pose") — this is benign, the map frame doesn't exist until AMCL
gets its initial pose, so don't chase it. See
references/common-failures.md.use_composition:=true) vs standalone nodes change crash
behavior. nav2_bringup defaults to component-container composition; a
crash inside one composed node can take down the whole container process,
whereas standalone nodes (use_composition:=false, with
use_respawn:=true) restart independently. Prefer standalone + respawn
while iterating on a new robot; composition is a later performance
optimization, not a default to fight while still debugging./cmd_vel may be TwistStamped, not Twist. Modern gz robot
integrations (TB3 on Jazzy among them) subscribe TwistStamped; a plain
Twist publisher never matches — no error, ros2 topic pub just waits
forever for a matching subscription — and the robot silently ignores
Nav2. Check with ros2 topic info -v /cmd_vel, and set
enable_stamped_cmd_vel: true in every cmd_vel-publishing section
(controller_server, velocity_smoother, behavior_server,
collision_monitor, docking_server). TB3 Jazzy's burger.yaml already
pre-sets enable_stamped_cmd_vel: true in all five of those sections, so
this trap only bites configs started from nav2_bringup's nav2_params.yaml.
Verified 2026-07-11 (nav-trial).turtlebot3_navigation2's burger.yaml, not
nav2_bringup's nav2_params.yaml. It is TB3-tuned and carries 13 of
nav2_bringup's 14 sections; it ships no use_sim_time keys and relies on
RewrittenYaml to inject them. Observed 2026-07-10 (nav-trial).burger.yaml's collision_monitor source_timeout is too tight
for its own lidar. The scan source ships source_timeout: 0.2, but the
burger lidar publishes at 5 Hz (a 0.2 s period), so ordinary jitter makes
collision_monitor reject the source and zero cmd_vel — the robot won't
move even with a healthy stack ("Latest source and current collision
monitor node timestamps differ on 0.2xx seconds. Ignoring the source.",
"Robot to stop due to invalid source"). Set source_timeout above the
sensor period (1.0 worked). Observed 2026-07-10 (nav-trial).bond_timeout: 4.0 self-destructs the
stack ("CRITICAL FAILURE: SERVER controller_server IS DOWN after not
receiving a heartbeat for 4000 ms"). Jazzy's navigation_launch.py
hard-codes the lifecycle_manager params, so bond_timeout cannot be set
through params_file/RewrittenYaml; the only fix is to launch the
servers under your own lifecycle_manager node with bond_timeout: 0.0
(see the direct-server-launch usage pattern). Observed 2026-07-10
(nav-trial).turtlebot3_world has pillars of r=0.15 at {-1.1, 0,
1.1}² (grep the SDF pillar poses); an octagon route of r=1.7 lands inside
them. Observed 2026-07-10 (nav-trial)./clock must actually be publishing before any node
with use_sim_time:=true will progress — a paused or not-yet-started
Gazebo world leaves every Nav2 node waiting on TF timestamps that never
arrive, which looks like a Nav2 hang rather than a sim issue.robot_radius for an
explicit footprint polygon in both local_costmap and global_costmap
in examples/nav2-params-diffdrive.yaml, and change FollowPath's
motion_model (e.g. "DiffDrive" → "Omni") if the base isn't
differential-drive — see references/tuning-guide.md.controller_server.FollowPath.plugin and
planner_server.GridBased.plugin fields select the algorithm; swapping
requires the matching plugin name and its own parameter block (e.g.
nav2_regulated_pure_pursuit_controller::RegulatedPurePursuitController or
nav2_smac_planner::SmacPlannerHybrid) — verify exact plugin/class names
against docs.nav2.org's configuration guide before writing them, they
are not interchangeable strings. See references/tuning-guide.md.examples/bringup-launch-snippet.py's params_file default and
examples/nav2-params-diffdrive.yaml's own filename must be kept in sync
if you rename either — the launch snippet resolves the params path
relative to itself, so a silent rename of one without the other produces a
"params file not found" failure at launch, not a subtle runtime bug.ros-navigation/navigation2#6123), swap jazzy for lyrical in every
install command and Docker base image in the environments skill's
Dockerfile.ros2 example; nothing in this skill's params/launch content itself is
distro-specific beyond the install step.references/nav2-architecture.md — the BT Navigator and default behavior
trees, the planner/controller/smoother/behavior/waypoint-follower servers,
costmap 2D layers (global vs local), the lifecycle manager, AMCL vs
slam_toolbox, and the nav2_simple_commander API.references/tuning-guide.md — costmap resolution/update-rate/inflation
tuning, footprint vs radius, controller/planner plugin selection,
velocity/acceleration limits, and the "one subsystem at a time" workflow.references/common-failures.md — the "robot won't move" diagnostic
checklist: lifecycle state, TF tree, use_sim_time consistency, costmap
obstacle sourcing, goal rejection, and cmd_vel not reaching the base.examples/nav2-params-diffdrive.yaml — adapted from nav2_bringup's
official minimal diff-drive nav2_params.yaml (status: unverified — file
header states the exact source and the deviations made).examples/bringup-launch-snippet.py — a project launch file that includes
nav2_bringup's own bringup_launch.py, pointed at this skill's example
params file (status: unverified — file header states the exact source).ros2 (foundation, load alongside), gazebo (sim),
visualization (debugging), environments (Docker/env
setup), architect (routes here).1.5.0 (2026-08-02): add controller-plugin-cmdvel-decoupling (Usage patterns) [reasons: obs-nav2-003] (applied by apply_deltas)
1.4.0 (2026-08-02): annotate SKILL.md; update custom-costmap-layer-plugin [reasons: obs-nav2-001, obs-nav2-002] (applied by apply_deltas)
1.3.0 (2026-08-02): add Usage patterns; add Usage patterns [reasons: obs-nav2-001, obs-nav2-002] (applied by apply_deltas)
1.2.1 (2026-08-01): anchor IDs added to claim-bearing items (learning-engine Phase 1); no content changes.
1.2.0 (2026-07-31): nav-trial absorption (Jazzy/TB3, observed 2026-07-10) — corrected SLAM-then-navigate (bringup slam:=True already runs a slam_toolbox node; a second online_async is double-SLAM), added Docker-stall bond_timeout:0.0 fix, TB3 burger.yaml starting point + pre-set enable_stamped_cmd_vel + collision_monitor source_timeout, benign costmap/AMCL log storm, live-SLAM recovery map corruption + waypoint clearance, and map_server yaml_filename override.
1.1.1 (2026-07-12): skill-refiner run 1 — provenance claims date-stamped ('this session' → 2026-07-10, the authoring session) so the staleness sweep can age them.
1.1.0 (2026-07-11): nav-trial absorption — TwistStamped cmd_vel gotcha, SLAM map-origin-at-start-pose convention, direct-server launch pattern (allow_substs / double-SLAM / bond_timeout), bringup-abort recovery added to common-failures.
Frequently asked questions
The nav-vertical core tool skill for robium: bringup, the BT Navigator and behavior trees, costmap layers, the planner/controller/smoother servers, localization (AMCL and slamtoolbox), waypoint following, and tuning a running stack. Nav2 config in this skill targets ROS 2 Jazzy…
The source record exposes this install command: npx skills add https://github.com/robium-ai/robium --skill "archive/nav2/variants/2026-08-02/A/skill". Inspect the command and pinned source before running it.
Alternatives
narrative-io/narrative-skills-marketplace
Translate a fuzzy analytical question into a rigorous investigation plan. Interrogates the ask, grounds the plan in the available data dictionary, applies analytical best practices, and produces a structured brief of query specifications for a downstream query-writing skill. Plans, does not write SQL. Use when: "why did X drop", "is there a relationship between A and B", "who are our highest-value customers", "what's driving the change in Y", "investigate this trend", "design an analysis for", "
objectstack-ai/objectstack
Author ObjectStack translation bundles — object/field labels, view text, app navigation strings, automation messages — and configure locale fallback, coverage reporting, and the per-locale source layout. Use when the user is adding `*.translation.ts` files, wiring a new locale, or resolving missing-translation warnings. Do not use for general i18n library questions unrelated to ObjectStack bundles.
indranilbanerjee/contentforge
Translate publication-ready ContentForge content into any of 15 languages at three localization levels (literal, adapted, transcreated) — preserving brand voice, keeping citation URLs and DOIs untouched, and adapting SEO keyword placements for the target market — delivered as a translated .docx plus a quality report via the brand's tracking backend. Triggers on "/contentforge:cf-translate", "translate this article to Spanish", "localize this for the German market", "make a French version of this
nexscope-ai/Amazon-Skills
Evaluate and plan Amazon marketplace expansion across countries and regions. Use when a seller asks which Amazon marketplace to enter, how to compare international demand and economics, what tax, product-compliance, logistics, localization, account, or launch workstreams to investigate, or how to build a gated global-selling roadmap. Do not use as legal, tax, customs, or certification advice.