☰
从零学习 MuJoCo(1):从零跑通第一个物理仿真
2026/10/17 9:21:30 网站建设 项目流程

从零学习 MuJoCo(1):从零跑通第一个物理仿真

作者:晓柒 · 从零学习 MuJoCo 系列

想象把一个小球放在离地面 1 米高的地方,然后松手。它会在重力作用下落到地面。在 MuJoCo 中复现这个过程,需要做两件事:先说明“小球和地面是什么样”,再让程序一步一步计算“小球接下来会在哪里”。

这篇教程就从这个例子开始。你将创建两个文件,运行一个小球下落的仿真,并通过修改几个数字,弄清楚模型、运动计算和画面显示是怎样配合的。

图 1:先搭建场景,再计算运动,最后观察结果。

1. MuJoCo 怎样让小球动起来?

MuJoCo 是一个物理仿真引擎,可以把它理解成负责计算物体运动的程序。

以小球为例,程序知道这些信息:

  • 小球的半径和质量;
  • 小球此刻的位置和速度;
  • 重力的大小与方向;
  • 地面在哪里。

有了这些信息,MuJoCo 就能计算一小段时间之后小球的位置和速度。反复计算,小球的运动过程就产生了。

**Viewer 是显示仿真画面的窗口。**本例使用的launch_passive模式由 Python 程序控制仿真:程序算一步,再把结果同步到 Viewer。窗口负责显示,循环负责推动小球运动。

图 2:mj_step()计算下一步,viewer.sync()将结果同步到画面。

先分清“设计图”和“当前状态”

运行 MuJoCo 时,经常会看到两个名字:MjModel和MjData。可以用同一个小球来区分它们:

对象可以怎样理解小球例子
MjModel场景的“设计图”,保存模型结构和参数小球半径 0.1 米、质量 1 千克,重力沿 z 轴向下
MjData仿真的“状态记录表”,保存当前状态和计算结果现在过去了多久、小球在哪里、正以多快的速度运动

小球下落时,半径和质量通常不变,位置和速度却一直在变化。因此,程序可以一直使用同一份model,不断更新data。

这两个名字暂时不用死记。后面的代码里,model对应设计图,data对应当前状态。

“前进一步”是前进多久?

本例把每一步的时间设为0.01秒:

<optiontimestep="0.01"/>

timestep就是“每次计算让仿真时间前进多久”。因此:

调用 1 次 mj_step() → 仿真前进 0.01 秒 调用 10 次 mj_step() → 仿真前进 0.1 秒 调用 100 次 mj_step() → 仿真前进约 1 秒

这里说的是模拟世界过去的时间。电脑实际花了多久,还取决于计算速度。后面的 Python 程序会用sleep适当等待,让画面速度大致接近真实时间。

2. 用 XML 搭一个“小球+地面”的场景

XML 是一种用标签组织内容的文本格式。MuJoCo 使用的这套模型描述格式叫MJCF。在这篇教程中,XML 文件负责写清楚“场景里有什么”。

先新建一个文件夹,把下面的内容保存为其中的hello.xml:

<mujocomodel="hello_mujoco"><!-- 每步前进 0.01 秒,重力沿 z 轴向下 --><optiontimestep="0.01"gravity="0 0 -9.81"/><worldbody><!-- 地面 --><geomname="ground"type="plane"size="2 2 0.1"/><!-- 小球,球心初始高度为 1 米 --><bodyname="ball"pos="0 0 1"><freejoint/><geomname="ball_geom"type="sphere"size="0.1"mass="1"/></body></worldbody></mujoco>

这段代码可以从外到内读:<mujoco>包住整个模型,<worldbody>里放地面和小球,小球的<body>里面再写它的运动方式和形状。

下面把其中几个关键部分拆开说明。

worldbody:物体放在哪里?

worldbody是整个场景的世界根节点,可以把它理解成放置物体的最外层。

本例中,它里面有两样东西:

worldbody ├── 地面 ground └── 小球 ball

地面直接放在worldbody下,固定在世界中。小球则单独放进一个body,方便定义它的位置和运动方式。

body和geom:物体与形状

先看这两行:

<bodyname="ball"pos="0 0 1"><geomtype="sphere"size="0.1"mass="1"/></body>

可以把它们读成一句话:

建立一个名为 ball 的刚体,把它放在(0, 0, 1);给它一个半径为 0.1 米、质量为 1 千克的球形几何体。

“刚体”就是运动时形状不发生变化的物体。本例把小球看作刚体,不考虑它被压扁或拉长。

这里的分工是:

  • body表示这个刚体,用于组织物体的位置和层级关系。
  • geom描述它的形状,还可以参与碰撞与质量计算。

一个body也可以有多个geom。例如,用几个简单形状拼成一件复杂物体,它们仍然属于同一个刚体。

pos="0 0 1"中的三个数字依次对应 x、y、z。本例把 z 轴作为高度方向,所以最后一个数字1表示球心在地面上方 1 米。

freejoint:小球为什么可以下落?

完整模型比上面的两行示例多了一行:

<freejoint/>

它为小球添加一个自由关节,让小球可以在空间中自由移动和转动。

这里的“自由度”指独立的运动方式。小球可以沿 x、y、z 三个方向移动,也可以绕三个方向转动,共6 个运动自由度。

如果删掉<freejoint/>,这个小球就固定在世界中的初始位置。即使重力还在,它也不会自由下落。

所以,想让物体动起来,需要同时考虑:它受到什么力,以及模型允许它怎样运动。

gravity:重力朝哪边?

gravity="0 0 -9.81"

这三个数依次表示 x、y、z 方向的重力加速度。前两个为 0,最后一个为负数,表示重力沿 z 轴负方向,也就是本例的“向下”。

-9.81的单位是米每二次方秒,常写成m/s²。先记住它控制重力大小与方向,后面可以直接改数字观察效果。

3. 用 Python 让场景运行起来

XML 已经说明了场景内容。接下来需要一个 Python 程序完成三件事:加载场景、计算运动、显示结果。

在hello.xml旁边新建hello.py,文件结构如下:

你的项目文件夹/ ├── hello.xml └── hello.py

把下面的代码保存到hello.py:

importtimefrompathlibimportPathimportmujocoimportmujoco.viewer# 在当前 Python 文件旁边寻找 hello.xmlxml_path=Path(__file__).parent/"hello.xml"# 加载模型“设计图”,再创建一份“状态记录表”model=mujoco.MjModel.from_xml_path(str(xml_path))data=mujoco.MjData(model)# 打开窗口;只要窗口没关闭,就不断计算和显示withmujoco.viewer.launch_passive(model,data)asviewer:whileviewer.is_running():step_start=time.perf_counter()# 计算下一个仿真时刻的状态mujoco.mj_step(model,data)# 把最新状态同步到窗口viewer.sync()# 如果本次计算用时不足 0.01 秒,就等待剩余时间remaining=model.opt.timestep-(time.perf_counter()-step_start)ifremaining>0:time.sleep(remaining)

加载模型:读入“设计图”

model=mujoco.MjModel.from_xml_path(str(xml_path))

这一行读取hello.xml,把里面的小球、地面、重力等描述转换成 MuJoCo 可以使用的模型。

xml_path是 XML 文件的位置。前面的Path(__file__).parent表示当前 Python 文件所在的文件夹,因此代码会到hello.py旁边寻找hello.xml。

创建数据:记录“现在是什么样”

data=mujoco.MjData(model)

模型加载好后,需要一份data来保存当前状态。仿真开始时,它包含初始位置和速度;随着程序运行,它会不断更新。

循环计算:一步接着一步

循环里最关键的是这两行:

mujoco.mj_step(model,data)viewer.sync()

第一行让 MuJoCo 根据当前状态计算下一步,并更新data;第二行让 Viewer 显示更新后的结果。

本例的timestep是0.01秒,所以每调用一次mj_step(),仿真就向前走0.01秒。while viewer.is_running()会反复执行它们,直到窗口关闭。

最后的sleep用于控制观看速度:如果电脑很快算完了一步,就稍等一下再继续。它只能让速度大致接近真实时间,不能保证精确计时。

4. 安装 MuJoCo 并运行程序

以下操作使用Windows PowerShell,并假设电脑已经安装 Python。先在保存hello.xml和hello.py的文件夹中打开终端。

第一步:创建项目环境

python-m venv.venv.\.venv\Scripts\Activate.ps1

第一行创建一个名为.venv的虚拟环境,第二行进入这个环境。可以把它理解成项目自己的“工具箱”:MuJoCo 等依赖包安装在这里,便于与其他项目分开管理。

第二步:安装并检查

python-m pip install mujoco python-c"import mujoco; print(mujoco.__version__)"

如果第二条命令能输出版本号,就说明当前环境能够找到 MuJoCo。

第三步:运行

python hello.py

Viewer 打开后,小球应从空中下落并与地面发生碰撞。具体的弹跳幅度与停稳过程会受到接触参数等因素影响,先观察下落和接触地面的过程。

图 3:本例程序的运行画面。小球的位置由程序持续计算,再同步到 Viewer。

除了看画面,也可以看数字

想知道程序里记录的小球高度,可以在循环中的mj_step()后面加一行打印:

mujoco.mj_step(model,data)print(f"time={data.time:.2f}s, ball_z={data.qpos[2]:.3f}m")viewer.sync()

其中:

  • data.time是仿真已经过去的时间。
  • 在这个只有一个自由关节的小球模型中,data.qpos的前 3 个数是小球的位置,data.qpos[2]就是球心高度。

运行后,应看到时间不断增加,球心高度从约 1 米开始下降。这里打印的是球心高度,所以球接触地面时,数值也不会直接变成 0。确认后可删掉打印语句,避免终端一直刷屏。

5. 改几个数字,看看结果是否符合预期

每次只改一个地方,保存 XML 后重新运行程序。完成一项实验后,恢复原值再试下一项,这样更容易判断是哪项修改影响了运动。

实验一:让小球从更高处落下

把:

pos="0 0 1"

改成:

pos="0 0 2"

最后一个数字是高度,所以小球的起点从 1 米变成了 2 米。它需要下落更长的距离,碰到地面的时间也会更晚。

实验二:让重力变小

把:

gravity="0 0 -9.81"

改成:

gravity="0 0 -2"

重力仍然向下,但数值的绝对值变小。小球下落时速度增加得更慢,看起来也会落得更慢。

实验三:让重力朝另一个方向

把重力改成:

gravity="9.81 0 0"

现在重力沿 x 轴正方向,小球会朝这个方向运动。这个实验说明,场景中的“向下”由重力向量决定;MuJoCo 会按照你写下的三个数字计算。

实验四:把小球固定住

恢复原来的重力,再删除:

<freejoint/>

重新运行后,小球会固定在初始位置。这个实验把“受到重力”和“允许移动”区分开来:自由关节提供了移动的可能,重力才会让小球自由下落。

6. 遇到报错时先检查什么?

No module named 'mujoco'

这表示运行程序的 Python 环境找不到 MuJoCo。电脑上可能有多个 Python,或者编辑器选用了另一个环境。

在运行hello.py的同一个终端执行:

python-m pip install mujoco python-c"import mujoco; print(mujoco.__version__)"

如果使用 VS Code,还需要打开命令面板,选择Python: Select Interpreter,再选中项目里的.venv\Scripts\python.exe。安装包和运行脚本要使用同一个 Python 环境。

找不到hello.xml

检查hello.xml是否放在hello.py旁边,以及文件名是否与程序中的写法一致。还要留意,文件是否误存成了hello.xml.txt。

窗口打开了,小球却不动

先检查三个地方:循环里有没有mj_step(model, data);后面有没有viewer.sync();小球的 XML 中有没有<freejoint/>。这三处分别关系到计算运动、更新显示和允许运动。

7. 记住这几个对应关系

名称本例中的作用
XML / MJCF写下场景规则:小球、地面、重力等
MjModel加载后的模型“设计图”
MjData小球当前的状态记录
worldbody整个场景的最外层
body定义一个刚体
geom描述刚体的形状,并参与碰撞等计算
freejoint让小球可以自由移动和转动
timestep每次计算让仿真时间前进多久
mj_step()计算下一步的状态
viewer.sync()把最新状态同步到画面

回到最初的小球:XML 说明它是什么样、从哪里出发;Python 加载模型并创建状态;mj_step()计算它接下来会在哪里;Viewer 把结果显示出来。这条流程同样适用于后续的机械臂和机器人仿真。

下一篇将用固定方块、自由方块和单摆,进一步解释body、geom和joint如何配合。

参考资料

  • MuJoCo 官方概览:模型、数据与仿真步进
  • MuJoCo Python 文档:安装与 Viewer
  • MuJoCo XML 参考:body、geom 与 freejoint

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询