-
Notifications
You must be signed in to change notification settings - Fork 10
Open
Labels
Description
Teleop Device Categories
teleop stack divided into device categories, each with its own base module:
- VR Headsets (Browser-based WebXR)
Meta Quest
Apple Vision Pro
PICO - Smart Devices (Browser-based / Native App)
iPhone IMU
Smart watches (Maybe) - Hand Controllers (USB/Bluetooth)
Joysticks (single/dual stick)
Game controllers (Xbox, PlayStation)
SpaceMouse
Keyboard
Design Rationale: Each category has distinct input modalities (6DoF tracking vs IMU vs discrete axes), so a single base class for all devices would be bloated. Instead, each category has its own base module (VRBaseModule, SmartDeviceBaseModule, HandControllerBaseModule) with shared interfaces where appropriate.
class VRBaseModule(Module):
"""Base for all VR headset teleop devices."""
# Lifecycle
def start(self) -> None: ...
def stop(self) -> None: ...
# Teleop session (toggled by button press)
def start_teleop(self) -> None: ...
def stop_teleop(self) -> None: ...
# Core logic
def calibrate(self) -> bool:
"""Reset t0, store P0/R0 as reference. Called on teleop start."""
def compute_delta(
self,
controller_poses: list[PoseStamped | None],
) -> list[PoseStamped | None]:
"""Compute delta from calibrated reference. Always PoseStamped."""
def convert_to_output(
self,
pose: PoseStamped,
output_type: type
) -> PoseStamped | TwistStamped:
"""Convert to target output type at publish boundary."""class QuestModule(VRBaseModule):
"""Meta Quest teleop via WebSocket/LCM bridge."""
# Inputs (from Deno bridge)
left_transform: In[LCMTransform]
right_transform: In[LCMTransform]
left_trigger: In[Float32]
right_trigger: In[Float32]
teleop_enable: In[Bool] # X button
# Outputs (user configurable - PoseStamped or TwistStamped)
left_controller: Out[PoseStamped]
right_controller: Out[PoseStamped]
left_trigger_value: Out[Float32]
right_trigger_value: Out[Float32]
# Callbacks
def _on_teleop_enable(self, msg: Bool) -> None:
"""Toggle start_teleop/stop_teleop on button press."""
def _on_transform(self, index: int, transform: Transform) -> None: ...
def _on_trigger(self, index: int, msg: Float32) -> None: ...
# Main loop
def control_loop(self) -> None:
"""Compute deltas, convert to output type, publish."""- X press to start, and X press to stop. When pressed X again, it starts from zero
Reactions are currently unavailable