2015-10-24 18:52:39 -03:00
|
|
|
#include "Plane.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
is_flying and crash detection logic
|
|
|
|
*/
|
|
|
|
|
2016-03-28 18:52:30 -03:00
|
|
|
#define CRASH_DETECTION_DELAY_MS 500
|
2015-11-17 20:11:21 -04:00
|
|
|
#define IS_FLYING_IMPACT_TIMER_MS 3000
|
2016-08-22 23:49:25 -03:00
|
|
|
#define GPS_IS_FLYING_SPEED_CMS 150
|
2015-11-17 20:11:21 -04:00
|
|
|
|
2015-10-24 18:52:39 -03:00
|
|
|
/*
|
|
|
|
Do we think we are flying?
|
|
|
|
Probabilistic method where a bool is low-passed and considered a probability.
|
|
|
|
*/
|
|
|
|
void Plane::update_is_flying_5Hz(void)
|
|
|
|
{
|
2018-09-04 19:12:12 -03:00
|
|
|
float aspeed=0;
|
2015-10-24 18:52:39 -03:00
|
|
|
bool is_flying_bool;
|
2015-11-19 23:04:52 -04:00
|
|
|
uint32_t now_ms = AP_HAL::millis();
|
2015-10-24 18:52:39 -03:00
|
|
|
|
2016-11-17 21:07:10 -04:00
|
|
|
uint32_t ground_speed_thresh_cm = (aparm.min_gndspeed_cm > 0) ? ((uint32_t)(aparm.min_gndspeed_cm*0.9f)) : GPS_IS_FLYING_SPEED_CMS;
|
2015-10-24 18:52:39 -03:00
|
|
|
bool gps_confirmed_movement = (gps.status() >= AP_GPS::GPS_OK_FIX_3D) &&
|
|
|
|
(gps.ground_speed_cm() >= ground_speed_thresh_cm);
|
|
|
|
|
|
|
|
// airspeed at least 75% of stall speed?
|
2018-09-04 19:12:12 -03:00
|
|
|
const float airspeed_threshold = MAX(aparm.airspeed_min,2)*0.75f;
|
2020-01-06 20:45:27 -04:00
|
|
|
bool airspeed_movement = ahrs.airspeed_estimate(aspeed) && (aspeed >= airspeed_threshold);
|
2015-10-24 18:52:39 -03:00
|
|
|
|
2018-09-04 19:12:12 -03:00
|
|
|
if (gps.status() < AP_GPS::GPS_OK_FIX_2D && arming.is_armed() && !airspeed_movement && isFlyingProbability > 0.3) {
|
|
|
|
// when flying with no GPS, use the last airspeed estimate to
|
|
|
|
// determine if we think we have airspeed movement. This
|
|
|
|
// prevents the crash detector from triggering when
|
|
|
|
// dead-reckoning under long GPS loss
|
|
|
|
airspeed_movement = aspeed >= airspeed_threshold;
|
|
|
|
}
|
2015-10-24 18:52:39 -03:00
|
|
|
|
2016-05-27 20:25:44 -03:00
|
|
|
if (quadplane.is_flying()) {
|
|
|
|
is_flying_bool = true;
|
|
|
|
|
|
|
|
} else if(arming.is_armed()) {
|
|
|
|
// when armed assuming flying and we need overwhelming evidence that we ARE NOT flying
|
2015-10-24 18:52:39 -03:00
|
|
|
// short drop-outs of GPS are common during flight due to banking which points the antenna in different directions
|
|
|
|
bool gps_lost_recently = (gps.last_fix_time_ms() > 0) && // we have locked to GPS before
|
|
|
|
(gps.status() < AP_GPS::GPS_OK_FIX_2D) && // and it's lost now
|
|
|
|
(now_ms - gps.last_fix_time_ms() < 5000); // but it wasn't that long ago (<5s)
|
|
|
|
|
|
|
|
if ((auto_state.last_flying_ms > 0) && gps_lost_recently) {
|
|
|
|
// we've flown before, remove GPS constraints temporarily and only use airspeed
|
|
|
|
is_flying_bool = airspeed_movement; // moving through the air
|
|
|
|
} else {
|
2020-12-12 22:14:27 -04:00
|
|
|
// Because ahrs.airspeed_estimate can return a continued high value after landing if flying in
|
|
|
|
// strong winds above stall speed it is necessary to include the IMU based movement check.
|
|
|
|
is_flying_bool = (airspeed_movement && !AP::ins().is_still()) || // moving through the air
|
2015-10-24 18:52:39 -03:00
|
|
|
gps_confirmed_movement; // locked and we're moving
|
|
|
|
}
|
|
|
|
|
2019-01-15 13:46:13 -04:00
|
|
|
if (control_mode == &mode_auto) {
|
2015-10-24 18:52:39 -03:00
|
|
|
/*
|
|
|
|
make is_flying() more accurate during various auto modes
|
|
|
|
*/
|
|
|
|
|
2015-11-17 20:11:21 -04:00
|
|
|
// Detect X-axis deceleration for probable ground impacts.
|
|
|
|
// Limit the max probability so it can decay faster. This
|
|
|
|
// will not change the is_flying state, anything above 0.1
|
|
|
|
// is "true", it just allows it to decay faster once we decide we
|
|
|
|
// aren't flying using the normal schemes
|
2015-12-30 20:15:57 -04:00
|
|
|
if (g.crash_accel_threshold == 0) {
|
|
|
|
crash_state.impact_detected = false;
|
2015-12-30 23:23:18 -04:00
|
|
|
} else if (ins.get_accel_peak_hold_neg_x() < -(g.crash_accel_threshold)) {
|
2015-11-17 20:11:21 -04:00
|
|
|
// large deceleration detected, lets lower confidence VERY quickly
|
2015-12-30 20:15:57 -04:00
|
|
|
crash_state.impact_detected = true;
|
|
|
|
crash_state.impact_timer_ms = now_ms;
|
|
|
|
if (isFlyingProbability > 0.2f) {
|
|
|
|
isFlyingProbability = 0.2f;
|
2015-11-17 20:11:21 -04:00
|
|
|
}
|
2015-12-30 20:15:57 -04:00
|
|
|
} else if (crash_state.impact_detected &&
|
2015-11-17 20:11:21 -04:00
|
|
|
(now_ms - crash_state.impact_timer_ms > IS_FLYING_IMPACT_TIMER_MS)) {
|
2015-12-30 20:15:57 -04:00
|
|
|
// no impacts seen in a while, clear the flag so we stop clipping isFlyingProbability
|
2015-11-17 20:11:21 -04:00
|
|
|
crash_state.impact_detected = false;
|
|
|
|
}
|
|
|
|
|
2015-10-24 18:52:39 -03:00
|
|
|
switch (flight_stage)
|
|
|
|
{
|
2016-12-13 22:20:57 -04:00
|
|
|
case AP_Vehicle::FixedWing::FLIGHT_TAKEOFF:
|
2015-10-24 18:52:39 -03:00
|
|
|
break;
|
|
|
|
|
2016-12-13 22:20:57 -04:00
|
|
|
case AP_Vehicle::FixedWing::FLIGHT_NORMAL:
|
2015-11-17 20:11:21 -04:00
|
|
|
if (in_preLaunch_flight_stage()) {
|
|
|
|
// while on the ground, an uncalibrated airspeed sensor can drift to 7m/s so
|
|
|
|
// ensure we aren't showing a false positive.
|
|
|
|
is_flying_bool = false;
|
|
|
|
crash_state.is_crashed = false;
|
|
|
|
auto_state.started_flying_in_auto_ms = 0;
|
|
|
|
}
|
2015-10-24 18:52:39 -03:00
|
|
|
break;
|
|
|
|
|
2016-12-13 22:20:57 -04:00
|
|
|
case AP_Vehicle::FixedWing::FLIGHT_VTOL:
|
2016-01-01 02:36:41 -04:00
|
|
|
// TODO: detect ground impacts
|
|
|
|
break;
|
|
|
|
|
2017-11-02 19:12:07 -03:00
|
|
|
case AP_Vehicle::FixedWing::FLIGHT_LAND:
|
2017-11-07 02:21:37 -04:00
|
|
|
if (landing.is_on_approach() && auto_state.sink_rate > 0.2f) {
|
2017-11-02 19:12:07 -03:00
|
|
|
is_flying_bool = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2016-12-13 23:18:48 -04:00
|
|
|
case AP_Vehicle::FixedWing::FLIGHT_ABORT_LAND:
|
2015-11-17 20:11:21 -04:00
|
|
|
if (auto_state.sink_rate < -0.5f) {
|
|
|
|
// steep climb
|
|
|
|
is_flying_bool = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2015-10-24 18:52:39 -03:00
|
|
|
} // switch
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// when disarmed assume not flying and need overwhelming evidence that we ARE flying
|
|
|
|
is_flying_bool = airspeed_movement && gps_confirmed_movement;
|
|
|
|
|
2017-01-10 03:42:57 -04:00
|
|
|
if ((flight_stage == AP_Vehicle::FixedWing::FLIGHT_TAKEOFF) || landing.is_flaring()) {
|
2015-10-24 18:52:39 -03:00
|
|
|
is_flying_bool = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-17 20:11:21 -04:00
|
|
|
if (!crash_state.impact_detected || !is_flying_bool) {
|
2015-12-30 20:15:57 -04:00
|
|
|
// when impact is detected, enforce a clip. Only allow isFlyingProbability to go down, not up.
|
2015-11-17 20:11:21 -04:00
|
|
|
// low-pass the result.
|
|
|
|
// coef=0.15f @ 5Hz takes 3.0s to go from 100% down to 10% (or 0% up to 90%)
|
|
|
|
isFlyingProbability = (0.85f * isFlyingProbability) + (0.15f * (float)is_flying_bool);
|
|
|
|
}
|
2015-10-24 18:52:39 -03:00
|
|
|
|
|
|
|
/*
|
|
|
|
update last_flying_ms so we always know how long we have not
|
|
|
|
been flying for. This helps for crash detection and auto-disarm
|
|
|
|
*/
|
|
|
|
bool new_is_flying = is_flying();
|
|
|
|
|
|
|
|
// we are flying, note the time
|
|
|
|
if (new_is_flying) {
|
|
|
|
|
|
|
|
auto_state.last_flying_ms = now_ms;
|
|
|
|
|
|
|
|
if (!previous_is_flying) {
|
|
|
|
// just started flying in any mode
|
|
|
|
started_flying_ms = now_ms;
|
|
|
|
}
|
|
|
|
|
2019-01-15 13:46:13 -04:00
|
|
|
if ((control_mode == &mode_auto) &&
|
2015-10-24 18:52:39 -03:00
|
|
|
((auto_state.started_flying_in_auto_ms == 0) || !previous_is_flying) ) {
|
|
|
|
|
|
|
|
// We just started flying, note that time also
|
|
|
|
auto_state.started_flying_in_auto_ms = now_ms;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
previous_is_flying = new_is_flying;
|
2020-09-19 05:38:20 -03:00
|
|
|
#if HAL_ADSB_ENABLED
|
2016-07-10 21:23:08 -03:00
|
|
|
adsb.set_is_flying(new_is_flying);
|
2020-09-19 05:38:20 -03:00
|
|
|
#endif
|
2019-02-11 10:20:38 -04:00
|
|
|
#if PARACHUTE == ENABLED
|
|
|
|
parachute.set_is_flying(new_is_flying);
|
|
|
|
#endif
|
2018-02-28 08:22:16 -04:00
|
|
|
#if STATS_ENABLED == ENABLED
|
2016-10-25 22:24:41 -03:00
|
|
|
g2.stats.set_flying(new_is_flying);
|
2018-02-28 08:22:16 -04:00
|
|
|
#endif
|
2019-03-01 02:31:08 -04:00
|
|
|
AP_Notify::flags.flying = new_is_flying;
|
2015-10-24 18:52:39 -03:00
|
|
|
|
2016-02-07 12:39:41 -04:00
|
|
|
crash_detection_update();
|
|
|
|
|
2017-10-23 21:39:45 -03:00
|
|
|
Log_Write_Status();
|
2017-06-19 00:16:43 -03:00
|
|
|
|
|
|
|
// tell AHRS flying state
|
2020-01-06 19:06:13 -04:00
|
|
|
set_likely_flying(new_is_flying);
|
2015-10-24 18:52:39 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
return true if we think we are flying. This is a probabilistic
|
|
|
|
estimate, and needs to be used very carefully. Each use case needs
|
|
|
|
to be thought about individually.
|
|
|
|
*/
|
|
|
|
bool Plane::is_flying(void)
|
|
|
|
{
|
|
|
|
if (hal.util->get_soft_armed()) {
|
2016-04-22 07:20:06 -03:00
|
|
|
if (quadplane.is_flying_vtol()) {
|
|
|
|
return true;
|
|
|
|
}
|
2015-10-24 18:52:39 -03:00
|
|
|
// when armed, assume we're flying unless we probably aren't
|
|
|
|
return (isFlyingProbability >= 0.1f);
|
|
|
|
}
|
|
|
|
|
|
|
|
// when disarmed, assume we're not flying unless we probably are
|
|
|
|
return (isFlyingProbability >= 0.9f);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine if we have crashed
|
|
|
|
*/
|
|
|
|
void Plane::crash_detection_update(void)
|
|
|
|
{
|
2019-01-15 13:46:13 -04:00
|
|
|
if (control_mode != &mode_auto || !aparm.crash_detection_enable)
|
2015-10-24 18:52:39 -03:00
|
|
|
{
|
|
|
|
// crash detection is only available in AUTO mode
|
|
|
|
crash_state.debounce_timer_ms = 0;
|
2015-11-17 20:11:21 -04:00
|
|
|
crash_state.is_crashed = false;
|
2015-10-24 18:52:39 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-19 23:04:52 -04:00
|
|
|
uint32_t now_ms = AP_HAL::millis();
|
2015-10-24 18:52:39 -03:00
|
|
|
bool crashed_near_land_waypoint = false;
|
|
|
|
bool crashed = false;
|
|
|
|
bool been_auto_flying = (auto_state.started_flying_in_auto_ms > 0) &&
|
|
|
|
(now_ms - auto_state.started_flying_in_auto_ms >= 2500);
|
|
|
|
|
2016-03-28 18:52:30 -03:00
|
|
|
if (!is_flying() && arming.is_armed())
|
2015-10-24 18:52:39 -03:00
|
|
|
{
|
2017-01-10 03:42:57 -04:00
|
|
|
if (landing.is_expecting_impact()) {
|
2015-10-24 18:52:39 -03:00
|
|
|
// We should be nice and level-ish in this flight stage. If not, we most
|
|
|
|
// likely had a crazy landing. Throttle is inhibited already at the flare
|
|
|
|
// but go ahead and notify GCS and perform any additional post-crash actions.
|
|
|
|
// Declare a crash if we are oriented more that 60deg in pitch or roll
|
2015-11-17 20:11:21 -04:00
|
|
|
if (!crash_state.checkedHardLanding && // only check once
|
2016-03-28 18:52:30 -03:00
|
|
|
been_auto_flying &&
|
|
|
|
(labs(ahrs.roll_sensor) > 6000 || labs(ahrs.pitch_sensor) > 6000)) {
|
2018-02-09 15:40:12 -04:00
|
|
|
|
2015-10-24 18:52:39 -03:00
|
|
|
crashed = true;
|
|
|
|
|
|
|
|
// did we "crash" within 75m of the landing location? Probably just a hard landing
|
|
|
|
crashed_near_land_waypoint =
|
2019-02-24 20:11:45 -04:00
|
|
|
current_loc.get_distance(mission.get_current_nav_cmd().content.location) < 75;
|
2015-10-24 18:52:39 -03:00
|
|
|
|
|
|
|
// trigger hard landing event right away, or never again. This inhibits a false hard landing
|
|
|
|
// event when, for example, a minute after a good landing you pick the plane up and
|
|
|
|
// this logic is still running and detects the plane is on its side as you carry it.
|
2018-02-09 17:00:24 -04:00
|
|
|
crash_state.debounce_timer_ms = now_ms;
|
2018-02-09 15:40:12 -04:00
|
|
|
crash_state.debounce_time_total_ms = 0; // no debounce
|
2015-10-24 18:52:39 -03:00
|
|
|
}
|
2015-11-17 20:11:21 -04:00
|
|
|
|
|
|
|
crash_state.checkedHardLanding = true;
|
|
|
|
|
2017-01-10 03:42:57 -04:00
|
|
|
} else if (landing.is_on_approach()) {
|
|
|
|
// when altitude gets low, we automatically flare so ground crashes
|
|
|
|
// most likely can not be triggered from here. However,
|
|
|
|
// a crash into a tree would be caught here.
|
|
|
|
if (been_auto_flying) {
|
|
|
|
crashed = true;
|
|
|
|
crash_state.debounce_time_total_ms = CRASH_DETECTION_DELAY_MS;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
switch (flight_stage)
|
|
|
|
{
|
|
|
|
case AP_Vehicle::FixedWing::FLIGHT_TAKEOFF:
|
|
|
|
if (g.takeoff_throttle_min_accel > 0 &&
|
|
|
|
!throttle_suppressed) {
|
|
|
|
// if you have an acceleration holding back throttle, but you met the
|
|
|
|
// accel threshold but still not fying, then you either shook/hit the
|
|
|
|
// plane or it was a failed launch.
|
|
|
|
crashed = true;
|
|
|
|
crash_state.debounce_time_total_ms = CRASH_DETECTION_DELAY_MS;
|
|
|
|
}
|
|
|
|
// TODO: handle auto missions without NAV_TAKEOFF mission cmd
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AP_Vehicle::FixedWing::FLIGHT_NORMAL:
|
|
|
|
if (!in_preLaunch_flight_stage() && been_auto_flying) {
|
|
|
|
crashed = true;
|
|
|
|
crash_state.debounce_time_total_ms = CRASH_DETECTION_DELAY_MS;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AP_Vehicle::FixedWing::FLIGHT_VTOL:
|
|
|
|
// we need a totally new method for this
|
|
|
|
crashed = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
} // switch
|
|
|
|
}
|
2015-10-24 18:52:39 -03:00
|
|
|
} else {
|
2015-11-17 20:11:21 -04:00
|
|
|
crash_state.checkedHardLanding = false;
|
2015-10-24 18:52:39 -03:00
|
|
|
}
|
|
|
|
|
2018-09-05 19:43:41 -03:00
|
|
|
// if we have no GPS lock and we don't have a functional airspeed
|
|
|
|
// sensor then don't do crash detection
|
|
|
|
if (gps.status() < AP_GPS::GPS_OK_FIX_3D && (!airspeed.use() || !airspeed.healthy())) {
|
|
|
|
crashed = false;
|
|
|
|
}
|
|
|
|
|
2015-10-24 18:52:39 -03:00
|
|
|
if (!crashed) {
|
|
|
|
// reset timer
|
|
|
|
crash_state.debounce_timer_ms = 0;
|
|
|
|
|
|
|
|
} else if (crash_state.debounce_timer_ms == 0) {
|
|
|
|
// start timer
|
|
|
|
crash_state.debounce_timer_ms = now_ms;
|
|
|
|
|
2016-03-28 18:52:30 -03:00
|
|
|
} else if ((now_ms - crash_state.debounce_timer_ms >= crash_state.debounce_time_total_ms) && !crash_state.is_crashed) {
|
2015-10-24 18:52:39 -03:00
|
|
|
crash_state.is_crashed = true;
|
2020-02-23 04:29:23 -04:00
|
|
|
if (aparm.crash_detection_enable & CRASH_DETECT_ACTION_BITMASK_DISARM) {
|
|
|
|
arming.disarm(AP_Arming::Method::CRASH);
|
2015-10-24 18:52:39 -03:00
|
|
|
}
|
2020-02-23 04:29:23 -04:00
|
|
|
if (crashed_near_land_waypoint) {
|
|
|
|
gcs().send_text(MAV_SEVERITY_CRITICAL, "Hard landing detected");
|
|
|
|
} else {
|
|
|
|
gcs().send_text(MAV_SEVERITY_EMERGENCY, "Crash detected");
|
2015-10-24 18:52:39 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-17 20:11:21 -04:00
|
|
|
/*
|
|
|
|
* return true if we are in a pre-launch phase of an auto-launch, typically used in bungee launches
|
|
|
|
*/
|
2019-05-04 06:56:07 -03:00
|
|
|
bool Plane::in_preLaunch_flight_stage(void)
|
|
|
|
{
|
|
|
|
if (control_mode == &mode_takeoff && throttle_suppressed) {
|
|
|
|
return true;
|
|
|
|
}
|
2019-01-15 13:46:13 -04:00
|
|
|
return (control_mode == &mode_auto &&
|
2015-11-17 20:11:21 -04:00
|
|
|
throttle_suppressed &&
|
2016-12-13 22:20:57 -04:00
|
|
|
flight_stage == AP_Vehicle::FixedWing::FLIGHT_NORMAL &&
|
2017-10-29 03:31:09 -03:00
|
|
|
mission.get_current_nav_cmd().id == MAV_CMD_NAV_TAKEOFF &&
|
|
|
|
!quadplane.is_vtol_takeoff(mission.get_current_nav_cmd().id));
|
2015-11-17 20:11:21 -04:00
|
|
|
}
|