agentleFS
enesbirlikenesbirlik/claude-code-robotics/skills/nav2-moveit2-config/SKILL.md

nav2-moveit2-config

Skill2 starsChanged 54 days agoMITView on GitHub

Use when configuring Navigation 2 (costmaps, behavior trees, controller/planner servers) or MoveIt 2 (SRDF, kinematics, planning pipelines, ros2_control integration) for a mobile or manipulator robot.

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Add RecoveryNode wrapping the main navigation subtree with fallback behaviors (spin, backup, wait) for production robots — a bare …

    line 167
  2. Very specific:

    Every <group> chain's base_link/tip_link must exist in the URDF exactly as spelled — MoveIt Setup Assistant generates this correctly from a …

    line 220
  3. Very specific:

    Always check plan_result truthiness (or the equivalent success field in the C++ API) before calling execute — MoveIt returns a …

    line 267

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Nav2 / MoveIt 2 Configuration Architecture

Standard, consistency-checked configuration layouts for Navigation 2 and MoveIt 2. Both frameworks fail in the same way when misconfigured: nodes start cleanly but planning/control silently produces useless or dangerous output, so the rules below emphasize cross-file consistency, not just per-file syntax.

1. Nav2 package layout

my_robot_navigation/
├── config/
│   ├── nav2_params.yaml       # every lifecycle-managed node's parameters, one file
│   └── behavior_tree/
│       └── navigate_w_recovery.xml
├── maps/
│   └── my_map.yaml
└── launch/
    └── navigation.launch.py    # includes nav2_bringup/launch/bringup_launch.py

2. nav2_params.yaml skeleton

amcl:
  ros__parameters:
    use_sim_time: true
    alpha1: 0.2
    alpha2: 0.2
    alpha3: 0.2
    alpha4: 0.2
    scan_topic: scan
    robot_model_type: "nav2_amcl::DifferentialMotionModel"

controller_server:
  ros__parameters:
    use_sim_time: true
    controller_frequency: 20.0
    min_x_velocity_threshold: 0.001
    failure_tolerance: 0.3
    controller_plugins: ["FollowPath"]
    FollowPath:
      plugin: "nav2_regulated_pure_pursuit_controller::RegulatedPurePursuitController"
      desired_linear_vel: 0.4
      max_angular_accel: 2.0
      use_collision_detection: true

planner_server:
  ros__parameters:
    use_sim_time: true
    planner_plugins: ["GridBased"]
    GridBased:
      plugin: "nav2_navfn_planner::NavfnPlanner"
      tolerance: 0.3

behavior_server:
  ros__parameters:
    use_sim_time: true
    behavior_plugins: ["spin", "backup", "wait"]
    spin:
      plugin: "nav2_behaviors::Spin"
    backup:
      plugin: "nav2_behaviors::BackUp"
    wait:
      plugin: "nav2_behaviors::Wait"

bt_navigator:
  ros__parameters:
    use_sim_time: true
    default_nav_to_pose_bt_xml: "$(find-pkg-share my_robot_navigation)/config/behavior_tree/navigate_w_recovery.xml"

global_costmap:
  global_costmap:
    ros__parameters:
      use_sim_time: true
      global_frame: map
      robot_base_frame: base_link
      resolution: 0.05
      robot_radius: 0.22
      plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
      static_layer:
        plugin: "nav2_costmap_2d::StaticLayer"
      obstacle_layer:
        plugin: "nav2_costmap_2d::ObstacleLayer"
        observation_sources: scan
        scan:
          topic: /scan
          data_type: "LaserScan"
          marking: true
          clearing: true
      inflation_layer:
        plugin: "nav2_costmap_2d::InflationLayer"
        inflation_radius: 0.55

local_costmap:
  local_costmap:
    ros__parameters:
      use_sim_time: true
      global_frame: odom
      robot_base_frame: base_link
      rolling_window: true
      width: 3
      height: 3
      resolution: 0.05
      robot_radius: 0.22
      plugins: ["obstacle_layer", "inflation_layer"]

lifecycle_manager:
  ros__parameters:
    use_sim_time: true
    autostart: true
    node_names:
      - controller_server
      - planner_server
      - behavior_server
      - bt_navigator
      - amcl

Consistency rules (these break navigation silently, not loudly):

  • robot_radius must match on global_costmap and local_costmap, and must be >= the robot's actual footprint radius from the URDF — use a footprint polygon instead of robot_radius for a non-circular base, applied identically in both costmaps.
  • inflation_radius should exceed robot_radius by a margin that reflects real localization and control error, not zero — too small and the planner routes paths that graze obstacles.
  • controller_server's controller_frequency should not exceed the rate at which local_costmap actually updates (update_frequency, default layer-dependent) — a controller running faster than its costmap updates is reacting to stale data.
  • global_costmap's global_frame is map (requires AMCL/SLAM providing map -> odom); local_costmap's global_frame is odom (works even without localization, from odometry alone) — swapping these is a common copy-paste mistake that breaks local avoidance the instant localization lags.
  • use_sim_time: true must be set identically across every node when running in Gazebo, and false on real hardware — a mismatch here causes TF extrapolation errors that look like a transform tree bug but are actually a clock source bug.
  • lifecycle_manager.node_names must list every lifecycle node actually brought up in the launch file, in an order where dependencies (e.g. amcl before nodes that consume /map -> /odom) are activated sensibly — a node left off this list never leaves the unconfigured state and bringup will hang waiting on it if anything depends on it.

3. Behavior tree basics

<root main_tree_to_execute="MainTree">
  <BehaviorTree ID="MainTree">
    <PipelineSequence name="NavigateWithReplanning">
      <RateController hz="1.0">
        <ComputePathToPose goal="{goal}" path="{path}" planner_id="GridBased"/>
      </RateController>
      <FollowPath path="{path}" controller_id="FollowPath"/>
    </PipelineSequence>
  </BehaviorTree>
</root>

Rules:

  • Reference planner_id/controller_id strings that exactly match the plugin keys registered in nav2_params.yaml (GridBased, FollowPath above) — a mismatched ID fails at runtime with an error that's easy to miss in a long bringup log.
  • Wrap replanning nodes in a RateController so the planner isn't invoked every tick — an un-throttled ComputePathToPose inside a tight BT loop can peg a CPU core.
  • Add RecoveryNode wrapping the main navigation subtree with fallback behaviors (spin, backup, wait) for production robots — a bare PipelineSequence with no recovery path means any transient planner failure aborts the whole navigation goal.

4. MoveIt 2 package layout

my_robot_moveit_config/
├── config/
│   ├── my_robot.srdf              # planning groups, disabled collision pairs, named states
│   ├── kinematics.yaml            # IK solver selection + params, per planning group
│   ├── joint_limits.yaml          # velocity/acceleration overrides for planning
│   ├── moveit_controllers.yaml    # maps planning groups to ros2_control action interfaces
│   └── ompl_planning.yaml         # planner IDs + projection evaluators, per group
└── launch/
    └── move_group.launch.py

5. SRDF, kinematics, and controller mapping

<!-- my_robot.srdf -->
<group name="arm">
  <chain base_link="base_link" tip_link="tool0"/>
</group>
<group_state name="home" group="arm">
  <joint name="joint_1" value="0"/>
  <joint name="joint_2" value="0"/>
</group_state>
<disable_collisions link1="link_1" link2="link_2" reason="Adjacent"/>
# kinematics.yaml
arm:
  kinematics_solver: kdl_kinematics_plugin/KDLKinematicsPlugin
  kinematics_solver_search_resolution: 0.005
  kinematics_solver_timeout: 0.05
  kinematics_solver_attempts: 3
# moveit_controllers.yaml
moveit_simple_controller_manager:
  controller_names: [arm_controller]
  arm_controller:
    type: FollowJointTrajectory
    action_ns: follow_joint_trajectory
    default: true
    joints: [joint_1, joint_2, joint_3, joint_4, joint_5, joint_6]

Rules:

  • Every <group> chain's base_link/tip_link must exist in the URDF exactly as spelled — MoveIt Setup Assistant generates this correctly from a loaded URDF, so prefer regenerating over hand-editing when the URDF's kinematic chain changes.
  • <disable_collisions> pairs come from Setup Assistant's self-collision sampling ("Adjacent", "Default", "Never" reasons) — never hand-add a disable_collisions entry to silence a real self-collision warning; fix the geometry or joint limits instead. A disabled pair that can actually collide is a real-world collision the planner will never avoid.
  • kinematics_solver_timeout and _attempts should be tuned so IK failures surface as a planning failure (safe) rather than the solver returning after an excessively long search that stalls a real-time-adjacent planning call.
  • moveit_controllers.yaml's joints: list must match, in the URDF's actual joint order, the joints in the SRDF <group> chain and the ros2_control hardware interface (see [[ros2-control]]) — a mismatched or reordered joint list sends trajectory points to the wrong physical joints.
  • Cross-check joint_limits.yaml velocity/acceleration overrides against the URDF's <limit> values on each joint (see the urdf-xacro-builder skill): MoveIt should never plan a trajectory that violates the URDF's own hard limits — if joint_limits.yaml is looser than the URDF, the executed trajectory can be rejected or, on some hardware interfaces, faulted at runtime.

6. Planning pipeline (OMPL) and using MoveGroupInterface

# ompl_planning.yaml
arm:
  planner_configs: [RRTConnectkConfigDefault]
  projection_evaluator: joints(joint_1,joint_2)
  longest_valid_segment_fraction: 0.005
from moveit.planning import MoveItPy

moveit = MoveItPy(node_name="moveit_py_client")
arm = moveit.get_planning_component("arm")
arm.set_start_state_to_current_state()
arm.set_goal_state(configuration_name="home")
plan_result = arm.plan()
if plan_result:
    moveit.execute(plan_result.trajectory, controllers=[])

Rules:

  • Set longest_valid_segment_fraction small enough that collision checking along a planned segment can't "tunnel" through a thin obstacle between sampled states — the OMPL default is often too coarse for a robot operating near cluttered fixtures.
  • Always check plan_result truthiness (or the equivalent success field in the C++ API) before calling execute — MoveIt returns a failed-but-non-throwing result on an infeasible goal, and executing an unchecked failed plan is undefined behavior.
  • Bring up move_group with the same use_sim_time convention as the rest of the stack, and make sure the ros2_control controller referenced in moveit_controllers.yaml is already active (via the controller manager) before the first execute() call, or the action goal will be rejected.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.