2、XML模型基础:理解MJCF格式、四足机器人模型结构拆解
好,咱们正式开始动手了。这一章,我会带你拆开MuJoCo的模型文件——MJCF格式。说白了,它就是一套用XML描述机器人长什么样的“说明书”。你写对了,MuJoCo就能看懂;写错了,仿真器直接罢工。
我记得刚接触MuJoCo那会儿,看着一堆<body>、<joint>、<geom>标签,完全摸不着头脑。后来踩了几个坑才明白,这三个标签就是四足机器人的“骨架”、“关节”和“皮肉”。
2.1 MJCF是什么?
MJCF全称是MuJoCo Simulation Description Format。它用XML树状结构来描述一个物理世界——包括机器人本体、地面、灯光、甚至重力方向。
一个最简的MJCF文件长这样:
<mujoco model="simple_quad">
<compiler angle="radian"/>
<worldbody>
<body name="base" pos="0 0 0.3">
<geom type="box" size="0.1 0.06 0.02" rgba="0.8 0.2 0.2 1"/>
<joint name="root" type="free"/>
</body>
</worldbody>
</mujoco>
嗯,这里要注意:<worldbody>是整个世界坐标系的原点,所有机器人部件都得挂在它下面。<compiler>告诉MuJoCo用弧度还是角度——我个人习惯用弧度,省得换算。
2.2 四足机器人的三大核心元素
四足机器人再复杂,拆开来看就三个东西:body(刚体)、joint(关节)、geom(几何体)。咱们一个一个说。
2.2.1 body——骨架
body代表一个刚体部件。它有自己的位置、质量和惯性。四足机器人的躯干、大腿、小腿、脚掌,每个都是一个body。
body可以嵌套。比如躯干下面挂四条腿,每条腿的大腿下面挂小腿,小腿下面挂脚掌。这种父子关系决定了运动学链。
<body name="thigh" pos="0 0 -0.15">
<!-- 大腿的几何体和关节 -->
</body>
这里的pos是相对于父body的位置。我刚开始写的时候,总把坐标写成坐标,结果腿飞到天上去了。记住:子body的坐标是相对于父body的。
2.2.2 joint——关节
joint定义了两个body之间的运动自由度。没有joint,两个body就是焊死的。
四足机器人常用的关节类型:
| 关节类型 | 自由度 | 用途 |
|---|---|---|
free |
6 (3平移+3旋转) | 躯干与世界的连接,让机器人可以自由移动 |
hinge |
1 (绕轴旋转) | 髋关节、膝关节,最常见的旋转关节 |
slide |
1 (沿轴平移) | 四足机器人很少用,但机械臂滑台会用到 |
每个关节还需要指定axis(旋转轴方向)和range(运动范围)。比如髋关节绕Z轴转,膝关节绕Y轴转。
0 1 0,但实际应该绕Y轴旋转。结果仿真时小腿像钟摆一样前后乱甩。检查了半小时才发现是轴方向写反了。建议每次写完关节,先手动转一下看看运动方向对不对。
2.2.3 geom——皮肉
geom是几何体,负责碰撞检测和视觉显示。它不参与动力学计算——那是body的事。geom只是“长”在body上的形状。
常用几何体类型:
- box:长方体,适合躯干
- cylinder:圆柱体,适合大腿
- sphere:球体,适合脚掌
- capsule:胶囊体,碰撞更顺滑,适合腿部
每个geom可以设置size(尺寸)、rgba(颜色)、friction(摩擦系数)。
<geom type="capsule" size="0.025" fromto="0 0 0 0 0 -0.15" rgba="0.2 0.6 0.8 1"/>
这里fromto指定了胶囊体的起点和终点,比用pos+size更直观。我个人很喜欢用capsule做腿部,碰撞检测比cylinder顺滑很多。
2.3 四足机器人模型结构拆解
咱们以一条腿为例,看看完整的body-joint-geom嵌套关系:
<body name="hip" pos="0.1 0 0">
<geom type="sphere" size="0.03" rgba="0.5 0.5 0.5 1"/>
<joint name="hip_joint" type="hinge" axis="0 0 1" range="-45 45"/>
<body name="thigh" pos="0 0 -0.12">
<geom type="capsule" size="0.025" fromto="0 0 0 0 0 -0.12" rgba="0.2 0.6 0.8 1"/>
<joint name="knee_joint" type="hinge" axis="0 1 0" range="-90 0"/>
<body name="shin" pos="0 0 -0.12">
<geom type="capsule" size="0.02" fromto="0 0 0 0 0 -0.12" rgba="0.4 0.8 0.4 1"/>
<body name="foot" pos="0 0 -0.12">
<geom type="sphere" size="0.025" rgba="0.1 0.1 0.1 1"/>
</body>
</body>
</body>
</body>
你想想看,这条腿的层级关系:髋→大腿→小腿→脚掌。每个层级都通过joint连接,每个body都挂着一个geom。这就是四足机器人最基本的“一条腿”模型。
<body>嵌套和pos偏移就能搞定。
2.4 编写第一个完整的四足机器人XML
好,咱们把上面所有知识串起来。下面是一个完整的四足机器人MJCF文件——它有一个躯干、四条腿,每条腿有髋关节和膝关节:
<mujoco model="quadruped_v1">
<compiler angle="degree" coordinate="local"/>
<worldbody>
<light name="top" pos="0 0 1"/>
<geom name="floor" type="plane" size="1 1 0.1" rgba="0.8 0.8 0.8 1"/>
<body name="torso" pos="0 0 0.3">
<geom type="box" size="0.15 0.08 0.04" rgba="0.9 0.1 0.1 1"/>
<joint name="root" type="free"/>
<!-- 前左腿 -->
<body name="FL_hip" pos="0.12 0.08 0">
<geom type="sphere" size="0.025" rgba="0.5 0.5 0.5 1"/>
<joint name="FL_hip_joint" type="hinge" axis="0 0 1" range="-45 45"/>
<body name="FL_thigh" pos="0 0 -0.1">
<geom type="capsule" size="0.02" fromto="0 0 0 0 0 -0.1" rgba="0.2 0.6 0.8 1"/>
<joint name="FL_knee_joint" type="hinge" axis="0 1 0" range="-90 0"/>
<body name="FL_shin" pos="0 0 -0.1">
<geom type="capsule" size="0.015" fromto="0 0 0 0 0 -0.1" rgba="0.4 0.8 0.4 1"/>
<body name="FL_foot" pos="0 0 -0.1">
<geom type="sphere" size="0.02" rgba="0.1 0.1 0.1 1"/>
</body>
</body>
</body>
</body>
<!-- 前右腿、后左腿、后右腿结构相同,只需调整pos中的x和y符号 -->
<!-- 这里省略重复代码,实际使用时复制并修改pos即可 -->
</body>
</worldbody>
</mujoco>
嗯,这里我只写了前左腿的完整结构。其他三条腿的代码完全一样,只需要把pos里的x和y符号改一下:前右腿是0.12 -0.08 0,后左腿是-0.12 0.08 0,后右腿是-0.12 -0.08 0。
2.5 模型结构总览
下面这张图展示了咱们刚才写的四足机器人模型结构。你可以看到body的嵌套关系、joint的连接位置、以及每个层级对应的geom形状。
从这张图可以清楚看到:躯干是根节点,下面挂四条腿。每条腿都是4级body嵌套,通过hinge关节连接。后两条腿的结构完全一样,只是位置偏移到躯干后方。
2.6 常见错误与调试技巧
写MJCF时,你大概率会遇到下面这些问题。我当年全踩过一遍:
- 模型加载后直接塌陷:多半是关节range设置不合理,或者初始姿态就不稳定。试试把躯干抬高一点,或者把关节range收窄。
- 腿穿模到地面以下:检查一下foot的geom位置对不对。我曾经把foot的pos写成了
0 0 -0.15,但实际应该到-0.12就触地了。 - 关节运动方向反了:axis写反了。把axis的某个分量取反试试。
- 模型加载报错“unexpected ”:XML语法错误。检查标签是否闭合,属性值是否加引号。