Skip to content

Commit 94c737e

Browse files
Merge branch 'main' into dependabot/pip/pillow-10.3.0
2 parents 8f1dede + c40d983 commit 94c737e

217 files changed

Lines changed: 1332 additions & 540 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dm_control/_render/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ def _import_glfw():
4545
def _import_osmesa():
4646
from dm_control._render.pyopengl.osmesa_renderer import OSMesaContext
4747
return OSMesaContext
48+
49+
50+
# Import removed.
4851
# pylint: enable=g-import-not-at-top
4952

5053

@@ -59,6 +62,7 @@ def no_renderer(*args, **kwargs):
5962
(constants.GLFW, _import_glfw),
6063
(constants.EGL, _import_egl),
6164
(constants.OSMESA, _import_osmesa),
65+
# Option removed.
6266
)
6367

6468
_NO_RENDERER = (

dm_control/_render/base.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535

3636
from absl import logging
3737
from dm_control._render import executor
38+
import numpy as np
39+
3840

3941
_CURRENT_CONTEXT_FOR_THREAD = collections.defaultdict(lambda: None)
4042
_CURRENT_THREAD_FOR_CONTEXT = collections.defaultdict(lambda: None)
@@ -147,6 +149,10 @@ def make_current(self):
147149
ctx.call(self._platform_make_current)
148150
yield ctx
149151

152+
def to_pixels(self, buffer):
153+
"""Converts the buffer to pixels."""
154+
return np.flipud(buffer)
155+
150156
@abc.abstractmethod
151157
def _platform_init(self, max_width, max_height):
152158
"""Performs an implementation-specific context initialization."""

dm_control/_render/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@
2828
OSMESA = ('osmesa',)
2929
GLFW = ('glfw', 'on', 'enable', 'enabled', 'true', '1', '')
3030
EGL = ('egl',)
31+
# Constant removed.
3132
NO_RENDERER = ('off', 'disable', 'disabled', 'false', '0')
3233

dm_control/autowrap/header_parsing.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,24 @@ def _nested_if_else(if_, pred, else_, endif, match_if_true, match_if_false):
7878
return ifelse
7979

8080

81+
def _nested_ifn_else(ifn_, pred, else_, endif, match_if_true, match_if_false):
82+
"""Constructs a parser for (possibly nested) if...(else)...endif blocks."""
83+
ifnelse = pp.Forward()
84+
ifnelse << pp.Group( # pylint: disable=expression-not-assigned
85+
ifn_ +
86+
pred("predicate") +
87+
pp.ZeroOrMore(match_if_true | ifnelse)("if_false") +
88+
pp.Optional(else_ +
89+
pp.ZeroOrMore(match_if_false | ifnelse)("if_true")) +
90+
endif)
91+
return ifnelse
92+
93+
8194
# Some common string patterns to suppress.
8295
# ------------------------------------------------------------------------------
8396
(LPAREN, RPAREN, LBRACK, RBRACK, LBRACE, RBRACE, SEMI, COMMA, EQUAL, FSLASH,
8497
BSLASH) = list(map(pp.Suppress, "()[]{};,=/\\"))
85-
X = (pp.Keyword("X") | pp.Keyword("XMJV")).suppress()
98+
X = (pp.Keyword("X") | pp.Keyword("XMJV") | pp.Keyword("XNV")).suppress()
8699
EOL = pp.LineEnd().suppress()
87100

88101
# Comments, continuation.
@@ -189,7 +202,9 @@ def _nested_if_else(if_, pred, else_, endif, match_if_true, match_if_false):
189202
UNCOND_DECL = DEF_FLAG | DEF_CONST | TYPE_DECL
190203

191204
# Declarations inside (possibly nested) #if(n)def... #else... #endif... blocks.
192-
COND_DECL = _nested_if_else(IFDEF, NAME, ELSE, ENDIF, UNCOND_DECL, UNCOND_DECL)
205+
COND_DECL = _nested_if_else(
206+
IFDEF, NAME, ELSE, ENDIF, UNCOND_DECL, UNCOND_DECL
207+
) | _nested_ifn_else(IFNDEF, NAME, ELSE, ENDIF, UNCOND_DECL, UNCOND_DECL)
193208
# Note: this doesn't work for '#if defined(FLAG)' blocks
194209

195210
# e.g. "mjtNum gravity[3]; // gravitational acceleration"

dm_control/blender/mujoco_exporter/blender_scene.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def is_none(self) -> bool:
122122

123123
@property
124124
def is_armature(self) -> bool:
125-
return (
125+
return ( # pytype: disable=bad-return-type
126126
self.native_obj
127127
and self.native_obj.type == _ARMATURE
128128
and not self.native_bone
@@ -138,19 +138,19 @@ def is_bone(self) -> bool:
138138

139139
@property
140140
def is_mesh(self) -> bool:
141-
return self.native_obj and self.native_obj.type == _MESH
141+
return self.native_obj and self.native_obj.type == _MESH # pytype: disable=bad-return-type
142142

143143
@property
144144
def is_light(self) -> bool:
145-
return self.native_obj and self.native_obj.type == _LIGHT
145+
return self.native_obj and self.native_obj.type == _LIGHT # pytype: disable=bad-return-type
146146

147147
@property
148148
def is_camera(self) -> bool:
149-
return self.native_obj and self.native_obj.type == _CAMERA
149+
return self.native_obj and self.native_obj.type == _CAMERA # pytype: disable=bad-return-type
150150

151151
@property
152152
def is_empty(self) -> bool:
153-
return self.native_obj and self.native_obj.type == _EMPTY
153+
return self.native_obj and self.native_obj.type == _EMPTY # pytype: disable=bad-return-type
154154

155155
@property
156156
def name(self) -> str:

dm_control/blender/mujoco_exporter/mujoco_scene.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def light_builder(
7474
el.setAttribute('pos', vec_to_mjcf(transform.pos))
7575
el.setAttribute('dir', vec_to_mjcf(transform.rot @ _OZ))
7676
el.setAttribute('directional', directional)
77-
el.setAttribute('castshadow', bool_to_mjcf(light.use_shadow))
77+
el.setAttribute('castshadow', bool_to_mjcf(light.use_shadow)) # pytype: disable=wrong-arg-types
7878
el.setAttribute('diffuse', color_to_mjcf(light.color))
7979
el.setAttribute('attenuation', attenuation)
8080
return el

dm_control/composer/entity.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,8 @@ def set_pose(self, physics, position=None, quaternion=None):
480480
if position is not None:
481481
physics.bind(root_joint).qpos[:3] = position
482482
if quaternion is not None:
483-
physics.bind(root_joint).qpos[3:] = quaternion
483+
normalised_quaternion = quaternion / np.linalg.norm(quaternion)
484+
physics.bind(root_joint).qpos[3:] = normalised_quaternion
484485
else:
485486
attachment_frame = mjcf.get_attachment_frame(self.mjcf_model)
486487
if attachment_frame is None:
@@ -521,7 +522,7 @@ def shift_pose(self,
521522
if position is not None:
522523
new_position = current_position + position
523524
if quaternion is not None:
524-
quaternion = np.array(quaternion, dtype=np.float64, copy=False)
525+
quaternion = np.asarray(quaternion, dtype=np.float64)
525526
new_quaternion = _multiply_quaternions(quaternion, current_quaternion)
526527
root_joint = mjcf.get_frame_freejoint(self.mjcf_model)
527528
if root_joint and rotate_velocity:

dm_control/composer/environment.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,9 @@ def _recompile_physics_and_update_observables(self):
249249

250250
def _recompile_physics(self):
251251
"""Creates a new Physics using the latest MJCF model from the task."""
252-
if getattr(self, '_physics', None):
253-
self._physics.free()
252+
physics = getattr(self, '_physics', None)
253+
if physics:
254+
physics.free()
254255
self._physics = mjcf.Physics.from_mjcf_model(
255256
self._task.root_entity.mjcf_model)
256257
self._physics.legacy_step = self._legacy_step
@@ -267,9 +268,10 @@ def physics(self):
267268
"""Returns a `weakref.ProxyType` pointing to the current `mjcf.Physics`.
268269
269270
Note that the underlying `mjcf.Physics` will be destroyed whenever the MJCF
270-
model is recompiled. It is therefore unsafe for external objects to hold a
271-
reference to `environment.physics`. Attempting to access attributes of a
272-
dead `Physics` instance will result in a `ReferenceError`.
271+
model is recompiled or environment.close() is called. It is therefore unsafe
272+
for external objects to hold a reference to `environment.physics`.
273+
Attempting to access attributes of a dead `Physics` instance will result in
274+
a `ReferenceError`.
273275
"""
274276
return self._physics_proxy
275277

@@ -462,6 +464,11 @@ def _substep(self, action):
462464
self._physics.step()
463465
self._hooks.after_substep(self._physics_proxy, self._random_state)
464466

467+
def close(self):
468+
super().close()
469+
self._physics.free()
470+
self._physics = None
471+
465472
def action_spec(self):
466473
"""Returns the action specification for this environment."""
467474
return self._task.action_spec(self._physics_proxy)

dm_control/composer/initializers/prop_initializer.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def __init__(self,
8888
velocities are less than this threshold.
8989
max_attempts_per_prop: The maximum number of rejection sampling attempts
9090
per prop. If a non-colliding pose cannot be found before this limit is
91-
reached, a `RuntimeError` will be raised.
91+
reached, an `EpisodeInitializationError` will be raised.
9292
settle_physics: (optional) If True, the physics simulation will be
9393
advanced for a few steps to allow the prop positions to settle.
9494
min_settle_physics_time: (optional) When `settle_physics` is True, lower
@@ -170,8 +170,9 @@ def __call__(self, physics, random_state, ignore_contacts_with_entities=None):
170170
subsequently).
171171
172172
Raises:
173-
RuntimeError: If `ignore_collisions == False` and a non-colliding prop
174-
pose could not be found within `max_attempts_per_prop`.
173+
EpisodeInitializationError: If `ignore_collisions == False` and a
174+
non-colliding prop pose could not be found within
175+
`max_attempts_per_prop`.
175176
"""
176177
if ignore_contacts_with_entities is None:
177178
ignore_contacts_with_entities = []
@@ -222,9 +223,12 @@ def place_props():
222223
break
223224

224225
if not success:
225-
raise RuntimeError(_REJECTION_SAMPLING_FAILED.format(
226-
model_name=prop.mjcf_model.model,
227-
max_attempts=self._max_attempts_per_prop))
226+
raise composer.EpisodeInitializationError(
227+
_REJECTION_SAMPLING_FAILED.format(
228+
model_name=prop.mjcf_model.model,
229+
max_attempts=self._max_attempts_per_prop,
230+
)
231+
)
228232

229233
for prop in ignore_contacts_with_entities:
230234
self._restore_contact_parameters(physics, prop, cached_contact_params)
@@ -237,22 +241,26 @@ def place_and_settle():
237241

238242
# Step physics and check prop states.
239243
original_time = physics.data.time
240-
props_isolator = utils.JointStaticIsolator(physics, self._prop_joints)
241-
prop_joints_mj = physics.bind(self._prop_joints)
242-
while physics.data.time - original_time < self._max_settle_physics_time:
243-
with props_isolator:
244-
physics.step()
245-
max_qvel = np.max(np.abs(prop_joints_mj.qvel))
246-
max_qacc = np.max(np.abs(prop_joints_mj.qacc))
247-
if (max_qvel < self._max_qvel_tol) and (
248-
max_qacc < self._max_qacc_tol) and (
249-
physics.data.time - original_time
250-
) > self._min_settle_physics_time:
251-
return True
252-
physics.data.time = original_time
244+
try:
245+
props_isolator = utils.JointStaticIsolator(physics, self._prop_joints)
246+
prop_joints_mj = physics.bind(self._prop_joints)
247+
while (
248+
physics.data.time - original_time < self._max_settle_physics_time
249+
):
250+
with props_isolator:
251+
physics.step()
252+
max_qvel = np.max(np.abs(prop_joints_mj.qvel))
253+
max_qacc = np.max(np.abs(prop_joints_mj.qacc))
254+
if (max_qvel < self._max_qvel_tol) and (
255+
max_qacc < self._max_qacc_tol) and (
256+
physics.data.time - original_time
257+
) > self._min_settle_physics_time:
258+
return True
259+
finally:
260+
physics.data.time = original_time
253261

254262
if self._raise_exception_on_settle_failure:
255-
raise RuntimeError(
263+
raise composer.EpisodeInitializationError(
256264
_SETTLING_PHYSICS_FAILED.format(
257265
max_attempts=self._max_settle_physics_attempts,
258266
max_time=self._max_settle_physics_time,

dm_control/composer/initializers/prop_initializer_test.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
# limitations under the License.
1414
# ============================================================================
1515

16-
"""Tests for prop_initializer."""
17-
1816
from absl.testing import absltest
1917
from absl.testing import parameterized
2018
from dm_control import composer
@@ -111,7 +109,9 @@ def test_rejection_sampling_failure(self):
111109
expected_message = prop_initializer._REJECTION_SAMPLING_FAILED.format(
112110
model_name=spheres[1].mjcf_model.model, # Props are placed in order.
113111
max_attempts=max_attempts_per_prop)
114-
with self.assertRaisesWithLiteralMatch(RuntimeError, expected_message):
112+
with self.assertRaisesWithLiteralMatch(
113+
composer.EpisodeInitializationError, expected_message
114+
):
115115
prop_placer(physics, random_state=np.random.RandomState(0))
116116

117117
def test_ignore_contacts_with_entities(self):
@@ -143,7 +143,9 @@ def test_ignore_contacts_with_entities(self):
143143
prop_placer_init(physics, random_state=np.random.RandomState(0))
144144
expected_message = prop_initializer._REJECTION_SAMPLING_FAILED.format(
145145
model_name=spheres[0].mjcf_model.model, max_attempts=1)
146-
with self.assertRaisesWithLiteralMatch(RuntimeError, expected_message):
146+
with self.assertRaisesWithLiteralMatch(
147+
composer.EpisodeInitializationError, expected_message
148+
):
147149
prop_placer_seq[0](physics, random_state=np.random.RandomState(0))
148150

149151
# Placing the first sphere should succeed if we ignore contacts involving
@@ -160,6 +162,8 @@ def test_ignore_contacts_with_entities(self):
160162
def test_settle_physics(self, settle_physics):
161163
radius = 0.1
162164
physics, spheres = _make_spheres(num_spheres=2, radius=radius, nconmax=1)
165+
physics_start_time = 1337.0
166+
physics.data.time = physics_start_time
163167

164168
# Only place the first sphere.
165169
prop_placer = prop_initializer.PropPlacer(
@@ -180,7 +184,10 @@ def test_settle_physics(self, settle_physics):
180184
del second_quaternion # Unused.
181185

182186
# The sphere that we were not placing should not have moved.
183-
self.assertEqual(second_position[2], 0.)
187+
self.assertEqual(second_position[2], 0.0)
188+
self.assertEqual(
189+
physics.data.time, physics_start_time, 'Physics time should be reset.'
190+
)
184191

185192
@parameterized.parameters([0, 1, 2, 3])
186193
def test_settle_physics_multiple_attempts(self, max_settle_physics_attempts):

0 commit comments

Comments
 (0)