- name
- mujoco-python
- description
- Python simulation lifecycle (MjSpec, MjModel, MjData), simulation stepping (mj_step, mj_step1, mj_step2, mj_forward), named access (data.body, model.geom), bind(), multi-threaded CPU batch simulation (mujoco.batch.Batch, mujoco.rollout), optimization and system ID (mujoco.minimize, mujoco.sysid, mjd_transitionFD), contact and touch sensors, sensordata, keyframe resets, integrators, mju_ spatial math (quaternions, poses), energy diagnostics. Use for Python physics simulation, state access, CPU batching, stepping loops, sensors. Do NOT use for model building/editing (mujoco-spec-editing), headless rendering (mujoco-rendering), GPU/TPU (mujoco-accelerated), cross-engine parity debugging (mujoco-parity), or GUI/viewers (mujoco-gui, mujoco-studio).
# MuJoCo Python: Simulation, Sensors & Math
> [!TIP]
>
> **Official MuJoCo documentation (`doc/*.rst`):** Read short chapters like
> [`../../programming/simulation.rst`](../../programming/simulation.rst)
> (`.. _Simulation:`) and [`../../overview.rst`](../../overview.rst) directly
> when in a MuJoCo checkout; for large reference files like
> [`../../python.rst`](../../python.rst) (`.. _PyStructs:`, `.. _PyNamed:`,
> `.. _PyBatch:`, `.. _PySysid:`),
> [`../../modeling.rst`](../../modeling.rst) (`.. _CTuning:`),
> [`../../computation/index.rst`](../../computation/index.rst) (`.. _Pipeline:`,
> `.. _geIntegrators:`, `.. _geChoosingTimestepIntegrator:`),
> [`../../XMLreference.rst`](../../XMLreference.rst), and
> [`../../APIreference/functions.rst`](../../APIreference/functions.rst), grep
> the literal `.. _Label:` anchor. Outside a checkout, fetch
> `https://mujoco.readthedocs.io/en/latest/<path>.html` (or `/en/stable/` for
> released pages; `functions.rst` renders inside
> `APIreference/APIfunctions.html`, and Sphinx anchors lowercase the label,
> e.g. `.. _PyBatch:` → `#pybatch`) and browse repo paths (`../../../`,
> `python/*.ipynb`) under
> `https://github.com/google-deepmind/mujoco/tree/main/`.
>
> **Related skills:**
>
> - [mujoco-spec-editing](../spec_editing/SKILL.md) — Procedural model
> editing and composing scenes with `MjSpec`.
> - [mujoco-rendering](../rendering/SKILL.md) — Offscreen image rendering,
> cameras, depth, and segmentation.
> - [mujoco-accelerated](../accelerated/SKILL.md) — GPU/TPU physics simulation
> with MJX (JAX) and MJWarp (CUDA).
> - [mujoco-parity](../parity/SKILL.md) — Cross-engine physics parity
> comparison against MJWarp and MJX.
> - [mujoco-studio](../studio/SKILL.md) — Interactive viewers, Studio viewer
> apps, and plugin architecture.
> [!CAUTION]
>
> - **NEVER use `mj_name2id`, `model.name2id`, or raw integer indexing**
> (e.g., `data.qpos[0]`, `data.xpos[body_id]`) to look up elements by name.
> Always use **named access** (`data.body('name').xpos`) or **`bind()`**
> instead.
> - **Do not use dm_control / PyMJCF lookup patterns.** `model.find('body',
> 'name')`, `.find_body()`, and `physics.named` do **not exist** in
> MuJoCo 3.x. Use direct accessors: `.body('name')`, `.geom('name')` etc. on
> `MjSpec`, `MjModel`, and `MjData`.
## 1. Compilation Lifecycle: MjSpec → MjModel → MjData
MuJoCo models follow a three-stage lifecycle:
```text
MjSpec ──spec.compile()──▶ MjModel ──MjData(model)──▶ MjData
│ │ │
│ (mutable blueprint) │ (compiled, mostly frozen) │ (simulation state)
│ │ │
└── spec.recompile(m, d) ─────┴────────────────────────────┘
```
1. **`MjSpec`**: Mutable data structure defining the simulation elements and
hierarchy.
2. **`MjModel`**: Compiled, optimized physics model containing constant and
precomputed parameters. Structural changes require recompilation.
3. **`MjData`**: Dynamic simulation state (positions, velocities, forces,
contacts, diagnostic buffers).
### Python Lifecycle
```python
import mujoco
# 1. Build or load spec
spec = mujoco.MjSpec()
body = spec.worldbody.add_body(name='pendulum', pos=[0, 0, 1])
body.add_geom(type=mujoco.mjtGeom.mjGEOM_SPHERE, size=[0.1])
body.add_joint(name='hinge', type=mujoco.mjtJoint.mjJNT_HINGE, axis=[0, 1, 0])
# 2. Compile to model and instantiate data
model = spec.compile()
data = mujoco.MjData(model)
mujoco.mj_forward(model, data)
# 3. Dynamic recompilation (preserves existing state)
body2 = spec.worldbody.add_body(name='ball2', pos=[1, 0, 1])
body2.add_geom(size=[0.1])
body2.add_freejoint()
model, data = spec.recompile(model, data) # Always reassign return values
```
### C++ Lifecycle
```cpp
#include <mujoco/mujoco.h>
// 1. Create or load spec (or: mj_parseXML(filename, nullptr, error, sizeof(error)))
mjSpec* spec = mj_makeSpec();
mjsBody* world = mjs_findBody(spec, "world");
mjsBody* body = mjs_addBody(world, nullptr);
mjsGeom* geom = mjs_addGeom(body, nullptr);
geom->type = mjGEOM_SPHERE;
geom->size[0] = 0.1;
// 2. Compile to model and create data
mjModel* model = mj_compile(spec, nullptr);
if (!model) {
printf("%s\n", mjs_getError(spec));
}
mjData* data = mj_makeData(model);
// 3. Step or evaluate
mj_forward(model, data);
// 4. Clean up allocated resources
mj_deleteData(data);
mj_deleteModel(model);
mj_deleteSpec(spec);
```
### Loading and Serializing
```python
# Direct model loading from file or string
model = mujoco.MjModel.from_xml_path('model.xml')
model = mujoco.MjModel.from_xml_string(xml_str)
model = mujoco.MjModel.from_binary_path('model.mjb') # Precompiled binary format
# Loading mutable spec from file or string
spec = mujoco.MjSpec.from_file('model.xml')
spec = mujoco.MjSpec.from_string(xml_str)
# Serialization
xml_string = spec.to_xml()
spec.encode('model.xml', model)
```
> [!CAUTION]
>
> `mujoco.load_model_from_path` does **not exist** in MuJoCo 3.x. Use
> `mujoco.MjModel.from_xml_path(path)` or `mujoco.MjSpec.from_file(path)`. For
> ready-to-run models, check `../../../model/` (`humanoid/humanoid.xml`,
> `tendon_arm/arm26.xml`, `car/car.xml`), `../../../test/engine/testdata/`, and
> MuJoCo Menagerie ([models.rst](../../models.rst), `.. _Menagerie:`).
## 2. Accessing State: Named Access & bind()
### 1. Named Access (on `MjModel` and `MjData`)
In Python, access elements by calling the element-type accessor with the element
name:
```python
# Reading state
torso_pos = data.body('torso').xpos # NumPy view (3,)
knee_angle = data.joint('knee').qpos # Shape depends on joint type
ball_size = model.geom('ball').size # NumPy view (3,)
# Writing controls and state
data.actuator('motor').ctrl = 1.0 # Writable view
data.joint('knee').qpos = 0.5
```
Supported element accessors: `body`, `geom`, `joint` (alias `jnt`), `site`,
`camera` (alias `cam`), `light`, `mesh`, `skin`, `texture` (alias `tex`),
`material` (alias `mat`), `pair`, `equality` (alias `eq`), `tendon` (alias
`ten`), `actuator`, `sensor`, `numeric`, `text`, `tuple`, `keyframe` (alias
`key`).
> [!WARNING]
>
> **Named access returns views, not copies.** In a simulation loop, modifying
> the simulation changes the array in-place. If logging or storing history,
> **always call `.copy()`**:
>
> ```python
> # ❌ WRONG — all history items will point to the same final state
> history.append(data.body('torso').xpos)
>
> # ✅ RIGHT — records distinct coordinates
> history.append(data.body('torso').xpos.copy())
> ```
### 2. `bind()` (Bridges `MjSpec` elements → `MjModel` / `MjData`)
When building models programmatically with `MjSpec`, use `bind()` to map spec
element handles directly to compiled data:
```python
geom = spec.worldbody.add_geom(name='ball', size=[0.1])
joint = spec.worldbody.add_joint(name='hinge', type=mujoco.mjtJoint.mjJNT_HINGE)
model = spec.compile()
data = mujoco.MjData(model)
# Read through bind
radius = model.bind(geom).size[0]
ball_pos = data.bind(geom).xpos
# Write through bind — always use .set()
data.bind(joint).set('qpos', 0.5)
# Bind lists of spec elements in batch
joints = [spec.joint('j1'), spec.joint('j2')]
joint_angles = data.bind(joints).qpos
```
## 3. Simulation Stepping & Execution Pipeline
### Stepping Pipeline (`mj_step` vs `mj_step1` / `mj_step2`)
- **`mj_step(model, data)`**: Advances the simulation by one timestep
(`model.opt.timestep`). It computes kinematics, forces, collision constraints,
accelerations, and numerical integration in one full pass.
- **Split Stepping (`mj_step1` + `mj_step2`)**: Used when custom controller
forces or external computations depend on the current kinematic state:
```python
# 1. Advance position kinematics and sensor computations
mujoco.mj_step1(model, data)
# 2. Apply control torques or external forces based on current kinematics
data.ctrl[:] = compute_feedback_control(data.qpos, data.qvel)
data.qfrc_applied[:] = compute_external_forces(data)
# 3. Complete dynamic acceleration and integration
mujoco.mj_step2(model, data)
```
### Sensor and Kinematic Initialization (`mj_forward`)
Before reading sensors or positions at time zero, call `mj_forward()` instead of
`mj_step()`. `mj_forward` evaluates kinematics and sensor outputs from the
current state **without advancing simulation time**:
```python
# ✅ RIGHT — initialize sensors and kinematics at t=0
mujoco.mj_forward(model, data)
initial_obs = data.sensordata.copy()
# Advance physics
for _ in range(num_steps):
mujoco.mj_step(model, data)
```
### Keyframe Resets & Restoring State
Use keyframes to reset configurations reliably:
```python
# Python: reset to named keyframe
key_id = model.key('home').id
mujoco.mj_resetDataKeyframe(model, data, key_id)
mujoco.mj_forward(model, data) # Recompute kinematics and sensors
```
In C++:
```cpp
int key_id = mj_name2id(m, mjOBJ_KEY, "home");
if (key_id >= 0) {
mj_resetDataKeyframe(m, d, key_id);
mj_forward(m, d);
}
```
### Integrators & Numerical Stability
Configured via `model.opt.integrator` (full equations and timestep/integrator
selection notes in [computation/index.rst](../../computation/index.rst),
`.. _geIntegrators:` and `.. _geChoosingTimestepIntegrator:`,
[modeling.rst](../../modeling.rst), `.. _CTuningGlobal:`, and
[XMLreference.rst](../../XMLreference.rst), `.. _option-integrator:`):
- `mjINT_IMPLICITFAST` (**recommended**): Velocity-implicit (damping, `kv`,
fluid drag, free-body gyroscopic forces) via symmetric Cholesky at ~Euler
cost.
View on GitHub