Skip to content

Commit 5a66105

Browse files
authored
Extend frenet_optimal_trajectory to support more scenarios (AtsushiSakai#1114)
* feat: Add third derivative and curvature rate calculations to CubicSpline classes * feat: Extend frenet_optimal_trajectory to support more scenarios * fix: frenet optimal trajectory type check * fix: cubic spline planner code style check * fix: frenet optimal trajectory review * feat: frenet_optimal_trajectory update doc * fix: frenet optimal trajectory review * fix: frenet optimal trajectory * fix: frenet optimal trajectory
1 parent 2bc59fe commit 5a66105

File tree

5 files changed

+674
-157
lines changed

5 files changed

+674
-157
lines changed

PathPlanning/CubicSpline/cubic_spline_planner.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ def calc_position(self, x):
7676
7777
if `x` is outside the data point's `x` range, return None.
7878
79+
Parameters
80+
----------
81+
x : float
82+
x position to calculate y.
83+
7984
Returns
8085
-------
8186
y : float
@@ -99,6 +104,11 @@ def calc_first_derivative(self, x):
99104
100105
if x is outside the input x, return None
101106
107+
Parameters
108+
----------
109+
x : float
110+
x position to calculate first derivative.
111+
102112
Returns
103113
-------
104114
dy : float
@@ -121,6 +131,11 @@ def calc_second_derivative(self, x):
121131
122132
if x is outside the input x, return None
123133
134+
Parameters
135+
----------
136+
x : float
137+
x position to calculate second derivative.
138+
124139
Returns
125140
-------
126141
ddy : float
@@ -137,6 +152,31 @@ def calc_second_derivative(self, x):
137152
ddy = 2.0 * self.c[i] + 6.0 * self.d[i] * dx
138153
return ddy
139154

155+
def calc_third_derivative(self, x):
156+
"""
157+
Calc third derivative at given x.
158+
159+
if x is outside the input x, return None
160+
161+
Parameters
162+
----------
163+
x : float
164+
x position to calculate third derivative.
165+
166+
Returns
167+
-------
168+
dddy : float
169+
third derivative for given x.
170+
"""
171+
if x < self.x[0]:
172+
return None
173+
elif x > self.x[-1]:
174+
return None
175+
176+
i = self.__search_index(x)
177+
dddy = 6.0 * self.d[i]
178+
return dddy
179+
140180
def __search_index(self, x):
141181
"""
142182
search data segment index
@@ -287,6 +327,33 @@ def calc_curvature(self, s):
287327
k = (ddy * dx - ddx * dy) / ((dx ** 2 + dy ** 2)**(3 / 2))
288328
return k
289329

330+
def calc_curvature_rate(self, s):
331+
"""
332+
calc curvature rate
333+
334+
Parameters
335+
----------
336+
s : float
337+
distance from the start point. if `s` is outside the data point's
338+
range, return None.
339+
340+
Returns
341+
-------
342+
k : float
343+
curvature rate for given s.
344+
"""
345+
dx = self.sx.calc_first_derivative(s)
346+
dy = self.sy.calc_first_derivative(s)
347+
ddx = self.sx.calc_second_derivative(s)
348+
ddy = self.sy.calc_second_derivative(s)
349+
dddx = self.sx.calc_third_derivative(s)
350+
dddy = self.sy.calc_third_derivative(s)
351+
a = dx * ddy - dy * ddx
352+
b = dx * dddy - dy * dddx
353+
c = dx * ddx + dy * ddy
354+
d = dx * dx + dy * dy
355+
return (b * d - 3.0 * a * c) / (d * d * d)
356+
290357
def calc_yaw(self, s):
291358
"""
292359
calc yaw
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
"""
2+
3+
A converter between Cartesian and Frenet coordinate systems
4+
5+
author: Wang Zheng (@Aglargil)
6+
7+
Ref:
8+
9+
- [Optimal Trajectory Generation for Dynamic Street Scenarios in a Frenet Frame]
10+
(https://www.researchgate.net/profile/Moritz_Werling/publication/224156269_Optimal_Trajectory_Generation_for_Dynamic_Street_Scenarios_in_a_Frenet_Frame/links/54f749df0cf210398e9277af.pdf)
11+
12+
"""
13+
14+
import math
15+
16+
17+
class CartesianFrenetConverter:
18+
"""
19+
A class for converting states between Cartesian and Frenet coordinate systems
20+
"""
21+
22+
@ staticmethod
23+
def cartesian_to_frenet(rs, rx, ry, rtheta, rkappa, rdkappa, x, y, v, a, theta, kappa):
24+
"""
25+
Convert state from Cartesian coordinate to Frenet coordinate
26+
27+
Parameters
28+
----------
29+
rs: reference line s-coordinate
30+
rx, ry: reference point coordinates
31+
rtheta: reference point heading
32+
rkappa: reference point curvature
33+
rdkappa: reference point curvature rate
34+
x, y: current position
35+
v: velocity
36+
a: acceleration
37+
theta: heading angle
38+
kappa: curvature
39+
40+
Returns
41+
-------
42+
s_condition: [s(t), s'(t), s''(t)]
43+
d_condition: [d(s), d'(s), d''(s)]
44+
"""
45+
dx = x - rx
46+
dy = y - ry
47+
48+
cos_theta_r = math.cos(rtheta)
49+
sin_theta_r = math.sin(rtheta)
50+
51+
cross_rd_nd = cos_theta_r * dy - sin_theta_r * dx
52+
d = math.copysign(math.hypot(dx, dy), cross_rd_nd)
53+
54+
delta_theta = theta - rtheta
55+
tan_delta_theta = math.tan(delta_theta)
56+
cos_delta_theta = math.cos(delta_theta)
57+
58+
one_minus_kappa_r_d = 1 - rkappa * d
59+
d_dot = one_minus_kappa_r_d * tan_delta_theta
60+
61+
kappa_r_d_prime = rdkappa * d + rkappa * d_dot
62+
63+
d_ddot = (-kappa_r_d_prime * tan_delta_theta +
64+
one_minus_kappa_r_d / (cos_delta_theta * cos_delta_theta) *
65+
(kappa * one_minus_kappa_r_d / cos_delta_theta - rkappa))
66+
67+
s = rs
68+
s_dot = v * cos_delta_theta / one_minus_kappa_r_d
69+
70+
delta_theta_prime = one_minus_kappa_r_d / cos_delta_theta * kappa - rkappa
71+
s_ddot = (a * cos_delta_theta -
72+
s_dot * s_dot *
73+
(d_dot * delta_theta_prime - kappa_r_d_prime)) / one_minus_kappa_r_d
74+
75+
return [s, s_dot, s_ddot], [d, d_dot, d_ddot]
76+
77+
@ staticmethod
78+
def frenet_to_cartesian(rs, rx, ry, rtheta, rkappa, rdkappa, s_condition, d_condition):
79+
"""
80+
Convert state from Frenet coordinate to Cartesian coordinate
81+
82+
Parameters
83+
----------
84+
rs: reference line s-coordinate
85+
rx, ry: reference point coordinates
86+
rtheta: reference point heading
87+
rkappa: reference point curvature
88+
rdkappa: reference point curvature rate
89+
s_condition: [s(t), s'(t), s''(t)]
90+
d_condition: [d(s), d'(s), d''(s)]
91+
92+
Returns
93+
-------
94+
x, y: position
95+
theta: heading angle
96+
kappa: curvature
97+
v: velocity
98+
a: acceleration
99+
"""
100+
if abs(rs - s_condition[0]) >= 1.0e-6:
101+
raise ValueError(
102+
"The reference point s and s_condition[0] don't match")
103+
104+
cos_theta_r = math.cos(rtheta)
105+
sin_theta_r = math.sin(rtheta)
106+
107+
x = rx - sin_theta_r * d_condition[0]
108+
y = ry + cos_theta_r * d_condition[0]
109+
110+
one_minus_kappa_r_d = 1 - rkappa * d_condition[0]
111+
112+
tan_delta_theta = d_condition[1] / one_minus_kappa_r_d
113+
delta_theta = math.atan2(d_condition[1], one_minus_kappa_r_d)
114+
cos_delta_theta = math.cos(delta_theta)
115+
116+
theta = CartesianFrenetConverter.normalize_angle(delta_theta + rtheta)
117+
118+
kappa_r_d_prime = rdkappa * d_condition[0] + rkappa * d_condition[1]
119+
120+
kappa = (((d_condition[2] + kappa_r_d_prime * tan_delta_theta) *
121+
cos_delta_theta * cos_delta_theta) / one_minus_kappa_r_d + rkappa) * \
122+
cos_delta_theta / one_minus_kappa_r_d
123+
124+
d_dot = d_condition[1] * s_condition[1]
125+
v = math.sqrt(one_minus_kappa_r_d * one_minus_kappa_r_d *
126+
s_condition[1] * s_condition[1] + d_dot * d_dot)
127+
128+
delta_theta_prime = one_minus_kappa_r_d / cos_delta_theta * kappa - rkappa
129+
130+
a = (s_condition[2] * one_minus_kappa_r_d / cos_delta_theta +
131+
s_condition[1] * s_condition[1] / cos_delta_theta *
132+
(d_condition[1] * delta_theta_prime - kappa_r_d_prime))
133+
134+
return x, y, theta, kappa, v, a
135+
136+
@ staticmethod
137+
def normalize_angle(angle):
138+
"""
139+
Normalize angle to [-pi, pi]
140+
"""
141+
a = math.fmod(angle + math.pi, 2.0 * math.pi)
142+
if a < 0.0:
143+
a += 2.0 * math.pi
144+
return a - math.pi

0 commit comments

Comments
 (0)