@@ -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
164398def longitudinal_brake (path : Path , deceleration : float , current_speed : float ) -> Trajectory :
165399 """Generates a longitudinal trajectory for braking along a path."""
0 commit comments