Skip to content

Getting Started

Abstract

MuJoCo Mojo is a high-level Python framework designed to turn static MuJoCo simulations into robust, scalable, and stochastic experiment pipelines.


Installation

Install mujoco-mojo using your preferred package manager. We recommend uv for speed and dependency management.

Bash
uv add mujoco-mojo
Bash
pip install mujoco-mojo

Core Concepts

Before writing code, it is helpful to understand the three pillars of a Mojo project:

  • The MojoModel: This is your "Source of Truth." It contains the mjcf object (your model) and the stochas object (distributions and random variables).
    • This class relies upon pydantic V2 to statically type and validate entries.
  • The Generate-Runtime Pattern: Mojo separates Generation (building the XML and sampling random values) from Runtime (the physics loop where forces and logic are applied).

    Example: Generate and Runtime Functions
    Python
    import mujoco_mojo as mojo
    
    def generate(
        mojo_model: mojo.MojoModel, *args, **kwargs) -> mojo.MojoModel:
        # Add geoms, sites, and sample distributions here
        return mojo_model
    
    def runtime(
        mojo_model: mojo.MojoModel,
        runtime_manager: rt.RuntimeManager,
        mj_model: mujoco.MjModel,
        mj_data: mujoco.MjData,
        *args,
        **kwargs,
    ):
        # Step the physics and apply logic here
        while mj_data.time < 10.0:
            runtime_manager.step(mj_model, mj_data)
    
  • Request-Based Telemetry: You don't (have to) manually log data. Instead, you "Request" that specific geoms, sites, or custom forces be tracked by the SignalManager.


The MJCF Object Model

The mojo.mjcf object is designed to be isomorphic to the MuJoCo XML schema.

You Can Do It!

If you know how to write an XML file, you know how to use mojo.mjcf.

It is recommended to reference the official XML Reference for specifics on the XML schema.

  • <worldbody> becomes mojo.WorldBody().
  • <body name="box"> becomes mojo.Body(name=mojo.BodyName("box")).
    • mojo.BodyName (and the other related names) are really strings, but are typed this way to allow for improved static analysis (e.g., if you try to use a BodyName where a SiteName is needed Pylance will warn you about the invalid type).
  • Attributes like pos and rgba are strictly typed using NumPy arrays and Pydantic validation.

This design ensures that there is "no magic". You are simply building a MuJoCo model using a strongly-typed Python API instead of a fragile string-based XML file.

Warning: Default Values

Some attributes in this package make use of default values. Wherever possible, the default values match what is stated in the XML Reference.

The to_xml method found in all XMLModel (which is most objects in mujoco_mojo.mjcf) has an argument (exclude_defaults) which will omit serializing fields set as default.

If you have a specific use case which is dependent on a value you leave as default, it is highly recommended that you pin that value as opposed to use the default. MuJoCo may change their defaults, and this package may fall behind. In that case, you would be using a "default" which is no longer the default.

Info: Implemented MJCF Tags
  • mujoco
  • option
    • option/⁠flag
  • compiler
    • compiler/⁠lengthrange
  • size
  • statistic
  • asset
    • asset/⁠mesh
      • mesh/⁠plugin
    • asset/⁠hfield
    • asset/⁠skin
    • asset/⁠texture
    • asset/⁠material
      • material/⁠layer
    • asset/⁠model
  • (world)body
    • body/⁠inertial
    • body/⁠joint
    • body/⁠freejoint
    • body/⁠geom
      • geom/⁠plugin
    • body/⁠site
    • body/⁠camera
    • body/⁠light
    • body/⁠composite
      • composite/⁠joint
      • composite/⁠geom
      • composite/⁠site
      • composite/⁠skin
      • composite/⁠plugin
    • body/⁠flexcomp
      • flexcomp/⁠contact
      • flexcomp/⁠edge
      • flexcomp/⁠elasticity
      • flexcomp/⁠pin
      • flexcomp/⁠plugin
    • body/⁠plugin
    • body/⁠attach
    • body/⁠frame
  • contact
    • contact/⁠pair
    • contact/⁠exclude
  • deformable
    • deformable/⁠flex
      • flex/⁠edge
      • flex/⁠elasticity
      • flex/⁠contact
    • deformable/⁠skin
      • skin/⁠bone
  • equality
    • equality/⁠connect
    • equality/⁠weld
    • equality/⁠joint
    • equality/⁠tendon
    • equality/⁠flex
    • equality/⁠distance
  • tendon
    • tendon/⁠spatial
      • spatial/⁠site
      • spatial/⁠geom
      • spatial/⁠pulley
    • tendon/⁠fixed
      • fixed/⁠joint
  • actuator
    • actuator/⁠general
    • actuator/⁠motor
    • actuator/⁠position
    • actuator/⁠velocity
    • actuator/⁠intvelocity
    • actuator/⁠damper
    • actuator/⁠cylinder
    • actuator/⁠muscle
    • actuator/⁠adhesion
    • actuator/⁠plugin
  • sensor
    • sensor/⁠touch
    • sensor/⁠accelerometer
    • sensor/⁠velocimeter
    • sensor/⁠gyro
    • sensor/⁠force
    • sensor/⁠torque
    • sensor/⁠magnetometer
    • sensor/⁠rangefinder
    • sensor/⁠camprojection
    • sensor/⁠jointpos
    • sensor/⁠jointvel
    • sensor/⁠tendonpos
    • sensor/⁠tendonvel
    • sensor/⁠actuatorpos
    • sensor/⁠actuatorvel
    • sensor/⁠actuatorfrc
    • sensor/⁠jointactuatorfrc
    • sensor/⁠tendonactuatorfrc
    • sensor/⁠ballquat
    • sensor/⁠ballangvel
    • sensor/⁠jointlimitpos
    • sensor/⁠jointlimitvel
    • sensor/⁠jointlimitfrc
    • sensor/⁠tendonlimitpos
    • sensor/⁠tendonlimitvel
    • sensor/⁠tendonlimitfrc
    • sensor/⁠framepos
    • sensor/⁠framequat
    • sensor/⁠framexaxis
    • sensor/⁠frameyaxis
    • sensor/⁠framezaxis
    • sensor/⁠framelinvel
    • sensor/⁠frameangvel
    • sensor/⁠framelinacc
    • sensor/⁠frameangacc
    • sensor/⁠subtreecom
    • sensor/⁠subtreelinvel
    • sensor/⁠subtreeangmom
    • sensor/⁠insidesite
    • collision sensors
    • sensor/⁠distance
    • sensor/⁠normal
    • sensor/⁠fromto
    • sensor/⁠contact
    • sensor/⁠tactile
    • sensor/⁠e_potential
    • sensor/⁠e_kinetic
    • sensor/⁠clock
    • sensor/⁠user
    • sensor/⁠plugin
  • keyframe
    • keyframe/⁠key
  • visual
    • visual/⁠global
    • visual/⁠quality
    • visual/⁠headlight
    • visual/⁠map
    • visual/⁠scale
    • visual/⁠rgba
  • default
    • default/⁠mesh
    • default/⁠material
    • default/⁠joint
    • default/⁠geom
    • default/⁠site
    • default/⁠camera
    • default/⁠light
    • default/⁠pair
    • default/⁠equality
    • default/⁠tendon
    • default/⁠general
    • default/⁠motor
    • default/⁠position
    • default/⁠velocity
    • default/⁠intvelocity
    • default/⁠damper
    • default/⁠cylinder
    • default/⁠muscle
    • default/⁠adhesion
  • custom
    • custom/⁠numeric
    • custom/⁠text
    • custom/⁠tuple
      • tuple/⁠element
  • extension
    • extension/⁠plugin
      • plugin/⁠instance
        • instance/⁠config

Random Values and Distributions

Mojo treats stochasticity as a first-class citizen. Instead of using np.random directly, you define a Distribution and sample it through the MojoModel.

Note: Extra Reading

For additional details on the stochastic tools provided, see the stochas documentation

Example: Sampling a Distribution with MojoModel
Python
1
2
3
4
5
6
7
8
9
stiffness = mojo_model.sample_dist(
    mojo.TruncatedNormalDistribution(
        name=mojo.DistName("spring_stiffness"),
        nominal=100,
        mu=100,
        sigma=20,
        low=0,
    )
)

Why use Mojo's sampling?

  • Reproducibility: Every draw is anchored to a global seed.
  • Named Values: Every random draw is saved as a NamedValue. This means your results database automatically knows exactly what "spring_stiffness" was used for Trial #42.
    • It also helps to ensure you do not accidentally overwrite or modify a value.
  • Global Overrides: You can run a job and tell Mojo to ignore the distribution and force a specific NamedValue for testing.

MuJoCo Syntax Highlighting

MuJoCo does not currently ship first-party Python typing stubs. To enable proper autocomplete and static type checking (Pylance/Pyright), generate local stubs using pybind11-stubgen.

Thanks to the work by @kevinzakka and @mluogh-xdof for figuring all this out!

  1. From your project root, generate stubs into a local typings/ directory:

    Bash
    pybind11-stubgen mujoco -o typings/ --numpy-array-wrap-with-annotated
    
  2. Recent MuJoCo builds compiled with newer pybind11 versions correctly expose enums as SupportsInt. If you encounter Pyright enum type errors, apply the compatibility patch:

    Bash
    python typings/patch_mujoco_enums.py typings/mujoco/_enums.pyi
    
  3. Then in pyproject.toml (for me, VSCode already type hints correctly, but this should fix things if you use pyright with your pre-commit hooks):

    pyproject.toml
    1
    2
    3
    4
    [tool.pyright]
    stubPath = "typings"
    venvPath = "."
    venv = ".venv"
    

Tip: Scaffold a New Project

Before diving into the generate script, run mujoco-mojo init in a new directory to get a working project skeleton — simulation.py, run.sh, and reloaded.sh — pre-wired and ready to edit.

Bash
mujoco-mojo init

See the Initializing a Project guide for details.

Success

This concludes our basic overview of the core concepts to MuJoCo Mojo. The next guide will cover how to get started with assembling your first model with the generate script.