2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
5 * @addtogroup StabilizationModule Stabilization Module
6 * @brief Stabilization PID loops in an airframe type independent manner
7 * @note This object updates the @ref ActuatorDesired "Actuator Desired" based on the
8 * PID loops on the @ref AttitudeDesired "Attitude Desired" and @ref AttitudeState "Attitude State"
12 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
13 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
14 * @brief Attitude stabilization module.
16 * @see The GNU Public License (GPL) Version 3
18 *****************************************************************************/
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 3 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful, but
26 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
27 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
30 * You should have received a copy of the GNU General Public License along
31 * with this program; if not, write to the Free Software Foundation, Inc.,
32 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #include <openpilot.h>
37 #include <sin_lookup.h>
38 #include <callbackinfo.h>
39 #include <ratedesired.h>
40 #include <actuatordesired.h>
41 #include <gyrostate.h>
42 #include <airspeedstate.h>
43 #include <stabilizationstatus.h>
44 #include <flightstatus.h>
45 #include <manualcontrolcommand.h>
46 #include <stabilizationbank.h>
47 #include <stabilizationdesired.h>
48 #include <actuatordesired.h>
50 #include <stabilization.h>
51 #include <virtualflybar.h>
52 #include <cruisecontrol.h>
53 #include <sanitycheck.h>
54 #if !defined(PIOS_EXCLUDE_ADVANCED_FEATURES)
55 #include <systemidentstate.h>
56 #endif /* !defined(PIOS_EXCLUDE_ADVANCED_FEATURES) */
60 #define CALLBACK_PRIORITY CALLBACK_PRIORITY_CRITICAL
62 #define UPDATE_EXPECTED (1.0f / PIOS_SENSOR_RATE)
63 #define UPDATE_MIN 1.0e-6f
64 #define UPDATE_MAX 1.0f
65 #define UPDATE_ALPHA 1.0e-2f
67 #define SYSTEM_IDENT_PERIOD ((uint32_t)75)
69 #if defined(PIOS_EXCLUDE_ADVANCED_FEATURES)
70 #define powapprox fastpow
71 #define expapprox fastexp
73 #define powapprox powf
74 #define expapprox expf
75 #endif /* !defined(PIOS_EXCLUDE_ADVANCED_FEATURES) */
78 static DelayedCallbackInfo
*callbackHandle
;
79 static float gyro_filtered
[3] = { 0, 0, 0 };
80 static float axis_lock_accum
[3] = { 0, 0, 0 };
81 static uint8_t previous_mode
[AXES
] = { 255, 255, 255, 255 };
82 static PiOSDeltatimeConfig timeval
;
83 static float speedScaleFactor
= 1.0f
;
84 static bool frame_is_multirotor
;
85 static bool measuredDterm_enabled
;
86 #if !defined(PIOS_EXCLUDE_ADVANCED_FEATURES)
87 static uint32_t systemIdentTimeVal
= 0;
88 #endif /* !defined(PIOS_EXCLUDE_ADVANCED_FEATURES) */
91 static void stabilizationInnerloopTask();
92 static void GyroStateUpdatedCb(__attribute__((unused
)) UAVObjEvent
*ev
);
94 static void AirSpeedUpdatedCb(__attribute__((unused
)) UAVObjEvent
*ev
);
97 void stabilizationInnerloopInit()
99 RateDesiredInitialize();
100 ActuatorDesiredInitialize();
101 GyroStateInitialize();
102 StabilizationStatusInitialize();
103 FlightStatusInitialize();
104 ManualControlCommandInitialize();
105 StabilizationDesiredInitialize();
106 ActuatorDesiredInitialize();
107 #if !defined(PIOS_EXCLUDE_ADVANCED_FEATURES)
108 SystemIdentStateInitialize();
109 #endif /* !defined(PIOS_EXCLUDE_ADVANCED_FEATURES) */
111 AirspeedStateInitialize();
112 AirspeedStateConnectCallback(AirSpeedUpdatedCb
);
114 PIOS_DELTATIME_Init(&timeval
, UPDATE_EXPECTED
, UPDATE_MIN
, UPDATE_MAX
, UPDATE_ALPHA
);
116 callbackHandle
= PIOS_CALLBACKSCHEDULER_Create(&stabilizationInnerloopTask
, CALLBACK_PRIORITY
, CBTASK_PRIORITY
, CALLBACKINFO_RUNNING_STABILIZATION1
, STACK_SIZE_BYTES
);
117 GyroStateConnectCallback(GyroStateUpdatedCb
);
119 // schedule dead calls every FAILSAFE_TIMEOUT_MS to have the watchdog cleared
120 PIOS_CALLBACKSCHEDULER_Schedule(callbackHandle
, FAILSAFE_TIMEOUT_MS
, CALLBACK_UPDATEMODE_LATER
);
122 frame_is_multirotor
= (GetCurrentFrameType() == FRAME_TYPE_MULTIROTOR
);
123 measuredDterm_enabled
= (stabSettings
.settings
.MeasureBasedDTerm
== STABILIZATIONSETTINGS_MEASUREBASEDDTERM_TRUE
);
124 #if !defined(PIOS_EXCLUDE_ADVANCED_FEATURES)
125 // Settings for system identification
126 systemIdentTimeVal
= PIOS_DELAY_GetRaw();
127 #endif /* !defined(PIOS_EXCLUDE_ADVANCED_FEATURES) */
130 static float get_pid_scale_source_value()
134 switch (stabSettings
.stabBank
.ThrustPIDScaleSource
) {
135 case STABILIZATIONBANK_THRUSTPIDSCALESOURCE_MANUALCONTROLTHROTTLE
:
136 ManualControlCommandThrottleGet(&value
);
138 case STABILIZATIONBANK_THRUSTPIDSCALESOURCE_STABILIZATIONDESIREDTHRUST
:
139 StabilizationDesiredThrustGet(&value
);
141 case STABILIZATIONBANK_THRUSTPIDSCALESOURCE_ACTUATORDESIREDTHRUST
:
142 ActuatorDesiredThrustGet(&value
);
145 ActuatorDesiredThrustGet(&value
);
156 typedef struct pid_curve_scaler
{
161 static float pid_curve_value(const pid_curve_scaler
*scaler
)
163 float y
= y_on_curve(scaler
->x
, scaler
->points
, sizeof(scaler
->points
) / sizeof(scaler
->points
[0]));
165 return 1.0f
+ (IS_REAL(y
) ? y
: 0.0f
);
168 static pid_scaler
create_pid_scaler(int axis
)
172 // Always scaled with the this.
173 scaler
.p
= scaler
.i
= scaler
.d
= speedScaleFactor
;
175 if (stabSettings
.thrust_pid_scaling_enabled
[axis
][0]
176 || stabSettings
.thrust_pid_scaling_enabled
[axis
][1]
177 || stabSettings
.thrust_pid_scaling_enabled
[axis
][2]) {
178 const pid_curve_scaler curve_scaler
= {
179 .x
= get_pid_scale_source_value(),
181 { 0.00f
, stabSettings
.floatThrustPIDScaleCurve
[0] },
182 { 0.25f
, stabSettings
.floatThrustPIDScaleCurve
[1] },
183 { 0.50f
, stabSettings
.floatThrustPIDScaleCurve
[2] },
184 { 0.75f
, stabSettings
.floatThrustPIDScaleCurve
[3] },
185 { 1.00f
, stabSettings
.floatThrustPIDScaleCurve
[4] }
189 float curve_value
= pid_curve_value(&curve_scaler
);
191 if (stabSettings
.thrust_pid_scaling_enabled
[axis
][0]) {
192 scaler
.p
*= curve_value
;
194 if (stabSettings
.thrust_pid_scaling_enabled
[axis
][1]) {
195 scaler
.i
*= curve_value
;
197 if (stabSettings
.thrust_pid_scaling_enabled
[axis
][2]) {
198 scaler
.d
*= curve_value
;
206 * WARNING! This callback executes with critical flight control priority every
207 * time a gyroscope update happens do NOT put any time consuming calculations
208 * in this loop unless they really have to execute with every gyro update
210 static void stabilizationInnerloopTask()
212 // watchdog and error handling
214 #ifdef PIOS_INCLUDE_WDG
215 PIOS_WDG_UpdateFlag(PIOS_WDG_STABILIZATION
);
220 // check if outer loop keeps executing
221 if (stabSettings
.monitor
.rateupdates
> -64) {
222 stabSettings
.monitor
.rateupdates
--;
224 if (stabSettings
.monitor
.rateupdates
< -(2 * OUTERLOOP_SKIPCOUNT
)) {
225 // warning if rate loop skipped more than 2 execution
228 if (stabSettings
.monitor
.rateupdates
< -(4 * OUTERLOOP_SKIPCOUNT
)) {
229 // critical if rate loop skipped more than 4 executions
232 // check if gyro keeps updating
233 if (stabSettings
.monitor
.gyroupdates
< 1) {
234 // error if gyro didn't update at all!
237 if (stabSettings
.monitor
.gyroupdates
> 1) {
238 // warning if we missed a gyro update
241 if (stabSettings
.monitor
.gyroupdates
> 3) {
242 // critical if we missed 3 gyro updates
245 stabSettings
.monitor
.gyroupdates
= 0;
248 AlarmsSet(SYSTEMALARMS_ALARM_STABILIZATION
, SYSTEMALARMS_ALARM_CRITICAL
);
250 AlarmsSet(SYSTEMALARMS_ALARM_STABILIZATION
, SYSTEMALARMS_ALARM_ERROR
);
252 AlarmsSet(SYSTEMALARMS_ALARM_STABILIZATION
, SYSTEMALARMS_ALARM_WARNING
);
254 AlarmsClear(SYSTEMALARMS_ALARM_STABILIZATION
);
258 RateDesiredData rateDesired
;
259 ActuatorDesiredData actuator
;
260 StabilizationStatusInnerLoopData enabled
;
261 FlightStatusControlChainData cchain
;
263 RateDesiredGet(&rateDesired
);
264 ActuatorDesiredGet(&actuator
);
265 StabilizationStatusInnerLoopGet(&enabled
);
266 FlightStatusControlChainGet(&cchain
);
267 float *rate
= &rateDesired
.Roll
;
268 float *actuatorDesiredAxis
= &actuator
.Roll
;
271 bool multirotor
= (GetCurrentFrameType() == FRAME_TYPE_MULTIROTOR
); // check if frame is a multirotor
272 dT
= PIOS_DELTATIME_GetAverageSeconds(&timeval
);
274 StabilizationStatusOuterLoopData outerLoop
;
275 StabilizationStatusOuterLoopGet(&outerLoop
);
276 bool allowPiroComp
= true;
279 for (t
= 0; t
< AXES
; t
++) {
280 bool reinit
= (StabilizationStatusInnerLoopToArray(enabled
)[t
] != previous_mode
[t
]);
281 previous_mode
[t
] = StabilizationStatusInnerLoopToArray(enabled
)[t
];
283 if (t
< STABILIZATIONSTATUS_INNERLOOP_THRUST
) {
285 stabSettings
.innerPids
[t
].iAccumulator
= 0;
286 if (frame_is_multirotor
) {
287 // Multirotors should dump axis lock accumulators when unarmed or throttle is low.
288 // Fixed wing or ground vehicles can fly/drive with low throttle.
289 axis_lock_accum
[t
] = 0;
292 // Any self leveling on roll or pitch must prevent pirouette compensation
293 if (t
< STABILIZATIONSTATUS_INNERLOOP_YAW
&& StabilizationStatusOuterLoopToArray(outerLoop
)[t
] != STABILIZATIONSTATUS_OUTERLOOP_DIRECT
) {
294 allowPiroComp
= false;
296 switch (StabilizationStatusInnerLoopToArray(enabled
)[t
]) {
297 case STABILIZATIONSTATUS_INNERLOOP_VIRTUALFLYBAR
:
298 stabilization_virtual_flybar(gyro_filtered
[t
], rate
[t
], &actuatorDesiredAxis
[t
], dT
, reinit
, t
, &stabSettings
.settings
);
300 case STABILIZATIONSTATUS_INNERLOOP_AXISLOCK
:
301 if (fabsf(rate
[t
]) > stabSettings
.settings
.MaxAxisLockRate
) {
302 // While getting strong commands act like rate mode
303 axis_lock_accum
[t
] = 0;
305 // For weaker commands or no command simply attitude lock (almost) on no gyro change
306 axis_lock_accum
[t
] += (rate
[t
] - gyro_filtered
[t
]) * dT
;
307 axis_lock_accum
[t
] = boundf(axis_lock_accum
[t
], -stabSettings
.settings
.MaxAxisLock
, stabSettings
.settings
.MaxAxisLock
);
308 rate
[t
] = axis_lock_accum
[t
] * stabSettings
.settings
.AxisLockKp
;
310 // IMPORTANT: deliberately no "break;" here, execution continues with regular RATE control loop to avoid code duplication!
311 // keep order as it is, RATE must follow!
312 case STABILIZATIONSTATUS_INNERLOOP_RATE
:
314 // limit rate to maximum configured limits (once here instead of 5 times in outer loop)
315 rate
[t
] = boundf(rate
[t
],
316 -StabilizationBankMaximumRateToArray(stabSettings
.stabBank
.MaximumRate
)[t
],
317 StabilizationBankMaximumRateToArray(stabSettings
.stabBank
.MaximumRate
)[t
]
319 pid_scaler scaler
= create_pid_scaler(t
);
320 actuatorDesiredAxis
[t
] = pid_apply_setpoint(&stabSettings
.innerPids
[t
], &scaler
, rate
[t
], gyro_filtered
[t
], dT
, measuredDterm_enabled
);
323 case STABILIZATIONSTATUS_INNERLOOP_ACRO
:
326 stickinput
[0] = boundf(rate
[0] / stabSettings
.stabBank
.ManualRate
.Roll
, -1.0f
, 1.0f
);
327 stickinput
[1] = boundf(rate
[1] / stabSettings
.stabBank
.ManualRate
.Pitch
, -1.0f
, 1.0f
);
328 stickinput
[2] = boundf(rate
[2] / stabSettings
.stabBank
.ManualRate
.Yaw
, -1.0f
, 1.0f
);
329 rate
[t
] = boundf(rate
[t
],
330 -StabilizationBankMaximumRateToArray(stabSettings
.stabBank
.MaximumRate
)[t
],
331 StabilizationBankMaximumRateToArray(stabSettings
.stabBank
.MaximumRate
)[t
]
334 pid_scaler ascaler
= create_pid_scaler(t
);
335 ascaler
.i
*= boundf(1.0f
- (1.5f
* fabsf(stickinput
[t
])), 0.0f
, 1.0f
); // this prevents Integral from getting too high while controlled manually
336 float arate
= pid_apply_setpoint(&stabSettings
.innerPids
[t
], &ascaler
, rate
[t
], gyro_filtered
[t
], dT
, measuredDterm_enabled
);
337 float factor
= fabsf(stickinput
[t
]) * stabSettings
.acroInsanityFactors
[t
];
338 actuatorDesiredAxis
[t
] = factor
* stickinput
[t
] + (1.0f
- factor
) * arate
;
342 #if !defined(PIOS_EXCLUDE_ADVANCED_FEATURES)
343 case STABILIZATIONSTATUS_INNERLOOP_SYSTEMIDENT
:
345 static int8_t identIteration
= 0;
346 static float identOffsets
[3] = { 0 };
348 if (PIOS_DELAY_DiffuS(systemIdentTimeVal
) / 1000.0f
> SYSTEM_IDENT_PERIOD
) {
349 const float SCALE_BIAS
= 7.1f
;
350 SystemIdentStateBetaData systemIdentBeta
;
352 SystemIdentStateBetaGet(&systemIdentBeta
);
353 systemIdentTimeVal
= PIOS_DELAY_GetRaw();
354 identOffsets
[0] = 0.0f
;
355 identOffsets
[1] = 0.0f
;
356 identOffsets
[2] = 0.0f
;
357 identIteration
= (identIteration
+ 1) & 7;
358 // why does yaw change twice a cycle and roll/pitch change only once?
359 uint8_t index
= ((uint8_t[]) { '\2', '\0', '\2', '\0', '\2', '\1', '\2', '\1' }
361 float scale
= expapprox(SCALE_BIAS
- SystemIdentStateBetaToArray(systemIdentBeta
)[index
]);
362 // if roll or pitch limit to 25% of range
363 if (identIteration
& 1) {
368 // else it is yaw that can be a little more radical
374 if (identIteration
& 2) {
377 identOffsets
[index
] = scale
;
379 // when identIteration==0: identOffsets[2] = yaw_scale;
380 // when identIteration==1: identOffsets[0] = roll_scale;
381 // when identIteration==2: identOffsets[2] = -yaw_scale;
382 // when identIteration==3: identOffsets[0] = -roll_scale;
383 // when identIteration==4: identOffsets[2] = yaw_scale;
384 // when identIteration==5: identOffsets[1] = pitch_scale;
385 // when identIteration==6: identOffsets[2] = -yaw_scale;
386 // when identIteration==7: identOffsets[1] = -pitch_scale;
387 // each change has one axis with an offset
388 // and another axis coming back to zero from having an offset
391 rate
[t
] = boundf(rate
[t
],
392 -StabilizationBankMaximumRateToArray(stabSettings
.stabBank
.MaximumRate
)[t
],
393 StabilizationBankMaximumRateToArray(stabSettings
.stabBank
.MaximumRate
)[t
]
395 pid_scaler scaler
= create_pid_scaler(t
);
396 actuatorDesiredAxis
[t
] = pid_apply_setpoint(&stabSettings
.innerPids
[t
], &scaler
, rate
[t
], gyro_filtered
[t
], dT
, measuredDterm_enabled
);
397 actuatorDesiredAxis
[t
] += identOffsets
[t
];
400 #endif /* !defined(PIOS_EXCLUDE_ADVANCED_FEATURES) */
402 case STABILIZATIONSTATUS_INNERLOOP_DIRECT
:
404 actuatorDesiredAxis
[t
] = rate
[t
];
408 switch (StabilizationStatusInnerLoopToArray(enabled
)[t
]) {
409 case STABILIZATIONSTATUS_INNERLOOP_CRUISECONTROL
:
410 actuatorDesiredAxis
[t
] = cruisecontrol_apply_factor(rate
[t
]);
412 case STABILIZATIONSTATUS_INNERLOOP_DIRECT
:
414 actuatorDesiredAxis
[t
] = rate
[t
];
420 // we only need to clamp the desired axis to a sane range if the frame is not a multirotor type
421 // we don't want to do any clamping until after the motors are calculated and scaled.
422 // need to figure out what to do with a tricopter tail servo.
423 actuatorDesiredAxis
[t
] = boundf(actuatorDesiredAxis
[t
], -1.0f
, 1.0f
);
427 actuator
.UpdateTime
= dT
* 1000;
429 if (cchain
.Stabilization
== FLIGHTSTATUS_CONTROLCHAIN_TRUE
) {
430 ActuatorDesiredSet(&actuator
);
432 // Force all axes to reinitialize when engaged
433 for (t
= 0; t
< AXES
; t
++) {
434 previous_mode
[t
] = 255;
438 if (allowPiroComp
&& stabSettings
.stabBank
.EnablePiroComp
== STABILIZATIONBANK_ENABLEPIROCOMP_TRUE
&& stabSettings
.innerPids
[0].iLim
> 1e-3f
&& stabSettings
.innerPids
[1].iLim
> 1e-3f
) {
439 // attempted piro compensation - rotate pitch and yaw integrals (experimental)
440 float angleYaw
= DEG2RAD(gyro_filtered
[2] * dT
);
441 float sinYaw
= sinf(angleYaw
);
442 float cosYaw
= cosf(angleYaw
);
443 float rollAcc
= stabSettings
.innerPids
[0].iAccumulator
/ stabSettings
.innerPids
[0].iLim
;
444 float pitchAcc
= stabSettings
.innerPids
[1].iAccumulator
/ stabSettings
.innerPids
[1].iLim
;
445 stabSettings
.innerPids
[0].iAccumulator
= stabSettings
.innerPids
[0].iLim
* (cosYaw
* rollAcc
+ sinYaw
* pitchAcc
);
446 stabSettings
.innerPids
[1].iAccumulator
= stabSettings
.innerPids
[1].iLim
* (cosYaw
* pitchAcc
- sinYaw
* rollAcc
);
450 FlightStatusArmedOptions armed
;
451 FlightStatusArmedGet(&armed
);
452 float throttleDesired
;
453 ManualControlCommandThrottleGet(&throttleDesired
);
454 if (armed
!= FLIGHTSTATUS_ARMED_ARMED
||
455 ((stabSettings
.settings
.LowThrottleZeroIntegral
== STABILIZATIONSETTINGS_LOWTHROTTLEZEROINTEGRAL_TRUE
) && throttleDesired
< 0)) {
456 // Force all axes to reinitialize when engaged
457 for (t
= 0; t
< AXES
; t
++) {
458 previous_mode
[t
] = 255;
462 PIOS_CALLBACKSCHEDULER_Schedule(callbackHandle
, FAILSAFE_TIMEOUT_MS
, CALLBACK_UPDATEMODE_LATER
);
466 static void GyroStateUpdatedCb(__attribute__((unused
)) UAVObjEvent
*ev
)
468 GyroStateData gyroState
;
470 GyroStateGet(&gyroState
);
472 gyro_filtered
[0] = gyro_filtered
[0] * stabSettings
.gyro_alpha
+ gyroState
.x
* (1 - stabSettings
.gyro_alpha
);
473 gyro_filtered
[1] = gyro_filtered
[1] * stabSettings
.gyro_alpha
+ gyroState
.y
* (1 - stabSettings
.gyro_alpha
);
474 gyro_filtered
[2] = gyro_filtered
[2] * stabSettings
.gyro_alpha
+ gyroState
.z
* (1 - stabSettings
.gyro_alpha
);
476 PIOS_CALLBACKSCHEDULER_Dispatch(callbackHandle
);
477 stabSettings
.monitor
.gyroupdates
++;
481 static void AirSpeedUpdatedCb(__attribute__((unused
)) UAVObjEvent
*ev
)
483 // Scale PID coefficients based on current airspeed estimation - needed for fixed wing planes
484 AirspeedStateData airspeedState
;
486 AirspeedStateGet(&airspeedState
);
487 if (stabSettings
.settings
.ScaleToAirspeed
< 0.1f
|| airspeedState
.CalibratedAirspeed
< 0.1f
) {
488 // feature has been turned off
489 speedScaleFactor
= 1.0f
;
491 // scale the factor to be 1.0 at the specified airspeed (for example 10m/s) but scaled by 1/speed^2
492 speedScaleFactor
= boundf((stabSettings
.settings
.ScaleToAirspeed
* stabSettings
.settings
.ScaleToAirspeed
) / (airspeedState
.CalibratedAirspeed
* airspeedState
.CalibratedAirspeed
),
493 stabSettings
.settings
.ScaleToAirspeedLimits
.Min
,
494 stabSettings
.settings
.ScaleToAirspeedLimits
.Max
);