Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/ArduPlane/is_flying.cpp
9492 views
1
#include "Plane.h"
2
3
#include <AP_Stats/AP_Stats.h> // statistics library
4
5
/*
6
is_flying and crash detection logic
7
*/
8
9
#define CRASH_DETECTION_DELAY_MS 500 // milliseconds
10
#define IS_FLYING_IMPACT_TIMER_MS 3000 // milliseconds
11
12
#define GPS_IS_FLYING_SPEED_MS 1.5 // metres/second
13
14
/*
15
Do we think we are flying?
16
Probabilistic method where a bool is low-passed and considered a probability.
17
*/
18
void Plane::update_is_flying_5Hz(void)
19
{
20
float aspeed=0;
21
bool is_flying_bool = false;
22
uint32_t now_ms = AP_HAL::millis();
23
24
const float ground_speed_thresh_ms = (aparm.min_groundspeed > 0) ? (aparm.min_groundspeed*0.9) : GPS_IS_FLYING_SPEED_MS;
25
bool gps_confirmed_movement = (gps.status() >= AP_GPS::GPS_OK_FIX_3D) &&
26
(gps.ground_speed() >= ground_speed_thresh_ms);
27
28
// airspeed at least 75% of stall speed?
29
const float airspeed_threshold = MAX(aparm.airspeed_min,2)*0.75f;
30
bool airspeed_movement = ahrs.airspeed_EAS(aspeed) && (aspeed >= airspeed_threshold);
31
32
if (gps.status() < AP_GPS::GPS_OK_FIX_2D && arming.is_armed() && !airspeed_movement && isFlyingProbability > 0.3) {
33
// when flying with no GPS, use the last airspeed estimate to
34
// determine if we think we have airspeed movement. This
35
// prevents the crash detector from triggering when
36
// dead-reckoning under long GPS loss
37
airspeed_movement = aspeed >= airspeed_threshold;
38
}
39
40
#if HAL_QUADPLANE_ENABLED
41
is_flying_bool = quadplane.is_flying();
42
#endif
43
if (is_flying_bool) {
44
// no need to look further
45
} else if(arming.is_armed()) {
46
// when armed assuming flying and we need overwhelming evidence that we ARE NOT flying
47
// short drop-outs of GPS are common during flight due to banking which points the antenna in different directions
48
bool gps_lost_recently = (gps.last_fix_time_ms() > 0) && // we have locked to GPS before
49
(gps.status() < AP_GPS::GPS_OK_FIX_2D) && // and it's lost now
50
(now_ms - gps.last_fix_time_ms() < 5000); // but it wasn't that long ago (<5s)
51
52
if ((auto_state.last_flying_ms > 0) && gps_lost_recently) {
53
// we've flown before, remove GPS constraints temporarily and only use airspeed
54
is_flying_bool = airspeed_movement; // moving through the air
55
} else {
56
// Because ahrs.airspeed_estimate can return a continued high value after landing if flying in
57
// strong winds above stall speed it is necessary to include the IMU based movement check.
58
is_flying_bool = (airspeed_movement && !AP::ins().is_still()) || // moving through the air
59
gps_confirmed_movement; // locked and we're moving
60
}
61
62
if (control_mode == &mode_auto) {
63
/*
64
make is_flying() more accurate during various auto modes
65
*/
66
67
// Detect X-axis deceleration for probable ground impacts.
68
// Limit the max probability so it can decay faster. This
69
// will not change the is_flying state, anything above 0.1
70
// is "true", it just allows it to decay faster once we decide we
71
// aren't flying using the normal schemes
72
if (g.crash_accel_threshold == 0) {
73
crash_state.impact_detected = false;
74
} else if (ins.get_accel_peak_hold_neg_x() < -(g.crash_accel_threshold)) {
75
// large deceleration detected, lets lower confidence VERY quickly
76
crash_state.impact_detected = true;
77
crash_state.impact_timer_ms = now_ms;
78
if (isFlyingProbability > 0.2f) {
79
isFlyingProbability = 0.2f;
80
}
81
} else if (crash_state.impact_detected &&
82
(now_ms - crash_state.impact_timer_ms > IS_FLYING_IMPACT_TIMER_MS)) {
83
// no impacts seen in a while, clear the flag so we stop clipping isFlyingProbability
84
crash_state.impact_detected = false;
85
}
86
87
switch (flight_stage)
88
{
89
case AP_FixedWing::FlightStage::TAKEOFF:
90
break;
91
92
case AP_FixedWing::FlightStage::NORMAL:
93
if (in_preLaunch_flight_stage()) {
94
// while on the ground, an uncalibrated airspeed sensor can drift to 7m/s so
95
// ensure we aren't showing a false positive.
96
is_flying_bool = false;
97
crash_state.is_crashed = false;
98
auto_state.started_flying_in_auto_ms = 0;
99
}
100
break;
101
102
case AP_FixedWing::FlightStage::VTOL:
103
// TODO: detect ground impacts
104
break;
105
106
case AP_FixedWing::FlightStage::LAND:
107
if (landing.is_on_approach() && auto_state.sink_rate > 0.2f) {
108
is_flying_bool = true;
109
}
110
break;
111
112
case AP_FixedWing::FlightStage::ABORT_LANDING:
113
if (auto_state.sink_rate < -0.5f) {
114
// steep climb
115
is_flying_bool = true;
116
}
117
break;
118
119
default:
120
break;
121
} // switch
122
}
123
} else {
124
// when disarmed assume not flying and need overwhelming evidence that we ARE flying
125
is_flying_bool = airspeed_movement && gps_confirmed_movement;
126
127
if ((flight_stage == AP_FixedWing::FlightStage::TAKEOFF) || landing.is_flaring()) {
128
is_flying_bool = false;
129
}
130
}
131
132
if (!crash_state.impact_detected || !is_flying_bool) {
133
// when impact is detected, enforce a clip. Only allow isFlyingProbability to go down, not up.
134
// low-pass the result.
135
// coef=0.15f @ 5Hz takes 3.0s to go from 100% down to 10% (or 0% up to 90%)
136
isFlyingProbability = (0.85f * isFlyingProbability) + (0.15f * (float)is_flying_bool);
137
}
138
139
/*
140
update last_flying_ms so we always know how long we have not
141
been flying for. This helps for crash detection and auto-disarm
142
*/
143
bool new_is_flying = is_flying();
144
145
// we are flying, note the time
146
if (new_is_flying) {
147
148
auto_state.last_flying_ms = now_ms;
149
150
if (!previous_is_flying) {
151
// just started flying in any mode
152
started_flying_ms = now_ms;
153
}
154
155
if ((control_mode == &mode_auto) &&
156
((auto_state.started_flying_in_auto_ms == 0) || !previous_is_flying) ) {
157
158
// We just started flying, note that time also
159
auto_state.started_flying_in_auto_ms = now_ms;
160
}
161
}
162
previous_is_flying = new_is_flying;
163
#if HAL_ADSB_ENABLED
164
adsb.set_is_flying(new_is_flying);
165
#endif
166
#if HAL_PARACHUTE_ENABLED
167
parachute.set_is_flying(new_is_flying);
168
#endif
169
#if AP_STATS_ENABLED
170
AP::stats()->set_flying(new_is_flying);
171
#endif
172
AP_Notify::flags.flying = new_is_flying;
173
174
crash_detection_update();
175
176
#if HAL_LOGGING_ENABLED
177
Log_Write_Status();
178
#endif
179
180
// tell AHRS flying state
181
set_likely_flying(new_is_flying);
182
183
// conservative ground mode value for rate D suppression
184
ground_mode = !is_flying() && !arming.is_armed_and_safety_off();
185
}
186
187
/*
188
return true if we think we are flying. This is a probabilistic
189
estimate, and needs to be used very carefully. Each use case needs
190
to be thought about individually.
191
*/
192
bool Plane::is_flying(void)
193
{
194
if (arming.is_armed_and_safety_off()) {
195
#if HAL_QUADPLANE_ENABLED
196
if (quadplane.is_flying_vtol()) {
197
return true;
198
}
199
#endif
200
// when armed, assume we're flying unless we probably aren't
201
return (isFlyingProbability >= 0.1f);
202
}
203
204
// when disarmed, assume we're not flying unless we probably are
205
return (isFlyingProbability >= 0.9f);
206
}
207
208
/*
209
* Determine if we have crashed
210
*/
211
void Plane::crash_detection_update(void)
212
{
213
if (control_mode != &mode_auto || !aparm.crash_detection_enable)
214
{
215
// crash detection is only available in AUTO mode
216
crash_state.debounce_timer_ms = 0;
217
crash_state.is_crashed = false;
218
return;
219
}
220
221
uint32_t now_ms = AP_HAL::millis();
222
bool crashed_near_land_waypoint = false;
223
bool crashed = false;
224
bool been_auto_flying = (auto_state.started_flying_in_auto_ms > 0) &&
225
(now_ms - auto_state.started_flying_in_auto_ms >= 2500);
226
227
if (!is_flying() && arming.is_armed())
228
{
229
if (landing.is_expecting_impact()) {
230
// We should be nice and level-ish in this flight stage. If not, we most
231
// likely had a crazy landing. Throttle is inhibited already at the flare
232
// but go ahead and notify GCS and perform any additional post-crash actions.
233
// Declare a crash if we are oriented more that 60deg in pitch or roll
234
if (!crash_state.checkedHardLanding && // only check once
235
been_auto_flying &&
236
(fabsf(ahrs.get_roll_deg()) > 60 || fabsf(ahrs.get_pitch_deg()) > 60)) {
237
crashed = true;
238
239
// did we "crash" within 75m of the landing location? Probably just a hard landing
240
crashed_near_land_waypoint =
241
current_loc.get_distance(mission.get_current_nav_cmd().content.location) < 75;
242
243
// trigger hard landing event right away, or never again. This inhibits a false hard landing
244
// event when, for example, a minute after a good landing you pick the plane up and
245
// this logic is still running and detects the plane is on its side as you carry it.
246
crash_state.debounce_timer_ms = now_ms;
247
crash_state.debounce_time_total_ms = 0; // no debounce
248
}
249
250
crash_state.checkedHardLanding = true;
251
252
} else if (landing.is_on_approach()) {
253
// when altitude gets low, we automatically flare so ground crashes
254
// most likely can not be triggered from here. However,
255
// a crash into a tree would be caught here.
256
if (been_auto_flying) {
257
crashed = true;
258
crash_state.debounce_time_total_ms = CRASH_DETECTION_DELAY_MS;
259
}
260
261
} else {
262
switch (flight_stage)
263
{
264
case AP_FixedWing::FlightStage::TAKEOFF:
265
if (g2.takeoff_throttle_accel_count == 1 && g.takeoff_throttle_min_accel > 0 &&
266
!throttle_suppressed) {
267
// if launching requires a single acceleration event and it
268
// has already happened but the aircraft is still not
269
// flying, then you either shook/hit the plane or it was a
270
// failed launch.
271
crashed = true;
272
crash_state.debounce_time_total_ms = CRASH_DETECTION_DELAY_MS;
273
}
274
// TODO: handle auto missions without NAV_TAKEOFF mission cmd
275
break;
276
277
case AP_FixedWing::FlightStage::NORMAL:
278
if (!in_preLaunch_flight_stage() && been_auto_flying) {
279
crashed = true;
280
crash_state.debounce_time_total_ms = CRASH_DETECTION_DELAY_MS;
281
}
282
break;
283
284
case AP_FixedWing::FlightStage::VTOL:
285
// we need a totally new method for this
286
crashed = false;
287
break;
288
289
default:
290
break;
291
} // switch
292
}
293
} else {
294
crash_state.checkedHardLanding = false;
295
}
296
297
// if we have no GPS lock and we don't have a functional airspeed
298
// sensor then don't do crash detection
299
if (gps.status() < AP_GPS::GPS_OK_FIX_3D) {
300
#if AP_AIRSPEED_ENABLED
301
if (!airspeed.use() || !airspeed.healthy()) {
302
crashed = false;
303
}
304
#else
305
crashed = false;
306
#endif
307
}
308
309
if (!crashed) {
310
// reset timer
311
crash_state.debounce_timer_ms = 0;
312
313
} else if (crash_state.debounce_timer_ms == 0) {
314
// start timer
315
crash_state.debounce_timer_ms = now_ms;
316
317
} else if ((now_ms - crash_state.debounce_timer_ms >= crash_state.debounce_time_total_ms) && !crash_state.is_crashed) {
318
crash_state.is_crashed = true;
319
if (aparm.crash_detection_enable & CRASH_DETECT_ACTION_BITMASK_DISARM) {
320
arming.disarm(AP_Arming::Method::CRASH);
321
}
322
if (crashed_near_land_waypoint) {
323
gcs().send_text(MAV_SEVERITY_CRITICAL, "Hard landing detected");
324
} else {
325
gcs().send_text(MAV_SEVERITY_EMERGENCY, "Crash detected");
326
}
327
}
328
}
329
330
/*
331
* return true if we are in a pre-launch phase of an auto-launch, typically used in bungee launches
332
*/
333
bool Plane::in_preLaunch_flight_stage(void)
334
{
335
if (control_mode == &mode_takeoff && throttle_suppressed) {
336
return true;
337
}
338
#if HAL_QUADPLANE_ENABLED
339
if (quadplane.is_vtol_takeoff(mission.get_current_nav_cmd().id)) {
340
return false;
341
}
342
#endif
343
return (control_mode == &mode_auto &&
344
throttle_suppressed &&
345
flight_stage == AP_FixedWing::FlightStage::NORMAL &&
346
mission.get_current_nav_cmd().id == MAV_CMD_NAV_TAKEOFF);
347
}
348
349