LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduCopter - land_detector.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 120 126 95.2 %
Date: 2026-09-09 11:02:08 Functions: 7 7 100.0 %

          Line data    Source code
       1             : #include "Copter.h"
       2             : 
       3             : #include <AP_Stats/AP_Stats.h>              // statistics library
       4             : 
       5             : // Code to detect a crash main ArduCopter code
       6             : #define LAND_CHECK_ANGLE_ERROR_DEG  30.0f       // maximum angle error to be considered landing
       7             : #define LAND_CHECK_LARGE_ANGLE_RAD   radians(15.0f)     // maximum angle target to be considered landing
       8             : #define LAND_CHECK_ACCEL_MOVING     3.0f        // maximum acceleration after subtracting gravity
       9             : 
      10             : 
      11             : // counter to verify landings
      12             : static uint32_t land_detector_count = 0;
      13             : 
      14             : // run land and crash detectors
      15             : // called at MAIN_LOOP_RATE
      16    30412448 : void Copter::update_land_and_crash_detectors()
      17             : {
      18             :     // update 1hz filtered acceleration
      19    30412448 :     Vector3f accel_ef_mss = ahrs.get_accel_ef();
      20    30412448 :     accel_ef_mss.z += GRAVITY_MSS;
      21    30412448 :     land_accel_ef_filter.apply(accel_ef_mss, scheduler.get_loop_period_s());
      22             : 
      23    30412448 :     update_land_detector();
      24             : 
      25             : #if HAL_PARACHUTE_ENABLED
      26             :     // check parachute
      27    30412448 :     parachute_check();
      28             : #endif
      29             : 
      30    30412448 :     crash_check();
      31    30412448 :     thrust_loss_check();
      32    30412448 :     yaw_imbalance_check();
      33    30412448 : }
      34             : 
      35             : // update_land_detector - checks if we have landed and updates the ap.land_complete flag
      36             : // called at MAIN_LOOP_RATE
      37    30412448 : void Copter::update_land_detector()
      38             : {
      39             :     // land detector can not use the following sensors because they are unreliable during landing
      40             :     // barometer altitude :                 ground effect can cause errors larger than 4m
      41             :     // EKF vertical velocity or altitude :  poor barometer and large acceleration from ground impact
      42             :     // earth frame angle or angle error :   landing on an uneven surface will force the airframe to match the ground angle
      43             :     // gyro output :                        on uneven surface the airframe may rock back an forth after landing
      44             :     // range finder :                       tend to be problematic at very short distances
      45             :     // input throttle :                     in slow land the input throttle may be only slightly less than hover
      46             : 
      47             : #if HAL_LOGGING_ENABLED
      48    30412448 :     uint16_t logging_flags = 0;
      49             : #define SET_LOG_FLAG(condition, flag) if (condition) { logging_flags |= (uint16_t)flag; }
      50             : #else
      51             : #define SET_LOG_FLAG(condition, flag)
      52             : #endif
      53             : 
      54    30412448 :     if (!motors->armed()) {
      55             :         // if disarmed, always landed.
      56    10667071 :         set_land_complete(true);
      57    19745377 :     } else if (ap.land_complete) {
      58             : #if FRAME_CONFIG == HELI_FRAME
      59             :         // if rotor speed and collective pitch are high then clear landing flag
      60      507640 :         if (!flightmode->is_taking_off() && motors->get_takeoff_collective() && motors->get_spool_state() == AP_Motors::SpoolState::THROTTLE_UNLIMITED) {
      61             : #else
      62             :         // if throttle output is high then clear landing flag
      63      835435 :         if (!flightmode->is_taking_off() && motors->get_throttle_out() > get_non_takeoff_throttle() && motors->get_spool_state() == AP_Motors::SpoolState::THROTTLE_UNLIMITED) {
      64             :             // this should never happen because take-off should be detected at the flight mode level
      65             :             // this here to highlight there is a bug or missing take-off detection
      66           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
      67             : #endif
      68           9 :             set_land_complete(false);
      69             :         }
      70    18402302 :     } else if (standby_active) {
      71             :         // land detector will not run in standby mode
      72           0 :         land_detector_count = 0;
      73             :     } else {
      74             : 
      75    18402302 :         float land_trigger_sec = LAND_DETECTOR_TRIGGER_SEC;
      76             : #if FRAME_CONFIG == HELI_FRAME
      77             :         // check for both manual collective modes and modes that use altitude hold. For manual collective (called throttle
      78             :         // because multi's use throttle), check that collective pitch is below land min collective position or throttle stick is zero.
      79             :         // Including the throttle zero check will ensure the conditions where stabilize stick zero position was not below collective min. For modes
      80             :         // that use altitude hold, check that the pilot is commanding a descent and collective is at min allowed for altitude hold modes.
      81             : 
      82             :         // check if landing
      83     1813936 :         const bool landing = flightmode->is_landing();
      84     1813936 :         SET_LOG_FLAG(landing, LandDetectorLoggingFlag::LANDING);
      85     1968252 :         bool motor_at_lower_limit = (flightmode->has_manual_throttle() && (motors->get_below_land_min_coll() || heli_flags.coll_stk_low) && fabsf(ahrs.get_roll_rad()) < M_PI/2.0f)
      86             : #if MODE_AUTOROTATE_ENABLED
      87     1785758 :                                     || (flightmode->mode_number() == Mode::Number::AUTOROTATE && motors->get_below_land_min_coll())
      88             : #endif
      89     3627872 :                                     || ((!get_force_flying() || landing) && motors->limit.throttle_lower && pos_control->get_vel_desired_U_ms() < 0.0f);
      90     1813936 :         bool throttle_mix_at_min = true;
      91             : #else
      92             :         // check that the average throttle output is near minimum (less than 12.5% hover throttle)
      93    16588366 :         bool motor_at_lower_limit = motors->limit.throttle_lower;
      94    16588366 :         bool throttle_mix_at_min = attitude_control->is_throttle_mix_min();
      95             :         // set throttle_mix_at_min to true because throttle is never at mix min in airmode
      96             :         // increase land_trigger_sec when using airmode
      97    16588366 :         if (flightmode->has_manual_throttle() && air_mode_active()) {
      98       10244 :             land_trigger_sec = LAND_AIRMODE_DETECTOR_TRIGGER_SEC;
      99       10244 :             throttle_mix_at_min = true;
     100             :         }
     101             : #endif
     102    18402302 :         SET_LOG_FLAG(motor_at_lower_limit, LandDetectorLoggingFlag::MOTOR_AT_LOWER_LIMIT);
     103    18402302 :         SET_LOG_FLAG(throttle_mix_at_min, LandDetectorLoggingFlag::THROTTLE_MIX_AT_MIN);
     104             : 
     105    18402302 :         uint8_t land_detector_scalar = 1;
     106             : #if AP_LANDINGGEAR_ENABLED
     107    18402302 :         if (landinggear.get_wow_state() != AP_LandingGear::LG_WOW_UNKNOWN) {
     108             :             // we have a WoW sensor so lets loosen the strictness of the landing detector
     109           0 :             land_detector_scalar = 2;
     110             :         }
     111             : #endif
     112             : 
     113             :         // check for aggressive flight requests - requested roll or pitch angle below 15 degrees
     114    18402302 :         const Vector3f& angle_target_rad = attitude_control->get_att_target_euler_rad();
     115    18402302 :         bool large_angle_request = angle_target_rad.xy().length_squared() > sq(LAND_CHECK_LARGE_ANGLE_RAD);
     116    18402302 :         SET_LOG_FLAG(large_angle_request, LandDetectorLoggingFlag::LARGE_ANGLE_REQUEST);
     117             : 
     118             :         // check for large external disturbance - angle error over 30 degrees
     119    18402302 :         const float angle_error = attitude_control->get_att_error_angle_deg();
     120    18402302 :         bool large_angle_error = (angle_error > LAND_CHECK_ANGLE_ERROR_DEG);
     121    18402302 :         SET_LOG_FLAG(large_angle_error, LandDetectorLoggingFlag::LARGE_ANGLE_ERROR);
     122             : 
     123             :         // check that the airframe is not accelerating (not falling or braking after fast forward flight)
     124    18402302 :         bool accel_stationary = (land_accel_ef_filter.get().length() <= LAND_DETECTOR_ACCEL_MAX * land_detector_scalar);
     125    18402302 :         SET_LOG_FLAG(accel_stationary, LandDetectorLoggingFlag::ACCEL_STATIONARY);
     126             : 
     127             :         // check that vertical speed is within 1m/s of zero
     128    18402302 :         float vel_d_ms = 0;
     129    18402302 :         UNUSED_RESULT(AP::ahrs().get_velocity_D(vel_d_ms, copter.vibration_check.high_vibes));
     130    18402302 :         const bool descent_rate_low = fabsf(vel_d_ms) < LAND_DETECTOR_VEL_Z_MAX * land_detector_scalar;
     131    18402302 :         SET_LOG_FLAG(descent_rate_low, LandDetectorLoggingFlag::DESCENT_RATE_LOW);
     132             : 
     133             :         // if we have a healthy rangefinder only allow landing detection below 2 meters
     134    18402302 :         bool rangefinder_check = (!rangefinder_alt_ok() || rangefinder_state.alt_m_filt.get() < LAND_RANGEFINDER_MIN_ALT_M);
     135    18402302 :         SET_LOG_FLAG(rangefinder_check, LandDetectorLoggingFlag::RANGEFINDER_BELOW_2M);
     136             : 
     137             :         // if we have weight on wheels (WoW) or ambiguous unknown. never no WoW
     138             : #if AP_LANDINGGEAR_ENABLED
     139    18402302 :         const bool WoW_check = (landinggear.get_wow_state() == AP_LandingGear::LG_WOW || landinggear.get_wow_state() == AP_LandingGear::LG_WOW_UNKNOWN);
     140             : #else
     141             :         const bool WoW_check = true;
     142             : #endif
     143    18402302 :         SET_LOG_FLAG(WoW_check, LandDetectorLoggingFlag::WOW);
     144             : 
     145    18402302 :         if (motor_at_lower_limit && throttle_mix_at_min && !large_angle_request && !large_angle_error && accel_stationary && descent_rate_low && rangefinder_check && WoW_check) {
     146             :             // landed criteria met - increment the counter and check if we've triggered
     147      160264 :             if( land_detector_count < land_trigger_sec*scheduler.get_loop_rate_hz()) {
     148      159872 :                 land_detector_count++;
     149             :             } else {
     150         392 :                 set_land_complete(true);
     151             :             }
     152             :         } else {
     153             :             // we've sensed movement up or down so reset land_detector
     154    18242038 :             land_detector_count = 0;
     155             :         }
     156             :     }
     157             : 
     158    30412448 :     set_land_complete_maybe(ap.land_complete || (land_detector_count >= LAND_DETECTOR_MAYBE_TRIGGER_SEC*scheduler.get_loop_rate_hz()));
     159             : 
     160             : #if HAL_LOGGING_ENABLED
     161             : // @LoggerMessage: LDET
     162             : // @Description: Land Detector State
     163             : // @Field: TimeUS: Time since system startup
     164             : // @Field: Flags: boolean state flags
     165             : // @FieldBitmaskEnum: Flags: Copter::LandDetectorLoggingFlag
     166             : // @Field: Count: landing_detector pass count
     167    30412448 :     SET_LOG_FLAG(ap.land_complete, LandDetectorLoggingFlag::LANDED);
     168    30412448 :     SET_LOG_FLAG(ap.land_complete_maybe, LandDetectorLoggingFlag::LANDED_MAYBE);
     169    30412448 :     SET_LOG_FLAG(standby_active, LandDetectorLoggingFlag::STANDBY_ACTIVE);
     170    30412448 :     Log_LDET(logging_flags, land_detector_count);
     171             : #undef SET_LOG_FLAG
     172             : #endif
     173    30412448 : }
     174             : 
     175             : #if HAL_LOGGING_ENABLED
     176    30412448 : void Copter::Log_LDET(uint16_t logging_flags, uint32_t detector_count)
     177             : {
     178             :     // do not log if no change:
     179    30412448 :     if (logging_flags == land_detector.last_logged_flags &&
     180    30389782 :         detector_count == land_detector.last_logged_count) {
     181    30231186 :         return;
     182             :     }
     183             :     // do not log more than 50Hz:
     184      181262 :     const auto now = AP_HAL::millis();
     185      181262 :     if (now - land_detector.last_logged_ms < 20) {
     186      143925 :         return;
     187             :     }
     188             : 
     189       37337 :     land_detector.last_logged_count = detector_count;
     190       37337 :     land_detector.last_logged_flags = logging_flags;
     191       37337 :     land_detector.last_logged_ms = now;
     192             : 
     193       37337 :     AP::logger().WriteStreaming(
     194             :         "LDET",
     195             :         "TimeUS," "Flags," "Count",
     196             :         "s"       "-"      "-",
     197             :         "F"       "-"      "-",
     198             :         "Q"       "H"      "I",
     199             :         AP_HAL::micros64(),
     200             :         logging_flags,
     201             :         land_detector_count
     202             :     );
     203             : }
     204             : #endif
     205             : 
     206             : // set land_complete flag and disarm motors if disarm-on-land is configured
     207    10839028 : void Copter::set_land_complete(bool b)
     208             : {
     209             :     // if no change, exit immediately
     210    10839028 :     if( ap.land_complete == b )
     211    10837118 :         return;
     212             : 
     213        1910 :     land_detector_count = 0;
     214             : 
     215             : #if HAL_LOGGING_ENABLED
     216        1910 :     if(b){
     217        1453 :         AP::logger().Write_Event(LogEvent::LAND_COMPLETE);
     218             :     } else {
     219         457 :         AP::logger().Write_Event(LogEvent::NOT_LANDED);
     220             :     }
     221             : #endif
     222        1910 :     ap.land_complete = b;
     223             : 
     224             : #if AP_STATS_ENABLED
     225        1910 :     AP::stats()->set_flying(!b);
     226             : #endif
     227             : 
     228             :     // tell AHRS flying state
     229        1910 :     set_likely_flying(!b);
     230             : 
     231        1910 :     if (!b) {
     232             :         // not landed, no further action
     233         457 :         return;
     234             :     }
     235             : 
     236             :     // landed; trigger disarm-on-land if configured
     237        1453 :     if ((g.throttle_behavior & THR_BEHAVE_DISARM_ON_LAND_DETECT) == 0) {
     238             :         // not configured to disarm on landing detection
     239        1452 :         return;
     240             :     }
     241             : 
     242           1 :     if (!motors->armed()) {
     243             :         // we are not currently armed, so we don't need to disarm:
     244             :         // n.b. should this be checking vehicle-armed rather than motors-armed?
     245           0 :         return;
     246             :     }
     247             : 
     248           1 :     if (flightmode->has_manual_throttle()) {
     249             :         // we do not use the landing detector to disarm if the vehicle
     250             :         // is in e.g. STABILIZE.  The normal DISARM_DELAY logic applies.
     251           0 :         return;
     252             :     }
     253             : 
     254             :     // the flightmode may not allow disarm on landing.  Note that this
     255             :     // check returns false for the LAND flight mode - it checks the
     256             :     // landing detector (ap.land_complete) itself.
     257           1 :     if (!flightmode->allows_arming(AP_Arming::Method::LANDING)) {
     258           0 :         return;
     259             :     }
     260             : 
     261             :     // all checks passed, disarm the vehicle:
     262           1 :     arming.disarm(AP_Arming::Method::LANDED);
     263             : }
     264             : 
     265             : // set land complete maybe flag
     266    30413996 : void Copter::set_land_complete_maybe(bool b)
     267             : {
     268             :     // if no change, exit immediately
     269    30413996 :     if (ap.land_complete_maybe == b)
     270    30412080 :         return;
     271             : 
     272        1916 :     if (b) {
     273        1456 :         LOGGER_WRITE_EVENT(LogEvent::LAND_COMPLETE_MAYBE);
     274             :     }
     275        1916 :     ap.land_complete_maybe = b;
     276             : }
     277             : 
     278             : // sets motors throttle_low_comp value depending upon vehicle state
     279             : //  low values favour pilot/autopilot throttle over attitude control, high values favour attitude control over throttle
     280             : //  has no effect when throttle is above hover throttle
     281     3800955 : void Copter::update_throttle_mix()
     282             : {
     283             : #if FRAME_CONFIG != HELI_FRAME
     284             :     // if disarmed or landed prioritise throttle
     285     3396732 :     if (!motors->armed() || ap.land_complete) {
     286     1323193 :         attitude_control->set_throttle_mix_min();
     287     1323193 :         return;
     288             :     }
     289             : 
     290     2073539 :     if (flightmode->has_manual_throttle()) {
     291             :         // manual throttle
     292       22277 :         if (channel_throttle->get_control_in() <= 0 && !air_mode_active()) {
     293         113 :             attitude_control->set_throttle_mix_min();
     294             :         } else {
     295       22164 :             attitude_control->set_throttle_mix_man();
     296             :         }
     297             :     } else {
     298             :         // autopilot controlled throttle
     299             : 
     300             :         // check for aggressive flight requests - requested roll or pitch angle below 15 degrees
     301     2051262 :         const Vector3f& angle_target_rad = attitude_control->get_att_target_euler_rad();
     302     2051262 :         bool large_angle_request = angle_target_rad.xy().length_squared() > sq(LAND_CHECK_LARGE_ANGLE_RAD);
     303             : 
     304             :         // check for large external disturbance - angle error over 30 degrees
     305     2051262 :         const float angle_error = attitude_control->get_att_error_angle_deg();
     306     2051262 :         bool large_angle_error = (angle_error > LAND_CHECK_ANGLE_ERROR_DEG);
     307             : 
     308             :         // check for large acceleration - falling or high turbulence
     309     2051262 :         const bool accel_moving = (land_accel_ef_filter.get().length() > LAND_CHECK_ACCEL_MOVING);
     310             : 
     311             :         // check for requested descent
     312     2051262 :         bool descent_not_demanded = pos_control->get_vel_desired_U_ms() >= 0.0f;
     313             : 
     314             :         // check if landing
     315     2051262 :         const bool landing = flightmode->is_landing();
     316             : 
     317     2051262 :         if (((large_angle_request || get_force_flying()) && !landing) || large_angle_error || accel_moving || descent_not_demanded) {
     318     1303597 :             attitude_control->set_throttle_mix_max(pos_control->get_vel_D_control_ratio());
     319             :         } else {
     320      747665 :             attitude_control->set_throttle_mix_min();
     321             :         }
     322             :     }
     323             : #endif
     324      404223 : }
     325             : 
     326             : // helper function for force flying flag
     327    22018032 : bool Copter::get_force_flying() const
     328             : {
     329             : #if FRAME_CONFIG == HELI_FRAME
     330     3599256 :     if (attitude_control->get_inverted_flight()) {
     331       19680 :         return true;
     332             :     }
     333             : #endif
     334    21998352 :     return force_flying;
     335             : }

Generated by: LCOV version 1.14