Skip to content

feat: add Doom-style teleop control module (#1401) - #1554

Closed
christiefhyang wants to merge 32 commits into
legacy-dev-dont-mergefrom
christie/feat-doom-control
Closed

christiefhyang wants to merge 32 commits into
legacy-dev-dont-mergefrom
christie/feat-doom-control

Conversation

@christiefhyang

@christiefhyang christiefhyang commented Mar 14, 2026 •

Copy link
Copy Markdown
Member

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:

  • Provide a more natural, FPS-like teleop experience that combines keyboard and mouse, instead of pure keyboard-only teleop.
  • Unify teleop behavior across Go2 and G1 so that users can reuse the same mental model and input mapping on both platforms.
  • Keep the implementation robot-agnostic, exposing only standard navigation interfaces so it can be reused in future blueprints without further changes.

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 with MuJoCo support.

Commands

Install dependencies:

uv sync --extra dev --extra cpu --extra sim

Go2

uv run dimos --simulation run unitree-go2 --doom_teleop

G1

uv run dimos --simulation run unitree-g1-joystick --doom_teleop

Verified

  • WSAD + mouse control the robot as expected
    • W / S: move forward / backward
    • A / D: turn left / right
    • Mouse motion: adds yaw to robot movement
  • Space and the mouse-based emergency behavior immediately stop the robot and clear motion commands.
  • When the teleop window loses focus, the module stops publishing motion commands so the robot does not continue moving on a stuck key.

IRL Test Plan (to be run by maintainers)

Robot

  • Unitree Go2 and/or G1

Command

Go2

dimos run unitree-go2 --doom_teleop

G1

dimos run unitree-g1-joystick --doom_teleop

Environment

Open indoor area with conservative speed limits on both the robot and the teleop module.


Checkpoints

  • Smooth start, turning, and stopping.
  • Emergency stop (Space / mouse) works immediately in any state.
  • No stuck-key behavior: releasing keys or losing window focus stops the robot instead of continuing to accelerate.

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

  • I have read and approved the CLA.

@christiefhyang
christiefhyang marked this pull request as ready for review March 14, 2026 08:26
@greptile-apps

greptile-apps Bot commented Mar 14, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a new DoomTeleop module that provides FPS-style WSAD + mouse teleoperation, and integrates it into new monolithic blueprint files for both the Unitree Go2 and G1 robots. The implementation correctly addresses several issues from prior review rounds (import paths, missing imports, config key names).

  • dimos/teleop/keyboard/doom_teleop.py — new DoomTeleop module with pygame-based keyboard/mouse input loop, lifecycle via start/stop, and optional odom/goal-pose integration.
  • dimos/robot/unitree/go2/blueprints/unitree_go2_blueprints.py and dimos/robot/unitree/g1/blueprints/unitree_g1_blueprints.py — new monolithic blueprint files composing all Go2/G1 configurations; two broken imports from dimos.robot.unitree_webrtc.* in the G1 file remain from prior review rounds (not repeated here).

Confidence Score: 3/5

Not safe to merge as-is: a top-level pygame import in doom_teleop.py will prevent the entire Go2 and G1 blueprint files from loading in any environment without pygame, and stop() can skip the base-class teardown when called before start().

The unconditional top-level import pygame in doom_teleop.py is pulled in by both new monolithic blueprint files at import time, meaning any blueprint in those files — standard, nav, agentic — silently breaks when pygame is absent. Additionally, stop() raises a RuntimeError before reaching super().stop(), leaving the module in an unclean state if cleanup is triggered before the thread is ever spawned.

dimos/teleop/keyboard/doom_teleop.py (pygame import strategy and stop() lifecycle), dimos/robot/unitree/g1/blueprints/unitree_g1_blueprints.py (still has two broken imports from dimos.robot.unitree_webrtc.* flagged in prior rounds, plus the with_joystick naming issue)

Important Files Changed

Filename Overview
dimos/teleop/keyboard/doom_teleop.py New DoomTeleop module for FPS-style WSAD+mouse teleop; unconditional top-level import pygame will break importing blueprint files that depend on this module when pygame is not installed.
dimos/robot/unitree/go2/blueprints/unitree_go2_blueprints.py New monolithic Go2 blueprint file; correctly imports doom_teleop and names the new blueprint go2_with_doom, but the top-level doom_teleop import drags pygame into every blueprint in this file.
dimos/robot/unitree/g1/blueprints/unitree_g1_blueprints.py New monolithic G1 blueprint file; has two broken imports from non-existent dimos.robot.unitree_webrtc.*, misleadingly names the doom_teleop blueprint with_joystick, and suffers from the same unconditional pygame import problem.

Sequence Diagram

sequenceDiagram
    participant User as User (keyboard/mouse)
    participant DoomTeleop
    participant pygame as pygame loop (bg thread)
    participant CmdVel as /cmd_vel (Out[Twist])
    participant GoalPose as /goal_pose (Out[PoseStamped])
    participant CancelGoal as /cancel_goal (Out[Bool])
    participant Odom as /odom (In[PoseStamped])

    DoomTeleop->>pygame: start() → spawn thread
    Odom-->>DoomTeleop: _on_odom() updates _current_pose

    loop 50 Hz
        User->>pygame: WSAD / mouse motion
        pygame->>CmdVel: publish(Twist)
    end

    User->>pygame: Right-click
    pygame->>GoalPose: publish(PoseStamped) [step forward]

    User->>pygame: Middle-click
    pygame->>GoalPose: publish(PoseStamped) [rotate in place]

    User->>pygame: Space / focus lost
    pygame->>CmdVel: publish(zero Twist)
    pygame->>CancelGoal: "publish(Bool=True)"

    DoomTeleop->>pygame: stop() → set stop event
    pygame->>CmdVel: publish(zero Twist)
    pygame->>CancelGoal: "publish(Bool=True)"
Loading

Reviews (3): Last reviewed commit: "Fix Doom teleop blueprint imports" | Re-trigger Greptile

Comment thread dimos/robot/unitree/g1/blueprints/unitree_g1_blueprints.py
Comment thread dimos/robot/unitree/g1/blueprints/unitree_g1_blueprints.py
foxglove_bridge(),
)
.global_config(n_dask_workers=4, robot_model="unitree_g1")
.transports(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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).

Suggested change
.transports(
.global_config(n_workers=4, robot_model="unitree_g1")

Comment thread dimos/robot/unitree/go2/blueprints/unitree_go2_blueprints.py
("world/robot/camera", "camera_optical", GO2Connection.camera_info_static),
],
),
).global_config(n_dask_workers=4, robot_model="unitree_go2")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Suggested change
).global_config(n_dask_workers=4, robot_model="unitree_go2")
).global_config(n_workers=4, robot_model="unitree_go2")

Comment thread dimos/robot/unitree/g1/blueprints/unitree_g1_blueprints.py Outdated
Comment thread dimos/teleop/keyboard/doom_teleop.py Outdated
Refactor Unitree G1 blueprint configurations to simplify connections and improve structure.
This module restores public blueprint names for the Unitree Go2, mapping legacy names to current modules. Several imports and configurations have been updated or removed.

@christiefhyang christiefhyang left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Updated file path

Comment thread dimos/teleop/keyboard/doom_teleop.py Outdated
# ESC quits
self._stop_event.set()

keys = pygame.key.get_pressed()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@Dreamsorcerer I switched DoomTeleop over to use pygame.key.get_pressed() for WASD instead of maintaining our own key set, while still keeping the existing focus-loss handling. This should avoid the gotchas you mentioned around tracking pressed keys manually.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Think you reverted it again?
310939c

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes I will update this soon. Encountered some issues with controls related to middle mouse button and right click.

@ruthwikdasyam
ruthwikdasyam force-pushed the christie/feat-doom-control branch from da7519f to 66c783e Compare March 22, 2026 00:24
This module provides pre-configured blueprints for various G1 robot setups, enhancing functionality and integration with additional components.
@christiefhyang

Copy link
Copy Markdown
Member Author

This update primarily fixes the interaction conflicts between “map click-to-goal” and “keyboard teleop” in the Command Center. The changes are focused on the actual web control pipeline, rather than the keyboard_teleop.py module itself.

Scope of Fixes
Updated frontend interaction logic in dimos/web/command-center-extension
Added backend support for cancel_goal events in dimos/web/websocket_vis/websocket_vis_module.py

Issues Resolved
Fixed the issue of “modifying the wrong file”: changes are now correctly applied to the Command Center / WebSocket control pipeline instead of mistakenly modifying keyboard_teleop.py
Fixed the issue where right-click and middle-click had no effect
Fixed the issue where users could still click to set a goal while teleop was active, causing conflicts between control modes

Behavior Changes
Left-click on map:
Only sends navigation goals when not in keyboard teleop mode
Right-click on map:
Sends cancel_goal to cancel the current navigation target
Middle-click on map:
Sends a stop teleop command, immediately zeroing velocity commands
Keyboard teleop state:
Frontend now synchronizes activation/deactivation state with upper layers
Automatically disables click-to-goal when teleop is active to prevent control conflicts

Main Modified Files
dimos/web/websocket_vis/websocket_vis_module.py
Added cancel_goal: Out[Bool]
Added cancel_goal socket event
dimos/web/command-center-extension/src/Connection.ts
Added cancelGoal()
dimos/web/command-center-extension/src/KeyboardControlPanel.tsx
Added onActiveChange
Syncs keyboard teleop state to upper layers
dimos/web/command-center-extension/src/components/VisualizerWrapper.tsx

Differentiates interactions by mouse button:
Left-click: set goal (only in goal mode)
Middle-click: stop teleop
Right-click: cancel goal
Disables browser default right-click menu
dimos/web/command-center-extension/src/App.tsx
Added isKeyboardTeleopActive
Disables map click-to-goal based on teleop state

@christiefhyang
christiefhyang marked this pull request as draft May 8, 2026 04:02
basic = autoconnect(
go2_connection(),
linux if platform.system() == "Linux" else mac,
websocket_vis(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Missing websocket_vis import — NameError at runtime

websocket_vis() is called on line 87 inside the basic blueprint, but websocket_vis is never imported in this file. Every attempt to instantiate any blueprint that includes basic (e.g. go2_with_doom, nav, agentic) will raise NameError: name 'websocket_vis' is not defined. The G1 blueprint file imports it correctly via from dimos.web.websocket_vis.websocket_vis_module import websocket_vis.

_perception_and_memory = autoconnect(
spatial_memory(),
object_tracking(frame_id="camera_link"),
utilization(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Missing utilization import — NameError at runtime

utilization() is called on line 124 inside _perception_and_memory, but it is never imported in this file. Any blueprint that composes _perception_and_memory (i.e. standard, standard_sim, agentic, agentic_sim) will raise NameError: name 'utilization' is not defined at module load time. The Go2 blueprint file correctly imports it via from dimos.utils.monitoring import utilization.

@codecov

codecov Bot commented May 8, 2026 •

Copy link
Copy Markdown

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
1866 1 1865 59
View the top 1 failed test(s) by shortest run time
dimos.robot.test_all_blueprints_generation::test_all_blueprints_is_current
Stack Traces | 2.89s run time
def test_all_blueprints_is_current() -> None:
        root = DIMOS_PROJECT_ROOT / "dimos"
        all_blueprints, all_modules = _scan_for_blueprints(root)
    
        common = set(all_blueprints.keys()) & set(all_modules.keys())
        assert not common, (
            f"Names must be unique across blueprints and modules, "
            f"but these appear in both: {sorted(common)}"
        )
    
        generated_content = _generate_all_blueprints_content(all_blueprints, all_modules)
    
        file_path = root / "robot" / "all_blueprints.py"
    
        if "CI" in os.environ:
            if not file_path.exists():
                pytest.fail(f"all_blueprints.py does not exist at {file_path}")
    
            current_content = file_path.read_text()
            if current_content != generated_content:
                diff = difflib.unified_diff(
                    current_content.splitlines(keepends=True),
                    generated_content.splitlines(keepends=True),
                    fromfile="all_blueprints.py (current)",
                    tofile="all_blueprints.py (generated)",
                )
                diff_str = "".join(diff)
>               pytest.fail(
                    f"all_blueprints.py is out of date. Run "
                    f"`pytest dimos/robot/test_all_blueprints_generation.py` locally to update.\n\n"
                    f"Diff:\n{diff_str}"
                )
E               Failed: all_blueprints.py is out of date. Run `pytest dimos/robot/test_all_blueprints_generation.py` locally to update.
E               
E               Diff:
E               --- all_blueprints.py (current)
E               +++ all_blueprints.py (generated)
E               @@ -16,6 +16,14 @@
E                # Run `pytest dimos/robot/test_all_blueprints_generation.py` to regenerate.
E                
E                all_blueprints = {
E               +    "agentic": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:agentic",
E               +    "agentic-huggingface": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:agentic_huggingface",
E               +    "agentic-mcp": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:agentic_mcp",
E               +    "agentic-ollama": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:agentic_ollama",
E               +    "agentic-sim": "dimos.robot.unitree.g1.blueprints.unitree_g1_blueprints:agentic_sim",
E               +    "basic": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:basic",
E               +    "basic-ros": "dimos.robot.unitree.g1.blueprints.unitree_g1_blueprints:basic_ros",
E               +    "basic-sim": "dimos.robot.unitree.g1.blueprints.unitree_g1_blueprints:basic_sim",
E                    "coordinator-basic": "dimos.control.blueprints.basic:coordinator_basic",
E                    "coordinator-cartesian-ik-mock": "dimos.control.blueprints.teleop:coordinator_cartesian_ik_mock",
E                    "coordinator-cartesian-ik-piper": "dimos.control.blueprints.teleop:coordinator_cartesian_ik_piper",
E               @@ -50,20 +58,31 @@
E                    "demo-object-scene-registration": "dimos.perception.demo_object_scene_registration:demo_object_scene_registration",
E                    "demo-osm": "dimos.mapping.osm.demo_osm:demo_osm",
E                    "demo-skill": "dimos.agents.skills.demo_skill:demo_skill",
E               +    "detection": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:detection",
E                    "drone-agentic": "dimos.robot.drone.blueprints.agentic.drone_agentic:drone_agentic",
E                    "drone-basic": "dimos.robot.drone.blueprints.basic.drone_basic:drone_basic",
E                    "dual-xarm6-planner": "dimos.manipulation.blueprints:dual_xarm6_planner",
E               +    "full-featured": "dimos.robot.unitree.g1.blueprints.unitree_g1_blueprints:full_featured",
E               +    "go2-with-doom": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:go2_with_doom",
E                    "keyboard-teleop-openarm": "dimos.robot.manipulators.openarm.blueprints:keyboard_teleop_openarm",
E                    "keyboard-teleop-openarm-mock": "dimos.robot.manipulators.openarm.blueprints:keyboard_teleop_openarm_mock",
E                    "keyboard-teleop-piper": "dimos.robot.manipulators.piper.blueprints:keyboard_teleop_piper",
E                    "keyboard-teleop-xarm6": "dimos.robot.manipulators.xarm.blueprints:keyboard_teleop_xarm6",
E                    "keyboard-teleop-xarm7": "dimos.robot.manipulators.xarm.blueprints:keyboard_teleop_xarm7",
E               +    "linux": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:linux",
E               +    "mac": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:mac",
E                    "mid360": "dimos.hardware.sensors.lidar.livox.livox_blueprints:mid360",
E                    "mid360-fastlio": "dimos.hardware.sensors.lidar.fastlio2.fastlio_blueprints:mid360_fastlio",
E                    "mid360-fastlio-voxels": "dimos.hardware.sensors.lidar.fastlio2.fastlio_blueprints:mid360_fastlio_voxels",
E                    "mid360-fastlio-voxels-native": "dimos.hardware.sensors.lidar.fastlio2.fastlio_blueprints:mid360_fastlio_voxels_native",
E               +    "nav": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:nav",
E                    "openarm-mock-planner-coordinator": "dimos.robot.manipulators.openarm.blueprints:openarm_mock_planner_coordinator",
E                    "openarm-planner-coordinator": "dimos.robot.manipulators.openarm.blueprints:openarm_planner_coordinator",
E               +    "ros": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:ros",
E               +    "spatial": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:spatial",
E               +    "standard": "dimos.robot.unitree.g1.blueprints.unitree_g1_blueprints:standard",
E               +    "standard-sim": "dimos.robot.unitree.g1.blueprints.unitree_g1_blueprints:standard_sim",
E               +    "standard-with-shm": "dimos.robot.unitree.g1.blueprints.unitree_g1_blueprints:standard_with_shm",
E                    "teleop-phone": "dimos.teleop.phone.blueprints:teleop_phone",
E                    "teleop-phone-go2": "dimos.teleop.phone.blueprints:teleop_phone_go2",
E                    "teleop-phone-go2-fleet": "dimos.teleop.phone.blueprints:teleop_phone_go2_fleet",
E               @@ -72,6 +91,7 @@
E                    "teleop-quest-rerun": "dimos.teleop.quest.blueprints:teleop_quest_rerun",
E                    "teleop-quest-xarm6": "dimos.teleop.quest.blueprints:teleop_quest_xarm6",
E                    "teleop-quest-xarm7": "dimos.teleop.quest.blueprints:teleop_quest_xarm7",
E               +    "temporal-memory": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:temporal_memory",
E                    "uintree-g1-primitive-no-nav": "dimos.robot.unitree.g1.blueprints.primitive.uintree_g1_primitive_no_nav:uintree_g1_primitive_no_nav",
E                    "unitree-g1": "dimos.robot.unitree.g1.blueprints.perceptive.unitree_g1:unitree_g1",
E                    "unitree-g1-agentic": "dimos.robot.unitree.g1.blueprints.agentic.unitree_g1_agentic:unitree_g1_agentic",
E               @@ -102,6 +122,10 @@
E                    "unitree-go2-webrtc-keyboard-teleop": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_webrtc_keyboard_teleop:unitree_go2_webrtc_keyboard_teleop",
E                    "unitree-go2-webrtc-rage-keyboard-teleop": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_webrtc_rage_keyboard_teleop:unitree_go2_webrtc_rage_keyboard_teleop",
E                    "unity-sim": "dimos.simulation.unity.blueprint:unity_sim",
E               +    "vlm-stream-test": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:vlm_stream_test",
E               +    "with-joystick": "dimos.robot.unitree.g1.blueprints.unitree_g1_blueprints:with_joystick",
E               +    "with-jpeglcm": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:with_jpeglcm",
E               +    "with-jpegshm": "dimos.robot.unitree.go2.blueprints.unitree_go2_blueprints:with_jpegshm",
E                    "xarm-perception": "dimos.manipulation.blueprints:xarm_perception",
E                    "xarm-perception-agent": "dimos.manipulation.blueprints:xarm_perception_agent",
E                    "xarm-perception-sim": "dimos.manipulation.blueprints:xarm_perception_sim",
E               @@ -124,6 +148,7 @@
E                    "demo-robot": "dimos.agents.skills.demo_robot.DemoRobot",
E                    "detection2-d-module": "dimos.perception.detection.module2D.Detection2DModule",
E                    "detection3-d-module": "dimos.perception.detection.module3D.Detection3DModule",
E               +    "doom-teleop": "dimos.teleop.keyboard.doom_teleop.DoomTeleop",
E                    "drone-camera-module": "dimos.robot.drone.camera_module.DroneCameraModule",
E                    "drone-connection-module": "dimos.robot.drone.connection_module.DroneConnectionModule",
E                    "drone-tracking-module": "dimos.robot.drone.drone_tracking_module.DroneTrackingModule",
E               @@ -183,7 +208,6 @@
E                    "simple-phone-teleop": "dimos.teleop.phone.phone_extensions.SimplePhoneTeleop",
E                    "spatial-memory": "dimos.perception.spatial_perception.SpatialMemory",
E                    "speak-skill": "dimos.agents.skills.speak_skill.SpeakSkill",
E               -    "temporal-memory": "dimos.perception.experimental.temporal_memory.temporal_memory.TemporalMemory",
E                    "twist-teleop-module": "dimos.teleop.quest.quest_extensions.TwistTeleopModule",
E                    "unitree-g1-skill-container": "dimos.robot.unitree.g1.skill_container.UnitreeG1SkillContainer",
E                    "unitree-skill-container": "dimos.robot.unitree.unitree_skill_container.UnitreeSkillContainer",

dimos/robot/test_all_blueprints_generation.py:66: Failed

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@christiefhyang

Copy link
Copy Markdown
Member Author

Addressed the current review comments:

  • Added the missing websocket_vis import in the Go2 blueprint file.
  • Added the missing utilization import in the G1 blueprint file.
  • Removed the stale dimos.robot.doom_teleop imports and kept the current dimos.teleop.keyboard.doom_teleop import.
  • Replaced n_dask_workers with n_workers in the new G1 and Go2 blueprint global_config calls.

Validation:
python -m py_compile dimos/robot/unitree/go2/blueprints/unitree_go2_blueprints.py dimos/robot/unitree/g1/blueprints/unitree_g1_blueprints.py

Note: ruff was not available in the local Python environment.

@christiefhyang
christiefhyang marked this pull request as ready for review June 4, 2026 11:53
import os
import threading

import pygame

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Unconditional top-level import pygame breaks all non-doom blueprints when pygame is absent

Both unitree_go2_blueprints.py and unitree_g1_blueprints.py import doom_teleop at module level, so any process that loads either file — even to run a completely unrelated blueprint like standard, nav, or agentic — will raise ImportError if pygame is not installed. The established pattern in this codebase (see keyboard_teleop_module.py and b1/joystick_module.py) is a guarded try/except ImportError or a deferred per-method import, precisely to avoid this. Moving the import pygame inside _pygame_loop (or wrapping it in a try/except at module level and raising a friendly error in start()) would contain the dependency to callers that actually invoke the doom teleop loop.

@paul-nechifor

Copy link
Copy Markdown
Contributor

Closed this old PR due to inactivity. Can be reopened later.

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