Open bryanvis opened 1 year ago
The model you posted loads with no issue and has only 4 DoFs (yet the error is complaining about DOF 5), so the problem is elsewhere, something about the walker that you are adding?
Edit: see below
Ah, in the target task this line self._walker.create_root_joints(self._arena.attach(self._walker))
is adding a joint. It's still not obvious to me why the simulation is breaking immediately
Revisited w/ the composer tutorial. I compressed it down to a single file:
from dm_control import composer
from dm_control.composer.observation import observable
import numpy as np
from dm_control.locomotion.arenas import floors
from dm_control import mjcf
def make_frank():
random_state = np.random.RandomState(42)
rgba = random_state.uniform([0, 0, 0, 1], [1, 1, 1, 1])
model = mjcf.RootElement()
model.compiler.angle = 'radian' # Use radians.
box = model.worldbody.add('body', pos=[0.05,0.05,0.05])
box.add(
'geom', name='torso', type='box', size=[0.05,0.05,0.05], rgba=rgba)
x = box.add("joint", axis=[1,0,0], type="slide", pos=(0,0,0))
model.actuator.add("motor", gear="100", joint=x)
# y = box.add("joint", axis=[0,1,0], type="slide", name="my", pos=(0,0,0))
# model.actuator.add("motor", gear="100", joint=y)
return model
class Creature(composer.Entity):
def _build(self):
self._model = make_frank()
def _build_observables(self):
return CreatureObservables(self)
@property
def mjcf_model(self):
return self._model
@property
def actuators(self):
return tuple(self._model.find_all('actuator'))
class CreatureObservables(composer.Observables):
@composer.observable
def joint_positions(self):
all_joints = self._entity.mjcf_model.find_all('joint')
return observable.MJCFFeature('qpos', all_joints)
@composer.observable
def joint_velocities(self):
all_joints = self._entity.mjcf_model.find_all('joint')
return observable.MJCFFeature('qvel', all_joints)
NUM_SUBSTEPS = 25
class BasicTask(composer.Task):
def __init__(self, creature):
self._creature = creature
self._arena = floors.Floor()
self._arena.add_free_entity(self._creature)
self._arena.mjcf_model.worldbody.add('light', pos=(0, 0, 4))
self._creature.observables.joint_positions.enabled = True
self._creature.observables.joint_velocities.enabled = True
self.control_timestep = NUM_SUBSTEPS * self.physics_timestep
@property
def root_entity(self):
return self._arena
def initialize_episode_mjcf(self, random_state):
pass
def initialize_episode(self, physics, random_state):
pass
def get_reward(self, physics):
return 0
def get_task():
creature = Creature()
task = BasicTask(creature)
env = composer.Environment(task, random_state=np.random.RandomState(42))
return env
Some observations:
make_frank()
with make_creature()
as in the tutorial runs fine.To try to debug further I got the arena xml at runtime:
<mujoco model="floor">
<compiler boundmass="1e-05" boundinertia="1e-11" coordinate="local" angle="radian" eulerseq="xyz"/>
<option timestep="0.002" integrator="RK4" cone="elliptic" noslip_iterations="5" noslip_tolerance="0.0"/>
<visual>
<headlight ambient="0.40000000000000002 0.40000000000000002 0.40000000000000002" diffuse="0.80000000000000004 0.80000000000000004 0.80000000000000004" specular="0.10000000000000001 0.10000000000000001 0.10000000000000001"/>
<map znear="0.01"/>
<scale forcewidth="0.01" contactwidth="0.06" contactheight="0.01" jointwidth="0.01" framelength="0.3" framewidth="0.01"/>
</visual>
<default>
<default class="/"/>
<default class="unnamed_model/"/>
</default>
<asset>
<texture name="groundplane" type="2d" builtin="checker" rgb1="0.20000000000000001 0.29999999999999999 0.40000000000000002" rgb2="0.10000000000000001 0.20000000000000001 0.29999999999999999" mark="edge" markrgb="0.80000000000000004 0.80000000000000004 0.80000000000000004" width="200" height="200"/>
<material name="groundplane" class="/" texture="groundplane" texrepeat="2 2" texuniform="true" reflectance="0.2"/>
</asset>
<worldbody>
<geom name="groundplane" class="/" type="plane" size="8 8 0.25" material="groundplane"/>
<camera name="top_camera" class="/" fovy="10.058147163570894" pos="0 0 100" quat="1 0 0 0"/>
<body name="unnamed_model/">
<freejoint name="unnamed_model/"/>
<body name="unnamed_model//unnamed_body_0" pos="0.050000000000000003 0.050000000000000003 0.050000000000000003">
<geom name="unnamed_model/torso" class="unnamed_model/" type="box" size="0.050000000000000003 0.050000000000000003 0.050000000000000003" rgba="0.15601864044243652 0.15599452033620265 0.058083612168199461 1" mass="1.0"/>
<joint name="unnamed_model//unnamed_joint_0" class="unnamed_model/" type="slide" pos="0 0 0" axis="1 0 0"/>
<joint name="unnamed_model/my" class="unnamed_model/" type="slide" pos="0 0 0" axis="0 1 0"/>
</body>
</body>
<light name="//unnamed_light_0" class="/" pos="0 0 4"/>
</worldbody>
<actuator>
<motor name="unnamed_model//unnamed_actuator_0" class="unnamed_model/" gear="100" joint="unnamed_model//unnamed_joint_0"/>
<motor name="unnamed_model//unnamed_actuator_1" class="unnamed_model/" gear="100" joint="unnamed_model/my"/>
</actuator>
</mujoco>
Some things I tried: changing the integrator to RK4; reducing the timestep; changing solver params. Would be happy to hear any suggestions to fix this :)
This is a very strange model, not clear to me what you are trying to do. Maybe if I understood your goal I could help you.
You have a free body with no mass that only compiles because of the boundmass
and boundinertia
(so really it has a very tiny mass), and then this body has a chid with two slider joints? I don't understand why you would do that. Do you want the body to have 6 DoFs or 2? These chained 7 DoFs make no sense. Also your actuators are far too strong.
@yuvaltassa Sorry, that XML is the compiled one at runtime, maybe the code in the comment before that makes more sense. The goal is to have a cube that is the entire agent; it should slide along the floor, and be able to move in the x and y direction as well as rotate on the xy plane (so 3 actions.) I've been able to accomplish the x/y direction but not the rotation — I'll try to get a minimal working code for that in a followup.
So you want the cube to have 4 DoFs? How could it rotate if it can't move up? The corners will collide with the floor.
By "rotate on the xy plane", I just meant that the cube should be able to spin as you might spin a top — don't need to move up. Does that make sense?
XML:
<mujoco model="ice">
<compiler angle="radian"/>
<default class="root">
<joint pos='0 0 0' limited='false' armature='0' damping='0' stiffness='0'/>
</default>
<worldbody>
<body name="agent" pos="0.05 0.05 0.05">
<camera name="fake_egocentric" pos="5 0 5" xyaxes="0 -1 0 0 0 1" fovy="90" />
<joint class="root" axis="1 0 0" name="tx" pos="0 0 0" type="slide"></joint>
<joint class="root" axis="0 1 0" name="ty" pos="0 0 0" type="slide"></joint>
<geom mass="1" pos="0 0 0" rgba="0.52734375 0.8046875 0.9765625 1" size="0.05 0.05 0.05" type="box" euler="1.57 0 0"></geom>
</body>
</worldbody>
<actuator>
<motor name="motortx" gear="100" joint="tx" ></motor>
<motor name="motorty" gear="100" joint="ty"></motor>
</actuator>
</mujoco>
Code:
from dm_control import composer
from dm_control.composer.observation import observable
import numpy as np
from dm_control.locomotion.soccer.pitch import Pitch
from dm_control import mjcf
import os
_XML_DIRNAME = os.path.join(os.path.dirname(__file__), '../ice/')
_XML_FILENAME = 'ice.xml'
random_state = np.random.RandomState(42)
class Creature(composer.Entity):
"""A multi-legged creature derived from `composer.Entity`."""
def _build(self):
self._model = mjcf.from_path(os.path.join(_XML_DIRNAME, _XML_FILENAME))
def _build_observables(self):
return CreatureObservables(self)
@property
def mjcf_model(self):
return self._model
@property
def actuators(self):
return tuple(self._model.find_all('actuator'))
def create_root_joints(self, attachment_frame):
root_class = self._model.find('default', 'root')
root_x = attachment_frame.add(
'joint', name='root_x', type='slide', axis=[1, 0, 0], dclass=root_class)
root_y = attachment_frame.add(
'joint', name='root_y', type='slide', axis=[0, 1, 0], dclass=root_class)
# Add simple observable features for joint angles and velocities.
class CreatureObservables(composer.Observables):
@composer.observable
def joint_positions(self):
all_joints = self._entity.mjcf_model.find_all('joint')
return observable.MJCFFeature('qpos', all_joints)
@composer.observable
def joint_velocities(self):
all_joints = self._entity.mjcf_model.find_all('joint')
return observable.MJCFFeature('qvel', all_joints)
from dm_control.locomotion.arenas import floors
NUM_SUBSTEPS = 25
class BasicTask(composer.Task):
def __init__(self, creature):
self._creature = creature
self._arena = floors.Floor()
self._creature.create_root_joints(self._arena.attach(self._creature))
self._arena.mjcf_model.worldbody.add('light', pos=(0, 0, 20))
self._creature.observables.joint_positions.enabled = True
self._creature.observables.joint_velocities.enabled = True
self.control_timestep = NUM_SUBSTEPS * self.physics_timestep
@property
def root_entity(self):
return self._arena
def initialize_episode_mjcf(self, random_state):
pass
def initialize_episode(self, physics, random_state):
pass
def get_reward(self, physics):
return 0
def get_task():
creature = Creature()
task = BasicTask(creature)
env = composer.Environment(task, random_state=np.random.RandomState(42))
return env
Here's what I got working for x/y direction. Admittedly, I pieced this together using the soccer example, and it'd be great to have guidance on the right way to model this.
(Some confusions I had anyways: Why does this only work with the cube's geom having euler rotation 1.57 and not 0 in the axis? How exactly is the create_root_joints making things work here?)
From your description, sounds like you want to add a hinge joint with an axis in the z-direction?
Not sure what you mean about euler
, in what way does it "not work"?
@yuvaltassa I tried adding such a hinge as so:
<mujoco model="ice">
<compiler angle="radian"/>
<worldbody>
<body name="agent" pos="0 0 0.05">
<camera name="fake_egocentric" pos="5 0 5" xyaxes="0 -1 0 0 0 1" fovy="90" />
<joint axis="1 0 0" name="tx" pos="0 0 0" type="slide"></joint>
<joint axis="0 1 0" name="ty" pos="0 0 0" type="slide"></joint>
<joint axis="0 0 1" name="rz" pos="0 0 0" type="hinge"></joint>
<geom mass="1" pos="0 0 0" rgba="0.52734375 0.8046875 0.9765625 1" size="0.05 0.05 0.05" type="box" euler="1.57 0 0"></geom>
</body>
</worldbody>
<actuator>
<motor name="motortx" gear="100" joint="tx" ></motor>
<motor name="motorty" gear="100" joint="ty"></motor>
<motor name="motorrz" gear="10" joint="rz"></motor>
</actuator>
</mujoco>
That does what I want it to by itself. But adding the floor & compiling (see code at the end) leads to this one at runtime:
<mujoco model="floor">
<compiler boundmass="1e-05" boundinertia="1e-11" coordinate="local" angle="radian" eulerseq="xyz"/>
<option timestep="0.002" cone="elliptic" noslip_iterations="5" noslip_tolerance="0.0"/>
<visual>
<headlight ambient="0.40000000000000002 0.40000000000000002 0.40000000000000002" diffuse="0.80000000000000004 0.80000000000000004 0.80000000000000004" specular="0.10000000000000001 0.10000000000000001 0.10000000000000001"/>
<map znear="0.01"/>
<scale forcewidth="0.01" contactwidth="0.06" contactheight="0.01" jointwidth="0.01" framelength="0.3" framewidth="0.01"/>
</visual>
<default>
<default class="/"/>
<default class="ice/"/>
</default>
<asset>
<texture name="groundplane" type="2d" builtin="checker" rgb1="0.20000000000000001 0.29999999999999999 0.40000000000000002" rgb2="0.10000000000000001 0.20000000000000001 0.29999999999999999" mark="edge" markrgb="0.80000000000000004 0.80000000000000004 0.80000000000000004" width="200" height="200"/>
<material name="groundplane" class="/" texture="groundplane" texrepeat="2 2" texuniform="true" reflectance="0.2"/>
</asset>
<worldbody>
<geom name="groundplane" class="/" type="plane" size="8 8 0.25" material="groundplane"/>
<camera name="top_camera" class="/" fovy="10.058147163570894" pos="0 0 100" quat="1 0 0 0"/>
<body name="ice/">
<body name="ice/agent" pos="0 0 0.050000000000000003">
<camera name="ice/fake_egocentric" class="ice/" fovy="90.0" pos="5 0 5" xyaxes="0 -1 0 0 0 1"/>
<joint name="ice/tx" class="ice/" type="slide" pos="0 0 0" axis="1 0 0"/>
<joint name="ice/ty" class="ice/" type="slide" pos="0 0 0" axis="0 1 0"/>
<joint name="ice/rz" class="ice/" type="hinge" pos="0 0 0" axis="0 0 1"/>
<geom name="ice//unnamed_geom_0" class="ice/" type="box" size="0.050000000000000003 0.050000000000000003 0.050000000000000003" rgba="0.52734375 0.8046875 0.9765625 1" mass="1.0" pos="0 0 0" euler="1.5700000000000001 0 0"/>
</body>
</body>
<light name="//unnamed_light_0" class="/" pos="0 0 20"/>
</worldbody>
<actuator>
<motor name="ice/motortx" class="ice/" gear="100" joint="ice/tx"/>
<motor name="ice/motorty" class="ice/" gear="100" joint="ice/ty"/>
<motor name="ice/motorrz" class="ice/" gear="10" joint="ice/rz"/>
</actuator>
</mujoco>
And here's a video when applying [0,0,1] action. I expect it to spin around the center of the cube.
Ok, I think this was simply due to friction and setting "condim" to 1 for both the floor & the cube gets me the behavior I was looking for! I'll try to tune it a bit. (If this is covering up something wrong, do let me know though :) )
I'm getting started with dm_control and am trying to model a bare bones environment: a box sliding around a floor. To do this, I adapted from mujoco-worldgen just a box with 3 slide joints and corresponding actuators. I also use the floor arena and then edited down locomotion walker/target tasks to be as simple as possible. XML here. Note that if I don't include joint armature = 1, then it immediately fails w/
Nan, Inf or huge value in QACC at DOF 5
at time t=0.000. When I include it though, the box does not move.Full zip of code is attached.
ice 2.zip