25
25
26
26
import pymavswarm .state as swarm_state
27
27
from pymavswarm .mission import SwarmMission
28
+ from pymavswarm .safety import HyperRectangle , Interval , SafetyChecker
28
29
from pymavswarm .state .generic import Generic
30
+ from pymavswarm .utils import latitude_conversion , longitude_conversion
29
31
30
32
31
33
class Agent :
@@ -79,7 +81,7 @@ def __init__(
79
81
self .__gps_info = swarm_state .GPSInfo (
80
82
0.0 , 0.0 , 0 , 0 , optional_context_props = context_props
81
83
)
82
- self .__location = swarm_state .Location (
84
+ self .__position = swarm_state .Position (
83
85
0.0 , 0.0 , 0.0 , optional_context_props = context_props
84
86
)
85
87
self .__ekf = swarm_state .EKFStatus (
@@ -127,7 +129,7 @@ def __init__(
127
129
self .__last_params_read = swarm_state .ParameterList (
128
130
max_length = max_params_stored , optional_context_props = context_props
129
131
)
130
- self .__home_position = swarm_state .Location (
132
+ self .__home_position = swarm_state .Position (
131
133
0.0 , 0.0 , 0.0 , optional_context_props = context_props
132
134
)
133
135
self .__hrl_state = swarm_state .Generic (
@@ -136,7 +138,7 @@ def __init__(
136
138
self .__ping = swarm_state .Generic (
137
139
"ping" , 0 , optional_context_props = context_props
138
140
)
139
- self .__last_gps_message_timestamp = swarm_state .Generic (
141
+ self .__last_position_message_timestamp = swarm_state .Generic (
140
142
"time_boot_ms" , 0 , optional_context_props = context_props
141
143
)
142
144
self .__clock_offset : deque [int ] = deque (maxlen = 5 )
@@ -208,14 +210,14 @@ def gps_info(self) -> swarm_state.GPSInfo:
208
210
return self .__gps_info
209
211
210
212
@property
211
- def location (self ) -> swarm_state .Location :
213
+ def position (self ) -> swarm_state .Position :
212
214
"""
213
- Location of an agent.
215
+ Position of an agent.
214
216
215
- :return: agent location
216
- :rtype: Location
217
+ :return: agent position
218
+ :rtype: Position
217
219
"""
218
- return self .__location
220
+ return self .__position
219
221
220
222
@property
221
223
def ekf (self ) -> swarm_state .EKFStatus :
@@ -368,12 +370,12 @@ def last_params_read(self) -> swarm_state.ParameterList:
368
370
return self .__last_params_read
369
371
370
372
@property
371
- def home_position (self ) -> swarm_state .Location :
373
+ def home_position (self ) -> swarm_state .Position :
372
374
"""
373
375
Home position of the agent.
374
376
375
377
:return: agent's home position
376
- :rtype: Location
378
+ :rtype: Position
377
379
"""
378
380
return self .__home_position
379
381
@@ -398,14 +400,14 @@ def ping(self) -> Generic:
398
400
return self .__ping
399
401
400
402
@property
401
- def last_gps_message_timestamp (self ) -> Generic :
403
+ def last_position_message_timestamp (self ) -> Generic :
402
404
"""
403
- Most recent time that the agent sent the GPS message in the global clock.
405
+ Most recent time that the agent sent the position message in the global clock.
404
406
405
407
:return: time since boot [ms]
406
408
:rtype: Generic
407
409
"""
408
- return self .__last_gps_message_timestamp
410
+ return self .__last_position_message_timestamp
409
411
410
412
def update_clock_offset (self , offset : int ) -> None :
411
413
"""
@@ -429,6 +431,135 @@ def clock_offset(self) -> int:
429
431
"""
430
432
return int (fmean (self .__clock_offset ))
431
433
434
+ def compute_reachable_set (
435
+ self ,
436
+ position_error : float ,
437
+ velocity_error : float ,
438
+ reach_time : float ,
439
+ initial_step_size : float = 0.5 ,
440
+ reach_timeout : float = 0.001 ,
441
+ uses_lat_lon : bool = True ,
442
+ ) -> tuple [HyperRectangle , float ]:
443
+ """
444
+ Compute the current reachable set of the agent.
445
+
446
+ :param position_error: 3D position error to account for in the measurement
447
+ :type position_error: float
448
+ :param velocity_error: 3D velocity error to account for in the measurement
449
+ :type velocity_error: float
450
+ :param reach_time: time that the reachable set should reach forward to
451
+ :type reach_time: float
452
+ :param initial_step_size: initial step to step forward when performing face
453
+ lifting (lower means higher accuracy but slower; higher means lower
454
+ accuracy but faster), defaults to 0.5
455
+ :type initial_step_size: float, optional
456
+ :param reach_timeout: maximum amount of time to spend computing the reachable
457
+ set [s], defaults to 0.001
458
+ :type reach_timeout: float, optional
459
+ :return: reachable set for the agent, time that the reachable set reaches to
460
+ from the start time
461
+ :rtype: tuple[HyperRectangle, float]
462
+ """
463
+ if uses_lat_lon :
464
+ rect = HyperRectangle (
465
+ [
466
+ # When computing the reachable state for the GPS position, we pass
467
+ # the origin as the position. After computing what the reachable
468
+ # change, we correct the original lat/lon position. This
469
+ # helps us eliminate *some* error that occurs during
470
+ # conversions.
471
+ Interval (- position_error , position_error ),
472
+ Interval (- position_error , position_error ),
473
+ Interval (
474
+ self .position .z - position_error ,
475
+ self .position .z + position_error ,
476
+ ),
477
+ Interval (
478
+ self .velocity .x - velocity_error ,
479
+ self .velocity .x + velocity_error ,
480
+ ),
481
+ Interval (
482
+ self .velocity .y - velocity_error ,
483
+ self .velocity .y + velocity_error ,
484
+ ),
485
+ Interval (
486
+ self .velocity .z - velocity_error ,
487
+ self .velocity .z + velocity_error ,
488
+ ),
489
+ ]
490
+ )
491
+ else :
492
+ rect = HyperRectangle (
493
+ [
494
+ Interval (
495
+ self .position .x - position_error ,
496
+ self .position .x + position_error ,
497
+ ),
498
+ Interval (
499
+ self .position .y - position_error ,
500
+ self .position .y + position_error ,
501
+ ),
502
+ Interval (
503
+ self .position .z - position_error ,
504
+ self .position .z + position_error ,
505
+ ),
506
+ Interval (
507
+ self .velocity .x - velocity_error ,
508
+ self .velocity .x + velocity_error ,
509
+ ),
510
+ Interval (
511
+ self .velocity .y - velocity_error ,
512
+ self .velocity .y + velocity_error ,
513
+ ),
514
+ Interval (
515
+ self .velocity .z - velocity_error ,
516
+ self .velocity .z + velocity_error ,
517
+ ),
518
+ ]
519
+ )
520
+
521
+ # Compute the sender's reachable state
522
+ (
523
+ reachable_state ,
524
+ reach_time_elapsed ,
525
+ ) = SafetyChecker .face_lifting_iterative_improvement (
526
+ rect ,
527
+ self .last_position_message_timestamp .value ,
528
+ (
529
+ self .acceleration .x ,
530
+ self .acceleration .y ,
531
+ self .acceleration .z ,
532
+ ),
533
+ reach_time ,
534
+ initial_step_size = initial_step_size ,
535
+ timeout = reach_timeout ,
536
+ )
537
+
538
+ if uses_lat_lon :
539
+ # Correct the latitude using the reachable positions
540
+ reachable_state .intervals [0 ].interval_min = latitude_conversion (
541
+ self .position .x ,
542
+ reachable_state .intervals [0 ].interval_min ,
543
+ )
544
+ reachable_state .intervals [0 ].interval_max = latitude_conversion (
545
+ self .position .x ,
546
+ reachable_state .intervals [0 ].interval_max ,
547
+ )
548
+
549
+ # Correct the longitude using the reachable positions
550
+ reachable_state .intervals [1 ].interval_min = longitude_conversion (
551
+ self .position .x ,
552
+ self .position .y ,
553
+ reachable_state .intervals [1 ].interval_min ,
554
+ )
555
+ reachable_state .intervals [1 ].interval_max = longitude_conversion (
556
+ self .position .x ,
557
+ self .position .y ,
558
+ reachable_state .intervals [1 ].interval_max ,
559
+ )
560
+
561
+ return reachable_state , reach_time_elapsed
562
+
432
563
def __str__ (self ) -> str :
433
564
"""
434
565
Print agent information in a human-readable format.
0 commit comments