Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/unilab/base/env_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,10 @@ def make_registry_env(
# carry the explicit cold-path id in the opaque override and bind
# immediately before registry construction. Binding a non-zero id pins
# CUDA_VISIBLE_DEVICES for this process, so forward the post-pin
# in-process index downstream. This binding is required, not a
# compatibility shim: spawn collectors are fresh interpreters that cannot
# inherit the parent process's binding, and only UniLab's
# ``bind_genesis_process_device`` sets the pinned-namespace flag its
# resolution helpers consult.
# in-process index downstream. This binding is a required cold path of
# every spawn collector: fresh interpreters cannot inherit the parent
# process's binding, and only UniLab's ``bind_genesis_process_device``
# sets the pinned-namespace flag its resolution helpers consult.
if sim_backend == "genesis" and env_cfg_override is not None:
genesis_device_id = env_cfg_override.get("genesis_device_id")
if genesis_device_id is not None:
Expand Down
17 changes: 0 additions & 17 deletions src/unilab/envs/manager_based_rl_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -1035,23 +1035,8 @@ def _get_overlay() -> list[list[DebugPrimitive]] | None:

return _get_overlay

def _enter_suppressed_hot_state_refresh(self) -> None:
"""Allow a cold proxy to temporarily replace NumPy body-state reads."""
raise NotImplementedError(
f"{type(self).__name__} does not support suppressed hot state refresh"
)

def _exit_suppressed_hot_state_refresh(self) -> None:
"""Undo :meth:`_enter_suppressed_hot_state_refresh`."""
raise NotImplementedError(
f"{type(self).__name__} does not support suppressed hot state refresh"
)

def _load_managers(self) -> None:
"""Construct managers in the pinned community dependency order."""
suppress_hot_state_refresh = bool(getattr(self, "_suppress_hot_state_refresh", False))
if suppress_hot_state_refresh:
self._enter_suppressed_hot_state_refresh()
self.event_manager = EventManager(self._cfg.events, self)
self.command_manager = (
CommandManager(self._cfg.commands, self) if self._cfg.commands else NullCommandManager()
Expand All @@ -1064,8 +1049,6 @@ def _load_managers(self) -> None:
self,
scale_by_dt=self._cfg.scale_rewards_by_dt,
)
if suppress_hot_state_refresh:
self._exit_suppressed_hot_state_refresh()
self.curriculum_manager = (
CurriculumManager(self._cfg.curriculum, self)
if self._cfg.curriculum
Expand Down
13 changes: 8 additions & 5 deletions src/unilab/envs/mdp/commands/velocity_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,17 @@ def tensor_sensor_names(self) -> tuple[str, ...]:

def _metric_velocities(self) -> tuple[torch.Tensor, torch.Tensor]:
read_plan = getattr(self._env.scene, "read_plan", None)
if read_plan is None:
raise RuntimeError(
"UniformVelocityCommand metrics require the scene's compiled packed "
"tensor read plan. The lifecycle order is manager construction, then "
"ManagerBasedRlEnv read-plan compilation, then per-step command "
"compute; consuming metrics before the plan exists is an ordering bug"
)
names = self.tensor_sensor_names
packed = read_plan.sensor_names.get(self.cfg.entity_name, ()) if read_plan else ()
packed = read_plan.sensor_names.get(self.cfg.entity_name, ())
# The scene pack negotiates backend-local aliases (Go2 ``local_linvel``/
# ``gyro``) but the semantic metric remains the canonical IMU contract.
if read_plan is None:
# Manager construction and command reset before plan compilation do
# not consume metrics; defer to the compiled packed phase.
return torch.zeros((self.num_envs, 3)), torch.zeros((self.num_envs, 3))
aliases = self.packed_sensor_aliases
resolved_names = tuple(
next((local for local in aliases.get(name, (name,)) if local in packed), name)
Expand Down
9 changes: 9 additions & 0 deletions tests/envs/mdp/test_uniform_velocity_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,15 @@ def test_metrics_and_fixed_interval_resampling_follow_manager_schedule() -> None
np.testing.assert_allclose(term.time_left, 0.2)


def test_metrics_fail_closed_before_read_plan_compilation() -> None:
env, _ = _env()
manager = _manager(env)
env.scene.read_plan = None

with pytest.raises(RuntimeError, match="compiled packed tensor read plan"):
manager.compute(0.1)


def test_heading_world_forward_and_standing_modes() -> None:
all_mask = torch.ones(4, dtype=torch.bool)

Expand Down
Loading