3. SRDF 与配置包:从模型到可动实体的关键一步
好,我们继续。上一章我们把 URDF 模型搭好了,机器人看起来有模有样了。但你会发现——它动不了。为什么?因为 URDF 只描述了「长什么样」,没告诉 MoveIt 「该怎么动」。
这就好比给你一辆车的图纸,但没给方向盘、油门和刹车。SRDF(Semantic Robot Description Format)就是干这个的。它给 URDF 模型「注入灵魂」,告诉 MoveIt 哪些关节能转、哪些零件会碰撞、规划路径时该避开什么。
我个人习惯把 SRDF 叫做「机器人的运动说明书」。没有它,MoveIt 就是个瞎子。
核心一句话:URDF 是骨架,SRDF 是神经和肌肉。两者缺一不可。
3.1 SRDF 文件到底长什么样?
SRDF 本质上是一个 XML 文件,通常命名为 <robot_name>.srdf。它不描述几何形状,只描述语义信息。我截取一个实际项目中的片段给你看:
<robot name="my_robot_arm">
<group name="arm_group">
<joint name="shoulder_pan_joint"/>
<joint name="shoulder_lift_joint"/>
<joint name="elbow_joint"/>
<joint name="wrist_1_joint"/>
<joint name="wrist_2_joint"/>
<joint name="wrist_3_joint"/>
</group>
<group name="gripper_group">
<joint name="gripper_left_finger_joint"/>
<joint name="gripper_right_finger_joint"/>
</group>
<disable_collisions link1="base_link" link2="arm_link_1" reason="Adjacent"/>
<disable_collisions link1="arm_link_2" link2="arm_link_3" reason="Never"/>
</robot>
看到没?它主要干了三件事:
- 定义规划组(Group):把关节打包成逻辑单元,比如「整条手臂」或「夹爪」。
- 声明碰撞豁免:告诉 MoveIt 「这两个连杆虽然挨着,但永远不会碰撞,别浪费时间检查它们」。
- 定义虚拟关节(可选):把机器人固定在某个参考系上,比如「底座焊在地面上」。
嗯,这里要注意:SRDF 不写死任何运动学参数,它只做「标记」。真正的运动学计算由 MoveIt 的运动学插件(比如 KDL、TRAC-IK)完成。
3.2 配置包生成:Setup Assistant 实战
手动写 SRDF?别傻了,我早期干过这事,写错一个标签名排查了俩小时。后来我学乖了——用 MoveIt Setup Assistant,一个图形化工具,点几下鼠标就能生成完整的配置包。
启动命令很简单:
roslaunch moveit_setup_assistant setup_assistant.launch
界面起来后,你会看到一个向导。我带你走一遍核心步骤:
- 加载 URDF 模型:点击 "Create New MoveIt Configuration Package",选择你的 URDF 文件。工具会自动解析所有连杆和关节。
- 自碰撞矩阵(Self-Collision Matrix):这是最关键的步骤。工具会扫描所有连杆对,自动生成一个「哪些对永远不会碰撞」的列表。默认采样密度是 10000 个随机位姿,我个人习惯调到 50000,覆盖率更高。
- 添加规划组:比如创建一个 "arm_group",把肩部到腕部的关节拖进去。你可以指定运动学求解器(KDL 是默认的,够用)。
- 定义预设位姿:比如 "home" 位姿(所有关节归零)、"vertical" 位姿(手臂竖直向上)。这些在后续编程中可以直接调用。
- 设置虚拟关节:如果你的机器人是固定底座,创建一个虚拟关节把 base_link 固定到 world 上。
- 生成配置包:点击 "Generate Package",输入包名,比如 "my_robot_moveit_config"。工具会生成一堆文件。
我的经验:生成后别急着用。先打开 config/ompl_planning.yaml,把规划时间从默认的 5 秒改成 2 秒。5 秒太保守了,实际调试时你会等疯的。
3.3 碰撞矩阵:为什么它这么重要?
碰撞矩阵(Collision Matrix)说白了就是一张「谁和谁不用检查碰撞」的表格。MoveIt 默认会检查所有连杆对是否碰撞,但很多对是永远不可能碰上的——比如相隔很远的两个连杆。
如果不做豁免,规划器会浪费大量 CPU 时间在无意义的碰撞检测上。我曾经在一个 7 轴机器人上没优化碰撞矩阵,规划一次路径要 8 秒;优化后降到 1.2 秒。你想想看,这差距有多大。
碰撞矩阵的典型规则:
| 规则类型 | 含义 | 示例 |
|---|---|---|
| Adjacent | 相邻连杆,通过关节连接,永远不会碰撞 | base_link 和 arm_link_1 |
| Never | 物理上不可能接触的两个连杆 | arm_link_2 和 gripper_finger |
| Default | 需要实时碰撞检测的连杆对 | arm_link_3 和周围障碍物 |
Setup Assistant 生成的默认矩阵已经覆盖了大部分 Adjacent 和 Never 情况。但如果你有特殊结构(比如并联机构),建议手动检查并补充。
避坑指南:我曾经把一个 "Never" 规则误写成 "Adjacent",结果两个本不该碰撞的连杆在某个奇异位姿下卡住了,规划器死活找不到路径。排查了三天才发现是碰撞矩阵写错了。所以——生成后一定要用 check_collision 工具验证一遍。
3.4 默认规划组:你的运动逻辑单元
规划组(Planning Group)是 MoveIt 的核心概念。你把一组关节打包成一个组,然后对这个组做运动规划。比如:
- arm_group:包含 6 个关节,用于笛卡尔空间运动。
- gripper_group:包含 2 个关节,用于夹爪开合。
- whole_body:包含所有关节,用于全身协调运动。
每个规划组可以指定不同的运动学求解器。我一般这样配:
| 规划组 | 求解器 | 适用场景 |
|---|---|---|
| arm_group | KDL | 通用,稳定,适合大多数工业臂 |
| gripper_group | 无(直接位置控制) | 夹爪不需要逆解,直接发关节位置 |
| whole_body | TRAC-IK | 复杂场景,速度比 KDL 快 3-5 倍 |
在 SRDF 中,规划组的定义很简单:
<group name="arm_group">
<joint name="joint_1"/>
<joint name="joint_2"/>
<joint name="joint_3"/>
<joint name="joint_4"/>
<joint name="joint_5"/>
<joint name="joint_6"/>
</group>
注意:关节的顺序不重要,MoveIt 会自动按 URDF 中的父子关系排序。但如果你有多个分支(比如双臂机器人),一定要分清楚每个组包含哪些关节。
3.5 知识体系总览
我把这一章的核心逻辑画成了一张图,方便你理解 SRDF 和配置包在整个 MoveIt 体系中的位置:
从这张图你可以看到,URDF 是输入,经过 Setup Assistant 处理后,输出三个核心产物:SRDF 文件、碰撞矩阵、规划组定义。这三者最终打包成 MoveIt 配置包,供后续的运动规划和控制使用。