Implementation:Google deepmind Dm control MJCF Element Add
| Metadata | |
|---|---|
| Knowledge Sources | dm_control |
| Domains | Physics Simulation, Robotics, Model Authoring |
| Last Updated | 2026-02-15 00:00 GMT |
Overview
Concrete tool for building MJCF model trees by adding child elements via element.add() and setting attributes through Python attribute assignment, backed by strongly typed attribute classes.
Description
The add() method on any MJCF element creates a new child element of the specified tag type and returns it. Keyword arguments become the initial attribute values for the new element. Internally, add() delegates to insert() which:
- Validates that the tag is a legitimate child of the parent element.
- Checks whether the child spec is repeated (can appear multiple times) or on-demand (created lazily). A non-repeated, non-on-demand child that already exists raises a
ValueError. - Constructs the new element via
_make_element(), which selects the correct element subclass (e.g.,_AttachableElementfor sites,_DefaultElementfor defaults). - Appends the new element to the parent's child list (or inserts at a specified position).
- Increments the name scope revision counter to invalidate any cached identifiers.
Attribute assignment works through Python's __setattr__ override on _ElementImpl. When a user writes element.pos = [1, 2, 3], the element checks whether the name corresponds to a known MJCF attribute and dispatches to the typed attribute object's value setter. The attribute classes in dm_control/mjcf/attribute.py handle validation:
- String -- must be a non-empty
str. - Integer -- must be convertible to
intwithout truncation. - Float -- must be convertible to
float. - Array -- converts to a 1-D NumPy array of the specified dtype and checks length constraints.
- Keyword -- must match one of the predefined valid values (case-insensitive).
- Identifier -- must be a unique string within its namespace and must not contain
/. - Reference -- must be a string or an
mjcf.Elementof the correct namespace. - File -- can be a file path string or an
Assetobject; the file contents are loaded and hashed for VFS registration.
Usage
This is the primary API for authoring model content after a root element exists. Every body, geom, joint, actuator, sensor, and other MJCF component is created through add() calls and configured through attribute setters.
Code Reference
| Property | Value |
|---|---|
| Source Location | dm_control/mjcf/element.py:L616-661 (add/insert), dm_control/mjcf/attribute.py:L1-578 (attribute types)
|
| Signature (add) | add(element_name, **kwargs) -> Element
|
| Signature (insert) | insert(element_name, position, **kwargs) -> Element
|
| Attribute setter | element.<attr_name> = value (via __setattr__)
|
| Import | from dm_control import mjcf
|
I/O Contract
Inputs (add):
| Parameter | Type | Description |
|---|---|---|
element_name |
str |
The MJCF tag of the child to create (e.g., 'body', 'geom', 'joint').
|
**kwargs |
various | Initial attribute values. Keys must be valid MJCF attributes for the child element. |
Inputs (attribute setter):
| Parameter | Type | Description |
|---|---|---|
value |
depends on attribute type | The new value; validated by the corresponding attribute class (Array, Float, Keyword, etc.).
|
Outputs:
| Output | Type | Description |
|---|---|---|
| return value (add) | mjcf.Element |
The newly created child element, ready for further mutation. |
| side effect (setter) | none | The attribute value is stored in the element's internal attribute dictionary and the name scope revision is incremented. |
Usage Examples
from dm_control import mjcf
import numpy as np
# Create a model and build its structure
model = mjcf.RootElement(model='arm')
# Add a body to the worldbody
upper_arm = model.worldbody.add('body', name='upper_arm', pos=[0, 0, 1])
# Add a geom and a joint to the body
upper_arm.add('geom', type='capsule', size=[0.04, 0.2])
upper_arm.add('joint', name='shoulder', type='hinge', axis=[0, 1, 0])
# Add a child body (forearm) to the upper arm
forearm = upper_arm.add('body', name='forearm', pos=[0, 0, 0.4])
forearm.add('geom', type='capsule', size=[0.03, 0.15])
forearm.add('joint', name='elbow', type='hinge', axis=[0, 1, 0],
range=[-2.0, 0.0])
# Modify attributes after creation
forearm.pos = [0, 0, 0.45] # Update position via attribute setter
forearm.geom[0].rgba = [1, 0, 0, 1] # Set color
# Add actuators that reference joints
model.actuator.add('motor', name='shoulder_motor', joint='shoulder')
model.actuator.add('motor', name='elbow_motor', joint='elbow')
# Add a sensor
model.sensor.add('jointpos', name='elbow_sensor', joint='elbow')
# Verify the model compiles
print(model.to_xml_string())