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

界面起来后,你会看到一个向导。我带你走一遍核心步骤:

  1. 加载 URDF 模型:点击 "Create New MoveIt Configuration Package",选择你的 URDF 文件。工具会自动解析所有连杆和关节。
  2. 自碰撞矩阵(Self-Collision Matrix):这是最关键的步骤。工具会扫描所有连杆对,自动生成一个「哪些对永远不会碰撞」的列表。默认采样密度是 10000 个随机位姿,我个人习惯调到 50000,覆盖率更高。
  3. 添加规划组:比如创建一个 "arm_group",把肩部到腕部的关节拖进去。你可以指定运动学求解器(KDL 是默认的,够用)。
  4. 定义预设位姿:比如 "home" 位姿(所有关节归零)、"vertical" 位姿(手臂竖直向上)。这些在后续编程中可以直接调用。
  5. 设置虚拟关节:如果你的机器人是固定底座,创建一个虚拟关节把 base_link 固定到 world 上。
  6. 生成配置包:点击 "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 体系中的位置:

SRDF与配置包知识体系 SRDF 与配置包核心逻辑 URDF 模型 MoveIt Setup Assistant SRDF 文件 碰撞矩阵 规划组定义 虚拟关节 / 预设位姿 Adjacent / Never / Default 运动学求解器绑定 MoveIt 配置包 配置包 = SRDF + 碰撞矩阵 + 规划组 + 参数

从这张图你可以看到,URDF 是输入,经过 Setup Assistant 处理后,输出三个核心产物:SRDF 文件、碰撞矩阵、规划组定义。这三者最终打包成 MoveIt 配置包,供后续的运动规划和控制使用。