0
# MJCF Model Building
1
2
Object-oriented library for creating, manipulating, and composing MuJoCo MJCF (MuJoCo XML Configuration Format) models programmatically. Provides high-level abstractions for model elements while maintaining full compatibility with MuJoCo's XML format and enabling complex model composition.
3
4
## Capabilities
5
6
### Model Loading and Parsing
7
8
Load MJCF models from various sources with full element hierarchy support.
9
10
```python { .api }
11
def from_xml_string(xml_string: str) -> 'RootElement':
12
"""
13
Parse MJCF model from XML string.
14
15
Parameters:
16
- xml_string: Complete MJCF XML content as string
17
18
Returns:
19
RootElement representing the parsed model
20
21
Example:
22
>>> xml = '<mujoco><worldbody><body name="box"/></worldbody></mujoco>'
23
>>> model = mjcf.from_xml_string(xml)
24
"""
25
26
def from_path(path: str) -> 'RootElement':
27
"""
28
Parse MJCF model from file path.
29
30
Parameters:
31
- path: Path to MJCF XML file
32
33
Returns:
34
RootElement representing the loaded model
35
36
Example:
37
>>> model = mjcf.from_path('/path/to/model.xml')
38
"""
39
40
def from_file(file_object) -> 'RootElement':
41
"""
42
Parse MJCF model from file object.
43
44
Parameters:
45
- file_object: Open file object containing MJCF XML
46
47
Returns:
48
RootElement representing the parsed model
49
"""
50
```
51
52
### Model Elements
53
54
Core classes for representing MJCF model structure and hierarchy.
55
56
```python { .api }
57
class RootElement:
58
"""
59
Root element of an MJCF model representing the complete model hierarchy.
60
61
Provides access to all model components and enables model-level operations
62
like compilation, attachment, and asset management.
63
"""
64
65
@property
66
def worldbody(self) -> 'Element':
67
"""Access to the worldbody element."""
68
69
@property
70
def asset(self) -> 'Element':
71
"""Access to the asset element."""
72
73
@property
74
def actuator(self) -> 'Element':
75
"""Access to actuator definitions."""
76
77
@property
78
def sensor(self) -> 'Element':
79
"""Access to sensor definitions."""
80
81
def find_all(self, tag: str) -> list:
82
"""
83
Find all elements with specified tag.
84
85
Parameters:
86
- tag: Element tag name to search for
87
88
Returns:
89
List of matching Element instances
90
"""
91
92
def compile_model(self) -> 'Physics':
93
"""
94
Compile MJCF model to Physics instance.
95
96
Returns:
97
Physics instance ready for simulation
98
"""
99
100
class Element:
101
"""
102
Base class for MJCF elements supporting attribute access and hierarchy.
103
104
Represents individual MJCF elements with dynamic attribute access,
105
parent-child relationships, and element-specific operations.
106
"""
107
108
@property
109
def tag(self) -> str:
110
"""Element tag name."""
111
112
@property
113
def parent(self) -> 'Element':
114
"""Parent element in hierarchy."""
115
116
def add(self, tag: str, **attributes) -> 'Element':
117
"""
118
Add child element with specified tag and attributes.
119
120
Parameters:
121
- tag: Child element tag name
122
- **attributes: Element attributes as keyword arguments
123
124
Returns:
125
Newly created child Element
126
127
Example:
128
>>> body = worldbody.add('body', name='box', pos=[0, 0, 1])
129
>>> geom = body.add('geom', type='box', size=[0.1, 0.1, 0.1])
130
"""
131
132
def remove(self) -> None:
133
"""Remove this element from parent."""
134
135
def find(self, tag: str, name: str = None) -> 'Element':
136
"""
137
Find first child element matching criteria.
138
139
Parameters:
140
- tag: Element tag to search for
141
- name: Optional name attribute to match
142
143
Returns:
144
First matching Element or None
145
"""
146
147
def find_all(self, tag: str) -> list:
148
"""
149
Find all child elements with specified tag.
150
151
Parameters:
152
- tag: Element tag to search for
153
154
Returns:
155
List of matching child Elements
156
"""
157
```
158
159
### Asset Management
160
161
Handle external assets like meshes, textures, and materials.
162
163
```python { .api }
164
class Asset:
165
"""
166
Represents MJCF asset with file path and metadata.
167
168
Handles asset file paths, automatic copying, and asset referencing
169
within MJCF models.
170
"""
171
172
@property
173
def path(self) -> str:
174
"""Asset file path."""
175
176
@property
177
def prefix(self) -> str:
178
"""Asset name prefix for namespacing."""
179
180
def export_with_assets(model: 'RootElement', out_dir: str,
181
model_filename: str = 'model.xml') -> None:
182
"""
183
Export model with all referenced assets to directory.
184
185
Parameters:
186
- model: RootElement to export
187
- out_dir: Output directory path
188
- model_filename: Name for main model file (default: 'model.xml')
189
190
Creates directory containing model XML and all asset files with
191
correct relative paths preserved.
192
"""
193
194
def export_with_assets_as_zip(model: 'RootElement', zip_path: str,
195
model_filename: str = 'model.xml') -> None:
196
"""
197
Export model with assets as ZIP archive.
198
199
Parameters:
200
- model: RootElement to export
201
- zip_path: Output ZIP file path
202
- model_filename: Name for main model file (default: 'model.xml')
203
204
Creates ZIP archive containing model and all assets.
205
"""
206
```
207
208
### Model Composition and Attachment
209
210
Tools for combining and modifying MJCF models.
211
212
```python { .api }
213
def get_attachment_frame(element: 'Element') -> 'Element':
214
"""
215
Get attachment frame for element composition.
216
217
Parameters:
218
- element: Element to get attachment frame for
219
220
Returns:
221
Element representing the attachment frame
222
"""
223
224
def get_frame_freejoint(frame: 'Element') -> 'Element':
225
"""
226
Get free joint associated with frame.
227
228
Parameters:
229
- frame: Frame element
230
231
Returns:
232
Associated free joint Element or None
233
"""
234
235
def get_frame_joints(frame: 'Element') -> list:
236
"""
237
Get all joints associated with frame.
238
239
Parameters:
240
- frame: Frame element
241
242
Returns:
243
List of joint Elements
244
"""
245
246
def get_freejoint(body: 'Element') -> 'Element':
247
"""
248
Get free joint for body element.
249
250
Parameters:
251
- body: Body element
252
253
Returns:
254
Associated free joint Element or None
255
"""
256
257
def commit_defaults(root: 'RootElement') -> None:
258
"""
259
Commit default values throughout model hierarchy.
260
261
Parameters:
262
- root: Root element to process
263
264
Applies default values to all elements recursively.
265
"""
266
```
267
268
### Constants and Utilities
269
270
```python { .api }
271
PREFIX_SEPARATOR: str
272
"""Separator used in element name prefixing for model composition."""
273
274
class Physics:
275
"""MJCF-specific physics instance with enhanced model access."""
276
277
def bind(self, element: 'Element') -> object:
278
"""
279
Bind MJCF element to physics data.
280
281
Parameters:
282
- element: MJCF element to bind
283
284
Returns:
285
Bound physics data object
286
"""
287
```
288
289
## Usage Examples
290
291
### Creating Models Programmatically
292
293
```python
294
from dm_control import mjcf
295
296
# Create new model
297
model = mjcf.RootElement()
298
299
# Add basic structure
300
worldbody = model.worldbody
301
302
# Create a simple pendulum
303
pendulum_body = worldbody.add('body', name='pendulum', pos=[0, 0, 1])
304
pendulum_body.add('joint', name='hinge', type='hinge', axis=[0, 1, 0])
305
pendulum_body.add('geom', name='bob', type='sphere', size=[0.05],
306
rgba=[1, 0, 0, 1])
307
308
# Add actuator
309
model.actuator.add('motor', name='motor', joint='hinge', gear=[1])
310
311
# Compile to physics
312
physics = model.compile_model()
313
```
314
315
### Loading and Modifying Models
316
317
```python
318
# Load existing model
319
model = mjcf.from_path('/path/to/robot.xml')
320
321
# Find and modify elements
322
arm = model.find('body', name='arm')
323
if arm:
324
# Add new sensor
325
arm.add('site', name='wrist_site', pos=[0, 0, 0.1])
326
model.sensor.add('touch', name='wrist_sensor', site='wrist_site')
327
328
# Add new actuator
329
model.actuator.add('position', name='new_actuator',
330
joint='elbow_joint', kp=100)
331
```
332
333
### Model Composition
334
335
```python
336
# Load base robot
337
robot = mjcf.from_path('/path/to/base_robot.xml')
338
339
# Load tool model
340
tool = mjcf.from_path('/path/to/tool.xml')
341
342
# Get attachment point
343
attachment_frame = robot.find('site', name='end_effector')
344
345
# Attach tool to robot (conceptual - actual attachment may require
346
# more complex operations depending on model structure)
347
if attachment_frame:
348
# This would typically involve more complex attachment logic
349
pass
350
```
351
352
### Asset Handling
353
354
```python
355
# Create model with assets
356
model = mjcf.RootElement()
357
358
# Add mesh asset
359
model.asset.add('mesh', name='custom_mesh', file='path/to/mesh.stl')
360
361
# Use asset in geometry
362
body = model.worldbody.add('body', name='custom_body')
363
body.add('geom', name='mesh_geom', type='mesh', mesh='custom_mesh')
364
365
# Export with assets
366
mjcf.export_with_assets(model, '/output/directory/')
367
368
# Or export as ZIP
369
mjcf.export_with_assets_as_zip(model, '/output/model.zip')
370
```
371
372
### Advanced Element Operations
373
374
```python
375
# Create complex hierarchy
376
model = mjcf.from_path('/path/to/base.xml')
377
378
# Find all bodies
379
bodies = model.find_all('body')
380
for body in bodies:
381
print(f"Body: {body.name}, Mass: {body.get('mass', 'default')}")
382
383
# Add sensors to all bodies
384
for i, body in enumerate(bodies):
385
if body.name: # Only named bodies
386
site_name = f"{body.name}_site"
387
sensor_name = f"{body.name}_sensor"
388
389
body.add('site', name=site_name, pos=[0, 0, 0])
390
model.sensor.add('accelerometer', name=sensor_name, site=site_name)
391
392
# Commit defaults to finalize
393
mjcf.commit_defaults(model)
394
```
395
396
### Physics Integration
397
398
```python
399
# Create model and compile
400
model = mjcf.from_path('/path/to/model.xml')
401
physics = model.compile_model()
402
403
# Bind MJCF elements to physics data
404
hinge_joint = model.find('joint', name='hinge')
405
if hinge_joint:
406
joint_data = physics.bind(hinge_joint)
407
# Use bound data for direct physics access
408
```