Skip to content

Conversation

@naveenkul
Copy link

@naveenkul naveenkul commented Oct 14, 2025

Add VR Teleoperation Module for Quest 3/3S Controllers

Adds native Quest 3/3S controller support for robot teleoperation via WebXR.

What's New:

  • MetaQuestModule - Full dimos integration with output streams for controller data
  • 90Hz streaming - Position, rotation, buttons, and thumbstick data at high frequency
  • WebXR interface - Browser-based, no APK required
  • Auto SSL setup - Generates certificates for HTTPS (WebXR requirement)
  • Standalone server - For testing and development

Usage:

quest = dimos.deploy(MetaQuestModule, port=8881)
quest.start()
quest.controller_right.observable().subscribe(handle_controller)

How it works: Quest browser connects to HTTPS server, streams controller data via WebSocket to dimos streams.

Files: dimos/vr/ module, test server, SSL cert generator, WebXR interface

@naveenkul naveenkul force-pushed the add-metaquest-module branch from ab00cd7 to 80a2cc5 Compare October 15, 2025 01:13
@leshy
Copy link
Contributor

leshy commented Oct 15, 2025

Looks amazing! we can keep special ControllerData model for full quest info, but for easy integration to robots would be helpful to emit PoseStamped also

I see you have

    position: tuple[float, float, float] = Field(description="Position (x, y, z) in meters")
    rotation: tuple[float, float, float, float]

you can do

from dimos.msgs.geometry_msgs import PoseStamped
pose = PoseStamped(
        ts=time.time(),
        position=(1.0, 2.0, 3.0),
        orientation=(0.1, 0.2, 0.3, 0.9),
)

so you can add 2 more outputs, left_pose and right_pose or something after this we can try hooking up directly to unitree or an arm


return generate

def _close_module(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added ModuleBase._close_module to cleanup things before we had start/stop. It's now called in ModuleBase.stop so you don't need to call _close_module anymore.

I think you should move this code to def stop() above and also add super().stop() at the end (to clean up all the module stuff, including _close_module).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PoseStamped added and moved camera cleanup to under stop()

from fastapi.staticfiles import StaticFiles
from pydantic import ValidationError

sys.path.insert(0, str(Path(__file__).parent.parent.parent))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

Comment on lines 223 to 241
self._running = False

self._server_thread = threading.Thread(target=run_server, daemon=True)
self._server_thread.start()

protocol = "https" if ssl_config else "http"
logger.info(f"VR server started at {protocol}://{self.host}:{self.port}")

except Exception as e:
self._running = False
logger.error(f"Failed to start VR server: {e}", exc_info=True)
raise

@rpc
def stop(self):
"""Stop the VR teleoperation server."""
if not self._running:
logger.info("VR server not running")
return
Copy link
Contributor

@paul-nechifor paul-nechifor Oct 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's an exception in the thread self._running gets set to false. That means that module.stop() cannot run fully because of the if not self._running condition.

I think you need to replace self._running = False with self.stop() in these two try-excepts

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed now. using self.stop()

}

@rpc
def generate_certificate(self, cert_dir: str = "dimos/vr/certificates"):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think dimos is for source code. You should probably place certificates in assets/vr/certificates. You can get it with:

from dimos.constants import DIMOS_PROJECT_ROOT
_CERTIFICATES_DIR = DIMOS_PROJECT_ROOT / "assets" / "vr" / "certificates"
_CERTIFICATES_DIR.mkdir(parents=True, exist_ok=True)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switched to using constants

@spomichter
Copy link
Contributor

@oliver-sommer Rebase on dev to fix all of the issues and conflicts

alexlin2 and others added 23 commits December 17, 2025 21:17
Former-commit-id: 26d5cc8 [formerly d8ebddd]
Former-commit-id: 50b2131
Former-commit-id: bdf5ab6 [formerly a77b8c0]
Former-commit-id: f78954a
Former-commit-id: b3a511e [formerly 3f8cd96]
Former-commit-id: 612beed
Former-commit-id: 1bc4683 [formerly 25c8235]
Former-commit-id: e692162
silence unnecessary unitree go 2 tricks

Former-commit-id: 539afe2 [formerly a6fce50]
Former-commit-id: e88e508
Former-commit-id: 55e1f47 [formerly 825c6dd]
Former-commit-id: 7ec7653
Former-commit-id: dc6f9d5 [formerly 5e9153e]
Former-commit-id: 688a361
Former-commit-id: f2d828e [formerly a60f72d]
Former-commit-id: 61554ed
Former-commit-id: 3587976 [formerly 93da41f]
Former-commit-id: a7bfabd
Former-commit-id: b9d3773 [formerly e0fe9b9]
Former-commit-id: 36093aa
Former-commit-id: 53d6010 [formerly ad82529]
Former-commit-id: 546c20e
Former-commit-id: 70d2d65 [formerly 97ea354]
Former-commit-id: 22d806c
Former-commit-id: ee02e41 [formerly a5e7b0b]
Former-commit-id: 5979ac6
Pshm to lcm

Former-commit-id: 8b797d4 [formerly eb0e794]
Former-commit-id: 1a8c212
Former-commit-id: 414cac7 [formerly 9ba84ae]
Former-commit-id: 468ec19
Former-commit-id: 66691d2 [formerly 72d6616]
Former-commit-id: 02ab8c6
Former-commit-id: d0432bf [formerly a56f244]
Former-commit-id: 383b947
Former-commit-id: 044b551 [formerly 590909f]
Former-commit-id: 9247011
Former-commit-id: 70ff849 [formerly db5a0de]
Former-commit-id: db56bb5
Former-commit-id: 7932bc8 [formerly 82080bc]
Former-commit-id: 0a2f748
Former-commit-id: 132f752 [formerly 51dd78b]
Former-commit-id: 95fa1cf
Former-commit-id: 7a23fb2 [formerly 36bc304]
Former-commit-id: 8c87f89
Former-commit-id: 185a4b9 [formerly 56eca4e]
Former-commit-id: f759f31
paul-nechifor and others added 19 commits December 23, 2025 17:19
Former-commit-id: db4f868 [formerly 4dea67e]
Former-commit-id: f4b3e97
Rename FakeRTC --> ReplayRTC

Former-commit-id: 5740322 [formerly 500edac]
Former-commit-id: 1fe4711
Former-commit-id: f31c6f1 [formerly 7ffad7e]
Former-commit-id: a6105ea
Former-commit-id: 75eb80c [formerly 37ff0f1]
Former-commit-id: dec3eec
…ce-rebase

Fix websocketvis performance rebase

Former-commit-id: ea0856c [formerly 98cdc40]
Former-commit-id: fe73322
…t-module

Former-commit-id: a894f0c [formerly c49cc57]
Former-commit-id: bf787ea
Former-commit-id: 45199a8 [formerly ac4e2a8]
Former-commit-id: e97f610
Former-commit-id: 6a5fa36 [formerly 80a2cc5]
Former-commit-id: 27adfec
Former-commit-id: f64d5d1 [formerly 1b2ea93]
Former-commit-id: 73880de
Former-commit-id: b2e2e6b [formerly 8e821b8]
Former-commit-id: 8063be0
Former-commit-id: 9837788 [formerly fbf1bc8]
Former-commit-id: 7276c34
Former-commit-id: fef1392 [formerly b44b3f1]
Former-commit-id: 82fc7b5
…ule upgrades

Former-commit-id: 5cabd77 [formerly d75c988]
Former-commit-id: d09cda2
Former-commit-id: dbd832e [formerly c61c40f]
Former-commit-id: 5d2c216
Former-commit-id: dfca893 [formerly 502c700]
Former-commit-id: b3f1f1c
…ntroller button tracking

Former-commit-id: 696cef8 [formerly b7ab843]
Former-commit-id: 5cbedf3
Former-commit-id: d3942da [formerly d9fa22e]
Former-commit-id: 61286c5
Issues:
Co-authored-by: Oliver Sommer

Former-commit-id: f8ef817
Former-commit-id: 52f5ea8
@greptile-apps
Copy link

greptile-apps bot commented Jan 8, 2026

Too many files changed for review.

2 similar comments
@greptile-apps
Copy link

greptile-apps bot commented Jan 8, 2026

Too many files changed for review.

@greptile-apps
Copy link

greptile-apps bot commented Jan 8, 2026

Too many files changed for review.

@greptile-apps
Copy link

greptile-apps bot commented Jan 8, 2026

Too many files changed for review.

@spomichter spomichter force-pushed the add-metaquest-module branch from 27f5ad5 to 811d2d3 Compare January 8, 2026 23:18
@greptile-apps
Copy link

greptile-apps bot commented Jan 8, 2026

Too many files changed for review.

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.

9 participants