Spaces:
Configuration error
Configuration error
Shuo Li commited on
Commit ·
f12661e
0
Parent(s):
first commit
Browse files- flight_environment.py +177 -0
- main.py +54 -0
- path_planner.py +24 -0
- trajectory_generator.py +17 -0
flight_environment.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import os
|
| 3 |
+
import numpy as np
|
| 4 |
+
import matplotlib.pyplot as plt
|
| 5 |
+
from math import sin,cos,tan
|
| 6 |
+
from mpl_toolkits.mplot3d import Axes3D
|
| 7 |
+
|
| 8 |
+
class FlightEnvironment:
|
| 9 |
+
def __init__(self,obs_num):
|
| 10 |
+
self.env_width = 20.0
|
| 11 |
+
self.env_length = 20.0
|
| 12 |
+
self.env_height = 5
|
| 13 |
+
self.space_size = (self.env_width,self.env_length,self.env_height)
|
| 14 |
+
self._obs_num = obs_num
|
| 15 |
+
|
| 16 |
+
self.cylinders = self.generate_random_cylinders(self.space_size,self._obs_num,0.1,0.3,5,5)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def generate_random_cylinders(self,space_size, N,
|
| 22 |
+
min_radius, max_radius,
|
| 23 |
+
min_height, max_height,
|
| 24 |
+
max_tries=100000):
|
| 25 |
+
|
| 26 |
+
X, Y, Z = space_size
|
| 27 |
+
cylinders = []
|
| 28 |
+
tries = 0
|
| 29 |
+
|
| 30 |
+
while len(cylinders) < N and tries < max_tries:
|
| 31 |
+
tries += 1
|
| 32 |
+
|
| 33 |
+
r = np.random.uniform(min_radius, max_radius)
|
| 34 |
+
h = np.random.uniform(min_height, min(max_height, Z))
|
| 35 |
+
|
| 36 |
+
x = np.random.uniform(r, X - r)
|
| 37 |
+
y = np.random.uniform(r, Y - r)
|
| 38 |
+
|
| 39 |
+
candidate = np.array([x, y, h, r])
|
| 40 |
+
|
| 41 |
+
no_overlapping = True
|
| 42 |
+
for c in cylinders:
|
| 43 |
+
dx = x - c[0]
|
| 44 |
+
dy = y - c[1]
|
| 45 |
+
dist = np.hypot(dx, dy)
|
| 46 |
+
if dist < (r + c[3]):
|
| 47 |
+
no_overlapping = False
|
| 48 |
+
break
|
| 49 |
+
|
| 50 |
+
if no_overlapping:
|
| 51 |
+
cylinders.append(candidate)
|
| 52 |
+
|
| 53 |
+
if len(cylinders) < N:
|
| 54 |
+
raise RuntimeError("Unable to generate a sufficient number of non-overlapping cylinders with the given parameters. Please reduce N or decrease the radius range.")
|
| 55 |
+
|
| 56 |
+
return np.vstack(cylinders)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def is_outside(self,point):
|
| 60 |
+
"""
|
| 61 |
+
Check whether a 3D point lies outside the environment boundary.
|
| 62 |
+
|
| 63 |
+
Parameters:
|
| 64 |
+
point : tuple or list (x, y, z)
|
| 65 |
+
The coordinates of the point to be checked.
|
| 66 |
+
|
| 67 |
+
Returns:
|
| 68 |
+
bool
|
| 69 |
+
True -> the point is outside the environment limits
|
| 70 |
+
False -> the point is within the valid environment region
|
| 71 |
+
"""
|
| 72 |
+
|
| 73 |
+
x,y,z = point
|
| 74 |
+
if (0 <= x <= self.env_width and
|
| 75 |
+
0 <= y <= self.env_length and
|
| 76 |
+
0 <= z <= self.env_height):
|
| 77 |
+
outside_env = False
|
| 78 |
+
else:
|
| 79 |
+
outside_env = True
|
| 80 |
+
return outside_env
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def is_collide(self, point, epsilon=0.2):
|
| 85 |
+
"""
|
| 86 |
+
Check whether a point in 3D space collides with a given set of cylinders (including a safety margin).
|
| 87 |
+
|
| 88 |
+
Parameters:
|
| 89 |
+
point: A numpy array or tuple of (x, y, z)
|
| 90 |
+
cylinders: An N×4 numpy array, each row is [cx, cy, h, r]
|
| 91 |
+
where cx, cy are the cylinder center coordinates in XY,
|
| 92 |
+
h is the height, and r is the radius
|
| 93 |
+
epsilon: Safety margin; if the point is closer than (r + epsilon),
|
| 94 |
+
it is also considered a collision
|
| 95 |
+
|
| 96 |
+
Returns:
|
| 97 |
+
True -> Collision (or too close)
|
| 98 |
+
False -> Safe
|
| 99 |
+
"""
|
| 100 |
+
cylinders = self.cylinders
|
| 101 |
+
px, py, pz = point
|
| 102 |
+
|
| 103 |
+
for cx, cy, h, r in cylinders:
|
| 104 |
+
if not (0 <= pz <= h):
|
| 105 |
+
continue
|
| 106 |
+
dist_xy = np.sqrt((px - cx)**2 + (py - cy)**2)
|
| 107 |
+
if dist_xy <= (r + epsilon):
|
| 108 |
+
return True
|
| 109 |
+
|
| 110 |
+
return False
|
| 111 |
+
|
| 112 |
+
def plot_cylinders(self,path = None):
|
| 113 |
+
"""
|
| 114 |
+
cylinders: N×4 array, [cx, cy, h, r]
|
| 115 |
+
"""
|
| 116 |
+
|
| 117 |
+
fig = plt.figure()
|
| 118 |
+
ax = fig.add_subplot(111, projection='3d')
|
| 119 |
+
|
| 120 |
+
cylinders = self.cylinders
|
| 121 |
+
space_size = self.space_size
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
Xmax, Ymax, Zmax = space_size
|
| 126 |
+
for cx, cy, h, r in cylinders:
|
| 127 |
+
z = np.linspace(0, h, 30)
|
| 128 |
+
theta = np.linspace(0, 2 * np.pi, 30)
|
| 129 |
+
theta, z = np.meshgrid(theta, z)
|
| 130 |
+
|
| 131 |
+
x = cx + r * np.cos(theta)
|
| 132 |
+
y = cy + r * np.sin(theta)
|
| 133 |
+
|
| 134 |
+
ax.plot_surface(x, y, z, color='skyblue', alpha=0.8)
|
| 135 |
+
theta2 = np.linspace(0, 2*np.pi, 30)
|
| 136 |
+
x_top = cx + r * np.cos(theta2)
|
| 137 |
+
y_top = cy + r * np.sin(theta2)
|
| 138 |
+
z_top = np.ones_like(theta2) * h
|
| 139 |
+
ax.plot_trisurf(x_top, y_top, z_top, color='steelblue', alpha=0.8)
|
| 140 |
+
|
| 141 |
+
ax.set_xlim(0, self.env_width)
|
| 142 |
+
ax.set_ylim(0, self.env_length)
|
| 143 |
+
ax.set_zlim(0, self.env_height)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
if path is not None:
|
| 147 |
+
path = np.array(path)
|
| 148 |
+
xs, ys, zs = path[:, 0], path[:, 1], path[:, 2]
|
| 149 |
+
ax.plot(xs, ys, zs, linewidth=2)
|
| 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):
|
| 157 |
+
"""Make axes of 3D plot have equal scale.
|
| 158 |
+
Compatible with Matplotlib ≥ 1.0.0
|
| 159 |
+
"""
|
| 160 |
+
x_limits = ax.get_xlim3d()
|
| 161 |
+
y_limits = ax.get_ylim3d()
|
| 162 |
+
z_limits = ax.get_zlim3d()
|
| 163 |
+
|
| 164 |
+
x_range = abs(x_limits[1] - x_limits[0])
|
| 165 |
+
y_range = abs(y_limits[1] - y_limits[0])
|
| 166 |
+
z_range = abs(z_limits[1] - z_limits[0])
|
| 167 |
+
|
| 168 |
+
max_range = max([x_range, y_range, z_range]) / 2.0
|
| 169 |
+
|
| 170 |
+
mid_x = (x_limits[0] + x_limits[1]) * 0.5
|
| 171 |
+
mid_y = (y_limits[0] + y_limits[1]) * 0.5
|
| 172 |
+
mid_z = (z_limits[0] + z_limits[1]) * 0.5
|
| 173 |
+
|
| 174 |
+
ax.set_xlim3d([mid_x - max_range, mid_x + max_range])
|
| 175 |
+
ax.set_ylim3d([mid_y - max_range, mid_y + max_range])
|
| 176 |
+
ax.set_zlim3d([mid_z - max_range, mid_z + max_range])
|
| 177 |
+
|
main.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flight_environment import FlightEnvironment
|
| 2 |
+
from path_planner import AStarPlanner
|
| 3 |
+
|
| 4 |
+
env = FlightEnvironment(50)
|
| 5 |
+
start = (1,2,0)
|
| 6 |
+
goal = (18,18,3)
|
| 7 |
+
|
| 8 |
+
# --------------------------------------------------------------------------------------------------- #
|
| 9 |
+
# Call your path planning algorithm here.
|
| 10 |
+
# The planner should return a collision-free path and store it in the variable `path`.
|
| 11 |
+
# `path` must be an N×3 numpy array, where:
|
| 12 |
+
# - column 1 contains the x-coordinates of all path points
|
| 13 |
+
# - column 2 contains the y-coordinates of all path points
|
| 14 |
+
# - column 3 contains the z-coordinates of all path points
|
| 15 |
+
# This `path` array will be provided to the `env` object for visualization.
|
| 16 |
+
|
| 17 |
+
path = [[0,0,0],[1,1,1],[2,2,2],[3,3,3]]
|
| 18 |
+
|
| 19 |
+
# --------------------------------------------------------------------------------------------------- #
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
env.plot_cylinders(path)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# --------------------------------------------------------------------------------------------------- #
|
| 26 |
+
# Call your trajectory planning algorithm here. The algorithm should
|
| 27 |
+
# generate a smooth trajectory that passes through all the previously
|
| 28 |
+
# planned path points.
|
| 29 |
+
#
|
| 30 |
+
# After generating the trajectory, plot it in a new figure.
|
| 31 |
+
# The figure should contain three subplots showing the time histories of
|
| 32 |
+
# x, y, and z respectively, where the horizontal axis represents time (in seconds).
|
| 33 |
+
#
|
| 34 |
+
# Additionally, you must also plot the previously planned discrete path
|
| 35 |
+
# points on the same figure to clearly show how the continuous trajectory
|
| 36 |
+
# follows these path points.
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
# --------------------------------------------------------------------------------------------------- #
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
# You must manage this entire project using Git.
|
| 46 |
+
# When submitting your assignment, upload the project to a code-hosting platform
|
| 47 |
+
# such as GitHub or GitLab. The repository must be accessible and directly cloneable.
|
| 48 |
+
#
|
| 49 |
+
# After cloning, running `python3 main.py` in the project root directory
|
| 50 |
+
# should successfully execute your program and display:
|
| 51 |
+
# 1) the 3D path visualization, and
|
| 52 |
+
# 2) the trajectory plot.
|
| 53 |
+
#
|
| 54 |
+
# You must also include the link to your GitHub/GitLab repository in your written report.
|
path_planner.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
In this file, you should implement your own path planning class or function.
|
| 3 |
+
Within your implementation, you may call `env.is_collide()` and `env.is_outside()`
|
| 4 |
+
to verify whether candidate path points collide with obstacles or exceed the
|
| 5 |
+
environment boundaries.
|
| 6 |
+
|
| 7 |
+
You are required to write the path planning algorithm by yourself. Copying or calling
|
| 8 |
+
any existing path planning algorithms from others is strictly
|
| 9 |
+
prohibited. Please avoid using external packages beyond common Python libraries
|
| 10 |
+
such as `numpy`, `math`, or `scipy`. If you must use additional packages, you
|
| 11 |
+
must clearly explain the reason in your report.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
|
trajectory_generator.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
In this file, you should implement your trajectory generation class or function.
|
| 3 |
+
Your method must generate a smooth 3-axis trajectory (x(t), y(t), z(t)) that
|
| 4 |
+
passes through all the previously computed path points. A positional deviation
|
| 5 |
+
up to 0.1 m from each path point is allowed.
|
| 6 |
+
|
| 7 |
+
You should output the generated trajectory and visualize it. The figure must
|
| 8 |
+
contain three subplots showing x, y, and z, respectively, with time t (in seconds)
|
| 9 |
+
as the horizontal axis. Additionally, you must plot the original discrete path
|
| 10 |
+
points on the same figure for comparison.
|
| 11 |
+
|
| 12 |
+
You are expected to write the implementation yourself. Do NOT copy or reuse any
|
| 13 |
+
existing trajectory generation code from others. Avoid using external packages
|
| 14 |
+
beyond general scientific libraries such as numpy, math, or scipy. If you decide
|
| 15 |
+
to use additional packages, you must clearly explain the reason in your report.
|
| 16 |
+
"""
|
| 17 |
+
|