python-motion-planning 2.0.dev2__py3-none-any.whl → 2.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 +568 -147
- python_motion_planning/common/utils/geometry.py +21 -31
- python_motion_planning/path_planner/graph_search/lazy_theta_star.py +15 -7
- python_motion_planning/path_planner/graph_search/theta_star.py +4 -3
- python_motion_planning/path_planner/sample_search/rrt.py +6 -6
- 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.1.dist-info}/METADATA +23 -16
- {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.1.dist-info}/RECORD +24 -22
- {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.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.1.dist-info}/licenses/LICENSE +0 -0
- {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@file: polynomial_curve.py
|
|
3
|
+
@author: Yang Haodong, Wu Maojia
|
|
4
|
+
@update: 2026.4.12
|
|
5
|
+
"""
|
|
6
|
+
from typing import List, Tuple, Dict, Any
|
|
7
|
+
import math
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from python_motion_planning.traj_optimizer.base_curve_generator import BaseCurveGenerator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Polynomial(BaseCurveGenerator):
|
|
15
|
+
"""
|
|
16
|
+
Class for quintic polynomial curve generator.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
*args: see the parent class.
|
|
20
|
+
max_acc: Maximum allowed acceleration magnitude.
|
|
21
|
+
max_jerk: Maximum allowed jerk magnitude.
|
|
22
|
+
*args: see the parent class.
|
|
23
|
+
|
|
24
|
+
References:
|
|
25
|
+
[1] https://en.wikipedia.org/wiki/Polynomial_trajectory
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
>>> import math
|
|
29
|
+
>>> generator = Polynomial(step=0.1, max_acc=2.82, max_jerk=10.0)
|
|
30
|
+
>>> points = [(0.0, 0.0, 0.0), (10.0, 10.0, -math.pi/2), (20.0, 5.0, math.pi/3)]
|
|
31
|
+
>>> path, curve_info = generator.generate(points)
|
|
32
|
+
>>> print(curve_info['success'])
|
|
33
|
+
True
|
|
34
|
+
"""
|
|
35
|
+
def __init__(self, *args,
|
|
36
|
+
max_acc: float = 2.82,
|
|
37
|
+
max_jerk: float = 10.0,
|
|
38
|
+
**kwargs) -> None:
|
|
39
|
+
super().__init__(*args, **kwargs)
|
|
40
|
+
self.max_acc = max_acc
|
|
41
|
+
self.max_jerk = max_jerk
|
|
42
|
+
self.dt = 0.1
|
|
43
|
+
self.t_min = 1
|
|
44
|
+
self.t_max = 30
|
|
45
|
+
|
|
46
|
+
def __str__(self) -> str:
|
|
47
|
+
return "Quintic Polynomial Curve"
|
|
48
|
+
|
|
49
|
+
class _Poly:
|
|
50
|
+
"""
|
|
51
|
+
Quintic polynomial solver for a single dimension.
|
|
52
|
+
"""
|
|
53
|
+
def __init__(self, state0: tuple, state1: tuple, t: float) -> None:
|
|
54
|
+
x0, v0, a0 = state0
|
|
55
|
+
xt, vt, at = state1
|
|
56
|
+
|
|
57
|
+
A = np.array([[t ** 3, t ** 4, t ** 5],
|
|
58
|
+
[3 * t ** 2, 4 * t ** 3, 5 * t ** 4],
|
|
59
|
+
[6 * t, 12 * t ** 2, 20 * t ** 3]])
|
|
60
|
+
b = np.array([xt - x0 - v0 * t - a0 * t ** 2 / 2,
|
|
61
|
+
vt - v0 - a0 * t,
|
|
62
|
+
at - a0])
|
|
63
|
+
X = np.linalg.solve(A, b)
|
|
64
|
+
|
|
65
|
+
self.p0 = x0
|
|
66
|
+
self.p1 = v0
|
|
67
|
+
self.p2 = a0 / 2.0
|
|
68
|
+
self.p3 = X[0]
|
|
69
|
+
self.p4 = X[1]
|
|
70
|
+
self.p5 = X[2]
|
|
71
|
+
|
|
72
|
+
def x(self, t: float) -> float:
|
|
73
|
+
return (self.p0 + self.p1 * t + self.p2 * t ** 2
|
|
74
|
+
+ self.p3 * t ** 3 + self.p4 * t ** 4 + self.p5 * t ** 5)
|
|
75
|
+
|
|
76
|
+
def dx(self, t: float) -> float:
|
|
77
|
+
return (self.p1 + 2 * self.p2 * t + 3 * self.p3 * t ** 2
|
|
78
|
+
+ 4 * self.p4 * t ** 3 + 5 * self.p5 * t ** 4)
|
|
79
|
+
|
|
80
|
+
def ddx(self, t: float) -> float:
|
|
81
|
+
return 2 * self.p2 + 6 * self.p3 * t + 12 * self.p4 * t ** 2 + 20 * self.p5 * t ** 3
|
|
82
|
+
|
|
83
|
+
def dddx(self, t: float) -> float:
|
|
84
|
+
return 6 * self.p3 + 24 * self.p4 * t + 60 * self.p5 * t ** 2
|
|
85
|
+
|
|
86
|
+
class _Trajectory:
|
|
87
|
+
"""
|
|
88
|
+
Container for a polynomial trajectory.
|
|
89
|
+
"""
|
|
90
|
+
def __init__(self):
|
|
91
|
+
self.clear()
|
|
92
|
+
|
|
93
|
+
def clear(self):
|
|
94
|
+
self.time = []
|
|
95
|
+
self.x = []
|
|
96
|
+
self.y = []
|
|
97
|
+
self.yaw = []
|
|
98
|
+
self.v = []
|
|
99
|
+
self.a = []
|
|
100
|
+
self.jerk = []
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def size(self) -> int:
|
|
104
|
+
assert (len(self.time) == len(self.x) == len(self.y) == len(self.yaw)
|
|
105
|
+
== len(self.v) == len(self.a) == len(self.jerk)), \
|
|
106
|
+
"Unequal dimensions of each attribute, this should not happen."
|
|
107
|
+
return len(self.time)
|
|
108
|
+
|
|
109
|
+
def generate(self, points: List[Tuple[float, ...]]) -> Tuple[List[Tuple[float, float, float]], Dict[str, Any]]:
|
|
110
|
+
"""
|
|
111
|
+
Generate a concatenated quintic polynomial curve through a list of poses.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
points: A list of poses (x, y, yaw) in world frame. Optionally the
|
|
115
|
+
points may also carry velocity and acceleration, i.e.
|
|
116
|
+
(x, y, yaw, v, a). Missing values are filled heuristically.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
path: A list of (x, y, yaw) waypoints of the generated curve in world frame.
|
|
120
|
+
curve_info: A dictionary containing the curve information (success, length).
|
|
121
|
+
"""
|
|
122
|
+
if len(points) < 2:
|
|
123
|
+
return [], {"success": False, "length": 0.0}
|
|
124
|
+
|
|
125
|
+
states: List[Tuple[float, float, float, float, float]] = []
|
|
126
|
+
for i, pt in enumerate(points):
|
|
127
|
+
if len(pt) >= 5:
|
|
128
|
+
states.append((float(pt[0]), float(pt[1]), float(pt[2]), float(pt[3]), float(pt[4])))
|
|
129
|
+
elif len(pt) == 3:
|
|
130
|
+
v = 0.0 if (i == 0 or i == len(points) - 1) else 1.0
|
|
131
|
+
states.append((float(pt[0]), float(pt[1]), float(pt[2]), v, 0.0))
|
|
132
|
+
else:
|
|
133
|
+
raise ValueError("Points must be (x, y, yaw) or (x, y, yaw, v, a).")
|
|
134
|
+
|
|
135
|
+
path: List[Tuple[float, float, float]] = []
|
|
136
|
+
for i in range(len(states) - 1):
|
|
137
|
+
traj = self._generate_segment(states[i], states[i + 1])
|
|
138
|
+
for j in range(traj.size):
|
|
139
|
+
path.append((float(traj.x[j]), float(traj.y[j]), float(traj.yaw[j])))
|
|
140
|
+
|
|
141
|
+
success = len(path) > 0
|
|
142
|
+
return path, {"success": success, "length": self.length(path) if success else 0.0}
|
|
143
|
+
|
|
144
|
+
def _generate_segment(self, start_state: Tuple[float, float, float, float, float],
|
|
145
|
+
goal_state: Tuple[float, float, float, float, float]) -> "_Trajectory":
|
|
146
|
+
"""
|
|
147
|
+
Generate a single quintic polynomial segment between two states.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
start_state: Initial state (x, y, yaw, v, a).
|
|
151
|
+
goal_state: Target state (x, y, yaw, v, a).
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
traj: The first trajectory that satisfies the acceleration and jerk constraints.
|
|
155
|
+
"""
|
|
156
|
+
sx, sy, syaw, sv, sa = start_state
|
|
157
|
+
gx, gy, gyaw, gv, ga = goal_state
|
|
158
|
+
|
|
159
|
+
sv_x, sv_y = sv * math.cos(syaw), sv * math.sin(syaw)
|
|
160
|
+
gv_x, gv_y = gv * math.cos(gyaw), gv * math.sin(gyaw)
|
|
161
|
+
|
|
162
|
+
sa_x, sa_y = sa * math.cos(syaw), sa * math.sin(syaw)
|
|
163
|
+
ga_x, ga_y = ga * math.cos(gyaw), ga * math.sin(gyaw)
|
|
164
|
+
|
|
165
|
+
traj = self._Trajectory()
|
|
166
|
+
|
|
167
|
+
for T in np.arange(self.t_min, self.t_max, self.step):
|
|
168
|
+
x_psolver = self._Poly((sx, sv_x, sa_x), (gx, gv_x, ga_x), T)
|
|
169
|
+
y_psolver = self._Poly((sy, sv_y, sa_y), (gy, gv_y, ga_y), T)
|
|
170
|
+
|
|
171
|
+
for t in np.arange(0.0, T + self.dt, self.dt):
|
|
172
|
+
traj.time.append(t)
|
|
173
|
+
traj.x.append(x_psolver.x(t))
|
|
174
|
+
traj.y.append(y_psolver.x(t))
|
|
175
|
+
|
|
176
|
+
vx, vy = x_psolver.dx(t), y_psolver.dx(t)
|
|
177
|
+
traj.v.append(math.hypot(vx, vy))
|
|
178
|
+
traj.yaw.append(math.atan2(vy, vx))
|
|
179
|
+
|
|
180
|
+
ax, ay = x_psolver.ddx(t), y_psolver.ddx(t)
|
|
181
|
+
a = math.hypot(ax, ay)
|
|
182
|
+
if len(traj.v) >= 2 and traj.v[-1] - traj.v[-2] < 0.0:
|
|
183
|
+
a *= -1
|
|
184
|
+
traj.a.append(a)
|
|
185
|
+
|
|
186
|
+
jx, jy = x_psolver.dddx(t), y_psolver.dddx(t)
|
|
187
|
+
j = math.hypot(jx, jy)
|
|
188
|
+
if len(traj.a) >= 2 and traj.a[-1] - traj.a[-2] < 0.0:
|
|
189
|
+
j *= -1
|
|
190
|
+
traj.jerk.append(j)
|
|
191
|
+
|
|
192
|
+
if (max(np.abs(traj.a)) <= self.max_acc
|
|
193
|
+
and max(np.abs(traj.jerk)) <= self.max_jerk):
|
|
194
|
+
return traj
|
|
195
|
+
traj.clear()
|
|
196
|
+
|
|
197
|
+
return traj
|