Skip to content

Commit e941b30

Browse files
committed
Adding a testing scenario. Code needs to be cleaned up
1 parent 78f1a58 commit e941b30

7 files changed

Lines changed: 133 additions & 132 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
0.0,0,0
2+
20,4,0

GEMstack/onboard/planning/astar.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818
class SearchNode(Generic[T]):
1919
"""Representation of a search node"""
2020

21-
__slots__ = ("data", "gscore", "fscore", "closed", "came_from", "in_openset", "cache")
21+
__slots__ = ("data", "gscore", "fscore", "tscore", "closed", "came_from", "in_openset", "cache")
2222

2323
def __init__(
24-
self, data: T, gscore: float = infinity, fscore: float = infinity
24+
self, data: T, gscore: float = infinity, fscore: float = infinity, tscore:float = infinity
2525
) -> None:
2626
self.data = data
2727
self.gscore = gscore
2828
self.fscore = fscore
29+
self.tscore = tscore
2930
self.closed = False
3031
self.in_openset = False
3132
self.came_from: Union[None, SearchNode[T]] = None
@@ -92,6 +93,23 @@ def heuristic_cost_estimate(self, current: T, goal: T) -> float:
9293
This method must be implemented in a subclass.
9394
"""
9495
raise NotImplementedError
96+
97+
@abstractmethod
98+
def terminal_cost_estimate(self, current: T, goal: T) -> float:
99+
"""Computes the estimated distance between a node and the goal.
100+
This function is called after all iterations of A* have been run
101+
and is used to determine the closest node to the goal found so far.
102+
103+
This method must be implemented in a subclass.
104+
105+
Args:
106+
current (T): Current T
107+
goal (T): goal T
108+
109+
Returns:
110+
float: _description_
111+
"""
112+
raise NotImplementedError
95113

96114
def distance_between(self, n1: T, n2: T) -> float:
97115
"""
@@ -152,7 +170,7 @@ def _gen():
152170
return reversed(list(_gen()))
153171

154172
def astar(
155-
self, start: T, goal: T, reversePath: bool = False
173+
self, start: T, goal: T, reversePath: bool = False, iterations: int = 5000
156174
) -> Union[Iterable[T], None]:
157175
if self.is_goal_reached(start, goal):
158176
return [start]
@@ -163,8 +181,11 @@ def astar(
163181
start, gscore=0.0, fscore=self.heuristic_cost_estimate(start, goal)
164182
)
165183
openSet.push(startNode)
184+
bestNode = startNode
185+
186+
iteration = 0
166187

167-
while openSet:
188+
while openSet and iteration < iterations:
168189
current = openSet.pop()
169190

170191
if self.is_goal_reached(current.data, goal):
@@ -184,6 +205,14 @@ def astar(
184205
fscore = gscore + self.heuristic_cost_estimate(
185206
neighbor.data, goal
186207
)
208+
tscore = self.terminal_cost_estimate(
209+
neighbor.data, goal
210+
)
211+
212+
# print(f"Checking node: {neighbor.data} with tscore {tscore}")
213+
if tscore < bestNode.tscore:
214+
# print(f"Found a better node: {neighbor.data} with tscore {tscore}")
215+
bestNode = neighbor
187216

188217
if neighbor.in_openset:
189218
if neighbor.fscore < fscore:
@@ -197,10 +226,14 @@ def astar(
197226
neighbor.came_from = current
198227
neighbor.gscore = gscore
199228
neighbor.fscore = fscore
229+
neighbor.tscore = tscore
200230

201231
openSet.push(neighbor)
202232

203-
return None
233+
iteration += 1
234+
235+
# print("Warning: A* search failed to find a path")
236+
return self.reconstruct_path(bestNode, reversePath)
204237

205238

206239
################################################################################

GEMstack/onboard/planning/hybrid_astar.py

Lines changed: 0 additions & 99 deletions
This file was deleted.

GEMstack/onboard/planning/parking_planning.py

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import List, Tuple, Union, Dict
22
from ..component import Component
3-
from ...state import AllState, VehicleState, Path, Trajectory, Route, ObjectFrameEnum, AgentState, Obstacle, ObjectPose
3+
from ...state import AllState, VehicleState, Path, Trajectory, Route, ObjectFrameEnum, AgentState, Obstacle, ObjectPose, PhysicalObject
44
from ...utils import serialization, settings
55
from ...mathutils.transforms import vector_madd
66
from ...mathutils.quadratic_equation import quad_root
@@ -26,9 +26,9 @@ def __init__(self, vehicle=None, obstacles=None, actions=None):
2626
self._vehicle = None
2727

2828
# SecondOrderDubinsCar() #x = (tx,ty,theta,v,dtheta) and u = (fwd_accel,wheel_angle_rate)
29-
self.vehicle_sim = IntegratorControlSpace(SecondOrderDubinsCar(), T=0.75, dt=0.25)
29+
self.vehicle_sim = IntegratorControlSpace(SecondOrderDubinsCar(), T=1, dt=0.1)
3030
#@TODO create a more standardized way to define the actions
31-
self._actions = [(1, -0.5), (1, -0.25), (1,0), (1, 0.25), (1,0.5),
31+
self._actions = [(2, -0.5), (2, -0.25), (2,0), (2, 0.25), (2,0.5),
3232
(0,0), (-1, -0.5), (-1,0), (-1, 0.5)]
3333

3434
@property
@@ -60,19 +60,38 @@ def is_goal_reached(self, current: List[float], goal: List[float]):
6060
# @TODO Currently, the threshold is just a random number, get rid of magic constants
6161
# print(f"Current Pose: {current}")
6262
# print(f"Goal Pose: {goal}")
63-
if np.abs(current[3]) > 0.5: return False # car must be stopped, this equality will only work in simulation
63+
if np.abs(current[3]) > 1: return False # car must be stopped, this equality will only work in simulation
6464
return np.linalg.norm(np.array([current[0], current[1]]) - np.array([goal[0], goal[1]])) < 0.5
6565
vehicle
66-
def heuristic_cost_estimate(self, vehicle_state_1, vehicle_state_2):
66+
def heuristic_cost_estimate(self, state_1, state_2):
6767
# @TODO Consider creating a more sophisticated heuristic
68-
"""computes the 'direct' distance between two (x,y) tuples"""
68+
"""computes the 'direct' distance between two (x,y) tuples.
69+
The states here are (x,y,theta,v,dtheta,t)
70+
"""
71+
(x1, y1, theta1, v1, dtheta1,t1) = state_1
72+
(x2, y2, theta2, v2, dtheta2, t2) = state_2
6973

70-
return math.hypot(vehicle_state_2[0] - vehicle_state_1[0], vehicle_state_2[1] - vehicle_state_1[1])
74+
return math.hypot(x2 - x1, y2 - y1, 2*(v2-v1))
7175
#return math.hypot(x2 - x1, y2 - y1, theta2 - theta1, v2-v1, dtheta2-dtheta1)
7276

73-
def distance_between(self, n1, n2):
74-
"""this method always returns 1, as two 'neighbors' are always adajcent"""
75-
return 1
77+
def terminal_cost_estimate(self, state_1, state_2):
78+
# @TODO Consider creating a more sophisticated heuristic
79+
"""computes the 'direct' distance between two (x,y) tuples.
80+
The states here are (x,y,theta,v,dtheta,t)
81+
"""
82+
(x1, y1, theta1, v1, dtheta1,t1) = state_1
83+
(x2, y2, theta2, v2, dtheta2, t2) = state_2
84+
85+
return math.hypot(x2 - x1, y2 - y1)
86+
87+
def distance_between(self, state_1, state_2):
88+
"""
89+
The states here are (x,y,theta,v,dtheta,t)
90+
"""
91+
(x1, y1, theta1, v1, dtheta1,t1) = state_1
92+
(x2, y2, theta2, v2, dtheta2, t2) = state_2
93+
94+
return math.hypot(x2 - x1, y2 - y1)
7695

7796
def neighbors(self, node):
7897
""" for a given configuration of the car in the maze, returns up to 4 adjacent(north,east,south,west)
@@ -101,13 +120,44 @@ def is_valid_neighbor(self, path):
101120
path (_type_): _description_
102121
"""
103122
for obstacle in self.obstacles:
123+
# print(f"Obstacle: {obstacle}")
104124
for point in path:
125+
vehicle_polygon = self.state_to_polygon(point)
126+
# print(f"Vehicle Polygon: {vehicle_polygon}")
105127
# print(point)
106128
# print(obstacle.polygon_parent())
107-
if collisions.circle_intersects_polygon_2d(point[:-1], 1, obstacle.polygon_parent()):
129+
# print("====================================")
130+
# print(f"Vehicle Polygon: {vehicle_polygon}")
131+
# print(f"Obstacle Polygon: {obstacle.polygon_parent()}")
132+
# print(f"Point: {point}")
133+
if collisions.polygon_intersects_polygon_2d(vehicle_polygon, obstacle.polygon_parent()):
108134
#polygon_intersects_polygon_2d when we have the acutal car geometry
109135
return False
110136
return True
137+
138+
def state_to_polygon(self, state):
139+
"""Convert a state to a polygon. The state is of the form (x,y,theta,v,dtheta,t)
140+
141+
Args:
142+
state (_type_): _description_
143+
144+
Returns:
145+
_type_: _description_
146+
"""
147+
x = state[0]
148+
y = state[1]
149+
theta = state[2]
150+
v = state[3]
151+
dtheta = state[4]
152+
t = state[5]
153+
154+
pose = ObjectPose(frame=ObjectFrameEnum.START,t=t, x=x,y=y,z=0,yaw=theta)
155+
156+
temp_obj = PhysicalObject(pose=pose,
157+
dimensions=self.vehicle.to_object().dimensions,
158+
outline=self.vehicle.to_object().outline)
159+
160+
return temp_obj.polygon()
111161

112162

113163
class ParkingPlanner(Component):
@@ -171,6 +221,9 @@ def update(self, state : AllState) -> Trajectory:
171221
"""
172222
vehicle = state.vehicle # type: VehicleState
173223
obstacles = state.obstacles # type: Dict[str, Obstacle]
224+
agents = state.agents # type: Dict[str, AgentState]
225+
print(f"Obstacles {obstacles}")
226+
print(f"Agents {agents}")
174227
route = state.route
175228
goal_point = route.points[-1] # I might need to change this to the same frame as the car?
176229
goal_pose = ObjectPose(frame=ObjectFrameEnum.START,t=15, x=goal_point[0],y=goal_point[1],z=0,yaw=0)
@@ -184,18 +237,19 @@ def update(self, state : AllState) -> Trajectory:
184237

185238

186239
# Update the planner
187-
self.planner.obstacles = list(obstacles.values())
240+
# self.planner.obstacles = list(obstacles.values())
241+
self.planner.obstacles = list(agents.values())
188242
self.planner.vehicle = vehicle
189243

190244
# Compute the new trajectory and return it
191-
res = list(self.planner.astar(start_state, goal_state))
245+
res = list(self.planner.astar(start_state, goal_state, 100))
192246
points = [state[:2] for state in res]
193247
times = [state[5] for state in res]
194248
path = Path(frame=vehicle.pose.frame, points=points)
195249
traj = Trajectory(path.frame,points,times)
196-
print("===========================")
197-
print(f"Points: {points}")
198-
print(f"Times: {times}")
250+
# print("===========================")
251+
# print(f"Points: {points}")
252+
# print(f"Times: {times}")
199253
# route = Path(frame=vehicle.pose.frame, points=points)
200254
# traj = longitudinal_plan(route, 2, -2, 10, vehicle.v, "milestone")
201255
# print(traj)

launch/parking.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ drive:
1717
planning:
1818
route_planning:
1919
type: StaticRoutePlanner
20-
args: [!relative_path '../GEMstack/knowledge/routes/forward_15m.csv','start']
20+
args: [!relative_path '../GEMstack/knowledge/routes/parking.csv','start']
2121
motion_planning:
2222
type: parking_planning.ParkingPlanner
2323
# args: [null]
@@ -64,7 +64,7 @@ variants:
6464
vehicle_interface:
6565
type: gem_mixed.GEMRealSensorsWithSimMotionInterface
6666
args:
67-
scene: !relative_path '../scenes/xyhead_demo.yaml'
67+
scene: !relative_path '../scenes/parking_demo.yaml'
6868
mission_execution: StandardExecutor
6969
require_engaged: False
7070
visualization: !include "klampt_visualization.yaml"
@@ -91,7 +91,7 @@ variants:
9191
vehicle_interface:
9292
type: gem_simulator.GEMDoubleIntegratorSimulationInterface
9393
args:
94-
scene: !relative_path '../scenes/xyhead_demo.yaml'
94+
scene: !relative_path '../scenes/parking_demo.yaml'
9595
visualization: !include "klampt_visualization.yaml"
9696
drive:
9797
perception:

scenes/parking_demo.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
vehicle_state: [4.0, 5.0, 0.0, 0.0, 0.0]
2+
agents:
3+
ped1:
4+
type: medium_truck
5+
position: [20.0, 8.0]
6+
nominal_velocity: 0
7+
target: [15.0, 2.0]
8+
behavior: loop
9+
yaw: 1.5708
10+
11+
ped2:
12+
type: medium_truck
13+
position: [30.0, 8.0]
14+
nominal_velocity: 0
15+
target: [15.0, 4.0]
16+
behavior: loop
17+
yaw: 1.5708
18+
19+

0 commit comments

Comments
 (0)