forked from udacity/FCND-Motion-Planning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
planning_utils.py
173 lines (136 loc) · 5.33 KB
/
planning_utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
from enum import Enum
from queue import PriorityQueue
import numpy as np
from scipy.spatial import Voronoi
from bresenham import bresenham
def heuristic(position, goal_position):
return np.linalg.norm(np.array(position) - np.array(goal_position))
# This basically copied from Excercise in Lesson 6.15 (Graph Search Exercise)
# Create Grid and Voronoi graph
def create_grid_and_edges(data, drone_altitude, safety_distance):
"""
Returns a grid representation of a 2D configuration space
along with Voronoi graph edges given obstacle data and the
drone's altitude.
"""
# minimum and maximum north coordinates
north_min = np.floor(np.min(data[:, 0] - data[:, 3]))
north_max = np.ceil(np.max(data[:, 0] + data[:, 3]))
# minimum and maximum east coordinates
east_min = np.floor(np.min(data[:, 1] - data[:, 4]))
east_max = np.ceil(np.max(data[:, 1] + data[:, 4]))
# given the minimum and maximum coordinates we can
# calculate the size of the grid.
north_size = int(np.ceil((north_max - north_min)))
east_size = int(np.ceil((east_max - east_min)))
# Initialize an empty grid
grid = np.zeros((north_size, east_size))
# Center offset for grid
north_min_center = np.min(data[:, 0])
east_min_center = np.min(data[:, 1])
# Points list for Voronoi
points = []
# Populate the grid with obstacles
for i in range(data.shape[0]):
north, east, alt, d_north, d_east, d_alt = data[i, :]
if alt + d_alt + safety_distance > drone_altitude:
obstacle = [
int(north - d_north - safety_distance - north_min_center),
int(north + d_north + safety_distance - north_min_center),
int(east - d_east - safety_distance - east_min_center),
int(east + d_east + safety_distance - east_min_center),
]
grid[obstacle[0]:obstacle[1], obstacle[2]:obstacle[3]] = 1
# add center of obstacles to points list
points.append([north - north_min, east - east_min])
# create a voronoi graph based on
# location of obstacle centres
graph = Voronoi(points)
# check each edge from graph.ridge_vertices for collision
edges = []
for v in graph.ridge_vertices:
p1 = graph.vertices[v[0]]
p2 = graph.vertices[v[1]]
cells = list(bresenham(int(p1[0]), int(p1[1]), int(p2[0]), int(p2[1])))
hit = False
for c in cells:
# First check if we're off the map
if np.amin(c) < 0 or c[0] >= grid.shape[0] or c[1] >= grid.shape[1]:
hit = True
break
# Next check if we're in collision
if grid[c[0], c[1]] == 1:
hit = True
break
# If the edge does not hit on obstacle
# add it to the list
if not hit:
# array to tuple for future graph creation step)
p1 = (p1[0], p1[1])
p2 = (p2[0], p2[1])
edges.append((p1, p2))
return grid, edges, int(north_min), int(east_min)
# A-star on a graph
def a_star_graph(graph, heuristic, start, goal):
path = []
queue = PriorityQueue()
queue.put((0, start))
visited = set(start)
branch = {}
found = False
while not queue.empty():
item = queue.get()
current_cost = item[0]
current_node = item[1]
if current_node == goal:
print('Found a path.')
found = True
break
else:
for next_node in graph[current_node]:
cost = graph.edges[current_node, next_node]['weight']
new_cost = current_cost + cost + heuristic(next_node, goal)
if next_node not in visited:
visited.add(next_node)
queue.put((new_cost, next_node))
branch[next_node] = (new_cost, current_node, next_node)
path = []
path_cost = 0
if found:
# retrace steps
path = []
n = goal
path_cost = branch[n][0]
while branch[n][1] != start:
path.append(branch[n][1])
n = branch[n][1]
path.append(branch[n][1])
return path[::-1], path_cost
# Return nearest graph vertex to a given point
def nearest_point(p, graph):
ret = (0,0)
min_dist = 10000
for pp in graph.nodes:
dist = np.linalg.norm(np.array(p) - np.array(pp))
if dist < min_dist:
min_dist = dist
ret = pp
return ret
def point(p):
return np.array([p[0], p[1], 1.]).reshape(1, -1)
# collinearity check: increase epsilon to get rid of 'almost collinear' points
def collinearity_check(p1, p2, p3, epsilon=50):
m = np.concatenate((p1, p2, p3), 0)
det = np.linalg.det(m)
return abs(det) < epsilon
# prune path using collinearity check - from exercise in lesson 6.9 (Putting it Together Exercise)
def prune_path(path):
pruned_path = []
p = path[0]
pruned_path.append(p)
for i in range(1, len(path) - 2):
if(not collinearity_check(point(p), point(path[i]), point(path[i+1]))):
pruned_path.append(path[i])
p = path[i]
pruned_path.append(path[len(path)-1])
return pruned_path