Skip to main content

mujoco-python

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).

Source facts

Repository
google-deepmind/mujoco
Last source activity
November 1, 2026 at 13:25
Detected SKILL.md language
English
Stars
16,031
Forks
1,912

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
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
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub