Skip to content

Oculus teleop - #5

Open
jbohg wants to merge 13 commits into
TRI-ML:mainfrom
jbohg:oculus-teleop
Open

jbohg wants to merge 13 commits into
TRI-ML:mainfrom
jbohg:oculus-teleop

Conversation

@jbohg

@jbohg jbohg commented May 13, 2026

Copy link
Copy Markdown

Oculus Quest Touch controller teleoperation

Adds support for teleoperation and data recording using Oculus Quest Touch controllers as an alternative to the SpaceMouse or leader arms. The control approach is inspired by the DROID teleoperation setup (droid/controllers/oculus_controller.py), adapted to raiden's TeleopInterface pattern and J-Parse IK stack.

New features

raiden/control/oculus.py — new OculusInterface(TeleopInterface)

  • Absolute-pose origin-tracking control with deadman grip (RG)
  • Press joystick (RJ) to zero orientation before grabbing
  • Index trigger: gripper (squeeze = close)
  • A button: start episode / mark success — B button: mark failure

raiden/robot/controller.py

  • start_oculus_teleop / stop_oculus_teleop / _oculus_control_loop — one thread per arm (bimanual supported)
  • _vec_to_reorder_mat helper for VR→robot axis permutation/flip
  • stop_teleoperation() calls stop_oculus_teleop() so the control thread stops before the arm returns home

raiden/cli.py + raiden/robot/teleop.py + raiden/recorder.py

  • --control oculus added to both TeleopCommand and RecordCommand
  • New flags: --oculus_spatial_coeff, --oculus_pos_action_gain, --oculus_rot_action_gain, --oculus_rmat_reorder
  • --follower_home_pos added to both commands (all control modes)

scripts/oculus_calib.py — axis calibration tool (no robot needed)

  • Connects to OculusReader and shows live VR→robot axis mapping table
  • While grip is held, shows position delta from latch point for each axis
  • Accepts --rmat_reorder to test different values before a full session

General fixes

raiden/robot/controller.py

  • follower_home_pos made configurable on RobotController.__init__ instead of a hardcoded constant — applies to all control modes
  • JAX IK pre-warmed in setup_for_teleop_recording() before motor init to prevent DM watchdog timeout during JIT compile

Dependencies

  • third_party/i2rt updated to public v1.1.1 — brings in per-joint gravity compensation YAML configs, shorter CAN receive timeout, and _drain_bus
  • pyzed bumped to 5.3 to match ZED SDK 5.3; ZED SVO playback switched to DEPTH_MODE.NEURAL_LIGHT

Usage

# Single arm
uv run rd teleop --control oculus --arms single

# Bimanual
uv run rd teleop --control oculus --arms bimanual

# Tune axes without robot
uv run python scripts/oculus_calib.py --rmat_reorder '-2,-1,-3,4'

# Record
uv run rd record --control oculus --arms single

jbohg and others added 13 commits May 12, 2026 12:51
New control mode: --control oculus
- OculusInterface (raiden/control/oculus.py): ADB-based Quest Touch
  controller input with deadman grip, orientation zeroing (joystick),
  A/B buttons for start/success/failure verdict
- RobotController: start/stop_oculus_teleop(), _oculus_control_loop()
  for 50Hz absolute-pose tracking; bimanual support (right ctrl →
  left arm, left ctrl → right arm)
- Pre-warm JAX IK before motor init to prevent DM watchdog timeouts
- _CAN_NAMES dict for flexible CAN interface name mapping
- follower_home_pos parameter on RobotController (all control modes)
- Update third_party/i2rt to latest: per-joint gravity compensation,
  grav_comp_kd, coulomb_friction from YAML configs; CAN timeout fixes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Bump pyzed wheel from 5.2 to 5.3 to match installed ZED SDK 5.3
- Fix stale docstring in zed.py (NEURAL → NEURAL_LIGHT)
- Add scripts/oculus_calib.py: no-robot axis calibration tool for
  tuning rmat_reorder before a full Oculus teleop session

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Points to the official release which already contains per-joint gravity
compensation YAML configs, shorter CAN receive timeout, and _drain_bus.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs in _oculus_control_loop:

1. R_rel was computed in the controller body frame (via vr_to_global_mat
   zeroing), causing yaw/roll coupling when the controller was tilted at
   zero time. Fix: store raw_pose at grip latch and compute R_delta_vr =
   raw_pose[:3,:3] @ raw_pose_origin[:3,:3].T directly in VR world frame.

2. VR +X (wrist-roll axis) and VR +Y (tilt/pitch axis) were swapped in
   the robot frame projection. Fix: apply _R_fix = [[0,1,0],[-1,0,0],[0,0,1]]
   to P before conjugating R_delta_vr, leaving the position path unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- OculusInterface: add start_hint property with controller-specific
  start instructions
- recorder: use interface.start_hint (if present) instead of generic
  leader-arm message
- recorder: fix emergency_stop handler to exit immediately when Ctrl+C
  is pressed between episodes (no active controller)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace removed ARM_YAM_XML_PATH / GRIPPER_LINEAR_4310_PATH constants
with ArmType.YAM / GripperType.LINEAR_4310 enums in
combine_arm_and_gripper_xml().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
By default the right Touch controller drives the left arm and the left
controller drives the right arm. --oculus-swap-controllers reverses this
so right→right and left→left.

Threads the flag through cli.py → run_bimanual_teleop → build_interface
→ OculusInterface → start_oculus_teleop → _oculus_control_loop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When standing in front of the robot (no --oculus-swap-controllers), the
user's left/right is mirrored relative to the robot's X axis. Forward/
backward was already correct. Negate delta_pos[0] in this case so that
moving left maps to robot-left and moving right maps to robot-right.

When --oculus-swap-controllers is set (standing behind the robot), the
mapping is already correct so flip_lateral=False.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Concise end-to-end guide covering: i2rt and raiden installation (separate
venvs), CAN interface naming, udev rules, Oculus ADB setup, per-session
startup, gravity comp testing per arm, Oculus teleop with controller
mapping and swap flag, camera config + calibration, and recording.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant