diff --git a/source/isaaclab/isaaclab/devices/device_base.py b/source/isaaclab/isaaclab/devices/device_base.py index b7955468cc1..bdaf980f8fc 100644 --- a/source/isaaclab/isaaclab/devices/device_base.py +++ b/source/isaaclab/isaaclab/devices/device_base.py @@ -8,7 +8,7 @@ import torch from abc import ABC, abstractmethod from collections.abc import Callable -from dataclasses import dataclass, field +from dataclasses import dataclass, field, MISSING from typing import Any from isaaclab.devices.retargeter_base import RetargeterBase, RetargeterCfg @@ -17,7 +17,8 @@ @dataclass class DeviceCfg: """Configuration for teleoperation devices.""" - + device_type: type["DeviceBase"] = MISSING + teleoperation_active_default: bool = True sim_device: str = "cpu" retargeters: list[RetargeterCfg] = field(default_factory=list) diff --git a/source/isaaclab/isaaclab/devices/gamepad/se2_gamepad.py b/source/isaaclab/isaaclab/devices/gamepad/se2_gamepad.py index dacf1cdb497..c42857a77fa 100644 --- a/source/isaaclab/isaaclab/devices/gamepad/se2_gamepad.py +++ b/source/isaaclab/isaaclab/devices/gamepad/se2_gamepad.py @@ -18,15 +18,6 @@ from ..device_base import DeviceBase, DeviceCfg -@dataclass -class Se2GamepadCfg(DeviceCfg): - """Configuration for SE2 gamepad devices.""" - - v_x_sensitivity: float = 1.0 - v_y_sensitivity: float = 1.0 - omega_z_sensitivity: float = 1.0 - dead_zone: float = 0.01 - class Se2Gamepad(DeviceBase): r"""A gamepad controller for sending SE(2) commands as velocity commands. @@ -54,7 +45,7 @@ class Se2Gamepad(DeviceBase): def __init__( self, - cfg: Se2GamepadCfg, + cfg: "Se2GamepadCfg", ): """Initialize the gamepad layer. @@ -209,3 +200,14 @@ def _resolve_command_buffer(self, raw_command: np.ndarray) -> np.ndarray: command[command_sign] *= -1 return command + + +@dataclass +class Se2GamepadCfg(DeviceCfg): + """Configuration for SE2 gamepad devices.""" + + device_type: type[Se2Gamepad] = Se2Gamepad + v_x_sensitivity: float = 1.0 + v_y_sensitivity: float = 1.0 + omega_z_sensitivity: float = 1.0 + dead_zone: float = 0.01 diff --git a/source/isaaclab/isaaclab/devices/gamepad/se3_gamepad.py b/source/isaaclab/isaaclab/devices/gamepad/se3_gamepad.py index 24f3b0ef387..9d42036f3af 100644 --- a/source/isaaclab/isaaclab/devices/gamepad/se3_gamepad.py +++ b/source/isaaclab/isaaclab/devices/gamepad/se3_gamepad.py @@ -18,15 +18,6 @@ from ..device_base import DeviceBase, DeviceCfg -@dataclass -class Se3GamepadCfg(DeviceCfg): - """Configuration for SE3 gamepad devices.""" - - dead_zone: float = 0.01 # For gamepad devices - pos_sensitivity: float = 1.0 - rot_sensitivity: float = 1.6 - retargeters: None = None - class Se3Gamepad(DeviceBase): """A gamepad controller for sending SE(3) commands as delta poses and binary command (open/close). @@ -61,7 +52,7 @@ class Se3Gamepad(DeviceBase): def __init__( self, - cfg: Se3GamepadCfg, + cfg: "Se3GamepadCfg", ): """Initialize the gamepad layer. @@ -260,3 +251,14 @@ def _resolve_command_buffer(self, raw_command: np.ndarray) -> np.ndarray: delta_command[delta_command_sign] *= -1 return delta_command + + +@dataclass +class Se3GamepadCfg(DeviceCfg): + """Configuration for SE3 gamepad devices.""" + + device_type: type[Se3Gamepad] = Se3Gamepad + dead_zone: float = 0.01 # For gamepad devices + pos_sensitivity: float = 1.0 + rot_sensitivity: float = 1.6 + retargeters: None = None diff --git a/source/isaaclab/isaaclab/devices/keyboard/se2_keyboard.py b/source/isaaclab/isaaclab/devices/keyboard/se2_keyboard.py index 53682c12428..8106783950b 100644 --- a/source/isaaclab/isaaclab/devices/keyboard/se2_keyboard.py +++ b/source/isaaclab/isaaclab/devices/keyboard/se2_keyboard.py @@ -17,14 +17,6 @@ from ..device_base import DeviceBase, DeviceCfg -@dataclass -class Se2KeyboardCfg(DeviceCfg): - """Configuration for SE2 keyboard devices.""" - - v_x_sensitivity: float = 0.8 - v_y_sensitivity: float = 0.4 - omega_z_sensitivity: float = 1.0 - class Se2Keyboard(DeviceBase): r"""A keyboard controller for sending SE(2) commands as velocity commands. @@ -50,7 +42,7 @@ class Se2Keyboard(DeviceBase): """ - def __init__(self, cfg: Se2KeyboardCfg): + def __init__(self, cfg: "Se2KeyboardCfg"): """Initialize the keyboard layer. Args: @@ -178,3 +170,13 @@ def _create_key_bindings(self): "NUMPAD_9": np.asarray([0.0, 0.0, -1.0]) * self.omega_z_sensitivity, "X": np.asarray([0.0, 0.0, -1.0]) * self.omega_z_sensitivity, } + + +@dataclass +class Se2KeyboardCfg(DeviceCfg): + """Configuration for SE2 keyboard devices.""" + + device_type: type[Se2Keyboard] = Se2Keyboard + v_x_sensitivity: float = 0.8 + v_y_sensitivity: float = 0.4 + omega_z_sensitivity: float = 1.0 diff --git a/source/isaaclab/isaaclab/devices/keyboard/se3_keyboard.py b/source/isaaclab/isaaclab/devices/keyboard/se3_keyboard.py index 49dd02db300..5a6d4159b9f 100644 --- a/source/isaaclab/isaaclab/devices/keyboard/se3_keyboard.py +++ b/source/isaaclab/isaaclab/devices/keyboard/se3_keyboard.py @@ -18,14 +18,6 @@ from ..device_base import DeviceBase, DeviceCfg -@dataclass -class Se3KeyboardCfg(DeviceCfg): - """Configuration for SE3 keyboard devices.""" - - pos_sensitivity: float = 0.4 - rot_sensitivity: float = 0.8 - retargeters: None = None - class Se3Keyboard(DeviceBase): """A keyboard controller for sending SE(3) commands as delta poses and binary command (open/close). @@ -58,7 +50,7 @@ class Se3Keyboard(DeviceBase): """ - def __init__(self, cfg: Se3KeyboardCfg): + def __init__(self, cfg: "Se3KeyboardCfg"): """Initialize the keyboard layer. Args: @@ -202,3 +194,13 @@ def _create_key_bindings(self): "C": np.asarray([0.0, 0.0, 1.0]) * self.rot_sensitivity, "V": np.asarray([0.0, 0.0, -1.0]) * self.rot_sensitivity, } + + +@dataclass +class Se3KeyboardCfg(DeviceCfg): + """Configuration for SE3 keyboard devices.""" + + device_type: type[Se3Keyboard] = Se3Keyboard + pos_sensitivity: float = 0.4 + rot_sensitivity: float = 0.8 + retargeters: None = None diff --git a/source/isaaclab/isaaclab/devices/openxr/openxr_device.py b/source/isaaclab/isaaclab/devices/openxr/openxr_device.py index 34cd4bb2cfe..cf64f4002fa 100644 --- a/source/isaaclab/isaaclab/devices/openxr/openxr_device.py +++ b/source/isaaclab/isaaclab/devices/openxr/openxr_device.py @@ -13,7 +13,7 @@ from typing import Any import carb - +import isaaclab.devices as devices from isaaclab.devices.openxr.common import HAND_JOINT_NAMES from isaaclab.devices.retargeter_base import RetargeterBase @@ -29,13 +29,6 @@ from isaacsim.core.prims import SingleXFormPrim -@dataclass -class OpenXRDeviceCfg(DeviceCfg): - """Configuration for OpenXR devices.""" - - xr_cfg: XrCfg | None = None - - class OpenXRDevice(DeviceBase): """An OpenXR-powered device for teleoperation and interaction. @@ -78,7 +71,7 @@ class TrackingTarget(Enum): def __init__( self, - cfg: OpenXRDeviceCfg, + cfg: "OpenXRDeviceCfg", retargeters: list[RetargeterBase] | None = None, ): """Initialize the OpenXR device. @@ -300,3 +293,12 @@ def _on_teleop_command(self, event: carb.events.IEvent): elif "reset" in msg: if "RESET" in self._additional_callbacks: self._additional_callbacks["RESET"]() + + +@dataclass +class OpenXRDeviceCfg(DeviceCfg): + """Configuration for OpenXR devices.""" + + device_type: type[OpenXRDevice] = OpenXRDevice + xr_cfg: XrCfg | None = None + teleoperation_active_default: bool = False diff --git a/source/isaaclab/isaaclab/devices/spacemouse/se2_spacemouse.py b/source/isaaclab/isaaclab/devices/spacemouse/se2_spacemouse.py index 190ddc19ebb..afd9e0c7158 100644 --- a/source/isaaclab/isaaclab/devices/spacemouse/se2_spacemouse.py +++ b/source/isaaclab/isaaclab/devices/spacemouse/se2_spacemouse.py @@ -19,15 +19,6 @@ from .utils import convert_buffer -@dataclass -class Se2SpaceMouseCfg(DeviceCfg): - """Configuration for SE2 space mouse devices.""" - - v_x_sensitivity: float = 0.8 - v_y_sensitivity: float = 0.4 - omega_z_sensitivity: float = 1.0 - sim_device: str = "cpu" - class Se2SpaceMouse(DeviceBase): r"""A space-mouse controller for sending SE(2) commands as delta poses. @@ -48,7 +39,7 @@ class Se2SpaceMouse(DeviceBase): """ - def __init__(self, cfg: Se2SpaceMouseCfg): + def __init__(self, cfg: "Se2SpaceMouseCfg"): """Initialize the spacemouse layer. Args: @@ -168,3 +159,14 @@ def _run_device(self): # additional callbacks if "R" in self._additional_callbacks: self._additional_callbacks["R"] + + +@dataclass +class Se2SpaceMouseCfg(DeviceCfg): + """Configuration for SE2 space mouse devices.""" + + device_type: type[Se2SpaceMouse] = Se2SpaceMouse + v_x_sensitivity: float = 0.8 + v_y_sensitivity: float = 0.4 + omega_z_sensitivity: float = 1.0 + sim_device: str = "cpu" diff --git a/source/isaaclab/isaaclab/devices/spacemouse/se3_spacemouse.py b/source/isaaclab/isaaclab/devices/spacemouse/se3_spacemouse.py index 54a1aebcea2..ead72e71a2a 100644 --- a/source/isaaclab/isaaclab/devices/spacemouse/se3_spacemouse.py +++ b/source/isaaclab/isaaclab/devices/spacemouse/se3_spacemouse.py @@ -18,15 +18,6 @@ from .utils import convert_buffer -@dataclass -class Se3SpaceMouseCfg(DeviceCfg): - """Configuration for SE3 space mouse devices.""" - - pos_sensitivity: float = 0.4 - rot_sensitivity: float = 0.8 - retargeters: None = None - - class Se3SpaceMouse(DeviceBase): """A space-mouse controller for sending SE(3) commands as delta poses. @@ -49,7 +40,7 @@ class Se3SpaceMouse(DeviceBase): """ - def __init__(self, cfg: Se3SpaceMouseCfg): + def __init__(self, cfg: "Se3SpaceMouseCfg"): """Initialize the space-mouse layer. Args: @@ -191,3 +182,13 @@ def _run_device(self): self._additional_callbacks["R"]() if data[1] == 3: self._read_rotation = not self._read_rotation + + +@dataclass +class Se3SpaceMouseCfg(DeviceCfg): + """Configuration for SE3 space mouse devices.""" + + device_type: type[Se3SpaceMouse] = Se3SpaceMouse + pos_sensitivity: float = 0.4 + rot_sensitivity: float = 0.8 + retargeters: None = None diff --git a/source/isaaclab/isaaclab/devices/teleop_device_factory.py b/source/isaaclab/isaaclab/devices/teleop_device_factory.py index 89787b86674..b394fa9c0d3 100644 --- a/source/isaaclab/isaaclab/devices/teleop_device_factory.py +++ b/source/isaaclab/isaaclab/devices/teleop_device_factory.py @@ -31,17 +31,6 @@ # May fail if xr is not in use from isaaclab.devices.openxr import OpenXRDevice, OpenXRDeviceCfg -# Map device types to their constructor and expected config type -DEVICE_MAP: dict[type[DeviceCfg], type[DeviceBase]] = { - Se3KeyboardCfg: Se3Keyboard, - Se3SpaceMouseCfg: Se3SpaceMouse, - Se3GamepadCfg: Se3Gamepad, - Se2KeyboardCfg: Se2Keyboard, - Se2GamepadCfg: Se2Gamepad, - Se2SpaceMouseCfg: Se2SpaceMouse, - OpenXRDeviceCfg: OpenXRDevice, -} - # Map configuration types to their corresponding retargeter classes RETARGETER_MAP: dict[type[RetargeterCfg], type[RetargeterBase]] = { @@ -53,7 +42,7 @@ def create_teleop_device( - device_name: str, devices_cfg: dict[str, DeviceCfg], callbacks: dict[str, Callable] | None = None + env, device_name: str, devices_cfg: dict[str, DeviceCfg], callbacks: dict[str, Callable] | None = None ) -> DeviceBase: """Create a teleoperation device based on configuration. @@ -76,13 +65,8 @@ def create_teleop_device( device_cfg = devices_cfg[device_name] callbacks = callbacks or {} - # Check if device config type is supported - cfg_type = type(device_cfg) - if cfg_type not in DEVICE_MAP: - raise ValueError(f"Unsupported device configuration type: {cfg_type.__name__}") - # Get the constructor for this config type - constructor = DEVICE_MAP[cfg_type] + constructor = device_cfg.device_type # Try to create retargeters if they are configured retargeters = [] @@ -101,10 +85,13 @@ def create_teleop_device( # Check if the constructor accepts retargeters parameter constructor_params = inspect.signature(constructor).parameters + params = {} if "retargeters" in constructor_params and retargeters: - device = constructor(cfg=device_cfg, retargeters=retargeters) - else: - device = constructor(cfg=device_cfg) + params["retargeters"] = retargeters + if "env" in constructor_params: + params["env"] = env + + device = constructor(cfg=device_cfg, **params) # Register callbacks for key, callback in callbacks.items():