Skip to content

Commit 32b545f

Browse files
authored
Enhance dubins path docs (AtsushiSakai#664)
* Engance dubins path docs * Update dubins_path.rst * fix doc artifact link in CI * wip * wip * wip * Update dubins_path.rst * wip * wip * wip * wip * wip
1 parent 462a8cd commit 32b545f

File tree

15 files changed

+235
-86
lines changed

15 files changed

+235
-86
lines changed

.circleci/config.yml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
command: |
1515
python -m venv venv
1616
. venv/bin/activate
17+
pip install -r requirements/requirements.txt
1718
pip install -r docs/doc_requirements.txt
1819
cd docs;make html
1920
- store_artifacts:

PathPlanning/DubinsPath/dubins_path_planning.py renamed to PathPlanning/DubinsPath/dubins_path_planner.py

+85-65
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,82 @@
55
author Atsushi Sakai(@Atsushi_twi)
66
77
"""
8-
import math
8+
import sys
9+
import os
10+
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../utils/")
911

10-
import matplotlib.pyplot as plt
12+
import math
1113
import numpy as np
12-
from scipy.spatial.transform import Rotation as Rot
14+
from utils.angle import angle_mod, create_2d_rotation_matrix
1315

1416
show_animation = True
1517

1618

17-
def dubins_path_planning(s_x, s_y, s_yaw, g_x, g_y, g_yaw, curvature,
18-
step_size=0.1):
19+
def plan_dubins_path(s_x, s_y, s_yaw,
20+
g_x, g_y, g_yaw,
21+
curvature,
22+
step_size=0.1):
1923
"""
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
24+
Path dubins path
25+
26+
Parameters
27+
----------
28+
s_x : float
29+
x position of the start point [m]
30+
s_y : float
31+
y position of the start point [m]
32+
s_yaw : float
33+
yaw angle of the start point [rad]
34+
g_x : float
35+
x position of the goal point [m]
36+
g_y : float
37+
y position of the end point [m]
38+
g_yaw : float
39+
yaw angle of the end point [rad]
40+
curvature : float
41+
curvature for curve [1/m]
42+
step_size : float (optional)
43+
step size between two path points [m]. Default is 0.1
44+
45+
Returns
46+
-------
47+
x_list: array
48+
x positions of the path
49+
y_list: array
50+
y positions of the path
51+
yaw_list: array
52+
yaw angles of the path
53+
modes: array
54+
mode list of the path
55+
lengths: array
56+
length list of the path segments.
4057
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
58+
"""
59+
# calculate local goal x, y, yaw
60+
l_rot = create_2d_rotation_matrix(s_yaw)
61+
le_xy = np.stack([g_x - s_x, g_y - s_y]).T @ l_rot
62+
local_goal_x = le_xy[0]
63+
local_goal_y = le_xy[1]
64+
local_goal_yaw = g_yaw - s_yaw
4465

4566
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)
67+
local_goal_x, local_goal_y, local_goal_yaw, curvature, step_size)
4768

48-
rot = Rot.from_euler('z', -s_yaw).as_matrix()[0:2, 0:2]
69+
# Convert a local coordinate path to the global coordinate
70+
rot = create_2d_rotation_matrix(-s_yaw)
4971
converted_xy = np.stack([lp_x, lp_y]).T @ rot
5072
x_list = converted_xy[:, 0] + s_x
5173
y_list = converted_xy[:, 1] + s_y
52-
yaw_list = [pi_2_pi(i_yaw + s_yaw) for i_yaw in lp_yaw]
74+
yaw_list = angle_mod(np.array(lp_yaw) + s_yaw)
5375

5476
return x_list, y_list, yaw_list, modes, lengths
5577

5678

57-
def mod2pi(theta):
58-
return theta - 2.0 * math.pi * math.floor(theta / 2.0 / math.pi)
59-
60-
61-
def pi_2_pi(angle):
62-
return (angle + math.pi) % (2 * math.pi) - math.pi
79+
def _mod2pi(theta):
80+
return angle_mod(theta, zero_2_2pi=True)
6381

6482

65-
def left_straight_left(alpha, beta, d):
83+
def _LSL(alpha, beta, d):
6684
sa = math.sin(alpha)
6785
sb = math.sin(beta)
6886
ca = math.cos(alpha)
@@ -76,9 +94,9 @@ def left_straight_left(alpha, beta, d):
7694
if p_squared < 0:
7795
return None, None, None, mode
7896
tmp1 = math.atan2((cb - ca), tmp0)
79-
t = mod2pi(-alpha + tmp1)
97+
t = _mod2pi(-alpha + tmp1)
8098
p = math.sqrt(p_squared)
81-
q = mod2pi(beta - tmp1)
99+
q = _mod2pi(beta - tmp1)
82100

83101
return t, p, q, mode
84102

@@ -96,9 +114,9 @@ def right_straight_right(alpha, beta, d):
96114
if p_squared < 0:
97115
return None, None, None, mode
98116
tmp1 = math.atan2((ca - cb), tmp0)
99-
t = mod2pi(alpha - tmp1)
117+
t = angle_mod(alpha - tmp1, zero_2_2pi=True)
100118
p = math.sqrt(p_squared)
101-
q = mod2pi(-beta + tmp1)
119+
q = _mod2pi(-beta + tmp1)
102120

103121
return t, p, q, mode
104122

@@ -116,8 +134,8 @@ def left_straight_right(alpha, beta, d):
116134
return None, None, None, mode
117135
p = math.sqrt(p_squared)
118136
tmp2 = math.atan2((-ca - cb), (d + sa + sb)) - math.atan2(-2.0, p)
119-
t = mod2pi(-alpha + tmp2)
120-
q = mod2pi(-mod2pi(beta) + tmp2)
137+
t = _mod2pi(-alpha + tmp2)
138+
q = _mod2pi(-_mod2pi(beta) + tmp2)
121139

122140
return t, p, q, mode
123141

@@ -135,8 +153,8 @@ def right_straight_left(alpha, beta, d):
135153
return None, None, None, mode
136154
p = math.sqrt(p_squared)
137155
tmp2 = math.atan2((ca + cb), (d - sa - sb)) - math.atan2(2.0, p)
138-
t = mod2pi(alpha - tmp2)
139-
q = mod2pi(beta - tmp2)
156+
t = _mod2pi(alpha - tmp2)
157+
q = _mod2pi(beta - tmp2)
140158

141159
return t, p, q, mode
142160

@@ -153,13 +171,13 @@ def right_left_right(alpha, beta, d):
153171
if abs(tmp_rlr) > 1.0:
154172
return None, None, None, mode
155173

156-
p = mod2pi(2 * math.pi - math.acos(tmp_rlr))
157-
t = mod2pi(alpha - math.atan2(ca - cb, d - sa + sb) + mod2pi(p / 2.0))
158-
q = mod2pi(alpha - beta - t + mod2pi(p))
174+
p = _mod2pi(2 * math.pi - math.acos(tmp_rlr))
175+
t = _mod2pi(alpha - math.atan2(ca - cb, d - sa + sb) + _mod2pi(p / 2.0))
176+
q = _mod2pi(alpha - beta - t + _mod2pi(p))
159177
return t, p, q, mode
160178

161179

162-
def left_right_left(alpha, beta, d):
180+
def _LRL(alpha, beta, d):
163181
sa = math.sin(alpha)
164182
sb = math.sin(beta)
165183
ca = math.cos(alpha)
@@ -170,9 +188,9 @@ def left_right_left(alpha, beta, d):
170188
tmp_lrl = (6.0 - d * d + 2.0 * c_ab + 2.0 * d * (- sa + sb)) / 8.0
171189
if abs(tmp_lrl) > 1:
172190
return None, None, None, mode
173-
p = mod2pi(2 * math.pi - math.acos(tmp_lrl))
174-
t = mod2pi(-alpha - math.atan2(ca - cb, d + sa - sb) + p / 2.0)
175-
q = mod2pi(mod2pi(beta) - alpha - t + mod2pi(p))
191+
p = _mod2pi(2 * math.pi - math.acos(tmp_lrl))
192+
t = _mod2pi(-alpha - math.atan2(ca - cb, d + sa - sb) + p / 2.0)
193+
q = _mod2pi(_mod2pi(beta) - alpha - t + _mod2pi(p))
176194

177195
return t, p, q, mode
178196

@@ -184,13 +202,13 @@ def dubins_path_planning_from_origin(end_x, end_y, end_yaw, curvature,
184202
D = math.hypot(dx, dy)
185203
d = D * curvature
186204

187-
theta = mod2pi(math.atan2(dy, dx))
188-
alpha = mod2pi(- theta)
189-
beta = mod2pi(end_yaw - theta)
205+
theta = _mod2pi(math.atan2(dy, dx))
206+
alpha = _mod2pi(- theta)
207+
beta = _mod2pi(end_yaw - theta)
190208

191-
planning_funcs = [left_straight_left, right_straight_right,
209+
planning_funcs = [_LSL, right_straight_right,
192210
left_straight_right, right_straight_left,
193-
right_left_right, left_right_left]
211+
right_left_right, _LRL]
194212

195213
best_cost = float("inf")
196214
bt, bp, bq, best_mode = None, None, None, None
@@ -319,6 +337,7 @@ def generate_local_course(total_length, lengths, modes, max_curvature,
319337

320338
def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r",
321339
ec="k"): # pragma: no cover
340+
import matplotlib.pyplot as plt
322341
if not isinstance(x, float):
323342
for (i_x, i_y, i_yaw) in zip(x, y, yaw):
324343
plot_arrow(i_x, i_y, i_yaw)
@@ -330,6 +349,7 @@ def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r",
330349

331350
def main():
332351
print("Dubins path planner sample start!!")
352+
import matplotlib.pyplot as plt
333353

334354
start_x = 1.0 # [m]
335355
start_y = 1.0 # [m]
@@ -341,13 +361,13 @@ def main():
341361

342362
curvature = 1.0
343363

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)
364+
path_x, path_y, path_yaw, mode, lengths = plan_dubins_path(start_x,
365+
start_y,
366+
start_yaw,
367+
end_x,
368+
end_y,
369+
end_yaw,
370+
curvature)
351371

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

PathPlanning/HybridAStar/car.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
77
"""
88

9-
from math import sqrt, cos, sin, tan, pi
9+
from math import cos, sin, tan, pi
1010

1111
import matplotlib.pyplot as plt
1212
import numpy as np

PathPlanning/RRTDubins/rrt_dubins.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
try:
2222
from rrt import RRT
23-
import dubins_path_planning
23+
import dubins_path_planner
2424
except ImportError:
2525
raise
2626

@@ -130,15 +130,15 @@ def draw_graph(self, rnd=None): # pragma: no cover
130130
plt.pause(0.01)
131131

132132
def plot_start_goal_arrow(self): # pragma: no cover
133-
dubins_path_planning.plot_arrow(
133+
dubins_path_planner.plot_arrow(
134134
self.start.x, self.start.y, self.start.yaw)
135-
dubins_path_planning.plot_arrow(
135+
dubins_path_planner.plot_arrow(
136136
self.end.x, self.end.y, self.end.yaw)
137137

138138
def steer(self, from_node, to_node):
139139

140140
px, py, pyaw, mode, course_lengths = \
141-
dubins_path_planning.dubins_path_planning(
141+
dubins_path_planner.plan_dubins_path(
142142
from_node.x, from_node.y, from_node.yaw,
143143
to_node.x, to_node.y, to_node.yaw, self.curvature)
144144

@@ -160,7 +160,7 @@ def steer(self, from_node, to_node):
160160

161161
def calc_new_cost(self, from_node, to_node):
162162

163-
_, _, _, _, course_length = dubins_path_planning.dubins_path_planning(
163+
_, _, _, _, course_length = dubins_path_planner.plan_dubins_path(
164164
from_node.x, from_node.y, from_node.yaw,
165165
to_node.x, to_node.y, to_node.yaw, self.curvature)
166166

PathPlanning/RRTStarDubins/rrt_star_dubins.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"/../RRTStar/")
2121

2222
try:
23-
import dubins_path_planning
23+
import dubins_path_planner
2424
from rrt_star import RRTStar
2525
except ImportError:
2626
raise
@@ -136,15 +136,15 @@ def draw_graph(self, rnd=None):
136136
plt.pause(0.01)
137137

138138
def plot_start_goal_arrow(self):
139-
dubins_path_planning.plot_arrow(
139+
dubins_path_planner.plot_arrow(
140140
self.start.x, self.start.y, self.start.yaw)
141-
dubins_path_planning.plot_arrow(
141+
dubins_path_planner.plot_arrow(
142142
self.end.x, self.end.y, self.end.yaw)
143143

144144
def steer(self, from_node, to_node):
145145

146146
px, py, pyaw, mode, course_lengths = \
147-
dubins_path_planning.dubins_path_planning(
147+
dubins_path_planner.plan_dubins_path(
148148
from_node.x, from_node.y, from_node.yaw,
149149
to_node.x, to_node.y, to_node.yaw, self.curvature)
150150

@@ -166,7 +166,7 @@ def steer(self, from_node, to_node):
166166

167167
def calc_new_cost(self, from_node, to_node):
168168

169-
_, _, _, _, course_lengths = dubins_path_planning.dubins_path_planning(
169+
_, _, _, _, course_lengths = dubins_path_planner.plan_dubins_path(
170170
from_node.x, from_node.y, from_node.yaw,
171171
to_node.x, to_node.y, to_node.yaw, self.curvature)
172172

docs/conf.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
# documentation root, use os.path.abspath to make it absolute, like shown here.
1414
#
1515
import os
16-
# import sys
17-
# sys.path.insert(0, os.path.abspath('.'))
16+
import sys
17+
sys.path.insert(0, os.path.abspath('../'))
1818

1919

2020
# -- Project information -----------------------------------------------------
@@ -42,6 +42,7 @@
4242
'sphinx.ext.autodoc',
4343
'sphinx.ext.mathjax',
4444
'sphinx.ext.viewcode',
45+
'sphinx.ext.napoleon',
4546
'IPython.sphinxext.ipython_console_highlighting',
4647
]
4748

docs/doc_requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
sphinx == 4.3.2 # For sphinx documentation
22
sphinx_rtd_theme == 1.0.0
33
IPython == 7.31.1 # For sphinx documentation
4+
sphinxcontrib-napoleon == 0.7 # For auto doc
9.98 KB
Loading
Loading

0 commit comments

Comments
 (0)