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.
Files changed (33) hide show
  1. python_motion_planning/__init__.py +1 -1
  2. python_motion_planning/common/env/map/grid.py +568 -147
  3. python_motion_planning/common/utils/geometry.py +21 -31
  4. python_motion_planning/path_planner/graph_search/lazy_theta_star.py +15 -7
  5. python_motion_planning/path_planner/graph_search/theta_star.py +4 -3
  6. python_motion_planning/path_planner/sample_search/rrt.py +6 -6
  7. python_motion_planning/path_planner/sample_search/rrt_connect.py +2 -2
  8. python_motion_planning/path_planner/sample_search/rrt_star.py +31 -11
  9. python_motion_planning/traj_optimizer/__init__.py +2 -0
  10. python_motion_planning/traj_optimizer/base_curve_generator.py +53 -0
  11. python_motion_planning/traj_optimizer/curve_generator/__init__.py +2 -0
  12. python_motion_planning/traj_optimizer/curve_generator/point_based/__init__.py +2 -0
  13. python_motion_planning/traj_optimizer/curve_generator/point_based/bspline.py +256 -0
  14. python_motion_planning/traj_optimizer/curve_generator/point_based/cubic_spline.py +115 -0
  15. python_motion_planning/traj_optimizer/curve_generator/pose_based/__init__.py +4 -0
  16. python_motion_planning/traj_optimizer/curve_generator/pose_based/bezier.py +121 -0
  17. python_motion_planning/traj_optimizer/curve_generator/pose_based/dubins.py +355 -0
  18. python_motion_planning/traj_optimizer/curve_generator/pose_based/polynomial.py +197 -0
  19. python_motion_planning/traj_optimizer/curve_generator/pose_based/reeds_shepp.py +606 -0
  20. {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.1.dist-info}/METADATA +23 -16
  21. {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.1.dist-info}/RECORD +24 -22
  22. {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.1.dist-info}/WHEEL +1 -1
  23. python_motion_planning/curve_generator/__init__.py +0 -9
  24. python_motion_planning/curve_generator/bezier_curve.py +0 -131
  25. python_motion_planning/curve_generator/bspline_curve.py +0 -271
  26. python_motion_planning/curve_generator/cubic_spline.py +0 -128
  27. python_motion_planning/curve_generator/curve.py +0 -64
  28. python_motion_planning/curve_generator/dubins_curve.py +0 -348
  29. python_motion_planning/curve_generator/fem_pos_smooth.py +0 -114
  30. python_motion_planning/curve_generator/polynomial_curve.py +0 -226
  31. python_motion_planning/curve_generator/reeds_shepp.py +0 -736
  32. {python_motion_planning-2.0.dev2.dist-info → python_motion_planning-2.1.dist-info}/licenses/LICENSE +0 -0
  33. {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