-
Notifications
You must be signed in to change notification settings - Fork 249
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
""" | ||
BOOTCAMPERS TO COMPLETE. | ||
Travel to designated waypoint. | ||
""" | ||
|
||
from .. import commands | ||
from .. import drone_report | ||
|
||
# Disable for bootcamp use | ||
# pylint: disable-next=unused-import | ||
from .. import drone_status | ||
from .. import location | ||
from ..private.decision import base_decision | ||
|
||
|
||
# Disable for bootcamp use | ||
# No enable | ||
# pylint: disable=duplicate-code,unused-argument | ||
|
||
|
||
class DecisionSimpleWaypoint(base_decision.BaseDecision): | ||
""" | ||
Travel to the designed waypoint. | ||
""" | ||
|
||
def __init__(self, waypoint: location.Location, acceptance_radius: float) -> None: | ||
""" | ||
Initialize all persistent variables here with self. | ||
""" | ||
self.waypoint = waypoint | ||
print(f"Waypoint: {waypoint}") | ||
|
||
self.acceptance_radius = acceptance_radius | ||
|
||
# ============ | ||
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
|
||
self.waypoint_found =False | ||
self.landing_pad_found = False | ||
|
||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
# ============ | ||
|
||
def run( | ||
self, report: drone_report.DroneReport, landing_pad_locations: "list[location.Location]" | ||
) -> commands.Command: | ||
""" | ||
Make the drone fly to the waypoint. | ||
You are allowed to create as many helper methods as you want, | ||
as long as you do not change the __init__() and run() signatures. | ||
This method will be called in an infinite loop, something like this: | ||
```py | ||
while True: | ||
report, landing_pad_locations = get_input() | ||
command = Decision.run(report, landing_pad_locations) | ||
put_output(command) | ||
``` | ||
""" | ||
# Default command | ||
command = commands.Command.create_null_command() | ||
|
||
# ============ | ||
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
|
||
status = report.status | ||
current_position = report.position | ||
waypoint = self.waypoint | ||
|
||
destination_x = waypoint.location_x - current_position.location_x | ||
destination_y = waypoint.location_y - current_position.location_y | ||
distance_squared = destination_x**2 + destination_y**2 | ||
|
||
if status == drone_status.DroneStatus.HALTED: | ||
if distance_squared < self.acceptance_radius**2: | ||
command = commands.Command.create_land_command() | ||
print("The drone is within the accepted range") | ||
else: command = commands.Command.create_set_relative_destination_command( | ||
destination_x, destination_y | ||
) | ||
print(f"Moving to waypoint{waypoint.location_x},{waypoint.location_y}") | ||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
# ============ | ||
|
||
return command |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
""" | ||
BOOTCAMPERS TO COMPLETE. | ||
Travel to designated waypoint and then land at a nearby landing pad. | ||
""" | ||
|
||
from .. import commands | ||
from .. import drone_report | ||
|
||
# Disable for bootcamp use | ||
# pylint: disable-next=unused-import | ||
from .. import drone_status | ||
from .. import location | ||
from ..private.decision import base_decision | ||
|
||
|
||
# Disable for bootcamp use | ||
# No enable | ||
# pylint: disable=duplicate-code,unused-argument | ||
|
||
|
||
class DecisionWaypointLandingPads(base_decision.BaseDecision): | ||
""" | ||
Travel to the designed waypoint and then land at the nearest landing pad. | ||
""" | ||
|
||
def __init__(self, waypoint: location.Location, acceptance_radius: float) -> None: | ||
""" | ||
Initialize all persistent variables here with self. | ||
""" | ||
self.waypoint = waypoint | ||
print(f"Waypoint: {waypoint}") | ||
|
||
self.acceptance_radius = acceptance_radius | ||
|
||
# ============ | ||
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
|
||
print(str(waypoint.location_x)+str(waypoint.location_y)) | ||
|
||
self.has_sent_landing_command = False | ||
|
||
self.find_nearest_landing_pad = False | ||
|
||
self.reached_waypoint = False | ||
|
||
self.moving_to_landing_pad = False | ||
|
||
self.counter = 0 | ||
|
||
def at_point(self, current_x: float, current_y: float): | ||
|
||
|
||
distance_squared = (self.waypoint.location_x - current_x) ** 2 + ( | ||
self.waypoint.location_y - current_y | ||
) ** 2 | ||
return distance_squared <= self.acceptance_radius ** 2 | ||
|
||
|
||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
# ============ | ||
|
||
def run( | ||
self, report: drone_report.DroneReport, landing_pad_locations: "list[location.Location]" | ||
) -> commands.Command: | ||
""" | ||
Make the drone fly to the waypoint and then land at the nearest landing pad. | ||
You are allowed to create as many helper methods as you want, | ||
as long as you do not change the __init__() and run() signatures. | ||
This method will be called in an infinite loop, something like this: | ||
```py | ||
while True: | ||
report, landing_pad_locations = get_input() | ||
command = Decision.run(report, landing_pad_locations) | ||
put_output(command) | ||
``` | ||
""" | ||
# Default command | ||
command = commands.Command.create_null_command() | ||
|
||
# ============ | ||
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
|
||
# Do something based on the report and the state of this class... | ||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
# ============ | ||
|
||
return command |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
""" | ||
BOOTCAMPERS TO COMPLETE. | ||
Detects landing pads. | ||
""" | ||
|
||
import pathlib | ||
|
||
import numpy as np | ||
import torch | ||
import ultralytics | ||
|
||
from .. import bounding_box | ||
|
||
|
||
# ============ | ||
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
# Bootcampers remove the following lines: | ||
# Allow linters and formatters to pass for bootcamp maintainers | ||
# No enable | ||
# pylint: disable=unused-argument,unused-private-member,unused-variable | ||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
# ============ | ||
|
||
|
||
class DetectLandingPad: | ||
""" | ||
Contains the YOLOv8 model for prediction. | ||
""" | ||
|
||
__create_key = object() | ||
|
||
# ============ | ||
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
|
||
# Chooses the GPU if it exists, otherwise runs on the CPU | ||
# If you have a CUDA capable GPU but want to force it to | ||
# run on the CPU instead, replace the right side with "cpu" | ||
__DEVICE = 0 if torch.cuda.is_available() else "cpu" | ||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
# ============ | ||
|
||
__MODEL_NAME = "best-2n.pt" | ||
|
||
@classmethod | ||
def create(cls, model_directory: pathlib.Path) -> "tuple[bool, DetectLandingPad | None]": | ||
""" | ||
model_directory: Directory to models. | ||
""" | ||
if not model_directory.is_dir(): | ||
return False, None | ||
|
||
model_path = pathlib.PurePosixPath( | ||
model_directory, | ||
cls.__MODEL_NAME, | ||
) | ||
|
||
try: | ||
model = ultralytics.YOLO(str(model_path)) | ||
# Library can throw any exception | ||
# pylint: disable-next=broad-exception-caught | ||
except Exception: | ||
return False, None | ||
|
||
return True, DetectLandingPad(cls.__create_key, model) | ||
|
||
def __init__(self, class_private_create_key: object, model: ultralytics.YOLO) -> None: | ||
""" | ||
Private constructor, use create() method. | ||
""" | ||
assert class_private_create_key is DetectLandingPad.__create_key, "Use create() method" | ||
|
||
self.__model = model | ||
|
||
def run(self, image: np.ndarray) -> "tuple[list[bounding_box.BoundingBox], np.ndarray]": | ||
""" | ||
Converts an image into a list of bounding boxes. | ||
image: The image to run on. | ||
Return: A tuple of (list of bounding boxes, annotated image) . | ||
The list of bounding boxes can be empty. | ||
""" | ||
# ============ | ||
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
|
||
# Ultralytics has documentation and examples | ||
|
||
# Use the model's predict() method to run inference | ||
# Parameters of interest: | ||
# * source | ||
# * conf | ||
# * device | ||
# * verbose | ||
# conf threshold is around 0.7 | ||
predictions = self.__model.predict( | ||
source=image, conf=0.7, device=self.__DEVICE, verbose =False | ||
) | ||
|
||
# Get the Result object | ||
prediction = predictions[0] | ||
|
||
# Plot the annotated image from the Result object | ||
# Include the confidence value | ||
image_annotated = prediction.plot(boxes=True, conf=True) | ||
|
||
# Get the xyxy boxes list from the Boxes object in the Result object | ||
boxes_xyxy = prediction.boxes.xyxy | ||
|
||
# Detach the xyxy boxes to make a copy, | ||
# move the copy into CPU space, | ||
# and convert to a numpy array | ||
boxes_cpu = boxes_xyxy.detach().cpu().numpy() | ||
|
||
# Loop over the boxes list and create a list of bounding boxes | ||
bounding_boxes = [] | ||
# Hint: .shape gets the dimensions of the numpy array | ||
# for i in range(0, ...): | ||
# # Create BoundingBox object and append to list | ||
# result, box = ... | ||
for boxes_xyxy in boxes_cpu: | ||
success, box = bounding_box.BoundingBox.create(boxes_xyxy) | ||
if not success: | ||
continue | ||
|
||
return [], image_annotated | ||
|
||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
# ============ |