第二章:机器人模型与URDF——从结构到实战
大家好,我是你们的老朋友。今天咱们聊聊URDF,也就是机器人描述文件。说实话,我刚入行那会儿,看到URDF文件里密密麻麻的XML标签,头都大了。但后来我发现,这东西说白了就是个「机器人简历」——告诉系统你的机器人长什么样、关节怎么动、哪里是眼睛哪里是腿。
核心认知:URDF不是代码,是数据。它描述的是机器人的「静态结构」和「运动学属性」。你想想看,没有它,仿真器连你的机器人有几个轮子都不知道。
2.1 URDF文件结构解析
一个标准的URDF文件,核心就两个东西:link(连杆)和joint(关节)。我习惯把link想象成骨头,joint想象成关节。骨头本身不会动,关节决定了骨头之间怎么动。
来看一个最简单的例子——一个两轮差速底盘:
<robot name="my_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" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</robot>
嗯,这里要注意几个关键点:
- visual:只管「看起来什么样」,不影响物理碰撞。我曾经见过有人把视觉模型做得特别精细,结果仿真卡成PPT——其实碰撞模型用个方块就够了。
- collision:这才是物理引擎真正用的形状。我建议能简单就简单,别搞复杂。
- inertial:惯性参数。很多人忽略这个,结果仿真里机器人飘得像气球。质量给个大概值就行,惯性矩阵用
ixx=iyy=izz=0.01这种近似值也能跑。
我的小技巧:写URDF时,先画个草图,标清楚每个link的坐标系原点。不然你写出来的关节位置,很可能机器人是「拧着」的。
2.2 从SolidWorks导出URDF
手工写URDF?对于简单机器人还行。但如果你用的是SolidWorks画的复杂模型,手写会疯掉的。好在有插件——sw2urdf,能一键导出。
我当年第一次用这个插件,导出后加载到仿真里,机器人直接散架了。后来发现是坐标系没对齐。这里分享几个避坑点:
- 先定义好基准坐标系:在SolidWorks里,把每个零件的原点对齐到关节轴线上。我习惯把base_link的原点放在地面投影中心。
- 关节类型别选错:旋转关节选revolute,轮子选continuous,滑动关节选prismatic。选错了仿真里关节会乱转。
- 导出后检查一下:用
check_urdf命令验证。我每次导出后必做这步,能发现90%的问题。
我曾经踩过的坑:导出时忘了勾选「使用STL文件」,结果导出了巨大的DAE文件,加载一次要5分钟。后来我统一用STL,文件小、加载快,而且碰撞检测更稳定。
导出后的文件结构大概是这样的:
my_robot/
├── urdf/
│ └── my_robot.urdf
├── meshes/
│ ├── base_link.stl
│ ├── left_wheel.stl
│ └── right_wheel.stl
├── launch/
│ └── display.launch
└── config/
└── joint_limits.yaml
你想想看,有了这个结构,你只需要改改display.launch里的路径,就能在RViz里看到你的机器人了。
2.3 在仿真中加载自定义机器人
有了URDF文件,怎么把它弄到Gazebo或RViz里?我一般用launch文件搞定。来看一个典型的加载脚本:
<launch>
<!-- 加载URDF到参数服务器 -->
<param name="robot_description"
textfile="$(find my_robot)/urdf/my_robot.urdf"/>
<!-- 启动机器人状态发布器 -->
<node name="robot_state_publisher"
pkg="robot_state_publisher"
type="robot_state_publisher"/>
<!-- 启动RViz -->
<node name="rviz" pkg="rviz" type="rviz"
args="-d $(find my_robot)/config/display.rviz"/>
</launch>
这里有个关键点:robot_state_publisher会读取robot_description参数,然后发布TF变换。没有它,你的机器人就是一坨静止的模型。
实战经验:我第一次加载自定义机器人时,RViz里啥都没有。后来发现是robot_description参数名写错了——少了个下划线。这种低级错误,我犯了不下三次。所以建议你每次改完URDF,先用rosparam get /robot_description看看参数有没有正确加载。
如果你要在Gazebo里仿真,还需要额外加一个gazebo_ros_control插件。这个插件负责把ROS的关节指令转成Gazebo的物理力。我习惯在URDF里这样写:
<gazebo>
<plugin name="gazebo_ros_control"
filename="libgazebo_ros_control.so">
<robotNamespace>/my_robot</robotNamespace>
</plugin>
</gazebo>
嗯,这里要注意:插件里的robotNamespace要和你的launch文件里的命名空间一致。不然控制指令发不到Gazebo里。
2.4 本章知识体系
为了让你更直观地理解URDF的整个工作流,我画了张图:
这张图展示了我个人总结的URDF工作流。你会发现,从设计到真机部署,中间有验证和调试两个关键环节。我见过太多人跳过验证,直接加载到仿真里,结果机器人姿态不对,又回头改URDF——白白浪费半天时间。
我的建议:每次修改URDF后,先用check_urdf检查语法,再用urdf_to_graphiz生成结构图。看一眼图,你就能发现关节父子关系有没有搞错。
好了,关于URDF的核心内容就这些。说白了,它就是机器人的「骨架描述」。你掌握了link和joint的写法,学会了从SolidWorks导出,再配合launch文件加载到仿真里,就已经能应付90%的场景了。剩下的10%,是在真机迁移时才会遇到的坑——比如关节限位不一致、惯性参数需要实测校准。这些我们后面章节再细聊。