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.
dm_control.mujoco.wrapper.mjbindings.types
should not be used This module was
specific to the previous implementation of the dm_control python bindings. For
example, types.MJRRECT
should be replaced with mujoco.MjrRect
.
MjData.contact
(often accessed as Physics.data.contact) used to offer an
interface similar to a numpy structured array. For example, data.contact.geom1
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].dist
After the upgrade:
contacts = physics.data.contact
dists = [
c.dist for c in contacts if c.geom1 == geom_id or c.geom2 == geom_id
]
Code that accesses .ptr.contents
on objects such as MjvScene
will need to be
updated. In most cases, simply using scene
instead of scene.ptr.contents
will work.
Code (mostly in tests) that expects dm_control.mujoco.wrapper.core.Error
exceptions, will receive different exceptions, thrown by the mujoco
library.
These will often be ValueError
(for errors caused by input parameters), or
mujoco.FatalError
(for low level errors in MuJoCo).
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.
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)
The following are some changes that can make your code more concise, but are not required for it to continue working.
Existing code that uses dm_control.mujoco.wrapper.mjbindings.mjlib
can
directly replace these modules with mujoco. Code that uses enums
or
constants
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.mjlib
mjlib.mj_objectVelocity(
physics.model.ptr, physics.data.ptr,
enums.mjtObj.mjOBJ_SITE,
site_id, vel, 0)
After:
import mujoco
mujoco.mj_objectVelocity(
physics.model.ptr, physics.data.ptr,
mujoco.mjtObj.mjOBJ_SITE,
site_id, vel, 0)
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 a
struct to its default value (e.g. mjv_defaultOption
, 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.mjlib
scene_option = wrapper.core.MjvOption()
mjlib.mjv_defaultOption(scene_option.ptr)
After:
from dm_control.mujoco import wrapper
scene_option = wrapper.core.MjvOption()