pathplanning 0.1.0__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.
- CurvesGenerator/__init__.py +1 -0
- CurvesGenerator/bezier_path.py +145 -0
- CurvesGenerator/bspline_curve.py +80 -0
- CurvesGenerator/cubic_spline.py +262 -0
- CurvesGenerator/draw.py +66 -0
- CurvesGenerator/dubins_path.py +351 -0
- CurvesGenerator/quartic_polynomial.py +43 -0
- CurvesGenerator/quintic_polynomial.py +143 -0
- CurvesGenerator/reeds_shepp.py +720 -0
- Sampling_based_Planning/__init__.py +1 -0
- Sampling_based_Planning/rrt_2D/__init__.py +1 -0
- Sampling_based_Planning/rrt_2D/adaptively_informed_trees.py +0 -0
- Sampling_based_Planning/rrt_2D/advanced_batch_informed_trees.py +0 -0
- Sampling_based_Planning/rrt_2D/batch_informed_trees.py +402 -0
- Sampling_based_Planning/rrt_2D/dubins_rrt_star.py +321 -0
- Sampling_based_Planning/rrt_2D/dynamic_rrt.py +342 -0
- Sampling_based_Planning/rrt_2D/env.py +45 -0
- Sampling_based_Planning/rrt_2D/extended_rrt.py +251 -0
- Sampling_based_Planning/rrt_2D/fast_marching_trees.py +220 -0
- Sampling_based_Planning/rrt_2D/informed_rrt_star.py +305 -0
- Sampling_based_Planning/rrt_2D/plotting.py +115 -0
- Sampling_based_Planning/rrt_2D/queue.py +62 -0
- Sampling_based_Planning/rrt_2D/rrt.py +114 -0
- Sampling_based_Planning/rrt_2D/rrt_connect.py +156 -0
- Sampling_based_Planning/rrt_2D/rrt_sharp.py +0 -0
- Sampling_based_Planning/rrt_2D/rrt_star.py +184 -0
- Sampling_based_Planning/rrt_2D/rrt_star_smart.py +311 -0
- Sampling_based_Planning/rrt_2D/utils.py +138 -0
- Sampling_based_Planning/rrt_3D/ABIT_star3D.py +148 -0
- Sampling_based_Planning/rrt_3D/BIT_star3D.py +353 -0
- Sampling_based_Planning/rrt_3D/FMT_star3D.py +192 -0
- Sampling_based_Planning/rrt_3D/__init__.py +1 -0
- Sampling_based_Planning/rrt_3D/dynamic_rrt3D.py +239 -0
- Sampling_based_Planning/rrt_3D/env3D.py +148 -0
- Sampling_based_Planning/rrt_3D/extend_rrt3D.py +102 -0
- Sampling_based_Planning/rrt_3D/informed_rrt_star3D.py +234 -0
- Sampling_based_Planning/rrt_3D/plot_util3D.py +167 -0
- Sampling_based_Planning/rrt_3D/queue.py +86 -0
- Sampling_based_Planning/rrt_3D/rrt3D.py +72 -0
- Sampling_based_Planning/rrt_3D/rrt_connect3D.py +187 -0
- Sampling_based_Planning/rrt_3D/rrt_star3D.py +107 -0
- Sampling_based_Planning/rrt_3D/rrt_star_smart3D.py +0 -0
- Sampling_based_Planning/rrt_3D/utils3D.py +416 -0
- Search_based_Planning/Search_3D/Anytime_Dstar3D.py +245 -0
- Search_based_Planning/Search_3D/Astar3D.py +119 -0
- Search_based_Planning/Search_3D/Dstar3D.py +192 -0
- Search_based_Planning/Search_3D/DstarLite3D.py +219 -0
- Search_based_Planning/Search_3D/LP_Astar3D.py +194 -0
- Search_based_Planning/Search_3D/LRT_Astar3D.py +87 -0
- Search_based_Planning/Search_3D/RTA_Astar3D.py +78 -0
- Search_based_Planning/Search_3D/__init__.py +1 -0
- Search_based_Planning/Search_3D/bidirectional_Astar3D.py +115 -0
- Search_based_Planning/Search_3D/env3D.py +147 -0
- Search_based_Planning/Search_3D/plot_util3D.py +155 -0
- Search_based_Planning/Search_3D/queue.py +154 -0
- Search_based_Planning/Search_3D/utils3D.py +359 -0
- Search_based_Planning/__init__.py +1 -0
- Search_based_Planning/plan2d/__init__.py +17 -0
- Search_based_Planning/plan2d/anytime_dstar.py +314 -0
- Search_based_Planning/plan2d/anytime_repairing_astar.py +220 -0
- Search_based_Planning/plan2d/astar.py +225 -0
- Search_based_Planning/plan2d/best_first_search.py +67 -0
- Search_based_Planning/plan2d/bidirectional_astar.py +229 -0
- Search_based_Planning/plan2d/breadth_first_search.py +69 -0
- Search_based_Planning/plan2d/depth_first_search.py +69 -0
- Search_based_Planning/plan2d/dijkstra.py +68 -0
- Search_based_Planning/plan2d/dstar.py +304 -0
- Search_based_Planning/plan2d/dstar_lite.py +239 -0
- Search_based_Planning/plan2d/learning_realtime_astar.py +230 -0
- Search_based_Planning/plan2d/lifelong_planning_astar.py +254 -0
- Search_based_Planning/plan2d/plan2d_facade.py +494 -0
- Search_based_Planning/plan2d/realtime_adaptive_astar.py +234 -0
- Search_based_Planning/plan2d/run.py +55 -0
- Search_based_Planning/plan2d/utils/__init__.py +1 -0
- Search_based_Planning/plan2d/utils/env.py +48 -0
- Search_based_Planning/plan2d/utils/plotting.py +258 -0
- Search_based_Planning/plan2d/utils/queue.py +62 -0
- pathplanning-0.1.0.dist-info/LICENSE +21 -0
- pathplanning-0.1.0.dist-info/METADATA +354 -0
- pathplanning-0.1.0.dist-info/RECORD +86 -0
- pathplanning-0.1.0.dist-info/WHEEL +5 -0
- pathplanning-0.1.0.dist-info/top_level.txt +4 -0
- pathplanningv2/__init__.py +29 -0
- pathplanningv2/api.py +18 -0
- pathplanningv2/registry.py +107 -0
- pathplanningv2/search2d.py +15 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RRT_CONNECT_2D
|
|
3
|
+
@author: damminhtien
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import math
|
|
9
|
+
import copy
|
|
10
|
+
import numpy as np
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
|
|
13
|
+
sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
|
|
14
|
+
"/../../Sampling_based_Planning/")
|
|
15
|
+
|
|
16
|
+
from Sampling_based_Planning.rrt_2D import env, plotting, utils
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Node:
|
|
20
|
+
def __init__(self, n):
|
|
21
|
+
self.x = n[0]
|
|
22
|
+
self.y = n[1]
|
|
23
|
+
self.parent = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RrtConnect:
|
|
27
|
+
def __init__(self, s_start, s_goal, step_len, goal_sample_rate, iter_max):
|
|
28
|
+
self.s_start = Node(s_start)
|
|
29
|
+
self.s_goal = Node(s_goal)
|
|
30
|
+
self.step_len = step_len
|
|
31
|
+
self.goal_sample_rate = goal_sample_rate
|
|
32
|
+
self.iter_max = iter_max
|
|
33
|
+
self.V1 = [self.s_start]
|
|
34
|
+
self.V2 = [self.s_goal]
|
|
35
|
+
|
|
36
|
+
self.env = env.Env()
|
|
37
|
+
self.plotting = plotting.Plotting(s_start, s_goal)
|
|
38
|
+
self.utils = utils.Utils()
|
|
39
|
+
|
|
40
|
+
self.x_range = self.env.x_range
|
|
41
|
+
self.y_range = self.env.y_range
|
|
42
|
+
self.obs_circle = self.env.obs_circle
|
|
43
|
+
self.obs_rectangle = self.env.obs_rectangle
|
|
44
|
+
self.obs_boundary = self.env.obs_boundary
|
|
45
|
+
|
|
46
|
+
def planning(self):
|
|
47
|
+
for i in range(self.iter_max):
|
|
48
|
+
node_rand = self.generate_random_node(self.s_goal, self.goal_sample_rate)
|
|
49
|
+
node_near = self.nearest_neighbor(self.V1, node_rand)
|
|
50
|
+
node_new = self.new_state(node_near, node_rand)
|
|
51
|
+
|
|
52
|
+
if node_new and not self.utils.is_collision(node_near, node_new):
|
|
53
|
+
self.V1.append(node_new)
|
|
54
|
+
node_near_prim = self.nearest_neighbor(self.V2, node_new)
|
|
55
|
+
node_new_prim = self.new_state(node_near_prim, node_new)
|
|
56
|
+
|
|
57
|
+
if node_new_prim and not self.utils.is_collision(node_new_prim, node_near_prim):
|
|
58
|
+
self.V2.append(node_new_prim)
|
|
59
|
+
|
|
60
|
+
while True:
|
|
61
|
+
node_new_prim2 = self.new_state(node_new_prim, node_new)
|
|
62
|
+
if node_new_prim2 and not self.utils.is_collision(node_new_prim2, node_new_prim):
|
|
63
|
+
self.V2.append(node_new_prim2)
|
|
64
|
+
node_new_prim = self.change_node(node_new_prim, node_new_prim2)
|
|
65
|
+
else:
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
if self.is_node_same(node_new_prim, node_new):
|
|
69
|
+
break
|
|
70
|
+
|
|
71
|
+
if self.is_node_same(node_new_prim, node_new):
|
|
72
|
+
return self.extract_path(node_new, node_new_prim)
|
|
73
|
+
|
|
74
|
+
if len(self.V2) < len(self.V1):
|
|
75
|
+
list_mid = self.V2
|
|
76
|
+
self.V2 = self.V1
|
|
77
|
+
self.V1 = list_mid
|
|
78
|
+
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def change_node(node_new_prim, node_new_prim2):
|
|
83
|
+
node_new = Node((node_new_prim2.x, node_new_prim2.y))
|
|
84
|
+
node_new.parent = node_new_prim
|
|
85
|
+
|
|
86
|
+
return node_new
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def is_node_same(node_new_prim, node_new):
|
|
90
|
+
if node_new_prim.x == node_new.x and \
|
|
91
|
+
node_new_prim.y == node_new.y:
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
def generate_random_node(self, sample_goal, goal_sample_rate):
|
|
97
|
+
delta = self.utils.delta
|
|
98
|
+
|
|
99
|
+
if np.random.random() > goal_sample_rate:
|
|
100
|
+
return Node((np.random.uniform(self.x_range[0] + delta, self.x_range[1] - delta),
|
|
101
|
+
np.random.uniform(self.y_range[0] + delta, self.y_range[1] - delta)))
|
|
102
|
+
|
|
103
|
+
return sample_goal
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def nearest_neighbor(node_list, n):
|
|
107
|
+
return node_list[int(np.argmin([math.hypot(nd.x - n.x, nd.y - n.y)
|
|
108
|
+
for nd in node_list]))]
|
|
109
|
+
|
|
110
|
+
def new_state(self, node_start, node_end):
|
|
111
|
+
dist, theta = self.get_distance_and_angle(node_start, node_end)
|
|
112
|
+
|
|
113
|
+
dist = min(self.step_len, dist)
|
|
114
|
+
node_new = Node((node_start.x + dist * math.cos(theta),
|
|
115
|
+
node_start.y + dist * math.sin(theta)))
|
|
116
|
+
node_new.parent = node_start
|
|
117
|
+
|
|
118
|
+
return node_new
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def extract_path(node_new, node_new_prim):
|
|
122
|
+
path1 = [(node_new.x, node_new.y)]
|
|
123
|
+
node_now = node_new
|
|
124
|
+
|
|
125
|
+
while node_now.parent is not None:
|
|
126
|
+
node_now = node_now.parent
|
|
127
|
+
path1.append((node_now.x, node_now.y))
|
|
128
|
+
|
|
129
|
+
path2 = [(node_new_prim.x, node_new_prim.y)]
|
|
130
|
+
node_now = node_new_prim
|
|
131
|
+
|
|
132
|
+
while node_now.parent is not None:
|
|
133
|
+
node_now = node_now.parent
|
|
134
|
+
path2.append((node_now.x, node_now.y))
|
|
135
|
+
|
|
136
|
+
return list(list(reversed(path1)) + path2)
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def get_distance_and_angle(node_start, node_end):
|
|
140
|
+
dx = node_end.x - node_start.x
|
|
141
|
+
dy = node_end.y - node_start.y
|
|
142
|
+
return math.hypot(dx, dy), math.atan2(dy, dx)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def main():
|
|
146
|
+
x_start = (2, 2) # Starting node
|
|
147
|
+
x_goal = (49, 24) # Goal node
|
|
148
|
+
|
|
149
|
+
rrt_conn = RrtConnect(x_start, x_goal, 0.8, 0.05, 5000)
|
|
150
|
+
path = rrt_conn.planning()
|
|
151
|
+
|
|
152
|
+
rrt_conn.plotting.animation_connect(rrt_conn.V1, rrt_conn.V2, path, "RRT_CONNECT")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
if __name__ == '__main__':
|
|
156
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RRT_star 2D
|
|
3
|
+
@author: damminhtien
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import math
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
|
|
12
|
+
"/../../Sampling_based_Planning/")
|
|
13
|
+
|
|
14
|
+
from Sampling_based_Planning.rrt_2D import env, plotting, utils, queue
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Node:
|
|
18
|
+
def __init__(self, n):
|
|
19
|
+
self.x = n[0]
|
|
20
|
+
self.y = n[1]
|
|
21
|
+
self.parent = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RrtStar:
|
|
25
|
+
def __init__(self, x_start, x_goal, step_len,
|
|
26
|
+
goal_sample_rate, search_radius, iter_max):
|
|
27
|
+
self.s_start = Node(x_start)
|
|
28
|
+
self.s_goal = Node(x_goal)
|
|
29
|
+
self.step_len = step_len
|
|
30
|
+
self.goal_sample_rate = goal_sample_rate
|
|
31
|
+
self.search_radius = search_radius
|
|
32
|
+
self.iter_max = iter_max
|
|
33
|
+
self.vertex = [self.s_start]
|
|
34
|
+
self.path = []
|
|
35
|
+
|
|
36
|
+
self.env = env.Env()
|
|
37
|
+
self.plotting = plotting.Plotting(x_start, x_goal)
|
|
38
|
+
self.utils = utils.Utils()
|
|
39
|
+
|
|
40
|
+
self.x_range = self.env.x_range
|
|
41
|
+
self.y_range = self.env.y_range
|
|
42
|
+
self.obs_circle = self.env.obs_circle
|
|
43
|
+
self.obs_rectangle = self.env.obs_rectangle
|
|
44
|
+
self.obs_boundary = self.env.obs_boundary
|
|
45
|
+
|
|
46
|
+
def planning(self):
|
|
47
|
+
for k in range(self.iter_max):
|
|
48
|
+
node_rand = self.generate_random_node(self.goal_sample_rate)
|
|
49
|
+
node_near = self.nearest_neighbor(self.vertex, node_rand)
|
|
50
|
+
node_new = self.new_state(node_near, node_rand)
|
|
51
|
+
|
|
52
|
+
if k % 500 == 0:
|
|
53
|
+
print(k)
|
|
54
|
+
|
|
55
|
+
if node_new and not self.utils.is_collision(node_near, node_new):
|
|
56
|
+
neighbor_index = self.find_near_neighbor(node_new)
|
|
57
|
+
self.vertex.append(node_new)
|
|
58
|
+
|
|
59
|
+
if neighbor_index:
|
|
60
|
+
self.choose_parent(node_new, neighbor_index)
|
|
61
|
+
self.rewire(node_new, neighbor_index)
|
|
62
|
+
|
|
63
|
+
index = self.search_goal_parent()
|
|
64
|
+
self.path = self.extract_path(self.vertex[index])
|
|
65
|
+
|
|
66
|
+
self.plotting.animation(self.vertex, self.path, "rrt*, N = " + str(self.iter_max))
|
|
67
|
+
|
|
68
|
+
def new_state(self, node_start, node_goal):
|
|
69
|
+
dist, theta = self.get_distance_and_angle(node_start, node_goal)
|
|
70
|
+
|
|
71
|
+
dist = min(self.step_len, dist)
|
|
72
|
+
node_new = Node((node_start.x + dist * math.cos(theta),
|
|
73
|
+
node_start.y + dist * math.sin(theta)))
|
|
74
|
+
|
|
75
|
+
node_new.parent = node_start
|
|
76
|
+
|
|
77
|
+
return node_new
|
|
78
|
+
|
|
79
|
+
def choose_parent(self, node_new, neighbor_index):
|
|
80
|
+
cost = [self.get_new_cost(self.vertex[i], node_new) for i in neighbor_index]
|
|
81
|
+
|
|
82
|
+
cost_min_index = neighbor_index[int(np.argmin(cost))]
|
|
83
|
+
node_new.parent = self.vertex[cost_min_index]
|
|
84
|
+
|
|
85
|
+
def rewire(self, node_new, neighbor_index):
|
|
86
|
+
for i in neighbor_index:
|
|
87
|
+
node_neighbor = self.vertex[i]
|
|
88
|
+
|
|
89
|
+
if self.cost(node_neighbor) > self.get_new_cost(node_new, node_neighbor):
|
|
90
|
+
node_neighbor.parent = node_new
|
|
91
|
+
|
|
92
|
+
def search_goal_parent(self):
|
|
93
|
+
dist_list = [math.hypot(n.x - self.s_goal.x, n.y - self.s_goal.y) for n in self.vertex]
|
|
94
|
+
node_index = [i for i in range(len(dist_list)) if dist_list[i] <= self.step_len]
|
|
95
|
+
|
|
96
|
+
if len(node_index) > 0:
|
|
97
|
+
cost_list = [dist_list[i] + self.cost(self.vertex[i]) for i in node_index
|
|
98
|
+
if not self.utils.is_collision(self.vertex[i], self.s_goal)]
|
|
99
|
+
return node_index[int(np.argmin(cost_list))]
|
|
100
|
+
|
|
101
|
+
return len(self.vertex) - 1
|
|
102
|
+
|
|
103
|
+
def get_new_cost(self, node_start, node_end):
|
|
104
|
+
dist, _ = self.get_distance_and_angle(node_start, node_end)
|
|
105
|
+
|
|
106
|
+
return self.cost(node_start) + dist
|
|
107
|
+
|
|
108
|
+
def generate_random_node(self, goal_sample_rate):
|
|
109
|
+
delta = self.utils.delta
|
|
110
|
+
|
|
111
|
+
if np.random.random() > goal_sample_rate:
|
|
112
|
+
return Node((np.random.uniform(self.x_range[0] + delta, self.x_range[1] - delta),
|
|
113
|
+
np.random.uniform(self.y_range[0] + delta, self.y_range[1] - delta)))
|
|
114
|
+
|
|
115
|
+
return self.s_goal
|
|
116
|
+
|
|
117
|
+
def find_near_neighbor(self, node_new):
|
|
118
|
+
n = len(self.vertex) + 1
|
|
119
|
+
r = min(self.search_radius * math.sqrt((math.log(n) / n)), self.step_len)
|
|
120
|
+
|
|
121
|
+
dist_table = [math.hypot(nd.x - node_new.x, nd.y - node_new.y) for nd in self.vertex]
|
|
122
|
+
dist_table_index = [ind for ind in range(len(dist_table)) if dist_table[ind] <= r and
|
|
123
|
+
not self.utils.is_collision(node_new, self.vertex[ind])]
|
|
124
|
+
|
|
125
|
+
return dist_table_index
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def nearest_neighbor(node_list, n):
|
|
129
|
+
return node_list[int(np.argmin([math.hypot(nd.x - n.x, nd.y - n.y)
|
|
130
|
+
for nd in node_list]))]
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def cost(node_p):
|
|
134
|
+
node = node_p
|
|
135
|
+
cost = 0.0
|
|
136
|
+
|
|
137
|
+
while node.parent:
|
|
138
|
+
cost += math.hypot(node.x - node.parent.x, node.y - node.parent.y)
|
|
139
|
+
node = node.parent
|
|
140
|
+
|
|
141
|
+
return cost
|
|
142
|
+
|
|
143
|
+
def update_cost(self, parent_node):
|
|
144
|
+
OPEN = queue.QueueFIFO()
|
|
145
|
+
OPEN.put(parent_node)
|
|
146
|
+
|
|
147
|
+
while not OPEN.empty():
|
|
148
|
+
node = OPEN.get()
|
|
149
|
+
|
|
150
|
+
if len(node.child) == 0:
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
for node_c in node.child:
|
|
154
|
+
node_c.Cost = self.get_new_cost(node, node_c)
|
|
155
|
+
OPEN.put(node_c)
|
|
156
|
+
|
|
157
|
+
def extract_path(self, node_end):
|
|
158
|
+
path = [[self.s_goal.x, self.s_goal.y]]
|
|
159
|
+
node = node_end
|
|
160
|
+
|
|
161
|
+
while node.parent is not None:
|
|
162
|
+
path.append([node.x, node.y])
|
|
163
|
+
node = node.parent
|
|
164
|
+
path.append([node.x, node.y])
|
|
165
|
+
|
|
166
|
+
return path
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def get_distance_and_angle(node_start, node_end):
|
|
170
|
+
dx = node_end.x - node_start.x
|
|
171
|
+
dy = node_end.y - node_start.y
|
|
172
|
+
return math.hypot(dx, dy), math.atan2(dy, dx)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def main():
|
|
176
|
+
x_start = (18, 8) # Starting node
|
|
177
|
+
x_goal = (37, 18) # Goal node
|
|
178
|
+
|
|
179
|
+
rrt_star = RrtStar(x_start, x_goal, 10, 0.10, 20, 10000)
|
|
180
|
+
rrt_star.planning()
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
if __name__ == '__main__':
|
|
184
|
+
main()
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RRT_STAR_SMART 2D
|
|
3
|
+
@author: damminhtien
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import math
|
|
9
|
+
import random
|
|
10
|
+
import numpy as np
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
import matplotlib.patches as patches
|
|
13
|
+
from scipy.spatial.transform import Rotation as Rot
|
|
14
|
+
|
|
15
|
+
sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
|
|
16
|
+
"/../../Sampling_based_Planning/")
|
|
17
|
+
|
|
18
|
+
from Sampling_based_Planning.rrt_2D import env, plotting, utils
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Node:
|
|
22
|
+
def __init__(self, n):
|
|
23
|
+
self.x = n[0]
|
|
24
|
+
self.y = n[1]
|
|
25
|
+
self.parent = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class RrtStarSmart:
|
|
29
|
+
def __init__(self, x_start, x_goal, step_len,
|
|
30
|
+
goal_sample_rate, search_radius, iter_max):
|
|
31
|
+
self.x_start = Node(x_start)
|
|
32
|
+
self.x_goal = Node(x_goal)
|
|
33
|
+
self.step_len = step_len
|
|
34
|
+
self.goal_sample_rate = goal_sample_rate
|
|
35
|
+
self.search_radius = search_radius
|
|
36
|
+
self.iter_max = iter_max
|
|
37
|
+
|
|
38
|
+
self.env = env.Env()
|
|
39
|
+
self.plotting = plotting.Plotting(x_start, x_goal)
|
|
40
|
+
self.utils = utils.Utils()
|
|
41
|
+
|
|
42
|
+
self.fig, self.ax = plt.subplots()
|
|
43
|
+
self.delta = self.utils.delta
|
|
44
|
+
self.x_range = self.env.x_range
|
|
45
|
+
self.y_range = self.env.y_range
|
|
46
|
+
self.obs_circle = self.env.obs_circle
|
|
47
|
+
self.obs_rectangle = self.env.obs_rectangle
|
|
48
|
+
self.obs_boundary = self.env.obs_boundary
|
|
49
|
+
|
|
50
|
+
self.V = [self.x_start]
|
|
51
|
+
self.beacons = []
|
|
52
|
+
self.beacons_radius = 2
|
|
53
|
+
self.direct_cost_old = np.inf
|
|
54
|
+
self.obs_vertex = self.utils.get_obs_vertex()
|
|
55
|
+
self.path = None
|
|
56
|
+
|
|
57
|
+
def planning(self):
|
|
58
|
+
n = 0
|
|
59
|
+
b = 2
|
|
60
|
+
InitPathFlag = False
|
|
61
|
+
self.ReformObsVertex()
|
|
62
|
+
|
|
63
|
+
for k in range(self.iter_max):
|
|
64
|
+
if k % 200 == 0:
|
|
65
|
+
print(k)
|
|
66
|
+
|
|
67
|
+
if (k - n) % b == 0 and len(self.beacons) > 0:
|
|
68
|
+
x_rand = self.Sample(self.beacons)
|
|
69
|
+
else:
|
|
70
|
+
x_rand = self.Sample()
|
|
71
|
+
|
|
72
|
+
x_nearest = self.Nearest(self.V, x_rand)
|
|
73
|
+
x_new = self.Steer(x_nearest, x_rand)
|
|
74
|
+
|
|
75
|
+
if x_new and not self.utils.is_collision(x_nearest, x_new):
|
|
76
|
+
X_near = self.Near(self.V, x_new)
|
|
77
|
+
self.V.append(x_new)
|
|
78
|
+
|
|
79
|
+
if X_near:
|
|
80
|
+
# choose parent
|
|
81
|
+
cost_list = [self.Cost(x_near) + self.Line(x_near, x_new) for x_near in X_near]
|
|
82
|
+
x_new.parent = X_near[int(np.argmin(cost_list))]
|
|
83
|
+
|
|
84
|
+
# rewire
|
|
85
|
+
c_min = self.Cost(x_new)
|
|
86
|
+
for x_near in X_near:
|
|
87
|
+
c_near = self.Cost(x_near)
|
|
88
|
+
c_new = c_min + self.Line(x_new, x_near)
|
|
89
|
+
if c_new < c_near:
|
|
90
|
+
x_near.parent = x_new
|
|
91
|
+
|
|
92
|
+
if not InitPathFlag and self.InitialPathFound(x_new):
|
|
93
|
+
InitPathFlag = True
|
|
94
|
+
n = k
|
|
95
|
+
|
|
96
|
+
if InitPathFlag:
|
|
97
|
+
self.PathOptimization(x_new)
|
|
98
|
+
if k % 5 == 0:
|
|
99
|
+
self.animation()
|
|
100
|
+
|
|
101
|
+
self.path = self.ExtractPath()
|
|
102
|
+
self.animation()
|
|
103
|
+
plt.plot([x for x, _ in self.path], [y for _, y in self.path], '-r')
|
|
104
|
+
plt.pause(0.01)
|
|
105
|
+
plt.show()
|
|
106
|
+
|
|
107
|
+
def PathOptimization(self, node):
|
|
108
|
+
direct_cost_new = 0.0
|
|
109
|
+
node_end = self.x_goal
|
|
110
|
+
|
|
111
|
+
while node.parent:
|
|
112
|
+
node_parent = node.parent
|
|
113
|
+
if not self.utils.is_collision(node_parent, node_end):
|
|
114
|
+
node_end.parent = node_parent
|
|
115
|
+
else:
|
|
116
|
+
direct_cost_new += self.Line(node, node_end)
|
|
117
|
+
node_end = node
|
|
118
|
+
|
|
119
|
+
node = node_parent
|
|
120
|
+
|
|
121
|
+
if direct_cost_new < self.direct_cost_old:
|
|
122
|
+
self.direct_cost_old = direct_cost_new
|
|
123
|
+
self.UpdateBeacons()
|
|
124
|
+
|
|
125
|
+
def UpdateBeacons(self):
|
|
126
|
+
node = self.x_goal
|
|
127
|
+
beacons = []
|
|
128
|
+
|
|
129
|
+
while node.parent:
|
|
130
|
+
near_vertex = [v for v in self.obs_vertex
|
|
131
|
+
if (node.x - v[0]) ** 2 + (node.y - v[1]) ** 2 < 9]
|
|
132
|
+
if len(near_vertex) > 0:
|
|
133
|
+
for v in near_vertex:
|
|
134
|
+
beacons.append(v)
|
|
135
|
+
|
|
136
|
+
node = node.parent
|
|
137
|
+
|
|
138
|
+
self.beacons = beacons
|
|
139
|
+
|
|
140
|
+
def ReformObsVertex(self):
|
|
141
|
+
obs_vertex = []
|
|
142
|
+
|
|
143
|
+
for obs in self.obs_vertex:
|
|
144
|
+
for vertex in obs:
|
|
145
|
+
obs_vertex.append(vertex)
|
|
146
|
+
|
|
147
|
+
self.obs_vertex = obs_vertex
|
|
148
|
+
|
|
149
|
+
def Steer(self, x_start, x_goal):
|
|
150
|
+
dist, theta = self.get_distance_and_angle(x_start, x_goal)
|
|
151
|
+
dist = min(self.step_len, dist)
|
|
152
|
+
node_new = Node((x_start.x + dist * math.cos(theta),
|
|
153
|
+
x_start.y + dist * math.sin(theta)))
|
|
154
|
+
node_new.parent = x_start
|
|
155
|
+
|
|
156
|
+
return node_new
|
|
157
|
+
|
|
158
|
+
def Near(self, nodelist, node):
|
|
159
|
+
n = len(self.V) + 1
|
|
160
|
+
r = 50 * math.sqrt((math.log(n) / n))
|
|
161
|
+
|
|
162
|
+
dist_table = [(nd.x - node.x) ** 2 + (nd.y - node.y) ** 2 for nd in nodelist]
|
|
163
|
+
X_near = [nodelist[ind] for ind in range(len(dist_table)) if dist_table[ind] <= r ** 2 and
|
|
164
|
+
not self.utils.is_collision(node, nodelist[ind])]
|
|
165
|
+
|
|
166
|
+
return X_near
|
|
167
|
+
|
|
168
|
+
def Sample(self, goal=None):
|
|
169
|
+
if goal is None:
|
|
170
|
+
delta = self.utils.delta
|
|
171
|
+
goal_sample_rate = self.goal_sample_rate
|
|
172
|
+
|
|
173
|
+
if np.random.random() > goal_sample_rate:
|
|
174
|
+
return Node((np.random.uniform(self.x_range[0] + delta, self.x_range[1] - delta),
|
|
175
|
+
np.random.uniform(self.y_range[0] + delta, self.y_range[1] - delta)))
|
|
176
|
+
|
|
177
|
+
return self.x_goal
|
|
178
|
+
else:
|
|
179
|
+
R = self.beacons_radius
|
|
180
|
+
r = random.uniform(0, R)
|
|
181
|
+
theta = random.uniform(0, 2 * math.pi)
|
|
182
|
+
ind = random.randint(0, len(goal) - 1)
|
|
183
|
+
|
|
184
|
+
return Node((goal[ind][0] + r * math.cos(theta),
|
|
185
|
+
goal[ind][1] + r * math.sin(theta)))
|
|
186
|
+
|
|
187
|
+
def SampleFreeSpace(self):
|
|
188
|
+
delta = self.delta
|
|
189
|
+
|
|
190
|
+
if np.random.random() > self.goal_sample_rate:
|
|
191
|
+
return Node((np.random.uniform(self.x_range[0] + delta, self.x_range[1] - delta),
|
|
192
|
+
np.random.uniform(self.y_range[0] + delta, self.y_range[1] - delta)))
|
|
193
|
+
|
|
194
|
+
return self.x_goal
|
|
195
|
+
|
|
196
|
+
def ExtractPath(self):
|
|
197
|
+
path = []
|
|
198
|
+
node = self.x_goal
|
|
199
|
+
|
|
200
|
+
while node.parent:
|
|
201
|
+
path.append([node.x, node.y])
|
|
202
|
+
node = node.parent
|
|
203
|
+
|
|
204
|
+
path.append([self.x_start.x, self.x_start.y])
|
|
205
|
+
|
|
206
|
+
return path
|
|
207
|
+
|
|
208
|
+
def InitialPathFound(self, node):
|
|
209
|
+
if self.Line(node, self.x_goal) < self.step_len:
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
return False
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def Nearest(nodelist, n):
|
|
216
|
+
return nodelist[int(np.argmin([(nd.x - n.x) ** 2 + (nd.y - n.y) ** 2
|
|
217
|
+
for nd in nodelist]))]
|
|
218
|
+
|
|
219
|
+
@staticmethod
|
|
220
|
+
def Line(x_start, x_goal):
|
|
221
|
+
return math.hypot(x_goal.x - x_start.x, x_goal.y - x_start.y)
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def Cost(node):
|
|
225
|
+
cost = 0.0
|
|
226
|
+
if node.parent is None:
|
|
227
|
+
return cost
|
|
228
|
+
|
|
229
|
+
while node.parent:
|
|
230
|
+
cost += math.hypot(node.x - node.parent.x, node.y - node.parent.y)
|
|
231
|
+
node = node.parent
|
|
232
|
+
|
|
233
|
+
return cost
|
|
234
|
+
|
|
235
|
+
@staticmethod
|
|
236
|
+
def get_distance_and_angle(node_start, node_end):
|
|
237
|
+
dx = node_end.x - node_start.x
|
|
238
|
+
dy = node_end.y - node_start.y
|
|
239
|
+
return math.hypot(dx, dy), math.atan2(dy, dx)
|
|
240
|
+
|
|
241
|
+
def animation(self):
|
|
242
|
+
plt.cla()
|
|
243
|
+
self.plot_grid("rrt*-Smart, N = " + str(self.iter_max))
|
|
244
|
+
plt.gcf().canvas.mpl_connect(
|
|
245
|
+
'key_release_event',
|
|
246
|
+
lambda event: [exit(0) if event.key == 'escape' else None])
|
|
247
|
+
|
|
248
|
+
for node in self.V:
|
|
249
|
+
if node.parent:
|
|
250
|
+
plt.plot([node.x, node.parent.x], [node.y, node.parent.y], "-g")
|
|
251
|
+
|
|
252
|
+
if self.beacons:
|
|
253
|
+
theta = np.arange(0, 2 * math.pi, 0.1)
|
|
254
|
+
r = self.beacons_radius
|
|
255
|
+
|
|
256
|
+
for v in self.beacons:
|
|
257
|
+
x = v[0] + r * np.cos(theta)
|
|
258
|
+
y = v[1] + r * np.sin(theta)
|
|
259
|
+
plt.plot(x, y, linestyle='--', linewidth=2, color='darkorange')
|
|
260
|
+
|
|
261
|
+
plt.pause(0.01)
|
|
262
|
+
|
|
263
|
+
def plot_grid(self, name):
|
|
264
|
+
|
|
265
|
+
for (ox, oy, w, h) in self.obs_boundary:
|
|
266
|
+
self.ax.add_patch(
|
|
267
|
+
patches.Rectangle(
|
|
268
|
+
(ox, oy), w, h,
|
|
269
|
+
edgecolor='black',
|
|
270
|
+
facecolor='black',
|
|
271
|
+
fill=True
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
for (ox, oy, w, h) in self.obs_rectangle:
|
|
276
|
+
self.ax.add_patch(
|
|
277
|
+
patches.Rectangle(
|
|
278
|
+
(ox, oy), w, h,
|
|
279
|
+
edgecolor='black',
|
|
280
|
+
facecolor='gray',
|
|
281
|
+
fill=True
|
|
282
|
+
)
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
for (ox, oy, r) in self.obs_circle:
|
|
286
|
+
self.ax.add_patch(
|
|
287
|
+
patches.Circle(
|
|
288
|
+
(ox, oy), r,
|
|
289
|
+
edgecolor='black',
|
|
290
|
+
facecolor='gray',
|
|
291
|
+
fill=True
|
|
292
|
+
)
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
plt.plot(self.x_start.x, self.x_start.y, "bs", linewidth=3)
|
|
296
|
+
plt.plot(self.x_goal.x, self.x_goal.y, "rs", linewidth=3)
|
|
297
|
+
|
|
298
|
+
plt.title(name)
|
|
299
|
+
plt.axis("equal")
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def main():
|
|
303
|
+
x_start = (18, 8) # Starting node
|
|
304
|
+
x_goal = (37, 18) # Goal node
|
|
305
|
+
|
|
306
|
+
rrt = RrtStarSmart(x_start, x_goal, 1.5, 0.10, 0, 1000)
|
|
307
|
+
rrt.planning()
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
if __name__ == '__main__':
|
|
311
|
+
main()
|