3. 机器人模型与URDF:从零搭建你的第一个机器人
各位同学,欢迎来到第三章。前两章我们把环境搭好了,概念也理清了。这一章,咱们来点实在的——亲手写出一个机器人模型。
URDF,全称是 Unified Robot Description Format。说白了,它就是机器人的「身份证」。你的机器人长什么样?有几个轮子?机械臂能转几圈?这些信息,URDF 都能描述清楚。
我个人习惯把 URDF 看作「乐高说明书」。你只需要把每个零件(link)和它们之间的连接方式(joint)写清楚,ROS 就能知道这个机器人该怎么动。
核心知识点速览
- URDF 基础语法:link、joint、几何与可视化
- 差速底盘模型:两轮+万向轮,经典移动机器人
- 机械臂模型:多关节串联,从基座到末端
- robot_state_publisher:让模型在 RViz 里动起来
3.1 URDF 基础语法:link 和 joint 的默契配合
URDF 文件本质上就是 XML。你不需要背语法,记住两个核心元素就够了:link 和 joint。
- link:描述一个刚体部件。比如底盘、轮子、机械臂的连杆。
- joint:描述两个 link 之间的连接关系。比如轮子怎么装在底盘上。
一个最简单的 link 长这样:
<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>
</link>
嗯,这里要注意:origin 里的 xyz 是位置,rpy 是旋转(roll, pitch, yaw)。我刚开始写 URDF 时,经常把 rpy 搞反,结果轮子在天上飞。后来养成了习惯——先在纸上画个坐标系,再填数值。
joint 的写法也不复杂:
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="-0.2 0.15 -0.05" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
我的小技巧:joint 的 type 有六种:revolute(旋转限位)、continuous(无限旋转)、prismatic(滑动)、fixed(固定)、floating(浮动)、planar(平面)。差速轮用 continuous,机械臂关节用 revolute。
3.2 创建差速底盘模型:两轮驱动,经典永不过时
差速底盘,说白了就是两个主动轮 + 一个或多个万向轮。通过控制左右轮的速度差来实现转向。我在做第一个 AGV 项目时,用的就是这种结构——简单、可靠、好调参。
一个完整的差速底盘 URDF 包含:
- base_link:底盘主体,通常是个长方体
- left_wheel / right_wheel:两个驱动轮,用 continuous joint 连接
- caster_wheel:万向轮,用 fixed 或 continuous 都行
来看一个完整的底盘模型片段:
<robot name="diff_drive_robot">
<link name="base_link">
<visual>
<geometry>
<box size="0.4 0.3 0.1"/>
</geometry>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
</link>
<link name="left_wheel">
<visual>
<geometry>
<cylinder radius="0.05" length="0.04"/>
</geometry>
</visual>
</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>
避坑指南:我曾经因为 wheel 的 origin 写错,导致轮子悬空。记住:轮子的 origin 是相对于 parent link(base_link)的。如果 base_link 厚度是 0.1,轮子半径 0.05,那 origin 的 z 值应该是 -0.05(轮子中心在底盘下表面)。
3.3 创建机械臂模型:从基座到末端执行器
机械臂的 URDF 比底盘复杂一些,因为涉及多个关节的串联。你想想看,一个 6 轴机械臂,需要 6 个 link 和 6 个 joint,每个关节都要定义旋转轴和限位。
我习惯从基座开始,一层一层往上写:
- base_link:固定在底盘或工作台上
- link1 ~ linkN:每个连杆对应一个关节
- end_effector:末端工具,比如夹爪或吸盘
一个两关节机械臂的示例:
<robot name="simple_arm">
<link name="base_link">
<visual>
<geometry>
<cylinder radius="0.05" length="0.1"/>
</geometry>
</visual>
</link>
<link name="arm_link1">
<visual>
<geometry>
<box size="0.03 0.03 0.2"/>
</geometry>
<origin xyz="0 0 0.1"/>
</visual>
</link>
<joint name="joint1" type="revolute">
<parent link="base_link"/>
<child link="arm_link1"/>
<origin xyz="0 0 0.1"/>
<axis xyz="0 0 1"/>
<limit lower="-3.14" upper="3.14" effort="10" velocity="1"/>
</joint>
</robot>
经验之谈:写机械臂 URDF 时,limit 里的 effort 和 velocity 别乱填。我见过有人把 effort 写成 1000,结果仿真里关节像打了鸡血一样乱抖。先查电机手册,再填数值。
3.4 使用 robot_state_publisher:让模型活过来
URDF 写好了,怎么在 RViz 里看到它?这就需要 robot_state_publisher 了。
这个节点做两件事:
- 读取 URDF 文件,发布到
/robot_description话题 - 根据关节角度,计算并发布 TF 变换
启动方式很简单:
ros2 launch robot_state_publisher robot_state_publisher.launch.py \
urdf_file:=/path/to/your/robot.urdf
或者,你也可以在 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',
arguments=['/path/to/robot.urdf']
)
])
关键点:robot_state_publisher 默认从 /robot_description 话题读取 URDF。如果你用参数传文件,它会自动发布到该话题。之后 RViz 订阅这个话题,就能显示模型了。
我记得第一次在 RViz 里看到自己写的 URDF 模型动起来时,那种成就感——嗯,就像你亲手拼的乐高终于能跑了。虽然只是个方块加两个圆柱,但那一刻,你真的觉得「这是我的机器人」。
好了,这一章的内容就到这里。URDF 是机器人建模的基石,多写多练,手感自然就来了。下一章我们会聊传感器模型和 xacro——一种让 URDF 更优雅的宏语言。