2. XML模型基础

好,我们正式开始动手了。这一章的目标很简单——搭一个静态场景:地面加一个小球。别小看这个例子,它背后是MuJoCo整个XML模型体系的骨架。你把这个搞懂了,后面搭四足机器人就是往上添零件的事。

2.1 MuJoCo的XML模型长什么样?

MuJoCo用XML文件描述整个世界。说白了,就是一个树状结构,根节点叫 mujoco,下面挂各种子节点。我个人习惯把这种结构想象成一棵倒着长的树——根在最上面,枝叶往下展开。

核心结构速览:

  • mujoco — 根节点,整个模型的容器
  • worldbody — 世界本体,所有物体的“家”
  • body — 刚体,可以嵌套,构成运动链
  • geom — 几何体,定义物体的形状和外观

下面这张图把关系画清楚了,你一看就懂:

MuJoCo XML 模型结构树 mujoco (根) worldbody (世界) body (刚体) geom (几何体) body 可以嵌套多个子 body,形成关节链;geom 必须挂在 body 下

2.2 四个核心元素,一个一个说清楚

2.2.1 mujoco — 模型的根

每个MuJoCo XML文件必须有且只有一个 mujoco 根节点。它就像房子的地基,所有东西都长在上面。你可以在里面设置编译器选项、默认参数,但初学者先别管那些,留空就行。

2.2.2 worldbody — 世界的容器

worldbody 是场景的根刚体。它代表“世界坐标系”,所有物体都挂在这个坐标系下。注意,worldbody 本身没有质量、没有惯性,它只是一个空间参考点。

我的习惯:把 worldbody 想象成舞台的地板。地板本身不动,但所有演员(物体)都站在上面。

2.2.3 body — 刚体,物体的“骨架”

body 定义了一个刚体。它有位置、朝向,可以嵌套子 body 形成运动链。每个 body 可以挂多个 geom,也可以挂关节、传感器等。

嗯,这里要注意:body 本身是不可见的。你看到的是它下面挂的 geom。所以 body 更像是一个坐标系,geom 才是你眼睛看到的东西。

2.2.4 geom — 几何体,物体的“皮肉”

geom 定义了物体的形状、大小、颜色、摩擦系数等。MuJoCo支持多种几何类型:

类型 说明 常用参数
plane 无限大平面,适合做地面 size 定义半长宽
sphere 球体 size 定义半径
box 长方体 size 定义半边长
cylinder 圆柱体 size 定义半径和半高
mesh 自定义网格 需要外部 STL 文件

我曾经踩过的坑:初学者容易把 geom 直接挂在 worldbody 下,忘了包一层 body。虽然MuJoCo允许这样写,但后续加关节、加传感器时会非常麻烦。我的建议是——哪怕只有一个物体,也给它套一个 body。养成好习惯,后面省大事。

2.3 动手:搭一个“地面+小球”场景

理论说完了,直接上代码。下面这个XML文件,就是我们要的静态场景:

<mujoco model="ground_and_ball">
  <worldbody>
    <!-- 地面 -->
    <body name="ground">
      <geom name="floor" type="plane" size="2 2 0.1" rgba="0.8 0.8 0.8 1"/>
    </body>

    <!-- 小球 -->
    <body name="ball" pos="0 0 0.5">
      <geom name="sphere" type="sphere" size="0.2" rgba="1 0 0 1"/>
    </body>
  </worldbody>
</mujoco>

来,一行一行拆开看:

  • model="ground_and_ball" — 给模型起个名字,方便识别
  • body name="ground" — 地面的刚体,位置默认在原点
  • geom type="plane" size="2 2 0.1" — 一个平面,半长宽2米,厚度0.1米(平面其实无限大,size只影响碰撞检测范围)
  • rgba="0.8 0.8 0.8 1" — 浅灰色,不透明
  • body name="ball" pos="0 0 0.5" — 小球刚体,位置在 (0, 0, 0.5),也就是地面以上半米
  • geom type="sphere" size="0.2" — 半径0.2米的球体
  • rgba="1 0 0 1" — 红色,不透明

一个小技巧:MuJoCo的坐标系统是右手系,Z轴向上。所以 pos="0 0 0.5" 表示小球在地面以上0.5米。如果你把Z改成负数,小球就钻到地底下去了——我刚开始学的时候就干过这事,仿真一运行,球直接消失,我还以为代码写错了。

2.4 怎么运行这个模型?

你有两种方式:

  1. 用MuJoCo自带的可视化工具:把上面的XML保存成 scene.xml,然后在终端运行 simulate scene.xml。你会看到一个灰色地面,上面飘着一个红球。
  2. 用Python加载:如果你装了 mujoco Python包,可以这样写:
import mujoco
import mujoco.viewer

# 加载模型
xml = """
<mujoco model="ground_and_ball">
  <worldbody>
    <body name="ground">
      <geom name="floor" type="plane" size="2 2 0.1" rgba="0.8 0.8 0.8 1"/>
    </body>
    <body name="ball" pos="0 0 0.5">
      <geom name="sphere" type="sphere" size="0.2" rgba="1 0 0 1"/>
    </body>
  </worldbody>
</mujoco>
"""

model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)

with mujoco.viewer.launch_passive(model, data) as viewer:
    mujoco.mj_step(model, data)
    viewer.sync()
    input("按回车退出...")

运行后,你会看到一个3D窗口,地面和小球都显示出来了。你可以用鼠标拖拽视角,从各个方向观察。

2.5 参数调一调,理解更深

光看还不够,我建议你动手改几个参数,看看效果:

参数 修改建议 你会看到什么
pos="0 0 0.5" 改成 0 0 1.0 小球飞得更高了
size="0.2" 改成 0.5 小球变大
rgba="1 0 0 1" 改成 0 1 0 1 小球变绿
type="sphere" 改成 box,size 改成 0.2 0.2 0.2 小球变成立方体

你想想看,改一个参数,场景就变了。这就是MuJoCo XML模型的魅力——用文本描述世界,改几行字就能创造不同的物理场景。

本章核心要点:

  • MuJoCo的XML模型是树状结构,根节点是 mujoco
  • worldbody 是世界的容器,所有物体都在里面
  • body 是刚体,geom 是几何体,两者配合使用
  • 每个 geom 必须有 type 和 size 参数
  • 颜色用 rgba 控制,四个值分别是红绿蓝透明度