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.
- python_motion_planning/common/env/map/base_map.py +2 -8
- python_motion_planning/common/env/map/grid.py +74 -81
- python_motion_planning/common/utils/__init__.py +2 -1
- python_motion_planning/common/utils/child_tree.py +22 -0
- python_motion_planning/common/visualizer/__init__.py +3 -1
- python_motion_planning/common/visualizer/base_visualizer.py +165 -0
- python_motion_planning/common/visualizer/{visualizer.py → visualizer_2d.py} +97 -220
- python_motion_planning/common/visualizer/visualizer_3d.py +242 -0
- python_motion_planning/controller/base_controller.py +37 -4
- python_motion_planning/controller/path_tracker/__init__.py +2 -1
- python_motion_planning/controller/path_tracker/apf.py +22 -23
- python_motion_planning/controller/path_tracker/dwa.py +14 -17
- python_motion_planning/controller/path_tracker/path_tracker.py +4 -1
- python_motion_planning/controller/path_tracker/pid.py +7 -1
- python_motion_planning/controller/path_tracker/pure_pursuit.py +7 -1
- python_motion_planning/controller/path_tracker/rpp.py +111 -0
- python_motion_planning/path_planner/__init__.py +2 -1
- python_motion_planning/path_planner/base_path_planner.py +45 -11
- python_motion_planning/path_planner/graph_search/__init__.py +4 -1
- python_motion_planning/path_planner/graph_search/a_star.py +12 -14
- python_motion_planning/path_planner/graph_search/dijkstra.py +15 -15
- python_motion_planning/path_planner/graph_search/gbfs.py +100 -0
- python_motion_planning/path_planner/graph_search/jps.py +199 -0
- python_motion_planning/path_planner/graph_search/lazy_theta_star.py +113 -0
- python_motion_planning/path_planner/graph_search/theta_star.py +17 -19
- python_motion_planning/path_planner/hybrid_search/__init__.py +1 -0
- python_motion_planning/path_planner/hybrid_search/voronoi_planner.py +204 -0
- python_motion_planning/path_planner/sample_search/__init__.py +2 -1
- python_motion_planning/path_planner/sample_search/rrt.py +70 -28
- python_motion_planning/path_planner/sample_search/rrt_connect.py +237 -0
- python_motion_planning/path_planner/sample_search/rrt_star.py +201 -151
- {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.dev2.dist-info}/METADATA +54 -19
- {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.dev2.dist-info}/RECORD +36 -27
- python_motion_planning/common/env/robot/tmp.py +0 -404
- {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.dev2.dist-info}/WHEEL +0 -0
- {python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.dev2.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
4
|
-
@update: 2025.
|
|
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*
|
|
18
|
-
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
44
|
-
self.
|
|
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 "
|
|
67
|
+
return "RRT*"
|
|
48
68
|
|
|
49
|
-
def plan(self) -> Union[
|
|
69
|
+
def plan(self) -> Union[List[Tuple[float, ...]], Dict[str, Any]]:
|
|
50
70
|
"""
|
|
51
|
-
RRT* path planning algorithm implementation
|
|
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
|
-
|
|
78
|
+
self._tree = {}
|
|
79
|
+
self._child = ChildTree()
|
|
59
80
|
start_node = Node(self.start, None, 0, 0)
|
|
60
|
-
|
|
81
|
+
self._tree[self.start] = start_node
|
|
61
82
|
|
|
62
|
-
#
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
68
|
-
if node_rand.current in tree:
|
|
92
|
+
|
|
93
|
+
if node_rand.current in self._tree:
|
|
69
94
|
continue
|
|
70
|
-
|
|
71
|
-
# Find nearest node
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
#
|
|
75
|
-
node_new = self._steer(
|
|
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
|
-
#
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
101
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
130
|
-
|
|
203
|
+
Get nearby nodes within rewiring radius.
|
|
204
|
+
|
|
131
205
|
Args:
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
189
|
-
node_new: Newly added node
|
|
190
|
-
near_nodes: List of nearby nodes
|
|
244
|
+
node: Node to propagate cost to children
|
|
191
245
|
"""
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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)
|
{python_motion_planning-2.0.dev1.dist-info → python_motion_planning-2.0.dev2.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-motion-planning
|
|
3
|
-
Version: 2.0.
|
|
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
|
-
|
|
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
|
-
|
|
|
729
|
-
└─
|
|
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.
|
|
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
|
-
**
|
|
756
|
-
**
|
|
757
|
-
**A\***|||
|
|
762
|
+
**GBFS**||
|
|
763
|
+
**A\***||
|
|
764
|
+
**JPS**||
|
|
765
|
+
**Theta\***||
|
|
766
|
+
**Lazy Theta\***||
|
|
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\***||
|
|
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
|
-
|
|
767
|
-
|
|
768
|
-
|
|
771
|
+
|
|
772
|
+
### Sample Search
|
|
773
|
+
|Planner|2D Grid|3D Grid
|
|
774
|
+
|-------|-------|-------
|
|
775
|
+
**RRT**||
|
|
776
|
+
**RRT\***||
|
|
777
|
+
**RRT-Connect**||
|
|
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
|
-
**
|
|
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**||
|
|
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
|
-
|
|
|
799
|
+
|Controller|2D|3D
|
|
783
800
|
|-------|-------|-------
|
|
784
801
|
|**Path Trakcer**||Not implemented
|
|
785
802
|
| **Pure Pursuit** ||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
|
-
|
|
|
819
|
+
|Generator|2D|3D|
|
|
803
820
|
| ------- | -------------------------------------------------------- | --------------------------------------------------------
|
|
804
821
|
| **Polynomia** | |Not implemented
|
|
805
822
|
| **Bezier** ||Not implemented
|
|
@@ -809,6 +826,24 @@ The visualization of the curve generators has not been implemented in current ve
|
|
|
809
826
|
| **Reeds-Shepp** ||Not implemented
|
|
810
827
|
| **Fem-Pos Smoother** ||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:
|