第 1 课
装好 MuJoCo 4.0,写第一个 MJCF 模型并渲染出第一张图
用 pip 安装 mujoco 4.0.0 并核对版本,认识 mujoco-core、mujoco-studio 与经典渲染依赖三部分;用 MJCF 写红方块加绿球模型,用按名字访问读 MjModel,看到 MjData 在 mj_kinematics 前后的变化;用 MUJOCO_GL=egl 离屏渲染,理解为什么不先 mj_forward 画面是全黑的。
课程目录第 1 / 3 课
学习位置仅保存在当前浏览器,有效期 180 天。
图文教材
本课对照的官方页面:Python 绑定文档 、官方入门 Colab 教程 、4.0.0 发布说明 。测试环境:2026-11-11,Asia/Shanghai,Linux x86_64 云主机(无独立显卡),Python 3.12.15,
mujoco4.0.0(PyPI 发布于 2026-11-06)。下面所有「对照结果」都是这台机器的真实输出,只做了节选。
MuJoCo 是 Google DeepMind 维护的开源物理引擎,机器人、强化学习和具身智能研究几乎都会用到它。本课先把 4.0.0 装好,弄清 4.0 新拆出来的三个包,再用官方教程里的「红方块 + 绿球」模型认识 MuJoCo 最核心的两个对象:描述「是什么」的 MjModel 和记录「此刻怎样」的 MjData,最后离屏渲染出第一张图。
第 1 步:建一个虚拟环境并安装 MuJoCo
官方推荐直接从 PyPI 安装,引擎本体已经打包在 wheel 里,不用另外下载编译。PyPI 上 4.0.0 要求 Python 3.11 或更高。
mkdir -p ~/mj-lab && cd ~/mj-lab
python3 -m venv .venv
source .venv/bin/activate
pip install mujoco
对照结果(节选):
Collecting mujoco
Downloading mujoco-4.0.0-py3-none-any.whl.metadata (232 kB)
Collecting mujoco-core==4.0.0 (from mujoco)
Collecting mujoco-studio==4.0.0 (from mujoco)
Collecting websockets>=13 (from mujoco-studio==4.0.0->mujoco)
Successfully installed glfw-2.11.0 mujoco-4.0.0 mujoco-core-4.0.0
mujoco-studio-4.0.0 numpy-2.5.4 pyopengl-3.1.10 websockets-17.4
Windows 上把 source .venv/bin/activate 换成 .venv\Scripts\activate。本课只在 Linux 上实测过。
第 2 步:认识 4.0 的三个包,确认版本
从 4.0.0 起,PyPI 上的 mujoco 变成了一个「总包」,它会装上三样东西:
| 包名 | 装了什么 |
|---|---|
| mujoco-core | C 引擎与最小 Python 绑定,只依赖 numpy |
| mujoco-studio | 新的 Studio 查看器与 Filament 渲染器 |
| glfw、pyopengl | 经典 OpenGL 渲染要用的依赖 |
官方 Python 文档写明:只想在服务器上跑仿真、训练强化学习、不需要任何画面时,可以只装 pip install mujoco-core(它只依赖 numpy)。确认版本:
pip list | grep -i mujoco
python -c "import mujoco; print(mujoco.__version__)"
对照结果:
mujoco 4.0.0
mujoco-core 4.0.0
mujoco-studio 4.0.0
4.0.0
第 3 步:用 MJCF 写第一个模型,读 MjModel
MuJoCo 的模型用一种叫 MJCF 的 XML 来写。最小的合法模型就是 <mujoco/>;所有物体都放在 <worldbody> 里。新建 hello_mujoco.py,内容如下(模型来自官方教程):
import mujoco
xml = """
<mujoco>
<worldbody>
<geom name="red_box" type="box" size=".2 .2 .2" rgba="1 0 0 1"/>
<geom name="green_sphere" pos=".2 .2 .2" size=".1" rgba="0 1 0 1"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
print('geom 数量:', model.ngeom)
print('所有 geom 名字:', [model.geom(i).name for i in range(model.ngeom)])
print('green_sphere 的 id:', model.geom('green_sphere').id)
print('green_sphere 的颜色:', model.geom('green_sphere').rgba)
try:
model.geom('blue_cone')
except KeyError as e:
print('写错名字时:', e)
data = mujoco.MjData(model)
print('计算前 geom_xpos:\n', data.geom_xpos)
mujoco.mj_kinematics(model, data)
print('计算后 geom_xpos:\n', data.geom_xpos)
print('按名字读位置:', data.geom('green_sphere').xpos)
两个细节:red_box 没写位置,默认就在原点;green_sphere 没写 type,默认类型就是球。MJCF 的每个属性都有默认值,完整列表在官方 XML Reference 里。
第 4 步:读 MjData,理解为什么要先「算一下」
运行上一步的脚本:
python hello_mujoco.py
对照结果:
geom 数量: 2
所有 geom 名字: ['red_box', 'green_sphere']
green_sphere 的 id: 1
green_sphere 的颜色: [0. 1. 0. 1.]
写错名字时: "Invalid name 'blue_cone'. Valid names: ['green_sphere', 'red_box']"
计算前 geom_xpos:
[[0. 0. 0.]
[0. 0. 0.]]
计算后 geom_xpos:
[[0. 0. 0. ]
[0.2 0.2 0.2]]
按名字读位置: [0.2 0.2 0.2]
怎么读这段输出:
MjModel里是不随时间变化的东西,比如有几个 geom、什么颜色、多大。model.geom('名字')这种「按名字访问」是 Python 绑定提供的便利写法,名字写错时会把所有合法名字列出来。MjData里是「此刻的状态」以及由状态算出来的量。刚创建时geom_xpos(每个 geom 在世界坐标里的位置)全是 0,因为还没算。调用mj_kinematics做一次运动学计算后,绿球才出现在 (0.2, 0.2, 0.2)。- 平时更常用的是
mj_forward,它把整条计算流水线跑到加速度为止,做的事比mj_kinematics多,但不容易漏算。下一步渲染就用它。
第 5 步:离屏渲染出第一张图
先装官方教程里用来存图和存视频的 mediapy(存视频还要系统里有 FFmpeg):
pip install mediapy
新建 first_image.py。这次给模型加一盏灯,否则画面很暗:
import mujoco
import mediapy as media
xml = """
<mujoco>
<worldbody>
<light name="top" pos="0 0 1"/>
<geom name="red_box" type="box" size=".2 .2 .2" rgba="1 0 0 1"/>
<geom name="green_sphere" pos=".2 .2 .2" size=".1" rgba="0 1 0 1"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)
with mujoco.Renderer(model) as renderer:
pixels = renderer.render()
print('不调用 mj_forward 时的平均亮度:', round(float(pixels.mean()), 1))
mujoco.mj_forward(model, data)
renderer.update_scene(data)
pixels = renderer.render()
print('图像尺寸:', pixels.shape, pixels.dtype)
print('调用 mj_forward 后的平均亮度:', round(float(pixels.mean()), 1))
media.write_image('first_image.png', pixels)
print('已保存 first_image.png')
在没有显示器的 Linux 服务器上,要先告诉 MuJoCo 用 EGL 做离屏渲染(官方 Colab 教程也是这样设置的);有桌面的电脑可以跳过这一行:
export MUJOCO_GL=egl
python first_image.py
对照结果:
不调用 mj_forward 时的平均亮度: 0.0
图像尺寸: (240, 320, 3) uint8
调用 mj_forward 后的平均亮度: 16.5
已保存 first_image.png
第一次渲染是全黑的(平均亮度 0),原因和第 4 步一样:没有先计算,场景里什么都没有。调用 mj_forward 并用 update_scene 把状态同步给渲染器之后,就能看到红方块和绿球。Renderer 默认输出 240×320 的 RGB 数组,可以用 mujoco.Renderer(model, 480, 640) 指定高和宽。
常见坑
- 装完 import 报找不到模块:多半是没激活虚拟环境,或者
pip和python不是同一个环境。用python -m pip install mujoco可以避免装错地方。 - 从 3.x 升级上来后 import 出错:4.0 把包拆分了,官方发布说明建议先卸载再装:
pip uninstall -y mujoco && pip install mujoco。 - 服务器上渲染报 OpenGL 相关错误:先确认设置了
MUJOCO_GL=egl。本课测试机上MUJOCO_GL=osmesa会报'NoneType' object has no attribute 'glGetError',原因是系统没装 OSMesa 库,换成 egl 即可。 - 读到的位置全是 0、渲染全黑:创建
MjData或改了状态之后,记得先调用mj_forward(或mj_kinematics)。 - 把数组存进列表后数值全一样:
MjData里的数组是直接指向引擎内存的视图,会被后续计算原地改写。要保存某一时刻的值,写data.qpos.copy()。
小结
这一课装好了 MuJoCo 4.0.0,知道了 mujoco 总包由 mujoco-core、mujoco-studio 和经典渲染依赖组成;用 MJCF 写了第一个模型,分清了 MjModel(模型描述)和 MjData(状态与计算结果),也知道读位置、渲染之前要先 mj_forward。下一课给物体加上关节让它动起来,把仿真录成视频,并复现官方教程里会自己翻身的「翻转陀螺」。
