mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-26 19:26:16 +00:00
fix(vlabench): refetch dm_control physics on every step
`env.physics` in dm_control is a weakref proxy; caching it in `_ensure_env` and reusing across steps/resets yields a `ReferenceError: weakly-referenced object no longer exists` once the underlying sim is rebuilt (e.g. after the first reset of a composer environment). Drop the cache and refetch `self._env.physics` at the call sites (step and IK) — it's a cheap attribute access. Made-with: Cursor
This commit is contained in:
@@ -136,8 +136,10 @@ class VLABenchEnv(gym.Env):
|
|||||||
|
|
||||||
# Deferred — created on first reset() inside worker subprocess to avoid
|
# Deferred — created on first reset() inside worker subprocess to avoid
|
||||||
# inheriting stale GPU/EGL contexts when AsyncVectorEnv spawns workers.
|
# inheriting stale GPU/EGL contexts when AsyncVectorEnv spawns workers.
|
||||||
|
# We never cache `env.physics`: dm_control exposes it as a weakref
|
||||||
|
# proxy that goes stale across resets (rebuilds the sim), so we always
|
||||||
|
# refetch it via `self._env.physics` at the call site.
|
||||||
self._env = None
|
self._env = None
|
||||||
self._physics = None
|
|
||||||
self.task_description = "" # populated on first reset
|
self.task_description = "" # populated on first reset
|
||||||
|
|
||||||
h, w = self.render_resolution
|
h, w = self.render_resolution
|
||||||
@@ -200,7 +202,6 @@ class VLABenchEnv(gym.Env):
|
|||||||
render_resolution=(h, w),
|
render_resolution=(h, w),
|
||||||
)
|
)
|
||||||
self._env = env
|
self._env = env
|
||||||
self._physics = env.physics
|
|
||||||
|
|
||||||
# Extract task description from the dm_control task
|
# Extract task description from the dm_control task
|
||||||
task_obj = env.task
|
task_obj = env.task
|
||||||
@@ -337,9 +338,10 @@ class VLABenchEnv(gym.Env):
|
|||||||
site_name = robot.end_effector_site.full_identifier
|
site_name = robot.end_effector_site.full_identifier
|
||||||
|
|
||||||
# Important: inplace=False so IK doesn't mutate physics state mid-step;
|
# Important: inplace=False so IK doesn't mutate physics state mid-step;
|
||||||
# we only want the solved qpos.
|
# we only want the solved qpos. Fetch a fresh physics handle — caching
|
||||||
|
# it can yield a stale weakref after a reset.
|
||||||
ik_result = qpos_from_site_pose(
|
ik_result = qpos_from_site_pose(
|
||||||
self._physics,
|
self._env.physics,
|
||||||
site_name=site_name,
|
site_name=site_name,
|
||||||
target_pos=pos,
|
target_pos=pos,
|
||||||
target_quat=quat,
|
target_quat=quat,
|
||||||
@@ -374,7 +376,6 @@ class VLABenchEnv(gym.Env):
|
|||||||
def step(self, action: np.ndarray) -> tuple[RobotObservation, float, bool, bool, dict[str, Any]]:
|
def step(self, action: np.ndarray) -> tuple[RobotObservation, float, bool, bool, dict[str, Any]]:
|
||||||
self._ensure_env()
|
self._ensure_env()
|
||||||
assert self._env is not None
|
assert self._env is not None
|
||||||
assert self._physics is not None
|
|
||||||
|
|
||||||
if action.ndim != 1:
|
if action.ndim != 1:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
@@ -385,7 +386,10 @@ class VLABenchEnv(gym.Env):
|
|||||||
if self.action_mode not in ("eef", "joint", "delta_eef"):
|
if self.action_mode not in ("eef", "joint", "delta_eef"):
|
||||||
raise ValueError(f"Unknown action_mode: {self.action_mode}")
|
raise ValueError(f"Unknown action_mode: {self.action_mode}")
|
||||||
|
|
||||||
ctrl_dim = int(self._physics.data.ctrl.shape[0])
|
# Always refetch physics — dm_control returns a weakref proxy that can
|
||||||
|
# go stale across resets.
|
||||||
|
physics = self._env.physics
|
||||||
|
ctrl_dim = int(physics.data.ctrl.shape[0])
|
||||||
ctrl = self._build_ctrl_from_action(action, ctrl_dim)
|
ctrl = self._build_ctrl_from_action(action, ctrl_dim)
|
||||||
timestep = self._env.step(ctrl)
|
timestep = self._env.step(ctrl)
|
||||||
|
|
||||||
@@ -395,7 +399,7 @@ class VLABenchEnv(gym.Env):
|
|||||||
# Check success via the task's termination condition
|
# Check success via the task's termination condition
|
||||||
is_success = False
|
is_success = False
|
||||||
if hasattr(self._env, "task") and hasattr(self._env.task, "should_terminate_episode"):
|
if hasattr(self._env, "task") and hasattr(self._env.task, "should_terminate_episode"):
|
||||||
is_success = bool(self._env.task.should_terminate_episode(self._physics))
|
is_success = bool(self._env.task.should_terminate_episode(self._env.physics))
|
||||||
|
|
||||||
terminated = is_success
|
terminated = is_success
|
||||||
truncated = False
|
truncated = False
|
||||||
@@ -420,7 +424,6 @@ class VLABenchEnv(gym.Env):
|
|||||||
if self._env is not None:
|
if self._env is not None:
|
||||||
self._env.close()
|
self._env.close()
|
||||||
self._env = None
|
self._env = None
|
||||||
self._physics = None
|
|
||||||
|
|
||||||
|
|
||||||
# ---- Factory helpers ---------------------------------------------------------
|
# ---- Factory helpers ---------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user