Implementation:Google deepmind Dm control MJCF Attach
| Metadata | |
|---|---|
| Knowledge Sources | dm_control |
| Domains | Physics Simulation, Robotics, Model Composition |
| Last Updated | 2026-02-15 00:00 GMT |
Overview
Concrete tool for composing multiple MJCF models into a single unified model via site.attach() and root.include_copy(), with automatic namespace scoping managed by the NameScope class.
Description
The MJCF Attach implementation provides two composition mechanisms:
1. site_element.attach(other_model)
This is the primary composition API, defined on _AttachableElement (which backs <site> and <worldbody> elements). The method:
- Validates that
other_modelis anRootElement, is not already attached, and is not the same model. - Performs a dry run of the full attachment to detect any attribute conflicts before committing.
- Assigns a unique scope name if the child model's name already exists in the parent's namescope. A numeric suffix (e.g.,
model_1,model_2) is appended automatically. - Sets
other_model.namescope.parent = self.namescope, establishing the hierarchical prefix chain. - Creates an
_AttachmentFrame-- a specialized<body>element that inherits the site's position, orientation, and other spatial attributes. The frame is inserted as a sibling of the site, immediately after it (and after any existing attachment frames). - Calls
self.root._attach(other_model, exclude_worldbody=True)to merge all non-worldbody sections (actuators, sensors, defaults, etc.) into the parent model. - Returns the attachment frame, which the caller can use to add joints or freejoints to give the attached model degrees of freedom.
2. root.include_copy(other_model, override_attributes=False)
This method copies all elements from other_model into the current model without creating a separate name scope. It uses the Copier class to deep-copy elements and then updates all internal references. This is the programmatic equivalent of the MJCF <include> directive.
3. root.detach()
Reverses a previous attachment by removing the child model's name scope from the parent and cleaning up the attachment frame.
NameScope (dm_control/mjcf/namescope.py) is the supporting data structure. Each scope maintains dictionaries of identifiers keyed by namespace (e.g., 'body', 'joint', 'geom'). The full_prefix() method computes the hierarchical prefix by walking up the parent chain and concatenating scope names with / separators. A revision counter is incremented on every structural change, enabling efficient cache invalidation for computed identifiers.
Usage
Use attach() to compose models with namespace isolation. Use include_copy() for flat merging without scoping. Use detach() to reverse an attachment.
Code Reference
| Property | Value |
|---|---|
| Source Location | dm_control/mjcf/element.py:L986-1042 (attach), dm_control/mjcf/element.py:L1045-1105 (_AttachmentFrame), dm_control/mjcf/element.py:L1212-1268 (RootElement.attach, include_copy, detach), dm_control/mjcf/namescope.py:L1-215
|
| Signature (attach) | attach(attachment: RootElement) -> _AttachmentFrame
|
| Signature (include_copy) | include_copy(other: RootElement, override_attributes=False) -> None
|
| Signature (detach) | detach() -> None
|
| Import | from dm_control import mjcf
|
I/O Contract
Inputs (attach):
| Parameter | Type | Description |
|---|---|---|
attachment |
mjcf.RootElement |
The child model to attach. Must not already be attached elsewhere. |
Inputs (include_copy):
| Parameter | Type | Description |
|---|---|---|
other |
mjcf.RootElement |
The model whose elements are copied into self.
|
override_attributes |
bool |
If True, conflicting attribute values in the source override those in the target.
|
Outputs:
| Output | Type | Description |
|---|---|---|
| return value (attach) | mjcf.Element (_AttachmentFrame) |
The attachment frame body; joints or freejoints can be added to it. |
| return value (include_copy) | None |
Elements are copied in place. |
| side effect | structural | The child model's namescope becomes a child of the parent's namescope; all identifiers in the child gain a hierarchical prefix. |
Usage Examples
from dm_control import mjcf
# Create an arena model
arena = mjcf.RootElement(model='arena')
arena.worldbody.add('geom', type='plane', size=[5, 5, 0.1])
spawn_site = arena.worldbody.add('site', name='spawn',
pos=[0, 0, 0.5])
# Create a robot model
robot = mjcf.RootElement(model='robot')
torso = robot.worldbody.add('body', name='torso')
torso.add('geom', type='box', size=[0.1, 0.1, 0.1], mass=1.0)
torso.add('joint', name='slide_x', type='slide', axis=[1, 0, 0])
robot.actuator.add('motor', name='motor_x', joint='slide_x')
# Attach the robot to the arena at the spawn site
attachment_frame = spawn_site.attach(robot)
# Optionally add a freejoint to give the robot full freedom
attachment_frame.add('freejoint', name='root')
# Attach a second copy -- automatically gets scope name 'robot_1'
robot2 = mjcf.RootElement(model='robot')
torso2 = robot2.worldbody.add('body', name='torso')
torso2.add('geom', type='sphere', size=[0.05], mass=0.5)
spawn_site2 = arena.worldbody.add('site', name='spawn2',
pos=[1, 0, 0.5])
spawn_site2.attach(robot2)
# Verify identifiers are properly scoped
print(arena.to_xml_string())
# Bodies will be named 'robot/torso' and 'robot_1/torso'
# Detach a model
robot.detach()