16 from typing
import Tuple, Union
20 from trajectory
import Path, Trajectory, TrajectoryParameters
22 logger = logging.getLogger(__name__)
26 """Handles all the logic for generating trajectories."""
29 """Init TrajectoryGenerator using the user supplied config."""
33 self, trajectory_params: TrajectoryParameters, t: float
34 ) -> Tuple[float, float, float]:
36 Get point on the arc trajectory using the following parameterization.
38 r(t) = <R * cos(t - pi/2) + a, R * sin(t - pi/2) + b>
46 trajectory_params: TrajectoryParameters
47 The parameters that describe the arc to create
49 A value between 0 - 1 that denotes where along the arc
50 to calculate the point
55 x coordinate of generated point
57 y coordinate of generated point
59 angle of tangent line to arc at point (x,y)
62 start_angle = trajectory_params.start_angle
64 arc_dist = t * trajectory_params.arc_length
65 angle_step = arc_dist / trajectory_params.turning_radius
67 if trajectory_params.left_turn:
70 t = start_angle + angle_step
72 trajectory_params.turning_radius * np.cos(t - np.pi / 2)
73 + trajectory_params.x_offset
76 trajectory_params.turning_radius * np.sin(t - np.pi / 2)
77 + trajectory_params.y_offset
85 start_angle = -start_angle
89 t = start_angle + angle_step
91 trajectory_params.turning_radius * -np.cos(t + np.pi / 2)
92 + trajectory_params.x_offset
95 trajectory_params.turning_radius * np.sin(t + np.pi / 2)
96 + trajectory_params.y_offset
104 self, start_point: np.array, end_point: np.array, t: float
105 ) -> Tuple[float, float]:
107 Get point on a line segment using the following parameterization.
109 r(t) = p + t * (q - p)
116 start_point: np.array(2,)
117 Starting point of the line segment
118 end_point: np.array(2,)
119 End point of the line segment
121 A value between 0 - 1 that denotes where along the segment
122 to calculate the point
127 x coordinate of generated point
129 y coordinate of generated point
132 return start_point + t * (end_point - start_point)
135 self, trajectory_params: TrajectoryParameters, primitive_resolution: float
138 Create the full trajectory path from the given trajectory parameters.
142 trajectory_params: TrajectoryParameters
143 The parameters that describe the trajectory to create
144 primitive_resolution: float
145 The desired distance between sampled points along the line.
146 This value is not strictly adhered to as the path may not
147 be neatly divisible, however the spacing will be as close
153 The trajectory path described by the trajectory parameters
156 number_of_steps = np.round(
157 trajectory_params.total_length / primitive_resolution
159 t_step = 1 / number_of_steps
161 start_to_arc_dist = np.linalg.norm(trajectory_params.arc_start_point)
163 transition_points = [
164 start_to_arc_dist / trajectory_params.total_length,
165 (start_to_arc_dist + trajectory_params.arc_length)
166 / trajectory_params.total_length,
175 for i
in range(1, number_of_steps + 1):
178 cur_t = min(cur_t, 1)
181 if cur_t <= transition_points[0]:
182 line_t = cur_t / transition_points[0]
184 np.array([0, 0]), trajectory_params.arc_start_point, line_t
186 yaw = trajectory_params.start_angle
189 elif cur_t <= transition_points[1]:
190 arc_t = (cur_t - transition_points[0]) / (
191 transition_points[1] - transition_points[0]
193 x, y, yaw = self.
_get_arc_point_get_arc_point(trajectory_params, arc_t)
197 line_t = (cur_t - transition_points[1]) / (1 - transition_points[1])
199 trajectory_params.arc_end_point, trajectory_params.end_point, line_t
201 yaw = trajectory_params.end_angle
212 yaws = np.array(yaws)
216 xs[-1], ys[-1] = trajectory_params.end_point
217 yaws[-1] = trajectory_params.end_angle
219 return Path(xs, ys, yaws)
221 def _get_intersection_point(
222 self, m1: float, c1: float, m2: float, c2: float
225 Get the intersection point of two lines.
227 The two lines are described by m1 * x + c1 and m2 * x + c2.
234 y-intercept of line 1
243 The intersection point of line 1 and 2
249 x_point = (c2 - c1) / (m1 - m2)
251 return np.array([x_point, line1(x_point)])
253 def _is_left_turn(self, intersection_point: np.array, end_point: np.array) -> bool:
255 Determine if a trajectory will be a left turn.
257 Uses the determinant to determine whether the arc formed by the
258 intersection and end point turns left or right.
262 intersection_point: np.array(2,)
263 The intersection point of the lines formed from the start
265 end_point: np.array(2,)
266 The chosen end point of the trajectory
271 True if curve turns left, false otherwise
274 matrix = np.vstack([intersection_point, end_point])
275 det = np.linalg.det(matrix)
279 def _is_dir_vec_correct(
280 self, point1: np.array, point2: np.array, line_angle: float
283 Check that the direction vector agrees with the line angle.
285 The direction vector is defined as the vector from point 1 to
291 The start point of the vector
293 The end point of the vector
295 The angle of a line to compare against the vector
300 True if both line and vector point in same direction
304 m = abs(np.tan(line_angle).round(5))
309 direction_vec_from_points = point2 - point1
311 direction_vec_from_gradient = np.array([1, m])
314 if abs(line_angle) > np.pi / 2:
315 direction_vec_from_gradient = np.array([-1, m])
316 elif abs(line_angle) == np.pi / 2:
317 direction_vec_from_gradient = np.array([0, m])
319 direction_vec_from_gradient = direction_vec_from_gradient.round(5)
320 direction_vec_from_points = direction_vec_from_points.round(5)
323 np.sign(direction_vec_from_points) == np.sign(direction_vec_from_gradient)
329 def _calculate_trajectory_params(
330 self, end_point: np.array, start_angle: float, end_angle: float
331 ) -> Union[TrajectoryParameters,
None]:
333 Calculate the parameters for a trajectory with the desired constraints.
335 The trajectory may consist of an arc and at most two line segments.
336 A straight trajectory will consist of a single line segment. Similarly,
337 a purely curving trajectory will only consist of an arc.
340 1. Extend a line from (0,0) with angle of start_angle
341 2. Extend a line from end_point with angle of end_angle
342 3. Compute their intersection point, I
343 4. Check that the intersection point leads to a
345 - If I is too close to (0,0) or the end point then
346 no arc greater than the turning radius will reach
347 from (0,0) to end point
349 If two segments from the same exterior point are tangent to
350 a circle then they are congruent
354 end_point: np.array(2,)
355 The desired end point of the trajectory
357 The start angle of the trajectory in radians
359 The end angle of the trajectory in radians
363 TrajectoryParameters or None
364 If a valid trajectory exists then the Trajectory parameters
365 are returned, otherwise None
369 arc_start_point = np.array([0, 0])
370 arc_end_point = end_point
374 m1 = np.tan(start_angle).round(5)
378 m2 = np.tan(end_angle).round(5)
384 if round(-m2 * x2 + y2, 5) == 0:
385 return TrajectoryParameters.no_arc(
386 end_point=end_point, start_angle=start_angle, end_angle=end_angle
391 abs(start_angle) == np.pi / 2
and arc_end_point[0] == arc_start_point[0]
393 return TrajectoryParameters.no_arc(
395 start_angle=start_angle,
401 'No trajectory possible for equivalent start and '
402 + f
'end angles that also passes through p = {x2, y2}'
412 arc_start_point, intersection_point, start_angle
415 'No trajectory possible since intersection point occurs '
416 +
'before start point on line 1'
422 if not self.
_is_dir_vec_correct_is_dir_vec_correct(intersection_point, arc_end_point, end_angle):
424 'No trajectory possible since intersection point occurs '
425 +
'after end point on line 2'
430 dist_a = round(np.linalg.norm(arc_start_point - intersection_point), 5)
433 dist_b = round(np.linalg.norm(arc_end_point - intersection_point), 5)
436 angle_between_lines = np.pi - abs(end_angle - start_angle)
445 min_valid_distance = round(
446 self.
turning_radiusturning_radius / np.tan(angle_between_lines / 2), 5
451 if dist_a < min_valid_distance
or dist_b < min_valid_distance:
453 'No trajectory possible where radius is larger than '
454 +
'minimum turning radius'
461 vec_line2 = arc_end_point - intersection_point
462 vec_line2 /= np.linalg.norm(vec_line2)
463 arc_end_point = intersection_point + dist_a * vec_line2
465 elif dist_a > dist_b:
468 vec_line1 = arc_start_point - intersection_point
469 vec_line1 /= np.linalg.norm(vec_line1)
471 arc_start_point = intersection_point + dist_b * vec_line1
473 x1, y1 = arc_start_point
474 x2, y2 = arc_end_point
482 return -1 / m2 * (x - x2) + y2
484 circle_center = np.array([x1, perp_line2(x1)])
488 return -1 / m1 * (x - x1) + y1
490 circle_center = np.array([x2, perp_line1(x2)])
492 perp_m1 = -1 / m1
if m1 != 0
else 0
493 perp_m2 = -1 / m2
if m2 != 0
else 0
496 perp_m1, -perp_m1 * x1 + y1, perp_m2, -perp_m2 * x2 + y2
501 radius = np.linalg.norm(circle_center - arc_end_point).round(5)
502 x_offset = circle_center[0].round(5)
503 y_offset = circle_center[1].round(5)
507 'Calculated circle radius is smaller than allowed turning '
508 + f
'radius: r = {radius}, min_radius = {self.turning_radius}'
512 left_turn = self.
_is_left_turn_is_left_turn(intersection_point, end_point)
531 primitive_resolution: float,
532 ) -> Union[Trajectory,
None]:
534 Create a trajectory from (0,0, start_angle) to (end_point, end_angle).
536 The trajectory will consist of a path that contains discrete points
537 that are spaced primitive_resolution apart.
541 end_point: np.array(2,)
542 The desired end point of the trajectory
544 The start angle of the trajectory in radians
546 The end angle of the trajectory in radians
547 primitive_resolution: float
548 The spacing between points along the trajectory
553 If a valid trajectory exists then the Trajectory is returned,
558 end_point, start_angle, end_angle
561 if trajectory_params
is None:
564 logger.debug(
'Trajectory found')
566 trajectory_path = self.
_create_path_create_path(trajectory_params, primitive_resolution)
568 return Trajectory(trajectory_path, trajectory_params)
bool _is_dir_vec_correct(self, np.array point1, np.array point2, float line_angle)
Union[TrajectoryParameters, None] _calculate_trajectory_params(self, np.array end_point, float start_angle, float end_angle)
Tuple[float, float] _get_line_point(self, np.array start_point, np.array end_point, float t)
bool _is_left_turn(self, np.array intersection_point, np.array end_point)
Union[Trajectory, None] generate_trajectory(self, np.array end_point, float start_angle, float end_angle, float primitive_resolution)
np.array _get_intersection_point(self, float m1, float c1, float m2, float c2)
def __init__(self, dict config)
Tuple[float, float, float] _get_arc_point(self, TrajectoryParameters trajectory_params, float t)
Path _create_path(self, TrajectoryParameters trajectory_params, float primitive_resolution)