2. 机器人URDF模型基础

URDF,全称是Unified Robot Description Format。说白了,它就是机器人的「身份证」加「骨架图」。你要让Gazebo和MoveIt认识你的机械臂,第一步就是得用URDF把它的样子说清楚。

我个人习惯把URDF理解成「搭积木」。你定义一堆零件(link),再用关节(joint)把它们连起来。嗯,就这么简单。

2.1 URDF语法:其实就两个核心标签

URDF是基于XML的。你只要掌握两个标签,就能搞定90%的建模工作:<link> 和 <joint>。

我刚开始学的时候,总想把所有参数一次性背下来。后来发现,记住核心结构就够了,细节查文档就行。来,先看一个最简机械臂的骨架:

<robot name="simple_arm">
    <link name="base_link">
        <visual>
            <geometry>
                <cylinder length="0.1" radius="0.05"/>
            </geometry>
        </visual>
    </link>

    <joint name="joint1" type="revolute">
        <parent link="base_link"/>
        <child link="arm_link"/>
        <origin xyz="0 0 0.05" rpy="0 0 0"/>
        <axis xyz="0 0 1"/>
    </joint>

    <link name="arm_link">
        <visual>
            <geometry>
                <box size="0.02 0.02 0.2"/>
            </geometry>
        </visual>
    </link>
</robot>

你看,一个底座(base_link),一根手臂(arm_link),中间用一个旋转关节(joint1)连起来。这就是一个最简单的单关节机械臂。

我的小技巧:写URDF时,先把link的名字起好。我习惯用 base_link、link1、link2、end_effector 这种命名方式。名字乱了,后面调试MoveIt时会疯掉。

2.2 link定义:视觉、碰撞、惯性三件套

每个link都有三个核心部分:visual(视觉)、collision(碰撞)、inertial(惯性)。

我在项目中遇到过一个问题:机械臂在Gazebo里仿真时,明明看着没碰到东西,却一直报碰撞。后来发现,我只定义了visual,没定义collision。Gazebo默认用visual做碰撞检测,但形状对不上。所以,collision一定要单独定义。

来看一个完整的link定义:

<link name="arm_link">
    <!-- 视觉部分:你看到的样子 -->
    <visual>
        <origin xyz="0 0 0.1" rpy="0 0 0"/>
        <geometry>
            <box size="0.04 0.04 0.2"/>
        </geometry>
        <material name="blue">
            <color rgba="0 0 1 1"/>
        </material>
    </visual>

    <!-- 碰撞部分:物理引擎算碰撞用的 -->
    <collision>
        <origin xyz="0 0 0.1" rpy="0 0 0"/>
        <geometry>
            <box size="0.04 0.04 0.2"/>
        </geometry>
    </collision>

    <!-- 惯性部分:算动力学必须的 -->
    <inertial>
        <origin xyz="0 0 0.1" rpy="0 0 0"/>
        <mass value="0.5"/>
        <inertia ixx="0.001" ixy="0" ixz="0"
                 iyx="0" iyy="0.001" iyz="0"
                 izx="0" izy="0" izz="0.001"/>
    </inertial>
</link>
注意:惯性矩阵(inertia)是新手最容易忽略的。如果你不写,Gazebo会报错。我曾经偷懒没算,随便填了个0.001,结果机械臂仿真时抖得像筛糠。后来老老实实按质量分布算了,才稳定下来。

2.3 joint定义:关节类型与坐标系

joint是连接link的桥梁。URDF支持6种关节类型,但机械臂最常用的是这几种:

类型 说明 典型用途
revolute 旋转关节,有角度限制 肩关节、肘关节
continuous 连续旋转关节,无限制 腕部旋转
prismatic 平移关节 滑轨、升降台
fixed 固定连接 传感器安装座

joint定义里,<origin> 和 <axis> 是最容易出错的。origin定义了子link相对于父link的位置偏移。axis定义了旋转轴的方向。

举个例子:

<joint name="shoulder_pan" 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="-3.14" upper="3.14" effort="10" velocity="1"/>
</joint>

这里,shoulder_link 在 base_link 上方0.1米处,绕Z轴旋转。limit定义了关节的运动范围、最大力矩和速度。

避坑指南:我曾经把origin的rpy写反了,结果机械臂的关节朝向全乱了。记住,rpy的顺序是绕固定轴:先绕X轴(roll),再绕Y轴(pitch),最后绕Z轴(yaw)。不是你想的绕自身轴!

2.4 标准机械臂URDF建模实战

好了,理论说完了。咱们动手搭一个6轴机械臂。我以常见的协作臂结构为例:

先画个结构图,让你对整个骨架有个概念:

6轴机械臂URDF骨架结构 base_link J1 shoulder_link J2 arm_link J3 forearm_link J4 wrist_link J5 end_effector link joint

你看,从底座到末端,一层层往上搭。每个joint连接两个link,形成一个运动链。

下面是一个完整的6轴机械臂URDF片段(只展示关键部分):

<robot name="six_axis_arm" xmlns:xacro="http://www.ros.org/wiki/xacro">
    <!-- 底座 -->
    <link name="base_link">
        <visual>
            <geometry>
                <cylinder length="0.05" radius="0.08"/>
            </geometry>
        </visual>
    </link>

    <!-- 关节1:腰部旋转 -->
    <joint name="joint1" type="revolute">
        <parent link="base_link"/>
        <child link="link1"/>
        <origin xyz="0 0 0.025" rpy="0 0 0"/>
        <axis xyz="0 0 1"/>
        <limit lower="-3.14" upper="3.14" effort="20" velocity="1.5"/>
    </joint>

    <link name="link1">
        <visual>
            <geometry>
                <box size="0.06 0.06 0.15"/>
            </geometry>
        </visual>
    </link>

    <!-- 关节2:肩部俯仰 -->
    <joint name="joint2" type="revolute">
        <parent link="link1"/>
        <child link="link2"/>
        <origin xyz="0 0 0.075" rpy="0 0 0"/>
        <axis xyz="0 1 0"/>
        <limit lower="-2.09" upper="2.09" effort="20" velocity="1.5"/>
    </joint>

    <!-- ... 后续关节类似,依次定义link3、link4、link5、link6 -->

    <!-- 末端执行器 -->
    <joint name="joint6" type="continuous">
        <parent link="link5"/>
        <child link="end_effector"/>
        <origin xyz="0 0 0.1" rpy="0 0 0"/>
        <axis xyz="0 0 1"/>
    </joint>

    <link name="end_effector">
        <visual>
            <geometry>
                <cylinder length="0.02" radius="0.02"/>
            </geometry>
        </visual>
    </link>
</robot>
我的建议:实际项目中,别手写这么长的URDF。用xacro宏定义,把重复的link和joint模板化。比如定义一个「标准连杆」宏,传个长度和颜色参数就行。我后来都是这么干的,省时省力。

2.5 验证你的URDF

写完了怎么知道对不对?ROS提供了两个好用的工具:

  • check_urdf:检查语法错误。我每次改完URDF,第一件事就是跑这个命令。
  • urdf_to_graphiz:生成结构图。能直观看到link和joint的层级关系。

用法很简单:

# 检查URDF
check_urdf my_arm.urdf

# 生成结构图
urdf_to_graphiz my_arm.urdf

如果一切正常,你会看到类似这样的输出:

robot name is: six_axis_arm
---------- Successfully Parsed XML ----------
root link: base_link has 1 child(ren)
    child(1):  link1
        child(1):  link2
            ...

嗯,看到「Successfully Parsed XML」就说明你的URDF没问题了。

曾经踩过的坑:有一次我改了URDF,忘了重新运行check_urdf,结果在Gazebo里加载时直接崩溃。排查了半天,发现是joint的parent和child写反了。所以,每次修改后,务必验证!

好了,URDF的基础就这些。说白了就是定义好每个零件的形状、位置、质量,然后用关节把它们串起来。下一节,我们会把这个URDF加载到Gazebo里,让它动起来。

可可精品资料库