fix(envs): set LiberoEnvConfig.fps default to 20 to match robosuite (#4124)

* fix(envs): set LiberoEnvConfig.fps default to 20 to match robosuite

LiberoEnvConfig.fps was set to 30, but the underlying robosuite
OffScreenRenderEnv always runs at its default control_freq of 20 Hz
since fps is never passed through. This mismatch silently decouples
the dataset/eval loop rate from the actual simulation step rate.

Set the default to 20 to match the real sim rate and avoid the
footgun.

Fixes #3368

* fix(libero): apply configured control frequency

---------

Co-authored-by: xinmotlanthua <275663218+xinmotlanthua@users.noreply.github.com>
This commit is contained in:
Steven Palma
2026-07-23 19:49:19 +02:00
committed by GitHub
parent f59eae4e27
commit cfd9ff969c
3 changed files with 21 additions and 1 deletions
+5 -1
View File
@@ -322,7 +322,7 @@ class HILSerlRobotEnvConfig(EnvConfig):
class LiberoEnv(EnvConfig): class LiberoEnv(EnvConfig):
task: str = "libero_10" # can also choose libero_spatial, libero_object, etc. task: str = "libero_10" # can also choose libero_spatial, libero_object, etc.
task_ids: list[int] | None = None task_ids: list[int] | None = None
fps: int = 30 fps: int = 20 # Must match robosuite's default control_freq (20 Hz)
episode_length: int | None = None episode_length: int | None = None
obs_type: str = "pixels_agent_pos" obs_type: str = "pixels_agent_pos"
render_mode: str = "rgb_array" render_mode: str = "rgb_array"
@@ -354,6 +354,9 @@ class LiberoEnv(EnvConfig):
control_mode: str = "relative" # or "absolute" control_mode: str = "relative" # or "absolute"
def __post_init__(self): def __post_init__(self):
if self.fps <= 0:
raise ValueError(f"fps must be positive, got {self.fps}")
if self.obs_type == "pixels": if self.obs_type == "pixels":
self.features[LIBERO_KEY_PIXELS_AGENTVIEW] = PolicyFeature( self.features[LIBERO_KEY_PIXELS_AGENTVIEW] = PolicyFeature(
type=FeatureType.VISUAL, shape=(self.observation_height, self.observation_width, 3) type=FeatureType.VISUAL, shape=(self.observation_height, self.observation_width, 3)
@@ -412,6 +415,7 @@ class LiberoEnv(EnvConfig):
"render_mode": self.render_mode, "render_mode": self.render_mode,
"observation_height": self.observation_height, "observation_height": self.observation_height,
"observation_width": self.observation_width, "observation_width": self.observation_width,
"control_freq": self.fps,
} }
if self.task_ids is not None: if self.task_ids is not None:
kwargs["task_ids"] = self.task_ids kwargs["task_ids"] = self.task_ids
+5
View File
@@ -125,10 +125,13 @@ class LiberoEnv(gym.Env):
n_envs: int = 1, n_envs: int = 1,
camera_name_mapping: dict[str, str] | None = None, camera_name_mapping: dict[str, str] | None = None,
num_steps_wait: int = 10, num_steps_wait: int = 10,
control_freq: int = 20,
control_mode: str = "relative", control_mode: str = "relative",
is_libero_plus: bool = False, is_libero_plus: bool = False,
): ):
super().__init__() super().__init__()
if control_freq <= 0:
raise ValueError(f"control_freq must be positive, got {control_freq}")
self.task_id = task_id self.task_id = task_id
self.is_libero_plus = is_libero_plus self.is_libero_plus = is_libero_plus
self.obs_type = obs_type self.obs_type = obs_type
@@ -154,6 +157,7 @@ class LiberoEnv(gym.Env):
} }
self.camera_name_mapping = camera_name_mapping self.camera_name_mapping = camera_name_mapping
self.num_steps_wait = num_steps_wait self.num_steps_wait = num_steps_wait
self.control_freq = control_freq
self.episode_index = episode_index self.episode_index = episode_index
self.episode_length = episode_length self.episode_length = episode_length
# Load once and keep # Load once and keep
@@ -260,6 +264,7 @@ class LiberoEnv(gym.Env):
bddl_file_name=self._task_bddl_file, bddl_file_name=self._task_bddl_file,
camera_heights=self.observation_height, camera_heights=self.observation_height,
camera_widths=self.observation_width, camera_widths=self.observation_width,
control_freq=self.control_freq,
) )
env.reset() env.reset()
self._env = env self._env = env
+11
View File
@@ -35,6 +35,17 @@ def test_unknown_type():
make_env_config("nonexistent") make_env_config("nonexistent")
def test_libero_fps_controls_simulator_frequency():
cfg = LiberoEnv(fps=17)
assert cfg.gym_kwargs["control_freq"] == 17
def test_libero_rejects_nonpositive_fps():
with pytest.raises(ValueError, match="fps must be positive"):
LiberoEnv(fps=0)
def test_identity_processors(): def test_identity_processors():
"""Base class get_env_processors() returns identity pipelines.""" """Base class get_env_processors() returns identity pipelines."""
cfg = make_env_config("aloha") cfg = make_env_config("aloha")