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
@@ -1,2 +1,3 @@
1
1
  from .geometry import Geometry
2
- from .frame_transformer import FrameTransformer
2
+ from .frame_transformer import FrameTransformer
3
+ from .child_tree import ChildTree
@@ -0,0 +1,22 @@
1
+ """
2
+ @file: frame_transformer.py
3
+ @author: Wu Maojia
4
+ @update: 2025.12.19
5
+ """
6
+ from typing import Union
7
+
8
+ class ChildTree:
9
+ def __init__(self):
10
+ self.tree = {}
11
+
12
+ def __getitem__(self, point: tuple) -> Union[set, None]:
13
+ return self.tree.get(point)
14
+
15
+ def add(self, point: tuple, child_point: tuple) -> None:
16
+ if self.tree.get(point) is None:
17
+ self.tree[point] = set()
18
+ self.tree[point].add(child_point)
19
+
20
+ def remove(self, point: tuple, child_point: tuple) -> None:
21
+ if self.tree.get(point) is not None:
22
+ self.tree[point].discard(child_point)
@@ -35,42 +35,31 @@ class Geometry:
35
35
  else:
36
36
  raise ValueError("Invalid distance type")
37
37
 
38
- # @staticmethod
39
- # def angle(v1: tuple, v2: tuple) -> float:
40
- # """
41
- # Calculate the angle between two vectors
42
-
43
- # Args:
44
- # v1: First vector
45
- # v2: Second vector
46
-
47
- # Returns:
48
- # angle_rad: Angle in rad between the two vectors
49
- # """
50
- # if len(v1) != len(v2):
51
- # raise ValueError("Dimension mismatch")
52
-
53
- # dot_product = sum(a * b for a, b in zip(v1, v2))
54
- # v1_norm = math.sqrt(sum(a**2 for a in v1))
55
- # v2_norm = math.sqrt(sum(b**2 for b in v2))
56
-
57
- # if v1_norm == 0 or v2_norm == 0:
58
- # raise ValueError("Zero vector cannot calculate angle")
59
-
60
- # cos_theta = dot_product / (v1_norm * v2_norm)
38
+ @staticmethod
39
+ def mod_to_2pi(orient: np.ndarray) -> np.ndarray:
40
+ """
41
+ Regularize orientation to be within [0, 2*pi)
61
42
 
62
- # cos_theta = min(1.0, max(-1.0, cos_theta))
43
+ Args:
44
+ orient: the orientation angle
63
45
 
64
- # angle_rad = math.acos(cos_theta)
65
-
66
- # return angle_rad
46
+ Returns:
47
+ new_orient: modded orientation
48
+ """
49
+ return np.mod(orient, 2 * np.pi)
67
50
 
68
51
  @staticmethod
69
52
  def regularize_orient(orient: np.ndarray) -> np.ndarray:
70
53
  """
71
54
  Regularize orientation to be within (-pi, pi]
55
+
56
+ Args:
57
+ orient: the orientation angle
58
+
59
+ Returns:
60
+ new_orient: regularized orientation
72
61
  """
73
- return np.mod(orient + np.pi, 2 * np.pi) - np.pi
62
+ return -np.mod(-orient + np.pi, 2 * np.pi) + np.pi
74
63
 
75
64
  @staticmethod
76
65
  def add_orient_to_2d_path(path: List[Tuple[float, float]]) -> List[Tuple[float, float, float]]:
@@ -102,4 +91,4 @@ class Geometry:
102
91
  last_x, last_y = path[-1]
103
92
  path_with_orient.append((last_x, last_y, path_with_orient[-1][2]))
104
93
 
105
- return path_with_orient
94
+ return path_with_orient
@@ -1 +1,3 @@
1
- from .visualizer import *
1
+ from .base_visualizer import *
2
+ from .visualizer_2d import *
3
+ from .visualizer_3d import *
@@ -0,0 +1,165 @@
1
+ """
2
+ @file: base_visualizer.py
3
+ @author: Wu Maojia
4
+ @update: 2025.12.20
5
+ """
6
+ from typing import Union, Dict, List, Tuple, Any
7
+ from abc import ABC, abstractmethod
8
+
9
+ import numpy as np
10
+
11
+ from python_motion_planning.common.utils import Geometry
12
+
13
+ class BaseVisualizer(ABC):
14
+ """
15
+ Base visualizer for motion planning.
16
+ """
17
+ def __init__(self):
18
+ self.dim = None
19
+ self.trajs = {}
20
+
21
+ def __del__(self):
22
+ self.close()
23
+
24
+ @abstractmethod
25
+ def show(self):
26
+ """
27
+ Show plot.
28
+ """
29
+ pass
30
+
31
+ @abstractmethod
32
+ def close(self):
33
+ """
34
+ Close plot.
35
+ """
36
+ pass
37
+
38
+ def get_traj_info(self,
39
+ rid: int,
40
+ ref_path: List[Tuple[float, ...]],
41
+ goal_pose: np.ndarray,
42
+ goal_dist_tol: float,
43
+ goal_orient_tol: float
44
+ ) -> Dict[str, Any]:
45
+ """
46
+ Get trajectory information.
47
+
48
+ Args:
49
+ rid: Robot ID.
50
+ ref_path: Reference pose path (world frame).
51
+ goal_pose: Goal pose.
52
+ goal_dist_tol: Distance tolerance for goal.
53
+ goal_orient_tol: Orientation tolerance for goal.
54
+ """
55
+ traj = self.trajs[rid]
56
+
57
+ info = {
58
+ "traj_length": 0.0,
59
+ "navigation_error": None,
60
+ "DTW": None,
61
+ "nDTW": None,
62
+ "success": False,
63
+ "dist_success": False,
64
+ "oracle_success": False,
65
+ "oracle_dist_success": False,
66
+ "success_time": None,
67
+ "dist_success_time": None,
68
+ "oracle_success_time": None,
69
+ "oracle_dist_success_time": None,
70
+ }
71
+
72
+ goal_pos = goal_pose[:self.dim]
73
+ goal_orient = goal_pose[self.dim:]
74
+
75
+ for i in range(len(traj["poses"])):
76
+ pose = traj["poses"][i]
77
+ time = traj["time"][i]
78
+
79
+ pos = pose[:self.dim]
80
+ orient = pose[self.dim:]
81
+
82
+ if i > 0:
83
+ info["traj_length"] += np.linalg.norm(pos - traj["poses"][i-1][:self.dim])
84
+
85
+ if np.linalg.norm(pos - goal_pos) <= goal_dist_tol:
86
+ if not info["oracle_dist_success"]:
87
+ info["oracle_dist_success"] = True
88
+ info["oracle_dist_success_time"] = time
89
+
90
+ if not info["dist_success"]:
91
+ info["dist_success"] = True
92
+ info["dist_success_time"] = time
93
+
94
+ if np.abs(Geometry.regularize_orient(orient - goal_orient)) <= goal_orient_tol:
95
+ if not info["oracle_success"]:
96
+ info["oracle_success"] = True
97
+ info["oracle_success_time"] = time
98
+
99
+ if not info["success"]:
100
+ info["success"] = True
101
+ info["success_time"] = time
102
+
103
+ else:
104
+ info["success"] = False
105
+ info["success_time"] = None
106
+
107
+ else:
108
+ info["success"] = False
109
+ info["success_time"] = None
110
+ info["dist_success"] = False
111
+ info["dist_success_time"] = None
112
+
113
+ info["navigation_error"] = float(np.linalg.norm(traj["poses"][-1][:self.dim] - goal_pos))
114
+ info["traj_length"] = float(info["traj_length"])
115
+ info["DTW"], info["nDTW"] = self.calc_dtw_ndtw(np.array(traj["poses"])[:, :self.dim], np.array(ref_path)[:, :self.dim])
116
+ return info
117
+
118
+ def calc_dtw_ndtw(self, path1: np.ndarray, path2: np.ndarray) -> Tuple[float, float]:
119
+ """
120
+ Compute the Dynamic Time Warping (DTW) and normalized DTW (nDTW)
121
+ between two N-dimensional paths.
122
+
123
+ Args:
124
+ path1 (np.ndarray): Path 1, shape (N, D)
125
+ path2 (np.ndarray): Path 2, shape (M, D)
126
+
127
+ Returns:
128
+ dtw: accumulated dynamic time warping distance
129
+ ndtw: normalized DTW in [0, 1], higher means more similar
130
+
131
+ Reference:
132
+ [1] General Evaluation for Instruction Conditioned Navigation using Dynamic Time Warping
133
+ """
134
+ # Input validation
135
+ if path1.ndim != 2 or path2.ndim != 2:
136
+ raise ValueError("Both paths must be 2D arrays with shape (T, D).")
137
+ if path1.shape[1] != path2.shape[1]:
138
+ raise ValueError("Paths must have the same dimensionality.")
139
+
140
+ N, M = len(path1), len(path2)
141
+
142
+ # Initialize DTW cost matrix
143
+ dtw_matrix = np.full((N + 1, M + 1), np.inf)
144
+ dtw_matrix[0, 0] = 0.0
145
+
146
+ # Fill the DTW matrix
147
+ for i in range(1, N + 1):
148
+ for j in range(1, M + 1):
149
+ # Euclidean distance between points
150
+ cost = np.linalg.norm(path1[i - 1] - path2[j - 1])
151
+ # Accumulate cost with the best previous step
152
+ dtw_matrix[i, j] = cost + min(
153
+ dtw_matrix[i - 1, j], # insertion
154
+ dtw_matrix[i, j - 1], # deletion
155
+ dtw_matrix[i - 1, j - 1] # match
156
+ )
157
+
158
+ # Final DTW distance
159
+ dtw = dtw_matrix[N, M]
160
+
161
+ # Normalized DTW: exponential decay with respect to path length
162
+ max_len = max(N, M)
163
+ ndtw = np.exp(-dtw / (max_len + 1e-8)) # nDTW ∈ (0, 1]
164
+
165
+ return float(dtw), float(ndtw)