Skip to content

Commit 78a0779

Browse files
authored
feat(nav): add clicked_point input to replanning planner and rerun clicking blueprint (#1394)
* feat(nav): add clicked_point input to replanning planner subscribe to PointStamped clicks from rerun viewer, convert to PoseStamped via .to_pose_stamped() and feed to goal handler. wire with: LCMTransport("/clicked_point", PointStamped) DIM-643 * feat(examples): add rerun click-to-navigate test script runs Go2 smart blueprint with viewer_mode=none, connects to custom rerun viewer on gRPC port 9877, wires /clicked_point LCM transport to planner's clicked_point input. DIM-643 * fix(examples): add --simulation flag to click test supports --simulation (mujoco), --robot-ip, --viewer-url args * fix(examples): init rerun and connect grpc before coordinator.start rr.init + rr.connect_grpc must happen before the bridge starts, otherwise rerun disables itself with no active sink * fix(examples): connect grpc after bridge start to avoid double rr.init the bridge calls rr.init('dimos') in start(). calling rr.init before that resets the recording stream. now we connect_grpc after start() so the bridge's rr.init creates the stream, then we add our gRPC sink. * fix(examples): init rerun in main process before connect_grpc the bridge runs in a worker subprocess (multiprocessing), so its rr.init() only affects that subprocess. the main process needs its own rr.init() + connect_grpc() for data to reach the viewer. also adds --replay flag for headless testing. * feat(rerun): add viewer_mode=connect for external viewer support adds connect mode to rerun bridge — connects to an external viewer via grpc instead of spawning a new one. needed for custom viewers like the click-to-navigate fork. example script now works like a normal blueprint. DIM-643 * fix(examples): use build().loop() per readme convention * feat(blueprints): add unitree-go2-click-nav blueprint proper blueprint registered in all_blueprints. run with: dimos run unitree-go2-click-nav --simulation --viewer-backend rerun-connect adds rerun-connect viewer backend that connects to an external rerun viewer (custom fork) instead of spawning a new one. DIM-643
1 parent ae781c1 commit 78a0779

File tree

6 files changed

+61
-3
lines changed

6 files changed

+61
-3
lines changed

dimos/core/global_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from dimos.mapping.occupancy.path_map import NavigationStrategy
2121

22-
ViewerBackend: TypeAlias = Literal["rerun", "rerun-web", "foxglove", "none"]
22+
ViewerBackend: TypeAlias = Literal["rerun", "rerun-web", "rerun-connect", "foxglove", "none"]
2323

2424

2525
def _get_all_numbers(s: str) -> list[float]:

dimos/navigation/replanning_a_star/module.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from dimos.core.global_config import GlobalConfig, global_config
2222
from dimos.core.module import Module
2323
from dimos.core.stream import In, Out
24-
from dimos.msgs.geometry_msgs import PoseStamped, Twist
24+
from dimos.msgs.geometry_msgs import PointStamped, PoseStamped, Twist
2525
from dimos.msgs.nav_msgs import OccupancyGrid, Path
2626
from dimos.navigation.base import NavigationInterface, NavigationState
2727
from dimos.navigation.replanning_a_star.global_planner import GlobalPlanner
@@ -31,6 +31,7 @@ class ReplanningAStarPlanner(Module, NavigationInterface):
3131
odom: In[PoseStamped] # TODO: Use TF.
3232
global_costmap: In[OccupancyGrid]
3333
goal_request: In[PoseStamped]
34+
clicked_point: In[PointStamped]
3435
target: In[PoseStamped]
3536

3637
goal_reached: Out[Bool]
@@ -60,6 +61,14 @@ def start(self) -> None:
6061
)
6162
self._disposables.add(Disposable(self.target.subscribe(self._planner.handle_goal_request)))
6263

64+
self._disposables.add(
65+
Disposable(
66+
self.clicked_point.subscribe(
67+
lambda pt: self._planner.handle_goal_request(pt.to_pose_stamped())
68+
)
69+
)
70+
)
71+
6372
self._disposables.add(self._planner.path.subscribe(self.path.publish))
6473

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

dimos/robot/all_blueprints.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"unitree-go2-agentic-mcp": "dimos.robot.unitree.go2.blueprints.agentic.unitree_go2_agentic_mcp:unitree_go2_agentic_mcp",
7777
"unitree-go2-agentic-ollama": "dimos.robot.unitree.go2.blueprints.agentic.unitree_go2_agentic_ollama:unitree_go2_agentic_ollama",
7878
"unitree-go2-basic": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_basic:unitree_go2_basic",
79+
"unitree-go2-click-nav": "dimos.robot.unitree.go2.blueprints.smart.unitree_go2_click_nav:unitree_go2_click_nav",
7980
"unitree-go2-detection": "dimos.robot.unitree.go2.blueprints.smart.unitree_go2_detection:unitree_go2_detection",
8081
"unitree-go2-ros": "dimos.robot.unitree.go2.blueprints.smart.unitree_go2_ros:unitree_go2_ros",
8182
"unitree-go2-spatial": "dimos.robot.unitree.go2.blueprints.smart.unitree_go2_spatial:unitree_go2_spatial",

dimos/robot/unitree/go2/blueprints/basic/unitree_go2_basic.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ def _static_base_link(rr: Any) -> list[Any]:
105105
from dimos.visualization.rerun.bridge import rerun_bridge
106106

107107
with_vis = autoconnect(_transports_base, rerun_bridge(**rerun_config))
108+
case "rerun-connect":
109+
from dimos.visualization.rerun.bridge import rerun_bridge
110+
111+
with_vis = autoconnect(
112+
_transports_base, rerun_bridge(viewer_mode="connect", **rerun_config)
113+
)
108114
case "rerun-web":
109115
from dimos.visualization.rerun.bridge import rerun_bridge
110116

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2025-2026 Dimensional Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
from dimos.core.blueprints import autoconnect
17+
from dimos.core.transport import LCMTransport
18+
from dimos.mapping.costmapper import cost_mapper
19+
from dimos.mapping.voxels import voxel_mapper
20+
from dimos.msgs.geometry_msgs import PointStamped
21+
from dimos.navigation.replanning_a_star.module import replanning_a_star_planner
22+
from dimos.robot.unitree.go2.blueprints.basic.unitree_go2_basic import unitree_go2_basic
23+
24+
unitree_go2_click_nav = (
25+
autoconnect(
26+
unitree_go2_basic,
27+
voxel_mapper(voxel_size=0.1),
28+
cost_mapper(),
29+
replanning_a_star_planner(),
30+
)
31+
.transports(
32+
{
33+
("clicked_point", PointStamped): LCMTransport("/clicked_point", PointStamped),
34+
}
35+
)
36+
.global_config(n_workers=6, robot_model="unitree_go2")
37+
)
38+
39+
__all__ = ["unitree_go2_click_nav"]

dimos/visualization/rerun/bridge.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class RerunConvertible(Protocol):
123123
def to_rerun(self) -> RerunData: ...
124124

125125

126-
ViewerMode = Literal["native", "web", "none"]
126+
ViewerMode = Literal["native", "web", "connect", "none"]
127127

128128

129129
def _default_blueprint() -> Blueprint:
@@ -158,6 +158,7 @@ class Config(ModuleConfig):
158158
entity_prefix: str = "world"
159159
topic_to_entity: Callable[[Any], str] | None = None
160160
viewer_mode: ViewerMode = "native"
161+
connect_url: str = "rerun+http://127.0.0.1:9877/proxy"
161162
memory_limit: str = "25%"
162163

163164
# Blueprint factory: callable(rrb) -> Blueprint for viewer layout configuration
@@ -265,6 +266,8 @@ def start(self) -> None:
265266
elif self.config.viewer_mode == "web":
266267
server_uri = rr.serve_grpc()
267268
rr.serve_web_viewer(connect_to=server_uri, open_browser=False)
269+
elif self.config.viewer_mode == "connect":
270+
rr.connect_grpc(self.config.connect_url)
268271
# "none" - just init, no viewer (connect externally)
269272

270273
if self.config.blueprint:

0 commit comments

Comments
 (0)