1
1
from .. import decision_command
2
- from .. import object_in_world
3
2
from .. import odometry_and_time
4
- from math import tan , pi , cos , sin
5
-
6
- """
7
-
8
- """
3
+ from math import tan , pi , cos , sin , ceil
9
4
10
5
class SearchPattern :
6
+ """
7
+ Implements a search pattern based on camera field of view, search height, and overlap.
8
+
9
+ Attributes:
10
+ camera_fov (float):
11
+ Camera's field of view, measured in degrees. Use the smallest measurement available.
12
+ Is essentially assumed to be a circle
13
+ search_height (float):
14
+ The altitude at which the search is conducted. This is used to calculate the pattern.
15
+ It does not make the drone go to this height
16
+ search_overlap (float):
17
+ Overlap between passes, between 0 and 1. Recomended probably 0.5-0.7.
18
+ current_position (OdometryAndTime):
19
+ The drone's current position.
20
+ acceptable_variance_squared (float):
21
+ The square of the acceptable distance from the target position.
22
+ """
23
+
11
24
def __init__ (self ,
12
- camera_fov : float ,#fov should be the smallest measurement (e.g. vertical for landscape) essentially assumes its a cricle with that angle
25
+ camera_fov : float ,
13
26
search_height : float ,
14
27
search_overlap : float ,
15
- current_position : odometry_and_time .OdometryAndTime ):#search overlap is float between 0 and 1. Represents how much overlap between passes (1 means fully overlaps (i.e. do not use). Likely value 0.2-0.5)
28
+ current_position : odometry_and_time .OdometryAndTime ,
29
+ acceptable_variance_squared : float ):
30
+
31
+ # Initialize the drone to the first position in the search pattern
16
32
self .current_ring = 0
17
33
self .current_pos_in_ring = 0
18
34
self .max_pos_in_ring = 0
19
35
self .search_radius = 0
20
-
21
- self .acceptable_variance_squared = 1 #CHANGE THIS VALUE
36
+ self .acceptable_variance_squared = acceptable_variance_squared
22
37
23
- self .search_start_pos = current_position
24
- self .target_posx = self .search_start_pos .odometry_data .position .north
25
- self .target_posy = self .search_start_pos .odometry_data .position .east
38
+ # Store the origin of the search and
39
+ self .search_origin = current_position .odometry_data .position
40
+ self .target_posx = self .search_origin .north
41
+ self .target_posy = self .search_origin .east
26
42
27
- self .search_width = 2 * search_height * tan (camera_fov / 2 )
43
+ # Calculate the gap between positions
44
+ self .search_width = 2 * search_height * tan ((camera_fov * 180 / pi ) / 2 )
28
45
self .search_gap = self .search_width * (1 - search_overlap )
46
+
47
+ def set_target_location (self ):
48
+ """
49
+ Updates the target position to the next position in the search pattern.
50
+ """
29
51
30
-
31
- def set_next_location (self ):
52
+ # If it is done the current ring, move to the next ring. If not, next step in current ring
32
53
if self .current_pos_in_ring >= self .max_pos_in_ring :
33
54
self .current_ring += 1
34
55
self .current_pos_in_ring = 0
35
56
self .search_radius = self .search_gap * self .current_ring
36
- self .max_pos_in_ring = self .search_radius * 2 * pi / self .search_gap
57
+ self .max_pos_in_ring = (ceil (self .search_radius * 2 * pi / self .search_gap ))
58
+ self .angle_between_positions = ((2 * pi ) / (self .max_pos_in_ring + 1 ))
37
59
else :
38
60
self .current_pos_in_ring += 1
39
61
40
-
41
- def find_current_location (self ) -> object_in_world .ObjectInWorld :
42
- # Based on curent ring and current pos in ring, set target_posx and target_posy
43
- self .relative_target_posx = self .search_radius * cos (2 * pi * self .current_pos_in_ring / self .max_pos_in_ring )
44
- self .relative_target_posy = self .search_radius * sin (2 * pi * self .current_pos_in_ring / self .max_pos_in_ring )
45
- self .target_posx = self .search_start_pos .odometry_data .position .north + self .relative_target_posx
46
- self .target_posy = self .search_start_pos .odometry_data .position .east + self .relative_target_posy
47
- return
62
+ # Angle measured counter-clockwise from x-axis for the target location
63
+ self .angle = self .angle_between_positions * self .current_pos_in_ring
64
+
65
+ # Calculate x and y coordinates of new target location
66
+ self .relative_target_posx = self .search_radius * cos (self .angle )
67
+ self .relative_target_posy = self .search_radius * sin (self .angle )
68
+
69
+ self .target_posx = self .search_origin .north + self .relative_target_posx
70
+ self .target_posy = self .search_origin .east + self .relative_target_posy
48
71
49
72
def distance_to_target_squared (self ,
50
- current_position : odometry_and_time .OdometryAndTime ) -> float :
51
- return (self .target_posx - current_position .odometry_data .position .east ) ** 2 + (self .target_posy - current_position .odometry_data .position .north ) ** 2
73
+ current_position : odometry_and_time .OdometryAndTime
74
+ ) -> float :
75
+ """
76
+ Returns the square of the distance to it's target location
77
+ """
78
+ return ((self .target_posx - current_position .odometry_data .position .east ) ** 2
79
+ + (self .target_posy - current_position .odometry_data .position .north ) ** 2 )
52
80
53
- def continue_search (self , current_position : odometry_and_time .OdometryAndTime ) -> decision_command .DecisionCommand :
54
- #if drone is at target position, set next location, otherwise, move to target location
81
+ def continue_search (self ,
82
+ current_position : odometry_and_time .OdometryAndTime
83
+ )-> decision_command .DecisionCommand :
84
+
85
+ """
86
+ Call this function to have the drone go to the next location in the search pattern
87
+ """
88
+ # If it is at it's current target location, update to the next target
55
89
if self .distance_to_target_squared (current_position ) < self .acceptable_variance_squared :
56
- self .set_next_location ()
57
- self .find_current_location ()
90
+ self .set_target_location ()
58
91
59
- return decision_command .DecisionCommand .create_move_to_absolute_position_command (self .target_posx , self .target_posy , current_position .odometry_data .position .down )
60
-
92
+ # Send command to go to target
93
+ return decision_command .DecisionCommand .create_move_to_absolute_position_command (
94
+ self .target_posx ,
95
+ self .target_posy ,
96
+ current_position .odometry_data .position .down )
97
+
0 commit comments