python-motion-planning 2.0.dev1__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.
Files changed (60) hide show
  1. python_motion_planning/__init__.py +1 -1
  2. python_motion_planning/common/env/map/base_map.py +2 -8
  3. python_motion_planning/common/env/map/grid.py +456 -198
  4. python_motion_planning/common/utils/__init__.py +2 -1
  5. python_motion_planning/common/utils/child_tree.py +22 -0
  6. python_motion_planning/common/utils/geometry.py +18 -29
  7. python_motion_planning/common/visualizer/__init__.py +3 -1
  8. python_motion_planning/common/visualizer/base_visualizer.py +165 -0
  9. python_motion_planning/common/visualizer/{visualizer.py → visualizer_2d.py} +97 -220
  10. python_motion_planning/common/visualizer/visualizer_3d.py +242 -0
  11. python_motion_planning/controller/base_controller.py +37 -4
  12. python_motion_planning/controller/path_tracker/__init__.py +2 -1
  13. python_motion_planning/controller/path_tracker/apf.py +22 -23
  14. python_motion_planning/controller/path_tracker/dwa.py +14 -17
  15. python_motion_planning/controller/path_tracker/path_tracker.py +4 -1
  16. python_motion_planning/controller/path_tracker/pid.py +7 -1
  17. python_motion_planning/controller/path_tracker/pure_pursuit.py +7 -1
  18. python_motion_planning/controller/path_tracker/rpp.py +111 -0
  19. python_motion_planning/path_planner/__init__.py +2 -1
  20. python_motion_planning/path_planner/base_path_planner.py +45 -11
  21. python_motion_planning/path_planner/graph_search/__init__.py +4 -1
  22. python_motion_planning/path_planner/graph_search/a_star.py +12 -14
  23. python_motion_planning/path_planner/graph_search/dijkstra.py +15 -15
  24. python_motion_planning/path_planner/graph_search/gbfs.py +100 -0
  25. python_motion_planning/path_planner/graph_search/jps.py +199 -0
  26. python_motion_planning/path_planner/graph_search/lazy_theta_star.py +113 -0
  27. python_motion_planning/path_planner/graph_search/theta_star.py +17 -19
  28. python_motion_planning/path_planner/hybrid_search/__init__.py +1 -0
  29. python_motion_planning/path_planner/hybrid_search/voronoi_planner.py +204 -0
  30. python_motion_planning/path_planner/sample_search/__init__.py +2 -1
  31. python_motion_planning/path_planner/sample_search/rrt.py +73 -31
  32. python_motion_planning/path_planner/sample_search/rrt_connect.py +237 -0
  33. python_motion_planning/path_planner/sample_search/rrt_star.py +220 -150
  34. python_motion_planning/traj_optimizer/__init__.py +2 -0
  35. python_motion_planning/traj_optimizer/base_curve_generator.py +53 -0
  36. python_motion_planning/traj_optimizer/curve_generator/__init__.py +2 -0
  37. python_motion_planning/traj_optimizer/curve_generator/point_based/__init__.py +2 -0
  38. python_motion_planning/traj_optimizer/curve_generator/point_based/bspline.py +256 -0
  39. python_motion_planning/traj_optimizer/curve_generator/point_based/cubic_spline.py +115 -0
  40. python_motion_planning/traj_optimizer/curve_generator/pose_based/__init__.py +4 -0
  41. python_motion_planning/traj_optimizer/curve_generator/pose_based/bezier.py +121 -0
  42. python_motion_planning/traj_optimizer/curve_generator/pose_based/dubins.py +355 -0
  43. python_motion_planning/traj_optimizer/curve_generator/pose_based/polynomial.py +197 -0
  44. python_motion_planning/traj_optimizer/curve_generator/pose_based/reeds_shepp.py +606 -0
  45. {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.1.dist-info}/METADATA +71 -29
  46. python_motion_planning-2.0.1.dist-info/RECORD +64 -0
  47. {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.1.dist-info}/WHEEL +1 -1
  48. python_motion_planning/common/env/robot/tmp.py +0 -404
  49. python_motion_planning/curve_generator/__init__.py +0 -9
  50. python_motion_planning/curve_generator/bezier_curve.py +0 -131
  51. python_motion_planning/curve_generator/bspline_curve.py +0 -271
  52. python_motion_planning/curve_generator/cubic_spline.py +0 -128
  53. python_motion_planning/curve_generator/curve.py +0 -64
  54. python_motion_planning/curve_generator/dubins_curve.py +0 -348
  55. python_motion_planning/curve_generator/fem_pos_smooth.py +0 -114
  56. python_motion_planning/curve_generator/polynomial_curve.py +0 -226
  57. python_motion_planning/curve_generator/reeds_shepp.py +0 -736
  58. python_motion_planning-2.0.dev1.dist-info/RECORD +0 -53
  59. {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.1.dist-info}/licenses/LICENSE +0 -0
  60. {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,256 @@
1
+ """
2
+ @file: bspline_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 BSpline(BaseCurveGenerator):
15
+ """
16
+ Class for B-Spline curve generator.
17
+
18
+ Args:
19
+ *args: see the parent class.
20
+ k: Degree of the B-Spline curve.
21
+ param_mode: Parameter selection mode, one of {"centripetal", "chord_length", "uniform_spaced"}.
22
+ spline_mode: B-Spline construction mode, one of {"interpolation", "approximation"}.
23
+ *kwargs: see the parent class.
24
+
25
+ References:
26
+ [1] https://en.wikipedia.org/wiki/B-spline
27
+
28
+ Examples:
29
+ >>> generator = BSpline(step=0.1, k=3)
30
+ >>> points = [(0.0, 0.0), (10.0, 10.0), (20.0, 5.0)]
31
+ >>> path, curve_info = generator.generate(points)
32
+ >>> print(curve_info['success'])
33
+ True
34
+ """
35
+ def __init__(self, *args,
36
+ k: int = 3,
37
+ param_mode: str = "centripetal",
38
+ spline_mode: str = "interpolation",
39
+ **kwargs) -> None:
40
+ super().__init__(*args, **kwargs)
41
+ self.k = k
42
+
43
+ if param_mode not in ("centripetal", "chord_length", "uniform_spaced"):
44
+ raise ValueError("Parameter selection mode error!")
45
+ self.param_mode = param_mode
46
+
47
+ if spline_mode not in ("interpolation", "approximation"):
48
+ raise ValueError("Spline mode selection error!")
49
+ self.spline_mode = spline_mode
50
+
51
+ def __str__(self) -> str:
52
+ return "B-Spline Curve"
53
+
54
+ def generate(self, points: List[Tuple[float, ...]]) -> Tuple[List[Tuple[float, float]], Dict[str, Any]]:
55
+ """
56
+ Generate a B-Spline curve through a list of 2D points.
57
+
58
+ Args:
59
+ points: A list of 2D points (x, y) in world frame. If the points contain
60
+ additional entries (e.g. yaw), only the first two values are used.
61
+
62
+ Returns:
63
+ path: A list of (x, y) waypoints of the generated curve in world frame.
64
+ curve_info: A dictionary containing the curve information (success, length).
65
+ """
66
+ if len(points) < 2:
67
+ return [], {"success": False, "length": 0.0}
68
+
69
+ points_2d = [(float(p[0]), float(p[1])) for p in points]
70
+
71
+ t = np.linspace(0, 1, int(1 / self.step))
72
+ params = self._param_selection(points_2d)
73
+ knot = self._knot_generation(params, len(points_2d))
74
+
75
+ if self.spline_mode == "interpolation":
76
+ control_pts = self._interpolation(points_2d, params, knot)
77
+ else:
78
+ control_pts = self._approximation(points_2d, params, knot)
79
+ h = len(control_pts)
80
+ new_points = [(control_pts[i][0], control_pts[i][1]) for i in range(h)]
81
+ params = self._param_selection(new_points)
82
+ knot = self._knot_generation(params, h)
83
+
84
+ curve = self._generate_curve(t, self.k, knot, control_pts)
85
+ path = [(float(pt[0]), float(pt[1])) for pt in curve]
86
+
87
+ return path, {"success": True, "length": self.length(path)}
88
+
89
+ def _base_function(self, i: int, k: int, t: float, knot: List[float]) -> float:
90
+ """
91
+ Calculate the base function using the Cox-de Boor recursion.
92
+
93
+ Args:
94
+ i: The index of the base function.
95
+ k: The degree of the curve.
96
+ t: Parameter value.
97
+ knot: The knot vector.
98
+
99
+ Returns:
100
+ Nik_t: The value of the base function Nik(t).
101
+ """
102
+ Nik_t = 0.0
103
+ if k == 0:
104
+ Nik_t = 1.0 if knot[i] <= t < knot[i + 1] else 0.0
105
+ else:
106
+ length1 = knot[i + k] - knot[i]
107
+ length2 = knot[i + k + 1] - knot[i + 1]
108
+ if not length1 and not length2:
109
+ Nik_t = 0.0
110
+ elif not length1:
111
+ Nik_t = (knot[i + k + 1] - t) / length2 * self._base_function(i + 1, k - 1, t, knot)
112
+ elif not length2:
113
+ Nik_t = (t - knot[i]) / length1 * self._base_function(i, k - 1, t, knot)
114
+ else:
115
+ Nik_t = ((t - knot[i]) / length1 * self._base_function(i, k - 1, t, knot)
116
+ + (knot[i + k + 1] - t) / length2 * self._base_function(i + 1, k - 1, t, knot))
117
+ return Nik_t
118
+
119
+ def _param_selection(self, points: List[Tuple[float, float]]) -> List[float]:
120
+ """
121
+ Calculate the parameters of the given points using the selected method.
122
+
123
+ Args:
124
+ points: A list of 2D points.
125
+
126
+ Returns:
127
+ parameters: The parameters of the given points.
128
+ """
129
+ n = len(points)
130
+ x_list = [pt[0] for pt in points]
131
+ y_list = [pt[1] for pt in points]
132
+ dx, dy = np.diff(x_list), np.diff(y_list)
133
+
134
+ if self.param_mode == "uniform_spaced":
135
+ return np.linspace(0, 1, n).tolist()
136
+
137
+ if self.param_mode == "chord_length":
138
+ parameters = np.zeros(n)
139
+ s = np.cumsum([math.hypot(idx, idy) for (idx, idy) in zip(dx, dy)])
140
+ for i in range(1, n):
141
+ parameters[i] = s[i - 1] / s[-1]
142
+ return parameters.tolist()
143
+
144
+ # centripetal
145
+ alpha = 0.5
146
+ s = np.cumsum([math.pow(math.hypot(idx, idy), alpha) for (idx, idy) in zip(dx, dy)])
147
+ parameters = np.zeros(n)
148
+ for i in range(1, n):
149
+ parameters[i] = s[i - 1] / s[-1]
150
+ return parameters.tolist()
151
+
152
+ def _knot_generation(self, param: List[float], n: int) -> List[float]:
153
+ """
154
+ Generate the knot vector.
155
+
156
+ Args:
157
+ param: The parameters of the given points.
158
+ n: The number of data points.
159
+
160
+ Returns:
161
+ knot: The knot vector.
162
+ """
163
+ m = n + self.k + 1
164
+ knot = np.zeros(m)
165
+ for i in range(n, m):
166
+ knot[i] = 1
167
+ for i in range(self.k + 1, n):
168
+ for j in range(i - self.k, i):
169
+ knot[i] = knot[i] + param[j]
170
+ knot[i] = knot[i] / self.k
171
+ return knot.tolist()
172
+
173
+ def _interpolation(self, points: List[Tuple[float, float]], param: List[float],
174
+ knot: List[float]) -> np.ndarray:
175
+ """
176
+ Build a B-Spline curve of degree k defined by N control points that passes
177
+ through all N data points.
178
+
179
+ Args:
180
+ points: A list of 2D points.
181
+ param: The parameters of the given points.
182
+ knot: The knot vector.
183
+
184
+ Returns:
185
+ control_points: The control points of the resulting B-Spline.
186
+ """
187
+ n = len(points)
188
+ N = np.zeros((n, n))
189
+
190
+ for i in range(n):
191
+ for j in range(n):
192
+ N[i][j] = self._base_function(j, self.k, param[i], knot)
193
+ N[n - 1][n - 1] = 1
194
+ N_inv = np.linalg.inv(N)
195
+
196
+ D = np.array(points)
197
+ return N_inv @ D
198
+
199
+ def _approximation(self, points: List[Tuple[float, float]], param: List[float],
200
+ knot: List[float]) -> np.ndarray:
201
+ """
202
+ Build a B-Spline curve of degree k defined by H control points (H < N) that
203
+ passes through the first and last data points and approximates the rest in
204
+ the least-square sense.
205
+
206
+ Args:
207
+ points: A list of 2D points.
208
+ param: The parameters of the given points.
209
+ knot: The knot vector.
210
+
211
+ Returns:
212
+ control_points: The control points of the resulting B-Spline.
213
+ """
214
+ n = len(points)
215
+ D = np.array(points)
216
+
217
+ h = n - 1
218
+
219
+ N = np.zeros((n, h))
220
+ for i in range(n):
221
+ for j in range(h):
222
+ N[i][j] = self._base_function(j, self.k, param[i], knot)
223
+ N_ = N[1: n - 1, 1: h - 1]
224
+
225
+ qk = np.zeros((n - 2, 2))
226
+ for i in range(1, n - 1):
227
+ qk[i - 1] = D[i, :] - N[i][0] * D[0, :] - N[i][h - 1] * D[-1, :]
228
+ Q = N_.T @ qk
229
+
230
+ P = np.linalg.inv(N_.T @ N_) @ Q
231
+ P = np.insert(P, 0, D[0, :], axis=0)
232
+ P = np.insert(P, len(P), D[-1, :], axis=0)
233
+ return P
234
+
235
+ def _generate_curve(self, t: np.ndarray, k: int, knot: List[float],
236
+ control_pts: np.ndarray) -> np.ndarray:
237
+ """
238
+ Sample points on the B-Spline curve.
239
+
240
+ Args:
241
+ t: The parameter values to sample at.
242
+ k: The degree of the B-Spline curve.
243
+ knot: The knot vector.
244
+ control_pts: The control points.
245
+
246
+ Returns:
247
+ curve: Sampled points along the B-Spline curve.
248
+ """
249
+ N = np.zeros((len(t), len(control_pts)))
250
+
251
+ for i in range(len(t)):
252
+ for j in range(len(control_pts)):
253
+ N[i][j] = self._base_function(j, k, t[i], knot)
254
+ N[len(t) - 1][len(control_pts) - 1] = 1
255
+
256
+ return N @ control_pts
@@ -0,0 +1,115 @@
1
+ """
2
+ @file: cubic_spline.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
+ import bisect
9
+
10
+ import numpy as np
11
+
12
+ from python_motion_planning.traj_optimizer.base_curve_generator import BaseCurveGenerator
13
+
14
+
15
+ class CubicSpline(BaseCurveGenerator):
16
+ """
17
+ Class for cubic spline curve generator.
18
+
19
+ Args:
20
+ *args: see the parent class.
21
+ *kwargs: see the parent class.
22
+
23
+ References:
24
+ [1] https://en.wikipedia.org/wiki/Spline_(mathematics)#Algorithm_for_computing_natural_cubic_splines
25
+
26
+ Examples:
27
+ >>> generator = CubicSpline(step=0.1)
28
+ >>> points = [(0.0, 0.0), (10.0, 10.0), (20.0, 5.0)]
29
+ >>> path, curve_info = generator.generate(points)
30
+ >>> print(curve_info['success'])
31
+ True
32
+ """
33
+ def __init__(self, *args, **kwargs) -> None:
34
+ super().__init__(*args, **kwargs)
35
+
36
+ def __str__(self) -> str:
37
+ return "Cubic Spline"
38
+
39
+ def generate(self, points: List[Tuple[float, ...]]) -> Tuple[List[Tuple[float, float]], Dict[str, Any]]:
40
+ """
41
+ Generate a cubic spline curve through a list of 2D points.
42
+
43
+ Args:
44
+ points: A list of 2D points (x, y) in world frame. If the points contain
45
+ additional entries (e.g. yaw), only the first two values are used.
46
+
47
+ Returns:
48
+ path: A list of (x, y) waypoints of the generated curve in world frame.
49
+ curve_info: A dictionary containing the curve information (success, length).
50
+ """
51
+ if len(points) < 2:
52
+ return [], {"success": False, "length": 0.0}
53
+
54
+ x_list = [float(p[0]) for p in points]
55
+ y_list = [float(p[1]) for p in points]
56
+
57
+ dx, dy = np.diff(x_list), np.diff(y_list)
58
+ ds = [math.hypot(idx, idy) for (idx, idy) in zip(dx, dy)]
59
+ s = [0.0]
60
+ s.extend(np.cumsum(ds))
61
+ t = np.arange(0, s[-1], self.step)
62
+
63
+ path_x, _ = self._spline(s, x_list, t)
64
+ path_y, _ = self._spline(s, y_list, t)
65
+
66
+ path = [(float(ix), float(iy)) for ix, iy in zip(path_x, path_y)]
67
+ return path, {"success": True, "length": self.length(path)}
68
+
69
+ def _spline(self, x_list: List[float], y_list: List[float],
70
+ t: np.ndarray) -> Tuple[List[float], List[float]]:
71
+ """
72
+ Build and evaluate a 1D natural cubic spline y = f(x).
73
+
74
+ Args:
75
+ x_list: Monotonically increasing x-coordinates of the control points.
76
+ y_list: y-coordinates of the control points.
77
+ t: Values of x at which to evaluate the spline.
78
+
79
+ Returns:
80
+ p: Values of the spline evaluated at t.
81
+ dp: Values of the spline derivative evaluated at t.
82
+ """
83
+ a, b, c, d = y_list, [], [], []
84
+ h = np.diff(x_list)
85
+ num = len(x_list)
86
+
87
+ A = np.zeros((num, num))
88
+ for i in range(1, num - 1):
89
+ A[i, i - 1] = h[i - 1]
90
+ A[i, i] = 2.0 * (h[i - 1] + h[i])
91
+ A[i, i + 1] = h[i]
92
+ A[0, 0] = 1.0
93
+ A[num - 1, num - 1] = 1.0
94
+
95
+ B = np.zeros(num)
96
+ for i in range(1, num - 1):
97
+ B[i] = (3.0 * (a[i + 1] - a[i]) / h[i]
98
+ - 3.0 * (a[i] - a[i - 1]) / h[i - 1])
99
+
100
+ c = np.linalg.solve(A, B)
101
+ for i in range(num - 1):
102
+ d.append((c[i + 1] - c[i]) / (3.0 * h[i]))
103
+ b.append((a[i + 1] - a[i]) / h[i] - h[i] * (c[i + 1] + 2.0 * c[i]) / 3.0)
104
+
105
+ p, dp = [], []
106
+ for it in t:
107
+ if it < x_list[0] or it > x_list[-1]:
108
+ continue
109
+ i = bisect.bisect(x_list, it) - 1
110
+ i = min(max(i, 0), num - 2)
111
+ dx = it - x_list[i]
112
+ p.append(a[i] + b[i] * dx + c[i] * dx ** 2 + d[i] * dx ** 3)
113
+ dp.append(b[i] + 2.0 * c[i] * dx + 3.0 * d[i] * dx ** 2)
114
+
115
+ return p, dp
@@ -0,0 +1,4 @@
1
+ from .bezier import *
2
+ from .dubins import *
3
+ from .polynomial import *
4
+ from .reeds_shepp import *
@@ -0,0 +1,121 @@
1
+ """
2
+ @file: bezier_curve.py
3
+ @author: Yang Haodong, Wu Maojia
4
+ @update: 2026.4.12
5
+ """
6
+ from typing import List, Tuple, Dict, Any
7
+
8
+ import numpy as np
9
+ from scipy.special import comb
10
+
11
+ from python_motion_planning.traj_optimizer.base_curve_generator import BaseCurveGenerator
12
+
13
+
14
+ class Bezier(BaseCurveGenerator):
15
+ """
16
+ Class for Bezier curve generator.
17
+
18
+ Args:
19
+ *args: see the parent class.
20
+ offset: The offset of control points (larger value yields sharper curvature).
21
+ *kwargs: see the parent class.
22
+
23
+ References:
24
+ [1] https://en.wikipedia.org/wiki/B%C3%A9zier_curve
25
+
26
+ Examples:
27
+ >>> import math
28
+ >>> generator = Bezier(step=0.1, offset=3.0)
29
+ >>> points = [(0.0, 0.0, 0.0), (10.0, 10.0, -math.pi/2), (20.0, 5.0, math.pi/3)]
30
+ >>> path, curve_info = generator.generate(points)
31
+ >>> print(curve_info['success'])
32
+ True
33
+ """
34
+ def __init__(self, *args,
35
+ offset: float = 3.0,
36
+ **kwargs) -> None:
37
+ super().__init__(*args, **kwargs)
38
+ self.offset = offset
39
+
40
+ def __str__(self) -> str:
41
+ return "Bezier Curve"
42
+
43
+ def generate(self, points: List[Tuple[float, float, float]]) -> Tuple[List[Tuple[float, float]], Dict[str, Any]]:
44
+ """
45
+ Generate a concatenated Bezier curve through a list of poses.
46
+
47
+ Args:
48
+ points: A list of poses (x, y, yaw) in world frame.
49
+
50
+ Returns:
51
+ path: A list of (x, y) waypoints of the generated curve in world frame.
52
+ curve_info: A dictionary containing the curve information (success, length).
53
+ """
54
+ if len(points) < 2:
55
+ return [], {"success": False, "length": 0.0}
56
+
57
+ path: List[Tuple[float, float]] = []
58
+ for i in range(len(points) - 1):
59
+ segment, _ = self._generate_segment(points[i], points[i + 1])
60
+ path.extend([(float(pt[0]), float(pt[1])) for pt in segment])
61
+
62
+ return path, {"success": True, "length": self.length(path)}
63
+
64
+ def _generate_segment(self, start_pose: Tuple[float, float, float],
65
+ goal_pose: Tuple[float, float, float]
66
+ ) -> Tuple[List[np.ndarray], List[Tuple[float, float]]]:
67
+ """
68
+ Generate a single Bezier curve segment between two poses.
69
+
70
+ Args:
71
+ start_pose: Initial pose (x, y, yaw).
72
+ goal_pose: Target pose (x, y, yaw).
73
+
74
+ Returns:
75
+ segment: A list of points sampled from the Bezier curve.
76
+ control_points: The control points of the Bezier curve.
77
+ """
78
+ sx, sy, _ = start_pose
79
+ gx, gy, _ = goal_pose
80
+ n_points = max(int(np.hypot(sx - gx, sy - gy) / self.step), 2)
81
+ control_points = self._get_control_points(start_pose, goal_pose)
82
+
83
+ segment = [self._bezier(t, control_points) for t in np.linspace(0, 1, n_points)]
84
+ return segment, control_points
85
+
86
+ def _bezier(self, t: float, control_points: List[Tuple[float, float]]) -> np.ndarray:
87
+ """
88
+ Calculate the Bezier curve point.
89
+
90
+ Args:
91
+ t: Scale factor in [0, 1].
92
+ control_points: Control points of the Bezier curve.
93
+
94
+ Returns:
95
+ point: Point on the Bezier curve at the given t.
96
+ """
97
+ n = len(control_points) - 1
98
+ control_points = np.array(control_points)
99
+ return np.sum([comb(n, i) * t ** i * (1 - t) ** (n - i) * control_points[i]
100
+ for i in range(n + 1)], axis=0)
101
+
102
+ def _get_control_points(self, start_pose: Tuple[float, float, float],
103
+ goal_pose: Tuple[float, float, float]) -> List[Tuple[float, float]]:
104
+ """
105
+ Calculate the control points heuristically from start and goal poses.
106
+
107
+ Args:
108
+ start_pose: Initial pose (x, y, yaw).
109
+ goal_pose: Target pose (x, y, yaw).
110
+
111
+ Returns:
112
+ control_points: Control points of the Bezier curve.
113
+ """
114
+ sx, sy, syaw = start_pose
115
+ gx, gy, gyaw = goal_pose
116
+
117
+ dist = np.hypot(sx - gx, sy - gy) / self.offset
118
+ return [(sx, sy),
119
+ (sx + dist * np.cos(syaw), sy + dist * np.sin(syaw)),
120
+ (gx - dist * np.cos(gyaw), gy - dist * np.sin(gyaw)),
121
+ (gx, gy)]