4、人形机器人 URDF 建模:构建一个人形机器人 URDF 模型(头部、躯干、双臂、双腿)、配置关节限位与惯性参数
URDF,全称 Unified Robot Description Format,是 ROS 生态里描述机器人结构的标准语言。说白了,它就是一份 XML 文件,告诉仿真器你的机器人长什么样、关节怎么动、质量分布在哪。
我刚开始做人形机器人仿真时,觉得 URDF 就是个「零件清单」。后来踩了不少坑才明白——URDF 写得好不好,直接决定了仿真能不能跑起来、跑得稳不稳。
核心要点:URDF 建模的本质是「树形结构」——从 base_link 开始,通过 joint 连接各个 link,形成一棵完整的运动学树。人形机器人通常以躯干为根节点,四肢和头部为子节点。
4.1 定义 link:给机器人「长肉」
每个 link 代表一个刚体部件。你需要告诉 URDF 三件事:几何形状、视觉外观、物理属性。
我个人习惯先画几何,再调惯性参数。因为惯性参数直接影响仿真稳定性——你想想看,如果质心位置写错了,机器人站着都会倒。
<link name="torso_link">
<visual>
<geometry>
<box size="0.4 0.25 0.3"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
<material name="torso_mat">
<color rgba="0.8 0.8 0.8 1.0"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.4 0.25 0.3"/>
</geometry>
</collision>
<inertial>
<mass value="8.0"/>
<inertia ixx="0.15" ixy="0.0" ixz="0.0"
iyy="0.12" iyz="0.0"
izz="0.10"/>
</inertial>
</link>
我的经验:躯干是人形机器人的「重心锚点」。我一般把躯干质量设为 6-10 kg,惯性矩按长方体公式估算:I = (1/12) * m * (w² + h²)。别偷懒用默认值,否则仿真时机器人会「飘」起来。
4.2 定义 joint:给机器人「装关节」
joint 是 URDF 的灵魂。它决定了两个 link 之间怎么动。人形机器人常用的 joint 类型就三种:
| 关节类型 | 自由度 | 典型应用 | 注意事项 |
|---|---|---|---|
| revolute | 1 旋转 | 肩关节、髋关节、膝关节 | 必须设置 limit |
| continuous | 1 无限旋转 | 头部旋转(可选) | 不需要 limit |
| fixed | 0 | 躯干与腰部连接 | 刚性连接 |
嗯,这里要注意——revolute 关节必须配置 limit,否则 Gazebo 会报错。我曾经因为忘了写膝关节的 limit,仿真器直接崩溃,排查了半天才发现问题。
<joint name="left_shoulder_joint" type="revolute">
<parent link="torso_link"/>
<child link="left_arm_link"/>
<origin xyz="0.2 0.15 0.1" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-1.57" upper="1.57" effort="30.0" velocity="2.0"/>
<dynamics damping="0.1" friction="0.05"/>
</joint>
避坑指南:我曾经在配置髋关节时,把 axis 写成了 "1 0 0"(绕 X 轴旋转),结果机器人走路时腿往侧面劈叉。人形机器人的髋关节屈伸应该绕 Y 轴(axis="0 1 0"),外展内收才绕 X 轴。别搞混了。
4.3 配置关节限位:别让机器人「掰断」自己
关节限位是安全红线。人形机器人的关节活动范围有限,写大了仿真会穿模,写小了动作做不出来。
我整理了一份常用限位参考表,基于我做过的一个 1.2m 高的人形机器人项目:
| 关节 | 下限 (rad) | 上限 (rad) | 最大力矩 (Nm) | 最大速度 (rad/s) |
|---|---|---|---|---|
| 肩关节(俯仰) | -2.09 | 2.09 | 30 | 3.0 |
| 肩关节(外展) | -0.52 | 1.57 | 25 | 2.5 |
| 肘关节 | -2.09 | 0.0 | 20 | 2.0 |
| 髋关节(俯仰) | -1.57 | 1.57 | 50 | 3.0 |
| 膝关节 | -2.09 | 0.0 | 60 | 2.5 |
| 踝关节 | -0.79 | 0.79 | 30 | 2.0 |
小技巧:effort(最大力矩)别设太大。我刚开始做双足行走时,把膝关节力矩设了 100 Nm,结果仿真里机器人一脚踩下去,地面直接穿透。后来改成 60 Nm,配合合适的 PD 增益,走路就稳了。
4.4 惯性参数:仿真稳定的「隐形之手」
惯性参数是 URDF 里最容易糊弄、也最容易出问题的地方。说白了,它决定了机器人「有多重、重心在哪、转动惯量多大」。
三个关键参数:
- mass:质量,单位 kg。每个 link 都要写,不能为 0。
- origin:质心位置,相对于 link 坐标系。默认是 (0,0,0),但实际零件质心往往有偏移。
- inertia:转动惯量矩阵,6 个独立分量(ixx, ixy, ixz, iyy, iyz, izz)。
我见过有人把所有 link 的 inertia 写成一样的,结果仿真时机器人像果冻一样抖动。为什么会这样?因为惯性矩不匹配,物理引擎算出来的动力学响应是错的。
<!-- 左臂 link 的惯性参数 -->
<inertial>
<mass value="2.5"/>
<origin xyz="0.0 0.0 -0.1"/>
<inertia ixx="0.02" ixy="0.0" ixz="0.0"
iyy="0.015" iyz="0.0"
izz="0.008"/>
</inertial>
快速估算公式:对于长方体,Ixx = (1/12) * m * (h² + d²),Iyy = (1/12) * m * (w² + d²),Izz = (1/12) * m * (w² + h²)。其中 w、h、d 分别是长、高、深。对于圆柱体,Izz = (1/2) * m * r²。
4.5 完整 URDF 文件结构
把上面所有零件拼起来,就是一个完整的人形机器人 URDF。我建议按这个顺序组织:
- 头部:head_link + head_joint(连接躯干)
- 躯干:torso_link(根节点)
- 左臂:left_arm_link + left_shoulder_joint → left_forearm_link + left_elbow_joint
- 右臂:right_arm_link + right_shoulder_joint → right_forearm_link + right_elbow_joint
- 左腿:left_leg_link + left_hip_joint → left_shank_link + left_knee_joint → left_foot_link + left_ankle_joint
- 右腿:right_leg_link + right_hip_joint → right_shank_link + right_knee_joint → right_foot_link + right_ankle_joint
<robot name="humanoid_robot">
<!-- 头部 -->
<link name="head_link">...</link>
<joint name="head_joint" type="fixed">...</joint>
<!-- 躯干 -->
<link name="torso_link">...</link>
<!-- 左臂 -->
<link name="left_arm_link">...</link>
<joint name="left_shoulder_joint" type="revolute">...</joint>
<link name="left_forearm_link">...</link>
<joint name="left_elbow_joint" type="revolute">...</joint>
<!-- 右臂 -->
<link name="right_arm_link">...</link>
<joint name="right_shoulder_joint" type="revolute">...</joint>
<link name="right_forearm_link">...</link>
<joint name="right_elbow_joint" type="revolute">...</joint>
<!-- 左腿 -->
<link name="left_leg_link">...</link>
<joint name="left_hip_joint" type="revolute">...</joint>
<link name="left_shank_link">...</link>
<joint name="left_knee_joint" type="revolute">...</joint>
<link name="left_foot_link">...</link>
<joint name="left_ankle_joint" type="revolute">...</joint>
<!-- 右腿 -->
<link name="right_leg_link">...</link>
<joint name="right_hip_joint" type="revolute">...</joint>
<link name="right_shank_link">...</link>
<joint name="right_knee_joint" type="revolute">...</joint>
<link name="right_foot_link">...</link>
<joint name="right_ankle_joint" type="revolute">...</joint>
</robot>
最后提醒一句:写完 URDF 后,一定要用 check_urdf 命令验证。我每次写完都会跑一遍:check_urdf my_robot.urdf。它会告诉你有没有 missing link、joint 循环、惯性参数缺失等问题。别跳过这步,血的教训。