diff --git a/docs/source/libero.mdx b/docs/source/libero.mdx index 6dba5c62e..880a18d60 100644 --- a/docs/source/libero.mdx +++ b/docs/source/libero.mdx @@ -92,6 +92,20 @@ LIBERO supports two control modes — `relative` (default) and `absolute`. Diffe --env.control_mode=relative # or "absolute" ``` +### Reset performance + +By default, LeRobot preserves LIBERO's hard-reset behavior. With fixed initial +states enabled, you can opt into soft resets to skip rebuilding the simulator +model and renderer on every episode: + +```bash +--env.init_states=true --env.hard_reset=false +``` + +Soft resets are faster but are not bit-identical to hard resets after the +environment's settling steps, so camera observations and policy results may +differ slightly. Use hard resets when reproducing benchmark results. + ### Policy inputs and outputs **Observations:** diff --git a/docs/source/libero_plus.mdx b/docs/source/libero_plus.mdx index b065649fa..173b81255 100644 --- a/docs/source/libero_plus.mdx +++ b/docs/source/libero_plus.mdx @@ -134,6 +134,20 @@ LIBERO-plus supports two control modes — `relative` (default) and `absolute`. --env.control_mode=relative # or "absolute" ``` +### Reset performance + +By default, LeRobot preserves LIBERO's hard-reset behavior. With fixed initial +states enabled, you can opt into soft resets to skip rebuilding the simulator +model and renderer on every episode: + +```bash +--env.init_states=true --env.hard_reset=false +``` + +Soft resets are faster but are not bit-identical to hard resets after the +environment's settling steps, so camera observations and policy results may +differ slightly. Use hard resets when reproducing benchmark results. + ### Policy inputs and outputs **Observations:** diff --git a/src/lerobot/envs/configs.py b/src/lerobot/envs/configs.py index 3c210052f..312b02625 100644 --- a/src/lerobot/envs/configs.py +++ b/src/lerobot/envs/configs.py @@ -328,6 +328,7 @@ class LiberoEnv(EnvConfig): render_mode: str = "rgb_array" camera_name: str = "agentview_image,robot0_eye_in_hand_image" init_states: bool = True + hard_reset: bool = True camera_name_mapping: dict[str, str] | None = None observation_height: int = 360 observation_width: int = 360 @@ -356,6 +357,8 @@ class LiberoEnv(EnvConfig): def __post_init__(self): if self.fps <= 0: raise ValueError(f"fps must be positive, got {self.fps}") + if not self.hard_reset and not self.init_states: + raise ValueError("hard_reset=False requires init_states=True") if self.obs_type == "pixels": self.features[LIBERO_KEY_PIXELS_AGENTVIEW] = PolicyFeature( @@ -416,6 +419,7 @@ class LiberoEnv(EnvConfig): "observation_height": self.observation_height, "observation_width": self.observation_width, "control_freq": self.fps, + "hard_reset": self.hard_reset, } if self.task_ids is not None: kwargs["task_ids"] = self.task_ids diff --git a/src/lerobot/envs/libero.py b/src/lerobot/envs/libero.py index b959ca8fb..a3e0f17c4 100644 --- a/src/lerobot/envs/libero.py +++ b/src/lerobot/envs/libero.py @@ -128,10 +128,13 @@ class LiberoEnv(gym.Env): control_freq: int = 20, control_mode: str = "relative", is_libero_plus: bool = False, + hard_reset: bool = True, ): super().__init__() if control_freq <= 0: raise ValueError(f"control_freq must be positive, got {control_freq}") + if not hard_reset and not init_states: + raise ValueError("hard_reset=False requires init_states=True") self.task_id = task_id self.is_libero_plus = is_libero_plus self.obs_type = obs_type @@ -158,6 +161,7 @@ class LiberoEnv(gym.Env): self.camera_name_mapping = camera_name_mapping self.num_steps_wait = num_steps_wait self.control_freq = control_freq + self.hard_reset = hard_reset self.episode_index = episode_index self.episode_length = episode_length # Load once and keep @@ -265,6 +269,9 @@ class LiberoEnv(gym.Env): camera_heights=self.observation_height, camera_widths=self.observation_width, control_freq=self.control_freq, + # Soft resets skip LIBERO's model and renderer rebuild. They are opt-in + # because settle steps can make their observations differ from hard resets. + hard_reset=self.hard_reset, ) env.reset() self._env = env