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.
In this repository5 other files it gives its agents.
- embedded-ros-bridgeSkill
- ros2-controlSkill
- ros2-ecosystem-packagesSkill
- ros2-workspaceSkill
- urdf-xacro-builderSkill
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
- Very specific: line 167
Add RecoveryNode wrapping the main navigation subtree with fallback behaviors (spin, backup, wait) for production robots — a bare …
- Very specific: line 220
Every <group> chain's base_link/tip_link must exist in the URDF exactly as spelled — MoveIt Setup Assistant generates this correctly from a …
- Very specific: line 267
Always check plan_result truthiness (or the equivalent success field in the C++ API) before calling execute — MoveIt returns a …
≈ “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.
Files
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.py2. 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
- amclConsistency rules (these break navigation silently, not loudly):
robot_radiusmust match onglobal_costmapandlocal_costmap, and must be>=the robot's actual footprint radius from the URDF — use afootprintpolygon instead ofrobot_radiusfor a non-circular base, applied identically in both costmaps.inflation_radiusshould exceedrobot_radiusby a margin that reflects real localization and control error, not zero — too small and the planner routes paths that graze obstacles.controller_server'scontroller_frequencyshould not exceed the rate at whichlocal_costmapactually updates (update_frequency, default layer-dependent) — a controller running faster than its costmap updates is reacting to stale data.global_costmap'sglobal_frameismap(requires AMCL/SLAM providingmap -> odom);local_costmap'sglobal_frameisodom(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: truemust be set identically across every node when running in Gazebo, andfalseon 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_namesmust list every lifecycle node actually brought up in the launch file, in an order where dependencies (e.g.amclbefore nodes that consume/map->/odom) are activated sensibly — a node left off this list never leaves theunconfiguredstate 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_idstrings that exactly match the plugin keys registered innav2_params.yaml(GridBased,FollowPathabove) — a mismatched ID fails at runtime with an error that's easy to miss in a long bringup log. - Wrap replanning nodes in a
RateControllerso the planner isn't invoked every tick — an un-throttledComputePathToPoseinside a tight BT loop can peg a CPU core. - Add
RecoveryNodewrapping the main navigation subtree with fallback behaviors (spin,backup,wait) for production robots — a barePipelineSequencewith 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.py5. 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'sbase_link/tip_linkmust 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 adisable_collisionsentry 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_timeoutand_attemptsshould 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'sjoints:list must match, in the URDF's actual joint order, the joints in the SRDF<group>chain and theros2_controlhardware interface (see [[ros2-control]]) — a mismatched or reordered joint list sends trajectory points to the wrong physical joints.- Cross-check
joint_limits.yamlvelocity/acceleration overrides against the URDF's<limit>values on each joint (see theurdf-xacro-builderskill): MoveIt should never plan a trajectory that violates the URDF's own hard limits — ifjoint_limits.yamlis 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.005from 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_fractionsmall 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_resulttruthiness (or the equivalent success field in the C++ API) before callingexecute— MoveIt returns a failed-but-non-throwing result on an infeasible goal, and executing an unchecked failed plan is undefined behavior. - Bring up
move_groupwith the sameuse_sim_timeconvention as the rest of the stack, and make sure theros2_controlcontroller referenced inmoveit_controllers.yamlis already active (via the controller manager) before the firstexecute()call, or the action goal will be rejected.
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
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.

