Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/ArduCopter/land_detector.cpp
Views: 1798
#include "Copter.h"12#include <AP_Stats/AP_Stats.h> // statistics library34// Code to detect a crash main ArduCopter code5#define LAND_CHECK_ANGLE_ERROR_DEG 30.0f // maximum angle error to be considered landing6#define LAND_CHECK_LARGE_ANGLE_CD 1500.0f // maximum angle target to be considered landing7#define LAND_CHECK_ACCEL_MOVING 3.0f // maximum acceleration after subtracting gravity8910// counter to verify landings11static uint32_t land_detector_count = 0;1213// run land and crash detectors14// called at MAIN_LOOP_RATE15void Copter::update_land_and_crash_detectors()16{17// update 1hz filtered acceleration18Vector3f accel_ef = ahrs.get_accel_ef();19accel_ef.z += GRAVITY_MSS;20land_accel_ef_filter.apply(accel_ef, scheduler.get_loop_period_s());2122update_land_detector();2324#if HAL_PARACHUTE_ENABLED25// check parachute26parachute_check();27#endif2829crash_check();30thrust_loss_check();31yaw_imbalance_check();32}3334// update_land_detector - checks if we have landed and updates the ap.land_complete flag35// called at MAIN_LOOP_RATE36void Copter::update_land_detector()37{38// land detector can not use the following sensors because they are unreliable during landing39// barometer altitude : ground effect can cause errors larger than 4m40// EKF vertical velocity or altitude : poor barometer and large acceleration from ground impact41// earth frame angle or angle error : landing on an uneven surface will force the airframe to match the ground angle42// gyro output : on uneven surface the airframe may rock back an forth after landing43// range finder : tend to be problematic at very short distances44// input throttle : in slow land the input throttle may be only slightly less than hover4546#if HAL_LOGGING_ENABLED47uint16_t logging_flags = 0;48#define SET_LOG_FLAG(condition, flag) if (condition) { logging_flags |= (uint16_t)flag; }49#else50#define SET_LOG_FLAG(condition, flag)51#endif5253if (!motors->armed()) {54// if disarmed, always landed.55set_land_complete(true);56} else if (ap.land_complete) {57#if FRAME_CONFIG == HELI_FRAME58// if rotor speed and collective pitch are high then clear landing flag59if (!flightmode->is_taking_off() && motors->get_takeoff_collective() && motors->get_spool_state() == AP_Motors::SpoolState::THROTTLE_UNLIMITED) {60#else61// if throttle output is high then clear landing flag62if (!flightmode->is_taking_off() && motors->get_throttle_out() > get_non_takeoff_throttle() && motors->get_spool_state() == AP_Motors::SpoolState::THROTTLE_UNLIMITED) {63// this should never happen because take-off should be detected at the flight mode level64// this here to highlight there is a bug or missing take-off detection65INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);66#endif67set_land_complete(false);68}69} else if (standby_active) {70// land detector will not run in standby mode71land_detector_count = 0;72} else {7374float land_trigger_sec = LAND_DETECTOR_TRIGGER_SEC;75#if FRAME_CONFIG == HELI_FRAME76// check for both manual collective modes and modes that use altitude hold. For manual collective (called throttle77// because multi's use throttle), check that collective pitch is below land min collective position or throttle stick is zero.78// Including the throttle zero check will ensure the conditions where stabilize stick zero position was not below collective min. For modes79// that use altitude hold, check that the pilot is commanding a descent and collective is at min allowed for altitude hold modes.8081// check if landing82const bool landing = flightmode->is_landing();83SET_LOG_FLAG(landing, LandDetectorLoggingFlag::LANDING);84bool motor_at_lower_limit = (flightmode->has_manual_throttle() && (motors->get_below_land_min_coll() || heli_flags.coll_stk_low) && fabsf(ahrs.get_roll()) < M_PI/2.0f)85#if MODE_AUTOROTATE_ENABLED86|| (flightmode->mode_number() == Mode::Number::AUTOROTATE && motors->get_below_land_min_coll())87#endif88|| ((!get_force_flying() || landing) && motors->limit.throttle_lower && pos_control->get_vel_desired_cms().z < 0.0f);89bool throttle_mix_at_min = true;90#else91// check that the average throttle output is near minimum (less than 12.5% hover throttle)92bool motor_at_lower_limit = motors->limit.throttle_lower;93bool throttle_mix_at_min = attitude_control->is_throttle_mix_min();94// set throttle_mix_at_min to true because throttle is never at mix min in airmode95// increase land_trigger_sec when using airmode96if (flightmode->has_manual_throttle() && air_mode == AirMode::AIRMODE_ENABLED) {97land_trigger_sec = LAND_AIRMODE_DETECTOR_TRIGGER_SEC;98throttle_mix_at_min = true;99}100#endif101SET_LOG_FLAG(motor_at_lower_limit, LandDetectorLoggingFlag::MOTOR_AT_LOWER_LIMIT);102SET_LOG_FLAG(throttle_mix_at_min, LandDetectorLoggingFlag::THROTTLE_MIX_AT_MIN);103104uint8_t land_detector_scalar = 1;105#if AP_LANDINGGEAR_ENABLED106if (landinggear.get_wow_state() != AP_LandingGear::LG_WOW_UNKNOWN) {107// we have a WoW sensor so lets loosen the strictness of the landing detector108land_detector_scalar = 2;109}110#endif111112// check for aggressive flight requests - requested roll or pitch angle below 15 degrees113const Vector3f angle_target = attitude_control->get_att_target_euler_cd();114bool large_angle_request = angle_target.xy().length() > LAND_CHECK_LARGE_ANGLE_CD;115SET_LOG_FLAG(large_angle_request, LandDetectorLoggingFlag::LARGE_ANGLE_REQUEST);116117// check for large external disturbance - angle error over 30 degrees118const float angle_error = attitude_control->get_att_error_angle_deg();119bool large_angle_error = (angle_error > LAND_CHECK_ANGLE_ERROR_DEG);120SET_LOG_FLAG(large_angle_error, LandDetectorLoggingFlag::LARGE_ANGLE_ERROR);121122// check that the airframe is not accelerating (not falling or braking after fast forward flight)123bool accel_stationary = (land_accel_ef_filter.get().length() <= LAND_DETECTOR_ACCEL_MAX * land_detector_scalar);124SET_LOG_FLAG(accel_stationary, LandDetectorLoggingFlag::ACCEL_STATIONARY);125126// check that vertical speed is within 1m/s of zero127bool descent_rate_low = fabsf(inertial_nav.get_velocity_z_up_cms()) < 100.0 * LAND_DETECTOR_VEL_Z_MAX * land_detector_scalar;128SET_LOG_FLAG(descent_rate_low, LandDetectorLoggingFlag::DESCENT_RATE_LOW);129130// if we have a healthy rangefinder only allow landing detection below 2 meters131bool rangefinder_check = (!rangefinder_alt_ok() || rangefinder_state.alt_cm_filt.get() < LAND_RANGEFINDER_MIN_ALT_CM);132SET_LOG_FLAG(rangefinder_check, LandDetectorLoggingFlag::RANGEFINDER_BELOW_2M);133134// if we have weight on wheels (WoW) or ambiguous unknown. never no WoW135#if AP_LANDINGGEAR_ENABLED136const bool WoW_check = (landinggear.get_wow_state() == AP_LandingGear::LG_WOW || landinggear.get_wow_state() == AP_LandingGear::LG_WOW_UNKNOWN);137#else138const bool WoW_check = true;139#endif140SET_LOG_FLAG(WoW_check, LandDetectorLoggingFlag::WOW);141142if (motor_at_lower_limit && throttle_mix_at_min && !large_angle_request && !large_angle_error && accel_stationary && descent_rate_low && rangefinder_check && WoW_check) {143// landed criteria met - increment the counter and check if we've triggered144if( land_detector_count < land_trigger_sec*scheduler.get_loop_rate_hz()) {145land_detector_count++;146} else {147set_land_complete(true);148}149} else {150// we've sensed movement up or down so reset land_detector151land_detector_count = 0;152}153}154155set_land_complete_maybe(ap.land_complete || (land_detector_count >= LAND_DETECTOR_MAYBE_TRIGGER_SEC*scheduler.get_loop_rate_hz()));156157#if HAL_LOGGING_ENABLED158// @LoggerMessage: LDET159// @Description: Land Detector State160// @Field: TimeUS: Time since system startup161// @Field: Flags: boolean state flags162// @FieldBitmaskEnum: Flags: Copter::LandDetectorLoggingFlag163// @Field: Count: landing_detector pass count164SET_LOG_FLAG(ap.land_complete, LandDetectorLoggingFlag::LANDED);165SET_LOG_FLAG(ap.land_complete_maybe, LandDetectorLoggingFlag::LANDED_MAYBE);166SET_LOG_FLAG(standby_active, LandDetectorLoggingFlag::STANDBY_ACTIVE);167Log_LDET(logging_flags, land_detector_count);168#undef SET_LOG_FLAG169#endif170}171172#if HAL_LOGGING_ENABLED173void Copter::Log_LDET(uint16_t logging_flags, uint32_t detector_count)174{175// do not log if no change:176if (logging_flags == land_detector.last_logged_flags &&177detector_count == land_detector.last_logged_count) {178return;179}180// do not log more than 50Hz:181const auto now = AP_HAL::millis();182if (now - land_detector.last_logged_ms < 20) {183return;184}185186land_detector.last_logged_count = detector_count;187land_detector.last_logged_flags = logging_flags;188land_detector.last_logged_ms = now;189190AP::logger().WriteStreaming(191"LDET",192"TimeUS," "Flags," "Count",193"s" "-" "-",194"F" "-" "-",195"Q" "H" "I",196AP_HAL::micros64(),197logging_flags,198land_detector_count199);200}201#endif202203// set land_complete flag and disarm motors if disarm-on-land is configured204void Copter::set_land_complete(bool b)205{206// if no change, exit immediately207if( ap.land_complete == b )208return;209210land_detector_count = 0;211212#if HAL_LOGGING_ENABLED213if(b){214AP::logger().Write_Event(LogEvent::LAND_COMPLETE);215} else {216AP::logger().Write_Event(LogEvent::NOT_LANDED);217}218#endif219ap.land_complete = b;220221#if AP_STATS_ENABLED222AP::stats()->set_flying(!b);223#endif224225// tell AHRS flying state226set_likely_flying(!b);227228if (!b) {229// not landed, no further action230return;231}232233// landed; trigger disarm-on-land if configured234if ((g.throttle_behavior & THR_BEHAVE_DISARM_ON_LAND_DETECT) == 0) {235// not configured to disarm on landing detection236return;237}238239if (!motors->armed()) {240// we are not currently armed, so we don't need to disarm:241// n.b. should this be checking vehicle-armed rather than motors-armed?242return;243}244245if (flightmode->has_manual_throttle()) {246// we do not use the landing detector to disarm if the vehicle247// is in e.g. STABILIZE. The normal DISARM_DELAY logic applies.248return;249}250251// the flightmode may not allow disarm on landing. Note that this252// check returns false for the LAND flight mode - it checks the253// landing detector (ap.land_complete) itself.254if (!flightmode->allows_arming(AP_Arming::Method::LANDING)) {255return;256}257258// all checks passed, disarm the vehicle:259arming.disarm(AP_Arming::Method::LANDED);260}261262// set land complete maybe flag263void Copter::set_land_complete_maybe(bool b)264{265// if no change, exit immediately266if (ap.land_complete_maybe == b)267return;268269if (b) {270LOGGER_WRITE_EVENT(LogEvent::LAND_COMPLETE_MAYBE);271}272ap.land_complete_maybe = b;273}274275// sets motors throttle_low_comp value depending upon vehicle state276// low values favour pilot/autopilot throttle over attitude control, high values favour attitude control over throttle277// has no effect when throttle is above hover throttle278void Copter::update_throttle_mix()279{280#if FRAME_CONFIG != HELI_FRAME281// if disarmed or landed prioritise throttle282if (!motors->armed() || ap.land_complete) {283attitude_control->set_throttle_mix_min();284return;285}286287if (flightmode->has_manual_throttle()) {288// manual throttle289if (channel_throttle->get_control_in() <= 0 && air_mode != AirMode::AIRMODE_ENABLED) {290attitude_control->set_throttle_mix_min();291} else {292attitude_control->set_throttle_mix_man();293}294} else {295// autopilot controlled throttle296297// check for aggressive flight requests - requested roll or pitch angle below 15 degrees298const Vector3f angle_target = attitude_control->get_att_target_euler_cd();299bool large_angle_request = angle_target.xy().length() > LAND_CHECK_LARGE_ANGLE_CD;300301// check for large external disturbance - angle error over 30 degrees302const float angle_error = attitude_control->get_att_error_angle_deg();303bool large_angle_error = (angle_error > LAND_CHECK_ANGLE_ERROR_DEG);304305// check for large acceleration - falling or high turbulence306const bool accel_moving = (land_accel_ef_filter.get().length() > LAND_CHECK_ACCEL_MOVING);307308// check for requested descent309bool descent_not_demanded = pos_control->get_vel_desired_cms().z >= 0.0f;310311// check if landing312const bool landing = flightmode->is_landing();313314if (((large_angle_request || get_force_flying()) && !landing) || large_angle_error || accel_moving || descent_not_demanded) {315attitude_control->set_throttle_mix_max(pos_control->get_vel_z_control_ratio());316} else {317attitude_control->set_throttle_mix_min();318}319}320#endif321}322323// helper function for force flying flag324bool Copter::get_force_flying() const325{326#if FRAME_CONFIG == HELI_FRAME327if (attitude_control->get_inverted_flight()) {328return true;329}330#endif331return force_flying;332}333334335