机械臂模型基础:URDF文件结构、link与joint的定义、在Rviz中可视化模型

做机器人仿真,第一步就是搞定模型描述。说白了,你得告诉计算机:这个机械臂长什么样?关节在哪?能怎么动?

URDF(Unified Robot Description Format)就是干这个的。它用XML格式,把机器人拆成一个个零件(link)和连接件(joint)。我刚开始接触时也觉得这东西有点繁琐,但后来发现——模型建得越清楚,后面调试越省心。

URDF文件长什么样?

一个完整的URDF文件,核心就两样东西:link 和 joint。你可以把link想象成骨骼,joint就是关节。骨骼决定形状,关节决定运动方式。

来看个最简单的例子——两连杆机械臂:

<robot name="simple_arm">
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.2 0.2 0.1"/>
      </geometry>
      <origin xyz="0 0 0" rpy="0 0 0"/>
    </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"/>
    <limit lower="-3.14" upper="3.14" effort="10" velocity="1"/>
  </joint>

  <link name="arm_link">
    <visual>
      <geometry>
        <cylinder length="0.4" radius="0.02"/>
      </geometry>
      <origin xyz="0 0 0.2" rpy="0 0 0"/>
    </visual>
  </link>
</robot>

嗯,结构很清晰。根节点是 <robot>,里面包着link和joint。每个link有名字,有几何形状。每个joint连接两个link,定义运动类型和范围。

link的定义——机械臂的每一块骨头

link描述的是刚体部分。每个link可以包含三块内容:

  • visual:可视化用的几何形状,Rviz里看到的就是它
  • collision:碰撞检测用的形状,可以比visual简单些,省计算量
  • inertial:惯性参数,质量、质心、惯量矩阵——做动力学仿真时必须填

我见过不少新手只写visual,不写collision和inertial。结果呢?仿真时要么碰撞检测不准,要么动力学计算报错。尤其是惯性参数,很多人嫌麻烦就随便填个1。我曾经在一个项目里吃过这个亏——机械臂仿真跑起来看着正常,一换真实硬件就抖得不行。后来查了半天,发现是惯量矩阵写错了。

关键点:做运动学仿真可以只写visual,但要做动力学仿真,inertial必须准确。质量、质心位置、惯量矩阵,一个都不能少。

几何形状支持三种基本类型:

形状 标签 参数 适用场景
长方体 <box> size="长 宽 高" 基座、连接件
圆柱体 <cylinder> length radius 连杆、臂体
球体 <sphere> radius 关节球、末端

你想想看,实际机械臂的形状千奇百怪,光靠这三种基本几何肯定不够。所以URDF还支持用 <mesh> 导入STL或DAE文件。我一般先用CAD软件导出STL,再在URDF里引用,这样模型更真实。

joint的定义——关节怎么动

joint是URDF里最讲究的部分。它决定了两个link之间的相对运动关系。joint的类型有六种:

类型 说明 自由度 常见用途
revolute 旋转关节,有角度限制 1 肩关节、肘关节
continuous 连续旋转,无限制 1 车轮、转台
prismatic 平移关节,有行程限制 1 伸缩臂、滑轨
fixed 固定连接,不能动 0 法兰连接、传感器安装
floating 6自由度浮动 6 移动基座、无人机
planar 平面运动(2平移+1旋转) 3 AGV底盘

joint里最重要的几个属性:

  • parent/child:父子关系,从父link指向子link。注意,URDF里joint的方向是从父到子,别搞反了
  • origin:joint在父link坐标系下的位置和姿态。这个坐标决定了子link相对于父link的初始位置
  • axis:旋转轴或平移方向。revolute和prismatic必须指定
  • limit:运动范围、最大力矩、最大速度。revolute和prismatic必须写

注意:origin和axis的坐标系都是相对于父link的。我曾经见过有人把axis写成了相对于世界坐标系,结果关节转起来方向全错了。调试了半天才发现是坐标系理解有误。

坐标系与变换树

URDF本质上定义了一棵变换树。每个joint就是一个变换节点,从父link坐标系变换到子link坐标系。Rviz里看到的机器人模型,就是沿着这棵树逐级计算出来的。

我习惯用 tf2 的工具来验证变换树是否正确。启动机器人后,在终端输入:

rosrun tf2_tools view_frames.py

会生成一个PDF,里面画出了所有坐标系之间的变换关系。如果树结构不对,一眼就能看出来。

下面这张图展示了URDF中link和joint的组织方式:

URDF 模型结构:link 与 joint 的树形组织 base_link joint1 (revolute) arm_link_1 joint2 (revolute) arm_link_2 gripper_link 关键规则 • 树形结构,无环 • 每个joint连接 一个parent和一个child • 只有一个根link 图例 基座 link 连杆 link 关节 joint 末端 link

在Rviz中可视化模型

模型写好了,怎么验证对不对?Rviz是最直观的工具。你不需要写任何代码,只要一个launch文件就能把模型加载进去。

基本步骤:

  1. 创建一个功能包,把URDF文件放进去
  2. 写一个launch文件,加载URDF并启动Rviz
  3. 在Rviz中添加RobotModel显示组件
  4. 添加TF显示组件,看坐标系对不对

一个最简的launch文件长这样:

<launch>
  <param name="robot_description" 
         textfile="$(find my_robot_description)/urdf/my_arm.urdf"/>
  <node name="robot_state_publisher" 
        pkg="robot_state_publisher" type="robot_state_publisher"/>
  <node name="rviz" pkg="rviz" type="rviz" 
        args="-d $(find my_robot_description)/config/display.rviz"/>
</launch>

这里有个关键点——robot_state_publisher。它负责读取URDF里的joint信息,发布tf变换。没有它,Rviz里只能看到一堆link飘在那,动不起来。

小技巧:第一次在Rviz里看模型时,记得把"Fixed Frame"设成你的根link名字(比如base_link)。默认是"map",如果没设置就会报错。我刚开始用Rviz时,卡在这步卡了半小时。

模型加载成功后,你可以用 joint_state_publisher 的GUI界面拖动关节,实时看机械臂动起来。这个工具会读取URDF里joint的limit范围,生成滑块。拖动滑块,模型就跟着动——非常直观。

常见坑与避坑指南

做URDF模型时,有几个坑我几乎每次都会遇到:

  • 坐标系搞反:origin的xyz和rpy,一定要搞清楚是相对于父link还是世界坐标系。答案是父link
  • joint方向反了:axis写反了方向,关节转起来和预期相反。解决办法:在Rviz里拖动joint,看实际旋转方向
  • link之间穿透:几何尺寸和origin没算好,两个link重叠在一起。我一般先在CAD里量好尺寸,再填到URDF里
  • 惯性参数乱填:动力学仿真时模型乱飞,十有八九是惯量矩阵写错了。建议用SolidWorks或Fusion360导出准确的惯性参数

我曾经在一个六轴机械臂项目里,因为一个joint的axis写成了 0 1 0 而不是 0 0 1,导致整个臂的运动学解算全错。查了两天才找到问题。嗯,从那以后我每次写完URDF,第一件事就是在Rviz里手动拖一遍所有关节。

URDF是机器人仿真的基石。模型建得扎实,后面的运动学、动力学、轨迹规划才能顺利推进。别急着跳过这一步——花时间把模型调好,后面能省十倍的时间。