feat: add Doom-style teleop control module (#1401)#1554
feat: add Doom-style teleop control module (#1401)#1554christiefhyang wants to merge 4 commits intodevfrom
Conversation
Greptile SummaryThis PR adds a new DOOM/FPS-style teleop module ( The
Confidence Score: 1/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
subgraph DoomTeleop["DoomTeleop Module"]
KB["Keyboard Input\n(W/S/A/D/Space)"]
MS["Mouse Input\n(Motion/Buttons)"]
PG["Pygame Loop\n(50 Hz)"]
KB --> PG
MS --> PG
end
subgraph Outputs["Published Messages"]
CV["cmd_vel\n(Twist)"]
GP["goal_pose\n(PoseStamped)"]
CG["cancel_goal\n(Bool)"]
end
subgraph Inputs["Subscriptions"]
OD["odom\n(PoseStamped)"]
end
PG -->|"Continuous velocity"| CV
PG -->|"RMB/MMB click"| GP
PG -->|"Space e-stop"| CG
OD -->|"Current pose for\nrelative goals"| PG
subgraph Blueprints["Blueprint Integration"]
G1["G1 Blueprint\n(with_joystick)"]
GO2["Go2 Blueprint\n(go2_with_doom)"]
end
CV --> G1
CV --> GO2
GP --> G1
GP --> GO2
Last reviewed commit: 985ab7e |
| from dimos.robot.unitree.connection.g1sim import g1_sim_connection | ||
| from dimos.robot.unitree_webrtc.keyboard_teleop import keyboard_teleop | ||
| from dimos.robot.unitree_webrtc.unitree_g1_skill_container import g1_skills | ||
| from dimos.utils.monitoring import utilization |
There was a problem hiding this comment.
Broken import — module path does not exist
The file dimos/robot/doom_teleop.py does not exist. The DoomTeleop module was added at dimos/teleop/keyboard/doom_teleop.py, so this import will raise ModuleNotFoundError at runtime.
| from dimos.utils.monitoring import utilization | |
| from dimos.teleop.keyboard.doom_teleop import doom_teleop |
The same issue is present in dimos/robot/unitree/go2/blueprints/unitree_go2_blueprints.py:66.
| from dimos.robot.unitree.connection.g1sim import g1_sim_connection | ||
| from dimos.robot.unitree_webrtc.keyboard_teleop import keyboard_teleop | ||
| from dimos.robot.unitree_webrtc.unitree_g1_skill_container import g1_skills |
There was a problem hiding this comment.
Broken imports — modules not found at these paths
These three imports reference dimos.robot.unitree_webrtc.*, but the actual modules live under different paths:
keyboard_teleopexists atdimos.robot.unitree.keyboard_teleop(seeunitree_g1_joystick.pyfor the correct import).keyboard_pose_teleopandunitree_g1_skill_containerdo not appear to exist anywhere in the codebase.
This file will fail to import entirely.
| from dimos.robot.unitree.connection.g1sim import g1_sim_connection | |
| from dimos.robot.unitree_webrtc.keyboard_teleop import keyboard_teleop | |
| from dimos.robot.unitree_webrtc.unitree_g1_skill_container import g1_skills | |
| from dimos.robot.unitree.keyboard_teleop import keyboard_teleop |
| foxglove_bridge(), | ||
| ) | ||
| .global_config(n_dask_workers=4, robot_model="unitree_g1") | ||
| .transports( |
There was a problem hiding this comment.
Wrong config key — n_dask_workers should be n_workers
GlobalConfig defines the field as n_workers (see dimos/core/global_config.py:38), and all existing blueprints in the repository use n_workers. The key n_dask_workers will be silently ignored (passed as **kwargs) or raise an error, meaning blueprints will fall back to the default of 2 workers instead of the intended count.
This affects every .global_config(n_dask_workers=...) call in both new blueprint files (G1 lines 92, 139, 144, 203; Go2 lines 101, 114, 170).
| .transports( | |
| .global_config(n_workers=4, robot_model="unitree_g1") |
| from dimos.robot.unitree.connection.go2 import GO2Connection, go2_connection | ||
| from dimos.robot.unitree_webrtc.unitree_skill_container import unitree_skills | ||
| from dimos.utils.monitoring import utilization | ||
| from dimos.web.websocket_vis.websocket_vis_module import websocket_vis |
There was a problem hiding this comment.
Broken import — module path does not exist
Same issue as in the G1 blueprint: dimos/robot/doom_teleop.py does not exist. The module lives at dimos/teleop/keyboard/doom_teleop.py.
| from dimos.web.websocket_vis.websocket_vis_module import websocket_vis | |
| from dimos.teleop.keyboard.doom_teleop import doom_teleop |
| ("world/robot/camera", "camera_optical", GO2Connection.camera_info_static), | ||
| ], | ||
| ), | ||
| ).global_config(n_dask_workers=4, robot_model="unitree_go2") |
There was a problem hiding this comment.
Wrong config key — n_dask_workers should be n_workers
GlobalConfig.n_workers is the correct field (see dimos/core/global_config.py:38). n_dask_workers is not a recognized config key. Every existing blueprint in the repo uses n_workers. This affects lines 101, 114, and 170 in this file.
| ).global_config(n_dask_workers=4, robot_model="unitree_go2") | |
| ).global_config(n_workers=4, robot_model="unitree_go2") |
This PR adds a Doom-style teleoperation module that lets both Unitree Go2 and G1 be driven with a shared WSAD + mouse control scheme, modeled after first-person shooter (FPS) controls. The DoomTeleop module is robot-agnostic and only uses standard navigation interfaces: continuous velocity via /cmd_vel and optional discrete pose goals via /goal_pose and /cancel_goal.
The module is then integrated into the updated Unitree Go2 and G1 blueprint architecture so that both robots can use the same keyboard plus mouse teleop without changing any existing transport topics.
Motivation:
Implementation Details:
DoomTeleop Module:
Keyboard control (continuous velocity)
• Maps keyboard input to Twist messages:
• W / S: move forward / backward
• A / D: turn left / right
• Space: emergency stop (publishes a zero Twist and clears internal input state)
• Publishes cmd_vel: Out[Twist], which is transported on the existing /cmd_vel channel in the blueprints.
• When the window loses focus, the module clears its input state and sends a zero Twist so the robot does not keep moving on a stuck key.
Mouse control (yaw & optional pose)
• Horizontal mouse motion adjusts yaw by adding to angular.z, providing an FPS-like look and turn behavior on top of keyboard turning.
• Optional discrete pose helpers when navigation goal topics are wired:
• Right-click: sends a small forward step goal (PoseStamped) in front of the current pose.
• Middle-click: sends a small in-place rotation goal around the current position.
• Uses the existing navigation goal interfaces:
• goal_pose: Out[PoseStamped] mapped to /goal_pose
• cancel_goal: Out[Bool] mapped to /cancel_goal
• Space also cancels any active goal when cancel_goal is available.
Robot-agnostic design
• DoomTeleop does not contain any Unitree- or robot-specific code.
• It only publishes Twist, PoseStamped and Bool on the standard navigation interfaces, and can be reused with any robot that exposes /cmd_vel, /goal_pose and /cancel_goal in its transports.
Lifecycle and threading
• Uses a background thread plus pygame to handle keyboard and mouse events asynchronously so teleop input does not block the rest of the system.
• start and stop follow the existing Module lifecycle:
• start calls super().start and spawns the input thread.
• stop stops the thread, publishes a final zero Twist and cancels any active goal.
Go2 Blueprint Integration
• Adds a go2_with_doom blueprint that composes the existing Unitree Go2 base navigation stack with doom_teleop.
• Reuses the existing Go2 transports for:
• /cmd_vel for velocity commands
• /odom and navigation goal topics where available for optional pose goals
• No changes are made to existing Go2 blueprints; go2_with_doom is an additive configuration.
G1 Blueprint Integration
• Integrates doom_teleop into the updated modular G1 blueprint structure so it can be used alongside the existing joystick and keyboard teleop options.
• Wires DoomTeleop only through the established /cmd_vel and navigation goal interfaces (goal_pose, cancel_goal, odom), matching the existing G1 navigation stack.
• Existing G1 blueprints remain available and unchanged; Doom teleop is exposed as an additional configuration that can be selected when WSAD + mouse control is desired.
Testing
Simulation (MuJoCo)
Environment: Ubuntu or WSL with MuJoCo support.
Commands
Install dependencies:
Go2
G1
Verified
IRL Test Plan (to be run by maintainers)
Robot
Command
Go2
G1
Environment
Open indoor area with conservative speed limits on both the robot and the teleop module.
Checkpoints
Breaking Changes
None. This PR introduces a new module that integrates with existing interfaces without modifying or breaking any existing control paths.
Contributor License Agreement