Skip to content

Commit ca68b4e

Browse files
committed
Merge branch 'A4_planning_part1' into A4_planning_patrick
2 parents 450a2f8 + 2b6c1d8 commit ca68b4e

4 files changed

Lines changed: 392 additions & 27 deletions

File tree

GEMstack/onboard/planning/longitudinal_planning.py

Lines changed: 261 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -123,43 +123,277 @@ def longitudinal_plan(path : Path, acceleration : float, deceleration : float, m
123123

124124
#TODO: actually do something to points and times
125125
points = [p for p in path_normalized.points]
126-
126+
times = [t for t in path_normalized.times]
127127
#=============================================
128128

129129
print("-----LONGITUDINAL PLAN-----")
130-
131-
look_ahead_time = 10.0
132-
dt = 0.2
133-
stop_distance_buffer = 0.2
134-
135-
trajectory_length = (look_ahead_time / dt) + 1
136-
times = np.linspace(0, look_ahead_time, int(trajectory_length)).tolist()
137-
138-
new_points = [points[0]]
139-
140-
for _ in range(1, len(times)):
141-
distance_to_stop = current_speed **2 / (2 * deceleration)
142-
if new_points[-1][0] >= points[-1][0]:
143-
new_points.append([new_points[-1][0], 0])
144-
elif (distance_to_stop + stop_distance_buffer) >= (points[-1][0] - new_points[-1][0]):
145-
# start decelerating
146-
new_points.append([new_points[-1][0] + current_speed * dt - 0.5 * deceleration * dt**2, 0])
147-
current_speed -= deceleration * dt
130+
print("path length: ", path.length())
131+
length = path.length()
132+
133+
# If the path is too short, just return the path for preventing sudden halt of simulation
134+
if length < 0.05:
135+
return Trajectory(path.frame, points, times)
136+
137+
# This assumes that the time denomination cannot be changed
138+
139+
# Starting point
140+
x0 = points[0][0]
141+
cur_point = points[0]
142+
cur_time = times[0]
143+
cur_index = 0
144+
145+
new_points = []
146+
new_times = []
147+
velocities = [] # for graphing and debugging purposes
148+
149+
while current_speed > 0 or cur_index == 0:
150+
# we want to iterate through all the points and add them
151+
# to the new points. However, we also want to add "critical points"
152+
# where we reach top speed, begin decelerating, and stop
153+
new_points.append(cur_point)
154+
new_times.append(cur_time)
155+
velocities.append(current_speed)
156+
print("=====================================")
157+
# print("new points: ", new_points)
158+
# print("current index: ", cur_index)
159+
# print("current speed: ", current_speed)
160+
# print("current position: ", cur_point)
161+
162+
# Information we will need:
163+
# Calculate how much time it would take to stop
164+
# Calculate how much distance it would take to stop
165+
min_delta_t_stop = current_speed/deceleration
166+
min_delta_x_stop = current_speed*min_delta_t_stop - 0.5*deceleration*min_delta_t_stop**2
167+
# print(min_delta_x_stop)
168+
assert min_delta_x_stop >= 0
169+
170+
171+
# Check if we are done
172+
173+
# If we cannot stop before or stop exactly at the final position requested
174+
if cur_point[0] + min_delta_x_stop >= points[-1][0]:
175+
print("In case one")
176+
# put on the breaks
177+
178+
# Calculate the next point in a special manner because of too-little time to stop
179+
if cur_index == len(points)-1:
180+
# the next point in this instance would be when we stop
181+
next_point = (cur_point[0] + min_delta_x_stop, 0)
182+
else:
183+
next_point = points[cur_index+1]
184+
185+
# keep breaking until the next milestone in path
186+
if next_point[0] <= points[-1][0]:
187+
# print("continuing to next point")
188+
delta_t_to_next_x = compute_time_to_x(cur_point[0], next_point[0], current_speed, -deceleration)
189+
cur_time += delta_t_to_next_x
190+
cur_point = next_point
191+
current_speed -= deceleration*delta_t_to_next_x
192+
cur_index += 1
193+
else:
194+
# continue to the point in which we would stop (current_velocity = 0)
195+
# update to the next point
196+
delta_t_to_next_x = compute_time_to_x(cur_point[0], next_point[0], current_speed, -deceleration)
197+
cur_point = next_point
198+
cur_time += delta_t_to_next_x
199+
# current_speed would not be exactly zero error would be less than 1e-4 but perfer to just set to zero
200+
#current_speed -= delta_t_to_next_x*deceleration
201+
current_speed = 0
202+
assert current_speed == 0
203+
204+
# This is the case where we are accelerating to max speed
205+
# because the first if-statement covers for when we decelerating,
206+
# the only time current_speed < max_speed is when we are accelerating
148207
elif current_speed < max_speed:
149-
# start accelerating
150-
new_points.append([new_points[-1][0] + current_speed * dt + 0.5 * acceleration * dt**2, 0])
151-
current_speed += acceleration * dt
208+
# print("In case two")
209+
# next point
210+
next_point = points[cur_index+1]
211+
# accelerate to max speed
212+
213+
# calculate the time it would take to reach max speed
214+
delta_t_to_max_speed = (max_speed - current_speed)/acceleration
215+
# calculate the distance it would take to reach max speed
216+
delta_x_to_max_speed = current_speed*delta_t_to_max_speed + 0.5*acceleration*delta_t_to_max_speed**2
217+
218+
delta_t_to_stop_from_max_speed = max_speed/deceleration
219+
delta_x_to_stop_from_max_speed = max_speed*delta_t_to_stop_from_max_speed - 0.5*deceleration*delta_t_to_stop_from_max_speed**2
220+
221+
delta_t_to_next_point = compute_time_to_x(cur_point[0], next_point[0], current_speed, acceleration)
222+
velocity_at_next_point = current_speed + delta_t_to_next_point*acceleration
223+
time_to_stop_from_next_point = velocity_at_next_point/deceleration
224+
delta_x_to_stop_from_next_point = velocity_at_next_point*time_to_stop_from_next_point - 0.5*deceleration*time_to_stop_from_next_point**2
225+
# if we would reach max speed after the next point,
226+
# just move to the next point and update the current speed and time
227+
if next_point[0] + delta_x_to_stop_from_next_point < points[-1][0] and \
228+
cur_point[0] + delta_x_to_max_speed >= next_point[0]:
229+
# ("go to next point")
230+
# accelerate to max speed
231+
delta_t_to_next_x = compute_time_to_x(cur_point[0], next_point[0], current_speed, acceleration)
232+
cur_time += delta_t_to_next_x
233+
cur_point = [next_point[0], 0]
234+
current_speed += delta_t_to_next_x*acceleration
235+
cur_index += 1
236+
237+
# This is the case where we would need to start breaking before reaching
238+
# top speed and before the next point (i.e. triangle shape velocity)
239+
elif cur_point[0] + delta_x_to_max_speed + delta_x_to_stop_from_max_speed >= points[-1][0]:
240+
# print(delta_x_to_max_speed)
241+
# print(delta_x_to_stop_from_max_speed)
242+
# Add a new point at the point where we should start breaking
243+
# print("Adding new point to start breaking")
244+
delta_t_to_next_x = compute_time_triangle(cur_point[0], points[-1][0], current_speed, 0, acceleration, deceleration)
245+
# print(delta_t_to_next_x)
246+
#delta_t_to_next_x = compute_time_to_x(cur_point[0], points[-1][0] - min_delta_x_stop, current_speed, acceleration)
247+
next_x = cur_point[0] + current_speed*delta_t_to_next_x + 0.5*acceleration*delta_t_to_next_x**2
248+
cur_time += delta_t_to_next_x
249+
cur_point = [next_x, 0]
250+
current_speed += delta_t_to_next_x*acceleration
251+
252+
# this is the case where we would reach max speed before the next point
253+
# we need to create a new point where we would reach max speed
254+
else:
255+
# print("adding new point")
256+
# we would need to add a new point at max speed
257+
cur_time += delta_t_to_max_speed
258+
cur_point = [cur_point[0] + delta_x_to_max_speed, 0]
259+
current_speed = max_speed
260+
261+
# This is the case where we are at max speed
262+
# special functionality is that this block must
263+
# add a point where we would need to start declerating to reach
264+
# the final point
265+
elif current_speed == max_speed:
266+
next_point = points[cur_index+1]
267+
# continue on with max speed
268+
# print("In case three")
269+
270+
# add point to start decelerating
271+
if next_point[0] + min_delta_x_stop >= points[-1][0]:
272+
# print("Adding new point to start decelerating")
273+
cur_time += (points[-1][0] - min_delta_x_stop - cur_point[0])/current_speed
274+
cur_point = [points[-1][0] - min_delta_x_stop,0]
275+
current_speed = max_speed
276+
else:
277+
# Continue on to next point
278+
# print("Continuing on to next point")
279+
cur_time += (next_point[0] - cur_point[0])/current_speed
280+
cur_point = next_point
281+
cur_index += 1
282+
283+
# This is an edge case and should only be reach
284+
# if the initial speed is greater than the max speed
285+
elif current_speed > max_speed:
286+
# We need to hit the breaks
287+
288+
next_point = points[cur_index+1]
289+
# print("In case four")
290+
# slow down to max speed
291+
delta_t_to_max_speed = (current_speed - max_speed)/deceleration
292+
delta_x_to_max_speed = current_speed*delta_t_to_max_speed - 0.5*deceleration*delta_t_to_max_speed**2
293+
294+
# If we would reach the next point before slowing down to max speed
295+
# keep going until we reach the next point
296+
if cur_point[0] + delta_x_to_max_speed >= next_point[0]:
297+
delta_t_to_next_x = compute_time_to_x(cur_point[0], next_point[0], current_speed, -deceleration)
298+
cur_time += delta_t_to_next_x
299+
cur_point = [next_point[0], 0]
300+
current_speed -= delta_t_to_next_x*deceleration
301+
cur_index += 1
302+
else:
303+
# We would reach max speed before the next point
304+
# we need to add a new point at the point where we
305+
# would reach max speed
306+
cur_time += delta_t_to_max_speed
307+
cur_point = [cur_point[0] + delta_x_to_max_speed, 0]
308+
current_speed = max_speed
309+
152310
else:
153-
new_points.append([new_points[-1][0] + current_speed * dt, 0])
154-
311+
# not sure what falls here
312+
raise ValueError("LONGITUDINAL PLAN ERROR: Not sure how we ended up here")
313+
314+
new_points.append(cur_point)
315+
new_times.append(cur_time)
316+
velocities.append(current_speed)
317+
155318
points = new_points
319+
times = new_times
320+
print("[PLAN] Computed points:", points)
321+
print("[TIME] Computed time:", times)
322+
print("[Velocities] Computed velocities:", velocities)
156323

157-
# for p, t in zip(points, times):
158-
# print(f"Time: {t:.2f}, Point: {p}")
324+
#=============================================
159325

160-
trajectory = Trajectory(path.frame,points,times)
326+
trajectory = Trajectory(path.frame,points,times,velocities)
161327
return trajectory
162328

329+
def compute_time_to_x(x0 : float, x1 : float, v : float, a : float) -> float:
330+
"""Computes the time to go from x0 to x1 with initial velocity v0 and final velocity v1
331+
with constant acceleration a. I am assuming that we will always have a solution by settings
332+
discriminant equal to zero, i'm not sure if this is an issue."""
333+
334+
"""Consider changing the system to use linear operators instead of explicitly calculating because of instances here"""
335+
# x1 = x0 + v0*t + 0.5*a*t^2
336+
# x1 - x0 = v0*t + 0.5*a*t^2
337+
# 0.5*a*t^2 + v0*t + x0 - x1 = 0
338+
# t = (-v0 + sqrt(v0^2 - 4*0.5*a*(x0-x1)))/(2*0.5*a)
339+
# t = (-v0 + sqrt(v0^2 + 2*a*(x1-x0)))/a
340+
# print("x0: ", x0)
341+
# print("x1: ", x1)
342+
# print("v: ", v)
343+
# print("a: ", a)
344+
345+
t1 = (-v + max(0,(v**2 - 2*a*(x0-x1)))**0.5)/a
346+
t2 = (-v - max(0,(v**2 - 2*a*(x0-x1)))**0.5)/a
347+
348+
# print("t1: ", t1)
349+
# print("t2: ", t2)
350+
351+
if math.isnan(t1): t1 = 0
352+
if math.isnan(t2): t2 = 0
353+
354+
# print("t1: ", t1)
355+
# print("t2: ", t2)
356+
357+
valid_times = [n for n in [t1, t2] if n > 0]
358+
if valid_times:
359+
return min(valid_times)
360+
else:
361+
return 0.0
362+
363+
def compute_time_triangle(x0 : float, xf: float, v0: float, vf : float, acceleration : float, deceleration : float) -> float:
364+
"""
365+
Compute the time to go from current point assuming we are accelerating to the point at which
366+
we would need to start breaking in order to reach the final point with velocity 0."""
367+
# xf = v0*t1 + .5a*t1^2 + v1t2 -0.5d*t2^2
368+
# 0 = v1 - d*t2 = v0 + a*t1 - d * t2
369+
# t1 = (d*t2 - v0)/a
370+
# xf = v0*(d*t2 - v0)/a + 0.5*a*(d*t2 - v0)^2/a^2 + v1*t2 - 0.5*d*t2^2
371+
# xf = v0*d*t2/a - v0^2/a + 0.5*a*(d*t2^2 - 2*v0*d*t2 + v0^2)/a^2 + v1*t2 - 0.5*d*t2^2
372+
# 0 = t2^2(0.5*a*d/a^2 - 0.5*d) + t2(v0*d/a - v0*d*a/a^2 + v1) -v0^2/a +0.5*a*v0^2/a^2 -xf
373+
roots = quad_root(0.5*acceleration + acceleration**2/deceleration - 0.5*acceleration**2/deceleration,
374+
v0+2*acceleration*v0/deceleration - acceleration*v0/deceleration,
375+
x0 - xf + v0**2/deceleration - 0.5*v0**2/deceleration)
376+
print(roots)
377+
t1 = max(roots)
378+
assert t1 > 0
379+
return t1
380+
381+
def quad_root(a : float, b : float, c : float) -> float:
382+
x1 = (-b + max(0,(b**2 - 4*a*c))**0.5)/(2*a)
383+
x2 = (-b - max(0,(b**2 - 4*a*c))**0.5)/(2*a)
384+
385+
# print("t1: ", t1)
386+
# print("t2: ", t2)
387+
388+
if math.isnan(x1): x1 = 0
389+
if math.isnan(x2): x2 = 0
390+
391+
# print("t1: ", t1)
392+
# print("t2: ", t2)
393+
394+
valid_roots = [n for n in [x1, x2] if not type(n) is complex]
395+
# print(f"Valid roots {valid_roots}")
396+
return valid_roots
163397

164398
def longitudinal_brake(path : Path, deceleration : float, current_speed : float) -> Trajectory:
165399
"""Generates a longitudinal trajectory for braking along a path."""

GEMstack/state/trajectory.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ def trim(self, start : float, end : float) -> Path:
169169
class Trajectory(Path):
170170
"""A timed, piecewise linear path."""
171171
times : List[float]
172+
velocities : Optional[List[float]] = None
172173

173174
def domain(self) -> Tuple[float,float]:
174175
"""Returns the time parameter domain"""

launch/pedestrian_detection.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,5 @@ variants:
9797
# agent_detection : pedestrian_detection.FakePedestrianDetector2D
9898
agent_detection : OmniscientAgentDetector #this option reads agents from the simulator
9999
state_estimation : OmniscientStateEstimator
100+
agent_detection : OmniscientAgentDetector
101+

0 commit comments

Comments
 (0)