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.
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
mjcfobject (your model) and thestochasobject (distributions and random variables).- This class relies upon
pydanticV2 to statically type and validate entries.
- This class relies upon
-
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
-
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>becomesmojo.WorldBody().<body name="box">becomesmojo.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 aBodyNamewhere aSiteNameis needed Pylance will warn you about the invalid type).
- Attributes like
posandrgbaare 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
- asset/mesh
- (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
- deformable/flex
- 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
- tendon/spatial
- 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
- plugin/instance
- extension/plugin
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
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
NamedValuefor 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!
-
From your project root, generate stubs into a local
typings/directory: -
Recent MuJoCo builds compiled with newer pybind11 versions correctly expose enums as
SupportsInt. If you encounter Pyright enum type errors, apply the compatibility patch: -
Then in
pyproject.toml(for me, VSCode already type hints correctly, but this should fix things if you usepyrightwith your pre-commit hooks):
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.
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.