14
14
import local_pathfinding .obstacles as ob
15
15
from local_pathfinding .local_path import LocalPath
16
16
17
- WAYPOINT_REACHED_THRESH_KM = 0.5
17
+ LOCAL_WAYPOINT_REACHED_THRESH_KM = 0.5
18
+ GLOBAL_WAYPOINT_REACHED_THRESH_KM = 3
19
+ PATHFINDING_RANGE_KM = 30
20
+ REALLY_FAR_M = 100000000
18
21
GEODESIC = Geod (ellps = "WGS84" )
19
22
20
23
@@ -119,7 +122,9 @@ def __init__(self):
119
122
120
123
# attributes
121
124
self .local_path = LocalPath (parent_logger = self .get_logger ())
122
- self .current_waypoint_index = 0
125
+ self .local_waypoint_index = 0
126
+ self .global_waypoint_index = - 1
127
+ self .saved_target_global_waypoint = None
123
128
self .use_mock_land = self .get_parameter ("use_mock_land" ).get_parameter_value ().bool_value
124
129
self .mode = self .get_parameter ("mode" ).get_parameter_value ().string_value
125
130
self .planner = self .get_parameter ("path_planner" ).get_parameter_value ().string_value
@@ -154,6 +159,8 @@ def global_path_callback(self, msg: ci.Path):
154
159
f"Received data from { self .global_path_sub .topic } : { gp .path_to_dict (msg )} "
155
160
)
156
161
self .global_path = msg
162
+ if self .saved_target_global_waypoint is None :
163
+ self .saved_target_global_waypoint = self .global_path .waypoints [- 1 ]
157
164
158
165
def filtered_wind_sensor_callback (self , msg : ci .WindSensor ):
159
166
self .get_logger ().debug (f"Received data from { self .filtered_wind_sensor_sub .topic } : { msg } " )
@@ -251,26 +258,89 @@ def get_desired_heading(self) -> float:
251
258
float: The desired heading
252
259
"""
253
260
254
- received_new_path = self .local_path .update_if_needed (
261
+ # Extra logic for when the global waypoint changes due to receiving a new global path
262
+ received_new_global_waypoint = False
263
+ if (
264
+ self .global_path .waypoints [self .global_waypoint_index ]
265
+ != self .saved_target_global_waypoint
266
+ ):
267
+ received_new_global_waypoint = True
268
+ self .global_waypoint_index = self .determine_start_point_in_new_global_path (
269
+ self .global_path , self .gps .lat_lon
270
+ )
271
+ self .saved_target_global_waypoint = self .global_path .waypoints [
272
+ self .global_waypoint_index
273
+ ]
274
+
275
+ # Check if we're close enough to the global waypoint to head to the next one
276
+ _ , _ , distance_to_waypoint_m = GEODESIC .inv (
277
+ self .gps .lat_lon .longitude ,
278
+ self .gps .lat_lon .latitude ,
279
+ self .saved_target_global_waypoint .longitude ,
280
+ self .saved_target_global_waypoint .latitude ,
281
+ )
282
+ if distance_to_waypoint_m < GLOBAL_WAYPOINT_REACHED_THRESH_KM :
283
+ received_new_global_waypoint = True
284
+ self .global_waypoint_index -= 1 # Since global waypoints are in reverse order
285
+
286
+ # At the end of the global path, alternate between the last two waypoints
287
+ if self .global_waypoint_index < 0 :
288
+ self .global_waypoint_index = 1
289
+
290
+ self .saved_target_global_waypoint = self .global_path .waypoints [
291
+ self .global_waypoint_index
292
+ ]
293
+
294
+ received_new_local_path = self .local_path .update_if_needed (
255
295
self .gps ,
256
296
self .ais_ships ,
257
297
self .global_path ,
298
+ received_new_global_waypoint ,
299
+ self .saved_target_global_waypoint ,
258
300
self .filtered_wind_sensor ,
259
301
self .planner ,
260
302
self .land_multi_polygon ,
261
303
)
262
304
263
- if received_new_path :
264
- self .current_waypoint_index = 0
305
+ if received_new_local_path :
306
+ self .local_waypoint_index = 0
265
307
266
- desired_heading , self .current_waypoint_index = (
308
+ desired_heading , self .local_waypoint_index = (
267
309
self .calculate_desired_heading_and_waypoint_index (
268
- self .local_path .path , self .current_waypoint_index , self .gps .lat_lon
310
+ self .local_path .path , self .local_waypoint_index , self .gps .lat_lon
269
311
)
270
312
)
271
313
272
314
return desired_heading
273
315
316
+ @staticmethod
317
+ def determine_start_point_in_new_global_path (
318
+ global_path : ci .Path , boat_lat_lon : ci .HelperLatLon
319
+ ):
320
+ """Used when we receive a new global path.
321
+ Finds the index of the first waypoint within 'pathfinding range' of gps location.
322
+ If none are found, it returns the index of the nearest waypoint."""
323
+
324
+ closest_m , index_of_closest = REALLY_FAR_M , - 1
325
+ for waypoint_index in range (len (global_path .waypoints ) - 1 , - 1 , - 1 ):
326
+ # Note: the global waypoints are in reverse order (index 0 is final waypoint)
327
+ waypoint = global_path .waypoints [waypoint_index ]
328
+
329
+ _ , _ , distance_to_waypoint_m = GEODESIC .inv (
330
+ boat_lat_lon .longitude ,
331
+ boat_lat_lon .latitude ,
332
+ waypoint .longitude ,
333
+ waypoint .latitude ,
334
+ )
335
+
336
+ if distance_to_waypoint_m < closest_m :
337
+ closest_m , index_of_closest = distance_to_waypoint_m , waypoint_index
338
+
339
+ if distance_to_waypoint_m < cs .km_to_meters (PATHFINDING_RANGE_KM ):
340
+ return waypoint_index
341
+
342
+ return index_of_closest
343
+
274
344
@staticmethod
275
345
def calculate_desired_heading_and_waypoint_index (
276
346
path : ci .Path , waypoint_index : int , boat_lat_lon : ci .HelperLatLon
@@ -280,7 +350,7 @@ def calculate_desired_heading_and_waypoint_index(
280
350
boat_lat_lon .longitude , boat_lat_lon .latitude , waypoint .longitude , waypoint .latitude
281
351
)
282
352
283
- if cs .meters_to_km (distance_to_waypoint_m ) < WAYPOINT_REACHED_THRESH_KM :
353
+ if cs .meters_to_km (distance_to_waypoint_m ) < LOCAL_WAYPOINT_REACHED_THRESH_KM :
284
354
# If we reached the current local waypoint, aim for the next one
285
355
waypoint_index += 1
286
356
waypoint = path .waypoints [waypoint_index ]
@@ -339,3 +409,4 @@ def _log_inactive_subs_warning(self):
339
409
340
410
if __name__ == "__main__" :
341
411
main ()
412
+ main ()
0 commit comments