You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5.6 KiB
5.6 KiB
G1 Teleop — Quest 3 Native App Setup
Overview
Godot 4.6.1 Quest 3 VR app for teleoperating a Unitree G1 humanoid robot. Uses OpenXR with Meta Quest hand tracking, body tracking, and passthrough.
Prerequisites
- Godot 4.6.1 — located at
Godot4_6_1/Godot_v4.6.1-stable_win64_console.exe - Android SDK + NDK — located at
android-sdk/ - JDK 17 — located at
jdk17/ - OpenXR Vendors plugin v4.3.0 — located at
addons/godotopenxrvendors/ - adb — located at
C:\Users\John\Downloads\platform-tools\adb.exe - Quest 3 in developer mode
Build Pipeline
# 1. Setup vendor plugin (copies AARs and .so files)
python build/setup_vendor_plugin.py
# 2. Build APK (headless Godot export)
build\build_461.bat
# 3. Install to Quest 3
adb install -r build/g1-teleop.apk
The build script runs: Godot_v4.6.1-stable_win64_console.exe --headless --quit --path . --export-debug "Quest 3" build/g1-teleop.apk
Architecture
Two-Phase Startup
- CONFIG phase: Dark VR environment with start screen UI, G1 robot models spinning on each side, hand laser pointer with pinch-to-click for menu interaction
- AR phase: Meta Quest passthrough with body tracking, webcam quad, gaze-activated exit balls
Key Files
| File | Purpose |
|---|---|
Main.gd |
Main controller — phase management, passthrough, gaze balls, G1 models, recenter |
Main.tscn |
Scene tree — XROrigin3D, camera, controllers, start screen, webcam quad |
scripts/vr_ui_pointer.gd |
Hand laser pointer, pinch-to-click, hand poke, controller ray interaction |
scripts/body_tracker.gd |
XRBodyTracker joint visualization and tracking data |
scripts/teleop_client.gd |
WebSocket client for robot communication |
scripts/start_screen.gd |
Config UI panel (connect, launch AR buttons) |
scripts/webcam_quad.gd |
Webcam video display |
project.godot |
Project settings including OpenXR extensions |
export_presets.cfg |
Android export config with Meta XR features |
models/g1_full.obj |
3D model of G1 robot (76MB OBJ, Z-up) |
Critical Project Settings (project.godot)
openxr/extensions/meta/passthrough=true # Required for Quest passthrough
openxr/extensions/meta/body_tracking=true # Required for body tracking
openxr/environment_blend_mode=0 # Must stay 0, passthrough enabled via code
Critical Export Settings (export_presets.cfg)
meta_xr_features/hand_tracking=2
meta_xr_features/passthrough=2
meta_xr_features/body_tracking=2
Technical Notes
Meta Quest Passthrough
- Requires
openxr/extensions/meta/passthrough=truein project.godot ANDmeta_xr_features/passthrough=2in export presets get_supported_environment_blend_modes()only returns[0](opaque) on Quest — must forceset_environment_blend_mode(XR_ENV_BLEND_MODE_ALPHA_BLEND)without checking support- The Meta vendor plugin intercepts this call via
XR_FB_PASSTHROUGHextension - Also requires
transparent_bg = trueon the viewport andclear_color = Color(0,0,0,0) - Do NOT add a WorldEnvironment with BG_COLOR black — it makes everything black in VR
Hand Tracking
- Palm joint's
-basis.zpoints perpendicular to palm (upward when hand flat), NOT forward - Ray direction must be computed geometrically: wrist to middle finger metacarpal
- XRHandTracker joints: PALM=0, WRIST=1, THUMB_TIP=5, INDEX_TIP=10, MIDDLE_META=11
- Pinch detection: thumb tip to index tip distance with hysteresis (press <2.5cm, release >3.5cm)
Scene Parenting
- Nodes under
XRCamera3Dfollow head tracking - Nodes under
XROrigin3Dstay stationary in tracking space - Webcam quad is under
XROrigin3D(stationary), notXRCamera3D
OBJ Model Loading
load("res://models/g1_full.obj")returns aMeshresource directly (not PackedScene)- OBJ files use Z-up; Godot uses Y-up — rotate -90 degrees on X axis to stand upright
- G1 model scale:
Vector3(0.0015, 0.0015, 0.0015) - Needs explicit material override and a DirectionalLight3D for shading
GDScript Gotchas
- Untyped Arrays return Variant — use explicit type:
var ball: MeshInstance3D = _gaze_balls[i] - Cannot infer types from Variant expressions — annotate variables explicitly
Gaze Ball System
- 4 balls above field of view: red (exit AR), yellow (reserved), green (reserved), blue (quit app)
- Head gaze detection via angle threshold (8 degrees)
- 5-second stare to activate with visual feedback (opacity, color, scale 1.0x to 1.8x)
- Laser beam from head to gazed ball
Recenter Support
- Connected to
XRInterface.pose_recenteredsignal - Must connect in both
is_initialized()andelifbranches of_ready() - Repositions start screen + G1 models (CONFIG) or webcam + gaze balls (AR)
Network
- Quest 3 and robot must be on the same network
- No SSL required (raw WebSocket)
- Default port: 8765
- Firewall: allow TCP 8765 on the robot
Troubleshooting
Body tracking not activating
- Ensure Quest 3 system software is up to date
- Check Settings → Movement Tracking → Body Tracking is enabled
- Ensure the Meta OpenXR Vendors plugin version is ≥ 4.1.1
WebSocket connection fails
- Verify robot IP is correct and reachable:
ping 10.0.0.64 - Check server is running:
ss -tlnp | grep 8765 - Check firewall:
sudo ufw allow 8765/tcp
Passthrough shows black/gray
- Verify
openxr/extensions/meta/passthrough=truein project.godot - Verify
meta_xr_features/passthrough=2in export_presets.cfg - Do NOT set
openxr/environment_blend_mode=2in project.godot (keep it 0) - Force alpha blend mode via code, not project settings
- Remove any WorldEnvironment node with black background