ROS 2 MoveIt 2 Guide 2026
MoveIt 2 is the de-facto motion-planning framework for ROS 2. This guide covers setup, SRDF authoring, planning groups, the MoveGroupInterface API, OMPL planner selection, collision objects, Cartesian paths, and trajectory execution.
MoveIt 2 Architecture Overview
Request Flow
Your Node → MoveGroupInterface → /move_group action server
↓
Planning Scene (collision) ←→ OMPL / PILZ / CHOMP planner
↓
Trajectory → FollowJointTrajectory → ros2_control controllers
Installation (Jazzy)
sudo apt install ros-jazzy-moveit # Optional extras sudo apt install ros-jazzy-moveit-planners-chomp # CHOMP planner sudo apt install ros-jazzy-pilz-industrial-motion-planners # PILZ PTP/LIN sudo apt install ros-jazzy-moveit-visual-tools # RViz helpers
SRDF — Semantic Robot Description Format
MoveIt 2 needs an SRDF alongside your URDF. It defines planning groups, end-effectors, and allowed collisions. Use moveit_setup_assistant or write it manually:
<?xml version="1.0" ?>
<robot name="my_arm">
<!-- Planning group: all joints from base to flange -->
<group name="arm">
<chain base_link="base_link" tip_link="tool0" />
</group>
<!-- End-effector group -->
<group name="gripper">
<joint name="left_finger_joint" />
<joint name="right_finger_joint" />
</group>
<!-- Named configurations -->
<group_state name="home" group="arm">
<joint name="joint1" value="0" />
<joint name="joint2" value="-1.57" />
<joint name="joint3" value="0" />
<joint name="joint4" value="-1.57" />
<joint name="joint5" value="0" />
<joint name="joint6" value="0" />
</group_state>
<!-- Disable collision between adjacent links -->
<disable_collisions link1="base_link" link2="link1" reason="Adjacent" />
<disable_collisions link1="link1" link2="link2" reason="Adjacent" />
<!-- End-effector definition -->
<end_effector name="gripper" parent_link="tool0" group="gripper" />
</robot>MoveGroupInterface — C++ Quick Start
#include <moveit/move_group_interface/move_group_interface.h> #include <moveit/planning_scene_interface/planning_scene_interface.h> #include <geometry_msgs/msg/pose.hpp> // In your node's constructor / callback auto move_group = moveit::planning_interface::MoveGroupInterface(node, "arm"); auto planning_scene = moveit::planning_interface::PlanningSceneInterface(); // ── Goal pose ────────────────────────────────────────────────────── geometry_msgs::msg::Pose target; target.orientation.w = 1.0; target.position.x = 0.4; target.position.y = 0.0; target.position.z = 0.5; move_group.setPoseTarget(target); // ── Planning ──────────────────────────────────────────────────────── moveit::planning_interface::MoveGroupInterface::Plan plan; bool ok = (move_group.plan(plan) == moveit::core::MoveItErrorCode::SUCCESS); // ── Execute ───────────────────────────────────────────────────────── if (ok) move_group.execute(plan);
Common API Calls
| Method | Purpose |
|---|---|
| setPoseTarget(pose) | Set Cartesian end-effector goal |
| setJointValueTarget(state) | Set named or numeric joint goal |
| setNamedTarget("home") | Use a named SRDF group state |
| setPositionTolerance(0.001) | Meters — default 1mm |
| setOrientationTolerance(0.01) | Radians — default ~0.57° |
| setMaxVelocityScalingFactor(0.5) | Fraction of URDF joint limits |
| setMaxAccelerationScalingFactor(0.3) | Fraction of URDF limits |
| getPlanningFrame() | Returns the planning frame (world/base_link) |
| getEndEffectorLink() | Returns the name of the EE link |
| move() | Plan + execute in one call (blocking) |
| asyncMove() | Non-blocking move |
| stop() | Halt execution immediately |
OMPL Planner Selection
| Planner | Best For | Tradeoff |
|---|---|---|
| RRTConnect | General 6-DOF arms | Fast but not optimal |
| RRTstar | Asymptotically optimal paths | Slow — use with long timeout |
| PRM | Repeated queries in same environment | Expensive to build, fast to query |
| TRRT | Torque/energy minimization | Needs cost map |
| BKPIECE1 | High-DOF / narrow passages | Balanced exploration |
# Set planner via API
move_group.setPlannerId("RRTConnectkConfigDefault");
move_group.setPlanningTime(10.0); // seconds
# Or in YAML (ompl_planning.yaml)
arm:
default_planner_config: RRTConnectkConfigDefault
planner_configs:
- RRTConnectkConfigDefault
- RRTstarkConfigDefaultCollision Objects and Planning Scene
#include <moveit_msgs/msg/collision_object.hpp>
#include <shape_msgs/msg/solid_primitive.hpp>
moveit_msgs::msg::CollisionObject box;
box.header.frame_id = move_group.getPlanningFrame();
box.id = "table";
// Define a box shape
shape_msgs::msg::SolidPrimitive primitive;
primitive.type = primitive.BOX;
primitive.dimensions = {1.0, 0.5, 0.05}; // x y z meters
geometry_msgs::msg::Pose pose;
pose.orientation.w = 1.0;
pose.position.x = 0.5;
pose.position.y = 0.0;
pose.position.z = 0.3;
box.primitives.push_back(primitive);
box.primitive_poses.push_back(pose);
box.operation = box.ADD;
// Apply to planning scene
planning_scene.applyCollisionObject(box);
// Attach an object to the end-effector (after grasping)
move_group.attachObject("box_to_pick", "tool0", {"left_finger", "right_finger"});
// Detach
move_group.detachObject("box_to_pick");Cartesian Paths
Cartesian paths move the EE through a list of waypoints in a straight line, useful for grasping and welding.
std::vector<geometry_msgs::msg::Pose> waypoints;
// Start from current pose
waypoints.push_back(move_group.getCurrentPose().pose);
// Move +10 cm in Z
geometry_msgs::msg::Pose wp1 = waypoints[0];
wp1.position.z += 0.1;
waypoints.push_back(wp1);
// Move +20 cm in X
geometry_msgs::msg::Pose wp2 = wp1;
wp2.position.x += 0.2;
waypoints.push_back(wp2);
moveit_msgs::msg::RobotTrajectory trajectory;
double fraction = move_group.computeCartesianPath(
waypoints,
0.01, // eef_step — interpolation resolution (meters)
0.0, // jump_threshold — 0 to disable
trajectory
);
RCLCPP_INFO(logger, "Cartesian path (%.2f%% achieved)", fraction * 100.0);
if (fraction > 0.95) move_group.execute(trajectory);Minimal MoveIt 2 Launch File
from launch import LaunchDescription
from launch_ros.actions import Node
from moveit_configs_utils import MoveItConfigsBuilder
def generate_launch_description():
moveit_config = (
MoveItConfigsBuilder("my_arm")
.robot_description(file_path="config/my_arm.urdf.xacro")
.robot_description_semantic(file_path="config/my_arm.srdf")
.trajectory_execution(file_path="config/moveit_controllers.yaml")
.planning_pipelines(pipelines=["ompl", "pilz_industrial_motion_planner"])
.to_moveit_configs()
)
return LaunchDescription([
Node(
package="moveit_ros_move_group",
executable="move_group",
output="screen",
parameters=[moveit_config.to_dict()],
),
Node(
package="rviz2",
executable="rviz2",
arguments=["-d", "config/moveit.rviz"],
parameters=[moveit_config.to_dict()],
),
])Controller Configuration
MoveIt 2 sends trajectories to ros2_control via FollowJointTrajectory action. Configure in moveit_controllers.yaml:
moveit_controller_manager: moveit_simple_controller_manager/MoveItSimpleControllerManager
moveit_simple_controller_manager:
controller_names:
- arm_controller
- gripper_controller
arm_controller:
type: FollowJointTrajectory
action_ns: follow_joint_trajectory
default: true
joints:
- joint1
- joint2
- joint3
- joint4
- joint5
- joint6
gripper_controller:
type: FollowJointTrajectory
action_ns: follow_joint_trajectory
default: true
joints:
- left_finger_joint
- right_finger_jointCommon Issues
Planning fails immediately
Check SRDF disable_collisions — initial state might be in self-collision. Run check_state_validity or increase allowed_planning_time.
IK solution not found
End-effector goal is out of reach. Print getEndEffectorLink() and verify URDF joint limits allow the pose.
Controller not found
moveit_controllers.yaml action_ns must match the action server name from ros2 action list. Add /arm_controller/ prefix if namespaced.
Trajectory execution aborted
Joint trajectory tolerances too tight. Loosen goal_time_tolerance or path_tolerance in the controller.
Collision object not visible
applyCollisionObject() call reached wrong node. Check PlanningSceneInterface constructor node context.
Next Steps
- Use ros2_control Hardware Interface to connect MoveIt 2 to real hardware.
- Add Nav2 SMAC Planner for mobile base + arm whole-body planning.
- Enable PILZ PTP/LIN planners for industrial straight-line segments between waypoints.