diff --git a/modules/bootcamp/decision_simple_waypoint.py b/modules/bootcamp/decision_simple_waypoint.py index 26098c2e..741f6421 100644 --- a/modules/bootcamp/decision_simple_waypoint.py +++ b/modules/bootcamp/decision_simple_waypoint.py @@ -36,8 +36,8 @@ def __init__(self, waypoint: location.Location, acceptance_radius: float) -> Non # ============ # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ # ============ - # Add your own + self.has_landed = False # ============ # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ @@ -67,11 +67,26 @@ def run( # ============ # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ # ============ + # Calculate distance to waypoint + delta_x = self.waypoint.location_x - report.position.location_x + delta_y = self.waypoint.location_y - report.position.location_y + dist = (delta_x**2 + delta_y**2) ** 0.5 + + if self.has_landed: + return command - # Do something based on the report and the state of this class... + if report.status.name == "LANDED": + self.has_landed = True + return command + + if dist > self.acceptance_radius: + if report.status.name == "HALTED": + command = commands.Command.create_set_relative_destination_command(delta_x, delta_y) + else: + if report.status.name == "HALTED": + command = commands.Command.create_land_command() # ============ # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ # ============ - return command diff --git a/modules/bootcamp/decision_waypoint_landing_pads.py b/modules/bootcamp/decision_waypoint_landing_pads.py index ade6f118..5da00e29 100644 --- a/modules/bootcamp/decision_waypoint_landing_pads.py +++ b/modules/bootcamp/decision_waypoint_landing_pads.py @@ -36,8 +36,9 @@ def __init__(self, waypoint: location.Location, acceptance_radius: float) -> Non # ============ # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ # ============ - - # Add your own + self.has_landed = False + self.reached_waypoint = False + self.closest_landing_pad = None # ============ # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ @@ -67,11 +68,47 @@ def run( # ============ # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ # ============ - - # Do something based on the report and the state of this class... + if self.has_landed: + return command + + if report.status.name == "LANDED": + self.has_landed = True + return command + + if not self.reached_waypoint: + # Calculate distance to waypoint + delta_x = self.waypoint.location_x - report.position.location_x + delta_y = self.waypoint.location_y - report.position.location_y + dist = (delta_x**2 + delta_y**2) ** 0.5 + + if dist > self.acceptance_radius: + if report.status.name == "HALTED": + command = commands.Command.create_set_relative_destination_command(delta_x, delta_y) + else: + self.reached_waypoint = True + # Find closest landing pad + min_dist = float("inf") + for pad in landing_pad_locations: + pad_delta_x = pad.location_x - report.position.location_x + pad_delta_y = pad.location_y - report.position.location_y + pad_dist = (pad_delta_x**2 + pad_delta_y**2) ** 0.5 + if pad_dist < min_dist: + min_dist = pad_dist + self.closest_landing_pad = pad + else: + # Move to closest landing pad + delta_x = self.closest_landing_pad.location_x - report.position.location_x + delta_y = self.closest_landing_pad.location_y - report.position.location_y + dist = (delta_x**2 + delta_y**2) ** 0.5 + + if dist > self.acceptance_radius: + if report.status.name == "HALTED": + command = commands.Command.create_set_relative_destination_command(delta_x, delta_y) + else: + if report.status.name == "HALTED": + command = commands.Command.create_land_command() # ============ # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ # ============ - return command diff --git a/modules/bootcamp/detect_landing_pad.py b/modules/bootcamp/detect_landing_pad.py index f17aa677..0f36f976 100644 --- a/modules/bootcamp/detect_landing_pad.py +++ b/modules/bootcamp/detect_landing_pad.py @@ -86,43 +86,40 @@ def run(self, image: np.ndarray) -> "tuple[list[bounding_box.BoundingBox], np.nd 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 - predictions = ... + # Run inference using the model's predict method + # Set confidence threshold to 0.7, use the correct device, and suppress verbose output + predictions = self.__model.predict( + source=image, + conf=0.7, + device=self.__DEVICE, + verbose=False, + ) - # Get the Result object - prediction = ... + # Get the first (and only) Result object + prediction = predictions[0] - # Plot the annotated image from the Result object - # Include the confidence value - image_annotated = ... + # Plot the annotated image from the Result object (includes confidence values) + image_annotated = prediction.plot() # Get the xyxy boxes list from the Boxes object in the Result object - 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 = prediction.boxes + if boxes is not None and boxes.xyxy is not None: + boxes_xyxy = boxes.xyxy + else: + boxes_xyxy = [] + + # Detach the xyxy boxes to make a copy, move to CPU, and convert to numpy array + if hasattr(boxes_xyxy, "cpu"): + boxes_cpu = boxes_xyxy.cpu().numpy() + else: + boxes_cpu = np.array(boxes_xyxy) # 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 = ... - - return [], image_annotated - # ============ - # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ - # ============ + for i in range(boxes_cpu.shape[0]): + bounds = boxes_cpu[i] + result, box = bounding_box.BoundingBox.create(bounds) + if result and box is not None: + bounding_boxes.append(box) + + return bounding_boxes, image_annotated