Skip to content

Commit 308b73c

Browse files
DeepMindcopybara-github
authored andcommitted
Adding a movable head to the BoxHead walker with an attached camera to allow for egocentric vision.
PiperOrigin-RevId: 302398491 Change-Id: I0da293b76507a1841d7943327933f14e18499821
1 parent 03bebdf commit 308b73c

4 files changed

Lines changed: 70 additions & 16 deletions

File tree

dm_control/locomotion/soccer/assets/boxhead/boxhead.xml

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,39 @@
66
<joint pos='0 0 0' limited='false' armature='0' damping='0' stiffness='0'/>
77
</default>
88
</default>
9-
109
<worldbody>
1110
<body pos='0 0 1.0' quat='1 0 0 -1' name='head_body'>
1211
<camera name="float_far" pos="-4 0 2" xyaxes="0 -1 0 .5 0 1" fovy="90"/>
13-
<body name='egocentric_camera' pos='0.1 0 0.2' xyaxes='0 -1 0 0.09950371902 0 0.99503719021'>
12+
<body name='egocentric_camera' pos='0 0 0.4' xyaxes='0 -1 0 0 0 1'>
1413
<inertial pos='0 0 0' mass='1' diaginertia='1 1 1'/>
15-
<joint name='camera_yaw' type='hinge' axis='0 1 0' limited='true' range='-0.5 0.5'/>
16-
<joint name='camera_pitch' type='hinge' axis='1 0 0' limited='true' range='-0.35 0.35'/>
17-
<geom type='cylinder' name='eye_l' pos='-.14 -0.2 -0.171' size="0.06 0.05" rgba='1 1 1 1' mass='0' contype="0" conaffinity="0"/>
18-
<geom type='cylinder' name='eye_r' pos='.14 -0.2 -0.171' size="0.06 0.05" rgba='1 1 1 1' mass='0' contype="0" conaffinity="0"/>
14+
<joint name='camera_yaw' type='hinge' axis='0 1 0' limited='false'/>
15+
<joint name='camera_pitch' type='hinge' axis='1 0 0' limited='true' range='-0.35 0.00'/>
16+
<geom type='cylinder' name='eye_l' pos='-.14 -0.4 -0.171' size="0.06 0.2" rgba='1 1 1 1' mass='0' contype="0" conaffinity="0"/>
17+
<geom type='cylinder' name='eye_r' pos='.14 -0.4 -0.171' size="0.06 0.2" rgba='1 1 1 1' mass='0' contype="0" conaffinity="0"/>
1918
<camera name='egocentric' fovy='80'/>
2019
</body>
2120
<camera name='tracking' pos='1.5 1.5 .7' xyaxes='-1 1 0 -1 -1 4' mode='trackcom'/>
2221
<joint name='steer' type='hinge' axis='0 0 -1' class="root" damping="4"/>
23-
2422
<geom type='box' group='5' name='top_down_cam_box' pos='0 0 2' size="1. 1. 0.1" rgba='.1 .1 .1 1' mass='0' contype="0" conaffinity="0"/>
2523
<geom type='box' name='head' pos='0 0 0' size="0.2 0.2 0.1" rgba='.1 .1 .1 1' mass='20' contype="1" conaffinity="1"/>
2624
<site name='torso_site' pos='0 0 0' size='0.05' rgba='1 0 0 1' group='4'/>
27-
2825
<body pos='0 0 0.05' name='torso'>
2926
<!-- wheel -->
3027
<joint name='kick' type='slide' damping='100' limited='true' range='0 .4' axis="0 0 -1" stiffness="0" solimplimit=".95 .99 .001" springref="-.4"/>
3128
<geom name='arm_l' type='capsule' fromto='0 .35 -.7 .03 .8 -.7' size='.1' rgba='.76 .76 .76 1' density='10'/>
3229
<geom name='arm_r' type='capsule' fromto='0 -.35 -.7 .03 -.8 -.7' size='.1' rgba='.76 .76 .76 1' density='10'/>
33-
3430
<body name='ball' pos='0 0 -0.7'>
3531
<joint name='roll' type='hinge' axis='0 1 0' damping='2'/>
3632
<geom name='shell' type='sphere' size='.35' density='100' condim='4' friction='.5 .02 .02'/>
3733
</body>
38-
3934
</body>
4035
</body>
4136
</worldbody>
42-
4337
<sensor>
4438
<velocimeter site="torso_site" name="sensor_torso_vel"/>
4539
<gyro site="torso_site" name="sensor_torso_gyro"/>
4640
<accelerometer site="torso_site" name="sensor_torso_accel"/>
4741
</sensor>
48-
4942
<actuator>
5043
<general name='roll' joint='roll' ctrlrange='-1 1' ctrllimited='true' gear='-30' biasprm="0" biastype="affine"/>
5144
<motor name='steer' joint='steer' ctrlrange='-1 1' ctrllimited='true' gear='55'/>

dm_control/locomotion/soccer/boxhead.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from dm_control import composer
2525
from dm_control import mjcf
2626
from dm_control.composer.observation import observable
27+
from dm_control.locomotion.walkers import initializers
2728
from dm_control.locomotion.walkers import legacy_base
2829
import numpy as np
2930
from PIL import Image
@@ -104,6 +105,7 @@ def _asset_png_with_background_rgba_bytes(asset_fname, background_rgba):
104105

105106

106107
class BoxHeadObservables(legacy_base.WalkerObservables):
108+
"""BoxHead observables with low-res camera and modulo'd rotational joints."""
107109

108110
def __init__(self, entity, camera_resolution):
109111
self._camera_resolution = camera_resolution
@@ -115,6 +117,43 @@ def egocentric_camera(self):
115117
return observable.MJCFCamera(self._entity.egocentric_camera,
116118
width=width, height=height)
117119

120+
@property
121+
def proprioception(self):
122+
proprioception = super(BoxHeadObservables, self).proprioception
123+
if self._entity.observable_camera_joints:
124+
return proprioception + [self.camera_joints_pos, self.camera_joints_vel]
125+
return proprioception
126+
127+
@composer.observable
128+
def camera_joints_pos(self):
129+
130+
def _sin(value, random_state):
131+
del random_state
132+
return np.sin(value)
133+
134+
def _cos(value, random_state):
135+
del random_state
136+
return np.cos(value)
137+
138+
sin_rotation_joints = observable.MJCFFeature(
139+
'qpos', self._entity.observable_camera_joints, corruptor=_sin)
140+
141+
cos_rotation_joints = observable.MJCFFeature(
142+
'qpos', self._entity.observable_camera_joints, corruptor=_cos)
143+
144+
def _camera_joints(physics):
145+
return np.concatenate([
146+
sin_rotation_joints(physics),
147+
cos_rotation_joints(physics)
148+
], -1)
149+
150+
return observable.Generic(_camera_joints)
151+
152+
@composer.observable
153+
def camera_joints_vel(self):
154+
return observable.MJCFFeature(
155+
'qvel', self._entity.observable_camera_joints)
156+
118157

119158
class BoxHead(legacy_base.Walker):
120159
"""A rollable and jumpable ball with a head."""
@@ -146,7 +185,8 @@ def _build(self,
146185
Raises:
147186
ValueError: if received invalid walker_id.
148187
"""
149-
super(BoxHead, self)._build(initializer=initializer)
188+
super(BoxHead, self)._build(
189+
initializer=initializer or initializers.NoOpInitializer())
150190
xml_path = os.path.join(_ASSETS_PATH, 'boxhead.xml')
151191
self._mjcf_root = mjcf.from_xml_string(resources.GetResource(xml_path, 'r'))
152192
if name:
@@ -246,7 +286,9 @@ def set_pose(self, physics, position=None, quaternion=None):
246286
1 - 2 * (quaternion[2] ** 2 + quaternion[3] ** 2))
247287
physics.bind(self._mjcf_root.find('joint', 'steer')).qpos = z_angle
248288

249-
def initialize_episode(self, physics, unused_random_state):
289+
def initialize_episode(self, physics, random_state):
290+
self.reinitialize_pose(physics, random_state)
291+
250292
if self._camera_control:
251293
_compensate_gravity(physics,
252294
self._mjcf_root.find('body', 'egocentric_camera'))
@@ -279,6 +321,15 @@ def end_effectors(self):
279321
def observable_joints(self):
280322
return (self._mjcf_root.find('joint', 'kick'),)
281323

324+
@composer.cached_property
325+
def observable_camera_joints(self):
326+
if self._camera_control:
327+
return (
328+
self._mjcf_root.find('joint', 'camera_yaw'),
329+
self._mjcf_root.find('joint', 'camera_pitch'),
330+
)
331+
return ()
332+
282333
@composer.cached_property
283334
def egocentric_camera(self):
284335
return self._mjcf_root.find('camera', 'egocentric')

dm_control/locomotion/walkers/initializers/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def initialize_pose(self, physics, walker, random_state):
4949

5050

5151
class RandomlySampledInitializer(WalkerInitializer):
52-
"""Initializer that random selects between many initializers."""
52+
"""An initializer that random selects between many initializers."""
5353

5454
def __init__(self, initializers):
5555
self._initializers = initializers
@@ -59,3 +59,13 @@ def initialize_pose(self, physics, walker, random_state):
5959
random_initalizer_idx = np.random.randint(0, self.num_initializers)
6060
self._initializers[random_initalizer_idx].initialize_pose(
6161
physics, walker, random_state)
62+
63+
64+
class NoOpInitializer(WalkerInitializer):
65+
"""An initializer that does nothing."""
66+
67+
def initialize_pose(self, physics, walker, random_state):
68+
pass
69+
70+
71+

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def find_data_files(package_dir, patterns):
166166

167167
setup(
168168
name='dm_control',
169-
version='0.0.301472480',
169+
version='0.0.302398491',
170170
description='Continuous control environments and MuJoCo Python bindings.',
171171
author='DeepMind',
172172
license='Apache License, Version 2.0',

0 commit comments

Comments
 (0)