Skip to content

Feat: Phone teleoperation stack with Go2 example#1280

Merged
ruthwikdasyam merged 33 commits intodevfrom
ruthwik_iphone_teleop
Feb 19, 2026
Merged

Feat: Phone teleoperation stack with Go2 example#1280
ruthwikdasyam merged 33 commits intodevfrom
ruthwik_iphone_teleop

Conversation

@ruthwikdasyam
Copy link
Contributor

@ruthwikdasyam ruthwikdasyam commented Feb 17, 2026

Phone Teleoperation for DimOS

Adding teleoperation via smartphone motion sensors. Tilt the phone to drive.
This extends the teleop subsystem introduced in the Quest PR, reusing the same architecture (WebSocket -> Deno LCM bridge -> Python module) but adapted for phone DeviceOrientation/DeviceMotion APIs

Issue - Linear

closes DIM-393
closes DIM-535

Approach

Web layer

  • Mobile web app (index.html) reads DeviceOrientation (roll, pitch, yaw) and DeviceMotion (gyro rates) at native sensor frequency
  • Encodes sensor data as TwistStamped and button state as Bool, sends over WebSocket at 50 Hz

Python module

  • PhoneTeleopModule subscribes to /phone/sensors (TwistStamped) and /phone/button (Bool) LCM channels
  • On engage (button hold), captures home orientation. Control loop at 50 Hz computes orientation delta from home, applies configurable gains, publishes TwistStamped velocity commands
  • Implements TeleopProtocol - same interface as QuestTeleopModule
  • Subclass hooks: _handle_engage(), _should_publish(), _get_output_twist(), _publish_msg()
  • Auto-launches/stops the Deno bridge subprocess with proper SIGTERM/SIGKILL shutdown

Also in this PR (teleop modules' improvements)

  • Deno bridge auto-launches with the module - no second terminal needed.
  • Robust subprocess lifecycle for both phone and quest teleop modules.

Breaking Changes

None

How to Test

With a Unitree Go2:

  1. Set ROBOT_IP env var to the Go2's IP
  2. Run dimos run phone-go2-teleop
  3. On phone, open https://<host-ip>:8444 and accept the self-signed certificate
  4. Tap "Start Sensors" - grant orientation/motion permissions
  5. Tap "Connect" - WS badge should turn blue
  6. Press and hold "HOLD TO TELEOP" - tilt phone to drive the Go2
  7. Release button - robot stops
  8. Tap "Disconnect"

@ruthwikdasyam ruthwikdasyam changed the base branch from main to dev February 17, 2026 08:30
@ruthwikdasyam ruthwikdasyam changed the title iphone teleop Feat: Phone teleoperation stack with Go2 example Feb 17, 2026
@ruthwikdasyam ruthwikdasyam marked this pull request as ready for review February 17, 2026 08:54
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 17, 2026

Greptile Summary

Adds a phone teleoperation stack that lets users drive robots by tilting a smartphone. The architecture mirrors the existing Quest VR teleop: a mobile web app reads DeviceOrientation/DeviceMotion sensor data, encodes it as LCM messages over WebSocket, and a Deno bridge forwards to Python. PhoneTeleopModule captures a home orientation on button press, computes delta-based velocity commands at 50 Hz, and publishes TwistStamped. Subclasses (SimplePhoneTeleop, PhoneGo2Teleop) filter axes for ground robots and wire to the Go2 cmd_vel interface. The PR also adds auto-launch of the Deno bridge subprocess to both phone and Quest modules, and moves Quest blueprints to dimos/teleop/quest/blueprints.py.

  • New PhoneTeleopModule implements TeleopProtocol with configurable gains, orientation-delta control, and overridable hooks for subclassing
  • SimplePhoneTeleop filters to mobile-base axes (pitch→linear.x, roll→linear.y, yaw→angular.z); PhoneGo2Teleop adds cmd_vel: Out[Twist] for direct Go2 wiring
  • Mobile web app handles iOS permission flow, 50 Hz WebSocket send loop, and pointer-event-based hold-to-drive button
  • Deno bridge server auto-generates self-signed TLS certs and uses a separate port (8444) from the Quest bridge (8443)
  • Both phone and Quest modules now auto-launch/stop the Deno bridge subprocess with SIGTERM/SIGKILL shutdown — however, _stop_server() has an unhandled TimeoutExpired exception after SIGKILL that can leak the process handle
  • Twist gains __add__ and __sub__ operators, used by the phone module for orientation delta computation
  • Server management code (_start_server/_stop_server) is duplicated between the phone and Quest modules and should be extracted into shared infrastructure

Confidence Score: 4/5

  • This PR is safe to merge with minor fixes needed in the subprocess shutdown path
  • The overall architecture is well-designed and follows existing patterns from the Quest teleop module. The code is clean, well-documented, and implements the TeleopProtocol correctly. The only functional issue is the unhandled TimeoutExpired exception in _stop_server() after SIGKILL, which could cause process handle leaks and exception propagation during shutdown. The duplicated server management code is a maintainability concern but not a correctness issue.
  • dimos/teleop/phone/phone_teleop_module.py and dimos/teleop/quest/quest_teleop_module.py need attention for the unhandled exception in _stop_server()

Important Files Changed

Filename Overview
dimos/msgs/geometry_msgs/Twist.py Adds __add__ and __sub__ operators for component-wise Twist arithmetic. Clean implementation consistent with existing Vector3 operators.
dimos/robot/all_blueprints.py Updates quest blueprint paths from dimos.teleop.blueprints to dimos.teleop.quest.blueprints after the file move, and adds new phone teleop blueprint/module entries.
dimos/teleop/phone/blueprints.py Defines simple_phone_teleop and phone_go2_teleop blueprint configurations with LCM transport wiring. Clean and follows existing blueprint conventions.
dimos/teleop/phone/phone_extensions.py Defines SimplePhoneTeleop (mobile-base axis filtering) and PhoneGo2Teleop (cmd_vel output for Go2 autoconnect). Well-structured subclass hierarchy.
dimos/teleop/phone/phone_teleop_module.py Core phone teleop module with sensor processing, control loop, and Deno bridge management. Has an unhandled TimeoutExpired exception in _stop_server() and duplicated server management code shared with Quest module.
dimos/teleop/phone/web/static/index.html Mobile web app for phone teleop. Handles DeviceOrientation/Motion APIs, iOS permissions, and WebSocket LCM encoding at 50 Hz. Well-implemented with proper pointer event handling.
dimos/teleop/phone/web/teleop_server.ts Deno HTTPS/WSS-to-LCM bridge server on port 8444 (separate from Quest's 8443). Auto-generates self-signed certs. Mirrors the Quest server structure closely.
dimos/teleop/quest/blueprints.py File moved from dimos/teleop/blueprints.py to dimos/teleop/quest/blueprints.py with no content changes. Path references in all_blueprints.py updated accordingly.
dimos/teleop/quest/quest_teleop_module.py Adds auto-launch/stop of Deno bridge server subprocess. Same TimeoutExpired bug and code duplication issue as the phone module.

Sequence Diagram

sequenceDiagram
    participant Phone as Phone Browser
    participant Deno as Deno Bridge (port 8444)
    participant LCM as LCM Transport
    participant PTM as PhoneTeleopModule
    participant Robot as Robot (Go2)

    Phone->>Phone: DeviceOrientation + DeviceMotion events
    loop Every 20ms (50 Hz)
        Phone->>Deno: WSS: TwistStamped (roll, pitch, yaw + gyro)
        Phone->>Deno: WSS: Bool (button state)
        Deno->>LCM: UDP: /phone/sensors (TwistStamped)
        Deno->>LCM: UDP: /phone/button (Bool)
    end

    LCM->>PTM: Subscribe /phone/sensors
    LCM->>PTM: Subscribe /phone/button

    loop Control Loop (50 Hz)
        PTM->>PTM: _handle_engage() (button hold → capture home)
        PTM->>PTM: _get_output_twist() (delta from home × gains)
        PTM->>PTM: _publish_msg() → cmd_vel: Out[Twist]
        PTM->>Robot: Twist velocity command
    end
Loading

Last reviewed commit: beb8fd1

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

11 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

# -----------------------------------------------------------------------------

# Simple phone teleop (mobile base axis filtering)
simple_phone_teleop = autoconnect(
Copy link
Contributor

Choose a reason for hiding this comment

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

nice

@ruthwikdasyam
Copy link
Contributor Author

Latest Changes:

  • removed protocol, no baseclass nor protocol necessary
  • no rpc calls, can be added later if/when in need
  • As no protocol, simplified PhoneTeleopModule

@ruthwikdasyam ruthwikdasyam merged commit d35844f into dev Feb 19, 2026
17 checks passed
@ruthwikdasyam ruthwikdasyam deleted the ruthwik_iphone_teleop branch February 19, 2026 02:55
spomichter added a commit that referenced this pull request Feb 21, 2026
Release v0.0.10: Manipulation Stack, MuJoCo Simulation, DDS Transport, Web and Native Visualization via Rerun


## Highlights

88+ commits, 20 contributors, 700+ files changed.

The TLDR: **a complete manipulation stack**, **MuJoCo simulation**, **DDS transport**, and **a rewritten visualization pipeline**. Agents are no longer bolted on top — they're refactored as native modules with direct stream access. The entire ROS message dependency has been removed from core DimOS, and we've added VR, phone, and arm teleoperation stacks. You can now vibecode a pick-and-place task from natural language to motor commands. Installation has been significantly streamlined — no more direnv, simpler setup, and the web viewer is now the default.

---

## 🚀 New Features

### Simulation
- **MuJoCo simulation module** — Run any DimOS blueprint in simulation with no hardware. Supports xArm and Unitree embodiments, parses MJCF/URDF for robot properties, monotonic clock timing (no `time.sleep`). `dimos --simulation run unitree-go2` ([#1035](#1035)) by @jca0
- **Simulation teleop blueprints** — Added simulation teleop blueprints for Piper, xArm6, and xArm7. ([#1308](#1308)) by @mustafab0

### Manipulation
- **Modular manipulation stack** — Full planning stack with Drake: FK/IK solvers (Jacobian + Drake optimization), RRT path planning, world model with obstacle monitoring, multi-robot management. xArm6/7 and Piper support. ([#1079](#1079)) by @mustafab0
- **Joint servo and cartesian controllers** — Joint position/velocity controllers and cartesian IK task with Pinocchio solver. PoseStamped stream input for real-time control. ([#1116](#1116)) by @mustafab0
- **GraspGen integration** — Grasp generation via Docker-hosted GPU model. Lazy container startup, thread-safe init, RPC `generate_grasps()` returns ranked PoseArray. ([#1119](#1119), [#1234](#1234)) by @JalajShuklaSS
- **Gripper control** — Gripper RPC methods on control coordinator, exposed adapter property for custom implementations. ([#1213](#1213)) by @mustafab0
- **Detection3D and Object support** — Object input topics, TF support on manipulation module, pointcloud-to-convex-hull for Drake imports. ([#1236](#1236)) by @mustafab0
- **Agentic pick and place** — Reimplemented manipulation skills for agent-driven pick-and-place workflows. ([#1237](#1237)) by @mustafab0

### Teleoperation
- **Quest VR teleoperation** — Full WebXR + Deno bridge stack. Quest controller data (pose, trigger, grip) streamed to DimOS modules. Monitor-style locking for control loops. ([#1215](#1215)) by @ruthwikdasyam
- **Phone teleoperation** — Control Go2 from your phone with a web-based teleop interface. ([#1280](#1280)) by @ruthwikdasyam
- **Arm teleop with Pinocchio IK** — Single and dual arm teleoperation using Pinocchio inverse kinematics. Blueprints for xArm, Piper, and dual configurations. ([#1246](#1246)) by @ruthwikdasyam

### Transports & Infrastructure
- **DDS transport protocol** — CycloneDDS transport with configurable QoS (high-throughput and reliable profiles). Optional install, benchmark integration. ([#1174](#1174)) by @Kaweees
- **Pubsub pattern subscriptions** — Glob and regex pattern matching for topic subscriptions. `subscribe_all()` for bridge-style consumers. Topic type encoding in channel strings (`/topic#module.ClassName`). ([#1114](#1114)) by @leshy
- **LCM raw bytes passthrough** — Skip `lcm_encode()` when message is already bytes. ([#1223](#1223)) by @leshy
- **Unified TimeSeriesStore** — Pluggable backends (InMemory, SQLite, Pickle, PostgreSQL) with SortedKeyList for O(log n) operations. Replaces the old replay system and TimestampedCollection. Collection API with slice, range, and streaming methods. ([#1080](#1080)) by @leshy
- **DimosROS benchmark tests** — Benchmark suite for ROS transport performance. ([#1087](#1087)) by @leshy

### Navigation
- **FASTLIO2 support** — Hardware-verified localization with arm64 support. Docker deployment with FAR Planner, terrain analysis, and bagfile playback mode. Builds or-tools from source on arm64. ([#1149](#1149)) by @baishibona
- **Native Livox + FASTLIO2 module** — First-class DimOS native module for Livox Mid-360 lidar with FASTLIO2 localization. ([#1235](#1235)) by @leshy

### Visualization
- **RerunBridge module and CLI** — New bridge that subscribes to all LCM messages and logs those with `to_rerun()` to Rerun viewer. GlobalConfig singleton, web viewer support. Replaces the old rerun initialization system. ([#1154](#1154)) by @leshy
- **Webcam rerun visualization** — Camera module logs to Rerun with pinhole projection for 3D visualization. ([#1117](#1117)) by @ruthwikdasyam
- **Default viewer switched to rerun-web** — Browser-based viewer is now the default for broader compatibility. No native viewer install needed. ([#1324](#1324)) by @spomichter

### Agents
- **Agent refactor** — Restructured agent module with cleaner imports and global config integration. ([#1211](#1211)) by @paul-nechifor
- **Timestamp knowledge** — Agents now have timestamp awareness in prompts for temporal reasoning. ([#1093](#1093)) by @ClaireBookworm
- **Observe skill** — Go2 can now observe (capture and describe) its environment via agent skill. ([#1109](#1109)) by @paul-nechifor

### Platform & Hardware
- **G1 without ROS** — Unitree G1 blueprints decoupled from ROS dependency. Lazy imports for fast startup. ([#1221](#1221)) by @jeff-hykin
- **ARM (aarch64) support** — DimOS runs on ARM hardware. Platform-conditional dependencies, open3d source builds for arm64. ([#1229](#1229)) by @jeff-hykin
- **Universal joint/hardware schema** — `HardwareComponent` dataclass with `JointState`, `JointName` type aliases. Backend registry with auto-discovery for SDK adapters. ([#1040](#1040), [#1067](#1067)) by @mustafab0

---

## 🔧 Improvements

- **Optional Dask** — Start without Dask using `--no-dask` flag. Startup time reduced from ~60s to ~45s. ([#1111](#1111), [#1232](#1232)) by @paul-nechifor
- **RPC rework** — Renamed `ModuleBlueprint` → `_BlueprintAtom`, `ModuleBlueprintSet` → `Blueprint`, `ModuleConnection` → `Stream`. Added `ModuleRef`, improved type hints throughout. ([#1143](#1143)) by @jeff-hykin
- **Image class simplification** — Rewritten as pure NumPy dataclass. Removed CUDA backend, unused methods (solve_pnp, csrt_tracker), and image_impls/ directory. ([#1161](#1161)) by @leshy
- **Odometry message cleanup** — Simplified Odometry message type. ([#1256](#1256)) by @leshy
- **Remove all ROS message dependencies** — Purged ROS message types from core DimOS. Refactored rosnav to use ROSTransport. Removed dead ROS bridge code. ([#1230](#1230)) by @alexlin2
- **Removed bad function serialization** — Eliminated unnecessary serialization of Python functions. ([#1121](#1121)) by @paul-nechifor
- **Benchmark IEC units** — Switched bandwidth benchmarks from SI to IEC units for accuracy. ([#1147](#1147)) by @leshy
- **Pubsub typing improvements** — Thread-safety locks on `subscribe_new_topics` and `subscribe_all`. Proper type params across pubsub stack. ([#1153](#1153)) by @leshy
- **Autogenerated blueprint list** — Blueprints are now auto-discovered and listed. ([#1100](#1100)) by @paul-nechifor
- **Generic Buttons message** — Renamed `QuestButtons` to `Buttons` with generic field names for cross-platform teleop. ([#1261](#1261)) by @ruthwikdasyam
- **Dev container uses ros-dev image** — `./bin/dev` now runs the ROS-enabled dev image. ([#1170](#1170)) by @leshy
- **LSP support** — Added python-lsp-server and python-lsp-ruff to dev dependencies. ([#1169](#1169)) by @leshy
- **Lazy-load pyrealsense2** — RealSense camera module uses lazy imports to avoid errors in simulation environments without the SDK. ([#1309](#1309)) by @spomichter
- **Removed unused mmcv and mmengine** — Dead Detic dependencies removed, eliminating slow source builds from install. ([#1319](#1319)) by @spomichter
- **Simplified installation** — Removed direnv requirement, streamlined install instructions across all platforms. ([#1315](#1315)) by @spomichter
- **DDS extra excluded from --all-extras** — `cyclonedds` requires a source build, so `dds` is now excluded from `uv sync --all-extras` by default. ([#1318](#1318)) by @spomichter
- **Nix pre-commit skip** — Skip pre-commit install if hooks already exist. ([#1162](#1162)) by @leshy
- **Removed base-requirements** — Consolidated dependency management. ([#1098](#1098)) by @paul-nechifor
- **Removed old graspnet** — Cleaned up deprecated graspnet version. ([#1248](#1248)) by @paul-nechifor
- **Code cleanup** — Removed `tofix` markers ([#1216](#1216)), fixed ruff issues ([#1112](#1112)), removed old README_installation.md ([#1101](#1101)) by @paul-nechifor

---

## 🐛 Bug Fixes

- Fix LFS updating (move from .local to venv) ([#1090](#1090)) by @jeff-hykin
- Launch hotfixes: git clone HTTPS, get_data main branch ([#1091](#1091)) by @spomichter
- Fix camera demo not showing in Rerun ([#1148](#1148)) by @jeff-hykin
- Default to rerun native viewer ([#1099](#1099)) by @Nabla7
- Fix exploration blocking agent loop ([#1258](#1258)) by @paul-nechifor
- Fix person-follow blocking agent loop ([#1278](#1278)) by @paul-nechifor
- Skip metric3d tests on unsupported xformers GPUs (Blackwell compute capability >9.0) ([#1225](#1225)) by @leshy
- Fix manipulation tests ([#1218](#1218), [#1247](#1247)) by @jeff-hykin, @paul-nechifor
- Fix control coordinator e2e test ([#1212](#1212)) by @mustafab0
- Fix xarm7-sim broken e2e tests ([#1294](#1294)) by @paul-nechifor
- Pin langchain to restore supported providers ([#1241](#1241)) by @spomichter
- Fix missing library dependencies in Nix flake ([#1240](#1240)) by @Kaweees
- Fix discord invite link ([#1122](#1122)) by @spomichter
- macOS edgecase fix ([#1096](#1096)) by @jeff-hykin
- Fix second N in logo ([#1250](#1250)) by @jeff-hykin
- Fix Unitree Go2 minor issues ([#1307](#1307)) by @paul-nechifor
- Fix broken tests ([#1305](#1305)) by @ruthwikdasyam
- Fix `uv sync` for some macOS systems ([#1322](#1322)) by @jeff-hykin
- Fix mmcv install ([#1313](#1313)) by @paul-nechifor
- Fix mypy issues ([#1150](#1150), [#1167](#1167), [#1257](#1257)) by @leshy, @paul-nechifor, @jeff-hykin
- Fix Nix install uv pip extras ([#1321](#1321)) by @spomichter

---

## 📚 Documentation

- **Major docs overhaul** — New README with feature grid, hardware table, quickstart. Navigation, transports, data streams, and agent docs. ([#1295](#1295)) by @leshy
- **Day 1 docs** — Comprehensive getting started guide, development docs, contributing guide, architecture overview. Executable blueprint docs via md-babel-py. ([#1064](#1064)) by @jeff-hykin
- **Arm integration guide** — How-to for integrating new robotic arms with DimOS. ([#1238](#1238)) by @mustafab0
- **MCP documentation update** — Updated MCP install and usage instructions. ([#1251](#1251)) by @Kaweees
- **Docker docs** — First pass on Docker deployment documentation. ([#1151](#1151)) by @leshy
- **Transports documentation** — Encode/decode mixins, SHM examples, ROS/DDS transport docs. ([#1107](#1107)) by @leshy
- **Rerun API examples** — Updated examples for the new RerunBridge API. ([#1262](#1262)) by @jeff-hykin
- **PR template** added ([#1172](#1172)) by @christiefhyang
- **Simplified install instructions** — Removed direnv, streamlined across all platforms. ([#1315](#1315)) by @spomichter
- **Python example restored** — Added back the Python usage example. ([#1317](#1317)) by @jeff-hykin
- **Nix install updated** — Replaced uv with pip for Nix compatibility. ([#1326](#1326)) by @ruthwikdasyam
- **README improvements** ([#1311](#1311)) by @paul-nechifor
- **Simplified writing docs** — Consolidated writing_docs to a single markdown file. ([#1254](#1254)) by @jeff-hykin

---

## 🏗️ CI & Build

- **ci-complete gate** — Dynamic branch protection via single aggregated status check. MD-only PRs no longer blocked. ([#1279](#1279)) by @spomichter
- **Path-based test filtering** — Test jobs fully skip (no container spin-up) when no relevant code changed. ([#1284](#1284), [#1286](#1286)) by @spomichter
- **Navigation docker build workflow** — CI builds for the ROS navigation stack. ([#1259](#1259)) by @spomichter
- **CUDA test marker** — `@pytest.mark.cuda` for GPU-dependent tests. ([#1220](#1220)) by @jeff-hykin
- **e2e test marker** — Marked end-to-end tests for selective CI runs. ([#1110](#1110)) by @paul-nechifor
- **pytest stdin fix** — Added `-s` to default addopts for LCM autoconf compatibility. ([#1320](#1320)) by @spomichter

---

## ⚠️ Breaking Changes

- **RPC renames**: `ModuleBlueprint` → `_BlueprintAtom`, `ModuleBlueprintSet` → `Blueprint`, `ModuleConnection` → `Stream` ([#1143](#1143))
- **Image class rewrite**: `CudaImage` and `NumpyImage` removed. Image is now a pure NumPy dataclass. Methods like `solve_pnp`, `csrt_tracker`, `from_depth`, `to_depth_meters` removed. ([#1161](#1161))
- **ROS messages removed from core**: All `to_ros`/`from_ros` conversion methods removed. Use `ROSTransport` instead. ([#1230](#1230))
- **QuestButtons → Buttons**: Renamed with generic field names. ([#1261](#1261))
- **RerunBridge replaces old rerun init**: `dimos.dashboard.rerun_init` removed. Use `RerunBridgeModule` or the `rerun-bridge` CLI. ([#1154](#1154))
- **Unitree directory restructuring**: `unitree_go2` → `unitree/go2`, `unitree_g1` → `unitree/g1`. Blueprint names updated. ([#1221](#1221))
- **Default viewer is now rerun-web**: Use `--viewer-backend rerun` to restore native viewer. ([#1324](#1324))

---

## Quickstart

```bash
# Install
uv pip install dimos[base,unitree]

# Try it (no hardware needed)
# NOTE: First run downloads ~2.4 GB from LFS
dimos --replay run unitree-go2

# Simulate
uv pip install dimos[base,unitree,sim]
dimos --simulation run unitree-go2
```

---

## New Contributors 🎉

- @ruthwikdasyam — Quest VR teleoperation, phone teleop, arm teleop, webcam rerun viz
- @JalajShuklaSS — GraspGen integration
- @jca0 — MuJoCo simulation module
- @christiefhyang — PR template

---

**Full Changelog**: [v0.0.9...v0.0.10](v0.0.9...v0.0.10)
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.

3 participants