README (README.md)
dm_control: Google DeepMind Infrastructure for Physics-Based Simulation.
Google DeepMind's software stack for physics-based simulation and Reinforcement
Learning environments, using MuJoCo physics.
An introductory tutorial for this package is available as a Colaboratory
notebook:
[](https://colab.research.google.com/github/google-deepmind/dm_control/blob/main/tutorial.ipynb)
Overview
This package consists of the following "core" components:
- [dm_control.mujoco]: Libraries that provide Python bindings to the MuJoCo
physics engine.
- [dm_control.suite]: A set of Python Reinforcement Learning environments
powered by the MuJoCo physics engine.
- [dm_control.viewer]: An interactive environment viewer.
Additionally, the following components are available for the creation of more
complex control tasks:
- [dm_control.mjcf]: A library for composing and modifying MuJoCo MJCF
models in Python.
- dm_control.composer: A library for defining rich RL environments from
reusable, self-contained components.
- [dm_control.locomotion]: Additional libraries for custom tasks.
- [dm_control.locomotion.soccer]: Multi-agent soccer tasks.
If you use this package, please cite our accompanying [publication]:
@article{tunyasuvunakool2020,
title = {dm_control: Software and tasks for continuous control},
journal = {Software Impacts},
volume = {6},
pages = {100022},
year = {2020},
issn = {2665-9638},
doi = {https://doi.org/10.1016/j.simpa.2020.100022},
url = {https://www.sciencedirect.com/science/article/pii/S2665963820300099},
author = {Saran Tunyasuvunakool and Alistair Muldal and Yotam Doron and
Siqi Liu and Steven Bohez and Josh Merel and Tom Erez and
Timothy Lillicrap and Nicolas Heess and Yuval Tassa},
}Installation
Install dm_control from PyPI by running
pip install dm_controlNote:dm_controlcannot be installed in "editable" mode (i.e.pip
install -e).
> While dm_control has been largely updated to use the pybind11-based bindingsprovided via the mujoco package, at this time it still relies on some legacycomponents that are automatically generated from MuJoCo header files in a way
that is incompatible with editable mode. Attempting to install dm_control ineditable mode will result in import errors like:
> ``ImportError: cannot import name 'constants' from partially initialized module 'dm_control.mujoco.wrapper.mjbindings' ...
`> The solution is to pip uninstall dm_control and then reinstall it withoutthe -e flag.Versioning
Starting from version 1.0.0, we adopt semantic versioning.
Prior to version 1.0.0, the dm_control Python package was versioned 0.0.N,N
where was an internal revision number that increased by an arbitrary amount
at every single Git commit.
If you want to install an unreleased version of dm_control directly from ourpip install
repository, you can do so by running
git+https://github.com/google-deepmind/dm_control.git.
Rendering
The MuJoCo Python bindings support three different OpenGL rendering backends:
EGL (headless, hardware-accelerated), GLFW (windowed, hardware-accelerated), and
OSMesa (purely software-based). At least one of these three backends must be
available in order render through dm_control.
* Hardware rendering with a windowing system is supported via GLFW and GLEW.
On Linux these can be installed using your distribution's package manager.
For example, on Debian and Ubuntu, this can be done by running sudo apt-get
install libglfw3 libglew2.0. Please note that:
- [dm_control.viewer] can only be used with GLFW.
- GLFW will not work on headless machines.
* "Headless" hardware rendering (i.e. without a windowing system such as X11)
requires [EXT_platform_device] support in the EGL driver. Recent Nvidia
drivers support this. You will also need GLEW. On Debian and Ubuntu, this
can be installed via sudo apt-get install libglew2.0.
* Software rendering requires GLX and OSMesa. On Debian and Ubuntu these can
be installed using sudo apt-get install libgl1-mesa-glx libosmesa6.
By default, dm_control will attempt to use GLFW first, then EGL, then OSMesa.MUJOCO_GL=
You can also specify a particular backend to use by setting the "glfw"
environment variable to , "egl", or "osmesa", respectively. WhenMUJOCO_EGL_DEVICE_ID=
rendering with EGL, you can also specify which GPU to use for rendering by
setting the environment variable to the target GPU ID.
Additional instructions for Homebrew users on macOS
1. The above instructions using pip should work, provided that you use a
Python interpreter that is installed by Homebrew (rather than the
system-default one).
2. Before running, the DYLD_LIBRARY_PATH environment variable needs to beexport DYLD_LIBRARY_PATH=$(brew --prefix)/lib:$DYLD_LIBRARY_PATH
updated with the path to the GLFW library. This can be done by running
.
[EXT_platform_device]: https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_device.txt
[Releases page on the MuJoCo GitHub repository]: https://github.com/google-deepmind/mujoco/releases
[MuJoCo website]: https://mujoco.org/
[publication]: https://doi.org/10.1016/j.simpa.2020.100022
[ctypes]: https://docs.python.org/3/library/ctypes.htmldm_control.mjcf
[]: dm_control/mjcf/README.mddm_control.mujoco
[]: dm_control/mujoco/README.mddm_control.suite
[]: dm_control/suite/README.mddm_control.viewer
[]: dm_control/viewer/README.mddm_control.locomotion
[]: dm_control/locomotion/README.mddm_control.locomotion.soccer
[]: dm_control/locomotion/soccer/README.md
---
CONTRIBUTING (CONTRIBUTING.md)
How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution,
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
GitHub Help for more
information on using pull requests.
---
Migration Guide 1.0 (migration_guide_1.0.md)
dm_control: 1.0.0 update guide
With 1.0.0, we changed the way dm_control uses the MuJoCo physics simulator, and
migrated to new python bindings. For most users, this will require no code
changes. However, some more advanced users will need to change their code
slightly. Below is a list of known changes that need to be made. Please contact
us if you've had to make any further changes that are not listed below.
Required changes
dm_control.mujoco.wrapper.mjbindings.types should not be used This module wastypes.MJRRECT
specific to the previous implementation of the dm_control python bindings. For
example, should be replaced with mujoco.MjrRect.
MjData.contact has changed
MjData.contact (often accessed as Physics.data.contact) used to offer andata.contact.geom1
interface similar to a numpy structured array. For example,
used to be a numpy array of geom IDs.
With the recent update, MjData.contact will appear as a list of MjContact
structs. Code that used to operate on the structured array will have to change.
For example, the following code would get an array containing the contact
distance for all contacts that involve geom_id:
contact = physics.data.contact
involves_geom = (contact.geom1 == geom_id) | (contact.geom2 == geom_id)
dists = contact[involves_geom].distAfter the upgrade:
contacts = physics.data.contact
dists = [
c.dist for c in contacts if c.geom1 == geom_id or c.geom2 == geom_id
].ptr.contents will not work
Code that accesses .ptr.contents on objects such as MjvScene will need to bescene
updated. In most cases, simply using instead of scene.ptr.contents
will work.
Different exceptions will be thrown from MuJoCo
Code (mostly in tests) that expects dm_control.mujoco.wrapper.core.Errormujoco
exceptions, will receive different exceptions, thrown by the library.ValueError
These will often be (for errors caused by input parameters), ormujoco.FatalError (for low level errors in MuJoCo).
Better error handling
The Python interpreter no longer crashes out when
mju_error
is called. Instead, mju_error calls are translated into mujoco.FatalError
exceptions in Python.
When Python callables are used as user-defined MuJoCo callbacks, they are now
permitted to raise exceptions, which will be correctly propagated back down the
Python call stack.
Change of signature for mj_saveModel
mj_saveModel
now expects a numpy uint8 array rather than a ctypes string buffer, and
doesn't require a "size" parameter (it's inferred from the numpy array size).
Before:
model_size = mjlib.mj_sizeModel(model.ptr)
buf = ctypes.create_string_buffer(model_size)
mjlib.mj_saveModel(model.ptr, None, buf, model_size)After:
model_size = mujoco.mj_sizeModel(model)
buf = np.empty(model_size, np.uint8)
mjlib.mj_saveModel(model.ptr, None, buf)Optional changes
The following are some changes that can make your code more concise, but are not
required for it to continue working.
Use the mujoco module directly, instead of mjlib
Existing code that uses dm_control.mujoco.wrapper.mjbindings.mjlib canenums
directly replace these modules with mujoco. Code that uses orconstants from dm_control.mujoco.wrapper.mjbindings can also use mujoco,
with slight type changes. All mujoco functions will accept the old enum values
or the new ones.
Before:
import dm_control.mujoco.wrapper.mjbindings
mjlib = mjbindings.mjlibmjlib.mj_objectVelocity(
physics.model.ptr, physics.data.ptr,
enums.mjtObj.mjOBJ_SITE,
site_id, vel, 0)
After:
import mujocomujoco.mj_objectVelocity(
physics.model.ptr, physics.data.ptr,
mujoco.mjtObj.mjOBJ_SITE,
site_id, vel, 0)
Assume structs are correctly initialized and memory is managed
The MuJoCo C API includes functions that manage the memory for certain structs.
Those include functions that allocate memory (e.g. mj_makeModel,mj_makeData, mjv_makeScene), functions that free memory (e.g.mj_deleteModel, mj_deleteData, mjv_freeScene), and functions that reset amjv_defaultOption
struct to its default value (e.g. , mj_defaultVisual).
The new Python bindings take care of this. Wrapper classes like
mujoco.MjvScene will automatically allocate memory when they're created, and
release it when they're deleted, and be created with default values set.
As such, allocating and freeing functions are not available through the mujoco
Python bindings. The "default" functions are still available, but in most cases
the calls can simply be removed.
Before:
from dm_control.mujoco import wrapper
from dm_control.mujoco.wrapper import mjbindings
mjlib = mjbindings.mjlibscene_option = wrapper.core.MjvOption()
mjlib.mjv_defaultOption(scene_option.ptr)
After:
from dm_control.mujoco import wrapperscene_option = wrapper.core.MjvOption()
---
Requirements (requirements.txt)
absl-py==2.3.1
dm-env==1.6
dm-tree==0.1.9
glfw==2.9.0
h5py==3.14.0
labmaze==1.0.6
lxml==6.0.1
mock==5.2.0
mujoco==3.11.0
numpy==2.3.2; python_version >= '3.11'
numpy==2.2.6; python_version == '3.10'
numpy==2.0.2; python_version == '3.9'
pillow==11.3.0
protobuf==3.19.4
pyopengl==3.1.10
pyparsing==3.2.3
pytest==8.4.1
pytest-xdist==3.8.0
pytest-timeout==2.4.0
requests==2.32.5
scipy==1.16.1; python_version >= '3.11'
scipy==1.15.3; python_version == '3.10'
scipy==1.13.1; python_version == '3.9'
setuptools==80.9.0
tqdm==4.67.1
---
Dm Control/Blender/Mujoco Exporter/README (dm_control/blender/mujoco_exporter/README.md)
Export Mujoco models from Blender
Prerequisites
The MuJoCo exporter works as Blender addon, tested with ver 3.4.1. You can
download Blender from https://www.blender.org.
Installation
Preparing a plugin installer
The install.sh command will deploy the installable version of the plugin to./addons/mujoco_model_exporter. This folder should be zipped:
$ cd ./addons && zip -r mujoco_model_exporter.zip mujoco_model_exporter/*Installing the plugin in Blender
Open Blender and select Edit/Preferences menu option.
{style="display:block;margin:auto"}
Clicking the Install button will open a file selection dialog that will allow
you to select the .zip archive with the plugin.
The next window will follow, listing the installed plugin and allowing you to
enable it.
{style="display:block;margin:auto"}
Check the checkbox next to its name to enable it.
Modelling for Mujoco.
The plugin was designed to allow the artists to keep using core Blender features
when building models for Mujoco.
feature | Mujoco | Blender
-------------- | ------------------------- | -----------------------------------
kinematic tree | hierarchy of Bodies | Armatures or hierarchies of Objects
geometry | Geoms | Meshes
materials | Material + Texture assets | Material + Texture definitions
lighting | Lights | Lights
Node naming
The exporter will copy the names of the bones, meshes and lights. Mujoco joints
will be named after the bone that owns the respective constraint, with a postfix
denoting the degree of freedom they enable.
Modelling kinematic trees using Armatures and IK
Blender Armatures allow to model kinematic capabilities of a Blender model. They
comprise a tree of bones, each of which can be further extended with IK
Constraints.
Only the bones affected by an IK chain will receive appropriate degrees of
freedom. This means that if you export a model without any IK chains, it will be
exported as a static model.
#### IK Constraints and bone limits
IK constraints offer the easiest way to verify how a kinematic chain would
behave in a physical environment. For that reason we chose to use them, along
with IK bone limits, to model Mujoco joints.
* Select the bone at the end of a kinematic chain,
* Add an IK Bone Constraint to it.
This webpage
contains detailed instructions.
* Add a target bone to the scene and use it as the IK constraint's target.
* Adjust the Chain Length value to reflect the number of parent bones that
should become a part of this IK chain.
* For each of the bones in the chain, visit the Bone/Inverse Kinematics tab
and adjust the locks and bone limits.
NOTE: you can take advantage of a helpful limit visualization gizmo.
Armature free joints.
Armatures form the kinematic trees, so it makes sense to give their roots all 6
degrees of freedom.
This behavior can be changed by disabling the Armature freejoint export
option.
Modelling the geometry
Mujoco uses parametric geometry comprised of primitive shapes such
as cubes, spheres and capsules. It does support triangle based mesh geometry
however.
Blender on the other hand deals exclusively in meshes, and even though it
contains a palette of such primitive shapes, these are not parametric.
#### MSH files
The exporter therefore exports the geometry as meshes. All meshes referenced by
the scene are exported into Mujoco's native .msh format.
When this plugin was created, the native .msh format was the only format to
support texture mapping. Since MuJoCo 2.1.2, .obj files are supported. The.msh format is expected to be deprecated and removed soon. Until this plugin.obj
is updated to output files we recommend all user to convert their .msh.obj
files to files using this utility.
#### Phong lighting model and lack of support for Cycles nodes
Mujoco implements a forward rendering model with Phong lightning and support for
reflective surfaces.
Mujoco material definition
is fixed and limited to:
* base color (diffuse color)
* specularity coefficient
* smoothness coefficient
* reflectiveness coefficient
These parameters translate well to Blender's default material definition, which
should be used to model the materials exported to Mujoco.
Due to the diversity and open-ended nature of Cycle's material nodes, the choice
was made not to support materials defined using them.
#### Division of meshes that employ multiple materials
Mujoco renderer supports a single material per mesh, in contrast to Blender's
multi material model. In order to support meshes with multiple materials, the
exporter divides them into submeshes, each of which uses a single material.
Since Blender materials are applied to faces, and a single face may only have a
single material assigned, there is no risk that the subdivided mesh will exhibit
any overlapping artifacts.
#### Effect of mesh division on inertia and mass.
The exported submeshes will have different geometry and volume than the
original. This will affect the mass and inertia of the geom that uses the
exported mesh.
Please note that Mujoco derives these quantities not from the mesh itself, but
rather from the convex hull calculated for it. The sum of hulls of subdivided
meshes are not guaranteed to be equal to the hull of the original mesh.
This phenomenon is illustrated on the image below.
Blender | Mujoco renderer | Mujoco convex hulls
----------------------------- | ------------------------------- | -------------------
| |
A different material was assigned to 2 out of 6 rectangular faces of the right
cube, causing that mesh to be split into two submeshes.
While they render well in Mujoco, the Moiré effect visible on the convex hull
comes from two overlapping hulls, each with a different volume.
#### Double-sided materials
Double sided materials cause the meshes (or faces) that use them to be exported
with the faces duplicated with the reverse face winding order. This is because
Mujoco renderer does not support any other face culling modes than back face
culling.
CAUTION: Because this operation affects the exported geometry, it may
indirectly affect the physical properties of a geom that references that mesh,
such as its mass and inertia. Please use this feature with caution.
#### Scaling
CAUTION: If you are using the scaling transform, the exporter will modify your
scene!
The exporter will by default reset the scaling transform on all bones and meshes
to ensure affine reference frame transformations.
This operation can be undo'ed after the exporter is completed, but the exporter
doesn't undo it automatically!
Textures
NOTE: This feature will be added in the next version of the exporter.
#### One texture channel
Mujoco renderer supports a single texture channel. It's therefore advised to use
a single UV map for meshes that are to be exported to Mujoco.
#### Choice between atlases and individual textures
Mujoco's fixed rendering pipeline is quite fast, and handles both variants well.
Therefore we leave it up to the artist to decide which it prefers using.
Please keep in mind that the exporter doesn't export the texture assets. Those
should be copied into the folder with the exported model manually.
---
Dm Control/Locomotion/README (dm_control/locomotion/README.md)
Locomotion task library
This package contains reusable components for defining control tasks that are
related to locomotion. New users are encouraged to start by browsing the
examples/ subdirectory, which contains preconfigured RL environments
associated with various research papers. These examples can serve as starting
points or be customized to design new environments using the components
available from this library.
<p float="left">
<img src="walls.png" height="200">
<img src="gaps.png" height="200">
</p>
Terminology
This library facilitates the creation of environments that require walkers
to perform a task in an arena.
- walkers refer to detached bodies that can move around in the
environment.
- arenas refer to the surroundings in which the walkers and possibly other
objects exist.
- tasks refer to the specification of observations and rewards that are
passed from the "environment" to the "agent", along with runtime details
such as initialization and termination logic.
Installation and requirements
See [the documentation for dm_control][installation-and-requirements].
Quickstart
from dm_control import composeraction_spec
from dm_control.locomotion.examples import basic_cmu_2019
import numpy as npBuild an example environment.
env = basic_cmu_2019.cmu_humanoid_run_walls()Get the
describing the control inputs.
action_spec = env.action_spec()Step through the environment for one episode with random actions.
time_step = env.reset()
while not time_step.last():
action = np.random.uniform(action_spec.minimum, action_spec.maximum,
size=action_spec.shape)
time_step = env.step(action)
print("reward = {}, discount = {}, observations = {}.".format(
time_step.reward, time_step.discount, time_step.observation))
[dm_control.viewer] can also be used to visualize and interact with the
environment, e.g.:
from dm_control import viewerviewer.launch(environment_loader=basic_cmu_2019.cmu_humanoid_run_walls)
Publications
This library contains environments that were adapted from several research
papers. Relevant references include:
- [Emergence of Locomotion Behaviours in Rich Environments (2017)][heess2017].
- [Learning human behaviors from motion capture by adversarial imitation
(2017)][merel2017].
- [Hierarchical visuomotor control of humanoids (2019)][merel2019a].
- [Neural probabilistic motor primitives for humanoid control (2019)][merel2019b].
- [Deep neuroethology of a virtual rodent (2020)][merel2020].
- [CoMic: Complementary Task Learning & Mimicry for Reusable Skills (2020)][hasenclever2020]
[installation-and-requirements]: ../../README.md#installation-and-requirements
[dm_control.viewer]: ../viewer/README.md
[heess2017]: https://arxiv.org/abs/1707.02286
[merel2017]: https://arxiv.org/abs/1707.02201
[merel2019a]: https://arxiv.org/abs/1811.09656
[merel2019b]: https://arxiv.org/abs/1811.11711
[merel2020]: https://openreview.net/pdf?id=SyxrxR4KPS
[hasenclever2020]: http://proceedings.mlr.press/v119/hasenclever20a.html
---
Dm Control/Locomotion/Soccer/README (dm_control/locomotion/soccer/README.md)
DeepMind MuJoCo Multi-Agent Soccer Environment.
This submodule contains the components and environment used in the following
works.
* [Emergent Coordination through Competition][boxhead]
(dynamic team play,
defensive team play).
* [From Motor Control to Team Play in Simulated Humanoid Football][humanoid].
Quickstart
import numpy as np
from dm_control.locomotion import soccer as dm_soccerInstantiates a 2-vs-2 BOXHEAD soccer environment with episodes of 10 seconds
each. Upon scoring, the environment reset player positions and the episode
continues. In this example, players can physically block each other and the
ball is trapped within an invisible box encapsulating the field.
env = dm_soccer.load(team_size=2,
time_limit=10.0,
disable_walker_contacts=False,
enable_field_box=True,
terminate_on_goal=False,
walker_type=dm_soccer.WalkerType.BOXHEAD)Retrieves action_specs for all 4 players.
action_specs = env.action_spec()Step through the environment for one episode with random actions.
timestep = env.reset()
while not timestep.last():
actions = []
for action_spec in action_specs:
action = np.random.uniform(
action_spec.minimum, action_spec.maximum, size=action_spec.shape)
actions.append(action)
timestep = env.step(actions) for i in range(len(action_specs)):
print(
"Player {}: reward = {}, discount = {}, observations = {}.".format(
i, timestep.reward[i], timestep.discount, timestep.observation[i]))
Rewards
The environment provides a reward of +1 to each player when their team scores a
goal, -1 when their team concedes a goal, or 0 if neither team scored on the
current timestep.
In addition to the sparse reward returned the environment, the player
observations also contain various environment statistics that may be used to
derive custom per-player shaping rewards. See environment.observation_spec()
for the additional statistics available to the agents.
Episode terminations
If terminate_on_goal is set to True, episodes will terminate immediatelytime_limit
with a discount factor of 0 when either side scores a goal or if the elapsed. If neither team scores within this time then the episode1.0
will terminate with a discount factor of .
If terminate_on_goal is set to False, players and ball positions aretime_limit
randomly initialized if either team scores a goal. Episodes always terminate
after with a discount factor of 1.0.
Environment Viewer
To visualize an example environment instance using the dm_control interactivedm_control/locomotion/soccer/explore.py
viewer, execute .
[boxhead]: http://arxiv.org/abs/1902.07151
[humanoid]: https://arxiv.org/abs/2105.12196
---
Dm Control/Locomotion/Tasks/Reference Pose/README (dm_control/locomotion/tasks/reference_pose/README.md)
Reference pose tasks
This directory contains components to define tasks based on reference poses (e.g
motion capture data) as well as a motion capture tracking tasks. The tasks and
associated utils were developed as part of
[CoMic: Complementary Task Learning & Mimicry for Reusable Skills (2020)][hasenclever2020].
The reference data is stored in HDF5 files, which can be loaded using the
HDF5TrajectoryLoader class in dm_control/locomotion/mocap/loader.py. Todm_control/locomotion/mocap/cmu_mocap_data.py
download the data used in the CoMic project, please use. In the reference pose tasks,Trajectory
reference trajectories are represented as objects (seedm_control/locomotion/mocap/trajectory.py). For an example of how to constructdm_control/locomotion/examples/cmu_2020_tracking.py
a task, see .
[hasenclever2020]: https://proceedings.icml.cc/static/paper_files/icml/2020/5013-Paper.pdf
---
Dm Control/Mjcf/README (dm_control/mjcf/README.md)
PyMJCF
IMPORTANT: If you find yourself stuck while using PyMJCF, check out the various
IMPORTANT boxes on this page and the Common gotchas section
at the bottom to see if any of them is relevant.
This library provides a Python object model for MuJoCo's XML-based
MJCF physics modeling language. The
goal of the library is to allow users to easily interact with and modify MJCF
models in Python, similarly to what the JavaScript DOM does for HTML.
A key feature of this library is the ability to easily compose multiple separate
MJCF models into a larger one. Disambiguation of duplicated names from different
models, or multiple instances of the same model, is handled automatically.
The following snippet provides a quick example of this library's typical use
case. Here, the UpperBody class can simply instantiate two copies of Arm,Arm
thus reducing code duplication. The names of bodies, joints, or geoms of each are automatically prefixed by their parent's names, and so no name
collision occurs.
from dm_control import mjcfclass Arm:
def __init__(self, name):
self.mjcf_model = mjcf.RootElement(model=name)
self.upper_arm = self.mjcf_model.worldbody.add('body', name='upper_arm')
self.shoulder = self.upper_arm.add('joint', name='shoulder', type='ball')
self.upper_arm.add('geom', name='upper_arm', type='capsule',
pos=[0, 0, -0.15], size=[0.045, 0.15])
self.forearm = self.upper_arm.add('body', name='forearm', pos=[0, 0, -0.3])
self.elbow = self.forearm.add('joint', name='elbow',
type='hinge', axis=[0, 1, 0])
self.forearm.add('geom', name='forearm', type='capsule',
pos=[0, 0, -0.15], size=[0.045, 0.15])
class UpperBody:
def __init__(self):
self.mjcf_model = mjcf.RootElement()
self.mjcf_model.worldbody.add(
'geom', name='torso', type='box', size=[0.15, 0.045, 0.25])
left_shoulder_site = self.mjcf_model.worldbody.add(
'site', size=[1e-6]*3, pos=[-0.15, 0, 0.25])
right_shoulder_site = self.mjcf_model.worldbody.add(
'site', size=[1e-6]*3, pos=[0.15, 0, 0.25])
self.left_arm = Arm(name='left_arm')
left_shoulder_site.attach(self.left_arm.mjcf_model)
self.right_arm = Arm(name='right_arm')
right_shoulder_site.attach(self.right_arm.mjcf_model)
body = UpperBody()
physics = mjcf.Physics.from_mjcf_model(body.mjcf_model)
Basic operations
Creating an MJCF model
In PyMJCF, the basic building block of a model is an mjcf.Element. Thismjcf.Element
corresponds to an element in the generated XML. However, user code _cannot_
instantiate a generic object directly.
A valid model always consists of a single root <mujoco> element. This ismjcf.RootElement
represented as the special type in PyMJCF, which _can_ be
instantiated in user code to create an empty model.
from dm_control import mjcfmjcf_model = mjcf.RootElement()
print(mjcf_model) # MJCF Element: <mujoco/>
Adding new elements
Attributes of the new element can be passed as kwargs:
my_box = mjcf_model.worldbody.add('geom', name='my_box',
type='box', pos=[0, .1, 0])
print(my_box) # MJCF Element: <geom name="my_box" type="box" pos="0. 0.1 0."/>Parsing an existing XML document
Alternatively, if an existing XML file already exists, PyMJCF can parse it to
create a Python object:
from dm_control import mjcfParse from path
mjcf_model = mjcf.from_path(filename)Parse from file
with open(filename) as f:
mjcf_model = mjcf.from_file(f)Parse from string
with open(filename) as f:
xml_string = f.read()
mjcf_model = mjcf.from_xml_string(xml_string)print(type(mjcf_model)) # <type 'mjcf.RootElement'>
Traversing through a model
Consider the following MJCF model:
<mujoco model="test">
<default>
<default class="brick">
<geom rgba="1 0 0 1"/>
</default>
</default>
<worldbody>
<body name="foo">
<freejoint/>
<inertial pos="0 0 0" mass="1"/>
<body name="bar">
<joint name="my_hinge" type="hinge"/>
<geom name="my_geom" pos="0 1 2" class="brick"/>
</body>
</body>
</worldbody>
</mujoco>The child elements and XML attributes of an Element object are exposed asclass
Python attributes. These attributes all have the same names as their XML
counterparts, with one exception: the XML attribute is named dclass inclass
order to avoid a clash with the Python keyword:
my_geom = mjcf_model.worldbody.body['foo'].body['bar'].geom['my_geom']
print(isinstance(mjcf_model, mjcf.Element)) # True
print(my_geom.name) # 'my_geom'
print(my_geom.pos) # np.array([0., 1., 2.], dtype=float)
print(my_geom.class) # SyntaxError
print(my_geom.dclass) # 'brick'Note that attribute values in the object model are not affected by defaults:
print(mjcf_model.default.default['brick'].geom.rgba) # [1, 0, 0, 1]
print(my_geom.rgba) # NoneFinding elements without traversing
We can also find elements directly without having to traverse through the object
hierarchy:
found_geom = mjcf_model.find('geom', 'my_geom')
print(found_geom == my_geom) # TrueFind all elements of a given type:
Note that <freejoint> is also considered a joint
joints = mjcf_model.find_all('joint')
print(len(joints)) # 2
print(joints[0] == mjcf_model.worldbody.body['foo'].freejoint) # True
print(joints[1] == mjcf_model.worldbody.body['foo'].body['bar'].joint[0]) # TrueNote that the order of elements returned by find_all is the same as the order
in which they are declared in the model.
Modifying XML attributes
Attributes can be modified, added, or removed:
my_geom.pos = [1, 2, 3]
print(my_geom.pos) # np.array([1., 2., 3.], dtype=float)
my_geom.quat = [0, 1, 0, 0]
print(my_geom.quat) # np.array([0., 1., 0., 0.], dtype=float)
del my_geom.quat
print(my_geom.quat) # NoneSchema violations result in errors:
print(my_geom.poss) # raise AttributeError (no child or attribute called poss)
my_geom.pos = 'invalid' # raise ValueError (assigning string to array)
my_geom.pos = [1, 2, 3, 4, 5, 6] # raise ValueError (array length is too long)raise ValueError (mass is a required attribute of <inertial>)
del mjcf_model.find('body', 'foo').inertial.massUniqueness of identifiers
PyMJCF enforces the uniqueness of "identifier" attributes within a model.
Identifiers consist of the class attribute of a <default>, and all name<body>
attributes. Their uniqueness is only enforced within a particular namespace. For
example, a is allowed to have the same name as a <geom>, whereas<position> and <velocity> actuators cannot have the same name.
mjcf_model.worldbody.add('geom', name='my_geom')
foo = mjcf_model.worldbody.find('body', 'foo')
foo.add('my_geom') # Error, duplicated geom name
foo.add('foo') # OK, a geom can have the same name as a body
mjcf_model.find('geom', 'foo').name = 'my_geom' # Error, duplicated geom nameReference attributes
Some attributes are references to other elements. For example, the joint<joint>
attribute of an actuator refers to a element in the model.
An mjcf.Element can be directly assigned to these reference attributes:
my_hinge = mjcf_model.find('joint', 'my_hinge')
my_actuator = mjcf_model.actuator.add('velocity', joint=my_hinge)This is the recommended way to assign reference attributes, since it guarantees
that the reference is not invalidated if the referenced element is renamed.
Alternatively, a string can also be assigned to reference attributes. In this
case, PyMJCF does not attempt to verify that the named element actually
exists in the model.
IMPORTANT: If the element being referenced is in a different model to the
reference attribute (e.g. in an attached model), the reference must be
created by directly assigning an mjcf.Element object to the attribute rather
than a string. Strings assigned to reference attributes cannot contain '/',
since they are automatically scoped by PyMJCF upon attachment.
Attaching models
In this section we will refer to an mjcf.RootElement simply as a "model".
Models can be _attached_ to other models in order to create compositional
scenes.
arena = mjcf.RootElement()
arena.worldbody.add('geom', name='ground', type='plane', size=[10, 10, 1])robot = mjcf.from_xml_file('robot.xml')
arena.attach(robot)
We refer to arena as the _parent model_, and robot as the _child model_ (or
the _attached model_).
Attachment frames
When a model is attached to a site, an empty body is created in the parent
model. This empty body is called an _attachment frame_.
The attachment frame is created as a child of the body that contains the
attachment site, and it has the same position and orientation as the site. When
the XML is generated, the attachment frame's contents shadow the contents of the
attached model's <worldbody>. The attachment frame's name in the generated XMLfully/qualified/prefix/
is the child's . The trailing slash ensures that the
attachment frame's name never collides with a user-defined body.
More concretely, if we have the following parent and child models:
<mujoco model="parent">
<worldbody>
<body>
<geom name="foo" type="box" pos="-0.2 0 0.3" size="0.5 0.3 0.1"/>
<site name="attachment_site" pos="1. 2. 3." quat="1. 0. 0. 1."/>
</body>
</worldbody>
</mujoco><mujoco model="child">
<worldbody>
<geom name="bar" type="box" pos="0.5 0.25 1." size="0.1 0.2 0.3"/>
</worldbody>
</mujoco>
Then the final generated XML will be:
<mujoco model="parent">
<worldbody>
<body>
<geom name="foo" type="box" pos="-0.2 0 0.3" size="0.5 0.3 0.1"/>
<site name="attachment_site" pos="1. 2. 3." quat="1. 0. 0. 1."/>
<body name="child/" pos="1. 2. 3." quat="1. 0. 0. 1.">
<geom name="child/my_box" type="box" pos="0.5 0.25 1." size="0.1 0.2 0.3"/>
</body>
</body>
</worldbody>
</mujoco>IMPORTANT: The attachment frame is created _transparently_ to the user. In
particular, it is NOT treated as a regular body by PyMJCF. Its name in the
generated XML should be considered implementation detail and should NOT be
relied on.
Having said that, it is sometimes necessary to access the attachment frame, for
example to add a joint between the parent and the child model. The easiest way
to do this is to hold a reference to the object returned by a call to attach:
attachment_frame = parent_model.attach('child')
attachment_frame.add('freejoint')Alternatively, if a model has already been attached, the find function can beattachment_frame
used with the namespace in order to retrieve the attachmentget_attachment_frame
frame. The convenience function in mjcf.traversal_utils
can find the child model's attachment frame without needing access to the parent
model.
frame_1 = parent_model.find('attachment_frame', 'child')Convenience function: get the attachment frame directly from a child model
frame_2 = mjcf.traversal_utils.get_attachment_frame(child_model)
print(frame_1 == frame_2) # TrueIMPORTANT: To encourage good modeling practices, the only allowed direct
children of an attachment frame are <joint> and <inertial>. Other types of<worldbody>
elements should instead add be added to the of the attached model.
Element ownership
IMPORTANT: Elements of child models do not appear when traversing through
the parent model.
Default classes
PyMJCF ensures that default classes of a parent model _never_ affect any of its
child models. This minimises the possibility that two models become subtly
"incompatible", as a model always behaves in the same way regardless of what it
is attached to.
The way that PyMJCF achieves this in practice is to move everything in a model's
global <default> context into a default class named /. In other words, a
PyMJCF-generated model never has anything in the global default context.
Instead, the generated model always looks like:
<mujoco>
<default>
<default class="/">
<geom rgba="1. 0. 0. 1."/>
</default>
</default>
</mujoco>IMPORTANT: This transformation is _transparent_ to the user. Within Python, the
above geom rgba setting is accessed as if it were a global default, i.e.
mjcf_model.default.geom.rgba. Generally speaking, users should never have to
worry about PyMJCF's internal handling of defaults.
When a model is attached, its / default class turns intofully/qualified/prefix/. The trailing slash ensures that this transformation
never conflicts with a user-named default class. More specifically, if we have
the following parent and child models:
<mujoco model="parent">
<default>
<geom rgba="1. 0. 0. 1."/>
<default class="green">
<geom rgba="0. 1. 0. 1."/>
</default>
</default>
</mujoco><mujoco model="child">
<default>
<joint range="0. 1."/>
<default class="stiff">
<joint stiffness="0.1"/>
</default>
</default>
</mujoco>
Then the final generated XML will be:
<mujoco model="parent">
<default>
<default class="/">
<geom rgba="1. 0. 0. 1."/>
<default class="green">
<geom rgba="0. 1. 0. 1."/>
</default>
</default>
<default class="child/">
<joint range="0. 1."/>
<default class="child/stiff">
<joint stiffness="0.1"/>
</default>
</default>
</default>
</mujoco>Global options
A model cannot be attached to another model if _any_ of the global options are
different. Global options consist of attributes of <compiler>, <option>,<size>, and <visual>. As with the handling of default classes, this is to
ensure that two models do not become subtly "incompatible". For example:
model_1 = mjcf.RootElement()
model_1.compiler.angle = 'radian'model_2 = mjcf.RootElement()
model_2.compiler.angle = 'degree'
model_1.attach(model_2) # Error!
An option is considered to be conflicting only if _both_ models _explicitly_
assign different values to it. An example of where conflicting options can
become problematic is:
model_1 = mjcf.RootElement()model_2 = mjcf.RootElement()
model_2.compiler.angle = 'degree'
model_1.attach(model_2) # No error, but all angles in model_1 are now wrong!
Here, model_1 assumes MuJoCo's default angle unit of radians. Since it doescompiler.angle
not explicitly assign a value to , PyMJCF does not detect aangle=degree
conflict with in model_2. All angles in model_1 are now
incorrectly interpreted as degrees.
Elements outside of <worldbody>
All children of non-worldbody elements, e.g. actuators or tendons, are
automatically merged in to appropriate places when a model is attached. Named
elements are prefixed as previously described.
Common gotchas
Use foo.dclass, not ~~foo.class~~
The class XML attribute corresponds to the dclass Python attribute inclass
PyMJCF. This is because is a reserved keyword in Python. However, it is'class'
OK to use in getattr.
<geom name="my_geom" class="red"/>print(my_geom.class) # SyntaxError
print(my_geom.dclass) # 'red'
print(getattr(my_geom, 'class')) # 'red'foo.type and foo.range are fine
The type and range attributes will trigger syntax highlighting in Cider, but
they are NOT reserved words in Python.
my_geom.type = 'capsule' # OK!
my_joint.range = [-1, 1] # OK!A model can only be attached once
A model cannot be attached twice. If you require multiple copies of the same
model in your simulation, make a deepcopy. Preferably, though, define a class
that constructs the model and just call the constructor as many times as
required.
---
Dm Control/Mujoco/README (dm_control/mujoco/README.md)
MuJoCo Python bindings
This submodule contains Python bindings for the MuJoCo physics engine. See our
tech report for further details.
Quickstart
from dm_control import mujocoLoad a model from an MJCF XML string.
xml_string = """
<mujoco>
<worldbody>
<light name="top" pos="0 0 1.5"/>
<geom name="floor" type="plane" size="1 1 .1"/>
<body name="box" pos="0 0 .3">
<joint name="up_down" type="slide" axis="0 0 1"/>
<geom name="box" type="box" size=".2 .2 .2" rgba="1 0 0 1"/>
<geom name="sphere" pos=".2 .2 .2" size=".1" rgba="0 1 0 1"/>
</body>
</worldbody>
</mujoco>
"""
physics = mujoco.Physics.from_xml_string(xml_string)Render the default camera view as a numpy array of pixels.
pixels = physics.render()Reset the simulation, move the slide joint upwards and recompute derived
quantities (e.g. the positions of the body and geoms).
with physics.reset_context():
physics.named.data.qpos['up_down'] = 0.5Print the positions of the geoms.
print(physics.named.data.geom_xpos)
FieldIndexer(geom_xpos):
x y z
0 floor [ 0 0 0 ]
1 box [ 0 0 0.8 ]
2 sphere [ 0.2 0.2 1 ]
Advance the simulation for 1 second.
while physics.time() < 1.:
physics.step()Print the new z-positions of the 'box' and 'sphere' geoms.
print(physics.named.data.geom_xpos[['box', 'sphere'], 'z'])
[ 0.19996362 0.39996362]
---
Dm Control/Mujoco/Wrapper/README (dm_control/mujoco/wrapper/README.md)
MuJoCo Wrapper
This package contains Python bindings for the [MuJoCo physics engine][1] using
[ctypes][2]. The bindings and some higher-level wrapper code are automaticallydm_control/autowrap/autowrap.py
generated from MuJoCo's header files by .
The main entry point for users of the generated bindings is
[dm_control.mujoco][3].
[1]: http://mujoco.org/
[2]: https://docs.python.org/2/library/ctypes.html
[3]: ../README.md
---
Dm Control/Suite/README (dm_control/suite/README.md)
DeepMind Control Suite.
This submodule contains the domains and tasks described in the
DeepMind Control Suite tech report.
Quickstart
from dm_control import suite
import numpy as npLoad one task:
env = suite.load(domain_name="cartpole", task_name="swingup")Iterate over a task set:
for domain_name, task_name in suite.BENCHMARKING:
env = suite.load(domain_name, task_name)Step through an episode and print out reward, discount and observation.
action_spec = env.action_spec()
time_step = env.reset()
while not time_step.last():
action = np.random.uniform(action_spec.minimum,
action_spec.maximum,
size=action_spec.shape)
time_step = env.step(action)
print(time_step.reward, time_step.discount, time_step.observation)Illustration video
Below is a video montage of solved Control Suite tasks, with reward
visualisation enabled.
[](https://www.youtube.com/watch?v=rAai4QzcYbs)
Quadruped domain [April 2019]
Roughly based on the 'ant' model introduced by Schulman et al. 2015. Main modifications to the body are:
- 4 DoFs per leg, 1 constraining tendon.
- 3 actuators per leg: 'yaw', 'lift', 'extend'.
- Filtered position actuators with timescale of 100ms.
- Sensors include an IMU, force/torque sensors, and rangefinders.
Four tasks:
- walk and run: self-right the body then move forward at a desired speed.escape
- : escape a bowl-shaped random terrain (uses rangefinders).fetch
- , go to a moving ball and bring it to a target.
All behaviors in the video below were trained with Abdolmaleki et al's
MPO.
[](https://www.youtube.com/watch?v=RhRLjbb7pBE)
---
Dm Control/Third Party/README (dm_control/third_party/README.md)
Third-party assets
This directory contains third-party assets which are licensed separately to the
the rest of dm_control. Please see the LICENSE files within the individual
subpackages for further details.
---
Dm Control/Third Party/Ant/README (dm_control/third_party/ant/README.md)
Ant MJCF model
This model is intended for use as a walker in dm_control.locomotion. It is
based on Philipp Moritz's original Ant model, which is available from
https://github.com/pcmoritz/mujoco-control-ant. Substantial modifications
have been made to the original model and therefore this model should be regarded
as distinct for practical purposes.
---
Dm Control/Viewer/README (dm_control/viewer/README.md)
Interactive environment viewer
The dm_control.viewer library can be used to visualize and interact with a
control environment. The following example shows how to launch the viewer with
an environment from the Control Suite:
from dm_control import suite
from dm_control import viewerLoad an environment from the Control Suite.
env = suite.load(domain_name="humanoid", task_name="stand")Launch the viewer application.
viewer.launch(env)For convenience we also provide a viewer launch script for the Control Suite in
dm_control/suite/explore.py.
Viewing the environment with a policy in the loop
The viewer is also capable of running the environment with a policy in the loop
to provide actions. This is done by passing the optional policy argument toviewer.launch. The policy should be a callable that accepts a TimeStep andenvironment.action_spec()
returns a numpy array of actions conforming to . The
example below shows how to execute a random uniform policy using the viewer:
from dm_control import suite
from dm_control import viewer
import numpy as npenv = suite.load(domain_name="humanoid", task_name="stand")
action_spec = env.action_spec()
Define a uniform random policy.
def random_policy(time_step):
del time_step # Unused.
return np.random.uniform(low=action_spec.minimum,
high=action_spec.maximum,
size=action_spec.shape)Launch the viewer application.
viewer.launch(env, policy=random_policy)Keyboard and mouse controls
The viewer contains a built in help screen that can be brought up by pressing
F1`. You will find a comprehensive description of keyboard and mouse controls
there.
Status view
Displays status of the simulation:
- State - Current status of the Runtime state machine.
- Time - Simulation clock accompanied by the current setting of time
multiplier.
- CPU - How much time per frame does physics simulation consume.
- FPS - How many frames per second is the application rendering.
- Camera - Name of the active camera.
- Paused - Is the simulation paused?
- Error - Recently caught error message.
---