OP-1900 have path_progress updated correctly for leg_remaining and error_below end...
[librepilot.git] / flight / modules / PathFollower / vtolautotakeoffcontroller.cpp
blob5087adaff4b78f40f18b0c81a949259c86824b9b
1 /*
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
19 * for more details.
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
26 extern "C" {
27 #include <openpilot.h>
29 #include <callbackinfo.h>
31 #include <math.h>
32 #include <pid.h>
33 #include <alarms.h>
34 #include <CoordinateConversions.h>
35 #include <sin_lookup.h>
36 #include <pathdesired.h>
37 #include <paths.h>
38 #include "plans.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>
64 // C++ includes
65 #include "vtolautotakeoffcontroller.h"
66 #include "pathfollowerfsm.h"
67 #include "vtolautotakeofffsm.h"
68 #include "pidcontroldown.h"
70 // Private constants
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)
87 if (!mActive) {
88 mActive = true;
89 mOverride = true;
90 SettingsUpdated();
91 fsm->Activate();
92 controlDown.Activate();
93 controlNE.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) {
103 // Are we inflight?
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;
108 } else {
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
112 // stabi command.
115 fsm->setControlState(autotakeoffState);
119 uint8_t VtolAutoTakeoffController::IsActive(void)
121 return mActive;
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)
132 if (mOverride) {
133 // override pathDesired from PathPLanner with current position,
134 // as we deliberately don' not care about the location of the waypoints on the map
135 float velocity_down;
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
154 } else {
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)
166 if (mActive) {
167 mActive = false;
168 fsm->Inactive();
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;
210 if (fsm == 0) {
211 fsm = VtolAutoTakeoffFSM::instance();
212 VtolAutoTakeoffFSM::instance()->Initialize(vtolPathFollowerSettings, pathDesired, flightStatus);
213 controlDown.Initialize(fsm);
215 return 0;
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();
240 float north, east;
241 controlNE.GetVelocityDesired(&north, &east);
242 velocityDesired.North = north;
243 velocityDesired.East = east;
245 // update pathstatus
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()
266 uint8_t result = 1;
267 StabilizationDesiredData stabDesired;
268 AttitudeStateData attitudeState;
269 StabilizationBankData stabSettings;
270 float northCommand;
271 float eastCommand;
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;
285 break;
286 default:
287 break;
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);
311 return result;
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);
335 break;
336 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED:
338 FlightStatusData flightStatus;
339 FlightStatusGet(&flightStatus);
340 if (flightStatus.Armed) {
341 autotakeoffState = STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORMIDTHROTTLE;
342 fsm->setControlState(autotakeoffState);
345 break;
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);
356 break;
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);
371 break;
373 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_ABORT:
375 FlightStatusData flightStatus;
376 FlightStatusGet(&flightStatus);
377 if (!flightStatus.Armed) {
378 autotakeoffState = STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_WAITFORARMED;
379 fsm->setControlState(autotakeoffState);
382 break;
383 case STATUSVTOLAUTOTAKEOFF_CONTROLSTATE_POSITIONHOLD:
384 // nothing to do. land has been requested. stay here for forever until mode change.
385 default:
386 break;
389 fsm->Update();
391 UpdateVelocityDesired();
393 int8_t result = UpdateStabilizationDesired();
394 if (result) {
395 AlarmsSet(SYSTEMALARMS_ALARM_GUIDANCE, SYSTEMALARMS_ALARM_OK);
396 } else {
397 pathStatus->Status = PATHSTATUS_STATUS_CRITICAL;
398 AlarmsSet(SYSTEMALARMS_ALARM_GUIDANCE, SYSTEMALARMS_ALARM_WARNING);
401 PathStatusSet(pathStatus);