Skip to content

Commit b43752c

Browse files
authored
Merge pull request #210 - Cleaner global planner api, faster rendering of real time robot position
Vis/fix2 - cleaner global planner api, faster rendering of real time robot position
2 parents 530684f + 969bbde commit b43752c

File tree

3 files changed

+41
-34
lines changed

3 files changed

+41
-34
lines changed

dimos/robot/global_planner/planner.py

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414

1515
from dataclasses import dataclass
1616
from abc import abstractmethod
17-
from typing import Tuple, Callable, Optional
17+
from typing import Callable, Optional
1818
import threading
1919

20-
from dimos.types.vector import VectorLike, to_vector, Vector
2120
from dimos.types.path import Path
2221
from dimos.types.costmap import Costmap
22+
from dimos.types.vector import VectorLike, to_vector, Vector
2323
from dimos.robot.global_planner.algo import astar
2424
from dimos.utils.logging_config import setup_logger
2525
from dimos.web.websocket_vis.helpers import Visualizable
@@ -29,7 +29,7 @@
2929

3030
@dataclass
3131
class Planner(Visualizable):
32-
local_nav: Callable[[Path, Optional[threading.Event]], bool]
32+
set_local_nav: Callable[[Path, Optional[threading.Event]], bool]
3333

3434
@abstractmethod
3535
def plan(self, goal: VectorLike) -> Path: ...
@@ -40,30 +40,23 @@ def set_goal(self, goal: VectorLike, goal_theta: float, stop_event: Optional[thr
4040
if not path:
4141
logger.warning("No path found to the goal.")
4242
return False
43+
return self.set_local_nav(path, stop_event=stop_event, goal_theta = goal_theta)
4344

44-
return self.local_nav(path, goal_theta = goal_theta, stop_event=stop_event)
45-
46-
45+
@dataclass
4746
class AstarPlanner(Planner):
48-
def __init__(
49-
self,
50-
costmap: Callable[[], Costmap],
51-
base_link: Callable[[], Tuple[Vector, Vector]],
52-
local_nav: Callable[[Vector], bool],
53-
):
54-
super().__init__(local_nav)
55-
self.base_link = base_link
56-
self.costmap = costmap
47+
get_costmap: Callable[[], Costmap]
48+
get_robot_pos: Callable[[], Vector]
49+
set_local_nav: Callable[[Path], bool]
50+
conservativism: int = 3
5751

5852
def plan(self, goal: VectorLike) -> Path:
59-
[pos, rot] = self.base_link()
60-
costmap = self.costmap()
61-
smudge = costmap.smudge(iterations=3)
53+
pos = self.get_robot_pos()
54+
costmap = self.get_costmap().smudge(iterations=self.conservativism)
6255

63-
self.vis("planner_costmap", smudge)
56+
self.vis("planner_costmap", costmap)
6457
self.vis("target", goal)
6558

66-
path = astar(smudge, goal, pos)
59+
path = astar(costmap, goal, pos)
6760

6861
if path:
6962
path = path.resample(0.5)

dimos/robot/unitree/unitree_go2.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,14 @@ def __init__(
172172
robot_length=0.6, # Unitree Go2 length in meters
173173
max_linear_vel=0.5,
174174
lookahead_distance=1.0,
175-
visualization_size=500 # 500x500 pixel visualization
175+
visualization_size=500, # 500x500 pixel visualization
176176
)
177177

178178
self.global_planner = AstarPlanner(
179-
costmap=lambda: Costmap.from_msg(self.ros_control.topic_latest("map", msg.OccupancyGrid)()),
180-
base_link=lambda: self.ros_control.transform_euler("base_link"),
181-
local_nav=self.navigate_path_local
179+
conservativism=3, # how close to obstacles robot is allowed to path plan
180+
set_local_nav=self.navigate_path_local,
181+
get_costmap=self.ros_control.topic_latest("map", Costmap),
182+
get_robot_pos=lambda: self.ros_control.transform_euler_pos("base_link"),
182183
)
183184

184185
# Create the visualization stream at 5Hz
@@ -268,19 +269,19 @@ def navigate_path_local(self, path: Path, timeout: float = 120.0, goal_theta: Op
268269
if self.local_planner.is_goal_reached():
269270
logger.info("Path traversed successfully.")
270271
path_completed = True
271-
break
272+
break
272273

273274
# Get planned velocity towards the current waypoint target
274275
vel_command = self.local_planner.plan()
275-
x_vel = vel_command.get('x_vel', 0.0)
276-
angular_vel = vel_command.get('angular_vel', 0.0)
276+
x_vel = vel_command.get("x_vel", 0.0)
277+
angular_vel = vel_command.get("angular_vel", 0.0)
277278

278279
# Send velocity command
279280
self.ros_control.move_vel_control(x=x_vel, y=0, yaw=angular_vel)
280281

281282
# Control loop frequency
282283
time.sleep(0.1)
283-
284+
284285
if not path_completed:
285286
logger.warning(f"Path following timed out after {timeout} seconds before completing the path.")
286287

@@ -293,7 +294,7 @@ def navigate_path_local(self, path: Path, timeout: float = 120.0, goal_theta: Op
293294
finally:
294295
logger.info("Stopping robot after path navigation attempt.")
295296
self.ros_control.stop()
296-
297+
297298
return path_completed
298299

299300
def get_skills(self) -> Optional[SkillLibrary]:

tests/test_websocketvis.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import math
12
import os
23
import time
34
import threading
@@ -11,6 +12,7 @@
1112
from reactivex import operators as ops
1213
import argparse
1314
import pickle
15+
import reactivex as rx
1416

1517

1618
def parse_args():
@@ -29,7 +31,7 @@ def main():
2931
websocket_vis.start()
3032

3133
if args.live:
32-
ros_control = UnitreeROSControl(node_name="simple_nav_test", mock_connection=False)
34+
ros_control = UnitreeROSControl(node_name="web_nav_test", mock_connection=False)
3335
robot = UnitreeGo2(ros_control=ros_control, ip=os.getenv("ROBOT_IP"))
3436
planner = robot.global_planner
3537

@@ -40,9 +42,9 @@ def main():
4042
pickle_path = f"{__file__.rsplit('/', 1)[0]}/mockdata/costmap.pickle"
4143
print(f"Loading costmap from {pickle_path}")
4244
planner = AstarPlanner(
43-
costmap=lambda: pickle.load(open(pickle_path, "rb")),
44-
base_link=lambda: [Vector(6.0, -1.5), Vector(1, 1, 1)],
45-
local_nav=lambda x: time.sleep(1) and True,
45+
get_costmap=lambda: pickle.load(open(pickle_path, "rb")),
46+
get_robot_pos=lambda: Vector(6.0, -1.5),
47+
set_local_nav=lambda x: time.sleep(1) and True,
4648
)
4749

4850
def msg_handler(msgtype, data):
@@ -63,7 +65,18 @@ def threaded_msg_handler(msgtype, data):
6365
websocket_vis.msg_handler = threaded_msg_handler
6466

6567
print(f"WebSocket server started on port {websocket_vis.port}")
66-
planner.plan(Vector(0, 0))
68+
print(planner.get_costmap())
69+
70+
planner.plan(Vector(0, 0)) # plan a path to the origin
71+
72+
def fakepos():
73+
# Simulate a fake vector position change (to test realtime rendering)
74+
vec = Vector(math.sin(time.time()) * 2, math.cos(time.time()) * 2, 0)
75+
print(vec)
76+
return vec
77+
78+
if not args.live:
79+
websocket_vis.connect(rx.interval(0.05).pipe(ops.map(lambda _: ["fakepos", fakepos()])))
6780

6881
try:
6982
# Keep the server running

0 commit comments

Comments
 (0)