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用弧度还是角度——我个人习惯用弧度,省得换算。

小技巧: 写MJCF时,缩进一定要对齐。树状结构一旦乱了,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轴转。

避坑指南: 我曾经把膝关节的axis写成了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。这就是四足机器人最基本的“一条腿”模型。

关键点: 四足机器人有4条腿,每条腿结构相同。你只需要写好一条腿,然后复制4份,调整一下位置和旋转角度就行。别傻傻地写4遍——用<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。

我的习惯: 我会把每条腿的body name加上前缀,比如FL_、FR_、RL_、RR_。这样在强化学习代码里控制关节时,一眼就能看出是哪个关节。别小看命名规范——项目大了之后,命名混乱会让你想砸电脑。

2.5 模型结构总览

下面这张图展示了咱们刚才写的四足机器人模型结构。你可以看到body的嵌套关系、joint的连接位置、以及每个层级对应的geom形状。

四足机器人MJCF模型结构图 <worldbody> torso (body) geom: box joint: root (free) FL_hip (body) joint: hinge (Z轴) FL_thigh (body) joint: hinge (Y轴) FL_shin (body) geom: capsule FL_foot (body) FR_hip (body) joint: hinge (Z轴) FR_thigh (body) joint: hinge (Y轴) FR_shin (body) geom: capsule FR_foot (body) 每条腿结构相同:hip → thigh → shin → foot,通过hinge关节串联

从这张图可以清楚看到:躯干是根节点,下面挂四条腿。每条腿都是4级body嵌套,通过hinge关节连接。后两条腿的结构完全一样,只是位置偏移到躯干后方。

2.6 常见错误与调试技巧

写MJCF时,你大概率会遇到下面这些问题。我当年全踩过一遍:

  • 模型加载后直接塌陷:多半是关节range设置不合理,或者初始姿态就不稳定。试试把躯干抬高一点,或者把关节range收窄。
  • 腿穿模到地面以下:检查一下foot的geom位置对不对。我曾经把foot的pos写成了0 0 -0.15,但实际应该到-0.12就触地了。
  • 关节运动方向反了:axis写反了。把axis的某个分量取反试试。
  • 模型加载报错“unexpected ”:XML语法错误。检查标签是否闭合,属性值是否加引号。
调试建议: 每次只改一个参数,然后运行仿真看看效果。别一次性改10个参数——出了问题你根本不知道是哪个改错了。我刚开始就吃过这个亏,改了一堆参数,结果模型飞了,完全不知道从哪查起。