2 ******************************************************************************
4 * @file vtollandcontroller.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2015.
6 * @brief Vtol landing controller loop
7 * @see The GNU Public License (GPL) Version 3
9 *****************************************************************************/
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <openpilot.h>
29 #include <callbackinfo.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' not care about the location of the waypoints on the map
136 float autotakeoff_height
;
137 PositionStateData positionState
;
138 PositionStateGet(&positionState
);
139 FlightModeSettingsAutoTakeOffVelocityGet(&velocity_down
);
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(velocity_down
);
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
);
164 void VtolAutoTakeoffController::Deactivate(void)
169 controlDown
.Deactivate();
170 controlNE
.Deactivate();
174 // AutoTakeoff Uses different vertical velocity PID.
175 void VtolAutoTakeoffController::SettingsUpdated(void)
177 const float dT
= vtolPathFollowerSettings
->UpdatePeriod
/ 1000.0f
;
179 controlNE
.UpdateParameters(vtolPathFollowerSettings
->HorizontalVelPID
.Kp
,
180 vtolPathFollowerSettings
->HorizontalVelPID
.Ki
,
181 vtolPathFollowerSettings
->HorizontalVelPID
.Kd
,
182 vtolPathFollowerSettings
->HorizontalVelPID
.Beta
,
184 vtolPathFollowerSettings
->HorizontalVelMax
);
187 controlNE
.UpdatePositionalParameters(vtolPathFollowerSettings
->HorizontalPosP
);
188 controlNE
.UpdateCommandParameters(-vtolPathFollowerSettings
->MaxRollPitch
, vtolPathFollowerSettings
->MaxRollPitch
, vtolPathFollowerSettings
->VelocityFeedforward
);
190 controlDown
.UpdateParameters(vtolPathFollowerSettings
->AutoTakeoffVerticalVelPID
.Kp
,
191 vtolPathFollowerSettings
->AutoTakeoffVerticalVelPID
.Ki
,
192 vtolPathFollowerSettings
->AutoTakeoffVerticalVelPID
.Kd
,
193 vtolPathFollowerSettings
->AutoTakeoffVerticalVelPID
.Beta
,
195 vtolPathFollowerSettings
->VerticalVelMax
);
196 controlDown
.UpdatePositionalParameters(vtolPathFollowerSettings
->VerticalPosP
);
197 VtolSelfTuningStatsData vtolSelfTuningStats
;
198 VtolSelfTuningStatsGet(&vtolSelfTuningStats
);
199 controlDown
.UpdateNeutralThrust(vtolSelfTuningStats
.NeutralThrustOffset
+ vtolPathFollowerSettings
->ThrustLimits
.Neutral
);
200 controlDown
.SetThrustLimits(vtolPathFollowerSettings
->ThrustLimits
.Min
, vtolPathFollowerSettings
->ThrustLimits
.Max
);
201 fsm
->SettingsUpdated();
204 // AutoTakeoff Uses a different FSM to its parent
205 int32_t VtolAutoTakeoffController::Initialize(VtolPathFollowerSettingsData
*ptr_vtolPathFollowerSettings
)
207 PIOS_Assert(ptr_vtolPathFollowerSettings
);
208 vtolPathFollowerSettings
= ptr_vtolPathFollowerSettings
;
211 fsm
= VtolAutoTakeoffFSM::instance();
212 VtolAutoTakeoffFSM::instance()->Initialize(vtolPathFollowerSettings
, pathDesired
, flightStatus
);
213 controlDown
.Initialize(fsm
);
219 void VtolAutoTakeoffController::UpdateVelocityDesired()
221 VelocityStateData velocityState
;
223 VelocityStateGet(&velocityState
);
224 VelocityDesiredData velocityDesired
;
225 PositionStateData positionState
;
226 PositionStateGet(&positionState
);
228 if (fsm
->PositionHoldState()) {
229 controlDown
.UpdatePositionState(positionState
.Down
);
230 controlDown
.ControlPosition();
233 controlDown
.UpdateVelocityState(velocityState
.Down
);
234 controlNE
.UpdateVelocityState(velocityState
.North
, velocityState
.East
);
236 controlNE
.UpdatePositionState(positionState
.North
, positionState
.East
);
237 controlNE
.ControlPosition();
239 velocityDesired
.Down
= controlDown
.GetVelocityDesired();
241 controlNE
.GetVelocityDesired(&north
, &east
);
242 velocityDesired
.North
= north
;
243 velocityDesired
.East
= east
;
246 pathStatus
->error
= 0.0f
;
247 pathStatus
->fractional_progress
= 0.0f
;
248 if (fsm
->PositionHoldState()) {
249 pathStatus
->fractional_progress
= 1.0f
;
250 // note if the takeoff waypoint and the launch position are significantly different
251 // the above check might need to expand to assessment of north and east.
253 pathStatus
->path_direction_north
= velocityDesired
.North
;
254 pathStatus
->path_direction_east
= velocityDesired
.East
;
255 pathStatus
->path_direction_down
= velocityDesired
.Down
;
257 pathStatus
->correction_direction_north
= velocityDesired
.North
- velocityState
.North
;
258 pathStatus
->correction_direction_east
= velocityDesired
.East
- velocityState
.East
;
259 pathStatus
->correction_direction_down
= velocityDesired
.Down
- velocityState
.Down
;
261 VelocityDesiredSet(&velocityDesired
);
264 int8_t VtolAutoTakeoffController::UpdateStabilizationDesired()
267 StabilizationDesiredData stabDesired
;
268 AttitudeStateData attitudeState
;
269 StabilizationBankData stabSettings
;
273 StabilizationDesiredGet(&stabDesired
);
274 AttitudeStateGet(&attitudeState
);
275 StabilizationBankGet(&stabSettings
);
277 controlNE
.GetNECommand(&northCommand
, &eastCommand
);
278 stabDesired
.Thrust
= controlDown
.GetDownCommand();
279 switch (autotakeoffState
) {
280 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED
:
281 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORMIDTHROTTLE
:
282 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_REQUIREUNARMEDFIRST
:
283 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_ABORT
:
284 stabDesired
.Thrust
= 0.0f
;
290 float angle_radians
= DEG2RAD(attitudeState
.Yaw
);
291 float cos_angle
= cosf(angle_radians
);
292 float sine_angle
= sinf(angle_radians
);
293 float maxPitch
= vtolPathFollowerSettings
->MaxRollPitch
;
294 stabDesired
.StabilizationMode
.Pitch
= STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE
;
295 stabDesired
.Pitch
= boundf(-northCommand
* cos_angle
- eastCommand
* sine_angle
, -maxPitch
, maxPitch
);
296 stabDesired
.StabilizationMode
.Roll
= STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE
;
297 stabDesired
.Roll
= boundf(-northCommand
* sine_angle
+ eastCommand
* cos_angle
, -maxPitch
, maxPitch
);
299 ManualControlCommandData manualControl
;
300 ManualControlCommandGet(&manualControl
);
302 stabDesired
.StabilizationMode
.Yaw
= STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK
;
303 stabDesired
.Yaw
= stabSettings
.MaximumRate
.Yaw
* manualControl
.Yaw
;
305 // default thrust mode to cruise control
306 stabDesired
.StabilizationMode
.Thrust
= STABILIZATIONDESIRED_STABILIZATIONMODE_CRUISECONTROL
;
308 fsm
->ConstrainStabiDesired(&stabDesired
); // excludes thrust
309 StabilizationDesiredSet(&stabDesired
);
314 void VtolAutoTakeoffController::UpdateAutoPilot()
316 // state machine updates:
317 // Vtol AutoTakeoff invocation from flight mode requires the following sequence:
318 // 1. Arming must be done whilst in the AutoTakeOff flight mode
319 // 2. If the AutoTakeoff flight mode is selected and already armed, requires disarming first
320 // 3. Wait for armed state
321 // 4. Once the user increases the throttle position to above 50%, then and only then initiate auto-takeoff.
322 // 5. Whilst the throttle is < 50% before takeoff, all stick inputs are being ignored.
323 // 6. If during the autotakeoff sequence, at any stage, if the throttle stick position reduces to less than 10%, landing is initiated.
325 switch (autotakeoffState
) {
326 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_REQUIREUNARMEDFIRST
:
328 FlightStatusData flightStatus
;
329 FlightStatusGet(&flightStatus
);
330 if (!flightStatus
.Armed
) {
331 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED
;
332 fsm
->setControlState(autotakeoffState
);
336 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED
:
338 FlightStatusData flightStatus
;
339 FlightStatusGet(&flightStatus
);
340 if (flightStatus
.Armed
) {
341 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORMIDTHROTTLE
;
342 fsm
->setControlState(autotakeoffState
);
346 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORMIDTHROTTLE
:
348 ManualControlCommandData cmd
;
349 ManualControlCommandGet(&cmd
);
351 if (cmd
.Throttle
> AUTOTAKEOFF_THROTTLE_LIMIT_TO_ALLOW_TAKEOFF_START
) {
352 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_INITIATE
;
353 fsm
->setControlState(autotakeoffState
);
357 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_INITIATE
:
359 ManualControlCommandData cmd
;
360 ManualControlCommandGet(&cmd
);
361 FlightStatusData flightStatus
;
362 FlightStatusGet(&flightStatus
);
364 // we do not do a takeoff abort in pathplanner mode
365 if (flightStatus
.ControlChain
.PathPlanner
!= FLIGHTSTATUS_CONTROLCHAIN_TRUE
&&
366 cmd
.Throttle
< AUTOTAKEOFF_THROTTLE_ABORT_LIMIT
) {
367 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_ABORT
;
368 fsm
->setControlState(autotakeoffState
);
373 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_ABORT
:
375 FlightStatusData flightStatus
;
376 FlightStatusGet(&flightStatus
);
377 if (!flightStatus
.Armed
) {
378 autotakeoffState
= STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED
;
379 fsm
->setControlState(autotakeoffState
);
383 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_POSITIONHOLD
:
384 // nothing to do. land has been requested. stay here for forever until mode change.
391 UpdateVelocityDesired();
393 int8_t result
= UpdateStabilizationDesired();
395 AlarmsSet(SYSTEMALARMS_ALARM_GUIDANCE
, SYSTEMALARMS_ALARM_OK
);
397 pathStatus
->Status
= PATHSTATUS_STATUS_CRITICAL
;
398 AlarmsSet(SYSTEMALARMS_ALARM_GUIDANCE
, SYSTEMALARMS_ALARM_WARNING
);
401 PathStatusSet(pathStatus
);