Back to Integrations
/ INTEGRATION · Simulation

MuJoCo

MuJoCo is DeepMind's open-source physics engine for contact-rich robotics research. Here is how recorded demonstration data connects to MJCF models.

Updated Aug 20265 min read
SHORT ANSWER

MuJoCo is Google DeepMind's physics engine, open source under Apache 2.0 since May 2022, and the default choice for contact-rich manipulation and locomotion research. Robot demonstration data connects to it through MJCF model authoring, URDF conversion, and using the engine as a kinematic validation harness for recorded trajectories.

MuJoCo — Multi-Joint dynamics with Contact — is a physics engine, not a full simulation platform: no built-in photorealistic renderer, no asset marketplace, no digital-twin authoring tools. What it has instead is a contact solver that research groups trust, a small and well-documented XML model format, and a license that costs nothing. Google DeepMind acquired MuJoCo in October 2021 and open-sourced it under Apache 2.0 in May 2022, ending the paid-license model it ran under as a Roboti LLC product. That switch, plus its reputation for numerically stable contact dynamics, is why it dominates academic manipulation and locomotion research even in a field with heavier commercial alternatives.

For a robot-data pipeline, MuJoCo sits in two places: as the physics backend for training environments (dm_control, mujoco_playground, and most published manipulation RL benchmarks build on it), and as a lightweight validation harness for recorded trajectories, independent of whether the actual training happens somewhere else.

Why contact-rich research leans on MuJoCo

Two things explain MuJoCo's pull in academic manipulation and locomotion work specifically, as opposed to simulation generally. First, its contact solver is built around soft, analytically differentiable constraints rather than the hard-contact formulations older engines used, which makes contact-heavy scenes — grasping, pushing, legged locomotion — numerically stable at the timesteps researchers actually want to run at. Second, MJCF is small enough to read end to end: a robot arm with a gripper is a few hundred lines of XML, which keeps the model itself inspectable rather than a black box generated by an importer. Neither property is unique to MuJoCo, but the combination, plus a zero-cost license since 2022, is why it shows up as the simulator of record in most published contact-rich manipulation papers rather than a heavier commercial alternative.

How the data gets there

MuJoCo's native model format is MJCF — an XML dialect distinct from URDF, the ROS-standard robot description format most real robot descriptions ship in. The two disagree on enough details that conversion is a real step, not a formality:

  • URDF describes a tree of links and joints and cannot express closed kinematic loops; MJCF can.
  • URDF inertia is specified per link; MJCF can compute inertia from mesh geometry and density, which sounds convenient but produces different numbers than a URDF's declared values if you are not careful.
  • URDF has no concept of contact stiffness, damping, or friction cone parameters; MJCF exposes all of them, and the defaults MuJoCo picks when they are unspecified rarely match the real robot's actual contact behavior.

MuJoCo ships a compiler that reads URDF directly, and standalone tools (urdf2mjcf, and the reverse-direction mjcf2urdf) exist for scripted conversion, but every source describing this path calls out the same failure modes: contact and actuator parameters that URDF never specified landing on MuJoCo's defaults, and broken mesh paths, chief among them. A URDF-to-MJCF conversion needs a visual and numerical review before you trust it, not just a successful parse.

Loading a model and checking a recorded episode against it looks like this:

import mujoco
import numpy as np
 
model = mujoco.MjModel.from_xml_path("franka_fr3/scene.xml")
data = mujoco.MjData(model)
 
# recorded_qpos: (T, nq) array of joint positions from a captured episode
for t, qpos in enumerate(recorded_qpos):
    data.qpos[:] = qpos
    mujoco.mj_kinematics(model, data)  # forward kinematics only, no dynamics step
 
    # compare the simulated end-effector pose to what was logged at capture time
    ee_pos = data.site("end_effector").xpos
    logged_pos = recorded_ee_pos[t]
    err = np.linalg.norm(ee_pos - logged_pos)
    if err > 0.01:
        print(f"frame {t}: FK/logged pose mismatch, {err*1000:.1f} mm")

mj_kinematics alone recomputes body and site positions from qpos without stepping dynamics — the right call for a consistency check, since you are not trying to simulate the episode, only verify that the geometry and the log agree.

The workflow in practice

Groups working with MuJoCo day to day tend to do a mix of the following:

  • Start from mujoco_menagerie rather than authoring from scratch. The repository, curated by DeepMind, carries working MJCF, meshes, and example scenes for common arms, grippers, humanoids, and quadrupeds. Loading the robot_descriptions Python package pulls a Menagerie model by name instead of vendoring XML by hand.
  • Convert once, then hand-tune. After an automated URDF-to-MJCF pass, someone with the real robot's datasheet reviews joint limits, actuator gains, and contact parameters against the physical spec before the model is trusted for anything beyond a rough kinematic check.
  • Use MuJoCo as a fast pre-flight check on captured data, independent of what trains the actual policy — replaying joint trajectories through forward kinematics catches calibration drift, timestamp misalignment between cameras and joint state, and out-of-limit commands before an episode reaches a training set.
  • Move to MJX for scale. MJX (MuJoCo XLA) reimplements the solver in JAX for GPU/TPU-parallel batched simulation, used when a team needs thousands of parallel rollouts for RL or large-scale domain randomization rather than a single real-time instance.

Gotchas

Solver settings change simulated behavior, not just speed. Two MJCF files with identical geometry but different solver, iterations, or timestep settings can produce visibly different contact dynamics. If you are comparing a sim rollout to a real trajectory, keep the solver configuration fixed and documented.

mj_forward is not idempotent under contact. Calling mj_forward repeatedly on the same state can alter results when the model involves contacts and tendons, because internal warm-start state carries over. Use mj_kinematics for a pure pose check, as above, rather than mj_forward if you do not want dynamics side effects.

No native camera-realism pipeline. MuJoCo's rendering is fast but not photorealistic, so it is a poor source of synthetic RGB training data on its own — teams pair it with a separate renderer, or use it purely for state/contact-level validation rather than image generation.

Closed loops and URDF don't mix. A parallel-jaw gripper or four-bar linkage modeled with a closed kinematic loop has no clean URDF representation. If the real robot has one, expect to author that part of the MJCF by hand rather than relying on any converter.

MJCFURDFUSD

KEY FACTS

STATUS
Open source (Apache 2.0) since May 2022, maintained by Google DeepMind
NATIVE FORMAT
MJCF (XML)
MODEL LIBRARY
mujoco_menagerie — curated robot models from DeepMind
GPU-PARALLEL VARIANT
MJX (MuJoCo XLA), JAX-based batched simulation

/ QUESTIONS

Frequently asked

Put this into practice.

Tell us what your robots need to learn. We will scope the rig, the operators, the protocol, and the first datasets — usually in one call.