Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
957c26e
switch to websocket
jeff-hykin Mar 21, 2026
f5a35bb
cleanup
jeff-hykin Mar 21, 2026
e67ae72
improvements
jeff-hykin Mar 21, 2026
b7bfb40
fix: ruff formatting + consistent error handling in websocket_server
jeff-hykin Mar 21, 2026
7cbe1b7
make it easy to use
jeff-hykin Mar 21, 2026
fa94c2e
cleanup
jeff-hykin Mar 21, 2026
42f2f38
consolidate viewer usage
jeff-hykin Mar 22, 2026
23d1d88
consolidate WebsocketVisModule
jeff-hykin Mar 22, 2026
dd5ff29
Merge branch 'dev' of github.com:dimensionalOS/dimos into jeff/fix/rc…
jeff-hykin Mar 22, 2026
27e66be
fix: address PR review - server ready race, path filter, skip guard
jeff-hykin Mar 22, 2026
fe84b4d
fix: set explicit ping interval/timeout on WebSocket server
jeff-hykin Mar 22, 2026
bbffeee
switch to websocket
jeff-hykin Mar 21, 2026
f99b2d4
cleanup
jeff-hykin Mar 21, 2026
26273d7
improvements
jeff-hykin Mar 21, 2026
62fb365
fix: ruff formatting + consistent error handling in websocket_server
jeff-hykin Mar 21, 2026
238b3de
make it easy to use
jeff-hykin Mar 21, 2026
5c14fe2
cleanup
jeff-hykin Mar 21, 2026
ab4daea
consolidate viewer usage
jeff-hykin Mar 22, 2026
dc5f2f8
consolidate WebsocketVisModule
jeff-hykin Mar 22, 2026
ba14725
fix: address PR review - server ready race, path filter, skip guard
jeff-hykin Mar 22, 2026
f670f12
fix: set explicit ping interval/timeout on WebSocket server
jeff-hykin Mar 22, 2026
7545a5a
fix(rerun-ws): log exception and unblock stop() on server startup fai…
jeff-hykin Mar 22, 2026
204d8b7
docs: add changes.md with fix descriptions and revert instructions
jeff-hykin Mar 22, 2026
2c39685
feat: vis_module helper + rerun bridge improvements
jeff-hykin Mar 30, 2026
7f063c6
Merge branch 'jeff/fix/rconnect' of github.com:dimensionalOS/dimos in…
jeff-hykin Mar 30, 2026
596d244
Merge remote-tracking branch 'origin/dev' into jeff/fix/rconnect
jeff-hykin Mar 30, 2026
3ecae4b
cleanup
jeff-hykin Mar 30, 2026
77e25eb
refine
jeff-hykin Mar 30, 2026
0a231d0
Merge branch 'dev' of github.com:dimensionalOS/dimos into jeff/fix/rc…
jeff-hykin Mar 30, 2026
3835a39
cleanup
jeff-hykin Mar 30, 2026
1c77b59
cleanup
jeff-hykin Mar 30, 2026
b1dcf0f
misc
jeff-hykin Mar 30, 2026
de37b65
Merge jeff/fix/vis_module: bridge improvements + vis_module consolida…
jeff-hykin Mar 30, 2026
26fa2fb
restoregraph
jeff-hykin Mar 30, 2026
2a81168
change name to be more generic
jeff-hykin Apr 2, 2026
85040f6
Merge branch 'dev' into jeff/fix/rconnect
jeff-hykin Apr 2, 2026
fb926c0
del
jeff-hykin Apr 2, 2026
d9007f0
Merge branch 'jeff/fix/rconnect' of github.com:dimensionalOS/dimos in…
jeff-hykin Apr 2, 2026
8e227ec
fix old Agent.blueprint()'s
jeff-hykin Apr 2, 2026
e01cf85
Merge remote-tracking branch 'origin/dev' into jeff/fix/rconnect
jeff-hykin Apr 2, 2026
e382113
fix(ci): regenerate all_blueprints.py, fix test_websocket_server stop…
jeff-hykin Apr 2, 2026
8e1d71a
Revered topics to cmd_vel to fix unitree go2 blueprints and fallback …
spomichter Apr 2, 2026
9f9d43d
Merge branch 'jeff/fix/rconnect' of https://github.com/dimensionalOS/…
spomichter Apr 2, 2026
032756e
new rerun cli options
jeff-hykin Apr 2, 2026
385313d
merge
jeff-hykin Apr 2, 2026
bf2e94c
CI code cleanup
jeff-hykin Apr 2, 2026
56a527f
add connect printout for headless
jeff-hykin Apr 2, 2026
af325dc
CI code cleanup
jeff-hykin Apr 2, 2026
8f9e8d8
Merge branch 'dev' of github.com:dimensionalOS/dimos into jeff/fix/rc…
jeff-hykin Apr 2, 2026
231d47b
restore name to tele_cmd_vel
jeff-hykin Apr 3, 2026
badc59d
clean _resolve_viewer_mode
jeff-hykin Apr 3, 2026
53c50a5
wire in proper cmd_vel mux and cancel for go2
jeff-hykin Apr 3, 2026
f9ef7bc
hide startup noise
jeff-hykin Apr 3, 2026
a775501
get rid of warning
jeff-hykin Apr 3, 2026
806acc9
fix sim on macos
jeff-hykin Apr 3, 2026
71ce2a9
fix warning
jeff-hykin Apr 3, 2026
0eed0b7
comment
jeff-hykin Apr 3, 2026
a90ac02
CI code cleanup
jeff-hykin Apr 3, 2026
9d2abd8
add test
jeff-hykin Apr 3, 2026
3513289
Merge branch 'jeff/fix/rconnect' of github.com:dimensionalOS/dimos in…
jeff-hykin Apr 3, 2026
50dcfeb
change how stop_movement works with cmd_vel
jeff-hykin Apr 3, 2026
cdd11ad
fix(mypy): resolve 7 type errors across 4 files
jeff-hykin Apr 3, 2026
0ee0c89
fix(ci): regenerate all_blueprints.py (add cmd-vel-mux)
jeff-hykin Apr 3, 2026
24ca9d8
cleanup
jeff-hykin Apr 5, 2026
cb9a307
Merge dev: resolve conflicts in rerun bridge and g1 blueprint
jeff-hykin Apr 6, 2026
1286606
fix: update blueprints import path in vis_module
jeff-hykin Apr 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dimos/core/docker_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from dimos.core.rpc_client import ModuleProxyProtocol, RpcCall
from dimos.protocol.rpc.pubsubrpc import LCMRPC
from dimos.utils.logging_config import setup_logger
from dimos.visualization.rerun.bridge import RERUN_GRPC_PORT, RERUN_WEB_PORT
from dimos.visualization.constants import RERUN_GRPC_PORT, RERUN_WEB_PORT

if TYPE_CHECKING:
from collections.abc import Callable
Expand Down
11 changes: 8 additions & 3 deletions dimos/core/global_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
# limitations under the License.

import re
from typing import Literal, TypeAlias

from pydantic_settings import BaseSettings, SettingsConfigDict

from dimos.models.vl.types import VlModelName

ViewerBackend: TypeAlias = Literal["rerun", "rerun-web", "rerun-connect", "foxglove", "none"]
from dimos.visualization.constants import (
RERUN_ENABLE_WEB,
RERUN_OPEN_DEFAULT,
RerunOpenOption,
ViewerBackend,
)


def _get_all_numbers(s: str) -> list[float]:
Expand All @@ -37,6 +40,8 @@ class GlobalConfig(BaseSettings):
replay_dir: str = "go2_sf_office"
new_memory: bool = False
viewer: ViewerBackend = "rerun"
rerun_open: RerunOpenOption = RERUN_OPEN_DEFAULT
rerun_web: bool = RERUN_ENABLE_WEB
n_workers: int = 2
memory_limit: str = "auto"
mujoco_camera_position: str | None = None
Expand Down
5 changes: 3 additions & 2 deletions dimos/hardware/sensors/camera/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from dimos.agents.annotation import skill
from dimos.core.coordination.blueprints import autoconnect
from dimos.core.core import rpc
from dimos.core.global_config import global_config
from dimos.core.module import Module, ModuleConfig
from dimos.core.stream import Out
from dimos.hardware.sensors.camera.spec import CameraHardware
Expand All @@ -32,7 +33,7 @@
from dimos.msgs.sensor_msgs.CameraInfo import CameraInfo
from dimos.msgs.sensor_msgs.Image import Image, sharpness_barrier
from dimos.spec import perception
from dimos.visualization.rerun.bridge import RerunBridgeModule
from dimos.visualization.vis_module import vis_module


def default_transform() -> Transform:
Expand Down Expand Up @@ -120,5 +121,5 @@ def stop(self) -> None:

demo_camera = autoconnect(
CameraModule.blueprint(),
RerunBridgeModule.blueprint(),
vis_module(viewer_backend=global_config.viewer),
)
39 changes: 24 additions & 15 deletions dimos/hardware/sensors/lidar/fastlio2/fastlio_blueprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,45 @@
from dimos.core.coordination.blueprints import autoconnect
from dimos.hardware.sensors.lidar.fastlio2.module import FastLio2
from dimos.mapping.voxels import VoxelGridMapper
from dimos.visualization.rerun.bridge import RerunBridgeModule
from dimos.visualization.vis_module import vis_module

voxel_size = 0.05

mid360_fastlio = autoconnect(
FastLio2.blueprint(voxel_size=voxel_size, map_voxel_size=voxel_size, map_freq=-1),
RerunBridgeModule.blueprint(
visual_override={
"world/lidar": lambda grid: grid.to_rerun(voxel_size=voxel_size, mode="boxes"),
}
vis_module(
"rerun",
rerun_config={
"visual_override": {
"world/lidar": lambda grid: grid.to_rerun(voxel_size=voxel_size, mode="boxes"),
},
},
),
).global_config(n_workers=2, robot_model="mid360_fastlio2")

mid360_fastlio_voxels = autoconnect(
FastLio2.blueprint(),
VoxelGridMapper.blueprint(publish_interval=1.0, voxel_size=voxel_size, carve_columns=False),
RerunBridgeModule.blueprint(
visual_override={
"world/global_map": lambda grid: grid.to_rerun(voxel_size=voxel_size, mode="boxes"),
"world/lidar": None,
}
vis_module(
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.

here

"rerun",
rerun_config={
"visual_override": {
"world/global_map": lambda grid: grid.to_rerun(voxel_size=voxel_size, mode="boxes"),
"world/lidar": None,
},
},
),
).global_config(n_workers=3, robot_model="mid360_fastlio2_voxels")

mid360_fastlio_voxels_native = autoconnect(
FastLio2.blueprint(voxel_size=voxel_size, map_voxel_size=voxel_size, map_freq=3.0),
RerunBridgeModule.blueprint(
visual_override={
"world/lidar": None,
"world/global_map": lambda grid: grid.to_rerun(voxel_size=voxel_size, mode="boxes"),
}
vis_module(
"rerun",
rerun_config={
"visual_override": {
"world/lidar": None,
"world/global_map": lambda grid: grid.to_rerun(voxel_size=voxel_size, mode="boxes"),
},
},
),
).global_config(n_workers=2, robot_model="mid360_fastlio2")
4 changes: 2 additions & 2 deletions dimos/hardware/sensors/lidar/livox/livox_blueprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

from dimos.core.coordination.blueprints import autoconnect
from dimos.hardware.sensors.lidar.livox.module import Mid360
from dimos.visualization.rerun.bridge import RerunBridgeModule
from dimos.visualization.vis_module import vis_module

mid360 = autoconnect(
Mid360.blueprint(),
RerunBridgeModule.blueprint(),
vis_module("rerun"),
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.

Why are we hard coding 'rerun' here?

).global_config(n_workers=2, robot_model="mid360")
4 changes: 2 additions & 2 deletions dimos/manipulation/grasping/demo_grasping.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from dimos.manipulation.grasping.grasping import GraspingModule
from dimos.perception.detection.detectors.yoloe import YoloePromptMode
from dimos.perception.object_scene_registration import ObjectSceneRegistrationModule
from dimos.robot.foxglove_bridge import FoxgloveBridge
from dimos.visualization.vis_module import vis_module

camera_module = RealSenseCamera.blueprint(enable_pointcloud=False)

Expand All @@ -44,7 +44,7 @@
("/tmp", "/tmp", "rw")
], # Grasp visualization debug standalone: python -m dimos.manipulation.grasping.visualize_grasps
),
FoxgloveBridge.blueprint(),
vis_module("foxglove"),
McpServer.blueprint(),
McpClient.blueprint(),
).global_config(viewer="foxglove")
122 changes: 122 additions & 0 deletions dimos/navigation/cmd_vel_mux.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Copyright 2026 Dimensional Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""CmdVelMux: merges nav and teleop velocity commands.

Teleop (tele_cmd_vel) takes priority over autonomous navigation
(nav_cmd_vel). When teleop is active, nav commands are suppressed
and a stop_movement signal is published. After a cooldown period
with no teleop input, nav commands resume.
"""

from __future__ import annotations

import threading
from typing import Any

from dimos_lcm.std_msgs import Bool

from dimos.core.core import rpc
from dimos.core.module import Module, ModuleConfig
from dimos.core.stream import In, Out
from dimos.msgs.geometry_msgs.Twist import Twist
from dimos.utils.logging_config import setup_logger

logger = setup_logger()


class CmdVelMuxConfig(ModuleConfig):
teleop_cooldown_sec: float = 1.0


class CmdVelMux(Module[CmdVelMuxConfig]):
"""Multiplexes nav_cmd_vel and tele_cmd_vel into a single cmd_vel output.

When teleop input arrives, stop_movement is published so downstream
modules (planner, explorer) can cancel their active goals.

Ports:
nav_cmd_vel (In[Twist]): Velocity from the autonomous planner.
tele_cmd_vel (In[Twist]): Velocity from keyboard/joystick teleop.
cmd_vel (Out[Twist]): Merged output — teleop wins when active.
stop_movement (Out[Bool]): Published when teleop begins.
"""

default_config = CmdVelMuxConfig

nav_cmd_vel: In[Twist]
tele_cmd_vel: In[Twist]
cmd_vel: Out[Twist]
stop_movement: Out[Bool]

def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self._teleop_active = False
self._lock = threading.Lock()
self._timer: threading.Timer | None = None

def __getstate__(self) -> dict[str, Any]:
state: dict[str, Any] = super().__getstate__() # type: ignore[no-untyped-call]
state.pop("_lock", None)
state.pop("_timer", None)
return state

def __setstate__(self, state: dict[str, Any]) -> None:
super().__setstate__(state)
self._lock = threading.Lock()
self._timer = None

@rpc
def start(self) -> None:
self.nav_cmd_vel.subscribe(self._on_nav)
self.tele_cmd_vel.subscribe(self._on_teleop)

@rpc
def stop(self) -> None:
with self._lock:
if self._timer is not None:
self._timer.cancel()
self._timer = None
super().stop()

def _on_nav(self, msg: Twist) -> None:
with self._lock:
if self._teleop_active:
return
self.cmd_vel.publish(msg)

def _on_teleop(self, msg: Twist) -> None:
was_active: bool
with self._lock:
was_active = self._teleop_active
self._teleop_active = True
if self._timer is not None:
self._timer.cancel()
self._timer = threading.Timer(
self.config.teleop_cooldown_sec,
self._end_teleop,
)
self._timer.daemon = True
self._timer.start()

if not was_active:
self.stop_movement.publish(Bool(data=True))
logger.info("Teleop active — published stop_movement")

self.cmd_vel.publish(msg)

def _end_teleop(self) -> None:
with self._lock:
self._teleop_active = False
self._timer = None
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class WavefrontFrontierExplorer(Module[WavefrontConfig]):
goal_reached: In[Bool]
explore_cmd: In[Bool]
stop_explore_cmd: In[Bool]
stop_movement: In[Bool]

# LCM outputs
goal_request: Out[PoseStamped]
Expand Down Expand Up @@ -171,6 +172,10 @@ def start(self) -> None:
unsub = self.stop_explore_cmd.subscribe(self._on_stop_explore_cmd)
self._disposables.add(Disposable(unsub))

if self.stop_movement.transport is not None:
unsub = self.stop_movement.subscribe(self._on_stop_movement)
self._disposables.add(Disposable(unsub))

@rpc
def stop(self) -> None:
self.stop_exploration()
Expand Down Expand Up @@ -201,6 +206,12 @@ def _on_stop_explore_cmd(self, msg: Bool) -> None:
logger.info("Received exploration stop command via LCM")
self.stop_exploration()

def _on_stop_movement(self, msg: Bool) -> None:
"""Handle stop movement from teleop — cancel active exploration."""
if msg.data and self.exploration_active:
logger.info("WavefrontFrontierExplorer: stop_movement received, stopping exploration")
self.stop_exploration()

def _count_costmap_information(self, costmap: OccupancyGrid) -> int:
"""
Count the amount of information in a costmap (free space + obstacles).
Expand Down
16 changes: 14 additions & 2 deletions dimos/navigation/replanning_a_star/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from dimos.core.core import rpc
from dimos.core.module import Module
from dimos.core.stream import In, Out
from dimos.utils.logging_config import setup_logger

logger = setup_logger()
from dimos.msgs.geometry_msgs.PointStamped import PointStamped
from dimos.msgs.geometry_msgs.PoseStamped import PoseStamped
from dimos.msgs.geometry_msgs.Twist import Twist
Expand All @@ -36,10 +39,11 @@ class ReplanningAStarPlanner(Module, NavigationInterface):
goal_request: In[PoseStamped]
clicked_point: In[PointStamped]
target: In[PoseStamped]
stop_movement: In[Bool]

goal_reached: Out[Bool]
navigation_state: Out[String] # TODO: set it
cmd_vel: Out[Twist]
nav_cmd_vel: Out[Twist]
path: Out[Path]
navigation_costmap: Out[OccupancyGrid]

Expand Down Expand Up @@ -70,9 +74,12 @@ def start(self) -> None:
)
)

if self.stop_movement.transport is not None:
self._disposables.add(Disposable(self.stop_movement.subscribe(self._on_stop_movement)))

self._disposables.add(self._planner.path.subscribe(self.path.publish))

self._disposables.add(self._planner.cmd_vel.subscribe(self.cmd_vel.publish))
self._disposables.add(self._planner.cmd_vel.subscribe(self.nav_cmd_vel.publish))

self._disposables.add(self._planner.goal_reached.subscribe(self.goal_reached.publish))

Expand All @@ -90,6 +97,11 @@ def stop(self) -> None:

super().stop()

def _on_stop_movement(self, msg: Bool) -> None:
if msg.data:
logger.info("ReplanningAStarPlanner: stop_movement received, cancelling goal")
self.cancel_goal()

@rpc
def set_goal(self, goal: PoseStamped) -> bool:
self._planner.handle_goal_request(goal)
Expand Down
Loading
Loading