teleop-xr
A Generic Framework to Teleoperate Robots in Augmented Reality (AR) using WebXR
Science Score: 44.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
✓CITATION.cff file
Found CITATION.cff file -
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Academic email domains
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (11.3%) to scientific vocabulary
Keywords
Repository
A Generic Framework to Teleoperate Robots in Augmented Reality (AR) using WebXR
Basic Info
Statistics
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
- Releases: 0
Topics
Metadata Files
README.md
TeleOp XR
A system for teleoperating robots in augmented reality (AR) using WebXR.
https://github.com/user-attachments/assets/d8a7df72-8b94-4e30-ba3d-987ea13ac2df
Overview
TeleOp XR enables users to visualize and control robots in AR directly from a browser. The system consists of:
- A FastAPI server that serves robot data, configuration, and a real-time control API
- A Three.js frontend for AR visualization, loaded from the backend
- Modular backend for different robot models (URDF), meshes, and configuration
- Dynamic AR menu system: The frontend can render a menu in AR based on the robot config. Each menu item is a button that, when clicked, triggers a callback in the robot backend to perform actions such as recording data, starting/stopping tasks, or custom robot-specific commands. The menu is defined in the robot's config file and can be customized before starting the server.
Users can: - Discover and select available robots - Visualize robots in AR using WebXR-compatible browsers and control the robot in AR - View live camera feeds (if available) - Record robot teleoperation episodes to collect training data - Interact with a dynamic menu in AR to trigger robot-specific actions
Architecture
The backend is modular and supports multiple robot types through a plugin-like architecture. Each robot configuration JSON file specifies a backend_type field, which tells the server which backend implementation to use for that robot. This allows you to add support for new robots without modifying the core server code.
- backend_type: A string in the robot config (e.g.,
"lerobot/so100") that points to the backend implementation module. - At runtime, the server loads the correct backend class based on this field and uses it to communicate with the robot hardware.
- Fast, low-latency communication: The system uses Protocol Buffers (protobuf) for efficient binary serialization over a WebSocket channel, enabling real-time robot control and feedback.
- Joints in a robot are grouped into
joint_groups, which can be controlled together. This is useful to support robots with multiple limbs (e.g., humanoids). - Dynamic AR menu: The
menufield in the robot config allows you to define custom actions that appear as buttons in the AR interface. When a button is clicked, the backend executes the corresponding command via a callback.
Example: SO100 Robot
- See
data/robots/so100.json:json { "name": "SO100", ... "backend_type": "lerobot/so100", ... "menu": [ { "name": "Record Episode", "description": "Record an episode", "command": "record_episode", "config": { "task": "test task", "repo_id": "lerobot/test", "fps": 30, "root": "/Users/<your-username>/.cache/huggingface/lerobot/test" } }, { "name": "Stop Recording", "description": "Stop recording an episode", "command": "stop_record_episode" } ], ... } - The backend implementation is in
server/robots/lerobot/so100.py, which defines aSo100class for this robot and supports a single so100 arm. - All robot backends inherit from
RobotInterface, which defines the required methods for connecting, reading, and commanding robots, as well as handling menu actions.
Dynamic AR Menu System
- The
menufield in the robot config is an array of menu items. Each item defines a button that appears in the AR UI. - Each menu item can have:
name: The button labeldescription: Tooltip or help textcommand: The action to trigger in the backendconfig: (optional) Additional parameters for the command
- When a button is clicked in AR, the backend receives the command and executes the corresponding callback. You can customize the menu for each robot by editing the config file before starting the server.
How to Add a New Robot Backend
- Implement a subclass of
RobotInterface- Create a new Python file in
server/robots/<your_backend>/(e.g.,server/robots/myrobot/myrobot.py). - Implement all required methods:
connect,disconnect,get_joint_state,set_joint_position,get_all_joint_states,set_all_joint_positions, etc. - Menu actions: Implement callbacks for any custom commands you want to expose in the AR menu. The backend should handle commands defined in the
menufield of the robot config.
- Create a new Python file in
- Reference your backend in the robot config
- In your robot's JSON config (in
data/robots/), set thebackend_typefield to match your module path (e.g.,"myrobot/myrobot"). - Define the AR menu: Add a
menufield to your config to specify which actions should be available in the AR UI. See the example above. - Modify the menu as needed: Before starting the server, edit the
menufield to match the actions you want to expose for your robot.
- In your robot's JSON config (in
- Restart the server
- The server will automatically load your backend for any robot with the matching
backend_typeand render the menu in AR based on the config.
- The server will automatically load your backend for any robot with the matching
This architecture makes it easy to extend TeleOp XR to new robot platforms—just implement the backend interface, update the config, and define your custom AR menu!
System Requirements
- Backend: Python 3.9+
- Frontend: Modern browser with WebXR and WebGL support (e.g., Chrome, Meta Quest Browser)
- Certificates: Self-signed or trusted certs for HTTPS (required for WebXR)
Getting Started
1. Backend Setup
- Create and activate a Python virtual environment:
bash
python -m venv .venv
source .venv/bin/activate
Note: If you are using the
lerobot/so100backend, you should use the same virtual environment as your LeRobot installation to use lerobot dependencies.
- Install dependencies (choose one):
- Using uv (recommended for speed):
bash uv pip install -e . - Or using pip:
bash pip install -e .
2. Certificates
WebXR requires HTTPS. Generate self-signed certificates (if you don't have them):
bash
./gen_certs.sh
This creates certs/cert.pem and certs/key.pem.
3. Configure Your Robot
Before you can control a robot, you must either modify an existing robot config file or add a new one in data/robots/ to match your hardware setup.
Example: Editing so100.json
Open data/robots/so100.json and update the following fields as needed:
backend_calibration_dir: Set this to the path where your robot's calibration data is stored.motor_id(in each joint): Update these to match the IDs assigned to your robot's motors.backend_config.port: Set this to the serial port or device path your robot is connected to (e.g.,/dev/ttyUSB0or/dev/tty.usbmodemXXXX).menu: Edit this array to customize the AR menu for your robot. Each item defines a button in the AR UI. You can add, remove, or modify menu items to match the actions you want to expose. For example, you might add a button to start/stop data recording, trigger a calibration routine, or run a custom script. The backend must implement the corresponding callbacks for each command.
Example snippet:
json
{
"name": "SO100",
...
"backend_calibration_dir": "/path/to/your/calibration/dir/",
...
"menu": [
{
"name": "Record Episode",
"description": "Record an episode",
"command": "record_episode",
"config": {
"task": "test task",
"repo_id": "lerobot/test",
"fps": 30,
"root": "/path/to/your/data/root"
}
},
{
"name": "Stop Recording",
"description": "Stop recording an episode",
"command": "stop_record_episode"
}
],
...
"joint_groups": [
{
...
"joints": [
{ "name": "Rotation", "motor_id": 7, ... },
{ "name": "Pitch", "motor_id": 8, ... },
...
],
"backend_config": {
"name": "main",
"port": "/dev/tty.usbmodemXXXX"
}
}
]
}
Note: These fields must match your actual hardware configuration for the system to work correctly. You should also review and update the
menufield to ensure the AR menu matches the actions you want to provide.
You can also add new robot config files in data/robots/ for additional robots, following the same structure.
4. Running the Server
Start the backend (serves both API and frontend):
bash
python run.py
- By default, runs on
https://<your-ip>:8000 - Data directories default to
./data/(see below for customization)
Custom Data Directories
You can specify custom locations for robot configs, URDFs, and meshes:
bash
python run.py --data-dir /path/to/data
python run.py --robots-dir /path/to/robots --urdf-dir /path/to/urdf --meshes-dir /path/to/meshes
5. Accessing the Frontend
Open your browser and navigate to:
https://<your-ip>:8000
- The main interface is
index.html.
Note: The first time you open the URL, your browser will warn you about the self-signed certificate. You must accept this warning to proceed. This is required for WebXR to work over HTTPS. (In Chrome, click "Advanced" and then "Proceed to
(unsafe)")
Project Structure
teleop-xr/
├── server/ # FastAPI backend and static frontend
│ ├── app.py # FastAPI app
│ ├── static/ # Frontend (HTML, JS, CSS)
│ │ ├── index.html # Main UI
│ │ ├── js/ # JS modules (Three.js, app logic, protobuf, etc.)
│ │ └── ...
│ ├── routes/ # API and WebSocket endpoints
│ ├── robots/ # Robot control logic (modular backends)
│ ├── models/ # Data models
│ └── ...
├── data/ # Robot data
│ ├── urdf/ # URDF robot models
│ ├── meshes/ # 3D mesh files (STL, OBJ, etc.)
│ └── robots/ # Robot configuration JSON files
├── common/ # Shared protocol buffer definitions
│ └── transport.proto
├── run.py # Backend startup script
├── gen_certs.sh # Script to generate self-signed certs
└── ...
Data Directory Structure
- data/urdf/: URDF files describing robot kinematics
- data/meshes/: 3D mesh files referenced by URDFs
- data/robots/: JSON configuration files for each robot
Communication Protocol
- REST API (
/api/robots):- Discover available robots
- Get robot configuration and joint info
- Set joint positions
- WebSocket (
/ws/teleop):- Real-time robot state and control
- Uses Protocol Buffers (protobuf) for binary serialization, providing fast, low-latency data exchange between frontend and backend (see
common/transport.proto)
Development
- Backend: Python (FastAPI, protobuf)
- Frontend: Static HTML/JS (Three.js, protobuf)
- Edit frontend in
server/static/and backend inserver/
Hot Reload (Backend)
The backend runs with --reload by default for development. Restart the server if you change protocol buffer definitions or data models.
Robot Configuration
Each robot is defined by a JSON file in data/robots/. Example fields:
json
{
"name": "SO100 Arm",
"menu": [
{
"name": "Record Episode",
"description": "Record an episode",
"command": "record_episode",
"config": {
"episode_name": "episode_1"
}
},
{
"name": "Stop Recording",
"description": "Stop recording an episode",
"command": "stop_record_episode"
}
],
"joint_groups": [
{
"name": "arm",
"urdf_path": "/urdf/so100.urdf",
"joints": [
{ "name": "shoulder", "initial_angle": 0.0 },
{ "name": "elbow", "initial_angle": 0.0 }
]
}
]
}
- menu: (optional) Array of menu items to render as buttons in the AR UI. Each item should have a
name,description,command, and optionalconfigobject. The backend must implement the logic for each command. - joint_groups: Array of joint group definitions.
Tip: Edit the
menufield in your robot config before starting the server to customize the AR menu for your use case.
Contributing
We welcome contributions! Please open an issue or submit a pull request.
Owner
- Name: Ajay Kumar Chintala
- Login: ajhai
- Kind: user
- Location: Sunnyvale, CA
- Repositories: 4
- Profile: https://github.com/ajhai
Citation (CITATION.cff)
cff-version: 1.2.0
title: "teleop-xr: A system for teleoperating robots in augmented reality"
message: "If you use this software, please cite it as below."
type: software
authors:
- given-names: "Ajay Kumar"
family-names: "Chintala"
url: "https://github.com/ajhai/teleop-xr"
GitHub Events
Total
- Watch event: 2
- Push event: 2
- Create event: 2
Last Year
- Watch event: 2
- Push event: 2
- Create event: 2
Dependencies
- aiortc >=1.11.0
- av >=14.2.0
- fastapi >=0.104.0
- grpcio >=1.59.0
- numpy >=2.2.4
- opencv-python-headless >=4.11.0.86
- protobuf >=4.25.1
- pydantic >=2.4.2
- uvicorn [standard]>=0.23.2
- aioice 0.9.0
- aiortc 1.11.0
- annotated-types 0.7.0
- anyio 4.9.0
- av 14.2.0
- cffi 1.17.1
- click 8.1.8
- colorama 0.4.6
- cryptography 44.0.2
- dnspython 2.7.0
- exceptiongroup 1.2.2
- fastapi 0.115.12
- google-crc32c 1.7.1
- h11 0.14.0
- httptools 0.6.4
- idna 3.10
- ifaddr 0.2.0
- numpy 2.2.4
- opencv-python-headless 4.11.0.86
- pycparser 2.22
- pydantic 2.11.1
- pydantic-core 2.33.0
- pyee 13.0.0
- pylibsrtp 0.11.0
- pyopenssl 25.0.0
- python-dotenv 1.1.0
- pyyaml 6.0.2
- sniffio 1.3.1
- starlette 0.46.1
- teleop-xr 0.1.0
- typing-extensions 4.13.0
- typing-inspection 0.4.0
- uvicorn 0.34.0
- uvloop 0.21.0
- watchfiles 1.0.4
- websockets 15.0.1