|
16 | 16 | # See the License for the specific language governing permissions and |
17 | 17 | # limitations under the License. |
18 | 18 |
|
| 19 | +import torch |
19 | 20 | from abc import ABC, abstractmethod |
| 21 | +from dataclasses import dataclass |
| 22 | +from typing import Any |
20 | 23 |
|
21 | | -from isaaclab.devices.openxr.retargeters import GR1T2RetargeterCfg |
22 | | -from isaaclab.devices.retargeter_base import RetargeterCfg |
| 24 | +from isaaclab.devices.openxr.retargeters import ( |
| 25 | + G1LowerBodyStandingMotionControllerRetargeterCfg, |
| 26 | + G1TriHandUpperBodyMotionControllerGripperRetargeterCfg, |
| 27 | + GR1T2RetargeterCfg, |
| 28 | +) |
| 29 | +from isaaclab.devices.retargeter_base import RetargeterBase, RetargeterCfg |
23 | 30 |
|
24 | 31 | from isaaclab_arena.assets.register import register_retargeter |
25 | 32 |
|
26 | 33 |
|
| 34 | +class DummyTorsoRetargeter(RetargeterBase): |
| 35 | + """Dummy retargeter that returns zero torso orientation commands. |
| 36 | +
|
| 37 | + This is used to pad the action space for G1 WBC Pink with motion controllers, |
| 38 | + which don't provide torso orientation commands. |
| 39 | + """ |
| 40 | + |
| 41 | + def __init__(self, cfg: "DummyTorsoRetargeterCfg"): |
| 42 | + super().__init__(cfg) |
| 43 | + |
| 44 | + def retarget(self, data: Any) -> torch.Tensor: |
| 45 | + """Return zeros for torso orientation (roll, pitch, yaw).""" |
| 46 | + return torch.zeros(3, device=self._sim_device) |
| 47 | + |
| 48 | + def get_requirements(self) -> list[RetargeterBase.Requirement]: |
| 49 | + """This retargeter doesn't require any device data.""" |
| 50 | + return [] |
| 51 | + |
| 52 | + |
| 53 | +@dataclass |
| 54 | +class DummyTorsoRetargeterCfg(RetargeterCfg): |
| 55 | + """Configuration for dummy torso retargeter.""" |
| 56 | + |
| 57 | + retargeter_type: type[RetargeterBase] = DummyTorsoRetargeter |
| 58 | + |
| 59 | + |
27 | 60 | class RetargetterBase(ABC): |
28 | 61 | device: str |
29 | 62 | embodiment: str |
30 | 63 |
|
31 | 64 | @abstractmethod |
32 | 65 | def get_retargeter_cfg( |
33 | 66 | self, embodiment: object, sim_device: str, enable_visualization: bool = False |
34 | | - ) -> RetargeterCfg: |
| 67 | + ) -> RetargeterCfg | list[RetargeterCfg] | None: |
| 68 | + """Get retargeter configuration. |
| 69 | +
|
| 70 | + Can return: |
| 71 | + - A single RetargeterCfg |
| 72 | + - A list of RetargeterCfg (for devices needing multiple retargeters) |
| 73 | + - None (for devices that don't need retargeters) |
| 74 | + """ |
35 | 75 | raise NotImplementedError |
36 | 76 |
|
37 | 77 |
|
@@ -125,3 +165,31 @@ def get_retargeter_cfg( |
125 | 165 | self, galbot_embodiment, sim_device: str, enable_visualization: bool = False |
126 | 166 | ) -> RetargeterCfg | None: |
127 | 167 | return None |
| 168 | + |
| 169 | + |
| 170 | +@register_retargeter |
| 171 | +class G1WbcPinkMotionControllersRetargeter(RetargetterBase): |
| 172 | + """Retargeter for G1 WBC Pink embodiment with motion controllers (Quest controllers).""" |
| 173 | + |
| 174 | + device = "openxr" |
| 175 | + embodiment = "g1_wbc_pink" |
| 176 | + |
| 177 | + def __init__(self): |
| 178 | + pass |
| 179 | + |
| 180 | + def get_retargeter_cfg( |
| 181 | + self, g1_embodiment, sim_device: str, enable_visualization: bool = False |
| 182 | + ) -> list[RetargeterCfg]: |
| 183 | + """Get motion controller retargeter configuration for G1. |
| 184 | +
|
| 185 | + Returns a list of retargeters: |
| 186 | + - Upper body (with gripper): outputs 16 dims [gripper(2), left_wrist(7), right_wrist(7)] |
| 187 | + - Lower body: outputs 4 dims [nav_cmd(3), hip_height(1)] |
| 188 | + - Dummy torso: outputs 3 dims [torso_orientation_rpy(3)] all zeros |
| 189 | + Total: 23 dims to match g1_wbc_pink action space |
| 190 | + """ |
| 191 | + return [ |
| 192 | + G1TriHandUpperBodyMotionControllerGripperRetargeterCfg(sim_device=sim_device), |
| 193 | + G1LowerBodyStandingMotionControllerRetargeterCfg(sim_device=sim_device), |
| 194 | + DummyTorsoRetargeterCfg(sim_device=sim_device), |
| 195 | + ] |
0 commit comments