New Our agentic pipeline is live — the agent researches an object before it builds it. Read the post →
RIGYD
Launch app ↗
mujocomjcfsimulation

MuJoCo Asset Requirements and Best Practices

MuJoCo loads almost any MJCF file, but a model that compiles is not a model that simulates correctly. This is the practical checklist for MuJoCo assets: compiler settings, mass and inertia, convex collision, contact parameters, joints, and the defaults that quietly break training.

Rigyd Team · Published 29 September 2026

MuJoCo is the physics engine most robot-learning teams reach for when they need contact-rich simulation that runs fast, from a single laptop to thousands of parallel environments on MJX or MuJoCo Warp. It is also forgiving in a dangerous way: almost any MJCF file compiles. An asset that compiles, renders, and even sits on a table can still carry the wrong mass, collide as the wrong shape, or become unstable the first time a gripper touches it.

This guide is the practical checklist: what MuJoCo expects from an asset, which defaults quietly break it, and how to check an asset before it costs you a training run. Every default cited here comes from the MuJoCo XML reference.

Quick answer: A MuJoCo asset is an MJCF model whose bodies have realistic mass and inertia, whose collision is built from convex geoms (MuJoCo replaces every mesh with its convex hull), whose contact parameters match the materials, and whose joints carry real limits, damping and armature. The defaults to watch are angles in degrees, geom density of water, mesh inertia set to legacy, and joint damping, armature and friction loss of zero.

Format requirements

MuJoCo reads:

  • MJCF (.xml), the native format and the one to target. It describes bodies, joints, geoms, actuators, sensors and simulation options in a single tree rooted at worldbody.
  • Mesh files as .stl, .obj or .msh, referenced from an <asset> block. The file extension decides the parser.
  • URDF, which MuJoCo can load directly. The documentation is explicit that URDF represents only a subset of what MuJoCo can model, so treat a URDF import as a starting point and convert to MJCF before you tune anything.

Best practice: keep one MJCF per asset with its meshes beside it, set meshdir in <compiler> so paths resolve, and make the asset a self-contained body that can be attached to any scene.

Compiler settings that change the meaning of the file

The <compiler> element decides how every number in the file is read. Four settings matter for assets:

AttributeDefaultWhy it matters
angledegreeJoint ranges and Euler angles are read in degrees. Radian values copied from URDF become near-zero limits.
autolimitstrueA joint is limited as soon as it has a range. Leave it on and always give articulated joints a range.
inertiafromgeomautoMass and inertia are inferred from geoms only when a body has no <inertial> element.
balanceinertiafalseAn inertia that violates the triangle inequality is a compile error, not a silent fix. Keep it that way and fix the inertia.

boundmass and boundinertia exist to patch massless bodies. If you need them, the asset has a modeling bug worth fixing instead.

Mass and inertia

Every moving body needs mass and inertia, and MuJoCo gets them one of two ways:

  • Explicitly, with an <inertial> element carrying mass, pos (the centre of mass), and diaginertia or fullinertia. This is the right choice when you know the real values.
  • Inferred from geoms, using each geom's mass or density. The default density is 1000, the density of water in SI units. A plastic cup, a steel bracket and a foam block left at the default all weigh as if they were water.

Mesh geoms add a second trap. The mesh inertia attribute defaults to legacy, which the documentation says over-counts volume for non-convex meshes and does not recommend. Use convex for solid parts, exact when the mesh is watertight and well-oriented, and shell for hollow objects such as bins, cups and housings, where the mass sits in the walls. For primitive geoms, shellinertia="true" does the same job.

For how to derive these values rather than guess them, see automatically estimating physics properties.

Collision geometry

This is where most MuJoCo assets go wrong, and the rule is simple: MuJoCo only collides convex shapes. Primitives are convex. Meshes can be non-convex and render that way, but for collision each mesh is replaced by its convex hull, computed with qhull. You can see the hulls in MuJoCo's simulate viewer with the H key.

A mug modelled as one mesh collides as a solid lump; nothing goes inside it and a gripper cannot hook the handle. To model a concave object, decompose it into several convex geoms attached to the same body. The decomposition method decides both fidelity and speed; V-HACD vs CoACD vs manual hulls compares the options, and collision mesh best practices covers hull counts per asset class.

Three practices keep collision correct and cheap:

  • Separate visual and collision geoms. Give high-detail visual geoms contype="0" and conaffinity="0" so they never enter collision, and put them in their own group. Collide only with the simplified convex geoms.
  • Cap hull size with maxhullvert. The default is unlimited. MuJoCo's MJX documentation suggests roughly 200 vertices or fewer per convex mesh for mesh-primitive collisions and fewer than 32 for convex-convex collisions, and recommends maxhullvert to get there.
  • Filter what can touch what. contype and conaffinity are 32-bit masks; two geoms collide only if one's contype shares a bit with the other's conaffinity. Parts of the same asset that should never touch each other should not be tested.

Contact parameters

MuJoCo's contact model is richer than a single friction coefficient, and the defaults are rarely right for a specific material:

  • friction takes three values, default 1 0.005 0.0001: sliding friction, torsional friction around the contact normal, and rolling friction.
  • condim sets which of those are used, default 3: 1 is frictionless, 3 is regular sliding friction, 4 adds torsional friction, 6 adds rolling friction. Objects that spin in the fingers or roll on a table need 4 or 6.
  • solref and solimp set how stiff and how damped the contact is, and how much penetration it tolerates. Soft materials and hard ones should not share values.
  • margin inflates the surface for contact detection, default 0. A small margin can stabilise resting contact; a large one makes objects float.
  • priority and solmix decide how two geoms' parameters combine when they differ.

Joints and articulated objects

Articulated assets in MuJoCo are nested bodies with joints between them, written as a tree rather than a list of links:

  • Joint types are hinge, slide, ball and free. A loose object that should fall, slide and be picked up needs a free joint (<freejoint/>) on its root body; without it the body is welded to its parent.
  • range sets the limits, read in degrees by default. With autolimits on, a range is all you need.
  • damping, armature and frictionloss all default to zero. Real hinges and slides have all three. armature adds inertia to the joint's motion, which also stabilises small, light parts such as latches and dials; frictionloss models dry friction, so a door stays where it is left.

Actuators are separate elements that act on joints. An object asset usually has none; a robot asset declares them explicitly.

Simulation options

The <option> element belongs to the scene, but an asset should be tested under the options it will run with:

  • timestep defaults to 0.002 s. The documentation calls it the single most important speed-accuracy trade-off. Stiff contacts need a smaller step; solver time constants in solref should be at least twice the timestep.
  • integrator defaults to Euler. MuJoCo's modeling guide recommends implicitfast as the default choice, especially for models with damping.
  • cone defaults to pyramidal. elliptic is the better physical model; pyramidal can be faster and more robust.
  • gravity defaults to 0 0 -9.81: Z is up. Author assets Z-up and in metres to match.

Validation: compiling is not enough

MuJoCo has no equivalent of NVIDIA's SimReady validator. The compiler catches malformed XML, missing files and invalid inertia, and that is where its guarantees end. Before an asset enters training:

  1. Compile it on its own and read every warning.
  2. Inspect the hulls in simulate with H, and confirm every concave feature the robot needs is represented by its own convex geom.
  3. Drop it and let it settle on a plane with a free joint. It should come to rest without jitter, sinking or drift.
  4. Check the numbers: total mass against the real object, centre of mass inside the body, joint limits in the units you meant.
  5. Exercise the joints to their limits and confirm damping and friction loss hold them where they should stop.
  6. If you also ship OpenUSD, compare the two exports; mass, limits and joint parameters should match.

Doing this by hand for every asset does not scale. Built-in validation for simulation assets explains how to move these checks into the pipeline that produces the asset.

Common pitfalls

  1. Radians in a degree file. Joint ranges from URDF or Python land in MJCF as radians while the compiler reads degrees.
  2. Everything weighs as water. No <inertial> and no density means default density 1000.
  3. Legacy mesh inertia. Non-convex meshes over-count volume unless inertia is convex, exact or shell.
  4. A single mesh for a concave object. It collides as its convex hull, so containers are solid and handles cannot be hooked.
  5. Visual meshes in collision. Detailed render meshes left at the default contype and conaffinity of 1 collide as large, expensive hulls.
  6. A welded root. An object without a free joint is fixed to the world and never moves.
  7. Zero damping and armature on small parts. Light latches and knobs oscillate or explode under contact.
  8. Unbounded hulls on GPU backends. Large convex hulls slow MJX and Warp far more than they slow CPU MuJoCo.

A pragmatic adoption sequence

  1. Start from a known-good model. MuJoCo Menagerie is the reference collection of robot models and a good template for structure and defaults.
  2. Author a few hero assets by hand so the team understands convex decomposition, contact parameters and joint tuning in MuJoCo terms.
  3. Write the validation steps above as a script and run it on every asset.
  4. Automate the long tail. Past a few dozen objects, generating assets with decomposition, physics and joints already set is faster than authoring them.
  5. Override with measured values where you have them, and randomize the rest.

How Rigyd approaches it

Rigyd exports MJCF and OpenUSD for every asset from text, images, or existing 3D files. Collision is built from CoACD convex decomposition, mass and inertia are derived from geometry and material rather than left at defaults, articulated parts carry their limits, damping and armature, and every asset is drop-and-settle tested in headless MuJoCo before it ships. In our parallel-environment benchmark, a Rigyd Rubik's cube with 310 hulls and six hinges ran 8,192 environments on one A100 in MuJoCo Warp. For the OpenUSD side of the same asset, see Isaac Sim asset requirements and best practices.

FAQ

Frequently asked questions

What format does MuJoCo expect for simulation assets?

MuJoCo's native format is MJCF, an XML model description. Meshes are referenced from it as STL, OBJ, or MSH files. MuJoCo can also load URDF, but URDF represents only a subset of what MJCF can express, so production assets are usually authored or converted to MJCF directly.

Does MuJoCo collide with the actual shape of a mesh?

No. MuJoCo replaces every mesh with its convex hull for collision, computed with qhull. A mug, a bowl, or a drawer collides as a solid convex shape unless it is decomposed into several convex geoms attached to the same body. That decomposition is the asset author's job, not the simulator's.

Why does my MuJoCo object weigh the wrong amount?

If a body has no inertial element and its geoms have no mass or density, MuJoCo infers mass from geometry at the default density of 1000 kg/m³, the density of water. Hollow objects are also over-counted unless the mesh inertia mode is set to shell or computed from a watertight mesh. Set mass explicitly or set a realistic density per geom.

Which MuJoCo defaults cause the most problems?

The compiler reads angles in degrees by default, so radian values copied from URDF become wildly wrong joint limits. Mesh inertia defaults to the legacy algorithm, which over-counts volume for non-convex meshes. Geom density defaults to water. Joint damping, armature and friction loss default to zero, which can make light articulated parts unstable.

How do I keep MuJoCo assets fast on MJX or MuJoCo Warp?

Keep convex hulls small and contacts few. MuJoCo's MJX documentation suggests roughly 200 vertices or fewer per convex mesh for mesh-primitive collisions and fewer than 32 for convex-convex collisions, and recommends the maxhullvert compiler attribute to get there. Mark visual-only geoms as non-colliding so the collision pipeline never sees them.

Build a simulation-ready asset

Turn a 3D model, image, or text description into validated OpenUSD and MJCF.