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
250 x_point = (c2 - c1) / (m1 - m2)
252 return np.array([x_point, line1(x_point)])
254 def _is_left_turn(self, intersection_point: np.array, end_point: np.array) -> bool:
256 Determine if a trajectory will be a left turn.
258 Uses the determinant to determine whether the arc formed by the
259 intersection and end point turns left or right.
263 intersection_point: np.array(2,)
264 The intersection point of the lines formed from the start
266 end_point: np.array(2,)
267 The chosen end point of the trajectory
272 True if curve turns left, false otherwise
275 matrix = np.vstack([intersection_point, end_point])
276 det = np.linalg.det(matrix)
280 def _is_dir_vec_correct(
281 self, point1: np.array, point2: np.array, line_angle: float
284 Check that the direction vector agrees with the line angle.
286 The direction vector is defined as the vector from point 1 to
292 The start point of the vector
294 The end point of the vector
296 The angle of a line to compare against the vector
301 True if both line and vector point in same direction
305 m = abs(np.tan(line_angle).round(5))
310 direction_vec_from_points = point2 - point1
312 direction_vec_from_gradient = np.array([1, m])
315 if abs(line_angle) > np.pi / 2:
316 direction_vec_from_gradient = np.array([-1, m])
317 elif abs(line_angle) == np.pi / 2:
318 direction_vec_from_gradient = np.array([0, m])
320 direction_vec_from_gradient = direction_vec_from_gradient.round(5)
321 direction_vec_from_points = direction_vec_from_points.round(5)
324 np.sign(direction_vec_from_points) == np.sign(direction_vec_from_gradient)
330 def _calculate_trajectory_params(
331 self, end_point: np.array, start_angle: float, end_angle: float
332 ) -> Union[TrajectoryParameters,
None]:
334 Calculate the parameters for a trajectory with the desired constraints.
336 The trajectory may consist of an arc and at most two line segments.
337 A straight trajectory will consist of a single line segment. Similarly,
338 a purely curving trajectory will only consist of an arc.
341 1. Extend a line from (0,0) with angle of start_angle
342 2. Extend a line from end_point with angle of end_angle
343 3. Compute their intersection point, I
344 4. Check that the intersection point leads to a
346 - If I is too close to (0,0) or the end point then
347 no arc greater than the turning radius will reach
348 from (0,0) to end point
350 If two segments from the same exterior point are tangent to
351 a circle then they are congruent
355 end_point: np.array(2,)
356 The desired end point of the trajectory
358 The start angle of the trajectory in radians
360 The end angle of the trajectory in radians
364 TrajectoryParameters or None
365 If a valid trajectory exists then the Trajectory parameters
366 are returned, otherwise None
370 arc_start_point = np.array([0, 0])
371 arc_end_point = end_point
375 m1 = np.tan(start_angle).round(5)
379 m2 = np.tan(end_angle).round(5)
385 if round(-m2 * x2 + y2, 5) == 0:
386 return TrajectoryParameters.no_arc(
387 end_point=end_point, start_angle=start_angle, end_angle=end_angle
392 abs(start_angle) == np.pi / 2
and arc_end_point[0] == arc_start_point[0]
394 return TrajectoryParameters.no_arc(
396 start_angle=start_angle,
402 'No trajectory possible for equivalent start and '
403 + f
'end angles that also passes through p = {x2, y2}'
413 arc_start_point, intersection_point, start_angle
416 'No trajectory possible since intersection point occurs '
417 +
'before start point on line 1'
423 if not self.
_is_dir_vec_correct_is_dir_vec_correct(intersection_point, arc_end_point, end_angle):
425 'No trajectory possible since intersection point occurs '
426 +
'after end point on line 2'
431 dist_a = round(np.linalg.norm(arc_start_point - intersection_point), 5)
434 dist_b = round(np.linalg.norm(arc_end_point - intersection_point), 5)
437 angle_between_lines = np.pi - abs(end_angle - start_angle)
446 min_valid_distance = round(
447 self.
turning_radiusturning_radius / np.tan(angle_between_lines / 2), 5
452 if dist_a < min_valid_distance
or dist_b < min_valid_distance:
454 'No trajectory possible where radius is larger than '
455 +
'minimum turning radius'
462 vec_line2 = arc_end_point - intersection_point
463 vec_line2 /= np.linalg.norm(vec_line2)
464 arc_end_point = intersection_point + dist_a * vec_line2
466 elif dist_a > dist_b:
469 vec_line1 = arc_start_point - intersection_point
470 vec_line1 /= np.linalg.norm(vec_line1)
472 arc_start_point = intersection_point + dist_b * vec_line1
474 x1, y1 = arc_start_point
475 x2, y2 = arc_end_point
483 return -1 / m2 * (x - x2) + y2
485 circle_center = np.array([x1, perp_line2(x1)])
489 return -1 / m1 * (x - x1) + y1
491 circle_center = np.array([x2, perp_line1(x2)])
493 perp_m1 = -1 / m1
if m1 != 0
else 0
494 perp_m2 = -1 / m2
if m2 != 0
else 0
497 perp_m1, -perp_m1 * x1 + y1, perp_m2, -perp_m2 * x2 + y2
502 radius = np.linalg.norm(circle_center - arc_end_point).round(5)
503 x_offset = circle_center[0].round(5)
504 y_offset = circle_center[1].round(5)
508 'Calculated circle radius is smaller than allowed turning '
509 + f
'radius: r = {radius}, min_radius = {self.turning_radius}'
513 left_turn = self.
_is_left_turn_is_left_turn(intersection_point, end_point)
532 primitive_resolution: float,
533 ) -> Union[Trajectory,
None]:
535 Create a trajectory from (0,0, start_angle) to (end_point, end_angle).
537 The trajectory will consist of a path that contains discrete points
538 that are spaced primitive_resolution apart.
542 end_point: np.array(2,)
543 The desired end point of the trajectory
545 The start angle of the trajectory in radians
547 The end angle of the trajectory in radians
548 primitive_resolution: float
549 The spacing between points along the trajectory
554 If a valid trajectory exists then the Trajectory is returned,
559 end_point, start_angle, end_angle
562 if trajectory_params
is None:
565 logger.debug(
'Trajectory found')
567 trajectory_path = self.
_create_path_create_path(trajectory_params, primitive_resolution)
569 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)