2023-03-22 05:45:41 -03:00
# include "AC_PrecLand_config.h"
# if AC_PRECLAND_ENABLED
2021-08-14 06:58:33 -03:00
# include <AC_PrecLand/AC_PrecLand.h>
2023-03-22 05:45:41 -03:00
# include "AC_PrecLand_StateMachine.h"
2021-08-14 06:58:33 -03:00
# include <AP_AHRS/AP_AHRS.h>
2022-08-24 08:18:42 -03:00
# include <GCS_MAVLink/GCS.h>
2021-08-14 06:58:33 -03:00
2023-10-11 04:41:49 -03:00
static const float MAX_POS_ERROR_M = 0.75f ; // Maximum position error for retry locations
2021-08-14 06:58:33 -03:00
static const uint32_t FAILSAFE_INIT_TIMEOUT_MS = 7000 ; // Timeout in ms before failsafe measures are started. During this period vehicle is completely stopped to give user the time to take over
static const float RETRY_OFFSET_ALT_M = 1.5f ; // This gets added to the altitude of the retry location
2023-10-11 04:41:49 -03:00
// Initialize the state machine. This is called every time vehicle switches mode
2021-08-14 06:58:33 -03:00
void AC_PrecLand_StateMachine : : init ( )
{
2021-08-24 03:39:48 -03:00
AC_PrecLand * _precland = AP : : ac_precland ( ) ;
if ( _precland = = nullptr ) {
// precland not enabled
return ;
}
if ( ! _precland - > enabled ( ) ) {
// precland is not enabled, prec land state machine methods should not be called!
return ;
}
2023-10-11 04:41:49 -03:00
// init is only called ONCE per mode change. So in a particular mode we can retry only a finite times.
2021-08-14 06:58:33 -03:00
// The counter will be reset if the statemachine is called from a different mode
_retry_count = 0 ;
// reset every other statemachine
reset_failed_landing_statemachine ( ) ;
}
2023-10-11 04:41:49 -03:00
// Reset the landing statemachines. This needs to be called every time the landing target is back in sight.
2021-08-14 06:58:33 -03:00
// So that if the landing target goes out of sight again, we can start the failed landing procedure back from the beginning stage
void AC_PrecLand_StateMachine : : reset_failed_landing_statemachine ( )
{
landing_target_lost_action = TargetLostAction : : INIT ;
_retry_state = RetryLanding : : INIT ;
failsafe_initialized = false ;
}
// Run Prec Land State Machine. During Prec Landing, we might encounter four scenarios:
// 1. We had the target in sight, but have lost it now. 2. We never had the target in sight and user wants to land.
// 3. We have the target in sight and can continue landing. 4. The sensor is out of range
// This method deals with all of these scenarios
// Returns the action needed to be done by the vehicle.
// Parameters: Vector3f "retry_pos_m" is filled with the required location if we need to retry landing.
AC_PrecLand_StateMachine : : Status AC_PrecLand_StateMachine : : update ( Vector3f & retry_pos_m )
{
// grab the current status of Landing Target
AC_PrecLand * _precland = AP : : ac_precland ( ) ;
if ( _precland = = nullptr ) {
// should never happen
return Status : : ERROR ;
}
2021-08-24 03:39:48 -03:00
if ( ! _precland - > enabled ( ) ) {
// precland is not enabled, prec land state machine should not be called!
return Status : : ERROR ;
}
2021-08-14 06:58:33 -03:00
AC_PrecLand : : TargetState precland_target_state = _precland - > get_target_state ( ) ;
switch ( precland_target_state ) {
case AC_PrecLand : : TargetState : : TARGET_RECENTLY_LOST :
// we have lost the target but had it in sight at least once recently
// action will depend on what user wants
return get_target_lost_actions ( retry_pos_m ) ;
case AC_PrecLand : : TargetState : : TARGET_NEVER_SEEN :
// we have no clue where we are supposed to be landing
// let user decide how strict our failsafe actions need to be
return Status : : FAILSAFE ;
case AC_PrecLand : : TargetState : : TARGET_OUT_OF_RANGE :
// The target isn't in sight, but we can't run any fail safe measures or do landing retry
// Therefore just descend for now, and check again later if retry is allowed
case AC_PrecLand : : TargetState : : TARGET_FOUND :
// no action required, target is in sight
reset_failed_landing_statemachine ( ) ;
return Status : : DESCEND ;
}
// should never reach here, all values are handled above
return Status : : ERROR ;
}
// Target is lost (i.e we had it in sight some time back), this method helps decide on what needs to be done next
// The chosen action depends on user set landing strictness and will be returned by this function
// Parameters: Vector3f "retry_pos_m" is filled with the required location if we need to retry landing.
AC_PrecLand_StateMachine : : Status AC_PrecLand_StateMachine : : get_target_lost_actions ( Vector3f & retry_pos_m )
{
AC_PrecLand * _precland = AP : : ac_precland ( ) ;
if ( _precland = = nullptr ) {
// should never happen
return Status : : ERROR ;
}
switch ( landing_target_lost_action ) {
case TargetLostAction : : INIT : {
// figure out how strict the user is with the landing
const RetryStrictness strictness = _precland - > get_retry_strictness ( ) ;
switch ( strictness ) {
case RetryStrictness : : NORMAL :
case RetryStrictness : : VERY_STRICT :
// We eventually want to retry landing, but lets descend for some time and hope the target gets in sight
// If not, we will retry landing
landing_target_lost_action = TargetLostAction : : DESCEND ;
break ;
case RetryStrictness : : NOT_STRICT :
// User just wants to land, prec land isn't a priority
landing_target_lost_action = TargetLostAction : : LAND_VERTICALLY ;
break ;
}
// at this stage we will be descending no matter what
// so no special action required
return Status : : DESCEND ;
}
case TargetLostAction : : DESCEND :
if ( AP_HAL : : millis ( ) - _precland - > get_last_valid_target_ms ( ) > = _precland - > get_min_retry_time_sec ( ) * 1000 ) {
// we have descended for some time and the target still isn't in sight
// lets retry
landing_target_lost_action = TargetLostAction : : RETRY_LANDING ;
_retry_state = RetryLanding : : INIT ;
}
// still descending, no other action
return Status : : DESCEND ;
case TargetLostAction : : RETRY_LANDING :
// retry the landing by going to another position
return retry_landing ( retry_pos_m ) ;
case TargetLostAction : : LAND_VERTICALLY :
// Just land vertically
// we will not be retrying to any location here on, so return false
return Status : : DESCEND ;
}
// should never reach here, all cases are handled above
return Status : : ERROR ;
}
// Retry landing based on a previously known location of the landing target
// Returns the action that should be taken by the vehicle
// Vector3f "retry_pos_m" is filled with the required location.
AC_PrecLand_StateMachine : : Status AC_PrecLand_StateMachine : : retry_landing ( Vector3f & retry_pos_m )
{
AC_PrecLand * _precland = AP : : ac_precland ( ) ;
if ( _precland = = nullptr ) {
// should never happen
return Status : : ERROR ;
}
if ( _precland - > get_max_retry_allowed ( ) = = 0 ) {
// user does not want retry
return Status : : FAILSAFE ;
}
if ( _retry_count > _precland - > get_max_retry_allowed ( ) ) {
// we have exhausted the amount of times vehicle was allowed to retry landing
// do failsafe measure so the vehicle isn't stuck in a constant loop
return Status : : FAILSAFE ;
}
// get the retry position. This depends on what retry behavior has been set by user
Vector3f go_to_pos ;
const RetryAction retry_action = _precland - > get_retry_behaviour ( ) ;
if ( retry_action = = RetryAction : : GO_TO_TARGET_LOC ) {
_precland - > get_last_detected_landing_pos ( go_to_pos ) ;
} else if ( retry_action = = RetryAction : : GO_TO_LAST_LOC ) {
_precland - > get_last_vehicle_pos_when_target_detected ( go_to_pos ) ;
}
// add a little bit offset so the vehicle climbs slightly higher than where it was
// remember this is "D" frame and in meters's
go_to_pos . z - = RETRY_OFFSET_ALT_M ;
switch ( _retry_state ) {
case RetryLanding : : INIT :
// Init the Retry
_retry_count + + ;
_retry_state = RetryLanding : : IN_PROGRESS ;
// inform the user what we are doing
if ( _retry_count < = _precland - > get_max_retry_allowed ( ) ) {
2024-02-15 11:26:16 -04:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " PrecLand: Retrying " ) ;
2021-08-14 06:58:33 -03:00
}
retry_pos_m = go_to_pos ;
return Status : : RETRYING ;
case RetryLanding : : IN_PROGRESS : {
// continue converging towards the target till we are close by
retry_pos_m = go_to_pos ;
Vector3f pos ;
if ( ! AP : : ahrs ( ) . get_relative_position_NED_origin ( pos ) ) {
return Status : : ERROR ;
}
const float dist_to_target = ( go_to_pos - pos ) . length ( ) ;
if ( ( dist_to_target < MAX_POS_ERROR_M ) ) {
// we have approx reached landing location previously detected
_retry_state = RetryLanding : : DESCEND ;
}
return Status : : RETRYING ;
}
case RetryLanding : : DESCEND : {
// descend a little bit before completing the retry
// This will descend to the original height of where landing target was first detected
Vector3f pos ;
if ( ! AP : : ahrs ( ) . get_relative_position_NED_origin ( pos ) ) {
return Status : : ERROR ;
}
// z_target is in "D" frame
const float z_target = go_to_pos . z + RETRY_OFFSET_ALT_M ;
retry_pos_m = Vector3f { pos . x , pos . y , z_target } ;
if ( fabsf ( pos . z - retry_pos_m . z ) < MAX_POS_ERROR_M ) {
// we have descended to the original height where we started the climb from
_retry_state = RetryLanding : : COMPLETE ;
2024-02-15 11:26:16 -04:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " PrecLand: Retry Completed " ) ;
2021-08-14 06:58:33 -03:00
}
return Status : : RETRYING ;
}
case RetryLanding : : COMPLETE :
// Vehicle has completed a retry, and most likely the landing location still isn't sight
// we have no choice but to force a failsafe action
return Status : : FAILSAFE ;
}
// should never reach here
return Status : : ERROR ;
}
// This is only called when the current status of the state machine returns "failsafe" and will return the action that the vehicle should do
// At the moment this method only allows you to stop in air permanently, or land vertically
// Failsafe will only trigger as a last resort
AC_PrecLand_StateMachine : : FailSafeAction AC_PrecLand_StateMachine : : get_failsafe_actions ( )
{
AC_PrecLand * _precland = AP : : ac_precland ( ) ;
if ( _precland = = nullptr ) {
// should never happen, just descend
return FailSafeAction : : DESCEND ;
}
if ( ! failsafe_initialized ) {
// start the timer
failsafe_start_ms = AP_HAL : : millis ( ) ;
failsafe_initialized = true ;
2024-02-15 11:26:16 -04:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " PrecLand: Failsafe Measures " ) ;
2021-08-14 06:58:33 -03:00
}
// Depending on the strictness we will either land vertically, wait for some time and then land vertically, not land at all
const RetryStrictness strictness = _precland - > get_retry_strictness ( ) ;
switch ( strictness ) {
case RetryStrictness : : VERY_STRICT :
// user does not want to land on anything but the target
// stop landing (hover)
return FailSafeAction : : HOLD_POS ;
case RetryStrictness : : NORMAL :
if ( AP_HAL : : millis ( ) - failsafe_start_ms < FAILSAFE_INIT_TIMEOUT_MS ) {
// stop the vehicle for at least a few seconds before descending
// this might give user the chance to take over
// we do not want to be too linent in landing vertically because of the strictness set by the user
return FailSafeAction : : HOLD_POS ;
}
// land the vehicle vertically
return FailSafeAction : : DESCEND ;
case RetryStrictness : : NOT_STRICT :
// User wants to prioritize landing over staying in the air
return FailSafeAction : : DESCEND ;
}
// should never reach here
return FailSafeAction : : DESCEND ;
}
2023-03-22 05:45:41 -03:00
# endif // AC_PRECLAND_ENABLED