4. URDF模型解析:从零搭建你的机械臂“骨架”

好,咱们进入第四讲。前面几章我们把ROS2的基础环境、通信机制都捋了一遍,现在终于要动手做点“看得见摸得着”的东西了——写一个URDF文件,让机械臂在Rviz2里站起来。

URDF,全称是Unified Robot Description Format,说白了就是机器人的“骨架说明书”。它用XML格式告诉ROS2:你的机器人长什么样、关节在哪、能怎么动。我个人习惯把URDF看作机器人的“身份证”,没有它,后续的规划、控制、仿真全都无从谈起。

核心要点:URDF只描述机器人的运动学与外观,不涉及动力学(质量、惯性等)。如果你要做动力学仿真,需要额外添加<inertial>标签。

URDF 模型解析知识体系 URDF 文件 link(刚体部件) joint(关节连接) robot_state_publisher → TF变换 Rviz2 可视化显示

4.1 编写一个简单的URDF文件

先别想太复杂。咱们从最简单的两连杆机械臂开始。你想想看,一个机械臂无非就是:几个刚体(link)通过关节(joint)串起来。

下面这个例子,我定义了一个基座(base_link)和一个连杆(arm_link),中间用一个旋转关节连着。

<?xml version="1.0"?>
<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"/>
      <material name="blue">
        <color rgba="0 0 1 1"/>
      </material>
    </visual>
  </link>

  <!-- 关节 -->
  <joint name="arm_joint" 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="-1.57" upper="1.57" 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"/>
      <material name="red">
        <color rgba="1 0 0 1"/>
      </material>
    </visual>
  </link>
</robot>

小技巧:写URDF时,<origin>的xyz和rpy最容易搞混。我的经验是:先画个草图,标清楚每个link的原点位置,再动手写代码。曾经有一次我写反了z轴方向,结果机械臂“钻到地底下”去了,排查了半天才发现。

4.2 link与joint的定义——骨架与关节

咱们拆开来看。link和joint是URDF的两大核心,缺一不可。

link(刚体部件)

每个link代表一个刚体。你可以给它定义:

  • visual:视觉外观,用于Rviz2显示。说白了就是“长什么样”。
  • collision:碰撞模型,用于物理仿真。可以比visual更简化,提高计算效率。
  • inertial:惯性参数(质量、惯性张量),用于动力学计算。

嗯,这里要注意:如果你只是做运动学展示,只写visual就够了。但如果你要做抓取仿真,collision和inertial必须补上,否则物理引擎会报错。

joint(关节连接)

joint定义了父子link之间的运动关系。常用的关节类型有:

类型 说明 自由度
revolute 旋转关节,有角度限制 1
continuous 连续旋转关节,无限制(如车轮) 1
prismatic 平移关节,直线运动 1
fixed 固定关节,两个link刚性连接 0

我个人习惯把joint想象成“铰链”。你想想看,人的手臂能弯曲,靠的就是肘关节这个revolute joint。机械臂也一样,每个旋转关节对应一个电机。

避坑指南:我曾经犯过一个低级错误——joint的<origin>写错了位置,导致子link飞到了天边。记住:joint的origin是相对于parent link的坐标系。子link的origin是相对于joint的坐标系。这两个别搞混了。

4.3 使用robot_state_publisher发布TF变换

URDF文件写好了,怎么让ROS2知道它?这时候就需要robot_state_publisher登场了。

这个节点会读取你的URDF,然后根据joint的状态(角度/位置),自动计算出每个link之间的TF变换。说白了,它负责回答“我的机械臂现在是什么姿势”。

启动方式很简单,在launch文件里加上:

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='robot_state_publisher',
            executable='robot_state_publisher',
            parameters=[{'robot_description': open('simple_arm.urdf').read()}]
        )
    ])

嗯,这里有个细节:robot_description参数必须是一个字符串,所以要用open().read()把文件内容读进来。我刚开始用的时候,直接传了文件路径,结果节点一直报错找不到模型。

启动之后,你可以用ros2 topic echo /tf_static看看发布的TF变换。你会看到类似这样的信息:

transforms:
  - header:
      frame_id: base_link
    child_frame_id: arm_link
    transform:
      translation:
        x: 0.0
        y: 0.0
        z: 0.1
      rotation:
        x: 0.0
        y: 0.0
        z: 0.0
        w: 1.0

这就是robot_state_publisher根据你的URDF和joint状态,自动算出来的坐标变换。

4.4 在Rviz2中显示模型

最后一步,也是最激动人心的一步——在Rviz2里看到你的机械臂。

启动Rviz2:

ros2 run rviz2 rviz2

然后在Rviz2界面中:

  1. 将Fixed Frame设置为base_link(你的机器人基座坐标系)。
  2. 点击左下角的Add按钮,选择RobotModel。
  3. 在RobotModel的属性中,将Robot Description设置为robot_description。

如果一切顺利,你应该能看到一个蓝色的方块(基座)上面连着一根红色的圆柱(连杆)。

常见问题:如果Rviz2里显示“No transform from [base_link] to [arm_link]”,说明robot_state_publisher没有正确运行。检查一下:

  • URDF文件是否有语法错误?用check_urdf命令检查。
  • launch文件中的robot_description参数是否正确加载?
  • joint的<origin>是否合理?

我记得第一次在Rviz2里看到自己写的机械臂动起来时,那种成就感真的难以形容。虽然只是一个简单的两连杆,但那是你亲手“造”出来的机器人。

进阶玩法:你可以用ros2 topic pub /joint_states sensor_msgs/msg/JointState手动发布关节角度,看看机械臂在Rviz2里实时运动。这就是后面章节做运动控制的基础。

好了,这一章的内容就到这里。URDF是ROS2机器人开发的基石,花点时间把link和joint的关系理清楚,后面做运动规划、抓取控制都会顺畅很多。