2 ******************************************************************************
4 * @file vtolautotakeoffcontroller.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016-2018
6 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2015.
7 * @brief Vtol auto takeoff controller loop
8 * @see The GNU Public License (GPL) Version 3
9 * @addtogroup LibrePilot LibrePilotModules Modules PathFollower Navigation
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 <statusvtolautotakeoff.h>
61 #include <pathsummary.h>
65 #include "vtolautotakeoffcontroller.h"
66 #include "pathfollowerfsm.h"
67 #include "vtolautotakeofffsm.h"
68 #include "pidcontroldown.h"
71 #define AUTOTAKEOFF_TO_INCREMENTAL_HEIGHT_MIN 2.0f
72 #define AUTOTAKEOFF_TO_INCREMENTAL_HEIGHT_MAX 50.0f
73 #define AUTOTAKEOFF_INFLIGHT_THROTTLE_CHECK_LIMIT 0.2f
74 #define AUTOTAKEOFF_THROTTLE_LIMIT_TO_ALLOW_TAKEOFF_START 0.3f
75 #define AUTOTAKEOFF_THROTTLE_ABORT_LIMIT 0.1f
77 // pointer to a singleton instance
78 VtolAutoTakeoffController
*VtolAutoTakeoffController::p_inst
= 0;
80 VtolAutoTakeoffController::VtolAutoTakeoffController()
81 : fsm(0), vtolPathFollowerSettings(0), mActive(false)
84 // Called when mode first engaged
85 void VtolAutoTakeoffController::Activate(void)
92 controlDown
.Activate();
94 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED
;
95 // We only allow takeoff if the state transition of disarmed to armed occurs
96 // whilst in the autotake flight mode
97 FlightStatusData flightStatus
;
98 FlightStatusGet(&flightStatus
);
99 StabilizationDesiredData stabiDesired
;
100 StabilizationDesiredGet(&stabiDesired
);
102 if (flightStatus
.Armed
) {
104 if (stabiDesired
.Thrust
> AUTOTAKEOFF_INFLIGHT_THROTTLE_CHECK_LIMIT
|| flightStatus
.ControlChain
.PathPlanner
== FLIGHTSTATUS_CONTROLCHAIN_TRUE
) {
105 // ok assume already in flight and just enter position hold
106 // if we are not actually inflight this will just be a violent autotakeoff
107 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_POSITIONHOLD
;
109 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_REQUIREUNARMEDFIRST
;
110 // Note that if this mode was invoked unintentionally whilst in flight, effectively
111 // all inputs get ignored and the vtol continues to fly to its previous
115 fsm
->setControlState(autotakeoffState
);
119 uint8_t VtolAutoTakeoffController::IsActive(void)
124 uint8_t VtolAutoTakeoffController::Mode(void)
126 return PATHDESIRED_MODE_AUTOTAKEOFF
;
129 // Objective updated in pathdesired, e.g. same flight mode but new target velocity
130 void VtolAutoTakeoffController::ObjectiveUpdated(void)
133 // override pathDesired from PathPlanner with current position,
134 // as we deliberately don't care about the location of the waypoints on the map
135 float autotakeoff_velocity
;
136 float autotakeoff_height
;
137 PositionStateData positionState
;
138 PositionStateGet(&positionState
);
139 FlightModeSettingsAutoTakeOffVelocityGet(&autotakeoff_velocity
);
140 FlightModeSettingsAutoTakeOffHeightGet(&autotakeoff_height
);
141 autotakeoff_height
= fabsf(autotakeoff_height
);
142 if (autotakeoff_height
< AUTOTAKEOFF_TO_INCREMENTAL_HEIGHT_MIN
) {
143 autotakeoff_height
= AUTOTAKEOFF_TO_INCREMENTAL_HEIGHT_MIN
;
144 } else if (autotakeoff_height
> AUTOTAKEOFF_TO_INCREMENTAL_HEIGHT_MAX
) {
145 autotakeoff_height
= AUTOTAKEOFF_TO_INCREMENTAL_HEIGHT_MAX
;
147 controlDown
.UpdateVelocitySetpoint(-autotakeoff_velocity
);
148 controlNE
.UpdateVelocitySetpoint(0.0f
, 0.0f
);
149 controlNE
.UpdatePositionSetpoint(positionState
.North
, positionState
.East
);
152 controlDown
.UpdatePositionSetpoint(positionState
.Down
- autotakeoff_height
);
153 mOverride
= false; // further updates always come from ManualControl and will control horizontal position
155 // Set the objective's target velocity
157 controlDown
.UpdateVelocitySetpoint(pathDesired
->ModeParameters
[PATHDESIRED_MODEPARAMETER_AUTOTAKEOFF_DOWN
]);
158 controlNE
.UpdateVelocitySetpoint(pathDesired
->ModeParameters
[PATHDESIRED_MODEPARAMETER_AUTOTAKEOFF_NORTH
],
159 pathDesired
->ModeParameters
[PATHDESIRED_MODEPARAMETER_AUTOTAKEOFF_EAST
]);
160 controlNE
.UpdatePositionSetpoint(pathDesired
->End
.North
, pathDesired
->End
.East
);
161 controlDown
.UpdatePositionSetpoint(pathDesired
->End
.Down
);
165 // Controller deactivated
166 void VtolAutoTakeoffController::Deactivate(void)
171 controlDown
.Deactivate();
172 controlNE
.Deactivate();
176 // AutoTakeoff Uses different vertical velocity PID.
177 void VtolAutoTakeoffController::SettingsUpdated(void)
179 const float dT
= vtolPathFollowerSettings
->UpdatePeriod
/ 1000.0f
;
181 controlNE
.UpdateParameters(vtolPathFollowerSettings
->HorizontalVelPID
.Kp
,
182 vtolPathFollowerSettings
->HorizontalVelPID
.Ki
,
183 vtolPathFollowerSettings
->HorizontalVelPID
.Kd
,
184 vtolPathFollowerSettings
->HorizontalVelPID
.Beta
,
186 vtolPathFollowerSettings
->HorizontalVelMax
);
189 controlNE
.UpdatePositionalParameters(vtolPathFollowerSettings
->HorizontalPosP
);
190 controlNE
.UpdateCommandParameters(-vtolPathFollowerSettings
->MaxRollPitch
, vtolPathFollowerSettings
->MaxRollPitch
, vtolPathFollowerSettings
->VelocityFeedforward
);
192 controlDown
.UpdateParameters(vtolPathFollowerSettings
->AutoTakeoffVerticalVelPID
.Kp
,
193 vtolPathFollowerSettings
->AutoTakeoffVerticalVelPID
.Ki
,
194 vtolPathFollowerSettings
->AutoTakeoffVerticalVelPID
.Kd
,
195 vtolPathFollowerSettings
->AutoTakeoffVerticalVelPID
.Beta
,
197 vtolPathFollowerSettings
->VerticalVelMax
);
198 controlDown
.UpdatePositionalParameters(vtolPathFollowerSettings
->VerticalPosP
);
199 VtolSelfTuningStatsData vtolSelfTuningStats
;
200 VtolSelfTuningStatsGet(&vtolSelfTuningStats
);
201 controlDown
.UpdateNeutralThrust(vtolSelfTuningStats
.NeutralThrustOffset
+ vtolPathFollowerSettings
->ThrustLimits
.Neutral
);
202 controlDown
.SetThrustLimits(vtolPathFollowerSettings
->ThrustLimits
.Min
, vtolPathFollowerSettings
->ThrustLimits
.Max
);
203 fsm
->SettingsUpdated();
206 // AutoTakeoff Uses a different FSM to its parent
207 int32_t VtolAutoTakeoffController::Initialize(VtolPathFollowerSettingsData
*ptr_vtolPathFollowerSettings
)
209 PIOS_Assert(ptr_vtolPathFollowerSettings
);
210 vtolPathFollowerSettings
= ptr_vtolPathFollowerSettings
;
213 fsm
= VtolAutoTakeoffFSM::instance();
214 VtolAutoTakeoffFSM::instance()->Initialize(vtolPathFollowerSettings
, pathDesired
, flightStatus
);
215 controlDown
.Initialize(fsm
);
221 void VtolAutoTakeoffController::UpdateVelocityDesired()
223 VelocityStateData velocityState
;
225 VelocityStateGet(&velocityState
);
226 VelocityDesiredData velocityDesired
;
227 PositionStateData positionState
;
228 PositionStateGet(&positionState
);
230 if (fsm
->PositionHoldState()) {
231 controlDown
.UpdatePositionState(positionState
.Down
);
232 controlDown
.ControlPosition();
235 controlDown
.UpdateVelocityState(velocityState
.Down
);
236 controlNE
.UpdateVelocityState(velocityState
.North
, velocityState
.East
);
238 controlNE
.UpdatePositionState(positionState
.North
, positionState
.East
);
239 controlNE
.ControlPosition();
241 velocityDesired
.Down
= controlDown
.GetVelocityDesired();
243 controlNE
.GetVelocityDesired(&north
, &east
);
244 velocityDesired
.North
= north
;
245 velocityDesired
.East
= east
;
248 pathStatus
->error
= 0.0f
;
249 pathStatus
->fractional_progress
= 0.0f
;
250 if (fsm
->PositionHoldState()) {
251 pathStatus
->fractional_progress
= 1.0f
;
252 // note if the takeoff waypoint and the launch position are significantly different
253 // the above check might need to expand to assessment of north and east.
255 pathStatus
->path_direction_north
= velocityDesired
.North
;
256 pathStatus
->path_direction_east
= velocityDesired
.East
;
257 pathStatus
->path_direction_down
= velocityDesired
.Down
;
259 pathStatus
->correction_direction_north
= velocityDesired
.North
- velocityState
.North
;
260 pathStatus
->correction_direction_east
= velocityDesired
.East
- velocityState
.East
;
261 pathStatus
->correction_direction_down
= velocityDesired
.Down
- velocityState
.Down
;
263 VelocityDesiredSet(&velocityDesired
);
266 int8_t VtolAutoTakeoffController::UpdateStabilizationDesired()
269 StabilizationDesiredData stabDesired
;
270 AttitudeStateData attitudeState
;
271 StabilizationBankData stabSettings
;
275 StabilizationDesiredGet(&stabDesired
);
276 AttitudeStateGet(&attitudeState
);
277 StabilizationBankGet(&stabSettings
);
279 controlNE
.GetNECommand(&northCommand
, &eastCommand
);
280 stabDesired
.Thrust
= controlDown
.GetDownCommand();
281 switch (autotakeoffState
) {
282 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED
:
283 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORMIDTHROTTLE
:
284 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_REQUIREUNARMEDFIRST
:
285 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_ABORT
:
286 stabDesired
.Thrust
= 0.0f
;
292 float angle_radians
= DEG2RAD(attitudeState
.Yaw
);
293 float cos_angle
= cosf(angle_radians
);
294 float sine_angle
= sinf(angle_radians
);
295 float maxPitch
= vtolPathFollowerSettings
->MaxRollPitch
;
296 stabDesired
.StabilizationMode
.Pitch
= STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE
;
297 stabDesired
.Pitch
= boundf(-northCommand
* cos_angle
- eastCommand
* sine_angle
, -maxPitch
, maxPitch
);
298 stabDesired
.StabilizationMode
.Roll
= STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE
;
299 stabDesired
.Roll
= boundf(-northCommand
* sine_angle
+ eastCommand
* cos_angle
, -maxPitch
, maxPitch
);
301 ManualControlCommandData manualControl
;
302 ManualControlCommandGet(&manualControl
);
304 stabDesired
.StabilizationMode
.Yaw
= STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK
;
305 stabDesired
.Yaw
= stabSettings
.MaximumRate
.Yaw
* manualControl
.Yaw
;
307 // default thrust mode to cruise control
308 stabDesired
.StabilizationMode
.Thrust
= STABILIZATIONDESIRED_STABILIZATIONMODE_CRUISECONTROL
;
310 fsm
->ConstrainStabiDesired(&stabDesired
); // excludes thrust
311 StabilizationDesiredSet(&stabDesired
);
316 void VtolAutoTakeoffController::UpdateAutoPilot()
318 // state machine updates:
319 // Vtol AutoTakeoff invocation from flight mode requires the following sequence:
320 // 1. Arming must be done whilst in the AutoTakeOff flight mode
321 // 2. If the AutoTakeoff flight mode is selected and already armed, requires disarming first
322 // 3. Wait for armed state
323 // 4. Once the user increases the throttle position to above 30%, then and only then initiate auto-takeoff.
324 // 5. Whilst the throttle is < 30% before takeoff, all stick inputs are being ignored.
325 // 6. If during the autotakeoff sequence, at any stage, the throttle stick position reduces to less than 10%, landing is initiated.
327 switch (autotakeoffState
) {
328 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_REQUIREUNARMEDFIRST
:
330 FlightStatusData flightStatus
;
331 FlightStatusGet(&flightStatus
);
332 if (!flightStatus
.Armed
) {
333 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED
;
334 fsm
->setControlState(autotakeoffState
);
338 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED
:
340 FlightStatusData flightStatus
;
341 FlightStatusGet(&flightStatus
);
342 if (flightStatus
.Armed
) {
343 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORMIDTHROTTLE
;
344 fsm
->setControlState(autotakeoffState
);
348 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORMIDTHROTTLE
:
350 ManualControlCommandData cmd
;
351 ManualControlCommandGet(&cmd
);
353 if (cmd
.Throttle
> AUTOTAKEOFF_THROTTLE_LIMIT_TO_ALLOW_TAKEOFF_START
) {
354 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_INITIATE
;
355 fsm
->setControlState(autotakeoffState
);
359 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_INITIATE
:
361 ManualControlCommandData cmd
;
362 ManualControlCommandGet(&cmd
);
363 FlightStatusData flightStatus
;
364 FlightStatusGet(&flightStatus
);
366 // we do not do a takeoff abort in pathplanner mode
367 if (flightStatus
.ControlChain
.PathPlanner
!= FLIGHTSTATUS_CONTROLCHAIN_TRUE
&&
368 cmd
.Throttle
< AUTOTAKEOFF_THROTTLE_ABORT_LIMIT
) {
369 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_ABORT
;
370 fsm
->setControlState(autotakeoffState
);
374 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_ABORT
:
376 FlightStatusData flightStatus
;
377 FlightStatusGet(&flightStatus
);
378 if (!flightStatus
.Armed
) {
379 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED
;
380 fsm
->setControlState(autotakeoffState
);
384 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_POSITIONHOLD
:
385 // nothing to do. land has been requested. stay here for forever until mode change.
392 UpdateVelocityDesired();
394 int8_t result
= UpdateStabilizationDesired();
396 AlarmsSet(SYSTEMALARMS_ALARM_GUIDANCE
, SYSTEMALARMS_ALARM_OK
);
398 pathStatus
->Status
= PATHSTATUS_STATUS_CRITICAL
;
399 AlarmsSet(SYSTEMALARMS_ALARM_GUIDANCE
, SYSTEMALARMS_ALARM_WARNING
);
402 PathStatusSet(pathStatus
);