python-motion-planning 2.0.dev1__py3-none-any.whl → 2.0.dev2__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 (37) hide show
  1. python_motion_planning/common/env/map/base_map.py +2 -8
  2. python_motion_planning/common/env/map/grid.py +74 -81
  3. python_motion_planning/common/utils/__init__.py +2 -1
  4. python_motion_planning/common/utils/child_tree.py +22 -0
  5. python_motion_planning/common/visualizer/__init__.py +3 -1
  6. python_motion_planning/common/visualizer/base_visualizer.py +165 -0
  7. python_motion_planning/common/visualizer/{visualizer.py → visualizer_2d.py} +97 -220
  8. python_motion_planning/common/visualizer/visualizer_3d.py +242 -0
  9. python_motion_planning/controller/base_controller.py +37 -4
  10. python_motion_planning/controller/path_tracker/__init__.py +2 -1
  11. python_motion_planning/controller/path_tracker/apf.py +22 -23
  12. python_motion_planning/controller/path_tracker/dwa.py +14 -17
  13. python_motion_planning/controller/path_tracker/path_tracker.py +4 -1
  14. python_motion_planning/controller/path_tracker/pid.py +7 -1
  15. python_motion_planning/controller/path_tracker/pure_pursuit.py +7 -1
  16. python_motion_planning/controller/path_tracker/rpp.py +111 -0
  17. python_motion_planning/path_planner/__init__.py +2 -1
  18. python_motion_planning/path_planner/base_path_planner.py +45 -11
  19. python_motion_planning/path_planner/graph_search/__init__.py +4 -1
  20. python_motion_planning/path_planner/graph_search/a_star.py +12 -14
  21. python_motion_planning/path_planner/graph_search/dijkstra.py +15 -15
  22. python_motion_planning/path_planner/graph_search/gbfs.py +100 -0
  23. python_motion_planning/path_planner/graph_search/jps.py +199 -0
  24. python_motion_planning/path_planner/graph_search/lazy_theta_star.py +113 -0
  25. python_motion_planning/path_planner/graph_search/theta_star.py +17 -19
  26. python_motion_planning/path_planner/hybrid_search/__init__.py +1 -0
  27. python_motion_planning/path_planner/hybrid_search/voronoi_planner.py +204 -0
  28. python_motion_planning/path_planner/sample_search/__init__.py +2 -1
  29. python_motion_planning/path_planner/sample_search/rrt.py +70 -28
  30. python_motion_planning/path_planner/sample_search/rrt_connect.py +237 -0
  31. python_motion_planning/path_planner/sample_search/rrt_star.py +201 -151
  32. {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.dev2.dist-info}/METADATA +54 -19
  33. {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.dev2.dist-info}/RECORD +36 -27
  34. python_motion_planning/common/env/robot/tmp.py +0 -404
  35. {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.dev2.dist-info}/WHEEL +0 -0
  36. {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.dev2.dist-info}/licenses/LICENSE +0 -0
  37. {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.dev2.dist-info}/top_level.txt +0 -0
@@ -1,29 +1,38 @@
1
1
  """
2
2
  @file: rrt_star.py
3
- @author: Wu Maojia, Yang Haodong
4
- @update: 2025.10.3
3
+ @author: Wu Maojia
4
+ @update: 2025.12.19
5
5
  """
6
6
  import math
7
7
  import random
8
- from typing import Union, Dict, List, Tuple
8
+ from typing import Union, Dict, List, Tuple, Any
9
+
10
+ import numpy as np
11
+ import faiss
12
+
13
+ from python_motion_planning.common import Node, Grid, TYPES
14
+ from python_motion_planning.path_planner.sample_search import RRT
15
+ from python_motion_planning.common.utils.child_tree import ChildTree
9
16
 
10
- from python_motion_planning.common import BaseMap, Node, Grid, TYPES
11
- from python_motion_planning.path_planner.sample_search.rrt import RRT
12
17
 
13
18
  class RRTStar(RRT):
14
19
  """
15
20
  Class for RRT* (Rapidly-exploring Random Tree Star) path planner.
16
-
17
- RRT* is an optimized version of RRT that provides asymptotically optimal paths
18
- by rewiring the tree to maintain lower-cost connections.
21
+
22
+ RRT* extends RRT by:
23
+ 1. Selecting the best parent (minimum cost) for a new node.
24
+ 2. Rewiring nearby nodes through the new node if it improves their cost.
19
25
 
20
26
  Args:
21
- *args: see the parent class.
22
- radius: Radius for finding nearby nodes during rewiring.
23
- *kwargs: see the parent class.
27
+ *args: see parent class.
28
+ rewire_radius: Neighborhood radius for rewiring (If None, adaptively calculated with gamma factor).
29
+ gamma: A factor for calculating rewire_radius. For details, see [1]. (Disabled when rewire_radius is not None)
30
+ stop_until_sample_num: Stop until sample number limitation is reached, otherwise stop when goal is found.
31
+ propagate_cost_to_children: Whether to propagate cost to children. This is a fix for ensuring the correctness of g-value of each node. But it may slow the algorithm a little.
32
+ *kwargs: see parent class.
24
33
 
25
34
  References:
26
- [1] Sampling-based algorithms for optimal motion planning
35
+ [1] Sampling-based Algorithms for Optimal Motion Planning
27
36
 
28
37
  Examples:
29
38
  >>> map_ = Grid(bounds=[[0, 15], [0, 15]])
@@ -32,178 +41,219 @@ class RRTStar(RRT):
32
41
  >>> print(path_info['success'])
33
42
  True
34
43
 
35
- >>> planner.map_.type_map[3:10, 6] = TYPES.OBSTACLE
44
+ >>> planner = RRTStar(map_=map_, start=(5, 5), goal=(10, 10), sample_num=1000, stop_until_sample_num=True)
36
45
  >>> path, path_info = planner.plan()
37
46
  >>> print(path_info['success'])
38
47
  True
39
48
  """
40
- def __init__(self, *args,
41
- radius: float = 10.0,** kwargs) -> None:
49
+
50
+ def __init__(self, *args,
51
+ rewire_radius: float = None,
52
+ gamma: float = 50.0,
53
+ stop_until_sample_num: int = False,
54
+ propagate_cost_to_children: bool = True,
55
+ **kwargs) -> None:
42
56
  super().__init__(*args, **kwargs)
43
- # Radius for finding nearby nodes during rewiring phase
44
- self.radius = radius
57
+ self.rewire_radius = rewire_radius
58
+ self.gamma = gamma
59
+ self.stop_until_sample_num = stop_until_sample_num
60
+ self.best_results = self.failed_info
61
+ self.propagate_cost_to_children = propagate_cost_to_children
62
+
63
+ self._tree = None
64
+ self._child = None
45
65
 
46
66
  def __str__(self) -> str:
47
- return "Rapidly-exploring Random Tree Star (RRT*)"
67
+ return "RRT*"
48
68
 
49
- def plan(self) -> Union[list, dict]:
69
+ def plan(self) -> Union[List[Tuple[float, ...]], Dict[str, Any]]:
50
70
  """
51
- RRT* path planning algorithm implementation with optimality properties.
52
-
71
+ RRT* path planning algorithm implementation.
72
+
53
73
  Returns:
54
74
  path: A list containing the path waypoints
55
75
  path_info: A dictionary containing path information
56
76
  """
57
77
  # Initialize tree with start node
58
- tree = {}
78
+ self._tree = {}
79
+ self._child = ChildTree()
59
80
  start_node = Node(self.start, None, 0, 0)
60
- tree[self.start] = start_node
81
+ self._tree[self.start] = start_node
61
82
 
62
- # Main sampling loop
63
- for _ in range(self.sample_num):
64
- # Generate random sample node
83
+ # Initialize FAISS index
84
+ if self.use_faiss:
85
+ faiss_index = faiss.IndexFlatL2(self.dim)
86
+ faiss_nodes = []
87
+ self._faiss_add_node(start_node, faiss_index, faiss_nodes)
88
+
89
+ for i in range(self.sample_num):
90
+ # Generate random sample
65
91
  node_rand = self._generate_random_node()
66
-
67
- # Skip if node already exists
68
- if node_rand.current in tree:
92
+
93
+ if node_rand.current in self._tree:
69
94
  continue
70
-
71
- # Find nearest node in tree
72
- node_near = self._get_nearest_node(tree, node_rand)
73
-
74
- # Create new node towards random sample
75
- node_new = self._steer(node_near, node_rand)
95
+
96
+ # Find nearest node
97
+ node_nearest = self._get_nearest_node(self._tree, node_rand, faiss_index, faiss_nodes)
98
+
99
+ # Steer towards random sample
100
+ node_new = self._steer(node_nearest, node_rand)
76
101
  if node_new is None:
77
102
  continue
78
-
79
- # Check if edge is collision-free
80
- if self.map_.in_collision(node_new.current, node_near.current):
81
- continue
82
103
 
83
- # Find all nearby nodes within radius
84
- near_nodes = self._find_near_nodes(tree, node_new)
85
-
86
- # Select optimal parent from nearby nodes
87
- node_new, node_near = self._choose_parent(node_new, node_near, near_nodes)
88
- if node_new is None:
104
+ # Collision check
105
+ if self.map_.in_collision(
106
+ self.map_.point_float_to_int(node_nearest.current),
107
+ self.map_.point_float_to_int(node_new.current)
108
+ ):
89
109
  continue
90
-
91
- # Add new node to tree
92
- tree[node_new.current] = node_new
93
-
94
- # Rewire tree to potentially improve existing paths
95
- self._rewire(tree, node_new, near_nodes)
96
-
97
- # Check if goal is reachable
110
+
111
+ # Find nearby nodes for choosing best parent
112
+ near_nodes = self._get_near_nodes(node_new, faiss_index, faiss_nodes)
113
+
114
+ # Choose parent with minimum cost
115
+ min_parent = node_nearest
116
+ min_cost = node_nearest.g + self.get_cost(node_nearest.current, node_new.current)
117
+
118
+ for node in near_nodes:
119
+ if self.map_.in_collision(
120
+ self.map_.point_float_to_int(node.current),
121
+ self.map_.point_float_to_int(node_new.current)
122
+ ):
123
+ continue
124
+ cost = node.g + self.get_cost(node.current, node_new.current)
125
+ if cost < min_cost:
126
+ min_parent = node
127
+ min_cost = cost
128
+
129
+ # Add new node
130
+ if self.propagate_cost_to_children:
131
+ self._child.remove(node_new.parent, node_new.current)
132
+ self._child.add(min_parent.current, node_new.current)
133
+
134
+ node_new.parent = min_parent.current
135
+ node_new.g = min_cost
136
+ self._tree[node_new.current] = node_new
137
+ if self.use_faiss:
138
+ self._faiss_add_node(node_new, faiss_index, faiss_nodes)
139
+
140
+ # Rewire nearby nodes through new node
141
+ for node in near_nodes:
142
+ if node.current == min_parent.current:
143
+ continue
144
+ if self.map_.in_collision(
145
+ self.map_.point_float_to_int(node.current),
146
+ self.map_.point_float_to_int(node_new.current)
147
+ ):
148
+ continue
149
+
150
+ new_cost = node_new.g + self.get_cost(node_new.current, node.current)
151
+ if new_cost < node.g:
152
+ if self.propagate_cost_to_children:
153
+ self._child.remove(node.parent, node.current)
154
+ self._child.add(node_new.current, node.current)
155
+
156
+ node.parent = node_new.current
157
+ node.g = new_cost
158
+ self._tree[node.current] = node
159
+
160
+ if self.propagate_cost_to_children:
161
+ self._propagate_cost_to_children(node)
162
+
163
+ # Check goal connection
98
164
  dist_to_goal = self.get_cost(node_new.current, self.goal)
99
165
  if dist_to_goal <= self.max_dist:
100
- # Check final edge to goal
101
- if not self.map_.in_collision(node_new.current, self.goal):
166
+ if not self.map_.in_collision(
167
+ self.map_.point_float_to_int(node_new.current),
168
+ self.map_.point_float_to_int(self.goal)
169
+ ):
102
170
  goal_cost = node_new.g + dist_to_goal
103
- # Update goal node if it already exists with a lower cost path
104
- if self.goal in tree:
105
- if goal_cost < tree[self.goal].g:
106
- tree[self.goal].parent = node_new.current
107
- tree[self.goal].g = goal_cost
108
- else:
109
- goal_node = Node(self.goal, node_new.current, goal_cost, 0)
110
- tree[self.goal] = goal_node
111
-
112
- # Extract and return the path
113
- path, length, cost = self.extract_path(tree)
114
- return path, {
115
- "success": True,
116
- "start": self.start,
117
- "goal": self.goal,
118
- "length": length,
119
- "cost": cost,
120
- "expand": tree,
121
- }
122
-
123
- # Planning failed
124
- self.failed_info[1]["expand"] = tree
125
- return self.failed_info
126
-
127
- def _find_near_nodes(self, tree: Dict[tuple, Node], node_new: Node) -> List[Node]:
171
+ if self.goal not in self._tree or goal_cost < self._tree[self.goal].g:
172
+ if node_new.current == self.goal:
173
+ self._tree[self.goal] = node_new
174
+ else:
175
+ self._tree[self.goal] = Node(
176
+ self.goal,
177
+ node_new.current,
178
+ goal_cost,
179
+ 0
180
+ )
181
+ path, length, cost = self.extract_path(self._tree)
182
+ self.best_results = path, {
183
+ "success": True,
184
+ "start": self.start,
185
+ "goal": self.goal,
186
+ "length": length,
187
+ "cost": cost,
188
+ "expand": self._tree,
189
+ }
190
+
191
+ if not self.stop_until_sample_num:
192
+ return self.best_results
193
+
194
+ n = len(self._tree) + 1
195
+ radius = self.gamma * ((math.log(n) / n) ** (1 / self.dim))
196
+
197
+ # Planning stopped
198
+ self.best_results[1]["expand"] = self._tree
199
+ return self.best_results
200
+
201
+ def _get_near_nodes(self, node_new: Node, index=None, nodes=None) -> List[Node]:
128
202
  """
129
- Find all nodes in the tree within a specified radius of the new node.
130
-
203
+ Get nearby nodes within rewiring radius.
204
+
131
205
  Args:
132
- tree: Current tree of nodes
133
- node_new: Newly created node
134
-
206
+ node_new: Newly added node
207
+ index: FAISS index (required when `use_faiss`=True)
208
+ nodes: List of nodes in FAISS index (required when `use_faiss`=True)
209
+
135
210
  Returns:
136
- near_nodes: List of nodes within the specified radius
211
+ near_nodes: List of nearby nodes
137
212
  """
213
+ # Adaptive radius from RRT* theory
214
+ if self.rewire_radius is None:
215
+ n = len(self._tree) + 1
216
+ radius = self.gamma * ((math.log(n) / n) ** (1 / self.dim))
217
+ if self.discrete:
218
+ radius = max(1, radius)
219
+ else:
220
+ radius = self.rewire_radius
221
+
138
222
  near_nodes = []
139
- for node in tree.values():
140
- if self.get_cost(node.current, node_new.current) <= self.radius:
141
- near_nodes.append(node)
223
+
224
+ if self.use_faiss:
225
+ # range search using faiss
226
+ query = np.asarray(node_new.current, dtype=np.float32).reshape(1, -1)
227
+ lims, D, I = index.range_search(query, radius * radius)
228
+ for idx in I:
229
+ near_nodes.append(nodes[idx])
230
+
231
+ else:
232
+ # brute force radius search
233
+ for node in self._tree.values():
234
+ if self.get_cost(node.current, node_new.current) <= radius:
235
+ near_nodes.append(node)
236
+
142
237
  return near_nodes
143
238
 
144
- def _choose_parent(self, node_new: Node, node_near: Node, near_nodes: List[Node]) -> Tuple[Union[Node, None], Node]:
145
- """
146
- Select the optimal parent for the new node from nearby nodes to minimize cost.
147
-
148
- Args:
149
- node_new: Newly created node
150
- node_near: Nearest node in the tree
151
- near_nodes: List of nearby nodes
152
-
153
- Returns:
154
- node_new: Updated new node with optimal parent
155
- node_near: The chosen parent node
239
+ def _propagate_cost_to_children(self, node: Node):
156
240
  """
157
- # Initialize with nearest node as potential parent
158
- node_new.g = node_near.g + self.get_cost(node_near.current, node_new.current)
159
- best_parent = node_near
160
-
161
- # Check all nearby nodes for potentially lower-cost paths
162
- for node_near_candidate in near_nodes:
163
- # Skip if candidate is the same as initial nearest node
164
- if node_near_candidate.current == best_parent.current:
165
- continue
166
-
167
- # Check if path from candidate to new node is collision-free
168
- if self.map_.in_collision(node_near_candidate.current, node_new.current):
169
- continue
170
-
171
- # Calculate cost through this candidate parent
172
- new_cost = node_near_candidate.g + self.get_cost(node_near_candidate.current, node_new.current)
173
-
174
- # Update parent if this path is cheaper
175
- if new_cost < node_new.g:
176
- node_new.g = new_cost
177
- best_parent = node_near_candidate
178
-
179
- # Set the best parent for the new node
180
- node_new.parent = best_parent.current
181
- return node_new, best_parent
241
+ Propagate cost update to children of a node.
182
242
 
183
- def _rewire(self, tree: Dict[tuple, Node], node_new: Node, near_nodes: List[Node]) -> None:
184
- """
185
- Rewire the tree to potentially improve paths for existing nodes using the new node.
186
-
187
243
  Args:
188
- tree: Current tree of nodes
189
- node_new: Newly added node
190
- near_nodes: List of nearby nodes
244
+ node: Node to propagate cost to children
191
245
  """
192
- for node_near in near_nodes:
193
- # Skip if node is the parent of the new node
194
- if node_near.current == node_new.parent:
195
- continue
196
-
197
- # Check if path from new node to existing node is collision-free
198
- if self.map_.in_collision(node_new.current, node_near.current):
199
- continue
200
-
201
- # Calculate potential new cost for existing node through new node
202
- new_cost = node_new.g + self.get_cost(node_new.current, node_near.current)
203
-
204
- # Update parent if new path is cheaper
205
- if new_cost < node_near.g:
206
- node_near.parent = node_new.current
207
- node_near.g = new_cost
208
- # Update the node in the tree
209
- tree[node_near.current] = node_near
246
+ child_set = self._child[node.current]
247
+ if child_set is None:
248
+ return
249
+
250
+ for child in child_set:
251
+ node_child = self._tree.get(child)
252
+
253
+ if node_child is not None:
254
+ old_g = node_child.g
255
+ node_child.g = node.g + self.get_cost(
256
+ node.current, node_child.current
257
+ )
258
+ if not math.isclose(node_child.g, old_g):
259
+ self._propagate_cost_to_children(node_child)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-motion-planning
3
- Version: 2.0.dev1
3
+ Version: 2.0.dev2
4
4
  Summary: Motion planning algorithms for Python
5
5
  Maintainer-email: Wu Maojia <omige@mail.nwpu.edu.cn>, Yang Haodong <913982779@qq.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -689,6 +689,9 @@ Requires-Dist: scipy
689
689
  Requires-Dist: matplotlib
690
690
  Requires-Dist: osqp
691
691
  Requires-Dist: gymnasium
692
+ Requires-Dist: faiss-cpu
693
+ Requires-Dist: pyvista
694
+ Requires-Dist: pyvistaqt
692
695
  Dynamic: license-file
693
696
 
694
697
 
@@ -701,11 +704,13 @@ Dynamic: license-file
701
704
  * `Path Planning`: It's based on path constraints (such as obstacles), planning the optimal path sequence for the robot to travel without conflict between the start and goal.
702
705
  * `Trajectory planning`: It plans the motion state to approach the global path based on kinematics, dynamics constraints and path sequence.
703
706
 
707
+ This repository provides the implementations of common `Motion planning` algorithms, including path planners on N-D grid and controllers for path-tracking, a visualizer based on matplotlib and a toy physical simulator to test controllers.
708
+
704
709
  The theory analysis can be found at [motion-planning](https://blog.csdn.net/frigidwinter/category_11410243.html).
705
710
 
706
711
  We also provide [ROS C++](https://github.com/ai-winter/ros_motion_planning) version and [Matlab](https://github.com/ai-winter/matlab_motion_planning) version.
707
712
 
708
- This repository provides the implementations of common `Motion planning` algorithms. **Your stars and forks are welcome**. Submitting pull requests or joining our development team are also welcome. For trivial modification, please directly contribute to `dev` branch. For big modification, please [contact](#contact) us before you contribute.
713
+ **Your stars and forks are welcome!**
709
714
 
710
715
  # Quick Start
711
716
 
@@ -725,8 +730,9 @@ python_motion_planning
725
730
  | └─path_tracker
726
731
  ├─path_planner
727
732
  | ├─graph_search
728
- | └─sample_search
729
- └─curve_generation
733
+ | ├─sample_search
734
+ | └─hybrid_search
735
+ └─curve_generator
730
736
  ```
731
737
 
732
738
  ## Install
@@ -740,7 +746,7 @@ conda activate pmp
740
746
  To install the repository, please run the following command in shell.
741
747
 
742
748
  ```shell
743
- pip install python-motion-planning==2.0.dev1
749
+ pip install python-motion-planning==2.0.dev2
744
750
  ```
745
751
 
746
752
  ## Run
@@ -749,29 +755,40 @@ Please refer to the Tutorials part of [online documentation](https://ai-winter.g
749
755
 
750
756
  # Demos
751
757
  ## Path Planner
752
-
758
+ ### Graph Search
753
759
  |Planner|2D Grid|3D Grid
754
760
  |-------|-------|-------
755
- **GBFS**|Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
756
- **Dijkstra**|![dijkstra_2d.svg](assets/dijkstra_2d.svg)|![dijkstra_3d.svg](assets/dijkstra_3d.svg)
757
- **A\***|![a_star_2d.svg](assets/a_star_2d.svg)|![a_star_3d.svg](assets/a_star_3d.svg)
758
- **JPS**|Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
761
+ **Dijkstra**|![dijkstra_2d.svg](assets/dijkstra_2d.svg)|![dijkstra_3d.png](assets/dijkstra_3d.png)
762
+ **GBFS**|![gbfs_2d.svg](assets/gbfs_2d.svg)|![gbfs_3d.png](assets/gbfs_3d.png)
763
+ **A\***|![a_star_2d.svg](assets/a_star_2d.svg)|![a_star_3d.png](assets/a_star_3d.png)
764
+ **JPS**|![jps_2d.svg](assets/jps_2d.svg)|![jps_3d.png](assets/jps_3d.png)
765
+ **Theta\***|![theta_star_2d.svg](assets/theta_star_2d.svg)|![theta_star_3d.png](assets/theta_star_3d.png)
766
+ **Lazy Theta\***|![lazy_theta_star_2d.svg](assets/lazy_theta_star_2d.svg)|![lazy_theta_star_3d.png](assets/lazy_theta_star_3d.png)
759
767
  **D\***|Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
760
768
  **LPA\***|Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
761
769
  **D\* Lite**|Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
762
- **Theta\***|![theta_star_2d.svg](assets/theta_star_2d.svg)|![theta_star_3d.svg](assets/theta_star_3d.svg)
763
- **Lazy Theta\***|Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
764
- **S-Theta\***|Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
765
770
  **Anya**|Not implemented|Not implemented
766
- **Voronoi**|Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
767
- **RRT**|![rrt_2d.svg](assets/rrt_2d.svg)|![rrt_3d.svg](assets/rrt_3d.svg)
768
- **RRT\***|![rrt_star_2d.svg](assets/rrt_star_2d.svg)|![rrt_star_3d.svg](assets/rrt_star_3d.svg)
771
+
772
+ ### Sample Search
773
+ |Planner|2D Grid|3D Grid
774
+ |-------|-------|-------
775
+ **RRT**|![rrt_2d.svg](assets/rrt_2d.svg)|![rrt_3d.png](assets/rrt_3d.png)
776
+ **RRT\***|![rrt_star_2d.svg](assets/rrt_star_2d.svg)|![rrt_star_3d.png](assets/rrt_star_3d.png)
777
+ **RRT-Connect**|![rrt_connect_2d.svg](assets/rrt_connect_2d.svg)|![rrt_connect_3d.png](assets/rrt_connect_3d.png)
769
778
  **Informed RRT**|Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
770
- **RRT-Connect**|Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
779
+ **PRM**|Not implemented|Not implemented
780
+
781
+ ### Evolutionary Search
782
+ |Planner|2D Grid|3D Grid
783
+ |-------|-------|-------
771
784
  | **ACO** |Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
772
785
  | **GA** |Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
773
786
  | **PSO** |Implemented in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1), not migrated|Not implemented
774
787
 
788
+ ### Hybrid Search
789
+ |Planner|2D Grid|3D Grid
790
+ |-------|-------|-------
791
+ **Voronoi Planner**|![voronoi_planner_2d.svg](assets/voronoi_planner_2d.svg)|![voronoi_planner_3d.png](assets/voronoi_planner_3d.png)
775
792
 
776
793
  ## Controller
777
794
 
@@ -779,7 +796,7 @@ We provide a toy simulator with simple physical simulation to test controllers (
779
796
 
780
797
  In the following demos, the blue robot 1 is the `CircularRobot`, and the orange robot 2 is the `DiffDriveRobot`.
781
798
 
782
- |Planner|2D|3D
799
+ |Controller|2D|3D
783
800
  |-------|-------|-------
784
801
  |**Path Trakcer**|![path_tracker_2d.gif](assets/path_tracker_2d.gif)|Not implemented
785
802
  | **Pure Pursuit** |![pure_pursuit_2d.gif](assets/pure_pursuit_2d.gif)|Not implemented
@@ -799,7 +816,7 @@ In the following demos, the blue robot 1 is the `CircularRobot`, and the orange
799
816
 
800
817
  The visualization of the curve generators has not been implemented in current version. They can be visualized in [V1.1.1](https://github.com/ai-winter/python_motion_planning/tree/v1.1.1).
801
818
 
802
- | Planner |2D|3D|
819
+ |Generator|2D|3D|
803
820
  | ------- | -------------------------------------------------------- | --------------------------------------------------------
804
821
  | **Polynomia** | ![polynomial_curve_python.gif](assets/polynomial_curve_python.gif)|Not implemented
805
822
  | **Bezier** |![bezier_curve_python.png](assets/bezier_curve_python.png)|Not implemented
@@ -809,6 +826,24 @@ The visualization of the curve generators has not been implemented in current ve
809
826
  | **Reeds-Shepp** |![reeds_shepp_python.png](assets/reeds_shepp_python.gif)|Not implemented
810
827
  | **Fem-Pos Smoother** |![fem_pos_smoother_python.png](assets/fem_pos_smoother_python.png)|Not implemented
811
828
 
829
+ # Future Works
830
+
831
+ * N-D controllers (path-trackers).
832
+
833
+ * Path planning for robotic arms.
834
+
835
+ * Path planning on topological map.
836
+
837
+ * Application on ROS2.
838
+
839
+ * Application in mainstream robot simulation environments (e.g. Gazebo, Carla, Airsim, PyBullet, MuJoCo, Issac Sim).
840
+
841
+ * More mainstream motion planning algorithms.
842
+
843
+ * Performance optimization.
844
+
845
+ Contributors are welcome! For trivial modification, please directly contribute to `dev` branch. For big modification, please [contact](#contact) us before you contribute.
846
+
812
847
  # Contact
813
848
 
814
849
  Long-term maintainers: