MoveIt 2 is the de facto standard for motion planning in ROS 2, enabling robotic arms to plan, execute, and adapt trajectories with collision avoidance. This guide covers installation, configuration, and practical use cases—from basic planning to real-time control—using MoveIt 2.8.0 (May 2026) on ROS 2 Jazzy.
TL;DR
- Install MoveIt 2 via
ros-jazzy-moveitor build from source for customization. - Use the MoveIt Setup Assistant to generate URDF/SRDF configs for your robot.
- Plan motions with OMPL planners (e.g.,
RRTConnectkConfigDefault) and visualize in RViz. - Enable MoveIt Servo for real-time trajectory tracking with
ros2_control. - Integrate perception (e.g., camera feeds) via ROS 2 topics and
moveit_msgs/PlanningScene.
1. Installation and the MoveIt Setup Assistant
Prerequisites
- Ubuntu 24.04 (Noble Numbat) + ROS 2 Jazzy (install guide).
- Python 3.10+ (MoveIt 2.8.0 requires Python 3.10+ due to
moveit_commanderupdates). - Git and colcon (for source builds).
Verify your setup:
echo $ROS_DISTRO # Should output "jazzy"
python3 --version # Should output 3.10+
Install MoveIt 2
Option A: Binary Install (Recommended)
sudo apt update
sudo apt install ros-jazzy-moveit
source /opt/ros/jazzy/setup.bash
Verify installation:
ros2 pkg prefix moveit_ros # Should return /opt/ros/jazzy
Option B: Source Build (For Customization)
mkdir -p ~/moveit2_ws/src && cd ~/moveit2_ws/src
git clone https://github.com/ros-planning/moveit2.git -b main
vcs import < moveit2/moveit2.repos
cd ~/moveit2_ws && colcon build --symlink-install
source install/setup.bash
Launch MoveIt Demo
Test with the Panda robot (included in MoveIt):
ros2 launch moveit2_demos panda_moveit_demo.launch.py
- Expected Output: RViz should open with the Panda arm and a planning scene.
- Gotcha: If RViz fails, ensure
ros-jazzy-rviz2andros-jazzy-ogreare installed:sudo apt install ros-jazzy-rviz2 ros-jazzy-ogre
2. Configuring a Robot (URDF/SRDF)
Step 1: Prepare Your URDF
MoveIt requires a URDF (robot description) and SRDF (semantic robot description). Use the MoveIt Setup Assistant to generate configs.
Example URDF (Simplified Panda Arm)
<!-- panda.urdf.xacro -->
<robot name="panda" xmlns:xacro="http://www.ros.org/wiki/xacro">
<link name="world"/>
<joint name="panda_joint" type="fixed">
<parent link="world"/>
<child link="panda_link0"/>
</joint>
<!-- Add your robot's joints/links here -->
</robot>
Step 2: Run the Setup Assistant
ros2 run moveit_setup_assistant setup_assistant
- Input:
- URDF file:
path/to/your_robot.urdf.xacro - Output directory:
~/moveit2_ws/src/your_robot_moveit_config
- URDF file:
- Output: Generates:
config/srdf/(semantic descriptions)config/joint_limits.yaml(joint constraints)launch/(launch files)
Step 3: Build and Source
cd ~/moveit2_ws && colcon build --packages-select your_robot_moveit_config
source install/setup.bash
Step 4: Launch Your Robot in MoveIt
ros2 launch your_robot_moveit_config demo.launch.py
- Expected Output: RViz with your robot’s kinematic tree and collision objects.
Gotchas
- Missing Joint Limits: If joints don’t move, check
joint_limits.yaml:joint_limits: panda_joint: has_velocity_limits: true max_velocity: 1.0 has_acceleration_limits: true max_acceleration: 2.0 - Collision Mesh Errors: Ensure all
<collision>and<visual>tags in URDF have<geometry>(e.g.,<mesh filename="package://meshes/panda_link.dae"/>). - SRDF Validation: Run:
ros2 run srdfdom check_srdf your_robot.srdf
3. Motion Planning with OMPL
Plan a Joint-Space Trajectory
Use moveit_commander (Python) or move_group (C++). Here’s a Python example:
#!/usr/bin/env python3
import rclpy
from moveit_commander import MoveGroupCommander
def plan_joint_trajectory():
rclpy.init()
group = MoveGroupCommander("panda_arm") # Group name from SRDF
group.set_planner_id("RRTConnectkConfigDefault") # OMPL planner
# Set a joint goal (radians)
joint_goal = [0.0, -0.785, 0.0, -2.356, 0.0, 1.571, 0.785]
group.set_joint_value_target(joint_goal)
# Plan and execute
plan = group.go(wait=True)
if plan:
print("Trajectory executed successfully!")
else:
print("Planning failed.")
if __name__ == "__main__":
plan_joint_trajectory()
rclpy.shutdown()
Key OMPL Planners in MoveIt 2.8.0
| Planner | Use Case | Notes |
|---|---|---|
RRTConnectkConfigDefault | General-purpose | Fast, good for redundant robots. |
TRRT | High-dimensional spaces | Better for many DOFs. |
ESTkConfigDefault | Precision tasks | Slower but more accurate. |
PilzIndustrial | Industrial arms (Pilz-compatible) | Requires moveit_pilz package. |
Visualize the Plan in RViz
- Add a Motion Planning display in RViz.
- Select your group (
panda_arm). - Click "Plan" to see the trajectory in red.
Gotchas
- Planning Failures: If
group.go()returnsFalse, try:- A different planner:
group.set_planner_id("TRRT"). - Adjusting
max_iterationsin the planner config:# config/planning_pipelines/panda.srdf planner_configs: RRTConnectkConfigDefault: max_iterations: 1000000
- A different planner:
- Self-Collisions: Ensure your URDF’s
<collision>tags are correct. Use:ros2 run moveit_ros_planning_interface check_collisions
4. Collision Checking and Planning Scenes
Add Objects to the Planning Scene
def add_box_to_scene():
scene = group.get_planning_scene()
box_pose = geometry_msgs.msg.PoseStamped()
box_pose.header.frame_id = "panda_link0"
box_pose.pose.position.z = 0.2 # Offset from end effector
scene.add_box("box1", box_pose, (0.1, 0.1, 0.1)) # (x, y, z) dimensions
scene.async_update() # Non-blocking update
Check Collisions
def check_collision():
collision_state = group.get_current_state()
result = group.check_state_collision(collision_state)
print(f"Collision detected: {result}")
Load/Save Scenes
# Save scene to file
scene.export_to_xml_file("scene.xml")
# Load scene
scene.load_from_xml_file("scene.xml")
Gotchas
- Stale Scene Data: Always call
scene.async_update()after modifications. - Object Frames: Ensure all object poses are in a valid frame (e.g.,
panda_link0). - Performance: For many objects, use
scene.async_update()to avoid blocking.
5. Cartesian Paths and Pick-and-Place
Plan a Cartesian Path
def plan_cartesian_path():
group.set_max_velocity_scaling_factor(0.5) # Slow down for precision
waypoints = []
# Start at current pose
waypoints.append(group.get_current_pose().pose)
# Define intermediate waypoints (6D pose: x,y,z,roll,pitch,yaw)
pose_target = geometry_msgs.msg.Pose()
pose_target.position.x = 0.3
pose_target.position.y = 0.1
pose_target.position.z = 0.2
pose_target.orientation.w = 1.0
waypoints.append(pose_target)
# Plan and execute
(plan, fraction) = group.compute_cartesian_path(
waypoints,
eef_step=0.01, # End-effector step size
jump_threshold=0.0
)
group.execute(plan)
Pick-and-Place Example
def pick_and_place():
# Approach the object
group.set_pose_target(pick_pose)
plan = group.go(wait=True)
# Attach the object (simulated)
group.attach_object("box1")
# Move to place pose
group.set_pose_target(place_pose)
plan = group.go(wait=True)
# Detach the object
group.detach_object("box1")
Gotchas
- Frame Mismatches: Ensure all poses are in the same frame (e.g.,
base_link). - Singularities: Cartesian paths may fail near singularities. Use:
group.set_goal_joint_tolerance(0.001) # Tighter tolerance - Gripper Control: For grippers, use
ros2_controlinterfaces (see Section 6).
6. MoveIt Servo for Real-Time Control
MoveIt Servo enables real-time trajectory tracking using ros2_control. This is critical for dynamic environments or high-speed tasks.
Prerequisites
- Install
ros2_controland hardware interfaces:sudo apt install ros-jazzy-ros2-control ros-jazzy-ros2-controllers
Configure Servo
-
Add Servo to Your Launch File:
<!-- launch/servo_demo.launch.py --> <launch> <include file="$(find your_robot_moveit_config)/launch/demo.launch.py"/> <node pkg="moveit_servo" exec="servo_node" name="servo_node"> <param name="robot_description" type="string" command="$(param robot_description)"/> <param name="group_name" value="panda_arm"/> <param name="servo_period" value="0.01"/> <!-- 100Hz --> </node> </launch> -
Run Servo:
ros2 launch your_robot_moveit_config servo_demo.launch.py
Send Real-Time Commands
def servo_cartesian_motion():
servo = MoveItServoCommander("panda_arm")
servo.start()
# Define a waypoint
waypoint = geometry_msgs.msg.PoseStamped()
waypoint.header.frame_id = "base_link"
waypoint.pose.position.x = 0.4
# Send at 100Hz
servo.send_cartesian_waypoint(waypoint, 0.01) # 0.01s interval
Gotchas
- Latency: Servo requires a real-time kernel for sub-10ms latency. Use:
sudo apt install ros-jazzy-ros2-realtime-tools - Controller Tuning: Adjust
servo_period(default: 0.01s). Lower values increase jitter. - Hardware Compatibility: Ensure your robot’s drivers support
ros2_control(e.g.,franka_ros2for Franka
