Heuristic:Google deepmind Dm control MJCF Model Composition Gotchas
| Knowledge Sources | |
|---|---|
| Domains | Model_Building, Debugging |
| Last Updated | 2026-02-15 05:00 GMT |
Overview
Common pitfalls and rules when composing MJCF models via PyMJCF's attach/include system, including default class isolation, global option matching, and cross-model references.
Description
PyMJCF's model attachment system enables modular robot construction by attaching child models (arms, hands, sensors) to parent models (arenas, bodies). However, several non-obvious rules govern how attached models interact. Violating these rules causes silent correctness bugs (wrong joint angles, conflicting defaults) rather than obvious errors. This heuristic documents the key gotchas from the PyMJCF README and codebase.
Usage
Apply this heuristic when building composite environments with multiple attached MJCF models, debugging unexpected physics behavior after model attachment, or creating reusable Entity subclasses in the Composer framework.
The Insight (Rule of Thumb)
- Rule 1 (Global Options Must Match): All models being attached must have compatible `<compiler>`, `<option>`, `<size>`, and `<visual>` global settings. Mismatches are silent -- e.g., if one model uses radians and another degrees, all angles in the first model will be interpreted incorrectly after attachment.
- Rule 2 (Default Classes Are Isolated): PyMJCF automatically transforms global defaults in child models into a scoped `default class="/"` to prevent default attribute pollution across model boundaries. This happens transparently but means defaults do NOT propagate from parent to child or vice versa.
- Rule 3 (Cross-Model References Need Elements): When setting reference attributes (e.g., `body1`, `body2` on constraints) that point across model boundaries, use `mjcf.Element` objects, NOT string names. Strings cannot contain the `/` separator used in namespaced identifiers.
- Rule 4 (Single Attachment): An MJCF model can only be attached to a parent once. To create multiple instances, use `copy.deepcopy()` or call the Entity constructor multiple times.
- Rule 5 (Attachment Frame Children): The automatically created attachment frame body only supports direct `<joint>` and `<inertial>` children. Other elements (geoms, sites) should go in the attached model's `<worldbody>`.
- Rule 6 (Python Keyword): Use `foo.dclass` instead of `foo.class` to access the default class attribute (since `class` is a Python keyword).
Reasoning
These gotchas stem from MJCF's XML-flat design colliding with PyMJCF's hierarchical composition model. MJCF was designed for single monolithic XML files where global settings are consistent. PyMJCF's namescoping and default isolation mechanisms bridge this gap, but they introduce constraints that are not documented in MuJoCo's own documentation -- only in dm_control's PyMJCF README.
The "silent mismatch" issue (Rule 1) is the most dangerous because it produces physically plausible but incorrect simulations. For example, attaching a model authored in degrees to a parent in radians will make all the child's joint ranges wrong by a factor of ~57x without any error.
Code evidence from `mjcf/README.md` (global options):
All models being attached must have matching <compiler>, <option>, <size>,
<visual> attributes. Mismatch silently breaks - e.g., if one model assumes
radians and other uses degrees, all angles in first model wrong!
Code evidence from `mjcf/README.md` (cross-model references):
When attaching models, if referencing elements in different models, MUST use
mjcf.Element objects, not strings. Strings cannot contain '/' and won't work
for cross-model references.