python-motion-planning 2.0.dev2__py3-none-any.whl → 2.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_motion_planning/__init__.py +1 -1
- python_motion_planning/common/env/map/grid.py +394 -129
- python_motion_planning/common/utils/geometry.py +18 -29
- python_motion_planning/path_planner/sample_search/rrt.py +5 -5
- python_motion_planning/path_planner/sample_search/rrt_connect.py +2 -2
- python_motion_planning/path_planner/sample_search/rrt_star.py +31 -11
- python_motion_planning/traj_optimizer/__init__.py +2 -0
- python_motion_planning/traj_optimizer/base_curve_generator.py +53 -0
- python_motion_planning/traj_optimizer/curve_generator/__init__.py +2 -0
- python_motion_planning/traj_optimizer/curve_generator/point_based/__init__.py +2 -0
- python_motion_planning/traj_optimizer/curve_generator/point_based/bspline.py +256 -0
- python_motion_planning/traj_optimizer/curve_generator/point_based/cubic_spline.py +115 -0
- python_motion_planning/traj_optimizer/curve_generator/pose_based/__init__.py +4 -0
- python_motion_planning/traj_optimizer/curve_generator/pose_based/bezier.py +121 -0
- python_motion_planning/traj_optimizer/curve_generator/pose_based/dubins.py +355 -0
- python_motion_planning/traj_optimizer/curve_generator/pose_based/polynomial.py +197 -0
- python_motion_planning/traj_optimizer/curve_generator/pose_based/reeds_shepp.py +606 -0
- {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.0.1.dist-info}/METADATA +22 -15
- {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.0.1.dist-info}/RECORD +22 -20
- {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.0.1.dist-info}/WHEEL +1 -1
- python_motion_planning/curve_generator/__init__.py +0 -9
- python_motion_planning/curve_generator/bezier_curve.py +0 -131
- python_motion_planning/curve_generator/bspline_curve.py +0 -271
- python_motion_planning/curve_generator/cubic_spline.py +0 -128
- python_motion_planning/curve_generator/curve.py +0 -64
- python_motion_planning/curve_generator/dubins_curve.py +0 -348
- python_motion_planning/curve_generator/fem_pos_smooth.py +0 -114
- python_motion_planning/curve_generator/polynomial_curve.py +0 -226
- python_motion_planning/curve_generator/reeds_shepp.py +0 -736
- {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.0.1.dist-info}/licenses/LICENSE +0 -0
- {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.0.1.dist-info}/top_level.txt +0 -0
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
@file: cubic_spline.py
|
|
3
|
-
@breif: Cubic spline generation
|
|
4
|
-
@author: Winter
|
|
5
|
-
@update: 2023.7.28
|
|
6
|
-
"""
|
|
7
|
-
import math
|
|
8
|
-
import bisect
|
|
9
|
-
import numpy as np
|
|
10
|
-
|
|
11
|
-
from .curve import Curve
|
|
12
|
-
|
|
13
|
-
class CubicSpline(Curve):
|
|
14
|
-
"""
|
|
15
|
-
Class for cubic spline generation.
|
|
16
|
-
|
|
17
|
-
Parameters:
|
|
18
|
-
step (float): Simulation or interpolation size
|
|
19
|
-
|
|
20
|
-
Examples:
|
|
21
|
-
>>> from python_motion_planning.curve_generation import CubicSpline
|
|
22
|
-
>>> points = [(0, 0, 0), (10, 10, -90), (20, 5, 60)]
|
|
23
|
-
>>> generator = CubicSpline(step)
|
|
24
|
-
>>> generator.run(points)
|
|
25
|
-
"""
|
|
26
|
-
def __init__(self, step: float) -> None:
|
|
27
|
-
super().__init__(step)
|
|
28
|
-
|
|
29
|
-
def __str__(self) -> str:
|
|
30
|
-
return "Cubic Spline"
|
|
31
|
-
|
|
32
|
-
def spline(self, x_list: list, y_list: list, t: list):
|
|
33
|
-
"""
|
|
34
|
-
Running both generation and animation.
|
|
35
|
-
|
|
36
|
-
Parameters:
|
|
37
|
-
x_list (list[tuple]): path points x-direction
|
|
38
|
-
y_list (list[tuple]): path points y-direction
|
|
39
|
-
t (list): parameter
|
|
40
|
-
|
|
41
|
-
Returns:
|
|
42
|
-
p (list): The (x, y) of curve with given t
|
|
43
|
-
dp (list): The derivative (dx, dy) of curve with given t
|
|
44
|
-
"""
|
|
45
|
-
# cubic polynomial functions
|
|
46
|
-
a, b, c, d = y_list, [], [], []
|
|
47
|
-
h = np.diff(x_list)
|
|
48
|
-
num = len(x_list)
|
|
49
|
-
|
|
50
|
-
# calculate coefficient matrix
|
|
51
|
-
A = np.zeros((num, num))
|
|
52
|
-
for i in range(1, num - 1):
|
|
53
|
-
A[i, i - 1] = h[i - 1]
|
|
54
|
-
A[i, i] = 2.0 * (h[i - 1] + h[i])
|
|
55
|
-
A[i, i + 1] = h[i]
|
|
56
|
-
A[0, 0] = 1.0
|
|
57
|
-
A[num - 1, num - 1] = 1.0
|
|
58
|
-
|
|
59
|
-
B = np.zeros(num)
|
|
60
|
-
for i in range(1, num - 1):
|
|
61
|
-
B[i] = 3.0 * (a[i + 1] - a[i]) / h[i] - \
|
|
62
|
-
3.0 * (a[i] - a[i - 1]) / h[i - 1]
|
|
63
|
-
|
|
64
|
-
c = np.linalg.solve(A, B)
|
|
65
|
-
for i in range(num - 1):
|
|
66
|
-
d.append((c[i + 1] - c[i]) / (3.0 * h[i]))
|
|
67
|
-
b.append((a[i + 1] - a[i]) / h[i] - h[i] * (c[i + 1] + 2.0 * c[i]) / 3.0)
|
|
68
|
-
|
|
69
|
-
# calculate spline value and its derivative
|
|
70
|
-
p, dp = [], []
|
|
71
|
-
for it in t:
|
|
72
|
-
if it < x_list[0] or it > x_list[-1]:
|
|
73
|
-
continue
|
|
74
|
-
i = bisect.bisect(x_list, it) - 1
|
|
75
|
-
dx = it - x_list[i]
|
|
76
|
-
p.append(a[i] + b[i] * dx + c[i] * dx**2 + d[i] * dx**3)
|
|
77
|
-
dp.append(b[i] + 2.0 * c[i] * dx + 3.0 * d[i] * dx**2)
|
|
78
|
-
|
|
79
|
-
return p, dp
|
|
80
|
-
|
|
81
|
-
def generation(self, start_pose: tuple, goal_pose: tuple):
|
|
82
|
-
pass
|
|
83
|
-
|
|
84
|
-
# def run(self, points: list):
|
|
85
|
-
# """
|
|
86
|
-
# Running both generation and animation.
|
|
87
|
-
|
|
88
|
-
# Parameters:
|
|
89
|
-
# points (list[tuple]): path points
|
|
90
|
-
# """
|
|
91
|
-
# assert len(points) >= 2, "Number of points should be at least 2."
|
|
92
|
-
# import matplotlib.pyplot as plt
|
|
93
|
-
|
|
94
|
-
# if len(points[0]) == 2:
|
|
95
|
-
# x_list = [ix for (ix, _) in points]
|
|
96
|
-
# y_list = [iy for (_, iy) in points]
|
|
97
|
-
# elif len(points[0]) == 3:
|
|
98
|
-
# x_list = [ix for (ix, _, _) in points]
|
|
99
|
-
# y_list = [iy for (_, iy, _) in points]
|
|
100
|
-
# else:
|
|
101
|
-
# raise NotImplementedError
|
|
102
|
-
|
|
103
|
-
# dx, dy = np.diff(x_list), np.diff(y_list)
|
|
104
|
-
# ds = [math.hypot(idx, idy) for (idx, idy) in zip(dx, dy)]
|
|
105
|
-
# s = [0]
|
|
106
|
-
# s.extend(np.cumsum(ds))
|
|
107
|
-
# t = np.arange(0, s[-1], self.step)
|
|
108
|
-
|
|
109
|
-
# path_x, d_path_x = self.spline(s, x_list, t)
|
|
110
|
-
# path_y, d_path_y = self.spline(s, y_list, t)
|
|
111
|
-
# path_yaw = [math.atan2(d_path_y[i], d_path_x[i]) for i in range(len(d_path_x))]
|
|
112
|
-
|
|
113
|
-
# # animation
|
|
114
|
-
# plt.figure("curve generation")
|
|
115
|
-
|
|
116
|
-
# # static
|
|
117
|
-
# plt.figure("curve generation")
|
|
118
|
-
# plt.plot(path_x, path_y, linewidth=2, c="#1f77b4")
|
|
119
|
-
# for x, y, _ in points:
|
|
120
|
-
# plt.plot(x, y, "xr", linewidth=2)
|
|
121
|
-
# plt.axis("equal")
|
|
122
|
-
# plt.title(str(self))
|
|
123
|
-
|
|
124
|
-
# plt.figure("yaw")
|
|
125
|
-
# plt.plot(t, [math.degrees(iyaw) for iyaw in path_yaw], "-r")
|
|
126
|
-
# plt.title("yaw curve")
|
|
127
|
-
|
|
128
|
-
# plt.show()
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
@file: curve.py
|
|
3
|
-
@breif: Trajectory generation
|
|
4
|
-
@author: Winter
|
|
5
|
-
@update: 2023.5.31
|
|
6
|
-
"""
|
|
7
|
-
import math
|
|
8
|
-
from abc import ABC, abstractmethod
|
|
9
|
-
|
|
10
|
-
class Curve(ABC):
|
|
11
|
-
def __init__(self, step: float) -> None:
|
|
12
|
-
"""
|
|
13
|
-
Base class for curve generation.
|
|
14
|
-
|
|
15
|
-
Parameters:
|
|
16
|
-
step (float): Simulation or interpolation size
|
|
17
|
-
"""
|
|
18
|
-
self.step = step
|
|
19
|
-
|
|
20
|
-
@abstractmethod
|
|
21
|
-
def run(self, points: list):
|
|
22
|
-
"""
|
|
23
|
-
Running both generation and animation.
|
|
24
|
-
"""
|
|
25
|
-
pass
|
|
26
|
-
|
|
27
|
-
@abstractmethod
|
|
28
|
-
def generation(self, start_pose: tuple, goal_pose: tuple):
|
|
29
|
-
"""
|
|
30
|
-
Generate the curve.
|
|
31
|
-
"""
|
|
32
|
-
pass
|
|
33
|
-
|
|
34
|
-
def trigonometric(self, alpha: float, beta: float):
|
|
35
|
-
"""
|
|
36
|
-
Calculate some useful trigonometric value with angles.
|
|
37
|
-
"""
|
|
38
|
-
return math.sin(alpha), math.sin(beta), math.cos(alpha), math.cos(beta), \
|
|
39
|
-
math.sin(alpha - beta), math.cos(alpha - beta)
|
|
40
|
-
|
|
41
|
-
def pi2pi(self, theta: float) -> float:
|
|
42
|
-
"""
|
|
43
|
-
Truncate the angle to the interval of -π to π.
|
|
44
|
-
"""
|
|
45
|
-
while theta > math.pi:
|
|
46
|
-
theta -= 2.0 * math.pi
|
|
47
|
-
while theta < -math.pi:
|
|
48
|
-
theta += 2.0 * math.pi
|
|
49
|
-
return theta
|
|
50
|
-
|
|
51
|
-
def mod2pi(self, theta: float) -> float:
|
|
52
|
-
"""
|
|
53
|
-
Perform modulus operation on 2π.
|
|
54
|
-
"""
|
|
55
|
-
return theta - 2.0 * math.pi * math.floor(theta / math.pi / 2.0)
|
|
56
|
-
|
|
57
|
-
def length(self, path: list) -> float:
|
|
58
|
-
"""
|
|
59
|
-
Calculate path or trajectory length with `path` format [(ix, iy)] (i from 0 to N)
|
|
60
|
-
"""
|
|
61
|
-
dist = 0
|
|
62
|
-
for i in range(len(path) - 1):
|
|
63
|
-
dist = dist + math.hypot(path[i + 1][0] - path[i][0], path[i + 1][1] - path[i][1])
|
|
64
|
-
return dist
|
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
@file: dubins_curve.py
|
|
3
|
-
@breif: Dubins curve generation
|
|
4
|
-
@author: Winter
|
|
5
|
-
@update: 2023.5.31
|
|
6
|
-
"""
|
|
7
|
-
import math
|
|
8
|
-
import numpy as np
|
|
9
|
-
|
|
10
|
-
from scipy.spatial.transform import Rotation as Rot
|
|
11
|
-
# from python_motion_planning.utils import Plot
|
|
12
|
-
from .curve import Curve
|
|
13
|
-
|
|
14
|
-
class Dubins(Curve):
|
|
15
|
-
"""
|
|
16
|
-
Class for Dubins curve generation.
|
|
17
|
-
|
|
18
|
-
Parameters:
|
|
19
|
-
step (float): Simulation or interpolation size
|
|
20
|
-
max_curv (float): The maximum curvature of the curve
|
|
21
|
-
|
|
22
|
-
Examples:
|
|
23
|
-
>>> from python_motion_planning.curve_generation import Dubins
|
|
24
|
-
>>> points = [(0, 0, 0), (10, 10, -90), (20, 5, 60)]
|
|
25
|
-
>>> generator = Dubins(step, max_curv)
|
|
26
|
-
>>> generator.run(points)
|
|
27
|
-
|
|
28
|
-
References:
|
|
29
|
-
[1] On curves of minimal length with a constraint on average curvature, and with prescribed initial and terminal positions and tangents
|
|
30
|
-
"""
|
|
31
|
-
def __init__(self, step: float, max_curv: float) -> None:
|
|
32
|
-
super().__init__(step)
|
|
33
|
-
self.max_curv = max_curv
|
|
34
|
-
|
|
35
|
-
def __str__(self) -> str:
|
|
36
|
-
return "Dubins Curve"
|
|
37
|
-
|
|
38
|
-
def LSL(self, alpha: float, beta: float, dist: float):
|
|
39
|
-
"""
|
|
40
|
-
Left-Straight-Left generation mode.
|
|
41
|
-
|
|
42
|
-
Parameters:
|
|
43
|
-
alpha (float): Initial pose of (0, 0, alpha)
|
|
44
|
-
beta (float): Goal pose of (dist, 0, beta)
|
|
45
|
-
dist (float): The distance between the initial and goal pose
|
|
46
|
-
|
|
47
|
-
Returns:
|
|
48
|
-
t (float): Moving lenght of segments
|
|
49
|
-
p (float): Moving lenght of segments
|
|
50
|
-
q (float): Moving lenght of segments
|
|
51
|
-
mode (list): Motion mode
|
|
52
|
-
"""
|
|
53
|
-
sin_a, sin_b, cos_a, cos_b, _, cos_a_b = self.trigonometric(alpha, beta)
|
|
54
|
-
|
|
55
|
-
p_lsl = 2 + dist ** 2 - 2 * cos_a_b + 2 * dist * (sin_a - sin_b)
|
|
56
|
-
if p_lsl < 0:
|
|
57
|
-
return None, None, None, ["L", "S", "L"]
|
|
58
|
-
else:
|
|
59
|
-
p_lsl = math.sqrt(p_lsl)
|
|
60
|
-
|
|
61
|
-
t_lsl = self.mod2pi(-alpha + math.atan2(cos_b - cos_a, dist + sin_a - sin_b))
|
|
62
|
-
q_lsl = self.mod2pi(beta - math.atan2(cos_b - cos_a, dist + sin_a - sin_b))
|
|
63
|
-
|
|
64
|
-
return t_lsl, p_lsl, q_lsl, ["L", "S", "L"]
|
|
65
|
-
|
|
66
|
-
def RSR(self, alpha: float, beta: float, dist: float):
|
|
67
|
-
"""
|
|
68
|
-
Right-Straight-Right generation mode.
|
|
69
|
-
|
|
70
|
-
Parameters:
|
|
71
|
-
alpha (float): Initial pose of (0, 0, alpha)
|
|
72
|
-
beta (float): Goal pose of (dist, 0, beta)
|
|
73
|
-
|
|
74
|
-
Returns:
|
|
75
|
-
t (float): Moving lenght of segments
|
|
76
|
-
p (float): Moving lenght of segments
|
|
77
|
-
q (float): Moving lenght of segments
|
|
78
|
-
mode (list): Motion mode
|
|
79
|
-
"""
|
|
80
|
-
sin_a, sin_b, cos_a, cos_b, _, cos_a_b = self.trigonometric(alpha, beta)
|
|
81
|
-
|
|
82
|
-
p_rsr = 2 + dist ** 2 - 2 * cos_a_b + 2 * dist * (sin_b - sin_a)
|
|
83
|
-
if p_rsr < 0:
|
|
84
|
-
return None, None, None, ["R", "S", "R"]
|
|
85
|
-
else:
|
|
86
|
-
p_rsr = math.sqrt(p_rsr)
|
|
87
|
-
|
|
88
|
-
t_rsr = self.mod2pi(alpha - math.atan2(cos_a - cos_b, dist - sin_a + sin_b))
|
|
89
|
-
q_rsr = self.mod2pi(-beta + math.atan2(cos_a - cos_b, dist - sin_a + sin_b))
|
|
90
|
-
|
|
91
|
-
return t_rsr, p_rsr, q_rsr, ["R", "S", "R"]
|
|
92
|
-
|
|
93
|
-
def LSR(self, alpha: float, beta: float, dist: float):
|
|
94
|
-
"""
|
|
95
|
-
Left-Straight-Right generation mode.
|
|
96
|
-
|
|
97
|
-
Parameters:
|
|
98
|
-
alpha (float): Initial pose of (0, 0, alpha)
|
|
99
|
-
beta (float): Goal pose of (dist, 0, beta)
|
|
100
|
-
dist (float): The distance between the initial and goal pose
|
|
101
|
-
|
|
102
|
-
Returns:
|
|
103
|
-
t (float): Moving lenght of segments
|
|
104
|
-
p (float): Moving lenght of segments
|
|
105
|
-
q (float): Moving lenght of segments
|
|
106
|
-
mode (list): Motion mode
|
|
107
|
-
"""
|
|
108
|
-
sin_a, sin_b, cos_a, cos_b, _, cos_a_b = self.trigonometric(alpha, beta)
|
|
109
|
-
|
|
110
|
-
p_lsr = -2 + dist ** 2 + 2 * cos_a_b + 2 * dist * (sin_a + sin_b)
|
|
111
|
-
if p_lsr < 0:
|
|
112
|
-
return None, None, None, ["L", "S", "R"]
|
|
113
|
-
else:
|
|
114
|
-
p_lsr = math.sqrt(p_lsr)
|
|
115
|
-
|
|
116
|
-
t_lsr = self.mod2pi(-alpha + math.atan2(-cos_a - cos_b, dist + sin_a + sin_b) - math.atan2(-2.0, p_lsr))
|
|
117
|
-
q_lsr = self.mod2pi(-beta + math.atan2(-cos_a - cos_b, dist + sin_a + sin_b) - math.atan2(-2.0, p_lsr))
|
|
118
|
-
|
|
119
|
-
return t_lsr, p_lsr, q_lsr, ["L", "S", "R"]
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def RSL(self, alpha: float, beta: float, dist: float):
|
|
123
|
-
"""
|
|
124
|
-
Right-Straight-Left generation mode.
|
|
125
|
-
|
|
126
|
-
Parameters:
|
|
127
|
-
alpha (float): Initial pose of (0, 0, alpha)
|
|
128
|
-
beta (float): Goal pose of (dist, 0, beta)
|
|
129
|
-
dist (float): The distance between the initial and goal pose
|
|
130
|
-
|
|
131
|
-
Returns:
|
|
132
|
-
t (float): Moving lenght of segments
|
|
133
|
-
p (float): Moving lenght of segments
|
|
134
|
-
q (float): Moving lenght of segments
|
|
135
|
-
mode (list): Motion mode
|
|
136
|
-
"""
|
|
137
|
-
sin_a, sin_b, cos_a, cos_b, _, cos_a_b = self.trigonometric(alpha, beta)
|
|
138
|
-
|
|
139
|
-
p_rsl = -2 + dist ** 2 + 2 * cos_a_b - 2 * dist * (sin_a + sin_b)
|
|
140
|
-
if p_rsl < 0:
|
|
141
|
-
return None, None, None, ["R", "S", "L"]
|
|
142
|
-
else:
|
|
143
|
-
p_rsl = math.sqrt(p_rsl)
|
|
144
|
-
|
|
145
|
-
t_rsl = self.mod2pi(alpha - math.atan2(cos_a + cos_b, dist - sin_a - sin_b) + math.atan2(2.0, p_rsl))
|
|
146
|
-
q_rsl = self.mod2pi(beta - math.atan2(cos_a + cos_b, dist - sin_a - sin_b) + math.atan2(2.0, p_rsl))
|
|
147
|
-
|
|
148
|
-
return t_rsl, p_rsl, q_rsl, ["R", "S", "L"]
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def RLR(self, alpha: float, beta: float, dist: float):
|
|
152
|
-
"""
|
|
153
|
-
Right-Left-Right generation mode.
|
|
154
|
-
|
|
155
|
-
Parameters:
|
|
156
|
-
alpha (float): Initial pose of (0, 0, alpha)
|
|
157
|
-
beta (float): Goal pose of (dist, 0, beta)
|
|
158
|
-
dist (float): The distance between the initial and goal pose
|
|
159
|
-
|
|
160
|
-
Returns:
|
|
161
|
-
t (float): Moving lenght of segments
|
|
162
|
-
p (float): Moving lenght of segments
|
|
163
|
-
q (float): Moving lenght of segments
|
|
164
|
-
mode (list): Motion mode
|
|
165
|
-
"""
|
|
166
|
-
sin_a, sin_b, cos_a, cos_b, _, cos_a_b = self.trigonometric(alpha, beta)
|
|
167
|
-
|
|
168
|
-
p_rlr = (6.0 - dist ** 2 + 2.0 * cos_a_b + 2.0 * dist * (sin_a - sin_b)) / 8.0
|
|
169
|
-
if abs(p_rlr) > 1.0:
|
|
170
|
-
return None, None, None, ["R", "L", "R"]
|
|
171
|
-
else:
|
|
172
|
-
p_rlr = self.mod2pi(2 * math.pi - math.acos(p_rlr))
|
|
173
|
-
|
|
174
|
-
t_rlr = self.mod2pi(alpha - math.atan2(cos_a - cos_b, dist - sin_a + sin_b) + p_rlr / 2.0)
|
|
175
|
-
q_rlr = self.mod2pi(alpha - beta - t_rlr + p_rlr)
|
|
176
|
-
|
|
177
|
-
return t_rlr, p_rlr, q_rlr, ["R", "L", "R"]
|
|
178
|
-
|
|
179
|
-
def LRL(self, alpha: float, beta: float, dist: float):
|
|
180
|
-
"""
|
|
181
|
-
Left-Right-Left generation mode.
|
|
182
|
-
|
|
183
|
-
Parameters:
|
|
184
|
-
alpha (float): Initial pose of (0, 0, alpha)
|
|
185
|
-
beta (float): Goal pose of (dist, 0, beta)
|
|
186
|
-
dist (float): The distance between the initial and goal pose
|
|
187
|
-
|
|
188
|
-
Returns:
|
|
189
|
-
t (float): Moving lenght of segments
|
|
190
|
-
p (float): Moving lenght of segments
|
|
191
|
-
q (float): Moving lenght of segments
|
|
192
|
-
mode (list): Motion mode
|
|
193
|
-
"""
|
|
194
|
-
sin_a, sin_b, cos_a, cos_b, _, cos_a_b = self.trigonometric(alpha, beta)
|
|
195
|
-
|
|
196
|
-
p_lrl = (6.0 - dist ** 2 + 2.0 * cos_a_b + 2.0 * dist * (sin_a - sin_b)) / 8.0
|
|
197
|
-
if abs(p_lrl) > 1.0:
|
|
198
|
-
return None, None, None, ["L", "R", "L"]
|
|
199
|
-
else:
|
|
200
|
-
p_lrl = self.mod2pi(2 * math.pi - math.acos(p_lrl))
|
|
201
|
-
|
|
202
|
-
t_lrl = self.mod2pi(-alpha + math.atan2(-cos_a + cos_b, dist + sin_a - sin_b) + p_lrl / 2.0)
|
|
203
|
-
q_lrl = self.mod2pi(beta - alpha - t_lrl + p_lrl)
|
|
204
|
-
|
|
205
|
-
return t_lrl, p_lrl, q_lrl, ["L", "R", "L"]
|
|
206
|
-
|
|
207
|
-
def interpolate(self, mode: str, length: float, init_pose: tuple):
|
|
208
|
-
"""
|
|
209
|
-
Planning path interpolation.
|
|
210
|
-
|
|
211
|
-
Parameters:
|
|
212
|
-
mode (str): motion, e.g., L, S, R
|
|
213
|
-
length (float): Single step motion path length
|
|
214
|
-
init_pose (tuple): Initial pose (x, y, yaw)
|
|
215
|
-
|
|
216
|
-
Returns:
|
|
217
|
-
new_pose (tuple): New pose (new_x, new_y, new_yaw) after moving
|
|
218
|
-
"""
|
|
219
|
-
x, y, yaw = init_pose
|
|
220
|
-
|
|
221
|
-
if mode == "S":
|
|
222
|
-
new_x = x + length / self.max_curv * math.cos(yaw)
|
|
223
|
-
new_y = y + length / self.max_curv * math.sin(yaw)
|
|
224
|
-
new_yaw = yaw
|
|
225
|
-
elif mode == "L":
|
|
226
|
-
new_x = x + (math.sin(yaw + length) - math.sin(yaw)) / self.max_curv
|
|
227
|
-
new_y = y - (math.cos(yaw + length) - math.cos(yaw)) / self.max_curv
|
|
228
|
-
new_yaw = yaw + length
|
|
229
|
-
elif mode == "R":
|
|
230
|
-
new_x = x - (math.sin(yaw - length) - math.sin(yaw)) / self.max_curv
|
|
231
|
-
new_y = y + (math.cos(yaw - length) - math.cos(yaw)) / self.max_curv
|
|
232
|
-
new_yaw = yaw - length
|
|
233
|
-
else:
|
|
234
|
-
raise NotImplementedError
|
|
235
|
-
|
|
236
|
-
return new_x, new_y, new_yaw
|
|
237
|
-
|
|
238
|
-
def generation(self, start_pose: tuple, goal_pose: tuple):
|
|
239
|
-
"""
|
|
240
|
-
Generate the Dubins Curve.
|
|
241
|
-
|
|
242
|
-
Parameters:
|
|
243
|
-
start_pose (tuple): Initial pose (x, y, yaw)
|
|
244
|
-
goal_pose (tuple): Target pose (x, y, yaw)
|
|
245
|
-
|
|
246
|
-
Returns:
|
|
247
|
-
best_cost (float): Best planning path length
|
|
248
|
-
best_mode (list): Best motion modes
|
|
249
|
-
x_list (list): Trajectory of x
|
|
250
|
-
y_list (list): Trajectory of y
|
|
251
|
-
yaw_list (list): Trajectory of yaw
|
|
252
|
-
"""
|
|
253
|
-
sx, sy, syaw = start_pose
|
|
254
|
-
gx, gy, gyaw = goal_pose
|
|
255
|
-
|
|
256
|
-
# coordinate transformation
|
|
257
|
-
gx, gy = gx - sx, gy - sy
|
|
258
|
-
theta = self.mod2pi(math.atan2(gy, gx))
|
|
259
|
-
dist = math.hypot(gx, gy) * self.max_curv
|
|
260
|
-
alpha = self.mod2pi(syaw - theta)
|
|
261
|
-
beta = self.mod2pi(gyaw - theta)
|
|
262
|
-
|
|
263
|
-
# select the best motion
|
|
264
|
-
planners = [self.LSL, self.RSR, self.LSR, self.RSL, self.RLR, self.LRL]
|
|
265
|
-
best_t, best_p, best_q, best_mode, best_cost = None, None, None, None, float("inf")
|
|
266
|
-
|
|
267
|
-
for planner in planners:
|
|
268
|
-
t, p, q, mode = planner(alpha, beta, dist)
|
|
269
|
-
if t is None:
|
|
270
|
-
continue
|
|
271
|
-
cost = (abs(t) + abs(p) + abs(q))
|
|
272
|
-
if best_cost > cost:
|
|
273
|
-
best_t, best_p, best_q, best_mode, best_cost = t, p, q, mode, cost
|
|
274
|
-
|
|
275
|
-
# interpolation
|
|
276
|
-
segments = [best_t, best_p, best_q]
|
|
277
|
-
points_num = int(sum(segments) / self.step) + len(segments) + 3
|
|
278
|
-
x_list = [0.0 for _ in range(points_num)]
|
|
279
|
-
y_list = [0.0 for _ in range(points_num)]
|
|
280
|
-
yaw_list = [alpha for _ in range(points_num)]
|
|
281
|
-
|
|
282
|
-
i = 0
|
|
283
|
-
for mode_, seg_length in zip(best_mode, segments):
|
|
284
|
-
# path increment
|
|
285
|
-
d_length = self.step if seg_length > 0.0 else -self.step
|
|
286
|
-
x, y, yaw = x_list[i], y_list[i], yaw_list[i]
|
|
287
|
-
# current path length
|
|
288
|
-
length = d_length
|
|
289
|
-
while abs(length) <= abs(seg_length):
|
|
290
|
-
i += 1
|
|
291
|
-
x_list[i], y_list[i], yaw_list[i] = self.interpolate(mode_, length, (x, y, yaw))
|
|
292
|
-
length += d_length
|
|
293
|
-
i += 1
|
|
294
|
-
x_list[i], y_list[i], yaw_list[i] = self.interpolate(mode_, seg_length, (x, y, yaw))
|
|
295
|
-
|
|
296
|
-
# failed
|
|
297
|
-
if len(x_list) <= 1:
|
|
298
|
-
return None, None, [], [], []
|
|
299
|
-
|
|
300
|
-
# remove unused data
|
|
301
|
-
while len(x_list) >= 1 and x_list[-1] == 0.0:
|
|
302
|
-
x_list.pop()
|
|
303
|
-
y_list.pop()
|
|
304
|
-
yaw_list.pop()
|
|
305
|
-
|
|
306
|
-
# coordinate transformation
|
|
307
|
-
rot = Rot.from_euler('z', theta).as_matrix()[0:2, 0:2]
|
|
308
|
-
converted_xy = rot @ np.stack([x_list, y_list])
|
|
309
|
-
x_list = converted_xy[0, :] + sx
|
|
310
|
-
y_list = converted_xy[1, :] + sy
|
|
311
|
-
yaw_list = [self.pi2pi(i_yaw + theta) for i_yaw in yaw_list]
|
|
312
|
-
|
|
313
|
-
return best_cost, best_mode, x_list, y_list, yaw_list
|
|
314
|
-
|
|
315
|
-
# def run(self, points: list):
|
|
316
|
-
# """
|
|
317
|
-
# Running both generation and animation.
|
|
318
|
-
|
|
319
|
-
# Parameters:
|
|
320
|
-
# points (list[tuple]): path points
|
|
321
|
-
# """
|
|
322
|
-
# assert len(points) >= 2, "Number of points should be at least 2."
|
|
323
|
-
# import matplotlib.pyplot as plt
|
|
324
|
-
|
|
325
|
-
# # generation
|
|
326
|
-
# path_x, path_y, path_yaw = [], [], []
|
|
327
|
-
# for i in range(len(points) - 1):
|
|
328
|
-
# _, _, x_list, y_list, yaw_list = self.generation(
|
|
329
|
-
# (points[i][0], points[i][1], np.deg2rad(points[i][2])),
|
|
330
|
-
# (points[i + 1][0], points[i + 1][1], np.deg2rad(points[i + 1][2])))
|
|
331
|
-
|
|
332
|
-
# for j in range(len(x_list)):
|
|
333
|
-
# path_x.append(x_list[j])
|
|
334
|
-
# path_y.append(y_list[j])
|
|
335
|
-
# path_yaw.append(yaw_list[j])
|
|
336
|
-
|
|
337
|
-
# # animation
|
|
338
|
-
# plt.figure("curve generation")
|
|
339
|
-
# plt.plot(path_x, path_y, linewidth=2, c="#1f77b4")
|
|
340
|
-
# for x, y, theta in points:
|
|
341
|
-
# Plot.plotArrow(x, y, np.deg2rad(theta), 2, 'blueviolet')
|
|
342
|
-
|
|
343
|
-
# plt.axis("equal")
|
|
344
|
-
# plt.title(str(self))
|
|
345
|
-
# plt.show()
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
@file: fem_pos_smooth.py
|
|
3
|
-
@breif: Fem-Pos Smoother
|
|
4
|
-
@author: Winter
|
|
5
|
-
@update: 2024.3.23
|
|
6
|
-
"""
|
|
7
|
-
import osqp
|
|
8
|
-
import numpy as np
|
|
9
|
-
from scipy import sparse
|
|
10
|
-
|
|
11
|
-
from .curve import Curve
|
|
12
|
-
|
|
13
|
-
class FemPosSmoother(Curve):
|
|
14
|
-
"""
|
|
15
|
-
Class for Fem-pos smoother.
|
|
16
|
-
|
|
17
|
-
Parameters:
|
|
18
|
-
|
|
19
|
-
Examples:
|
|
20
|
-
>>> from python_motion_planning.curve_generation import FemPosSmoother
|
|
21
|
-
>>> points = [(0, 0, 0), (10, 10, -90), (20, 5, 60)]
|
|
22
|
-
>>> generator = FemPosSmoother(w_smooth, w_length, w_ref, dx_l, dx_u, dy_l, dy_u)
|
|
23
|
-
>>> generator.run(points)
|
|
24
|
-
"""
|
|
25
|
-
def __init__(self, w_smooth: float, w_length: float, w_ref: float,
|
|
26
|
-
dx_l: float, dx_u: float, dy_l: float, dy_u: float) -> None:
|
|
27
|
-
super().__init__(0.1)
|
|
28
|
-
self.w_smooth = w_smooth
|
|
29
|
-
self.w_length = w_length
|
|
30
|
-
self.w_ref = w_ref
|
|
31
|
-
self.dx_l = dx_l
|
|
32
|
-
self.dx_u = dx_u
|
|
33
|
-
self.dy_l = dy_l
|
|
34
|
-
self.dy_u = dy_u
|
|
35
|
-
|
|
36
|
-
def __str__(self) -> str:
|
|
37
|
-
return "Fem-pos Smoother"
|
|
38
|
-
|
|
39
|
-
def generation(self, start_pose: tuple, goal_pose: tuple):
|
|
40
|
-
pass
|
|
41
|
-
|
|
42
|
-
# def run(self, points: list, display: bool = True):
|
|
43
|
-
# """
|
|
44
|
-
# Running both generation and animation.
|
|
45
|
-
|
|
46
|
-
# Parameters:
|
|
47
|
-
# points (list[tuple]): path points
|
|
48
|
-
# """
|
|
49
|
-
# assert len(points) >= 3, "Number of points should be at least 3."
|
|
50
|
-
# if len(points[0]) == 3:
|
|
51
|
-
# points = [(ix, iy) for ix, iy, _ in points]
|
|
52
|
-
# import matplotlib.pyplot as plt
|
|
53
|
-
|
|
54
|
-
# n = len(points)
|
|
55
|
-
# P = np.zeros((2 * n, 2 * n))
|
|
56
|
-
|
|
57
|
-
# X = np.eye(2) * self.w_smooth
|
|
58
|
-
# Y = np.eye(2) * self.w_length
|
|
59
|
-
# Z = np.eye(2) * self.w_ref
|
|
60
|
-
|
|
61
|
-
# P[0:2, 0:2] = X + Y + Z
|
|
62
|
-
# P[0:2, 2:4] = -2 * X - Y
|
|
63
|
-
# P[2:4, 2:4] = 5 * X + 2 * Y + Z
|
|
64
|
-
# P[2 * n - 2:2 * n, 2 * n - 2:2 * n] = X + Y + Z
|
|
65
|
-
# P[2 * n - 4:2 * n - 2, 2 * n - 4:2 * n - 2] = 5 * X + 2 * Y + Z
|
|
66
|
-
# P[2 * n - 4:2 * n - 2, 2 * n - 2:2 * n] = -2 * X - Y
|
|
67
|
-
|
|
68
|
-
# for i in range(2, n - 2):
|
|
69
|
-
# P[2 * i:2 * i + 2, 2 * i:2 * i + 2] = 6 * X + 2 * Y + Z
|
|
70
|
-
# for i in range(2, n - 1):
|
|
71
|
-
# P[2 * i - 2:2 * i, 2 * i:2 * i + 2] = -4 * X - Y
|
|
72
|
-
# for i in range(2, n):
|
|
73
|
-
# P[2 * i - 4:2 * i - 2, 2 * i:2 * i + 2] = X
|
|
74
|
-
|
|
75
|
-
# A_I = np.eye(2 * n)
|
|
76
|
-
# g = np.zeros((2 * n, 1))
|
|
77
|
-
# lower = np.zeros((2 * n, 1))
|
|
78
|
-
# upper = np.zeros((2 * n, 1))
|
|
79
|
-
# for i, p in enumerate(points):
|
|
80
|
-
# g[2 * i], g[2 * i + 1] = -2 * self.w_ref * p[0], -2 * self.w_ref * p[1]
|
|
81
|
-
# lower[2 * i], lower[2 * i + 1] = p[0] - self.dx_l, p[1] - self.dy_l
|
|
82
|
-
# upper[2 * i], upper[2 * i + 1] = p[0] + self.dx_u, p[1] + self.dy_u
|
|
83
|
-
|
|
84
|
-
# # solve
|
|
85
|
-
# solver = osqp.OSQP()
|
|
86
|
-
# solver.setup(sparse.csc_matrix(P), g, sparse.csc_matrix(A_I), lower, upper, verbose=False)
|
|
87
|
-
# res = solver.solve()
|
|
88
|
-
# opt = res.x
|
|
89
|
-
|
|
90
|
-
# path_x, path_y = [], []
|
|
91
|
-
# for i in range(n):
|
|
92
|
-
# path_x.append(opt[2 * i])
|
|
93
|
-
# path_y.append(opt[2 * i + 1])
|
|
94
|
-
|
|
95
|
-
# if display:
|
|
96
|
-
# plt.figure("curve generation")
|
|
97
|
-
# plt.plot(path_x, path_y, linewidth=2, c="#ff0000", marker="o", label="smooth path")
|
|
98
|
-
# raw_x, raw_y = [], []
|
|
99
|
-
# for i, (x, y) in enumerate(points):
|
|
100
|
-
# # label = "bounding box" if i == 0 else None
|
|
101
|
-
# # plt.gca().add_patch(
|
|
102
|
-
# # plt.Rectangle(xy=(x - self.dx_l, y - self.dy_l), width=self.dx_u + self.dx_l,
|
|
103
|
-
# # height=self.dy_u + self.dy_l, color='red', linestyle="--", fill=False, label=label)
|
|
104
|
-
# # )
|
|
105
|
-
# raw_x.append(x)
|
|
106
|
-
# raw_y.append(y)
|
|
107
|
-
# plt.plot(raw_x, raw_y, linewidth=2, c="#1f77b4", marker="x", label="raw path")
|
|
108
|
-
# # plt.axis("equal")
|
|
109
|
-
# plt.legend()
|
|
110
|
-
# plt.title(str(self))
|
|
111
|
-
|
|
112
|
-
# plt.show()
|
|
113
|
-
|
|
114
|
-
# return [(ix, iy) for (ix, iy) in zip(path_x, path_y)]
|