3. 机器人模型基础:URDF模型结构、Xacro宏定义、传感器与执行器配置、模型可视化与调试
做具身智能,第一件事是什么?不是写算法,不是调参数,而是先把你的机器人「描述清楚」。
你想想看,仿真环境不认识你的机器人长什么样、关节在哪、传感器装在哪。你得给它一份「说明书」。这份说明书,就是 URDF。
我刚开始接触这个的时候,觉得 URDF 不就是个 XML 嘛,写起来应该不难。结果第一次把模型丢进 Gazebo,机器人直接在地上劈了个叉——关节限位写反了。嗯,从那以后我再也不敢小看这个「说明书」了。
3.1 URDF 模型结构:机器人的「骨架」
URDF(Unified Robot Description Format)说白了就是用 XML 描述机器人的运动学树。核心就两个元素:link 和 joint。
- link:机器人的刚体部件。比如底盘、手臂连杆、轮子。每个 link 有视觉、碰撞、惯性三个属性。
- joint:连接两个 link 的关节。定义了父子关系、类型(旋转、滑动、固定)、限位、原点偏移。
我习惯把 URDF 想象成一棵倒挂的树。根节点通常是 base_link,然后通过 joint 挂上轮子、挂上躯干、挂上手臂。每个 link 只能有一个父 joint,但可以有多个子 joint。
核心原则:URDF 只描述运动学,不描述动力学。关节的摩擦、阻尼、电机特性,那是后面要加的东西。
来看一个最简单的两轮底盘 URDF 片段:
<robot name="simple_robot">
<link name="base_link">
<visual>
<geometry>
<box size="0.4 0.3 0.1"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
</visual>
<collision>
<geometry>
<box size="0.4 0.3 0.1"/>
</geometry>
</collision>
<inertial>
<mass value="2.0"/>
<inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/>
</inertial>
</link>
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="-0.15 -0.2 -0.05" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</robot>
注意几个坑:origin 是子 link 相对于父 joint 的偏移,不是相对于父 link。我见过有人把这两个搞混,结果轮子飞到天上去了。
3.2 Xacro 宏定义:别再复制粘贴了
写一个简单的底盘还好,要是写一个六轴机械臂,每个 link 和 joint 都手写?那得写到天荒地老。而且一旦要改轮子半径,你得改多少个地方?
Xacro(XML Macros)就是来解决这个问题的。它让你定义变量、写宏、做数学运算。说白了,就是给 URDF 加了个「预处理」阶段。
我的习惯:把所有可调参数(轮距、轴距、连杆长度、关节限位)都定义成 xacro 属性。这样调参的时候改一个地方就行,不用满文件找数字。
举个例子,定义一个轮子的宏:
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="my_robot">
<xacro:property name="wheel_radius" value="0.1"/>
<xacro:property name="wheel_width" value="0.05"/>
<xacro:macro name="wheel" params="name prefix">
<link name="${prefix}_${name}_wheel">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
</visual>
</link>
</xacro:macro>
<xacro:wheel name="left" prefix="front"/>
<xacro:wheel name="right" prefix="front"/>
</robot>
看到没?${wheel_radius} 引用属性,${name} 和 ${prefix} 是宏参数。这样你定义一次,后面想生成多少个轮子都行。
我曾经维护过一个六足机器人的 URDF,48 个关节,手写的话光 joint 定义就得 500 行。用 xacro 宏,一个腿的宏写一次,循环调用 6 次,总共不到 100 行。改腿长?改一个属性就行。
3.3 传感器与执行器配置
光有骨架还不够。你的机器人得「感知」和「行动」。这就需要在 URDF 里挂载传感器和执行器。
传感器在 URDF 里也是 link,只不过它通常是一个「虚拟 link」,不参与碰撞。常见的传感器包括:
| 传感器类型 | URDF 配置要点 | 常见坑点 |
|---|---|---|
| 激光雷达 (LiDAR) | 挂载为 link,指定安装位置和朝向 | 原点偏移算错,导致点云偏移 |
| 深度相机 (RGB-D) | 需要定义视觉 mesh 和光学坐标系 | 光学坐标系和视觉坐标系没对齐 |
| IMU | 通常放在 base_link 或躯干中心 | 安装位置偏离质心,数据有偏置 |
| 力/力矩传感器 | 放在关节处,作为虚拟 link | 需要配合 <gazebo> 标签启用 |
注意:URDF 本身不定义传感器的「数据输出」。它只定义传感器的「物理位置和外观」。传感器怎么发数据、发什么话题,那是后面通过插件或驱动来配置的。
执行器就是关节的驱动。在 URDF 里,你通过 <transmission> 标签把 joint 和 actuator 关联起来:
<transmission name="left_wheel_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="left_wheel_joint">
<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
</joint>
<actuator name="left_wheel_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
这里 hardwareInterface 决定了这个关节是位置控制、速度控制还是力矩控制。我建议一开始都用速度接口,调试起来最直观。
3.4 模型可视化与调试
模型写完了,怎么知道对不对?别直接扔进仿真,先可视化检查。
我最常用的工具是 RViz。加载 URDF 模型,几秒钟就能看到 3D 渲染。你可以旋转、缩放、查看每个 link 和 joint 的坐标系。
调试步骤我一般这么走:
- 检查模型是否加载成功:在终端运行
check_urdf my_robot.urdf,它会告诉你语法错误和结构问题。 - 在 RViz 中查看:添加 RobotModel 显示,看每个 link 的位置和朝向对不对。
- 检查关节运动:用
joint_state_publisher手动拖动关节,看运动学是否正确。 - 检查碰撞模型:把 collision 显示打开,看碰撞体是否和视觉模型匹配。
- 检查惯性参数:用
urdf_to_graphiz生成树状图,确认父子关系。
避坑指南:我曾经有一次模型在 RViz 里看着完美,一进 Gazebo 就乱抖。查了半天,发现是惯性矩阵填了全零。记住,惯性矩阵不能为零,否则物理引擎会崩溃。
下面这张图是我自己总结的 URDF 调试流程,每次遇到模型问题就按这个走:
还有一个实用技巧:用 roslaunch urdf_tutorial display.launch model:=my_robot.urdf 可以快速启动 RViz 并加载模型,省去手动配置的麻烦。
调试的时候,我建议你重点关注三个东西:
- 坐标系:每个 link 的 origin 是不是你想要的?用 RViz 的 TF 显示功能看坐标轴方向。
- 关节限位:旋转关节的
lower和upper有没有写反?我犯过这个错。 - 碰撞体简化:碰撞模型尽量用 box、cylinder、sphere 组合,别用复杂 mesh。物理引擎算起来快很多。
重要提醒:URDF 里的单位是米和弧度。如果你从 SolidWorks 导出的模型单位是毫米,记得除以 1000。不然你的机器人会变成「巨无霸」。
好了,模型基础就这些。写 URDF 就像搭积木,一开始慢一点,把每个 link 和 joint 的关系理清楚。后面调试导航、做仿真联调的时候,你会发现模型越扎实,后面越省心。