1. MuJoCo 初识:从零开始认识这个物理引擎

大家好,我是你们这门课的老朋友。做了十几年机器人仿真,我接触过的物理引擎少说也有五六个。但 MuJoCo 是我个人最偏爱的一个——不是因为它完美无缺,而是因为它「懂」机器人。

这一章,咱们先不急着写代码。先聊聊 MuJoCo 到底是什么,为什么值得你花时间学它,以及物理引擎里那些绕不开的核心概念。最后,我会带你跑起第一个仿真程序。

MuJoCo 是什么?

MuJoCo 的全称是 Multi-Joint dynamics with Contact,直译过来就是「多关节动力学与接触」。它是由 Roboti LLC 开发,后来被 Google DeepMind 收购的开源物理引擎。

说白了,它是一个专门用来模拟机器人、生物力学和物理交互的软件工具。你给它一个机器人模型,告诉它关节怎么连、地面摩擦力多大,它就能算出下一秒机器人会怎么动。

我最早接触 MuJoCo 是在 2018 年,当时做一个四足机器人的步态规划。用别的引擎算接触力,动不动就穿透地面,调参数调到怀疑人生。换了 MuJoCo 之后,第一次跑通仿真,那个接触稳定性让我印象特别深。

为什么选择 MuJoCo?

市面上物理引擎不少,Bullet、ODE、PhysX、DART……为什么我推荐 MuJoCo?

特性 MuJoCo 其他引擎(典型)
接触建模 基于凸优化,稳定不穿透 惩罚法,容易穿透或抖动
计算速度 极快,支持实时仿真 中等,复杂场景可能掉帧
精度 解析梯度,适合控制与优化 数值梯度,精度有限
Python 接口 原生支持,API 简洁 需要额外绑定
开源 Apache 2.0 协议 部分引擎商用受限

我个人最看重的是两点:接触稳定和解析梯度。做机器人控制的人都知道,接触力算不准,后面的规划全是白搭。而解析梯度意味着你可以把 MuJoCo 嵌入到优化算法里,做轨迹优化、参数校准,效率高一个数量级。

核心优势一句话总结: MuJoCo 在「精度」和「速度」之间找到了一个极好的平衡点,尤其适合机器人领域的仿真与控制。

物理引擎的核心概念

在动手之前,咱们得先搞懂三个基础概念。你想想看,物理引擎本质上就是在解一个「物体怎么动」的问题。那它需要知道三件事:

自由度(Degrees of Freedom, DoF)

自由度就是描述物体运动状态所需的独立变量个数。一个自由漂浮的刚体有 6 个自由度:3 个平移 + 3 个旋转。一个机械臂的每个关节通常贡献 1 个自由度(旋转或平移)。

MuJoCo 里,自由度是建模的基础。你定义好关节类型,引擎自动计算系统的总自由度。我记得有一次帮学生调模型,他死活跑不通,最后发现是关节类型写错了,自由度对不上。嗯,这种坑踩过一次就记住了。

约束(Constraints)

约束就是限制物体运动的规则。关节是约束,接触也是约束。MuJoCo 把约束统一处理成「等式或不等式条件」,然后用优化方法求解。

举个例子:你让一个球掉到地上,球不能穿透地面——这就是一个不等式约束。MuJoCo 用凸优化来保证约束严格满足,不会出现「穿模」的情况。

积分器(Integrator)

积分器是物理引擎的「时间推进器」。它根据当前状态和受力,算出下一时刻的状态。MuJoCo 支持多种积分器:

积分器类型 特点 适用场景
Euler 简单、快,但精度低 快速预览、低精度需求
RK4 精度高,计算量稍大 需要精确轨迹的场景
Implicit 稳定,适合刚性系统 机器人接触、抓取

我个人习惯在机器人仿真中优先用 Implicit 积分器。它虽然计算量稍大,但稳定性好,尤其在处理接触时不会出现「弹簧效应」——就是那种物体明明该停住,却还在微微抖动的现象。

小技巧: 刚开始调试模型时,先用 Euler 积分器快速验证逻辑,确认没问题后再切到 Implicit 做精确仿真。这样能省不少时间。

物理引擎工作流程

下面这张图展示了 MuJoCo 仿真一个时间步的核心流程。我建议你仔细看看,理解了这个流程,后面调参就心里有数了。

MuJoCo 单步仿真流程 输入当前状态 计算力与力矩 求解约束(接触/关节) 积分器推进时间步 输出下一时刻状态 关键说明 • 约束求解是 MuJoCo 的核心优势 • 积分器类型影响精度与速度 • 每一步都保证约束不违反 • 支持自定义力与控制器

安装与第一个仿真程序

好了,理论说够了,咱们来点实际的。安装 MuJoCo 其实很简单,我推荐用 Python 接口,方便调试。

安装步骤

  1. 安装 MuJoCo 本体:从 GitHub 下载最新版,或者直接用 pip 安装 Python 绑定。
  2. 安装 mujoco-py:pip install mujoco(注意,新版本已经统一为 mujoco 包)。
  3. 验证安装:运行一个简单的测试脚本。

注意: 如果你用的是 Windows,建议用 WSL2 或者直接上 Linux。MuJoCo 在 Windows 上也能跑,但有些底层优化在 Linux 上更稳定。我曾经在 Windows 上折腾了半天,最后切到 Ubuntu 一次搞定。

第一个仿真程序

下面这个程序会创建一个简单的摆锤模型,然后仿真 5 秒钟。代码不长,但包含了 MuJoCo 仿真的核心步骤。

import mujoco
import numpy as np

# 创建模型 XML
xml = """
<mujoco>
    <worldbody>
        <body name="pendulum" pos="0 0 0">
            <joint name="hinge" type="hinge" pos="0 0 0" axis="0 1 0"/>
            <geom name="rod" type="capsule" fromto="0 0 0 0 0 -0.5" size="0.02" rgba="0.8 0.2 0.2 1"/>
            <geom name="mass" type="sphere" pos="0 0 -0.5" size="0.05" rgba="0.2 0.2 0.8 1"/>
        </body>
    </worldbody>
</mujoco>
"""

# 创建模型和数据
model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)

# 仿真 5 秒,步长 0.01 秒
duration = 5.0
steps = int(duration / model.opt.timestep)

for i in range(steps):
    mujoco.mj_step(model, data)
    if i % 100 == 0:  # 每 100 步打印一次
        print(f"Time: {data.time:.2f}s, Angle: {data.qpos[0]:.3f} rad")

print("仿真完成!")

这段代码做了几件事:

  • 用 XML 定义了一个摆锤模型,包含一个铰链关节和一个杆+重物
  • 创建 MjModel 和 MjData 对象——这是 MuJoCo 的核心数据结构
  • 循环调用 mj_step 推进仿真
  • 每 100 步输出一次当前角度

你运行之后会看到角度在变化,这就是物理引擎在计算摆锤的摆动。简单吧?但背后涉及了自由度计算、约束求解、积分推进——就是咱们刚才讲的那一套。

调试建议: 第一次跑仿真时,把 model.opt.timestep 打印出来看看。默认是 0.002 秒,如果你觉得仿真太慢或太快,可以调整这个值。但注意,步长越大,精度越低。

本章小结

这一章咱们走了一遍 MuJoCo 的入门流程。你知道了它是什么、为什么选它、核心概念怎么理解,还亲手跑起了第一个仿真。别小看这个摆锤,它包含了物理引擎最核心的要素:自由度、约束、积分器。