2 ******************************************************************************
4 * @file vtollandfsm.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2018
6 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2015.
7 * @brief This landing state machine is a helper state machine to the
9 * @see The GNU Public License (GPL) Version 3
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include <openpilot.h>
34 #include <CoordinateConversions.h>
35 #include <sin_lookup.h>
36 #include <pathdesired.h>
39 #include <sanitycheck.h>
41 #include <homelocation.h>
42 #include <accelstate.h>
43 #include <vtolpathfollowersettings.h>
44 #include <flightstatus.h>
45 #include <flightmodesettings.h>
46 #include <pathstatus.h>
47 #include <positionstate.h>
48 #include <velocitystate.h>
49 #include <velocitydesired.h>
50 #include <stabilizationdesired.h>
51 #include <airspeedstate.h>
52 #include <attitudestate.h>
53 #include <takeofflocation.h>
54 #include <poilocation.h>
55 #include <manualcontrolcommand.h>
56 #include <systemsettings.h>
57 #include <stabilizationbank.h>
58 #include <stabilizationdesired.h>
59 #include <vtolselftuningstats.h>
60 #include <statusvtolland.h>
61 #include <pathsummary.h>
65 #include <vtollandfsm.h>
69 #define TIMER_COUNT_PER_SECOND (1000 / vtolPathFollowerSettings->UpdatePeriod)
70 #define MIN_LANDRATE 0.1f
71 #define MAX_LANDRATE 0.6f
72 #define LOW_ALT_DESCENT_REDUCTION_FACTOR 0.7f // TODO Need to make the transition smooth
73 #define LANDRATE_LOWLIMIT_FACTOR 0.5f
74 #define LANDRATE_HILIMIT_FACTOR 1.5f
75 #define TIMEOUT_INIT_ALTHOLD (3 * TIMER_COUNT_PER_SECOND)
76 #define TIMEOUT_WTG_FOR_DESCENTRATE (10 * TIMER_COUNT_PER_SECOND)
77 #define WTG_FOR_DESCENTRATE_COUNT_LIMIT 10
78 #define TIMEOUT_AT_DESCENTRATE 10
79 #define TIMEOUT_GROUNDEFFECT (1 * TIMER_COUNT_PER_SECOND)
80 #define TIMEOUT_THRUSTDOWN (2 * TIMER_COUNT_PER_SECOND)
81 #define LANDING_PID_SCALAR_P 2.0f
82 #define LANDING_PID_SCALAR_I 10.0f
83 #define LANDING_SLOWDOWN_HEIGHT -5.0f
84 #define BOUNCE_VELOCITY_TRIGGER_LIMIT -0.3f
85 #define BOUNCE_ACCELERATION_TRIGGER_LIMIT -9.0f // -6.0 found to be too sensitive
86 #define BOUNCE_TRIGGER_COUNT 4
87 #define GROUNDEFFECT_SLOWDOWN_FACTOR 0.3f
88 #define GROUNDEFFECT_SLOWDOWN_COUNT 4
90 VtolLandFSM::PathFollowerFSM_LandStateHandler_T
VtolLandFSM::sLandStateTable
[LAND_STATE_SIZE
] = {
91 [LAND_STATE_INACTIVE
] = { .setup
= &VtolLandFSM::setup_inactive
, .run
= 0 },
92 [LAND_STATE_INIT_ALTHOLD
] = { .setup
= &VtolLandFSM::setup_init_althold
, .run
= &VtolLandFSM::run_init_althold
},
93 [LAND_STATE_WTG_FOR_DESCENTRATE
] = { .setup
= &VtolLandFSM::setup_wtg_for_descentrate
, .run
= &VtolLandFSM::run_wtg_for_descentrate
},
94 [LAND_STATE_AT_DESCENTRATE
] = { .setup
= &VtolLandFSM::setup_at_descentrate
, .run
= &VtolLandFSM::run_at_descentrate
},
95 [LAND_STATE_WTG_FOR_GROUNDEFFECT
] = { .setup
= &VtolLandFSM::setup_wtg_for_groundeffect
, .run
= &VtolLandFSM::run_wtg_for_groundeffect
},
96 [LAND_STATE_GROUNDEFFECT
] = { .setup
= &VtolLandFSM::setup_groundeffect
, .run
= &VtolLandFSM::run_groundeffect
},
97 [LAND_STATE_THRUSTDOWN
] = { .setup
= &VtolLandFSM::setup_thrustdown
, .run
= &VtolLandFSM::run_thrustdown
},
98 [LAND_STATE_THRUSTOFF
] = { .setup
= &VtolLandFSM::setup_thrustoff
, .run
= &VtolLandFSM::run_thrustoff
},
99 [LAND_STATE_DISARMED
] = { .setup
= &VtolLandFSM::setup_disarmed
, .run
= &VtolLandFSM::run_disarmed
}
102 // pointer to a singleton instance
103 VtolLandFSM
*VtolLandFSM::p_inst
= 0;
106 VtolLandFSM::VtolLandFSM()
107 : mLandData(0), vtolPathFollowerSettings(0), pathDesired(0), flightStatus(0)
113 // Public API methods
115 * Initialise the module, called on startup
116 * \returns 0 on success or -1 if initialisation failed
118 int32_t VtolLandFSM::Initialize(VtolPathFollowerSettingsData
*ptr_vtolPathFollowerSettings
,
119 PathDesiredData
*ptr_pathDesired
,
120 FlightStatusData
*ptr_flightStatus
)
122 PIOS_Assert(ptr_vtolPathFollowerSettings
);
123 PIOS_Assert(ptr_pathDesired
);
124 PIOS_Assert(ptr_flightStatus
);
126 if (mLandData
== 0) {
127 mLandData
= (VtolLandFSMData_T
*)pios_malloc(sizeof(VtolLandFSMData_T
));
128 PIOS_Assert(mLandData
);
130 memset(mLandData
, 0, sizeof(VtolLandFSMData_T
));
131 vtolPathFollowerSettings
= ptr_vtolPathFollowerSettings
;
132 pathDesired
= ptr_pathDesired
;
133 flightStatus
= ptr_flightStatus
;
139 void VtolLandFSM::Inactive(void)
141 memset(mLandData
, 0, sizeof(VtolLandFSMData_T
));
145 // Initialise the FSM
146 void VtolLandFSM::initFSM(void)
148 if (vtolPathFollowerSettings
!= 0) {
149 setState(STATUSVTOLLAND_STATE_INACTIVE
, STATUSVTOLLAND_STATEEXITREASON_NONE
);
151 mLandData
->currentState
= STATUSVTOLLAND_STATE_INACTIVE
;
155 void VtolLandFSM::Activate()
157 memset(mLandData
, 0, sizeof(VtolLandFSMData_T
));
158 mLandData
->currentState
= STATUSVTOLLAND_STATE_INACTIVE
;
159 mLandData
->flLowAltitude
= false;
160 mLandData
->flAltitudeHold
= false;
161 mLandData
->fsmLandStatus
.averageDescentRate
= MIN_LANDRATE
;
162 mLandData
->fsmLandStatus
.averageDescentThrust
= vtolPathFollowerSettings
->ThrustLimits
.Neutral
;
163 mLandData
->fsmLandStatus
.calculatedNeutralThrust
= vtolPathFollowerSettings
->ThrustLimits
.Neutral
;
164 mLandData
->boundThrustMin
= vtolPathFollowerSettings
->ThrustLimits
.Min
;
165 mLandData
->boundThrustMax
= vtolPathFollowerSettings
->ThrustLimits
.Max
;
166 TakeOffLocationGet(&(mLandData
->takeOffLocation
));
167 mLandData
->fsmLandStatus
.AltitudeAtState
[STATUSVTOLLAND_STATE_INACTIVE
] = 0.0f
;
170 if (pathDesired
->Mode
== PATHDESIRED_MODE_LAND
) {
171 #ifndef DEBUG_GROUNDIMPACT
172 setState(STATUSVTOLLAND_STATE_INITALTHOLD
, STATUSVTOLLAND_STATEEXITREASON_NONE
);
174 setState(STATUSVTOLLAND_STATE_WTGFORGROUNDEFFECT
, STATUSVTOLLAND_STATEEXITREASON_NONE
);
177 // move to error state and callback to position hold
178 setState(STATUSVTOLLAND_STATE_DISARMED
, STATUSVTOLLAND_STATEEXITREASON_NONE
);
182 PathFollowerFSMState_T
VtolLandFSM::GetCurrentState(void)
184 switch (mLandData
->currentState
) {
185 case STATUSVTOLLAND_STATE_INACTIVE
:
186 return PFFSM_STATE_INACTIVE
;
189 case STATUSVTOLLAND_STATE_DISARMED
:
190 return PFFSM_STATE_DISARMED
;
194 return PFFSM_STATE_ACTIVE
;
200 void VtolLandFSM::Update()
203 if (GetCurrentState() != PFFSM_STATE_INACTIVE
) {
208 int32_t VtolLandFSM::runState(void)
210 uint8_t flTimeout
= false;
212 mLandData
->stateRunCount
++;
214 if (mLandData
->stateTimeoutCount
> 0 && mLandData
->stateRunCount
> mLandData
->stateTimeoutCount
) {
218 // If the current state has a static function, call it
219 if (sLandStateTable
[mLandData
->currentState
].run
) {
220 (this->*sLandStateTable
[mLandData
->currentState
].run
)(flTimeout
);
225 int32_t VtolLandFSM::runAlways(void)
227 void assessAltitude(void);
232 // PathFollower implements the PID scheme and has a objective
233 // set by a PathDesired object. Based on the mode, pathfollower
234 // uses FSM's as helper functions that manage state and event detection.
235 // PathFollower calls into FSM methods to alter its commands.
237 void VtolLandFSM::BoundThrust(float &ulow
, float &uhigh
)
239 ulow
= mLandData
->boundThrustMin
;
240 uhigh
= mLandData
->boundThrustMax
;
243 if (mLandData
->flConstrainThrust
) {
244 uhigh
= mLandData
->thrustLimit
;
248 void VtolLandFSM::ConstrainStabiDesired(StabilizationDesiredData
*stabDesired
)
250 if (mLandData
->flZeroStabiHorizontal
&& stabDesired
) {
251 stabDesired
->Pitch
= 0.0f
;
252 stabDesired
->Roll
= 0.0f
;
253 stabDesired
->Yaw
= 0.0f
;
257 void VtolLandFSM::CheckPidScaler(pid_scaler
*local_scaler
)
259 if (mLandData
->flLowAltitude
) {
260 local_scaler
->p
= LANDING_PID_SCALAR_P
;
261 local_scaler
->i
= LANDING_PID_SCALAR_I
;
266 // Set the new state and perform setup for subsequent state run calls
267 // This is called by state run functions on event detection that drive
268 // state transitions.
269 void VtolLandFSM::setState(StatusVtolLandStateOptions newState
, StatusVtolLandStateExitReasonOptions reason
)
271 mLandData
->fsmLandStatus
.StateExitReason
[mLandData
->currentState
] = reason
;
273 if (mLandData
->currentState
== newState
) {
276 mLandData
->currentState
= newState
;
278 if (newState
!= STATUSVTOLLAND_STATE_INACTIVE
) {
279 float altitudeAboveTakeoff
= assessAltitude();
280 mLandData
->fsmLandStatus
.AltitudeAtState
[newState
] = altitudeAboveTakeoff
;
283 // Restart state timer counter
284 mLandData
->stateRunCount
= 0;
286 // Reset state timeout to disabled/zero
287 mLandData
->stateTimeoutCount
= 0;
289 if (sLandStateTable
[mLandData
->currentState
].setup
) {
290 (this->*sLandStateTable
[mLandData
->currentState
].setup
)();
293 updateVtolLandFSMStatus();
297 // Timeout utility function for use by state init implementations
298 void VtolLandFSM::setStateTimeout(int32_t count
)
300 mLandData
->stateTimeoutCount
= count
;
303 void VtolLandFSM::updateVtolLandFSMStatus()
305 mLandData
->fsmLandStatus
.State
= mLandData
->currentState
;
306 if (mLandData
->flLowAltitude
) {
307 mLandData
->fsmLandStatus
.AltitudeState
= STATUSVTOLLAND_ALTITUDESTATE_LOW
;
309 mLandData
->fsmLandStatus
.AltitudeState
= STATUSVTOLLAND_ALTITUDESTATE_HIGH
;
311 StatusVtolLandSet(&mLandData
->fsmLandStatus
);
315 float VtolLandFSM::BoundVelocityDown(float velocity_down
)
317 velocity_down
= boundf(velocity_down
, MIN_LANDRATE
, MAX_LANDRATE
);
318 if (mLandData
->flLowAltitude
) {
319 velocity_down
*= LOW_ALT_DESCENT_REDUCTION_FACTOR
;
321 mLandData
->fsmLandStatus
.targetDescentRate
= velocity_down
;
323 if (mLandData
->flAltitudeHold
) {
326 return velocity_down
;
330 float VtolLandFSM::assessAltitude(void)
334 PositionStateDownGet(&positionDown
);
335 float takeOffDown
= 0.0f
;
336 if (mLandData
->takeOffLocation
.Status
== TAKEOFFLOCATION_STATUS_VALID
) {
337 takeOffDown
= mLandData
->takeOffLocation
.Down
;
339 float positionDownRelativeToTakeoff
= positionDown
- takeOffDown
;
340 if (positionDownRelativeToTakeoff
< LANDING_SLOWDOWN_HEIGHT
) {
341 mLandData
->flLowAltitude
= false;
343 mLandData
->flLowAltitude
= true;
345 // Return the altitude above takeoff, which is the negation of positionDownRelativeToTakeoff
346 return -positionDownRelativeToTakeoff
;
350 // FSM Setup and Run method implementation
353 void VtolLandFSM::setup_inactive(void)
355 // Re-initialise local variables
356 mLandData
->flZeroStabiHorizontal
= false;
357 mLandData
->flConstrainThrust
= false;
360 // State: INIT ALTHOLD
361 void VtolLandFSM::setup_init_althold(void)
363 setStateTimeout(TIMEOUT_INIT_ALTHOLD
);
364 // get target descent velocity
365 mLandData
->flZeroStabiHorizontal
= false;
366 mLandData
->fsmLandStatus
.targetDescentRate
= BoundVelocityDown(pathDesired
->ModeParameters
[PATHDESIRED_MODEPARAMETER_LAND_VELOCITYVECTOR_DOWN
]);
367 mLandData
->flConstrainThrust
= false;
368 mLandData
->flAltitudeHold
= true;
369 mLandData
->boundThrustMin
= vtolPathFollowerSettings
->ThrustLimits
.Min
;
370 mLandData
->boundThrustMax
= vtolPathFollowerSettings
->ThrustLimits
.Max
;
373 void VtolLandFSM::run_init_althold(uint8_t flTimeout
)
376 mLandData
->flAltitudeHold
= false;
377 setState(STATUSVTOLLAND_STATE_WTGFORDESCENTRATE
, STATUSVTOLLAND_STATEEXITREASON_TIMEOUT
);
382 // State: WAITING FOR DESCENT RATE
383 void VtolLandFSM::setup_wtg_for_descentrate(void)
385 setStateTimeout(TIMEOUT_WTG_FOR_DESCENTRATE
);
386 // get target descent velocity
387 mLandData
->flZeroStabiHorizontal
= false;
388 mLandData
->observationCount
= 0;
389 mLandData
->observation2Count
= 0;
390 mLandData
->flConstrainThrust
= false;
391 mLandData
->flAltitudeHold
= false;
392 mLandData
->boundThrustMin
= vtolPathFollowerSettings
->ThrustLimits
.Min
;
393 mLandData
->boundThrustMax
= vtolPathFollowerSettings
->ThrustLimits
.Max
;
396 void VtolLandFSM::run_wtg_for_descentrate(uint8_t flTimeout
)
398 // Look at current actual thrust...are we already shutdown??
399 VelocityStateData velocityState
;
401 VelocityStateGet(&velocityState
);
402 StabilizationDesiredData stabDesired
;
403 StabilizationDesiredGet(&stabDesired
);
405 // We don't expect PID to get exactly the target descent rate, so have a lower
406 // water mark but need to see 5 observations to be confident that we have semi-stable
409 // we need to see velocity down within a range of control before we proceed, without which we
410 // really don't have confidence to allow later states to run.
411 if (velocityState
.Down
> (LANDRATE_LOWLIMIT_FACTOR
* mLandData
->fsmLandStatus
.targetDescentRate
) &&
412 velocityState
.Down
< (LANDRATE_HILIMIT_FACTOR
* mLandData
->fsmLandStatus
.targetDescentRate
)) {
413 if (mLandData
->observationCount
++ > WTG_FOR_DESCENTRATE_COUNT_LIMIT
) {
414 setState(STATUSVTOLLAND_STATE_ATDESCENTRATE
, STATUSVTOLLAND_STATEEXITREASON_DESCENTRATEOK
);
420 setState(STATUSVTOLLAND_STATE_INITALTHOLD
, STATUSVTOLLAND_STATEEXITREASON_TIMEOUT
);
425 // State: AT DESCENT RATE
426 void VtolLandFSM::setup_at_descentrate(void)
428 setStateTimeout(TIMEOUT_AT_DESCENTRATE
);
429 mLandData
->flZeroStabiHorizontal
= false;
430 mLandData
->observationCount
= 0;
431 mLandData
->sum1
= 0.0f
;
432 mLandData
->sum2
= 0.0f
;
433 mLandData
->flConstrainThrust
= false;
434 mLandData
->fsmLandStatus
.averageDescentRate
= MIN_LANDRATE
;
435 mLandData
->fsmLandStatus
.averageDescentThrust
= vtolPathFollowerSettings
->ThrustLimits
.Neutral
;
436 mLandData
->boundThrustMin
= vtolPathFollowerSettings
->ThrustLimits
.Min
;
437 mLandData
->boundThrustMax
= vtolPathFollowerSettings
->ThrustLimits
.Max
;
440 void VtolLandFSM::run_at_descentrate(uint8_t flTimeout
)
442 VelocityStateData velocityState
;
444 VelocityStateGet(&velocityState
);
446 StabilizationDesiredData stabDesired
;
447 StabilizationDesiredGet(&stabDesired
);
449 mLandData
->sum1
+= velocityState
.Down
;
450 mLandData
->sum2
+= stabDesired
.Thrust
;
451 mLandData
->observationCount
++;
453 mLandData
->fsmLandStatus
.averageDescentRate
= boundf((mLandData
->sum1
/ (float)(mLandData
->observationCount
)), 0.5f
* MIN_LANDRATE
, 1.5f
* MAX_LANDRATE
);
454 mLandData
->fsmLandStatus
.averageDescentThrust
= boundf((mLandData
->sum2
/ (float)(mLandData
->observationCount
)), vtolPathFollowerSettings
->ThrustLimits
.Min
, vtolPathFollowerSettings
->ThrustLimits
.Max
);
456 // We need to calculate a neutral limit to use later to constrain upper thrust range during states where we are close to the ground
457 // As our battery gets flat, ThrustLimits.Neutral needs to constrain us too much and we get too fast a descent rate. We can
458 // detect this by the fact that the descent rate will exceed the target and the required thrust will exceed the neutral value
459 mLandData
->fsmLandStatus
.calculatedNeutralThrust
= mLandData
->fsmLandStatus
.averageDescentRate
/ mLandData
->fsmLandStatus
.targetDescentRate
* mLandData
->fsmLandStatus
.averageDescentThrust
;
460 mLandData
->fsmLandStatus
.calculatedNeutralThrust
= boundf(mLandData
->fsmLandStatus
.calculatedNeutralThrust
, vtolPathFollowerSettings
->ThrustLimits
.Neutral
, vtolPathFollowerSettings
->ThrustLimits
.Max
);
463 setState(STATUSVTOLLAND_STATE_WTGFORGROUNDEFFECT
, STATUSVTOLLAND_STATEEXITREASON_DESCENTRATEOK
);
468 // State: WAITING FOR GROUND EFFECT
469 void VtolLandFSM::setup_wtg_for_groundeffect(void)
472 mLandData
->flZeroStabiHorizontal
= false;
473 mLandData
->observationCount
= 0;
474 mLandData
->observation2Count
= 0;
475 mLandData
->sum1
= 0.0f
;
476 mLandData
->sum2
= 0.0f
;
477 mLandData
->flConstrainThrust
= false;
478 mLandData
->fsmLandStatus
.WtgForGroundEffect
.BounceVelocity
= 0.0f
;
479 mLandData
->fsmLandStatus
.WtgForGroundEffect
.BounceAccel
= 0.0f
;
480 mLandData
->boundThrustMin
= vtolPathFollowerSettings
->ThrustLimits
.Min
;
481 mLandData
->boundThrustMax
= vtolPathFollowerSettings
->ThrustLimits
.Max
;
484 void VtolLandFSM::run_wtg_for_groundeffect(__attribute__((unused
)) uint8_t flTimeout
)
486 // detect material downrating in thrust for 1 second.
487 VelocityStateData velocityState
;
489 VelocityStateGet(&velocityState
);
490 AccelStateData accelState
;
491 AccelStateGet(&accelState
);
495 HomeLocationg_eGet(&g_e
);
497 StabilizationDesiredData stabDesired
;
498 StabilizationDesiredGet(&stabDesired
);
501 uint8_t flBounce
= (velocityState
.Down
< BOUNCE_VELOCITY_TRIGGER_LIMIT
);
503 mLandData
->fsmLandStatus
.WtgForGroundEffect
.BounceVelocity
= velocityState
.Down
;
505 mLandData
->fsmLandStatus
.WtgForGroundEffect
.BounceVelocity
= 0.0f
;
508 // invert sign of accel to the standard convention of down is +ve and subtract the gravity to get
509 // a relative acceleration term.
510 float bounceAccel
= -accelState
.z
- g_e
;
511 uint8_t flBounceAccel
= (bounceAccel
< BOUNCE_ACCELERATION_TRIGGER_LIMIT
);
513 mLandData
->fsmLandStatus
.WtgForGroundEffect
.BounceAccel
= bounceAccel
;
515 mLandData
->fsmLandStatus
.WtgForGroundEffect
.BounceAccel
= 0.0f
;
518 if (flBounce
) { // || flBounceAccel) { // accel trigger can occur due to vibration and is too sensitive
519 mLandData
->observation2Count
++;
520 if (mLandData
->observation2Count
> BOUNCE_TRIGGER_COUNT
) {
521 setState(STATUSVTOLLAND_STATE_GROUNDEFFECT
, (flBounce
? STATUSVTOLLAND_STATEEXITREASON_BOUNCEVELOCITY
: STATUSVTOLLAND_STATEEXITREASON_BOUNCEACCEL
));
525 mLandData
->observation2Count
= 0;
528 // detect low descent rate
529 uint8_t flDescentRateLow
= (velocityState
.Down
< (GROUNDEFFECT_SLOWDOWN_FACTOR
* mLandData
->fsmLandStatus
.averageDescentRate
));
530 if (flDescentRateLow
) {
531 mLandData
->boundThrustMax
= mLandData
->fsmLandStatus
.calculatedNeutralThrust
;
532 mLandData
->observationCount
++;
533 if (mLandData
->observationCount
> GROUNDEFFECT_SLOWDOWN_COUNT
) {
534 #ifndef DEBUG_GROUNDIMPACT
535 setState(STATUSVTOLLAND_STATE_GROUNDEFFECT
, STATUSVTOLLAND_STATEEXITREASON_LOWDESCENTRATE
);
540 mLandData
->observationCount
= 0;
543 updateVtolLandFSMStatus();
546 // STATE: GROUNDEFFET
547 void VtolLandFSM::setup_groundeffect(void)
549 setStateTimeout(TIMEOUT_GROUNDEFFECT
);
550 mLandData
->flZeroStabiHorizontal
= false;
551 PositionStateData positionState
;
552 PositionStateGet(&positionState
);
553 mLandData
->expectedLandPositionNorth
= positionState
.North
;
554 mLandData
->expectedLandPositionEast
= positionState
.East
;
555 mLandData
->flConstrainThrust
= false;
557 // now that we have ground effect limit max thrust to neutral
558 mLandData
->boundThrustMin
= -0.1f
;
559 mLandData
->boundThrustMax
= mLandData
->fsmLandStatus
.calculatedNeutralThrust
;
561 void VtolLandFSM::run_groundeffect(__attribute__((unused
)) uint8_t flTimeout
)
563 StabilizationDesiredData stabDesired
;
565 StabilizationDesiredGet(&stabDesired
);
566 if (stabDesired
.Thrust
< 0.0f
) {
567 setState(STATUSVTOLLAND_STATE_THRUSTOFF
, STATUSVTOLLAND_STATEEXITREASON_ZEROTHRUST
);
571 // Stay in this state until we get a low altitude flag.
572 if (mLandData
->flLowAltitude
== false) {
573 // worst case scenario is that we land and the pid brings thrust down to zero.
577 // detect broad sideways drift. If for some reason we have a hard landing that the bounce detection misses, this will kick in
578 PositionStateData positionState
;
579 PositionStateGet(&positionState
);
580 float north_error
= mLandData
->expectedLandPositionNorth
- positionState
.North
;
581 float east_error
= mLandData
->expectedLandPositionEast
- positionState
.East
;
582 float positionError
= sqrtf(north_error
* north_error
+ east_error
* east_error
);
583 if (positionError
> 1.5f
) {
584 setState(STATUSVTOLLAND_STATE_THRUSTDOWN
, STATUSVTOLLAND_STATEEXITREASON_POSITIONERROR
);
589 setState(STATUSVTOLLAND_STATE_THRUSTDOWN
, STATUSVTOLLAND_STATEEXITREASON_TIMEOUT
);
594 void VtolLandFSM::setup_thrustdown(void)
596 setStateTimeout(TIMEOUT_THRUSTDOWN
);
597 mLandData
->flZeroStabiHorizontal
= true;
598 mLandData
->flConstrainThrust
= true;
599 StabilizationDesiredData stabDesired
;
600 StabilizationDesiredGet(&stabDesired
);
601 mLandData
->thrustLimit
= stabDesired
.Thrust
;
602 mLandData
->sum1
= stabDesired
.Thrust
/ (float)TIMEOUT_THRUSTDOWN
;
603 mLandData
->boundThrustMin
= -0.1f
;
604 mLandData
->boundThrustMax
= mLandData
->fsmLandStatus
.calculatedNeutralThrust
;
607 void VtolLandFSM::run_thrustdown(__attribute__((unused
)) uint8_t flTimeout
)
609 // reduce thrust setpoint step by step
610 mLandData
->thrustLimit
-= mLandData
->sum1
;
612 StabilizationDesiredData stabDesired
;
613 StabilizationDesiredGet(&stabDesired
);
614 if (stabDesired
.Thrust
< 0.0f
|| mLandData
->thrustLimit
< 0.0f
) {
615 setState(STATUSVTOLLAND_STATE_THRUSTOFF
, STATUSVTOLLAND_STATEEXITREASON_ZEROTHRUST
);
619 setState(STATUSVTOLLAND_STATE_THRUSTOFF
, STATUSVTOLLAND_STATEEXITREASON_TIMEOUT
);
624 void VtolLandFSM::setup_thrustoff(void)
626 mLandData
->thrustLimit
= -1.0f
;
627 mLandData
->flZeroStabiHorizontal
= true;
628 mLandData
->flConstrainThrust
= true;
629 mLandData
->boundThrustMin
= -0.1f
;
630 mLandData
->boundThrustMax
= 0.0f
;
633 void VtolLandFSM::run_thrustoff(__attribute__((unused
)) uint8_t flTimeout
)
635 setState(STATUSVTOLLAND_STATE_DISARMED
, STATUSVTOLLAND_STATEEXITREASON_NONE
);
639 void VtolLandFSM::setup_disarmed(void)
642 mLandData
->flConstrainThrust
= false;
643 mLandData
->flZeroStabiHorizontal
= true;
644 mLandData
->observationCount
= 0;
645 mLandData
->boundThrustMin
= -0.1f
;
646 mLandData
->boundThrustMax
= 0.0f
;
648 // force disarm unless in pathplanner mode
649 // to clear, a new pathfollower mode must be selected that is not land,
650 // and also a non-pathfollower mode selection will set this to uninitialised.
651 if (flightStatus
->ControlChain
.PathPlanner
!= FLIGHTSTATUS_CONTROLCHAIN_TRUE
) {
652 AlarmsSet(SYSTEMALARMS_ALARM_GUIDANCE
, SYSTEMALARMS_ALARM_CRITICAL
);
656 void VtolLandFSM::run_disarmed(__attribute__((unused
)) uint8_t flTimeout
)
658 if (flightStatus
->ControlChain
.PathPlanner
!= FLIGHTSTATUS_CONTROLCHAIN_TRUE
) {
659 AlarmsSet(SYSTEMALARMS_ALARM_GUIDANCE
, SYSTEMALARMS_ALARM_CRITICAL
);
662 #ifdef DEBUG_GROUNDIMPACT
663 if (mLandData
->observationCount
++ > 100) {
664 setState(STATUSVTOLLAND_STATE_WTGFORGROUNDEFFECT
, STATUSVTOLLAND_STATEEXITREASON_NONE
);