-
Notifications
You must be signed in to change notification settings - Fork 200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Himank Bootcamp PR #11
base: main
Are you sure you want to change the base?
Changes from all commits
8ec11f3
a7e0a33
3c67cb2
5e64fc5
318294d
43f0dc4
52da233
d8dc641
b521824
6851633
a7d9960
f42441a
20f6395
b6dbf23
adcd012
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,6 @@ venv/ | |
|
||
# Logging | ||
*log* | ||
|
||
# pyenv | ||
.python-version |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,12 +37,37 @@ def __init__(self, waypoint: location.Location, acceptance_radius: float): | |
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
|
||
# Add your own | ||
self.action_dict = dict(zip(("MOVE", "HALT", "LAND"), range(3,6))) | ||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
# ============ | ||
|
||
def distance_between_waypoint(self, given_location:location.Location) -> "tuple[float, float]": | ||
""" | ||
Returns the distance between the given location and waypoint. | ||
""" | ||
given_loc_from_waypoint_location_x = self.waypoint.location_x - given_location.location_x | ||
given_loc_from_waypoint_location_y = self.waypoint.location_y - given_location.location_y | ||
return (given_loc_from_waypoint_location_x, given_loc_from_waypoint_location_y) | ||
|
||
def check_if_near_waypoint(self, given_location:location.Location) -> bool: | ||
""" | ||
Checks if the given location is on the waypoint by an acceptance radius. | ||
""" | ||
absolute_acceptance_radius = abs(self.acceptance_radius) | ||
difference_location_x, difference_location_y = self.distance_between_waypoint(given_location) | ||
if abs(difference_location_x) < absolute_acceptance_radius and abs(difference_location_y) < absolute_acceptance_radius: | ||
return True | ||
return False | ||
|
||
def next_relative_coordinates_to_waypoint(self, given_location:location.Location) -> "tuple[float, float]": | ||
""" | ||
Returns the relative x and y coordinates for drone to be sent to. | ||
""" | ||
relative_x, relative_y = self.distance_between_waypoint(given_location) | ||
return (relative_x, relative_y) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove unneeded parantheses. |
||
|
||
def run(self, | ||
report: drone_report.DroneReport, | ||
landing_pad_locations: "list[location.Location]") -> commands.Command: | ||
|
@@ -68,10 +93,28 @@ def run(self, | |
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
|
||
# Do something based on the report and the state of this class... | ||
action = None | ||
report_status = report.status | ||
report_position = report.position | ||
|
||
if report_status == drone_status.DroneStatus.LANDED: | ||
action = None | ||
elif report_status == drone_status.DroneStatus.HALTED: | ||
if self.check_if_near_waypoint(report_position): | ||
action = self.action_dict["LAND"] | ||
else: | ||
action = self.action_dict["MOVE"] | ||
|
||
if action is None: | ||
pass | ||
Comment on lines
+108
to
+109
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant, remove. |
||
elif action == self.action_dict["MOVE"]: | ||
relative_x, relative_y = self.next_relative_coordinates_to_waypoint(report_position) | ||
command = commands.Command.create_set_relative_destination_command(relative_x, relative_y) | ||
elif action == self.action_dict["HALT"]: | ||
command = commands.Command.create_halt_command() | ||
elif action == self.action_dict["LAND"]: | ||
command = commands.Command.create_land_command() | ||
|
||
# Remove this when done | ||
raise NotImplementedError | ||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,12 +37,72 @@ def __init__(self, waypoint: location.Location, acceptance_radius: float): | |
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
|
||
# Add your own | ||
self.action_dict = dict(zip(("MOVE", "HALT", "LAND"), range(3,6))) | ||
self.origin = location.Location(0,0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if the drone didn't start at (0,0) , but could start anywhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then, in that case, wouldn't we need to keep track of the initial position of the drone? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Alternatively, don't use the initial position of the drone. |
||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
# ============ | ||
|
||
@staticmethod | ||
def shortest_distance(target_location:location.Location, given_location:location.Location) -> float: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good use of static method! |
||
""" | ||
Finds out the shortest distance between two given locations. | ||
""" | ||
x_1, y_1 = given_location.location_x, given_location.location_y | ||
x_2, y_2 = target_location.location_x, target_location.location_y | ||
x_square = (x_2 - x_1) ** 2 | ||
y_square = (y_2 - y_1) ** 2 | ||
return (x_square + y_square) ** 0.5 | ||
|
||
def relative_coordinates_of_target(self, target_location:location.Location, given_location:location.Location) -> "tuple[float, float]": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can also be static. |
||
""" | ||
Returns the relative coordinates of target w.r.t given location. | ||
""" | ||
relative_location_x = target_location.location_x - given_location.location_x | ||
relative_location_y = target_location.location_y - given_location.location_y | ||
return (relative_location_x, relative_location_y) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove unneeded parantheses. |
||
|
||
def check_if_near_target(self, target_location:location.Location, given_location:location.Location) -> bool: | ||
""" | ||
Checks if the given location is near the target by an acceptance radius. | ||
""" | ||
absolute_acceptance_radius = abs(self.acceptance_radius) | ||
difference_location_x, difference_location_y = self.relative_coordinates_of_target(target_location, given_location) | ||
if abs(difference_location_x) < absolute_acceptance_radius and abs(difference_location_y) < absolute_acceptance_radius: | ||
return True | ||
return False | ||
|
||
def next_relative_coordinates_to_target(self, | ||
target_location:location.Location, | ||
given_location:location.Location) -> "tuple[float, float]": | ||
""" | ||
Returns the relative x and y coordinates for drone to be sent to. | ||
""" | ||
relative_x, relative_y = self.relative_coordinates_of_target(target_location, given_location) | ||
divider = 1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this since it doesn't do anything. |
||
if abs(relative_x) > abs(relative_y): | ||
return (relative_x/divider, relative_y) | ||
elif abs(relative_x) < abs(relative_y): | ||
return (relative_x, relative_y/divider) | ||
else: | ||
return (relative_x/divider, relative_y/divider) | ||
|
||
def closest_landing_pad(self, | ||
given_location:location.Location, | ||
landing_pad_locations: "list[location.Location]") -> location.Location: | ||
""" | ||
Finds out the closest landing pad from the given location by checking out their distances. | ||
""" | ||
closest_location = landing_pad_locations[0] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if there were no landing pads? (You do not need to change anything, just something to think about). |
||
for landing_pad in landing_pad_locations[1:]: | ||
distance_from_given_location = DecisionWaypointLandingPads.shortest_distance(landing_pad, given_location) | ||
distance_from_closest_location = DecisionWaypointLandingPads.shortest_distance(closest_location, given_location) | ||
Comment on lines
+99
to
+100
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calculating the distance repeatedly for the closest landing pad is wasteful, is there a way to avoid this? |
||
if distance_from_given_location < distance_from_closest_location: | ||
closest_location = landing_pad | ||
return closest_location | ||
|
||
|
||
def run(self, | ||
report: drone_report.DroneReport, | ||
landing_pad_locations: "list[location.Location]") -> commands.Command: | ||
|
@@ -68,10 +128,39 @@ def run(self, | |
# ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ | ||
# ============ | ||
|
||
# Do something based on the report and the state of this class... | ||
action = None | ||
report_status = report.status | ||
report_position = report.position | ||
target = self.waypoint | ||
|
||
if report_status == drone_status.DroneStatus.HALTED: | ||
landing_pad = self.closest_landing_pad(self.waypoint, landing_pad_locations) | ||
action = self.action_dict["MOVE"] | ||
if self.check_if_near_target(self.waypoint, report_position): | ||
target = landing_pad | ||
if (self.check_if_near_target(landing_pad, report_position) | ||
and (DecisionWaypointLandingPads.shortest_distance(self.waypoint, report_position) - DecisionWaypointLandingPads.shortest_distance(self.waypoint, landing_pad) < 0.1) | ||
and ( | ||
(self.check_if_near_target(landing_pad, self.origin) and self.check_if_near_target(self.waypoint, self.origin)) | ||
or (not self.check_if_near_target(report_position, self.origin)) | ||
)): | ||
""" | ||
self.check_if_near_target(landing_pad, self.origin) - if nearest landing pad is origin | ||
self.check_if_near_target(self.waypoint, self.origin) - if waypoint is origin | ||
self.check_if_near_target(report_position, self.origin) - if current position is origin | ||
""" | ||
action = self.action_dict["LAND"] | ||
|
||
if action is None: | ||
pass | ||
elif action == self.action_dict["MOVE"]: | ||
relative_x, relative_y = self.next_relative_coordinates_to_target(target, report_position) | ||
command = commands.Command.create_set_relative_destination_command(relative_x, relative_y) | ||
elif action == self.action_dict["HALT"]: | ||
command = commands.Command.create_halt_command() | ||
elif action == self.action_dict["LAND"]: | ||
command = commands.Command.create_land_command() | ||
|
||
# Remove this when done | ||
raise NotImplementedError | ||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -86,32 +86,36 @@ def run(self, image: np.ndarray) -> "tuple[list[bounding_box.BoundingBox], np.nd | |
# * conf | ||
# * device | ||
# * verbose | ||
predictions = ... | ||
predictions = self.__model.predict(source=image, | ||
conf=0.7, | ||
device=self.__DEVICE, | ||
verbose=False) | ||
|
||
# Get the Result object | ||
prediction = ... | ||
prediction = predictions[0] | ||
|
||
# Plot the annotated image from the Result object | ||
# Include the confidence value | ||
image_annotated = ... | ||
image_annotated = prediction.plot(pil=True, conf=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The expected image is a numpy ndarray, not a PIL object. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to the Ultralytics documentation, setting When I tried running pytest with The error message was the following: I do not know whether this is a bug or something else. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The If you leave |
||
|
||
# Get the xyxy boxes list from the Boxes object in the Result object | ||
boxes_xyxy = ... | ||
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_cpu = prediction.boxes.cpu().xyxy.numpy() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean by "detach"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pytorch tensors have a |
||
|
||
# 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, ...): | ||
for i in range(0, boxes_cpu.shape[0]): | ||
# Create BoundingBox object and append to list | ||
# result, box = ... | ||
result, box = bounding_box.BoundingBox.create(boxes_cpu[i]) | ||
if result: | ||
bounding_boxes.append(box) | ||
Comment on lines
+115
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In statistics, it is preferred to remove the whole data point if there is an issue (here, the data point is an image). (You do not need to change anything here, just something to think about). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does that mean removing the entire list of bounding boxes if even one bounding box has an issue? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, an empty list would be returned instead (the entire image is skipped). But you don't need to change anything. |
||
|
||
# Remove this when done | ||
raise NotImplementedError | ||
return (bounding_boxes, image_annotated) | ||
|
||
# ============ | ||
# ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can use
enum
instead.