Skip to content

Commit 276dd3c

Browse files
committed
fix bug
1 parent b1c0917 commit 276dd3c

7 files changed

Lines changed: 132 additions & 92 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"type": "Roadgraph", "data": {"frame": 0, "curves": {}, "lanes": {"0": {"type": 0, "surface": 0, "route_name": "", "left": {"type": 0, "segments": [[[-88.236129, 40.09275451039261, 0.0], [-88.235527, 40.09275451039261, 0.0]]], "crossable": true, "elevation": null, "height": null}, "right": {"type": 0, "segments": [[[-88.236129, 40.092727489607384, 0.0], [-88.235527, 40.092727489607384, 0.0]]], "crossable": true, "elevation": null, "height": null}, "center": null, "begin": null, "end": null}, "1": {"type": 0, "surface": 0, "route_name": "", "left": {"type": 0, "segments": [[[-88.235527, 40.09275451039261, 0.0], [-88.235527, 40.092805489607386, 0.0]]], "crossable": true, "elevation": null, "height": null}, "right": {"type": 0, "segments": [[[-88.235527, 40.09272748960739, 0.0], [-88.235527, 40.092832510392604, 0.0]]], "crossable": true, "elevation": null, "height": null}, "center": null, "begin": null, "end": null}, "2": {"type": 0, "surface": 0, "route_name": "", "left": {"type": 0, "segments": [[[-88.235527, 40.092805489607386, 0.0], [-88.236129, 40.092805489607386, 0.0]]], "crossable": true, "elevation": null, "height": null}, "right": {"type": 0, "segments": [[[-88.235527, 40.09283251039261, 0.0], [-88.236129, 40.09283251039261, 0.0]]], "crossable": true, "elevation": null, "height": null}, "center": null, "begin": null, "end": null}, "4": {"type": 0, "surface": 0, "route_name": "", "left": {"type": 0, "segments": [[[-88.236129, 40.092805489607386, 0.0], [-88.236129, 40.09275451039261, 0.0]]], "crossable": true, "elevation": null, "height": null}, "right": {"type": 0, "segments": [[[-88.236129, 40.092832510392604, 0.0], [-88.236129, 40.09272748960739, 0.0]]], "crossable": true, "elevation": null, "height": null}, "center": null, "begin": null, "end": null}}, "regions": {}, "signs": {}, "static_obstacles": {}, "connections": []}}

GEMstack/onboard/planning/RRT.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import numpy as np
22
import random
33
import math
4+
import time
5+
46

57
class Obstacle:
68
def __init__(self,x=0,y=0,r=0.2):
@@ -17,7 +19,9 @@ def __init__(self,x=0,y=0,heading = None):
1719
self.cost = float('inf') # Cost to reach this node
1820

1921
class BiRRT:
20-
def __init__(self, start : list, goal : list, obstacles : list, MAP_SIZE : list, OFFSET : float = 0.8):
22+
def __init__(self, start : list, goal : list, obstacles : list, MAP_SIZE : list,
23+
OFFSET : float = 0.8, time_limit : float = 1.0, heading_limit = math.pi/6,
24+
step_size = 0.5, search_r = 1.4, MAX_Iteration = 20000):
2125

2226
self.path = []
2327
self.tree_from_start = []
@@ -42,16 +46,19 @@ def __init__(self, start : list, goal : list, obstacles : list, MAP_SIZE : list,
4246
self.OFFSET = OFFSET # meter
4347

4448
# max iteration size for performing route search
45-
self.MAX_Iteration = 20000
46-
49+
self.MAX_Iteration = MAX_Iteration
50+
51+
# max search time
52+
self.time_limit = time_limit
53+
4754
# step size for local planner
48-
self.step_size = 0.5 # meter
55+
self.step_size = step_size # meter
4956

5057
# radius for determine neighbor node
51-
self.search_r = 1.4 # meter
58+
self.search_r = search_r # meter
5259

5360
# angle limit for vehicle turning per step size
54-
self.heading_limit = math.pi/6 # limit the heading change in route
61+
self.heading_limit = heading_limit # limit the heading change in route
5562

5663
# Map boundary
5764
self.MAP_X_LOW = MAP_SIZE[0] # meter
@@ -64,9 +71,13 @@ def search(self):
6471
# initialize two tree
6572
self.tree_from_start.append(self.start_point)
6673
self.tree_from_end.append(self.end_point)
67-
74+
75+
start_time = time.time()
6876
# perform search within max number of iterration
6977
for iterration in range(self.MAX_Iteration):
78+
if time.time()-start_time > self.time_limit:
79+
break
80+
7081
# uniformly sample a point within in the map
7182
sample_p = Point(random.uniform(self.MAP_X_LOW,self.MAP_X_HIGH),random.uniform(self.MAP_Y_LOW,self.MAP_Y_HIGH))
7283
Direction = None

GEMstack/onboard/planning/route_planning_component.py

Lines changed: 22 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# from GEMstack.state.physical_object import ObjectFrameEnum
99
# from GEMstack.state.route import PlannerEnum, Route
1010
from .rrt_star import RRTStar
11-
11+
from ..interface.gem import GEMInterface
1212
from ..component import Component
1313
from ...state import AllState, Roadgraph, Route, PlannerEnum, ObjectFrameEnum, Path, VehicleState, AgentState, AgentEnum
1414
from .RRT import BiRRT
@@ -93,32 +93,35 @@ def __init__(self, roadgraphfn : str = None, map_frame : str = None):
9393
else:
9494
raise ValueError("Unknown roadgraph file extension", ext)
9595

96-
# TODO: Transform global map to start frame
96+
# TODO: Transform global map to start frame?
9797
if self.map_frame == ObjectFrameEnum.GLOBAL:
98-
self.start_pose_global = None # Read from GNSS
98+
self.start_pose_global = None # Read from GNSS?
9999
self.roadgraph = self.roadgraph.to_frame(ObjectFrameEnum.START, start_pose_abs=self.start_pose_global)
100100

101101
# If parking route existed, not search anymore. For simulation testing only. TODO: Delete after integration
102102
self.parking_route_existed = False
103103

104+
# Used as route searchers' time limit as well as the update rate of the component
105+
self.update_rate = 10.0
106+
104107
def state_inputs(self):
105108
return ["all"]
106109

107110
def state_outputs(self) -> List[str]:
108111
return ['route']
109112

110113
def rate(self):
111-
return 10.0
114+
return self.update_rate
112115

113116
def update(self, state: AllState):
114117
print("Route Planner's mission:", state.mission_plan.planner_type)
115118
# print("type of mission plan:", type(PlannerEnum.RRT_STAR))
116119
# print("Route Planner's mission:", state.mission_plan.planner_type.value == PlannerEnum.RRT_STAR.value)
117120
# print("Route Planner's mission:", state.mission_plan.planner_type.value == PlannerEnum.PARKING.value)
118121
# print("Mission plan:", state.mission_plan)
119-
# print("Vehicle x:", state.vehicle.pose.x)
120-
# print("Vehicle y:", state.vehicle.pose.y)
121-
# print("Vehicle yaw:", state.vehicle.pose.yaw)
122+
print("Vehicle x:", state.vehicle.pose.x)
123+
print("Vehicle y:", state.vehicle.pose.y)
124+
print("Vehicle yaw:", state.vehicle.pose.yaw)
122125

123126
if state.mission_plan.planner_type.value == PlannerEnum.PARKING.value:
124127
print("I am in PARKING mode")
@@ -180,7 +183,8 @@ def update(self, state: AllState):
180183
current_pose = plan_available_pose_on_lane([state.vehicle.pose.x, state.vehicle.pose.y],
181184
self.roadgraph, current_yaw=state.vehicle.pose.yaw)
182185
goal_pose = plan_available_pose_on_lane([goal_pose_.x, goal_pose_.y], self.roadgraph)
183-
searcher = BiRRT(current_pose, goal_pose, self.lane_points, map_size, OFFSET=0.8)
186+
searcher = BiRRT(current_pose, goal_pose, self.lane_points, map_size, OFFSET=0.8, time_limit=0.1,
187+
heading_limit=math.pi/6, step_size = 0.5, search_r = 1.4, MAX_Iteration = 20000)
184188
waypoints = searcher.search()
185189

186190
# For now, waypoints of [x, y, heading] is not working in longitudinal_planning. Use [x, y] instead.
@@ -200,57 +204,25 @@ def update(self, state: AllState):
200204
print("I am in PARALLEL_PARKING mode")
201205
# TODO: Get cones information from perception, should have a AgentEnum for cones, for example AgentEnum.CONE
202206
# cones = []
203-
# for obj in state.agents.values():
204-
# if obj.type == AgentEnum.CONE:
205-
# cones.append(obj)
207+
# for agent in state.agents.values():
208+
# if agent.type == AgentEnum.CONE:
209+
# cones.append(agent)
206210

207211
""" BEGIN: For simulation test only. Delete after integration """""""""""""""
208-
def generate_turn(pose, turn_angle=45, turn_radius=3.0, turn_direction="right",
209-
acceleration=5.0, deceleration=2.0, max_speed=1.0, num_points=10):
210-
theta = np.deg2rad(turn_angle)
212+
def generate_turn(pose, step=1.0, num_points=10):
211213
x, y, yaw = pose
212-
R = turn_radius
213-
if turn_direction == "left":
214-
cx = x + R * np.cos(yaw - np.pi / 2)
215-
cy = y + R * np.sin(yaw - np.pi / 2)
216-
start_angle = yaw - np.pi / 2
217-
angles = np.linspace(start_angle, start_angle - theta, num_points)
218-
end_yaw = start_angle - theta
219-
elif turn_direction == "right":
220-
cx = x + R * np.cos(yaw + np.pi / 2)
221-
cy = y + R * np.sin(yaw + np.pi / 2)
222-
start_angle = yaw + np.pi / 2
223-
angles = np.linspace(start_angle, start_angle + theta, num_points)
224-
end_yaw = start_angle + theta
225-
else:
226-
raise ValueError('Invalid turn direction')
227-
end_yaw = (end_yaw + np.pi) % (2 * np.pi) - np.pi # normalize angle
214+
yaw_left = yaw + math.pi / 4 # 左前方偏 45°
228215
waypoints = []
229-
for angle in angles:
230-
xi = cx + R * np.cos(angle)
231-
yi = cy + R * np.sin(angle)
232-
waypoints.append([xi, yi])
233-
end_pose = waypoints[-1][0], waypoints[-1][1], end_yaw
234-
return waypoints, end_pose
235-
236-
### FOR SIMULATION TEST ONLY ###
237-
def generate_pull_over_route(pose, turn_angle=45, turn_radius=3.0, turn_direction="right"):
238-
waypoints1, end_pose = generate_turn(pose, turn_angle=turn_angle, turn_radius=turn_radius,
239-
turn_direction=turn_direction)
240-
if turn_direction == "right":
241-
waypoints2, _ = generate_turn(end_pose, turn_angle=turn_angle, turn_radius=turn_radius,
242-
turn_direction="left")
243-
else:
244-
waypoints2, _ = generate_turn(end_pose, turn_angle=turn_angle, turn_radius=turn_radius,
245-
turn_direction="right")
246-
waypoints = waypoints1 + waypoints2
216+
for i in range(1, num_points + 1):
217+
dx = step * i * math.cos(yaw_left)
218+
dy = step * i * math.sin(yaw_left)
219+
waypoints.append([x + dx, y + dy])
247220
return waypoints
248221

249222
if not self.parking_route_existed:
250223
current_pose = [state.vehicle.pose.x, state.vehicle.pose.y, state.vehicle.pose.yaw]
251-
waypoints = generate_pull_over_route(current_pose, turn_angle=45, turn_radius=2.0, turn_direction="right")
224+
waypoints = generate_turn(current_pose)
252225
self.route = Route(frame=ObjectFrameEnum.START, points=waypoints)
253-
254226
self.parking_route_existed = True
255227
else:
256228
self.route = state.route

GEMstack/onboard/planning/summoning_mission_planning.py

Lines changed: 80 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,48 @@
33
from ...utils import serialization
44
from ...state import Route, ObjectFrameEnum, AllState, VehicleState, Roadgraph, MissionObjective, MissionEnum, \
55
VehicleIntent, VehicleIntentEnum, MissionPlan, PlannerEnum, ObjectPose
6+
from ..interface.gem import GEMInterface, GNSSReading
67
import os
78
import numpy as np
89
import requests
910
import json
1011
import time
1112
import math
13+
from ...utils import settings
1214

1315

14-
def check_distance(goal, current_pose):
15-
goal = np.array(goal)[:2]
16-
current_pose = np.array([current_pose.x, current_pose.y])
17-
return np.linalg.norm(goal - current_pose)
16+
def check_distance(goal_pose : ObjectPose, current_pose : ObjectPose):
17+
goal = np.array([goal_pose.x, goal_pose.y])
18+
current = np.array([current_pose.x, current_pose.y])
19+
return np.linalg.norm(goal - current)
20+
21+
22+
class StateMachine:
23+
def __init__(self, state_list : List = None):
24+
self.state_list = state_list
25+
self.state_index = 0
26+
self.initial_state = self.state_list[0]
27+
self.current_state = self.state_list[self.state_index]
28+
29+
def next_state(self):
30+
if self.state_index < len(self.state_list) - 1:
31+
self.state_index += 1
32+
return self.state_list[self.state_index]
33+
else:
34+
self.state_index = 0
35+
return self.state_list[0]
1836

1937

2038
class SummoningMissionPlanner(Component):
21-
def __init__(self, distance_to_goal_to_start_parking, distance_error_of_idle_from_parking, state_machine):
22-
print("Initializing SummoningMissionPlanner:", distance_to_goal_to_start_parking, distance_error_of_idle_from_parking, state_machine)
23-
self.distance_to_goal_to_start_parking = distance_to_goal_to_start_parking
24-
self.distance_error_of_idle_from_parking = distance_error_of_idle_from_parking
25-
self.state_list = [eval(s) for s in state_machine]
39+
def __init__(self, mode, state_machine):
40+
self.state_machine = StateMachine([eval(s) for s in state_machine])
41+
self.goal_location = None
42+
self.new_goal = False
43+
self.goal_pose = None
44+
self.start_pose = None
45+
self.start_time = time.time()
2646

27-
self.count = 0 # for simulate a delay to get the gaol location
47+
self.count = 0 # for test only, simulate a delay to get the gaol location
2848

2949
def state_inputs(self):
3050
return ['all']
@@ -38,46 +58,78 @@ def rate(self):
3858
def update(self, state: AllState):
3959
vehicle = state.vehicle
4060
mission_plan = state.mission_plan
41-
if mission_plan is None:
42-
mission_plan = MissionPlan()
43-
mission_plan.planner_type = PlannerEnum.IDLE
4461

45-
# for simulate a delay to get the gaol location
62+
# To simulate a delay to get the goal location
4663
self.count += 1
4764
if self.count <3:
4865
return mission_plan
4966

5067
# TODO: Modify to a GET request to get goal location from the server
68+
# current_goal_location = self.goal_location
5169
# url = ""
5270
# response = requests.get(url)
5371
# if response.status_code == 200:
5472
# data = response.json()
5573
# goal_location = data['goal_location']
56-
# goal_location = ObjectPose(t=time.time(), x=goal_location[0], y=goal_location[1], frame=ObjectFrameEnum.GLOBAL)
74+
# goal_frame = data['goal_frame']
5775

58-
goal_location = [10, 11] # for test only
59-
self.goal_pose = ObjectPose(t=time.time(), x=goal_location[0], y=goal_location[1],
60-
frame=ObjectFrameEnum.START)
76+
goal_location = [10, 11] # for simulation test only
77+
goal_frame = 'start'
6178

62-
if mission_plan.planner_type == PlannerEnum.IDLE and self.goal_pose is not None:
63-
mission_plan.goal_pose = self.goal_pose
64-
mission_plan.planner_type = PlannerEnum.SUMMON_DRIVING
79+
if self.goal_location == goal_location:
80+
self.new_goal = False
81+
else:
82+
self.new_goal = True
83+
self.goal_location = goal_location
84+
85+
if self.new_goal:
86+
if goal_frame == 'global':
87+
self.goal_pose = ObjectPose(t=time.time(), x=self.goal_location[0], y=self.goal_location[1],
88+
frame=ObjectFrameEnum.GLOBAL)
89+
elif goal_frame == 'cartesian':
90+
self.goal_pose = ObjectPose(t=time.time(), x=self.goal_location[0], y=self.goal_location[1],
91+
frame=ObjectFrameEnum.ABSOLUTE_CARTESIAN)
92+
elif goal_frame == 'start':
93+
self.goal_pose = ObjectPose(t=time.time() - self.start_time, x=self.goal_location[0], y=self.goal_location[1],
94+
frame=ObjectFrameEnum.START)
95+
else:
96+
raise ValueError("Invalid frame argument")
97+
98+
# Initiate state
99+
if mission_plan is None:
100+
mission_plan = MissionPlan()
101+
mission_plan.planner_type = self.state_machine.initial_state
102+
103+
# Receive goal location from the server and start driving
104+
elif mission_plan.planner_type == PlannerEnum.IDLE:
105+
if self.new_goal:
106+
mission_plan.goal_pose = self.goal_pose
107+
mission_plan.start_pose = self.start_pose
108+
mission_plan.planner_type = self.state_machine.next_state()
65109

110+
# Close to the goal location, begin to search for parking
66111
elif mission_plan.planner_type == PlannerEnum.SUMMON_DRIVING:
67-
dist = check_distance([self.goal_pose.x, self.goal_pose.y], vehicle.pose)
68-
if dist < self.distance_error_of_idle_from_parking:
69-
mission_plan.planner_type = PlannerEnum.PARALLEL_PARKING
112+
mission_plan.goal_pose = self.goal_pose
113+
mission_plan.start_pose = self.start_pose
114+
dist = check_distance(mission_plan.goal_pose, vehicle.pose)
115+
print("Distance to the goal:", dist)
116+
if dist < 5:
117+
mission_plan.planner_type = self.state_machine.next_state()
70118

119+
# Finish parking, back to idle and wait for the next goal location
71120
elif mission_plan.planner_type == PlannerEnum.PARALLEL_PARKING:
72-
dist = check_distance(state.route.points[-1], vehicle.pose)
73-
if dist < self.distance_error_of_idle_from_parking:
74-
mission_plan.planner_type = PlannerEnum.IDLE
121+
if state.route:
122+
dist = check_distance(mission_plan.goal_pose, vehicle.pose)
123+
print("Distance to the end point of the route:", dist)
124+
if dist < 0.2 and vehicle.v < 0.01:
125+
mission_plan.planner_type = self.state_machine.next_state()
75126

127+
# No state change
76128
else:
77129
mission_plan = state.mission_plan
78130

79131
# TODO: POST request to update status to the server
80132
# data =
81-
# response = requests.post(url=, data=)
133+
# response = requests.post(url=url, data=data)
82134

83135
return mission_plan

GEMstack/state/mission_plan.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class MissionPlan:
1313
planner_type : PlannerEnum = PlannerEnum.IDLE
1414
# other mission-specific parameters can be added here
1515
goal_pose: Optional[ObjectPose] = None
16+
start_pose: Optional[ObjectPose] = None
1617

1718

1819

GEMstack/state/route.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66

77
from enum import Enum
88

9-
# @dataclass
10-
# @register
9+
1110
class PlannerEnum(Enum):
1211
RRT_STAR = 0 #position / yaw in m / radians relative to starting pose of vehicle
1312
HYBRID_A_STAR = 1 #position / yaw in m / radians relative to current pose of vehicle

0 commit comments

Comments
 (0)