Fix: fetch mujoco_menagerie for directly-built envs (snow gait crashed on a fresh container)

#13
Files changed (4) hide show
  1. app.py +5 -0
  2. g1_getup.py +1 -0
  3. render_worker.py +13 -1
  4. snow_gait.py +5 -0
app.py CHANGED
@@ -170,8 +170,13 @@ def build_envs():
170
  if ENVS:
171
  return ENVS["env"], ENVS["eval_env"], ENVS["randomization_fn"]
172
  from mujoco_playground import registry
 
173
 
174
  STATE["status"] = "building env"
 
 
 
 
175
  if TASK == "getup":
176
  import g1_getup
177
  log("task: getup (fall recovery) -- flat ground, full-collision G1")
 
170
  if ENVS:
171
  return ENVS["env"], ENVS["eval_env"], ENVS["randomization_fn"]
172
  from mujoco_playground import registry
173
+ from mujoco_playground._src import mjx_env
174
 
175
  STATE["status"] = "building env"
176
+ # registry.load() clones mujoco_menagerie on demand; the snow-gait and get-up
177
+ # envs are constructed directly, which skips that, so a fresh container fails
178
+ # with "Error opening file ... left_hip_pitch_link.STL".
179
+ mjx_env.ensure_menagerie_exists()
180
  if TASK == "getup":
181
  import g1_getup
182
  log("task: getup (fall recovery) -- flat ground, full-collision G1")
g1_getup.py CHANGED
@@ -102,6 +102,7 @@ class G1Getup(g1_base.G1Env):
102
  ) -> None:
103
  # G1Env.__init__ compiles from an XML path; this task needs the generated
104
  # full-collision model instead, so set the model up here and skip it.
 
105
  mjx_env.MjxEnv.__init__(self, config, config_overrides)
106
  scene, assets = build_scene()
107
  self._model_assets = assets
 
102
  ) -> None:
103
  # G1Env.__init__ compiles from an XML path; this task needs the generated
104
  # full-collision model instead, so set the model up here and skip it.
105
+ mjx_env.ensure_menagerie_exists() # registry.load() would do this for us
106
  mjx_env.MjxEnv.__init__(self, config, config_overrides)
107
  scene, assets = build_scene()
108
  self._model_assets = assets
render_worker.py CHANGED
@@ -15,13 +15,25 @@ def main(mjb, qpos_path, out, fps, width, height, camera):
15
  model = mujoco.MjModel.from_binary_path(mjb)
16
  data = mujoco.MjData(model)
17
  qpos = np.load(qpos_path)
18
- cam = camera if mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_CAMERA, camera) >= 0 else -1
 
 
 
 
 
 
 
 
 
 
19
  with mujoco.Renderer(model, height=int(height), width=int(width)) as r, \
20
  imageio.get_writer(out, fps=float(fps), codec="libx264", quality=7,
21
  macro_block_size=None) as w:
22
  for q in qpos:
23
  data.qpos[:] = q
24
  mujoco.mj_forward(model, data)
 
 
25
  r.update_scene(data, camera=cam)
26
  w.append_data(r.render())
27
 
 
15
  model = mujoco.MjModel.from_binary_path(mjb)
16
  data = mujoco.MjData(model)
17
  qpos = np.load(qpos_path)
18
+
19
+ # "follow" is a free camera kept above and behind the robot. The scene's own
20
+ # "track" camera sits 0.35 m above the centre of mass, which ends up inside
21
+ # the hillside on sloped terrain and renders the ground black.
22
+ follow = camera == "follow"
23
+ if follow:
24
+ cam = mujoco.MjvCamera()
25
+ cam.azimuth, cam.elevation, cam.distance = 140.0, -15.0, 6.0
26
+ else:
27
+ cam = camera if mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_CAMERA, camera) >= 0 else -1
28
+
29
  with mujoco.Renderer(model, height=int(height), width=int(width)) as r, \
30
  imageio.get_writer(out, fps=float(fps), codec="libx264", quality=7,
31
  macro_block_size=None) as w:
32
  for q in qpos:
33
  data.qpos[:] = q
34
  mujoco.mj_forward(model, data)
35
+ if follow:
36
+ cam.lookat[:] = data.qpos[:3]
37
  r.update_scene(data, camera=cam)
38
  w.append_data(r.render())
39
 
snow_gait.py CHANGED
@@ -34,6 +34,7 @@ import jax.numpy as jp
34
  from ml_collections import config_dict
35
  from mujoco import mjx
36
 
 
37
  from mujoco_playground._src.locomotion.g1 import joystick as g1_joystick
38
 
39
  # Defaults, all overridable from the Space (see app.py).
@@ -73,6 +74,10 @@ class G1SnowGait(g1_joystick.Joystick):
73
  config: Optional[config_dict.ConfigDict] = None,
74
  config_overrides: Optional[Dict[str, Union[str, int, list[Any]]]] = None,
75
  ) -> None:
 
 
 
 
76
  super().__init__(task=task, config=config or snow_gait_config(),
77
  config_overrides=config_overrides)
78
  self._pelvis_body_id = self._mj_model.body("pelvis").id
 
34
  from ml_collections import config_dict
35
  from mujoco import mjx
36
 
37
+ from mujoco_playground._src import mjx_env
38
  from mujoco_playground._src.locomotion.g1 import joystick as g1_joystick
39
 
40
  # Defaults, all overridable from the Space (see app.py).
 
74
  config: Optional[config_dict.ConfigDict] = None,
75
  config_overrides: Optional[Dict[str, Union[str, int, list[Any]]]] = None,
76
  ) -> None:
77
+ # Playground clones mujoco_menagerie inside registry.load(); this class is
78
+ # built directly, so fetch the meshes here or the model compiler fails on
79
+ # a machine that has not downloaded them yet.
80
+ mjx_env.ensure_menagerie_exists()
81
  super().__init__(task=task, config=config or snow_gait_config(),
82
  config_overrides=config_overrides)
83
  self._pelvis_body_id = self._mj_model.body("pelvis").id