3、机器人URDF模型基础:URDF文件结构解析、link与joint的定义、标准机械臂模型示例(如UR5)

好,咱们进入第三章。URDF,全称是Unified Robot Description Format,统一机器人描述格式。说白了,它就是机器人的「身份证」加「骨架说明书」。你要让MoveIt认识你的机械臂,第一步就是给它一份合格的URDF文件。

我刚开始接触ROS时,觉得URDF不就是个XML嘛,随便写写就行。结果第一次加载模型,Gazebo里机械臂直接「散架」了——关节乱飞,连杆穿模。嗯,从那以后,我再也不敢小看这个文件了。

核心认知:URDF不是代码,它是数据。它描述的是机器人的运动学树——谁是谁的父级,谁绕着谁转,每个部件长什么样、多重、惯量多少。MoveIt拿到URDF后,才能构建出碰撞检测模型和运动学求解器。

3.1 URDF文件结构:一棵XML树

URDF本质上是一棵XML树。根节点是<robot>,里面包含两类核心子节点:<link>和<joint>。你可以把link想象成骨头,joint想象成关节。骨头之间靠关节连接,形成一条运动链。

一个最简单的URDF骨架长这样:

<robot name="my_robot">
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.2 0.2 0.1"/>
      </geometry>
    </visual>
  </link>

  <joint name="joint1" type="revolute">
    <parent link="base_link"/>
    <child link="arm_link"/>
    <origin xyz="0 0 0.1" rpy="0 0 0"/>
    <axis xyz="0 0 1"/>
    <limit lower="-3.14" upper="3.14" effort="10" velocity="1"/>
  </joint>

  <link name="arm_link">
    <visual>
      <geometry>
        <cylinder length="0.3" radius="0.05"/>
      </geometry>
    </visual>
  </link>
</robot>

你看,结构非常清晰。每个joint必须指定parent和child,这就构成了树的有向边。整个机器人就是一棵从base_link出发的树。

我的习惯:写URDF时,我会先画一张树状图,标出所有link和joint的父子关系。画清楚了再写XML,基本不会出错。别偷懒,这一步省不了。

3.2 link的定义:视觉、碰撞与惯性

每个link可以包含三个子块:<visual>、<collision>和<inertial>。它们各自负责不同的任务。

子块 作用 必须? 我的建议
<visual> 显示用的几何形状和材质 推荐有 用STL或DAE文件更真实
<collision> 碰撞检测用的简化几何体 强烈推荐 用box或cylinder代替复杂mesh,性能好很多
<inertial> 质量、质心和惯量矩阵 MoveIt需要 别填0,否则动力学计算会崩

这里有个坑,我踩过好几次。visual和collision可以不一样。比如你的机械臂外壳是个复杂的异形件,visual用STL文件显示得很漂亮。但collision我建议用个圆柱体或长方体包住它就行。为什么?因为碰撞检测时,复杂mesh的计算量非常大,实时性根本扛不住。

我曾经犯过的错:给一个六轴机械臂的每个link都用了精细STL做collision。结果MoveIt的规划速度从0.5秒变成了5秒。后来全部换成圆柱体和长方体,速度直接回到0.3秒。记住:collision geometry越简单越好。

惯性参数是另一个重灾区。很多初学者直接写<inertial><mass value="0"/></inertial>,然后抱怨MoveIt报错。质量不能为0,惯量矩阵也不能全0。如果你不知道具体数值,至少给个估算值。对于小零件,质量0.1kg、惯量用1e-6级别也能凑合跑。

3.3 joint的定义:类型、轴与极限

joint是URDF的灵魂。它决定了机械臂怎么动。joint有六种类型,但咱们做机械臂抓取,常用的就两种:

  • revolute(旋转关节):绕一个固定轴旋转,有角度上下限。UR5的六个关节全是这种。
  • continuous(连续旋转关节):可以无限旋转,没有限位。比如某些AGV的轮子。
  • prismatic(移动关节):直线滑动,有位移上下限。抓取场景用得少,但夹爪的张开闭合有时会用。
  • fixed(固定关节):焊死的,没有自由度。用于把传感器或末端工具固定在某个link上。

每个joint必须定义<parent>和<child>,这是树结构的边。然后是<origin>,它描述了child link相对于parent link的位姿。这个坐标变换非常关键,写错了整个机械臂的构型就歪了。

举个例子,UR5的肩关节(joint1)连接base_link和shoulder_link。origin的xyz通常设为0 0 0.08916,表示shoulder_link在base_link上方8.9厘米处。rpy是旋转的欧拉角,一般设0 0 0,除非关节安装有偏角。

<joint name="shoulder_pan_joint" type="revolute">
  <parent link="base_link"/>
  <child link="shoulder_link"/>
  <origin xyz="0 0 0.08916" rpy="0 0 0"/>
  <axis xyz="0 0 1"/>
  <limit lower="-6.2832" upper="6.2832" effort="150" velocity="3.15"/>
</joint>

注意<axis>,它定义了旋转轴的方向。xyz三个值表示轴向量。对于旋转关节,这个向量必须归一化。我见过有人写xyz="1 1 0",结果关节转起来是斜的,因为向量长度是√2,不是单位向量。

避坑指南:axis的xyz值最好只用0和1,并且确保只有一个1。比如0 0 1表示绕Z轴转。如果你非要写0.707 0.707 0,请确认你清楚自己在做什么。

3.4 标准机械臂模型示例:UR5

UR5是优傲机器人公司的明星产品,六轴协作机械臂。它的URDF文件是学习的最佳范本。咱们来看看它的运动学树结构:

UR5 运动学树结构 base_link shoulder_link upper_arm_link forearm_link wrist_1_link wrist_2_link shoulder_pan_joint shoulder_lift_joint elbow_joint wrist_1_joint wrist_2_joint UR5共6个旋转关节,全部为revolute类型,形成6自由度串联运动链

UR5的URDF文件在ROS官方仓库里可以找到。我建议你直接下载一份,对照着看。它的每个link都定义了完整的visual、collision和inertial,joint的limit参数也是真实值。这是最好的学习材料。

我个人习惯把UR5的URDF拆成三部分来理解:

  1. 基座部分:base_link,通常是一个圆盘或方形底座,固定在工作台上。
  2. 手臂部分:shoulder_link、upper_arm_link、forearm_link,这三个构成了主要的运动范围。
  3. 腕部部分:wrist_1_link、wrist_2_link、wrist_3_link,负责末端姿态调整。

你想想看,为什么UR5的腕部有三个关节?因为要实现末端执行器在三维空间中的任意姿态,至少需要三个旋转自由度。前三个关节决定位置,后三个关节决定姿态。这就是六轴机械臂的经典构型。

关键点:URDF中的joint顺序决定了运动学链的拓扑。MoveIt会按照这个顺序构建DH参数或旋量模型。如果你把joint的顺序搞反了,比如把wrist_1_joint写到了elbow_joint前面,那运动学求解器算出来的结果全是错的。

3.5 编写URDF的实用建议

最后,分享几条我这些年攒下来的经验:

  • 用xacro代替纯URDF:xacro是URDF的宏语言,支持变量、数学运算和文件包含。UR5的官方模型就是用xacro写的。比如你可以定义${arm_length}这样的变量,改一个值就能调整整条手臂的长度。
  • 坐标系命名要规范:base_link、shoulder_link、elbow_link……一眼就能看出是哪个部件。别用link_1、link_2这种名字,调试时你会疯掉的。
  • 验证工具要用起来:ROS提供了check_urdf命令,可以检查URDF的语法错误。urdf_to_graphiz可以生成树状图,直观看到父子关系。我每次写完URDF,第一件事就是跑这两个工具。
  • 别忘了末端执行器:抓取场景中,机械臂末端要装夹爪或吸盘。这个工具通常作为一个单独的link,通过fixed joint连接到wrist_3_link上。别把它写进机械臂本体里,方便以后更换。

又一个坑:URDF文件中的路径引用。如果你用了<mesh filename="package://my_robot/meshes/arm.stl"/>,请确保package路径正确。我见过有人把STL文件放错文件夹,结果模型加载出来全是默认的绿色方块。检查路径!检查路径!检查路径!