MoveIt2 Robot Arm Guide 2026
Complete MoveIt2 guide from installation to real robot deployment — SRDF config, Python API, OMPL planners, collision avoidance, and Cartesian paths.
1. Installation
Install MoveIt2
Install MoveIt2 for ROS 2 Jazzy or Humble.
# ROS 2 Jazzy (Ubuntu 24.04) sudo apt install ros-jazzy-moveit -y # ROS 2 Humble (Ubuntu 22.04) # sudo apt install ros-humble-moveit -y # Additional packages sudo apt install ros-jazzy-moveit-visual-tools \ ros-jazzy-moveit-setup-assistant \ ros-jazzy-moveit-ros-planning-interface -y
💡 The moveit meta-package installs move_group, OMPL, CHOMP, PILZ planners, RViz plugin, and Python bindings.
Run MoveIt2 demo with Panda arm
Verify your installation with the Franka Panda demo — the standard MoveIt2 reference platform.
# Install Panda demo package sudo apt install ros-jazzy-moveit-resources-panda-moveit-config -y # Launch interactive demo ros2 launch moveit_resources_panda_moveit_config demo.launch.py # You should see RViz2 with the Panda arm. # Use the interactive marker to drag the end-effector and click 'Plan & Execute'.
2. MoveIt2 Configuration
Configure your robot for MoveIt2 with SRDF, kinematics.yaml, and ompl_planning.yaml.
SRDF — Semantic Robot Description
Create the SRDF (semantic description) that defines planning groups, poses, and collision rules. Example for a 6-DOF arm:
<?xml version="1.0"?>
<robot name="my_robot">
<!-- Planning group for the arm -->
<group name="arm">
<chain base_link="base_link" tip_link="tool0"/>
</group>
<!-- Planning group for the gripper -->
<group name="gripper">
<joint name="finger_joint_1"/>
<joint name="finger_joint_2"/>
</group>
<!-- Named poses -->
<group_state name="home" group="arm">
<joint name="joint_1" value="0"/>
<joint name="joint_2" value="-1.57"/>
<joint name="joint_3" value="1.57"/>
<joint name="joint_4" value="-1.57"/>
<joint name="joint_5" value="0"/>
<joint name="joint_6" value="0"/>
</group_state>
<!-- End-effector -->
<end_effector name="eef" parent_link="tool0"
group="gripper" parent_group="arm"/>
<!-- Disable self-collision for adjacent links -->
<disable_collisions link1="link_1" link2="link_2" reason="Adjacent"/>
</robot>kinematics.yaml — IK Solver Configuration
Configure the IK solver. KDL is default; pick3ikfast or bio_ik for better performance.
arm: kinematics_solver: kdl_kinematics_plugin/KDLKinematicsPlugin kinematics_solver_search_resolution: 0.005 kinematics_solver_timeout: 0.005 # For better performance, use bio_ik: # kinematics_solver: bio_ik/BioIKKinematics # kinematics_solver_attempts: 10 gripper: kinematics_solver: kdl_kinematics_plugin/KDLKinematicsPlugin kinematics_solver_search_resolution: 0.005 kinematics_solver_timeout: 0.005
💡 bio_ik (sudo apt install ros-jazzy-bio-ik) handles redundant 7-DOF arms like Franka much better than KDL.
ompl_planning.yaml — Planner Configuration
Configure OMPL planners. RRTConnect is fastest; for quality paths use BIT* or LBTRRT.
arm:
default_planner_config: RRTConnectkConfigDefault
planner_configs:
- RRTConnectkConfigDefault # fastest, good for most tasks
- RRTstarkConfigDefault # asymptotically optimal
- BITstarkConfigDefault # best quality, slower
- PRMkConfigDefault # good for repeated queries
longest_valid_segment_fraction: 0.005
planner_configs:
RRTConnectkConfigDefault:
type: geometric::RRTConnect
range: 0.0
BITstarkConfigDefault:
type: geometric::BITstar
stop_on_solution_improvement: false3. Python API
Use moveit_py to plan and execute robot motions from Python scripts or ROS 2 nodes.
Move to a named pose
Use MoveGroupCommander to move the arm to a named pose defined in the SRDF.
import rclpy
from rclpy.node import Node
from moveit.planning import MoveItPy
from moveit.core.robot_state import RobotState
import time
def main():
rclpy.init()
node = Node("moveit_py_demo")
# Initialize MoveItPy — connects to move_group
robot = MoveItPy(node_name="moveit_py")
arm = robot.get_planning_component("arm")
# Move to named pose "home" (defined in SRDF)
arm.set_start_state_to_current_state()
arm.set_goal_state(configuration_name="home")
plan_result = arm.plan()
if plan_result:
robot.execute(plan_result.trajectory, controllers=[])
print("Moved to home pose!")
else:
print("Planning failed!")
time.sleep(2.0)
rclpy.shutdown()
if __name__ == "__main__":
main()Move to a 6D pose goal
Specify an exact end-effector pose using geometry_msgs/Pose.
from geometry_msgs.msg import PoseStamped
# Set pose goal for end-effector
pose_goal = PoseStamped()
pose_goal.header.frame_id = "base_link"
pose_goal.pose.position.x = 0.4
pose_goal.pose.position.y = 0.0
pose_goal.pose.position.z = 0.4
pose_goal.pose.orientation.x = 0.0
pose_goal.pose.orientation.y = 0.0
pose_goal.pose.orientation.z = 0.0
pose_goal.pose.orientation.w = 1.0
arm.set_start_state_to_current_state()
arm.set_goal_state(
pose_stamped_msg=pose_goal,
pose_link="tool0" # link to achieve the pose
)
plan_result = arm.plan()
if plan_result:
robot.execute(plan_result.trajectory, controllers=[])Cartesian path (straight-line motion)
Compute a Cartesian path for the end-effector to follow a straight line. Essential for insertion tasks.
from moveit.core.kinematic_constraints import construct_joint_constraint
from moveit_msgs.msg import RobotTrajectory
import copy
# Build waypoints for a straight-line forward motion
waypoints = []
# Start from current pose
arm.set_start_state_to_current_state()
current_pose = arm.get_current_pose("tool0").pose
# Move 10cm forward in X
for i in range(1, 6):
wpose = copy.deepcopy(current_pose)
wpose.position.x += 0.02 # 2cm per step
waypoints.append(copy.deepcopy(wpose))
# Compute Cartesian path — eef_step=1mm resolution
(plan, fraction) = arm.compute_cartesian_path(
waypoints,
eef_step=0.001, # 1mm resolution
avoid_collisions=True,
)
print(f"Cartesian path coverage: {fraction * 100:.1f}%")
if fraction > 0.95: # >95% path achieved
robot.execute(plan, controllers=[])💡 fraction < 1.0 means the path was blocked by collision before the last waypoint. Tune eef_step or adjust waypoints.
Add collision objects to the scene
Add box/sphere/mesh collision objects to the Planning Scene so MoveIt2 avoids them.
from moveit.planning import PlanningSceneMonitor
from shape_msgs.msg import SolidPrimitive
from moveit_msgs.msg import CollisionObject
from geometry_msgs.msg import Pose
# Get the planning scene interface
psm = robot.get_planning_scene_monitor()
with psm.read_write() as scene:
# Add a box obstacle (e.g., a table)
collision_object = CollisionObject()
collision_object.header.frame_id = "base_link"
collision_object.id = "table"
box = SolidPrimitive()
box.type = SolidPrimitive.BOX
box.dimensions = [0.6, 0.8, 0.05] # 60x80x5cm
box_pose = Pose()
box_pose.position.x = 0.5
box_pose.position.y = 0.0
box_pose.position.z = -0.025
box_pose.orientation.w = 1.0
collision_object.primitives = [box]
collision_object.primitive_poses = [box_pose]
collision_object.operation = CollisionObject.ADD
scene.apply_collision_object(collision_object)
scene.current_state.update()
print("Table added to planning scene. MoveIt2 will now avoid it.")5. Real Robot Deployment
Connect MoveIt2 to physical robots using official ROS 2 drivers.
Universal Robots — launch with MoveIt2
Connect MoveIt2 to a real Universal Robot using the official ROS 2 driver.
# Install UR ROS 2 driver sudo apt install ros-jazzy-ur -y # For UR10e at IP 192.168.1.100: ros2 launch ur_robot_driver ur_control.launch.py \ ur_type:=ur10e \ robot_ip:=192.168.1.100 \ use_fake_hardware:=false \ launch_rviz:=false # In a second terminal, launch MoveIt2: ros2 launch ur_moveit_config ur_moveit.launch.py \ ur_type:=ur10e \ use_fake_hardware:=false
💡 Set use_fake_hardware:=true for simulation without real hardware. The robot IP must be reachable and the robot must be in 'remote control' mode.
Franka FR3 — launch with MoveIt2
Connect MoveIt2 to a real Franka Research 3 arm (requires FCI license).
# Install franka_ros2 sudo apt install ros-jazzy-franka-ros2 -y # On a real Franka FR3: ros2 launch franka_fr3_moveit_config moveit.launch.py \ robot_ip:=192.168.1.10 # This launches: move_group, RViz2, franka_robot_state_broadcaster # FCI (Franka Control Interface) must be unlocked via Desk web UI first
6. MoveIt2 Planner Comparison
| Planner | Type | Speed | Quality | Best for |
|---|---|---|---|---|
| RRTConnect | Sampling | ⚡⚡⚡ | ⭐⭐ | Most tasks — fastest to any valid path |
| RRT* | Sampling | ⚡⚡ | ⭐⭐⭐ | When path quality matters, given time budget |
| BIT* | Informed | ⚡ | ⭐⭐⭐⭐⭐ | Best path quality, long planning time OK |
| CHOMP | Gradient | ⚡⚡ | ⭐⭐⭐⭐ | Smooth, collision-minimizing trajectories |
| PILZ PTP | Analytic | ⚡⚡⚡⚡ | ⭐⭐⭐ | Point-to-point industrial motions (deterministic) |
| PILZ LIN | Analytic | ⚡⚡⚡⚡ | ⭐⭐⭐ | Linear (Cartesian) motions — assembly, insertion |