Spaces:
Configuration error
Configuration error
show both plots at the same time
Browse files- flight_environment.py +1 -1
- main.py +6 -5
- planners/path_planner.py +99 -12
- planners/rrt_planner.py +0 -99
flight_environment.py
CHANGED
|
@@ -150,7 +150,7 @@ class FlightEnvironment:
|
|
| 150 |
ax.scatter(xs[0], ys[0], zs[0], s=40)
|
| 151 |
ax.scatter(xs[-1], ys[-1], zs[-1], s=40)
|
| 152 |
self.set_axes_equal(ax)
|
| 153 |
-
plt.show()
|
| 154 |
|
| 155 |
|
| 156 |
def set_axes_equal(self,ax):
|
|
|
|
| 150 |
ax.scatter(xs[0], ys[0], zs[0], s=40)
|
| 151 |
ax.scatter(xs[-1], ys[-1], zs[-1], s=40)
|
| 152 |
self.set_axes_equal(ax)
|
| 153 |
+
plt.show(block=False)
|
| 154 |
|
| 155 |
|
| 156 |
def set_axes_equal(self,ax):
|
main.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
| 1 |
from flight_environment import FlightEnvironment
|
| 2 |
-
from planners.
|
| 3 |
from planners.trajectory_generator import plan_trajectory_through_path, plot_trajectory
|
| 4 |
from plot_plotly import plot_cylinders
|
| 5 |
|
| 6 |
-
env = FlightEnvironment(
|
| 7 |
start = (1,2,0)
|
| 8 |
goal = (18,18,3)
|
| 9 |
|
|
@@ -20,12 +20,13 @@ goal = (18,18,3)
|
|
| 20 |
path = rrt_planner(env, start, goal, step_size=1)
|
| 21 |
|
| 22 |
# --------------------------------------------------------------------------------------------------- #
|
|
|
|
| 23 |
|
| 24 |
-
#
|
| 25 |
plot_cylinders(env, path)
|
| 26 |
|
| 27 |
-
#
|
| 28 |
-
#
|
| 29 |
|
| 30 |
# --------------------------------------------------------------------------------------------------- #
|
| 31 |
# Call your trajectory planning algorithm here. The algorithm should
|
|
|
|
| 1 |
from flight_environment import FlightEnvironment
|
| 2 |
+
from planners.path_planner import rrt_planner
|
| 3 |
from planners.trajectory_generator import plan_trajectory_through_path, plot_trajectory
|
| 4 |
from plot_plotly import plot_cylinders
|
| 5 |
|
| 6 |
+
env = FlightEnvironment(200)
|
| 7 |
start = (1,2,0)
|
| 8 |
goal = (18,18,3)
|
| 9 |
|
|
|
|
| 20 |
path = rrt_planner(env, start, goal, step_size=1)
|
| 21 |
|
| 22 |
# --------------------------------------------------------------------------------------------------- #
|
| 23 |
+
# 3D env plot, choose one - Plotly or Matplotlib - comment the other
|
| 24 |
|
| 25 |
+
# Plotly
|
| 26 |
plot_cylinders(env, path)
|
| 27 |
|
| 28 |
+
# Matplotlib (laggy)
|
| 29 |
+
#env.plot_cylinders(path)
|
| 30 |
|
| 31 |
# --------------------------------------------------------------------------------------------------- #
|
| 32 |
# Call your trajectory planning algorithm here. The algorithm should
|
planners/path_planner.py
CHANGED
|
@@ -1,12 +1,99 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass
|
| 2 |
+
import numpy as np
|
| 3 |
+
import time
|
| 4 |
+
|
| 5 |
+
def is_segment_collision_free(env, p1, p2, step=0.1):
|
| 6 |
+
"""Return True if straight segment p1->p2 is free.
|
| 7 |
+
- env: FlightEnvironment instance
|
| 8 |
+
- p1,p2: iterable (x,y,z)
|
| 9 |
+
- step: distance between samples along the segment (meters)
|
| 10 |
+
"""
|
| 11 |
+
dist = np.linalg.norm(p2 - p1)
|
| 12 |
+
if dist == 0:
|
| 13 |
+
return not env.is_collide(tuple(p1)) and not env.is_collide(tuple(p1))
|
| 14 |
+
n = max(1, int(np.ceil(dist / step)))
|
| 15 |
+
for i in range(n + 1):
|
| 16 |
+
t = i / n
|
| 17 |
+
p = p1 * (1 - t) + p2 * t
|
| 18 |
+
if env.is_outside(tuple(p)) or env.is_collide(tuple(p)):
|
| 19 |
+
return False
|
| 20 |
+
return True
|
| 21 |
+
|
| 22 |
+
def rrt_planner(env, start, goal, max_iters=10000, step_size=1.0, goal_tolerance=0.8, goal_sample_rate=0.1):
|
| 23 |
+
"""
|
| 24 |
+
Basic RRT planner in continuous 3D space.
|
| 25 |
+
Returns a path as an (N x 3) numpy array from start to goal (inclusive).
|
| 26 |
+
"""
|
| 27 |
+
start = np.array(start, dtype=float)
|
| 28 |
+
goal = np.array(goal, dtype=float)
|
| 29 |
+
|
| 30 |
+
# quick checks
|
| 31 |
+
if env.is_outside(tuple(start)) or env.is_collide(tuple(start)):
|
| 32 |
+
raise RuntimeError("Start is invalid (outside or in collision).")
|
| 33 |
+
if env.is_outside(tuple(goal)) or env.is_collide(tuple(goal)):
|
| 34 |
+
raise RuntimeError("Goal is invalid (outside or in collision).")
|
| 35 |
+
|
| 36 |
+
# recorded statistics stored here, can be inspected later
|
| 37 |
+
stats = dict(collisions=0)
|
| 38 |
+
tstart = time.time()
|
| 39 |
+
|
| 40 |
+
@dataclass
|
| 41 |
+
class Node:
|
| 42 |
+
point: np.ndarray
|
| 43 |
+
parent: np.ndarray = None
|
| 44 |
+
|
| 45 |
+
# environment bounds
|
| 46 |
+
Xmax, Ymax, Zmax = env.space_size
|
| 47 |
+
|
| 48 |
+
nodes = [Node(start)]
|
| 49 |
+
|
| 50 |
+
for it in range(max_iters):
|
| 51 |
+
# sample random point sometimes, otherwise take goal (->bias)
|
| 52 |
+
if np.random.rand() < goal_sample_rate:
|
| 53 |
+
sample = goal.copy()
|
| 54 |
+
else:
|
| 55 |
+
sample = np.array([np.random.uniform(0, Xmax),
|
| 56 |
+
np.random.uniform(0, Ymax),
|
| 57 |
+
np.random.uniform(0, Zmax)])
|
| 58 |
+
# find nearest node
|
| 59 |
+
dists = [np.linalg.norm(node.point - sample) for node in nodes]
|
| 60 |
+
nearest_idx = int(np.argmin(dists))
|
| 61 |
+
nearest = nodes[nearest_idx]
|
| 62 |
+
|
| 63 |
+
direction = sample - nearest.point
|
| 64 |
+
norm = np.linalg.norm(direction)
|
| 65 |
+
if norm == 0:
|
| 66 |
+
continue
|
| 67 |
+
direction = direction / norm
|
| 68 |
+
|
| 69 |
+
new_point = nearest.point + direction * min(step_size, norm)
|
| 70 |
+
|
| 71 |
+
# ensure within bounds
|
| 72 |
+
if env.is_outside(tuple(new_point)):
|
| 73 |
+
continue
|
| 74 |
+
|
| 75 |
+
# check collision along segment
|
| 76 |
+
if not is_segment_collision_free(env, nearest.point, new_point, step=step_size / 5.0):
|
| 77 |
+
stats["collisions"]+=1
|
| 78 |
+
continue
|
| 79 |
+
|
| 80 |
+
new_node = Node(new_point, nearest)
|
| 81 |
+
nodes.append(new_node)
|
| 82 |
+
|
| 83 |
+
# check if we reached goal
|
| 84 |
+
if np.linalg.norm(new_point - goal) <= goal_tolerance:
|
| 85 |
+
# try connect directly to goal
|
| 86 |
+
if is_segment_collision_free(env, new_point, goal, step=step_size / 5.0):
|
| 87 |
+
goal_node = Node(goal, new_node)
|
| 88 |
+
nodes.append(goal_node)
|
| 89 |
+
# reconstruct path
|
| 90 |
+
path_nodes = []
|
| 91 |
+
cur = goal_node
|
| 92 |
+
while cur is not None:
|
| 93 |
+
path_nodes.append(cur.point)
|
| 94 |
+
cur = cur.parent
|
| 95 |
+
path = np.array(path_nodes[::-1])
|
| 96 |
+
print("reached goal", len(nodes), "nodes", len(path), "waypoints, avoided", stats['collisions'], "collisions", f'{time.time()-tstart:.2f}', "seconds")
|
| 97 |
+
return path
|
| 98 |
+
|
| 99 |
+
raise RuntimeError(f"RRT failed to find a path within the given iterations. {stats=}")
|
planners/rrt_planner.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
| 1 |
-
from dataclasses import dataclass
|
| 2 |
-
import numpy as np
|
| 3 |
-
import time
|
| 4 |
-
|
| 5 |
-
def is_segment_collision_free(env, p1, p2, step=0.1):
|
| 6 |
-
"""Return True if straight segment p1->p2 is free.
|
| 7 |
-
- env: FlightEnvironment instance
|
| 8 |
-
- p1,p2: iterable (x,y,z)
|
| 9 |
-
- step: distance between samples along the segment (meters)
|
| 10 |
-
"""
|
| 11 |
-
dist = np.linalg.norm(p2 - p1)
|
| 12 |
-
if dist == 0:
|
| 13 |
-
return not env.is_collide(tuple(p1)) and not env.is_collide(tuple(p1))
|
| 14 |
-
n = max(1, int(np.ceil(dist / step)))
|
| 15 |
-
for i in range(n + 1):
|
| 16 |
-
t = i / n
|
| 17 |
-
p = p1 * (1 - t) + p2 * t
|
| 18 |
-
if env.is_outside(tuple(p)) or env.is_collide(tuple(p)):
|
| 19 |
-
return False
|
| 20 |
-
return True
|
| 21 |
-
|
| 22 |
-
def rrt_planner(env, start, goal, max_iters=10000, step_size=1.0, goal_tolerance=0.8, goal_sample_rate=0.1):
|
| 23 |
-
"""
|
| 24 |
-
Basic RRT planner in continuous 3D space.
|
| 25 |
-
Returns a path as an (N x 3) numpy array from start to goal (inclusive).
|
| 26 |
-
"""
|
| 27 |
-
start = np.array(start, dtype=float)
|
| 28 |
-
goal = np.array(goal, dtype=float)
|
| 29 |
-
|
| 30 |
-
# quick checks
|
| 31 |
-
if env.is_outside(tuple(start)) or env.is_collide(tuple(start)):
|
| 32 |
-
raise RuntimeError("Start is invalid (outside or in collision).")
|
| 33 |
-
if env.is_outside(tuple(goal)) or env.is_collide(tuple(goal)):
|
| 34 |
-
raise RuntimeError("Goal is invalid (outside or in collision).")
|
| 35 |
-
|
| 36 |
-
# recorded statistics stored here, can be inspected later
|
| 37 |
-
stats = dict(collisions=0)
|
| 38 |
-
tstart = time.time()
|
| 39 |
-
|
| 40 |
-
@dataclass
|
| 41 |
-
class Node:
|
| 42 |
-
point: np.ndarray
|
| 43 |
-
parent: np.ndarray = None
|
| 44 |
-
|
| 45 |
-
# environment bounds
|
| 46 |
-
Xmax, Ymax, Zmax = env.space_size
|
| 47 |
-
|
| 48 |
-
nodes = [Node(start)]
|
| 49 |
-
|
| 50 |
-
for it in range(max_iters):
|
| 51 |
-
# sample random point sometimes, otherwise take goal (->bias)
|
| 52 |
-
if np.random.rand() < goal_sample_rate:
|
| 53 |
-
sample = goal.copy()
|
| 54 |
-
else:
|
| 55 |
-
sample = np.array([np.random.uniform(0, Xmax),
|
| 56 |
-
np.random.uniform(0, Ymax),
|
| 57 |
-
np.random.uniform(0, Zmax)])
|
| 58 |
-
# find nearest node
|
| 59 |
-
dists = [np.linalg.norm(node.point - sample) for node in nodes]
|
| 60 |
-
nearest_idx = int(np.argmin(dists))
|
| 61 |
-
nearest = nodes[nearest_idx]
|
| 62 |
-
|
| 63 |
-
direction = sample - nearest.point
|
| 64 |
-
norm = np.linalg.norm(direction)
|
| 65 |
-
if norm == 0:
|
| 66 |
-
continue
|
| 67 |
-
direction = direction / norm
|
| 68 |
-
|
| 69 |
-
new_point = nearest.point + direction * min(step_size, norm)
|
| 70 |
-
|
| 71 |
-
# ensure within bounds
|
| 72 |
-
if env.is_outside(tuple(new_point)):
|
| 73 |
-
continue
|
| 74 |
-
|
| 75 |
-
# check collision along segment
|
| 76 |
-
if not is_segment_collision_free(env, nearest.point, new_point, step=step_size / 5.0):
|
| 77 |
-
stats["collisions"]+=1
|
| 78 |
-
continue
|
| 79 |
-
|
| 80 |
-
new_node = Node(new_point, nearest)
|
| 81 |
-
nodes.append(new_node)
|
| 82 |
-
|
| 83 |
-
# check if we reached goal
|
| 84 |
-
if np.linalg.norm(new_point - goal) <= goal_tolerance:
|
| 85 |
-
# try connect directly to goal
|
| 86 |
-
if is_segment_collision_free(env, new_point, goal, step=step_size / 5.0):
|
| 87 |
-
goal_node = Node(goal, new_node)
|
| 88 |
-
nodes.append(goal_node)
|
| 89 |
-
# reconstruct path
|
| 90 |
-
path_nodes = []
|
| 91 |
-
cur = goal_node
|
| 92 |
-
while cur is not None:
|
| 93 |
-
path_nodes.append(cur.point)
|
| 94 |
-
cur = cur.parent
|
| 95 |
-
path = np.array(path_nodes[::-1])
|
| 96 |
-
print("reached goal", len(nodes), "nodes", len(path), "waypoints", f'{time.time()-tstart:.2f}', "seconds")
|
| 97 |
-
return path
|
| 98 |
-
|
| 99 |
-
raise RuntimeError(f"RRT failed to find a path within the given iterations. {stats=}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|