diff --git a/src/lerobot/envs/vlabench.py b/src/lerobot/envs/vlabench.py index 548edf846..ea72b77cb 100644 --- a/src/lerobot/envs/vlabench.py +++ b/src/lerobot/envs/vlabench.py @@ -43,8 +43,8 @@ from .utils import _LazyAsyncVectorEnv logger = logging.getLogger(__name__) ACTION_DIM = 7 # pos(3) + euler(3) + gripper(1) -ACTION_LOW = -1.0 -ACTION_HIGH = 1.0 +ACTION_LOW = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0], dtype=np.float32) +ACTION_HIGH = np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], dtype=np.float32) # Default max episode steps per task type DEFAULT_MAX_EPISODE_STEPS = 500 @@ -177,9 +177,7 @@ class VLABenchEnv(gym.Env): else: raise ValueError(f"Unsupported obs_type: {self.obs_type}") - self.action_space = spaces.Box( - low=ACTION_LOW, high=ACTION_HIGH, shape=(ACTION_DIM,), dtype=np.float32 - ) + self.action_space = spaces.Box(low=ACTION_LOW, high=ACTION_HIGH, dtype=np.float32) # Max attempts to rebuild the underlying env when MuJoCo throws # `PhysicsError` (e.g. mjWARN_BADQACC) during VLABench's 20-step @@ -346,6 +344,19 @@ class VLABenchEnv(gym.Env): dtype=np.float64, ) + @staticmethod + def _normalize_gripper_action(gripper: float) -> float: + """Normalize the scalar gripper command to VLABench's [0, 1] convention. + + The native VLABench collector uses 0=open and 1=closed. Older LeRobot + integrations often assumed a symmetric [-1, 1] gripper channel, so we + preserve backward compatibility by remapping negative values from + [-1, 1] -> [0, 1] before clipping. + """ + if gripper < 0.0: + gripper = 0.5 * (float(np.clip(gripper, -1.0, 1.0)) + 1.0) + return float(np.clip(gripper, 0.0, 1.0)) + def _build_ctrl_from_action(self, action: np.ndarray, ctrl_dim: int) -> np.ndarray: """Convert a 7D EEF action into the `ctrl_dim`-sized joint command vector. @@ -367,7 +378,7 @@ class VLABenchEnv(gym.Env): pos = np.asarray(action[:3], dtype=np.float64) rx, ry, rz = float(action[3]), float(action[4]), float(action[5]) - gripper = float(action[6]) + gripper = self._normalize_gripper_action(float(action[6])) quat = self._euler_xyz_to_quat_wxyz(rx, ry, rz) assert self._env is not None @@ -390,7 +401,7 @@ class VLABenchEnv(gym.Env): # Gripper: action scalar in [0, 1] (0=open, 1=closed). Map linearly to # finger qpos in [CLOSED, OPEN]. Franka has 2 mirrored fingers. - g = float(np.clip(gripper, 0.0, 1.0)) + g = gripper finger_qpos = self._FRANKA_FINGER_OPEN + g * (self._FRANKA_FINGER_CLOSED - self._FRANKA_FINGER_OPEN) ctrl = np.zeros(ctrl_dim, dtype=np.float64) diff --git a/tests/envs/test_vlabench_env.py b/tests/envs/test_vlabench_env.py new file mode 100644 index 000000000..97456cc57 --- /dev/null +++ b/tests/envs/test_vlabench_env.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +# Copyright 2025 The HuggingFace Inc. team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +from lerobot.envs.vlabench import ACTION_HIGH, ACTION_LOW, VLABenchEnv + + +def test_vlabench_action_space_uses_zero_to_one_gripper_channel(): + env = VLABenchEnv() + + np.testing.assert_array_equal(env.action_space.low, ACTION_LOW) + np.testing.assert_array_equal(env.action_space.high, ACTION_HIGH) + assert env.action_space.shape == (7,) + assert env.action_space.low[-1] == 0.0 + assert env.action_space.high[-1] == 1.0 + np.testing.assert_array_equal(env.action_space.low[:6], np.full(6, -1.0, dtype=np.float32)) + np.testing.assert_array_equal(env.action_space.high[:6], np.full(6, 1.0, dtype=np.float32)) + + +def test_vlabench_gripper_action_normalization_keeps_backward_compatibility(): + assert VLABenchEnv._normalize_gripper_action(-1.0) == 0.0 + assert VLABenchEnv._normalize_gripper_action(-0.5) == 0.25 + assert VLABenchEnv._normalize_gripper_action(0.0) == 0.0 + assert VLABenchEnv._normalize_gripper_action(0.5) == 0.5 + assert VLABenchEnv._normalize_gripper_action(1.0) == 1.0 + assert VLABenchEnv._normalize_gripper_action(1.5) == 1.0