Back to Simulation Environments

MuJoCo Physics Engine

25 min

MuJoCo Physics Engine

MuJoCo (Multi-Joint dynamics with Contact) is a fast and accurate physics engine widely used in robotics and reinforcement learning research. It's now open-source and maintained by DeepMind.

Why MuJoCo?

MuJoCo excels at:

  1. Speed: Optimized for fast simulation
  2. Accuracy: Stable contact dynamics
  3. Differentiability: Supports gradient computation
  4. Flexibility: Easy model definition

Installation

bash
# Install MuJoCo
pip install mujoco

# Install with viewer
pip install mujoco-python-viewer

# For RL environments
pip install gymnasium[mujoco]

Basic Usage

Loading and Simulating

python
import mujoco
import numpy as np

# Load model from XML
model = mujoco.MjModel.from_xml_path("robot.xml")
data = mujoco.MjData(model)

# Simulation loop
for _ in range(1000):
    # Set control
    data.ctrl[:] = np.random.randn(model.nu)
    
    # Step simulation
    mujoco.mj_step(model, data)
    
    # Get state
    position = data.qpos.copy()
    velocity = data.qvel.copy()
    
    print(f"Position: {position}, Velocity: {velocity}")

Model Definition (MJCF)

xml
<!-- robot.xml -->
<mujoco model="simple_robot">
  <worldbody>
    <!-- Ground plane -->
    <geom type="plane" size="5 5 0.1" rgba="0.8 0.8 0.8 1"/>
    
    <!-- Robot body -->
    <body name="torso" pos="0 0 1">
      <joint type="free"/>
      <geom type="capsule" size="0.1" fromto="0 0 0 0 0 0.5" rgba="1 0 0 1"/>
      
      <!-- Arm -->
      <body name="upper_arm" pos="0 0 0.5">
        <joint name="shoulder" type="hinge" axis="0 1 0" range="-90 90"/>
        <geom type="capsule" size="0.05" fromto="0 0 0 0.3 0 0" rgba="0 1 0 1"/>
        
        <!-- Forearm -->
        <body name="forearm" pos="0.3 0 0">
          <joint name="elbow" type="hinge" axis="0 1 0" range="0 135"/>
          <geom type="capsule" size="0.04" fromto="0 0 0 0.25 0 0" rgba="0 0 1 1"/>
        </body>
      </body>
    </body>
  </worldbody>
  
  <actuator>
    <motor joint="shoulder" ctrlrange="-1 1"/>
    <motor joint="elbow" ctrlrange="-1 1"/>
  </actuator>
</mujoco>

Advanced Features

Contact Dynamics

python
def get_contact_info(model, data):
    """Extract contact information"""
    contacts = []
    
    for i in range(data.ncon):
        contact = data.contact[i]
        
        # Get contacting bodies
        geom1 = model.geom(contact.geom1).name
        geom2 = model.geom(contact.geom2).name
        
        # Contact force
        force = np.zeros(6)
        mujoco.mj_contactForce(model, data, i, force)
        
        contacts.append({
            "geom1": geom1,
            "geom2": geom2,
            "position": contact.pos.copy(),
            "force": force[:3],
            "torque": force[3:]
        })
    
    return contacts

Inverse Kinematics

python
def inverse_kinematics(model, data, target_pos, site_name, max_iter=100):
    """Solve IK for a site to reach target position"""
    site_id = model.site(site_name).id
    
    for _ in range(max_iter):
        # Current site position
        current_pos = data.site_xpos[site_id]
        
        # Error
        error = target_pos - current_pos
        if np.linalg.norm(error) < 1e-4:
            break
        
        # Compute Jacobian
        jacp = np.zeros((3, model.nv))
        mujoco.mj_jacSite(model, data, jacp, None, site_id)
        
        # Pseudoinverse solution
        dq = np.linalg.pinv(jacp) @ error
        
        # Update joint positions
        data.qpos[:] += dq * 0.1
        mujoco.mj_forward(model, data)
    
    return data.qpos.copy()

Rendering

python
import mujoco.viewer

# Interactive viewer
with mujoco.viewer.launch_passive(model, data) as viewer:
    while viewer.is_running():
        mujoco.mj_step(model, data)
        viewer.sync()

# Offscreen rendering
renderer = mujoco.Renderer(model, height=480, width=640)

def render_frame(data):
    renderer.update_scene(data)
    return renderer.render()

Integration with Gymnasium

python
import gymnasium as gym

# Create environment
env = gym.make("Humanoid-v4", render_mode="rgb_array")

# Collect data for world model
def collect_trajectory(env, policy, num_steps=1000):
    trajectory = {
        "observations": [],
        "actions": [],
        "rewards": [],
        "frames": []
    }
    
    obs, info = env.reset()
    
    for _ in range(num_steps):
        action = policy(obs)
        next_obs, reward, terminated, truncated, info = env.step(action)
        frame = env.render()
        
        trajectory["observations"].append(obs)
        trajectory["actions"].append(action)
        trajectory["rewards"].append(reward)
        trajectory["frames"].append(frame)
        
        obs = next_obs
        
        if terminated or truncated:
            obs, info = env.reset()
    
    return trajectory

MuJoCo for World Model Training

python
class MuJoCoWorldModelEnv:
    """Environment wrapper for world model training"""
    
    def __init__(self, model_path):
        self.model = mujoco.MjModel.from_xml_path(model_path)
        self.data = mujoco.MjData(self.model)
        self.renderer = mujoco.Renderer(self.model)
    
    def get_observation(self):
        return {
            "qpos": self.data.qpos.copy(),
            "qvel": self.data.qvel.copy(),
            "image": self.render()
        }
    
    def step(self, action):
        self.data.ctrl[:] = action
        mujoco.mj_step(self.model, self.data)
        return self.get_observation()
    
    def render(self):
        self.renderer.update_scene(self.data)
        return self.renderer.render()
    
    def get_state(self):
        """Get full simulation state for world model"""
        return {
            "qpos": self.data.qpos.copy(),
            "qvel": self.data.qvel.copy(),
            "ctrl": self.data.ctrl.copy(),
            "time": self.data.time
        }
    
    def set_state(self, state):
        """Set simulation state"""
        self.data.qpos[:] = state["qpos"]
        self.data.qvel[:] = state["qvel"]
        self.data.ctrl[:] = state["ctrl"]
        self.data.time = state["time"]
        mujoco.mj_forward(self.model, self.data)

Summary

MuJoCo provides fast, accurate physics simulation essential for training world models. Its speed enables large-scale data collection, while its accuracy ensures learned models transfer to real robots.