ROS2系列教程:Moveit2环境与机械臂建模准备

🕒 阅读时间:15 分钟📝 字数:3370👀 阅读量:Loading...

本文是 ROS2 系列教程的第 35 篇

本文是 ROS2 系列教程的第 35 篇:MoveIt2 环境与机械臂建模准备。第 31~34 篇我们完成了移动底盘机器人的完整实战(建图、导航、任务、安全)。从本篇开始,系列进入最后一个大阶段:MoveIt2 机械臂篇。第 35 篇先解决“地基”:MoveIt2 是什么、环境怎么装、机械臂怎么用 URDF/Xacro 描述、怎么用工具自动生成 MoveIt2 配置包,最后用 RViz 可视化验证模型。移动机器人关心“轮子转多少”,机械臂关心“关节动多少”——坐标系与运动学思维将从这里焕然一新。

一、MoveIt2 到底是什么

1.1 一句话理解

MoveIt2 是 ROS2 的机械臂运动规划框架:你给它一个机械臂的 URDF 模型和一个目标位姿(比如“把手伸到桌上那个杯子”),它负责规划出一条无碰撞的关节轨迹,并交给控制器执行。

它不是一个单体程序,而是一组模块的集合:

MoveIt2 核心模块
├── Move Group 节点 # 中枢:接收目标,调度规划
├── Robot Model / URDF # 机械臂几何与运动学描述
├── Planning Scene # 场景:障碍物、自碰撞、允许碰撞矩阵
├── Motion Planner # 规划器:OMPL(默认)/ Pilz / STOMP
├── Kinematics # 运动学:正向 FK / 逆解 IK(KDL / IKFast / TRAC-IK)
├── Collision Checking # 碰撞检测:FCL 库
├── MoveIt 控制接口 # 与 ros2_control 对接(第 28 篇基础)
└── RViz 插件(MotionPlanning) # 可视化与手动拖拽目标

1.2 MoveIt2 vs MoveIt1

对比 MoveIt1(ROS1) MoveIt2(ROS2)
驱动接口 自定义 ControllerManager ros2_control 统一管理
参数 YAML + rosparam YAML + rclcpp 参数节点
规划器 OMPL OMPL(C++ 重构版)
行为树 SMACH Behavior Trees(可选)
维护状态 维护模式 活跃开发

学习建议:直接学 MoveIt2。教程环境是 Humble 版 MoveIt2,与 ROS2 Humble 一一对应。

二、环境安装(一步到位)

2.1 二进制安装(推荐)

MoveIt2 官方提供二进制包,比源码编译快得多:

Terminal window
# 1. 确认 ROS2 Humble 已安装
source /opt/ros/humble/setup.bash
echo $ROS_DISTRO # 期望输出 humble
# 2. 安装 MoveIt2 全部二进制包
sudo apt install ros-humble-moveit
# 3. 安装机械臂模型(示例用 Panda 或 UR5)
sudo apt install ros-humble-panda-moveit-config
# 4. 验证安装
ros2 pkg list | grep moveit
# 期望看到:moveit_core moveit_ros_planning moveit_ros_planning_interface 等

安装完成后用一个小测试确认能跑:

Terminal window
ros2 launch moveit2_tutorials demo.launch.py
# 或直接用 Panda 演示
ros2 launch panda_moveit_config demo.launch.py

如果能看到 RViz 里出现一个 Panda 机械臂模型(7 个关节的红色/灰色手臂),环境就绪。

2.2 源码编译(可选,进阶)

二进制满足不了需求(想改 MoveIt2 源码)时再走源码:

Terminal window
mkdir -p ~/moveit2_ws/src && cd ~/moveit2_ws
wget https://raw.githubusercontent.com/moveit/moveit2/humble/moveit2.repos
vcs import src < moveit2.repos
rosdep install -r --from-paths src --ignore-src --rosdistro humble -y
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release

源码编译约需 20~40 分钟(视机器性能),编译失败常见原因是依赖缺失,跑 rosdep install 即可。

2.3 安装验证清单

Terminal window
# 快速自查 5 项
ros2 pkg prefix moveit_core # 1. 核心包存在
ros2 pkg prefix moveit_ros_planning_interface # 2. 接口包存在
ls /opt/ros/humble/share/moveit_ros_visualization # 3. RViz 插件存在
which ros2 # 4. ROS2 可用
echo $ROS_DISTRO # 5. 版本正确

三、机械臂的 URDF 描述(与移动机器人的差异)

3.1 机械臂 URDF 的独特之处

第 16 篇我们写过移动底盘的 URDF。机械臂 URDF 有三点明显不同:

  1. 关节类型是 revolute(旋转):移动底盘的轮子是 continuous(连续旋转),机械臂关节是有限角度旋转,用 <limit> 限制关节范围。
  2. 每个关节一个电机:不是“两轮差速”一个控制器,而是每个关节一个 joint 控制器。
  3. 末端执行器(end effector):最末端的 link 是夹爪/吸盘/工具,MoveIt2 要单独知道它。

一个简化版两连杆机械臂 URDF(理解结构用):

<!-- two_link_arm.urdf:两连杆机械臂(简化教学版) -->
<robot name="two_link_arm">
<!-- 基座:固定在地面 -->
<link name="base_link">
<visual>
<geometry><box size="0.2 0.2 0.1"/></geometry>
</visual>
<collision>
<geometry><box size="0.2 0.2 0.1"/></geometry>
</collision>
</link>
<!-- 肩关节:绕 z 轴旋转,-90°~90° -->
<joint name="shoulder_joint" type="revolute">
<parent link="base_link"/>
<child link="shoulder_link"/>
<origin xyz="0 0 0.1" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-1.57" upper="1.57" effort="10" velocity="1.0"/>
</joint>
<link name="shoulder_link">
<visual>
<geometry><box size="0.05 0.05 0.4"/></geometry>
<origin xyz="0 0 0.2"/>
</visual>
</link>
<!-- 肘关节:绕 y 轴旋转(另一个方向) -->
<joint name="elbow_joint" type="revolute">
<parent link="shoulder_link"/>
<child link="elbow_link"/>
<origin xyz="0 0 0.4" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.57" upper="1.57" effort="10" velocity="1.0"/>
</joint>
<link name="elbow_link">
<visual>
<geometry><box size="0.05 0.05 0.35"/></geometry>
<origin xyz="0 0 0.175"/>
</visual>
</link>
<!-- 末端执行器 -->
<link name="tool0"/>
<joint name="tool_joint" type="fixed">
<parent link="elbow_link"/>
<child link="tool0"/>
<origin xyz="0 0 0.35"/>
</joint>
</robot>

三个关键点:

  • <limit lower upper>:必填,MoveIt2 规划不能越过关节限位。
  • <axis>:旋转轴方向(肩绕 z,肘绕 y),写错轴模型会“乱扭”。
  • 末端固定关节:工具坐标系 tool0 用固定关节挂在末杆上。

3.2 用 Xacro 参数化(正式项目必用)

真实机械臂(如 Panda、UR5)连杆多、参数繁,全部写死在一个 URDF 里维护困难。用 Xacro 宏定义“连杆 + 关节”模板:

<!-- arm.xacro:参数化机械臂 -->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="arm">
<!-- 定义一个连杆宏:名字 + 长度 + 半径 -->
<xacro:macro name="arm_link" params="prefix length radius">
<link name="${prefix}_link">
<visual>
<geometry><cylinder radius="${radius}" length="${length}"/></geometry>
<origin xyz="0 0 ${length/2}"/>
<material name="gray"><color rgba="0.5 0.5 0.5 1"/></material>
</visual>
<collision>
<geometry><cylinder radius="${radius}" length="${length}"/></geometry>
<origin xyz="0 0 ${length/2}"/>
</collision>
</link>
<joint name="${prefix}_joint" type="revolute">
<parent link="${prefix}_base"/>
<child link="${prefix}_link"/>
<axis xyz="0 0 1"/>
<limit lower="-3.14" upper="3.14" effort="20" velocity="1.5"/>
</joint>
</xacro:macro>
<!-- 调用 3 次生成三连杆 -->
<xacro:arm_link prefix="link1" length="0.3" radius="0.04"/>
<xacro:arm_link prefix="link2" length="0.3" radius="0.04"/>
<xacro:arm_link prefix="link3" length="0.2" radius="0.03"/>
</robot>

好处:改长度只改一处参数;加一节手臂只加一行调用。真实机械臂的 xacro 都这么组织(link 定义 + joint 定义分离 + 宏复用)。

3.3 检查模型的必备工具

模型写完先自查,再进 MoveIt2:

Terminal window
# 1. 语法与几何检查
check_urdf arm.urdf # 输出 link/joint 树,报错会指出问题
# 2. Xacro 展开为纯 URDF(MoveIt2 用的是展开后的)
xacro arm.xacro > arm_generated.urdf
# 3. RViz 可视化
ros2 launch urdf_tutorial display.launch.py model:=arm_generated.urdf
# 或手动:robot_state_publisher + RViz 加载
# 4. TF 树确认
ros2 run tf2_ros tf2_echo base_link tool0

check_urdf 输出应显示完整父子链:base_link → shoulder → elbow → tool0。有断链说明 joint 的 parent/child 写错了。

四、示例 1:用工具生成 MoveIt2 配置包

手工写 MoveIt2 配置(SRDF、规划组、控制器 YAML、launch)繁琐且易错。官方工具 moveit_setup_assistant 一条龙生成。

4.1 启动 Setup Assistant

Terminal window
ros2 run moveit_setup_assistant moveit_setup_assistant

界面操作流程(英文界面,按顺序):

  1. Create New MoveIt Configuration Package → 选择你的 URDF/Xacro 文件(如 arm_generated.urdf)。
  2. Self-Collisions:点 “Generate Collision Matrix” 自动计算自碰撞矩阵(哪些 link 对永远不会碰,直接排除,省规划时间)。
  3. Virtual Joints:机械臂装在移动底盘上时,加一个虚拟关节 virtual_joint(固定类型,parent 为 world/odom)。纯固定机械臂可跳过。
  4. Planning Groups:点 “Add Group”,勾选所有关节组成一个运动组(如 arm_group,Kinematic Solver 选 kdl_kinematics_plugin/KDLKinematicsPlugin)。
  5. Robot Poses(可选):预设几个常用姿态(home、vertical 等),便于测试。
  6. End Effectors:把 tool0 标记为末端执行器,命名 eef_group,parent link 填 tool0。
  7. Passive Joints / Controllers:默认即可,控制器 YAML 由助理生成。
  8. Author Information:填包名 arm_moveit_config。
  9. 点 Generate Package 选择输出目录,生成完毕。

4.2 生成的包结构

arm_moveit_config/
├── config/
│ ├── arm.srdf # 语义描述:规划组/虚拟关节/末端
│ ├── joint_limits.yaml # 关节限位(从 URDF 提取,可覆盖)
│ ├── kinematics.yaml # 逆解求解器配置(KDL)
│ ├── ompl_planning.yaml # OMPL 规划器配置
│ ├── moveit_controllers.yaml # 与 ros2_control 的映射
│ └── move_group.yaml # move_group 节点参数
├── launch/
│ ├── demo.launch.py # 一键演示:RViz + move_group + 仿真控制器
│ ├── move_group.launch.py
│ └── setup_assistant.launch.py
├── package.xml
└── CMakeLists.txt

4.3 一键测试生成的包

Terminal window
ros2 launch arm_moveit_config demo.launch.py

成功标准:RViz 出现机械臂模型 + MoveIt MotionPlanning 面板。此时已经可以用“拖拽末端目标”或“规划执行”按钮让虚拟机械臂动起来——第 36 篇将详细讲运动规划,这里先确认链路通了。

五、示例 2:手动编写 SRDF(理解原理)

Setup Assistant 自动生成 SRDF,但理解它才能改它。SRDF(Semantic Robot Description Format)告诉 MoveIt2 “哪些 link 组成了一个可规划的组”。核心三块:

<!-- arm.srdf 核心片段(手动版) -->
<robot name="arm">
<!-- 1. 规划组:arm_group 包含全部 3 个关节 -->
<group name="arm_group">
<joint name="shoulder_joint"/>
<joint name="elbow_joint"/>
<joint name="wrist_joint"/>
</group>
<!-- 2. 末端执行器组 -->
<group name="eef_group">
<link name="tool0"/>
</group>
<end_effector name="eef" parent_link="elbow_link" group="eef_group"/>
<!-- 3. 默认姿态:规划前回到 home 位 -->
<group_state name="home" group="arm_group">
<joint name="shoulder_joint" value="0.0"/>
<joint name="elbow_joint" value="0.0"/>
<joint name="wrist_joint" value="0.0"/>
</group_state>
<!-- 4. 禁用碰撞对:肩-肘永远不会碰,告诉规划器不用查 -->
<disable_collisions link1="base_link" link2="shoulder_link" reason="Adjacent"/>
<disable_collisions link1="shoulder_link" link2="elbow_link" reason="Adjacent"/>
</robot>
  • group:规划的最小单位,MoveIt2 对整个组做运动学计算。
  • group_state:预设姿态,测试时一键回到某姿态。
  • disable_collisions:相邻连杆必选(它们本来就固定相连),不相邻的靠 Setup Assistant 自动算。

六、示例 3:kinematics.yaml 与逆解求解器

6.1 逆解(IK)是什么

  • 正解 FK:给定关节角度 → 求末端位置(唯一,好算)。
  • 逆解 IK:给定末端目标位姿 → 求关节角度(可能无解/多解,难)。

MoveIt2 用运动学插件做 IK。kinematics.yaml 声明用哪个插件:

# kinematics.yaml:声明 IK 求解器
arm_group:
kinematics_solver: kdl_kinematics_plugin/KDLKinematicsPlugin
kinematics_solver_search_resolution: 0.005
kinematics_solver_timeout: 0.005
kinematics_solver_attempts: 3

常用求解器对比:

求解器 特点 适用
KDL(默认) 通用数值解,够用 大多数机械臂
TRAC-IK KDL 加强版,成功率更高 复杂构型 / 奇异位形
IKFast 解析解,快且准 6 自由度且运动学结构简单

6.2 手写测试 IK

Terminal window
# 用 moveit 的 IK 命令行工具测试(需先启动 demo)
ros2 launch arm_moveit_config demo.launch.py
# 在另一个终端调用 IK 服务(服务名随配置可能不同)
ros2 service list | grep compute_ik

规划失败先怀疑 IK:把末端目标挪到工作空间内(离基座 0.3~0.5m、不越过肩关节正上方),再试。

七、MoveIt2 与 ros2_control 的对接(真机准备)

第 28 篇讲过 ros2_control。MoveIt2 规划出的轨迹要执行,必须通过 ros2_control 发给关节控制器。对接的“合同”就是 moveit_controllers.yaml:

# moveit_controllers.yaml:MoveIt2 到 ros2_control 的映射
moveit_simple_controller_manager:
controller_names:
- arm_controller
arm_controller:
type: FollowJointTrajectory
action_ns: follow_joint_trajectory
default: true
joints:
- shoulder_joint
- elbow_joint
- wrist_joint

配合 ros2_control 端的控制器配置(第 28 篇的 joint_trajectory_controller):

# arm_controllers.yaml(ros2_control 侧)
controller_manager:
ros__parameters:
update_rate: 100
arm_controller:
type: joint_trajectory_controller/JointTrajectoryController
arm_controller:
ros__parameters:
joints:
- shoulder_joint
- elbow_joint
- wrist_joint
command_interfaces: [position]
state_interfaces: [position]

职责划分:MoveIt2 负责“想清楚怎么动”(规划),ros2_control 负责“真去动”(发指令给电机)。两者通过 Action 通信:MoveIt2 发 FollowJointTrajectory 目标,控制器执行后回报结果。真机篇(第 40 篇)会完整展开。

八、常见问题与排障

现象 原因 解决
安装后 ros2 pkg list 查不到 moveit 没 source /opt/ros/humble/setup.bash source 后再查
demo.launch.py 报包找不到 没 build / 没 source 工作空间 colcon build + source install/setup.bash
RViz 无机械臂模型 robot_description 没发布 检查 robot_state_publisher 是否启动
关节乱扭 URDF 的 axis 写错 逐个核对 joint 的 axis 与物理轴一致
逆解无解 目标超出工作空间 目标移到工作空间内(半径 = 臂长总和)
规划失败但没报错 目标与自碰撞冲突 查看 Planning Scene,检查初始姿态
Setup Assistant 打不开 缺 GUI 库 / 显示问题 确认有桌面环境;检查 DISPLAY 变量
生成的包 demo 报控制器错 控制器 YAML 未生成/未 source 检查 moveit_controllers.yaml 是否被加载

九、小结与下一篇预告

本篇核心:

  1. MoveIt2 是“规划框架”不是单个程序:Move Group 中枢 + OMPL 规划 + KDL 逆解 + FCL 碰撞检测 + RViz 可视化。
  2. 机械臂 URDF 与移动底盘不同:revolute 关节 + 限位 + 末端执行器,用 Xacro 宏参数化。
  3. Setup Assistant 一键生成配置包(SRDF / kinematics / controllers),理解 SRDF 才能手改。
  4. MoveIt2 规划、ros2_control 执行,两者靠 Action 对接——真机控制的地基。

至此,MoveIt2 的“地基”已打好:环境装好、模型建好、配置包生成、可视化验证通过。

下一篇(第 36 篇)「MoveIt2 仿真与运动规划(基础)」:深入 demo 启动流程,用 MoveIt2 的 Python/C++ 接口写第一个运动规划程序,理解 move_group 的 API(设置目标、规划、执行、轨迹可视化),并动手控制仿真机械臂做点到点运动与笛卡尔路径运动。

ROS2系列教程:Moveit2环境与机械臂建模准备

作者:Wang Shijie

本文链接:https://wsj-blog.pages.dev/posts/ros2-xi-lie-jiao-cheng-moveit2-huan-jing-yu-ji-xie-bi-jian/

本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

Creative Commons

留言评论