Skip to content

Commit 6f06b53

Browse files
authored
fix dubins path length bug and clean up codes. (AtsushiSakai#527)
* fix dubins path length bug and clean up codes. * fix line length CI error * fix line length CI error * fix line length CI error * fix line length CI error * fix line length CI error * fix line length CI error * fix line length CI error * fix line length CI error
1 parent 21e7487 commit 6f06b53

File tree

4 files changed

+124
-94
lines changed

4 files changed

+124
-94
lines changed

PathPlanning/DubinsPath/dubins_path_planning.py

Lines changed: 91 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,46 @@
1414
show_animation = True
1515

1616

17+
def dubins_path_planning(s_x, s_y, s_yaw, g_x, g_y, g_yaw, curvature,
18+
step_size=0.1):
19+
"""
20+
Dubins path planner
21+
22+
:param s_x: x position of start point [m]
23+
:param s_y: y position of start point [m]
24+
:param s_yaw: yaw angle of start point [rad]
25+
:param g_x: x position of end point [m]
26+
:param g_y: y position of end point [m]
27+
:param g_yaw: yaw angle of end point [rad]
28+
:param curvature: curvature for curve [1/m]
29+
:param step_size: (optional) step size between two path points [m]
30+
:return:
31+
x_list: x positions of a path
32+
y_list: y positions of a path
33+
yaw_list: yaw angles of a path
34+
modes: mode list of a path
35+
lengths: length of path segments.
36+
"""
37+
38+
g_x -= s_x
39+
g_y -= s_y
40+
41+
l_rot = Rot.from_euler('z', s_yaw).as_matrix()[0:2, 0:2]
42+
le_xy = np.stack([g_x, g_y]).T @ l_rot
43+
le_yaw = g_yaw - s_yaw
44+
45+
lp_x, lp_y, lp_yaw, modes, lengths = dubins_path_planning_from_origin(
46+
le_xy[0], le_xy[1], le_yaw, curvature, step_size)
47+
48+
rot = Rot.from_euler('z', -s_yaw).as_matrix()[0:2, 0:2]
49+
converted_xy = np.stack([lp_x, lp_y]).T @ rot
50+
x_list = converted_xy[:, 0] + s_x
51+
y_list = converted_xy[:, 1] + s_y
52+
yaw_list = [pi_2_pi(i_yaw + s_yaw) for i_yaw in lp_yaw]
53+
54+
return x_list, y_list, yaw_list, modes, lengths
55+
56+
1757
def mod2pi(theta):
1858
return theta - 2.0 * math.pi * math.floor(theta / 2.0 / math.pi)
1959

@@ -148,14 +188,14 @@ def dubins_path_planning_from_origin(end_x, end_y, end_yaw, curvature,
148188
alpha = mod2pi(- theta)
149189
beta = mod2pi(end_yaw - theta)
150190

151-
planners = [left_straight_left, right_straight_right, left_straight_right,
152-
right_straight_left, right_left_right,
153-
left_right_left]
191+
planning_funcs = [left_straight_left, right_straight_right,
192+
left_straight_right, right_straight_left,
193+
right_left_right, left_right_left]
154194

155195
best_cost = float("inf")
156196
bt, bp, bq, best_mode = None, None, None, None
157197

158-
for planner in planners:
198+
for planner in planning_funcs:
159199
t, p, q, mode = planner(alpha, beta, d)
160200
if t is None:
161201
continue
@@ -166,10 +206,15 @@ def dubins_path_planning_from_origin(end_x, end_y, end_yaw, curvature,
166206
best_cost = cost
167207
lengths = [bt, bp, bq]
168208

169-
x_list, y_list, yaw_list, directions = generate_local_course(
170-
sum(lengths), lengths, best_mode, curvature, step_size)
209+
x_list, y_list, yaw_list, directions = generate_local_course(sum(lengths),
210+
lengths,
211+
best_mode,
212+
curvature,
213+
step_size)
214+
215+
lengths = [length / curvature for length in lengths]
171216

172-
return x_list, y_list, yaw_list, best_mode, best_cost
217+
return x_list, y_list, yaw_list, best_mode, lengths
173218

174219

175220
def interpolate(ind, length, mode, max_curvature, origin_x, origin_y,
@@ -203,49 +248,15 @@ def interpolate(ind, length, mode, max_curvature, origin_x, origin_y,
203248
return path_x, path_y, path_yaw, directions
204249

205250

206-
def dubins_path_planning(s_x, s_y, s_yaw, g_x, g_y, g_yaw, c, step_size=0.1):
207-
"""
208-
Dubins path planner
209-
210-
input:
211-
s_x x position of start point [m]
212-
s_y y position of start point [m]
213-
s_yaw yaw angle of start point [rad]
214-
g_x x position of end point [m]
215-
g_y y position of end point [m]
216-
g_yaw yaw angle of end point [rad]
217-
c curvature [1/m]
218-
219-
"""
220-
221-
g_x = g_x - s_x
222-
g_y = g_y - s_y
223-
224-
l_rot = Rot.from_euler('z', s_yaw).as_matrix()[0:2, 0:2]
225-
le_xy = np.stack([g_x, g_y]).T @ l_rot
226-
le_yaw = g_yaw - s_yaw
227-
228-
lp_x, lp_y, lp_yaw, mode, lengths = dubins_path_planning_from_origin(
229-
le_xy[0], le_xy[1], le_yaw, c, step_size)
230-
231-
rot = Rot.from_euler('z', -s_yaw).as_matrix()[0:2, 0:2]
232-
converted_xy = np.stack([lp_x, lp_y]).T @ rot
233-
x_list = converted_xy[:, 0] + s_x
234-
y_list = converted_xy[:, 1] + s_y
235-
yaw_list = [pi_2_pi(i_yaw + s_yaw) for i_yaw in lp_yaw]
236-
237-
return x_list, y_list, yaw_list, mode, lengths
238-
239-
240-
def generate_local_course(total_length, lengths, mode, max_curvature,
251+
def generate_local_course(total_length, lengths, modes, max_curvature,
241252
step_size):
242253
n_point = math.trunc(total_length / step_size) + len(lengths) + 4
243254

244-
path_x = [0.0 for _ in range(n_point)]
245-
path_y = [0.0 for _ in range(n_point)]
246-
path_yaw = [0.0 for _ in range(n_point)]
255+
p_x = [0.0 for _ in range(n_point)]
256+
p_y = [0.0 for _ in range(n_point)]
257+
p_yaw = [0.0 for _ in range(n_point)]
247258
directions = [0.0 for _ in range(n_point)]
248-
index = 1
259+
ind = 1
249260

250261
if lengths[0] > 0.0:
251262
directions[0] = 1
@@ -254,7 +265,7 @@ def generate_local_course(total_length, lengths, mode, max_curvature,
254265

255266
ll = 0.0
256267

257-
for (m, length, i) in zip(mode, lengths, range(len(mode))):
268+
for (m, length, i) in zip(modes, lengths, range(len(modes))):
258269
if length == 0.0:
259270
continue
260271
elif length > 0.0:
@@ -263,54 +274,57 @@ def generate_local_course(total_length, lengths, mode, max_curvature,
263274
dist = -step_size
264275

265276
# set origin state
266-
origin_x, origin_y, origin_yaw = \
267-
path_x[index], path_y[index], path_yaw[index]
277+
origin_x, origin_y, origin_yaw = p_x[ind], p_y[ind], p_yaw[ind]
268278

269-
index -= 1
279+
ind -= 1
270280
if i >= 1 and (lengths[i - 1] * lengths[i]) > 0:
271281
pd = - dist - ll
272282
else:
273283
pd = dist - ll
274284

275285
while abs(pd) <= abs(length):
276-
index += 1
277-
path_x, path_y, path_yaw, directions = interpolate(
278-
index, pd, m, max_curvature, origin_x, origin_y, origin_yaw,
279-
path_x, path_y, path_yaw, directions)
286+
ind += 1
287+
p_x, p_y, p_yaw, directions = interpolate(ind, pd, m,
288+
max_curvature,
289+
origin_x,
290+
origin_y,
291+
origin_yaw,
292+
p_x, p_y,
293+
p_yaw,
294+
directions)
280295
pd += dist
281296

282297
ll = length - pd - dist # calc remain length
283298

284-
index += 1
285-
path_x, path_y, path_yaw, directions = interpolate(
286-
index, length, m, max_curvature, origin_x, origin_y, origin_yaw,
287-
path_x, path_y, path_yaw, directions)
299+
ind += 1
300+
p_x, p_y, p_yaw, directions = interpolate(ind, length, m,
301+
max_curvature,
302+
origin_x, origin_y,
303+
origin_yaw,
304+
p_x, p_y, p_yaw,
305+
directions)
288306

289-
if len(path_x) <= 1:
307+
if len(p_x) <= 1:
290308
return [], [], [], []
291309

292310
# remove unused data
293-
while len(path_x) >= 1 and path_x[-1] == 0.0:
294-
path_x.pop()
295-
path_y.pop()
296-
path_yaw.pop()
311+
while len(p_x) >= 1 and p_x[-1] == 0.0:
312+
p_x.pop()
313+
p_y.pop()
314+
p_yaw.pop()
297315
directions.pop()
298316

299-
return path_x, path_y, path_yaw, directions
317+
return p_x, p_y, p_yaw, directions
300318

301319

302320
def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r",
303321
ec="k"): # pragma: no cover
304-
"""
305-
Plot arrow
306-
"""
307-
308322
if not isinstance(x, float):
309323
for (i_x, i_y, i_yaw) in zip(x, y, yaw):
310324
plot_arrow(i_x, i_y, i_yaw)
311325
else:
312-
plt.arrow(x, y, length * math.cos(yaw), length * math.sin(yaw),
313-
fc=fc, ec=ec, head_width=width, head_length=width)
326+
plt.arrow(x, y, length * math.cos(yaw), length * math.sin(yaw), fc=fc,
327+
ec=ec, head_width=width, head_length=width)
314328
plt.plot(x, y)
315329

316330

@@ -327,9 +341,13 @@ def main():
327341

328342
curvature = 1.0
329343

330-
path_x, path_y, path_yaw, mode, path_length = dubins_path_planning(
331-
start_x, start_y, start_yaw,
332-
end_x, end_y, end_yaw, curvature)
344+
path_x, path_y, path_yaw, mode, lengths = dubins_path_planning(start_x,
345+
start_y,
346+
start_yaw,
347+
end_x,
348+
end_y,
349+
end_yaw,
350+
curvature)
333351

334352
if show_animation:
335353
plt.plot(path_x, path_y, label="final course " + "".join(mode))

PathPlanning/RRTDubins/rrt_dubins.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,10 @@ def plot_start_goal_arrow(self): # pragma: no cover
133133

134134
def steer(self, from_node, to_node):
135135

136-
px, py, pyaw, mode, course_length = dubins_path_planning.dubins_path_planning(
137-
from_node.x, from_node.y, from_node.yaw,
138-
to_node.x, to_node.y, to_node.yaw, self.curvature)
136+
px, py, pyaw, mode, course_lengths = \
137+
dubins_path_planning.dubins_path_planning(
138+
from_node.x, from_node.y, from_node.yaw,
139+
to_node.x, to_node.y, to_node.yaw, self.curvature)
139140

140141
if len(px) <= 1: # cannot find a dubins path
141142
return None
@@ -148,7 +149,7 @@ def steer(self, from_node, to_node):
148149
new_node.path_x = px
149150
new_node.path_y = py
150151
new_node.path_yaw = pyaw
151-
new_node.cost += course_length
152+
new_node.cost += sum([abs(c) for c in course_lengths])
152153
new_node.parent = from_node
153154

154155
return new_node

PathPlanning/RRTStarDubins/rrt_star_dubins.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,10 @@ def plot_start_goal_arrow(self):
139139

140140
def steer(self, from_node, to_node):
141141

142-
px, py, pyaw, mode, course_length = dubins_path_planning.dubins_path_planning(
143-
from_node.x, from_node.y, from_node.yaw,
144-
to_node.x, to_node.y, to_node.yaw, self.curvature)
142+
px, py, pyaw, mode, course_lengths = \
143+
dubins_path_planning.dubins_path_planning(
144+
from_node.x, from_node.y, from_node.yaw,
145+
to_node.x, to_node.y, to_node.yaw, self.curvature)
145146

146147
if len(px) <= 1: # cannot find a dubins path
147148
return None
@@ -154,18 +155,20 @@ def steer(self, from_node, to_node):
154155
new_node.path_x = px
155156
new_node.path_y = py
156157
new_node.path_yaw = pyaw
157-
new_node.cost += course_length
158+
new_node.cost += sum([abs(c) for c in course_lengths])
158159
new_node.parent = from_node
159160

160161
return new_node
161162

162163
def calc_new_cost(self, from_node, to_node):
163164

164-
_, _, _, _, course_length = dubins_path_planning.dubins_path_planning(
165+
_, _, _, _, course_lengths = dubins_path_planning.dubins_path_planning(
165166
from_node.x, from_node.y, from_node.yaw,
166167
to_node.x, to_node.y, to_node.yaw, self.curvature)
167168

168-
return from_node.cost + course_length
169+
cost = sum([abs(c) for c in course_lengths])
170+
171+
return from_node.cost + cost
169172

170173
def get_random_node(self):
171174

tests/test_dubins_path_planning.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import conftest
21
import numpy as np
2+
3+
import conftest
34
from PathPlanning.DubinsPath import dubins_path_planning
45

56
np.random.seed(12345)
67

78

8-
def check_edge_condition(px, py, pyaw, start_x, start_y, start_yaw,
9-
end_x, end_y, end_yaw):
9+
def check_edge_condition(px, py, pyaw, start_x, start_y, start_yaw, end_x,
10+
end_y, end_yaw):
1011
assert (abs(px[0] - start_x) <= 0.01)
1112
assert (abs(py[0] - start_y) <= 0.01)
1213
assert (abs(pyaw[0] - start_yaw) <= 0.01)
@@ -15,6 +16,12 @@ def check_edge_condition(px, py, pyaw, start_x, start_y, start_yaw,
1516
assert (abs(pyaw[-1] - end_yaw) <= 0.01)
1617

1718

19+
def check_path_length(px, py, lengths):
20+
path_len = sum(
21+
[np.hypot(dx, dy) for (dx, dy) in zip(np.diff(px), np.diff(py))])
22+
assert (abs(path_len - sum(lengths)) <= 0.1)
23+
24+
1825
def test_1():
1926
start_x = 1.0 # [m]
2027
start_y = 1.0 # [m]
@@ -26,12 +33,12 @@ def test_1():
2633

2734
curvature = 1.0
2835

29-
px, py, pyaw, mode, clen = dubins_path_planning.dubins_path_planning(
36+
px, py, pyaw, mode, lengths = dubins_path_planning.dubins_path_planning(
3037
start_x, start_y, start_yaw, end_x, end_y, end_yaw, curvature)
3138

32-
check_edge_condition(px, py, pyaw,
33-
start_x, start_y, start_yaw,
34-
end_x, end_y, end_yaw)
39+
check_edge_condition(px, py, pyaw, start_x, start_y, start_yaw, end_x,
40+
end_y, end_yaw)
41+
check_path_length(px, py, lengths)
3542

3643

3744
def test_2():
@@ -53,12 +60,13 @@ def test_3():
5360

5461
curvature = 1.0 / (np.random.rand() * 5.0)
5562

56-
px, py, pyaw, mode, clen = dubins_path_planning.dubins_path_planning(
63+
px, py, pyaw, mode, lengths = \
64+
dubins_path_planning.dubins_path_planning(
5765
start_x, start_y, start_yaw, end_x, end_y, end_yaw, curvature)
5866

59-
check_edge_condition(px, py, pyaw,
60-
start_x, start_y, start_yaw,
61-
end_x, end_y, end_yaw)
67+
check_edge_condition(px, py, pyaw, start_x, start_y, start_yaw, end_x,
68+
end_y, end_yaw)
69+
check_path_length(px, py, lengths)
6270

6371

6472
if __name__ == '__main__':

0 commit comments

Comments
 (0)