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"
11 * @file AutoTune/autotune.c
12 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016-2017.
13 * dRonin, http://dRonin.org/, Copyright (C) 2015-2016
14 * Tau Labs, http://taulabs.org, Copyright (C) 2013-2014
15 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
16 * @brief Automatic PID tuning module.
18 * @see The GNU Public License (GPL) Version 3
20 *****************************************************************************/
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 3 of the License, or
25 * (at your option) any later version.
27 * This program is distributed in the hope that it will be useful, but
28 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
29 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
32 * You should have received a copy of the GNU General Public License along
33 * with this program; if not, write to the Free Software Foundation, Inc.,
34 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 #include <openpilot.h>
39 #include <flightstatus.h>
40 #include <manualcontrolcommand.h>
41 #include <manualcontrolsettings.h>
42 #include <flightmodesettings.h>
43 #include <gyrostate.h>
44 #include <actuatordesired.h>
45 #include <stabilizationdesired.h>
46 #include <stabilizationsettings.h>
47 #include <systemidentsettings.h>
48 #include <systemidentstate.h>
49 #include <pios_board_info.h>
50 #include <systemsettings.h>
52 #include <stabilization.h>
53 #include <hwsettings.h>
54 #include <stabilizationsettingsbank1.h>
55 #include <stabilizationsettingsbank2.h>
56 #include <stabilizationsettingsbank3.h>
57 #include <accessorydesired.h>
59 #if defined(PIOS_EXCLUDE_ADVANCED_FEATURES)
60 #define powapprox fastpow
61 #define expapprox fastexp
63 #define powapprox powf
64 #define expapprox expf
65 #endif /* defined(PIOS_EXCLUDE_ADVANCED_FEATURES) */
69 #undef STACK_SIZE_BYTES
70 // Pull Request version tested on Sparky2. 292 bytes of stack left when configured with 1340
71 // Beware that Nano needs 156 bytes more stack than Sparky2
72 #define STACK_SIZE_BYTES 1340
73 #define TASK_PRIORITY (tskIDLE_PRIORITY + 1)
78 #if !defined(AT_QUEUE_NUMELEM)
79 #define AT_QUEUE_NUMELEM 18
82 #define TASK_STARTUP_DELAY_MS 250 /* delay task startup this much, waiting on accessory valid */
83 #define NOT_AT_MODE_DELAY_MS 50 /* delay this many ms if not in autotune mode */
84 #define NOT_AT_MODE_RATE (1000.0f / NOT_AT_MODE_DELAY_MS) /* this many loops per second if not in autotune mode */
85 #define SMOOTH_QUICK_FLUSH_DELAY 0.5f /* wait this long after last change to flush to permanent storage */
86 #define SMOOTH_QUICK_FLUSH_TICKS (SMOOTH_QUICK_FLUSH_DELAY * NOT_AT_MODE_RATE) /* this many ticks after last change to flush to permanent storage */
88 #define MAX_PTS_PER_CYCLE 4 /* max gyro updates to process per loop see YIELD_MS and consider gyro rate */
89 #define INIT_TIME_DELAY_MS 100 /* delay to allow stab bank, etc. to be populated after flight mode switch change detection */
90 #define SYSTEMIDENT_TIME_DELAY_MS 2000 /* delay before starting systemident (shaking) flight mode */
91 #define INIT_TIME_DELAY2_MS 2500 /* delay before starting to capture data */
92 #define YIELD_MS 2 /* delay this long between processing sessions see MAX_PTS_PER_CYCLE and consider gyro rate */
94 // CheckSettings() returned error bits
97 #define ROLL_BETA_LOW 4
98 #define PITCH_BETA_LOW 8
99 #define YAW_BETA_LOW 16
100 #define TAU_TOO_LONG 32
101 #define TAU_TOO_SHORT 64
102 #define CPU_TOO_SLOW 128
104 #define FMS_TOGGLE_STEP_DISABLED 0.0f
107 enum AUTOTUNE_STATE
{ AT_INIT
, AT_INIT_DELAY
, AT_INIT_DELAY2
, AT_START
, AT_RUN
, AT_FINISHED
, AT_WAITING
};
108 struct at_queued_data
{
109 float y
[3]; /* Gyro measurements */
110 float u
[3]; /* Actuator desired */
111 float throttle
; /* Throttle desired */
112 uint32_t gyroStateCallbackTimestamp
; /* PIOS_DELAY_GetRaw() time of GyroState callback */
113 uint32_t sensorReadTimestamp
; /* PIOS_DELAY_GetRaw() time of sensor read */
118 static SystemIdentSettingsData systemIdentSettings
;
119 // save memory because metadata is only briefly accessed, when normal data struct is not being used
120 // unnamed union issues a warning
122 SystemIdentStateData systemIdentState
;
123 UAVObjMetadata systemIdentStateMetaData
;
125 static StabilizationBankManualRateData manualRate
;
126 static xTaskHandle taskHandle
;
127 static xQueueHandle atQueue
;
128 static float gX
[AF_NUMX
] = { 0 };
129 static float gP
[AF_NUMP
] = { 0 };
130 static float gyroReadTimeAverage
;
131 static float gyroReadTimeAverageAlpha
;
132 static float gyroReadTimeAverageAlphaAlpha
;
134 static float smoothQuickValue
;
135 static float flightModeSwitchToggleStepValue
;
136 static volatile uint32_t atPointsSpilled
;
137 static uint32_t throttleAccumulator
;
138 static uint8_t rollMax
, pitchMax
;
139 static int8_t accessoryToUse
;
140 static bool moduleEnabled
;
144 static void AtNewGyroData(UAVObjEvent
*ev
);
145 static bool AutoTuneFoundInFMS();
146 static void AutoTuneTask(void *parameters
);
147 static void AfInit(float X
[AF_NUMX
], float P
[AF_NUMP
]);
148 static void AfPredict(float X
[AF_NUMX
], float P
[AF_NUMP
], const float u_in
[3], const float gyro
[3], const float dT_s
, const float t_in
);
149 static bool CheckFlightModeSwitchForPidRequest(uint8_t flightMode
);
150 static uint8_t CheckSettings();
151 static uint8_t CheckSettingsRaw();
152 static void ComputeStabilizationAndSetPidsFromDampAndNoise(float damp
, float noise
);
153 static void FlightModeSettingsUpdatedCb(UAVObjEvent
*ev
);
154 static void InitSystemIdent(bool loadDefaults
);
155 static void UpdateSmoothQuickSource(uint8_t smoothQuickSource
, bool loadDefaults
);
156 static void ProportionPidsSmoothToQuick();
157 static void UpdateSystemIdentState(const float *X
, const float *noise
, float dT_s
, uint32_t predicts
, uint32_t spills
, float hover_throttle
);
158 static void UpdateStabilizationDesired(bool doingIdent
);
162 * Initialise the module, called on startup
163 * \returns 0 on success or -1 if initialisation failed
165 int32_t AutoTuneInitialize(void)
167 HwSettingsOptionalModulesData optionalModules
;
169 HwSettingsOptionalModulesGet(&optionalModules
);
171 #if defined(MODULE_AUTOTUNE_BUILTIN)
172 moduleEnabled
= true;
173 optionalModules
.AutoTune
= HWSETTINGS_OPTIONALMODULES_ENABLED
;
174 HwSettingsOptionalModulesSet(&optionalModules
);
176 if (optionalModules
.AutoTune
== HWSETTINGS_OPTIONALMODULES_ENABLED
) {
177 // even though the AutoTune module is automatically enabled
178 // (below, when the flight mode switch is configured to use autotune)
179 // there are use cases where the user may even want it enabled without being on the FMS
180 // that allows PIDs to be adjusted in flight
181 moduleEnabled
= true;
183 // if the user did not manually enable the autotune module
184 // do it for them if they have autotune on their flight mode switch
185 moduleEnabled
= AutoTuneFoundInFMS();
187 #endif /* defined(MODULE_AUTOTUNE_BUILTIN) */
190 AccessoryDesiredInitialize();
191 ActuatorDesiredInitialize();
192 FlightStatusInitialize();
193 GyroStateInitialize();
194 ManualControlCommandInitialize();
195 StabilizationBankInitialize();
196 SystemIdentStateInitialize();
198 atQueue
= xQueueCreate(AT_QUEUE_NUMELEM
, sizeof(struct at_queued_data
));
200 moduleEnabled
= false;
203 if (!moduleEnabled
) {
204 // only need to watch for enabling AutoTune in FMS if AutoTune module is _not_ running
205 FlightModeSettingsConnectCallback(FlightModeSettingsUpdatedCb
);
213 * Initialise the module, called on startup
214 * \returns 0 on success or -1 if initialisation failed
216 int32_t AutoTuneStart(void)
218 // Start main task if it is enabled
220 GyroStateConnectCallback(AtNewGyroData
);
221 xTaskCreate(AutoTuneTask
, "AutoTune", STACK_SIZE_BYTES
/ 4, NULL
, TASK_PRIORITY
, &taskHandle
);
222 PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_AUTOTUNE
, taskHandle
);
228 MODULE_INITCALL(AutoTuneInitialize
, AutoTuneStart
);
232 * Module thread, should not return.
234 static void AutoTuneTask(__attribute__((unused
)) void *parameters
)
236 float noise
[3] = { 0 };
238 uint32_t lastUpdateTime
= 0; // initialization is only for compiler warning
239 uint32_t lastTime
= 0;
240 uint32_t measureTime
= 0;
241 uint32_t updateCounter
= 0;
242 enum AUTOTUNE_STATE state
= AT_INIT
;
243 uint8_t currentSmoothQuickSource
= 0;
244 bool saveSiNeeded
= false;
245 bool savePidNeeded
= false;
247 // wait for the accessory values to stabilize
248 // otherwise they come up as zero, then change to their real value
249 // and that causes the PIDs to be re-exported (if smoothquick is active), which the user may not want
250 vTaskDelay(TASK_STARTUP_DELAY_MS
/ portTICK_RATE_MS
);
252 // get max attitude / max rate
253 // for use in generating Attitude mode commands from this module
254 // note that the values could change when they change flight mode (and the associated bank)
255 StabilizationBankRollMaxGet(&rollMax
);
256 StabilizationBankPitchMaxGet(&pitchMax
);
257 StabilizationBankManualRateGet(&manualRate
);
258 // correctly set accessoryToUse and flightModeSwitchTogglePosition
259 // based on what is in SystemIdent
260 // so that the user can use the PID smooth->quick slider in flights following the autotune flight
261 InitSystemIdent(false);
262 smoothQuickValue
= systemIdentSettings
.SmoothQuickValue
;
266 bool doingIdent
= false;
267 bool canSleep
= true;
268 FlightStatusData flightStatus
;
269 FlightStatusGet(&flightStatus
);
271 if (flightStatus
.Armed
== FLIGHTSTATUS_ARMED_DISARMED
) {
273 saveSiNeeded
= false;
274 // Save SystemIdentSettings to permanent settings
275 UAVObjSave(SystemIdentSettingsHandle(), 0);
278 savePidNeeded
= false;
279 // Save PIDs to permanent settings
280 switch (systemIdentSettings
.DestinationPidBank
) {
281 case SYSTEMIDENTSETTINGS_DESTINATIONPIDBANK_BANK1
:
282 UAVObjSave(StabilizationSettingsBank1Handle(), 0);
284 case SYSTEMIDENTSETTINGS_DESTINATIONPIDBANK_BANK2
:
285 UAVObjSave(StabilizationSettingsBank2Handle(), 0);
287 case SYSTEMIDENTSETTINGS_DESTINATIONPIDBANK_BANK3
:
288 UAVObjSave(StabilizationSettingsBank3Handle(), 0);
294 // if using flight mode switch "quick toggle 3x" to "try smooth -> quick PIDs" is enabled
295 // and user toggled into and back out of AutoTune three times in the last two seconds
296 // and the autotune data gathering is complete
297 // and the autotune data gathered is good
298 // note: CheckFlightModeSwitchForPidRequest(mode) only returns true if current mode is not autotune
299 if (flightModeSwitchToggleStepValue
> FMS_TOGGLE_STEP_DISABLED
&& CheckFlightModeSwitchForPidRequest(flightStatus
.FlightMode
)
300 && systemIdentSettings
.Complete
&& !CheckSettings()) {
301 if (flightStatus
.Armed
== FLIGHTSTATUS_ARMED_ARMED
) {
302 // if user toggled while armed set PID's to next in sequence
303 // if you assume that smoothest is -1 and quickest is +1
304 // this corresponds to 0,+.50,+1.00,-1.00,-.50 (for 5 position toggle)
305 smoothQuickValue
+= flightModeSwitchToggleStepValue
;
306 if (smoothQuickValue
> 1.001f
) {
307 smoothQuickValue
= -1.0f
;
309 // Assume the value is 0
310 if (fabsf(smoothQuickValue
) < 0.001f
) {
311 smoothQuickValue
= 0.0f
;
314 // if they did the 3x FMS toggle while disarmed, set PID's back to the middle of smoothquick
315 smoothQuickValue
= 0.0f
;
317 // calculate PIDs based on new smoothQuickValue and save to the PID bank
318 ProportionPidsSmoothToQuick();
319 // save new PIDs permanently when / if disarmed
320 savePidNeeded
= true;
321 // we also save the new knob/toggle value for startup next time
322 // this keeps the PIDs in sync with the toggle position
326 // Check if the SmoothQuickSource changed,
327 // allow config changes without reboot or reinit
328 uint8_t smoothQuickSource
;
329 SystemIdentSettingsSmoothQuickSourceGet(&smoothQuickSource
);
330 if (smoothQuickSource
!= currentSmoothQuickSource
) {
331 UpdateSmoothQuickSource(smoothQuickSource
, true);
332 currentSmoothQuickSource
= smoothQuickSource
;
335 //////////////////////////////////////////////////////////////////////////////////////
336 // if configured to use a slider for smooth-quick and the autotune module is running
337 // (note that the module can be automatically or manually enabled)
338 // then the smooth-quick slider is always active (when not actually in autotune mode)
340 // when the slider is active it will immediately change the PIDs
341 // and it will schedule the PIDs to be written to permanent storage
343 // if the FC is disarmed, the perm write will happen on next loop
344 // but if the FC is armed, the perm write will only occur when the FC goes disarmed
345 //////////////////////////////////////////////////////////////////////////////////////
347 // we don't want it saving to permanent storage many times
348 // while the user is moving the knob once, so wait till the knob stops moving
349 static uint8_t savePidDelay
;
350 // any time we are not in AutoTune mode:
351 // - the user may be using the accessory0-3 knob/slider to request PID changes
352 // - the state machine needs to be reset
353 // - the local version of Attitude mode gets skipped
354 if (flightStatus
.FlightMode
!= FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE
) {
355 // if accessory0-3 is configured as a PID changing slider/knob over the smooth to quick range
356 // and FC is not currently running autotune
357 // and accessory0-3 changed by at least 1/85 of full range (2)
358 // (don't bother checking to see if the requested accessory# is configured properly
359 // if it isn't, the value will be 0 which is the center of [-1,1] anyway)
360 if (accessoryToUse
!= -1 && systemIdentSettings
.Complete
&& !CheckSettings()) {
361 AccessoryDesiredData accessoryValue
;
362 AccessoryDesiredInstGet(accessoryToUse
, &accessoryValue
);
363 // if the accessory changed more than 2 percent of total range (~20µs)
364 // the smoothQuickValue will be changed
365 if (fabsf(smoothQuickValue
- accessoryValue
.AccessoryVal
) > 0.02f
) {
366 smoothQuickValue
= accessoryValue
.AccessoryVal
;
367 // calculate PIDs based on new smoothQuickValue and save to the PID bank
368 ProportionPidsSmoothToQuick();
369 // this schedules the first possible write of the PIDs to occur a fraction of a second or so from now
370 // and changes the scheduled time if it is already scheduled
371 savePidDelay
= SMOOTH_QUICK_FLUSH_TICKS
;
372 } else if (savePidDelay
&& --savePidDelay
== 0) {
373 // this flags that the PIDs can be written to permanent storage right now
374 // but they will only be written when the FC is disarmed
375 // so this means immediate (after NOT_AT_MODE_DELAY_MS) or wait till FC is disarmed
376 savePidNeeded
= true;
377 // we also save the new knob/toggle value for startup next time
378 // this avoids rewriting the PIDs at each startup
379 // because knob is unknown / not where it is expected / looks like knob moved
386 vTaskDelay(NOT_AT_MODE_DELAY_MS
/ portTICK_RATE_MS
);
394 // beware that control comes here every time the user toggles the flight mode switch into AutoTune
395 // and it isn't appropriate to reset the main state here
396 // init must wait until after a delay has passed:
397 // - to make sure they intended to stay in this mode
398 // - to wait for the stab bank to get populated with the new bank info
399 // This is a race. It is possible that flightStatus.FlightMode has been changed,
400 // but the stab bank hasn't been changed yet.
401 state
= AT_INIT_DELAY
;
402 lastUpdateTime
= xTaskGetTickCount();
406 diffTime
= xTaskGetTickCount() - lastUpdateTime
;
407 // after a small delay, get the stab bank values and SystemIdentSettings in case they changed
408 // this is a very small delay (100ms), so "quick 3x fms toggle" gets in here
409 if (diffTime
> INIT_TIME_DELAY_MS
) {
410 // do these here so the user has at most a 1/10th second
411 // with controls that use the previous bank's rates
412 StabilizationBankRollMaxGet(&rollMax
);
413 StabilizationBankPitchMaxGet(&pitchMax
);
414 StabilizationBankManualRateGet(&manualRate
);
415 // load SystemIdentSettings so that they can change it
416 // and do smooth-quick on changed values
417 InitSystemIdent(false);
418 // wait for FC to arm in case they are doing this without a flight mode switch
419 // that causes the 2+ second delay that follows to happen after arming
420 // which gives them a chance to take off before the shakes start
421 // the FC must be armed and if we check here it also allows switchless setup to use autotune
422 if (flightStatus
.Armed
== FLIGHTSTATUS_ARMED_ARMED
) {
423 state
= AT_INIT_DELAY2
;
424 lastUpdateTime
= xTaskGetTickCount();
430 // delay for 2 seconds before actually starting the SystemIdent flight mode and AutoTune.
431 // that allows the user to get his fingers on the sticks
432 // and avoids starting the AutoTune if the user is toggling the flight mode switch
433 // to select other PIDs on the "simulated Smooth Quick slider".
434 // or simply "passing through" this flight mode to get to another flight mode
435 diffTime
= xTaskGetTickCount() - lastUpdateTime
;
436 // after 2 seconds start systemident flight mode
437 if (diffTime
> SYSTEMIDENT_TIME_DELAY_MS
) {
438 // load default tune and clean up any NANs from previous tune
439 InitSystemIdent(true);
441 // and write it out to the UAVO so innerloop can see the default values
442 UpdateSystemIdentState(gX
, NULL
, 0.0f
, 0, 0, 0.0f
);
443 // before starting SystemIdent stabilization mode
450 diffTime
= xTaskGetTickCount() - lastUpdateTime
;
452 // after an additional short delay, start capturing data
453 if (diffTime
> INIT_TIME_DELAY2_MS
) {
455 // save SI data even if partial or bad, aids in diagnostics
457 // don't save PIDs until data gathering is complete
458 // and the complete data has been sanity checked
459 savePidNeeded
= false;
460 // get the tuning duration in case the user just changed it
461 measureTime
= (uint32_t)systemIdentSettings
.TuningDuration
* (uint32_t)1000;
462 // init the "previous packet timestamp"
463 lastTime
= PIOS_DELAY_GetRaw();
464 /* Drain the queue of all current data */
465 xQueueReset(atQueue
);
466 /* And reset the point spill counter */
469 throttleAccumulator
= 0;
472 lastUpdateTime
= xTaskGetTickCount();
477 diffTime
= xTaskGetTickCount() - lastUpdateTime
;
480 // 4 gyro samples per cycle
482 // that is 500 gyro samples per second if it sleeps each time
483 // actually less than 500 because it cycle time is processing time + 2ms
484 for (int i
= 0; i
< MAX_PTS_PER_CYCLE
; i
++) {
485 struct at_queued_data pt
;
486 /* Grab an autotune point */
487 if (xQueueReceive(atQueue
, &pt
, 0) != pdTRUE
) {
488 /* We've drained the buffer fully */
492 /* calculate time between successive points */
493 dT_s
= PIOS_DELAY_DiffuS2(lastTime
, pt
.gyroStateCallbackTimestamp
) * 1.0e-6f
;
494 /* This is for the first point, but
495 * also if we have extended drops */
496 if (dT_s
> 5.0f
/ PIOS_SENSOR_RATE
) {
497 dT_s
= 5.0f
/ PIOS_SENSOR_RATE
;
499 lastTime
= pt
.gyroStateCallbackTimestamp
;
500 // original algorithm handles time from GyroStateGet() to detected motion
501 // this algorithm also includes the time from raw gyro read to GyroStateGet()
502 gyroReadTimeAverage
= gyroReadTimeAverage
* alpha
503 + PIOS_DELAY_DiffuS2(pt
.sensorReadTimestamp
, pt
.gyroStateCallbackTimestamp
) * 1.0e-6f
* (1.0f
- alpha
);
504 alpha
= alpha
* gyroReadTimeAverageAlphaAlpha
+ gyroReadTimeAverageAlpha
* (1.0f
- gyroReadTimeAverageAlphaAlpha
);
505 AfPredict(gX
, gP
, pt
.u
, pt
.y
, dT_s
, pt
.throttle
);
506 for (int j
= 0; j
< 3; ++j
) {
507 const float NOISE_ALPHA
= 0.9997f
; // 10 second time constant at 300 Hz
508 noise
[j
] = NOISE_ALPHA
* noise
[j
] + (1 - NOISE_ALPHA
) * (pt
.y
[j
] - gX
[j
]) * (pt
.y
[j
] - gX
[j
]);
510 // This will work up to 8kHz with an 89% throttle position before overflow
511 throttleAccumulator
+= 10000 * pt
.throttle
;
512 // Update uavo every 256 cycles to avoid
514 if (((++updateCounter
) & 0xff) == 0) {
515 float hoverThrottle
= ((float)(throttleAccumulator
/ updateCounter
)) / 10000.0f
;
516 UpdateSystemIdentState(gX
, noise
, dT_s
, updateCounter
, atPointsSpilled
, hoverThrottle
);
519 if (diffTime
> measureTime
) { // Move on to next state
520 // permanent flag that AT is complete and PIDs can be calculated
526 // update with info from the last few data points
527 if ((updateCounter
& 0xff) != 0) {
528 float hoverThrottle
= ((float)(throttleAccumulator
/ updateCounter
)) / 10000.0f
;
529 UpdateSystemIdentState(gX
, noise
, dT_s
, updateCounter
, atPointsSpilled
, hoverThrottle
);
531 // data is automatically considered bad if FC was disarmed at the time AT completed
532 if (flightStatus
.Armed
== FLIGHTSTATUS_ARMED_ARMED
) {
533 // always calculate and save PIDs if disabling sanity checks
534 if (!CheckSettings()) {
535 ProportionPidsSmoothToQuick();
536 savePidNeeded
= true;
537 // mark these results as good in the log settings so they can be viewed in playback
538 u
.systemIdentState
.Complete
= true;
539 SystemIdentStateCompleteSet(&u
.systemIdentState
.Complete
);
540 // mark these results as good in the permanent settings so they can be used next flight too
541 // this is written to the UAVO below, outside of the ARMED and CheckSettings() checks
542 systemIdentSettings
.Complete
= true;
544 // always raise an alarm if sanity checks failed
545 // even if disabling sanity checks
546 // that way user can still see that they failed
547 uint8_t failureBits
= CheckSettingsRaw();
549 // raise a warning that includes failureBits to indicate what failed
550 ExtendedAlarmsSet(SYSTEMALARMS_ALARM_SYSTEMCONFIGURATION
, SYSTEMALARMS_ALARM_WARNING
,
551 SYSTEMALARMS_EXTENDEDALARMSTATUS_AUTOTUNE
, failureBits
);
554 // need to save UAVO after .Complete gets potentially set
555 // SystemIdentSettings needs the whole UAVO saved so it is saved outside the previous checks
556 SystemIdentSettingsSet(&systemIdentSettings
);
562 // after tuning, wait here till user switches to another flight mode
567 // fly in Attitude mode or in SystemIdent mode
568 UpdateStabilizationDesired(doingIdent
);
571 vTaskDelay(YIELD_MS
/ portTICK_RATE_MS
);
577 // FlightModeSettings callback
578 // determine if autotune is enabled in the flight mode switch
579 static bool AutoTuneFoundInFMS()
582 FlightModeSettingsFlightModePositionOptions fms
[FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_NUMELEM
];
583 uint8_t num_flightMode
;
585 FlightModeSettingsFlightModePositionGet(fms
);
586 ManualControlSettingsFlightModeNumberGet(&num_flightMode
);
587 for (uint8_t i
= 0; i
< num_flightMode
; ++i
) {
588 if (fms
[i
] == FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_AUTOTUNE
) {
597 // gyro sensor callback
598 // get gyro data and actuatordesired into a packet
599 // and put it in the queue for later processing
600 static void AtNewGyroData(UAVObjEvent
*ev
)
602 static struct at_queued_data q_item
;
603 static bool last_sample_unpushed
= false;
605 ActuatorDesiredData actuators
;
608 if (!ev
|| !ev
->obj
|| ev
->instId
!= 0 || ev
->event
!= EV_UPDATED
) {
612 // object will at times change asynchronously so must copy data here, with locking
613 // and do it as soon as possible
614 timestamp
= PIOS_DELAY_GetRaw();
616 ActuatorDesiredGet(&actuators
);
618 if (last_sample_unpushed
) {
619 /* Last time we were unable to queue up the gyro data.
620 * Try again, last chance! */
621 if (xQueueSend(atQueue
, &q_item
, 0) != pdTRUE
) {
626 q_item
.gyroStateCallbackTimestamp
= timestamp
;
627 q_item
.y
[0] = q_item
.y
[0] * stabSettings
.gyro_alpha
+ gyro
.x
* (1 - stabSettings
.gyro_alpha
);
628 q_item
.y
[1] = q_item
.y
[1] * stabSettings
.gyro_alpha
+ gyro
.y
* (1 - stabSettings
.gyro_alpha
);
629 q_item
.y
[2] = q_item
.y
[2] * stabSettings
.gyro_alpha
+ gyro
.z
* (1 - stabSettings
.gyro_alpha
);
630 q_item
.u
[0] = actuators
.Roll
;
631 q_item
.u
[1] = actuators
.Pitch
;
632 q_item
.u
[2] = actuators
.Yaw
;
633 q_item
.throttle
= actuators
.Thrust
;
634 q_item
.sensorReadTimestamp
= gyro
.SensorReadTimestamp
;
636 if (xQueueSend(atQueue
, &q_item
, 0) != pdTRUE
) {
637 last_sample_unpushed
= true;
639 last_sample_unpushed
= false;
644 // this callback is only enabled if the AutoTune module is not running
645 // if it sees that AutoTune was added to the FMS it issues BOOT and ? alarms
646 static void FlightModeSettingsUpdatedCb(__attribute__((unused
)) UAVObjEvent
*ev
)
648 if (AutoTuneFoundInFMS()) {
649 ExtendedAlarmsSet(SYSTEMALARMS_ALARM_BOOTFAULT
, SYSTEMALARMS_ALARM_CRITICAL
, SYSTEMALARMS_EXTENDEDALARMSTATUS_REBOOTREQUIRED
, 0);
654 // check for the user quickly toggling the flight mode switch
655 // into and out of AutoTune, 3 times
656 // that is a signal that the user wants to try the next PID settings
657 // on the scale from smooth to quick
658 // when it exceeds the quickest setting, it starts back at the smoothest setting
659 static bool CheckFlightModeSwitchForPidRequest(uint8_t flightMode
)
661 static uint32_t lastUpdateTime
;
662 static uint8_t flightModePrev
;
663 static uint8_t counter
;
666 // only count transitions into and out of autotune
667 if ((flightModePrev
== FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE
) ^ (flightMode
== FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE
)) {
668 flightModePrev
= flightMode
;
669 updateTime
= xTaskGetTickCount();
670 // if it has been over 2 seconds, reset the counter
671 if (updateTime
- lastUpdateTime
> 2000) {
674 // if the counter is reset, start a new time period
676 lastUpdateTime
= updateTime
;
678 // if flight mode has toggled into autotune 3 times but is currently not autotune
679 if (++counter
>= 5 && flightMode
!= FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE
) {
689 // read SystemIdent uavos, update the local structures
690 // and set some flags based on the values
691 // it is used two ways:
692 // - on startup it reads settings so the user can reuse an old tune with smooth-quick
693 // - at tune time, it inits the state to default values of uavo xml file, in preparation for tuning
694 static void InitSystemIdent(bool loadDefaults
)
696 SystemIdentSettingsGet(&systemIdentSettings
);
698 // get these 10.0 10.0 7.0 -4.0 from default values of SystemIdent (.Beta and .Tau)
699 // so that if they are changed there (mainly for future code changes), they will be changed here too
700 // save metadata from being changed by the following SetDefaults()
701 SystemIdentStateGetMetadata(&u
.systemIdentStateMetaData
);
702 SystemIdentStateSetDefaults(SystemIdentStateHandle(), 0);
703 SystemIdentStateSetMetadata(&u
.systemIdentStateMetaData
);
704 SystemIdentStateGet(&u
.systemIdentState
);
705 // Tau, GyroReadTimeAverage, Beta, and the Complete flag get default values
706 // in preparation for running AutoTune
707 systemIdentSettings
.Tau
= u
.systemIdentState
.Tau
;
708 systemIdentSettings
.GyroReadTimeAverage
= u
.systemIdentState
.GyroReadTimeAverage
;
709 memcpy(&systemIdentSettings
.Beta
, &u
.systemIdentState
.Beta
, sizeof(SystemIdentSettingsBetaData
));
710 systemIdentSettings
.Complete
= u
.systemIdentState
.Complete
;
712 // Tau, GyroReadTimeAverage, Beta, and the Complete flag get stored values
713 // so the user can fly another battery to select and test PIDs with the slider/knob
714 u
.systemIdentState
.Tau
= systemIdentSettings
.Tau
;
715 u
.systemIdentState
.GyroReadTimeAverage
= systemIdentSettings
.GyroReadTimeAverage
;
716 memcpy(&u
.systemIdentState
.Beta
, &systemIdentSettings
.Beta
, sizeof(SystemIdentStateBetaData
));
717 u
.systemIdentState
.Complete
= systemIdentSettings
.Complete
;
719 SystemIdentStateSet(&u
.systemIdentState
);
721 // (1.0f / PIOS_SENSOR_RATE) is gyro period
722 // the -1/10 makes it converge nicely, the other values make it converge the same way if the configuration is changed
723 // gyroReadTimeAverageAlphaAlpha is 0.9996 when the tuning duration is the default of 60 seconds
724 gyroReadTimeAverageAlphaAlpha
= expapprox(-1.0f
/ PIOS_SENSOR_RATE
/ ((float)systemIdentSettings
.TuningDuration
/ 10.0f
));
725 if (!IS_REAL(gyroReadTimeAverageAlphaAlpha
)) {
726 gyroReadTimeAverageAlphaAlpha
= expapprox(-1.0f
/ 500.0f
/ (60 / 10)); // basically 0.9996
728 // 0.99999988f is as close to 1.0f as possible to make final average as smooth as possible
729 gyroReadTimeAverageAlpha
= 0.99999988f
;
730 gyroReadTimeAverage
= u
.systemIdentState
.GyroReadTimeAverage
;
732 UpdateSmoothQuickSource(systemIdentSettings
.SmoothQuickSource
, loadDefaults
);
736 // Update SmoothQuickSource to be used
737 static void UpdateSmoothQuickSource(uint8_t smoothQuickSource
, bool loadDefaults
)
739 // disable PID changing with accessory0-3 and flight mode switch toggle
741 flightModeSwitchToggleStepValue
= FMS_TOGGLE_STEP_DISABLED
;
743 switch (smoothQuickSource
) {
744 case SYSTEMIDENTSETTINGS_SMOOTHQUICKSOURCE_ACCESSORY0
:
747 case SYSTEMIDENTSETTINGS_SMOOTHQUICKSOURCE_ACCESSORY1
:
750 case SYSTEMIDENTSETTINGS_SMOOTHQUICKSOURCE_ACCESSORY2
:
753 case SYSTEMIDENTSETTINGS_SMOOTHQUICKSOURCE_ACCESSORY3
:
756 // enable PID changing with flight mode switch
757 // -1 to +1 give a range = 2, define step value for desired positions: 3, 5, 7
758 case SYSTEMIDENTSETTINGS_SMOOTHQUICKSOURCE_FMSTOGGLE3POS
:
759 flightModeSwitchToggleStepValue
= 1.0f
;
761 case SYSTEMIDENTSETTINGS_SMOOTHQUICKSOURCE_FMSTOGGLE5POS
:
762 flightModeSwitchToggleStepValue
= 0.5f
;
764 case SYSTEMIDENTSETTINGS_SMOOTHQUICKSOURCE_FMSTOGGLE7POS
:
765 flightModeSwitchToggleStepValue
= 0.33f
;
767 case SYSTEMIDENTSETTINGS_SMOOTHQUICKSOURCE_DISABLED
:
770 flightModeSwitchToggleStepValue
= FMS_TOGGLE_STEP_DISABLED
;
773 // don't allow init of current toggle position in the middle of 3x fms toggle
774 if (loadDefaults
&& (flightModeSwitchToggleStepValue
> FMS_TOGGLE_STEP_DISABLED
)) {
775 // set toggle to middle of range
776 smoothQuickValue
= 0.0f
;
781 // update the gain and delay with current calculated value
782 // these are stored in the settings for use with next battery
783 // and also in the state for logging purposes
784 static void UpdateSystemIdentState(const float *X
, const float *noise
,
785 float dT_s
, uint32_t predicts
, uint32_t spills
, float hover_throttle
)
787 u
.systemIdentState
.Beta
.Roll
= X
[6];
788 u
.systemIdentState
.Beta
.Pitch
= X
[7];
789 u
.systemIdentState
.Beta
.Yaw
= X
[8];
790 u
.systemIdentState
.Bias
.Roll
= X
[10];
791 u
.systemIdentState
.Bias
.Pitch
= X
[11];
792 u
.systemIdentState
.Bias
.Yaw
= X
[12];
793 u
.systemIdentState
.Tau
= X
[9];
795 u
.systemIdentState
.Noise
.Roll
= noise
[0];
796 u
.systemIdentState
.Noise
.Pitch
= noise
[1];
797 u
.systemIdentState
.Noise
.Yaw
= noise
[2];
799 u
.systemIdentState
.Period
= dT_s
* 1000.0f
;
800 u
.systemIdentState
.NumAfPredicts
= predicts
;
801 u
.systemIdentState
.NumSpilledPts
= spills
;
802 u
.systemIdentState
.HoverThrottle
= hover_throttle
;
803 u
.systemIdentState
.GyroReadTimeAverage
= gyroReadTimeAverage
;
805 // 'settings' tau, beta, and GyroReadTimeAverage have same value as 'state' versions
806 // the state version produces a GCS log
807 // the settings version is remembered after power off/on
808 systemIdentSettings
.Tau
= u
.systemIdentState
.Tau
;
809 memcpy(&systemIdentSettings
.Beta
, &u
.systemIdentState
.Beta
, sizeof(SystemIdentSettingsBetaData
));
810 systemIdentSettings
.GyroReadTimeAverage
= u
.systemIdentState
.GyroReadTimeAverage
;
811 systemIdentSettings
.SmoothQuickValue
= smoothQuickValue
;
813 SystemIdentStateSet(&u
.systemIdentState
);
817 // when running AutoTune mode, this bypasses manualcontrol.c / stabilizedhandler.c
818 // to control whether the multicopter should be in Attitude mode vs. SystemIdent mode
819 static void UpdateStabilizationDesired(bool doingIdent
)
821 StabilizationDesiredData stabDesired
;
822 ManualControlCommandData manualControlCommand
;
824 ManualControlCommandGet(&manualControlCommand
);
826 stabDesired
.Roll
= manualControlCommand
.Roll
* rollMax
;
827 stabDesired
.Pitch
= manualControlCommand
.Pitch
* pitchMax
;
828 stabDesired
.Yaw
= manualControlCommand
.Yaw
* manualRate
.Yaw
;
829 stabDesired
.Thrust
= manualControlCommand
.Thrust
;
832 stabDesired
.StabilizationMode
.Roll
= STABILIZATIONDESIRED_STABILIZATIONMODE_SYSTEMIDENT
;
833 stabDesired
.StabilizationMode
.Pitch
= STABILIZATIONDESIRED_STABILIZATIONMODE_SYSTEMIDENT
;
834 stabDesired
.StabilizationMode
.Yaw
= STABILIZATIONDESIRED_STABILIZATIONMODE_SYSTEMIDENT
;
836 stabDesired
.StabilizationMode
.Roll
= STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE
;
837 stabDesired
.StabilizationMode
.Pitch
= STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE
;
838 stabDesired
.StabilizationMode
.Yaw
= STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK
;
841 if (systemIdentSettings
.ThrustControl
== SYSTEMIDENTSETTINGS_THRUSTCONTROL_ALTITUDEVARIO
) {
842 stabDesired
.StabilizationMode
.Thrust
= STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO
;
844 stabDesired
.StabilizationMode
.Thrust
= STABILIZATIONDESIRED_STABILIZATIONMODE_MANUAL
;
847 StabilizationDesiredSet(&stabDesired
);
851 // check the completed autotune state (mainly gain and delay)
852 // to see if it is reasonable
853 // return a bit mask of errors detected
854 static uint8_t CheckSettingsRaw()
858 // inverting the comparisons then negating the bool result should catch the nans but it doesn't
859 // so explictly check for nans
860 if (!IS_REAL(expapprox(u
.systemIdentState
.Tau
))) {
863 if (!IS_REAL(expapprox(u
.systemIdentState
.Beta
.Roll
))) {
866 if (!IS_REAL(expapprox(u
.systemIdentState
.Beta
.Pitch
))) {
869 if (!IS_REAL(expapprox(u
.systemIdentState
.Beta
.Yaw
))) {
873 // Check the axis gains
874 // Extreme values: Your roll or pitch gain was lower than expected. This will result in large PID values.
875 if (u
.systemIdentState
.Beta
.Roll
< 6) {
876 retVal
|= ROLL_BETA_LOW
;
878 if (u
.systemIdentState
.Beta
.Pitch
< 6) {
879 retVal
|= PITCH_BETA_LOW
;
881 // yaw gain is no longer checked, because the yaw options only include:
882 // - not calculating yaw
883 // - limiting yaw gain between two sane values (default)
884 // - ignoring errors and accepting the calculated yaw
886 // Check the response speed
887 // Extreme values: Your estimated response speed (tau) is slower than normal. This will result in large PID values.
888 if (expapprox(u
.systemIdentState
.Tau
) > 0.1f
) {
889 retVal
|= TAU_TOO_LONG
;
891 // Extreme values: Your estimated response speed (tau) is faster than normal. This will result in large PID values.
892 else if (expapprox(u
.systemIdentState
.Tau
) < 0.008f
) {
893 retVal
|= TAU_TOO_SHORT
;
896 // Sanity check: CPU is too slow compared to gyro rate
897 if (gyroReadTimeAverage
> (1.0f
/ PIOS_SENSOR_RATE
)) {
898 retVal
|= CPU_TOO_SLOW
;
905 // check the completed autotune state (mainly gain and delay)
906 // to see if it is reasonable
907 // override bad yaw values if configured that way
908 // return a bit mask of errors detected
909 static uint8_t CheckSettings()
911 uint8_t retVal
= CheckSettingsRaw();
913 if (systemIdentSettings
.DisableSanityChecks
) {
920 // given Tau"+"GyroReadTimeAverage(delay) and Beta(gain) from the tune (and user selection of smooth to quick) calculate the PIDs
921 // this code came from dRonin GCS and has been converted from double precision math to single precision
922 static void ComputeStabilizationAndSetPidsFromDampAndNoise(float dampRate
, float noiseRate
)
924 _Static_assert(sizeof(StabilizationSettingsBank1Data
) == sizeof(StabilizationBankData
), "sizeof(StabilizationSettingsBank1Data) != sizeof(StabilizationBankData)");
925 StabilizationBankData
volatile stabSettingsBank
;
926 switch (systemIdentSettings
.DestinationPidBank
) {
927 case SYSTEMIDENTSETTINGS_DESTINATIONPIDBANK_BANK1
:
928 StabilizationSettingsBank1Get((void *)&stabSettingsBank
);
930 case SYSTEMIDENTSETTINGS_DESTINATIONPIDBANK_BANK2
:
931 StabilizationSettingsBank2Get((void *)&stabSettingsBank
);
933 case SYSTEMIDENTSETTINGS_DESTINATIONPIDBANK_BANK3
:
934 StabilizationSettingsBank3Get((void *)&stabSettingsBank
);
938 // These three parameters define the desired response properties
939 // - rate scale in the fraction of the natural speed of the system
941 // - damp is the amount of damping in the system. higher values
942 // make oscillations less likely
943 // - ghf is the amount of high frequency gain and limits the influence
945 const float ghf
= noiseRate
/ 1000.0f
;
946 const float damp
= dampRate
/ 100.0f
;
948 float tau
= expapprox(u
.systemIdentState
.Tau
) + systemIdentSettings
.GyroReadTimeAverage
;
949 float exp_beta_roll_times_ghf
= expapprox(u
.systemIdentState
.Beta
.Roll
) * ghf
;
950 float exp_beta_pitch_times_ghf
= expapprox(u
.systemIdentState
.Beta
.Pitch
) * ghf
;
952 float wn
= 1.0f
/ tau
;
954 for (int i
= 0; i
< 30; i
++) {
955 float tau_d_roll
= (2.0f
* damp
* tau
* wn
- 1.0f
) / (4.0f
* tau
* damp
* damp
* wn
* wn
- 2.0f
* damp
* wn
- tau
* wn
* wn
+ exp_beta_roll_times_ghf
);
956 float tau_d_pitch
= (2.0f
* damp
* tau
* wn
- 1.0f
) / (4.0f
* tau
* damp
* damp
* wn
* wn
- 2.0f
* damp
* wn
- tau
* wn
* wn
+ exp_beta_pitch_times_ghf
);
957 // Select the slowest filter property
958 tau_d
= (tau_d_roll
> tau_d_pitch
) ? tau_d_roll
: tau_d_pitch
;
959 wn
= (tau
+ tau_d
) / (tau
* tau_d
) / (2.0f
* damp
+ 2.0f
);
962 // Set the real pole position. The first pole is quite slow, which
963 // prevents the integral being too snappy and driving too much
965 const float a
= ((tau
+ tau_d
) / tau
/ tau_d
- 2.0f
* damp
* wn
) / 20.0f
;
966 const float b
= ((tau
+ tau_d
) / tau
/ tau_d
- 2.0f
* damp
* wn
- a
);
968 // Calculate the gain for the outer loop by approximating the
969 // inner loop as a single order lpf. Set the outer loop to be
970 // critically damped;
971 const float zeta_o
= 1.3f
;
972 float kp_o
= 1.0f
/ 4.0f
/ (zeta_o
* zeta_o
) / (1.0f
/ wn
);
974 // Except, if this is very high, we may be slew rate limited and pick
975 // up oscillation that way. Fix it with very soft clamping.
976 // (dRonin) MaximumRate defaults to 350, 6.5 corresponds to where we begin
977 // clamping rate ourselves. ESCs, etc, it depends upon gains
978 // and any pre-emphasis they do. Still give ourselves partial credit
979 // for inner loop bandwidth.
981 // In dRonin, MaximumRate defaults to 350 and they begin clamping at outer Kp 6.5
982 // To avoid oscillation, find the minimum rate, calculate the ratio of that to 350,
983 // and scale (linearly) with that. Skip yaw. There is no outer yaw in the GUI.
984 const uint16_t minRate
= MIN(stabSettingsBank
.MaximumRate
.Roll
, stabSettingsBank
.MaximumRate
.Pitch
);
985 const float kp_o_clamp
= systemIdentSettings
.OuterLoopKpSoftClamp
* ((float)minRate
/ 350.0f
);
986 if (kp_o
> kp_o_clamp
) {
987 kp_o
= kp_o_clamp
- sqrtf(kp_o_clamp
) + sqrtf(kp_o
);
989 kp_o
*= 0.95f
; // Pick up some margin.
990 // Add a zero at 1/15th the innermost bandwidth.
991 const float ki_o
= 0.75f
* kp_o
/ (2.0f
* M_PI_F
* tau
* 15.0f
);
994 float betaMinLn
= 1000.0f
;
995 StabilizationBankRollRatePIDData
volatile *rollPitchPid
= NULL
; // satisfy compiler warning only
997 for (int i
= 0; i
< ((systemIdentSettings
.CalculateYaw
!= SYSTEMIDENTSETTINGS_CALCULATEYAW_FALSE
) ? 3 : 2); i
++) {
998 float betaLn
= SystemIdentStateBetaToArray(u
.systemIdentState
.Beta
)[i
];
999 float beta
= expapprox(betaLn
);
1007 ki
= a
* b
* wn
* wn
* tau
* tau_d
/ beta
;
1008 kp
= tau
* tau_d
* ((a
+ b
) * wn
* wn
+ 2.0f
* a
* b
* damp
* wn
) / beta
- ki
* tau_d
;
1009 kd
= (tau
* tau_d
* (a
* b
+ wn
* wn
+ (a
+ b
) * 2.0f
* damp
* wn
) - 1.0f
) / beta
- kp
* tau_d
;
1010 if (betaMinLn
> betaLn
) {
1012 // RollRatePID PitchRatePID YawRatePID
1013 // form an array of structures
1015 // this pointer arithmetic no longer works as expected in a gcc 64 bit test program
1016 // rollPitchPid = &(&stabSettingsBank.RollRatePID)[i];
1018 rollPitchPid
= &stabSettingsBank
.RollRatePID
;
1020 rollPitchPid
= (StabilizationBankRollRatePIDData
*)&stabSettingsBank
.PitchRatePID
;
1025 // yaw uses a mixture of yaw and the slowest axis (pitch) for it's beta and thus PID calculation
1026 // calculate the ratio to use when converting from the slowest axis (pitch) to the yaw axis
1027 // as (e^(betaMinLn-betaYawLn))^0.6
1028 // which is (e^betaMinLn / e^betaYawLn)^0.6
1029 // which is (betaMin / betaYaw)^0.6
1030 // which is betaMin^0.6 / betaYaw^0.6
1031 // now given that kp for each axis can be written as kpaxis = xp / betaaxis
1032 // for xp that is constant across all axes
1033 // then kpmin (probably kppitch) was xp / betamin (probably betapitch)
1034 // which we multiply by betaMin^0.6 / betaYaw^0.6 to get the new Yaw kp
1035 // so the new kpyaw is (xp / betaMin) * (betaMin^0.6 / betaYaw^0.6)
1036 // which is (xp / betaMin) * (betaMin^0.6 / betaYaw^0.6)
1037 // which is (xp * betaMin^0.6) / (betaMin * betaYaw^0.6)
1038 // which is xp / (betaMin * betaYaw^0.6 / betaMin^0.6)
1039 // which is xp / (betaMin^0.4 * betaYaw^0.6)
1040 // hence the new effective betaYaw for Yaw P is (betaMin^0.4)*(betaYaw^0.6)
1041 beta
= expapprox(0.6f
* (betaMinLn
- u
.systemIdentState
.Beta
.Yaw
));
1042 // this casting assumes that RollRatePID is the same as PitchRatePID
1043 kp
= rollPitchPid
->Kp
* beta
;
1044 ki
= 0.8f
* rollPitchPid
->Ki
* beta
;
1045 kd
= 0.8f
* rollPitchPid
->Kd
* beta
;
1054 // use the ratio with the largest roll/pitch kp to limit yaw kp to a reasonable value
1055 // use largest roll/pitch kp because it is the axis most slowed by rotational inertia
1056 // and yaw is also slowed maximally by rotational inertia
1057 // note that kp, ki, kd are all proportional in beta
1058 // so reducing them all proportionally is the same as changing beta
1061 switch (systemIdentSettings
.CalculateYaw
) {
1062 case SYSTEMIDENTSETTINGS_CALCULATEYAW_TRUELIMITTORATIO
:
1063 max
= kpMax
* systemIdentSettings
.YawToRollPitchPIDRatioMax
;
1064 min
= kpMax
* systemIdentSettings
.YawToRollPitchPIDRatioMin
;
1066 case SYSTEMIDENTSETTINGS_CALCULATEYAW_TRUEIGNORELIMIT
:
1074 if (min
> 0.0f
&& kp
< min
) {
1076 } else if (max
> 0.0f
&& kp
> max
) {
1084 // reduce kd if so configured
1085 // both of the quads tested for d term oscillation exhibit some degree of it with the stock autotune PIDs
1086 // if may be that adjusting stabSettingsBank.DerivativeCutoff would have a similar affect
1087 // reducing kd requires that kp and ki be reduced to avoid ringing
1088 // the amount to reduce kp and ki is taken from ZN tuning
1089 // specifically kp is parameterized based on the ratio between kp(PID) and kp(PI) as the D factor varies from 1 to 0
1090 // https://en.wikipedia.org/wiki/PID_controller
1092 // -----------------------------------
1094 // PI 0.45*Ku 1.2*Kp/Tu -
1095 // PID 0.60*Ku 2.0*Kp/Tu Kp*Tu/8
1097 // so Kp is multiplied by (.45/.60) if Kd is reduced to 0
1098 // and Ki is multiplied by (1.2/2.0) if Kd is reduced to 0
1099 #define KP_REDUCTION (.45f / .60f)
1100 #define KI_REDUCTION (1.2f / 2.0f)
1102 // this link gives some additional ratios that are different
1103 // the reduced overshoot ratios are invalid for this purpose
1104 // https://en.wikipedia.org/wiki/Ziegler%E2%80%93Nichols_method
1106 // ------------------------------------------------
1108 // PI 0.45*Ku Tu/1.2 -
1109 // PD 0.80*Ku - Tu/8
1110 // classic PID 0.60*Ku Tu/2.0 Tu/8 #define KP_REDUCTION (.45f/.60f) #define KI_REDUCTION (1.2f/2.0f)
1111 // Pessen Integral Rule 0.70*Ku Tu/2.5 3.0*Tu/20 #define KP_REDUCTION (.45f/.70f) #define KI_REDUCTION (1.2f/2.5f)
1112 // some overshoot 0.33*Ku Tu/2.0 Tu/3 #define KP_REDUCTION (.45f/.33f) #define KI_REDUCTION (1.2f/2.0f)
1113 // no overshoot 0.20*Ku Tu/2.0 Tu/3 #define KP_REDUCTION (.45f/.20f) #define KI_REDUCTION (1.2f/2.0f)
1115 // reduce roll and pitch, but not yaw
1116 // yaw PID is entirely based on roll or pitch PIDs which have already been reduced
1118 kp
= kp
* KP_REDUCTION
+ kp
* systemIdentSettings
.DerivativeFactor
* (1.0f
- KP_REDUCTION
);
1119 ki
= ki
* KI_REDUCTION
+ ki
* systemIdentSettings
.DerivativeFactor
* (1.0f
- KI_REDUCTION
);
1120 kd
*= systemIdentSettings
.DerivativeFactor
;
1125 stabSettingsBank
.RollRatePID
.Kp
= kp
;
1126 stabSettingsBank
.RollRatePID
.Ki
= ki
;
1127 stabSettingsBank
.RollRatePID
.Kd
= kd
;
1128 stabSettingsBank
.RollPI
.Kp
= kp_o
;
1129 stabSettingsBank
.RollPI
.Ki
= ki_o
;
1132 stabSettingsBank
.PitchRatePID
.Kp
= kp
;
1133 stabSettingsBank
.PitchRatePID
.Ki
= ki
;
1134 stabSettingsBank
.PitchRatePID
.Kd
= kd
;
1135 stabSettingsBank
.PitchPI
.Kp
= kp_o
;
1136 stabSettingsBank
.PitchPI
.Ki
= ki_o
;
1139 stabSettingsBank
.YawRatePID
.Kp
= kp
;
1140 stabSettingsBank
.YawRatePID
.Ki
= ki
;
1141 stabSettingsBank
.YawRatePID
.Kd
= kd
;
1143 // if we ever choose to use these
1144 // (e.g. mag yaw attitude)
1146 stabSettingsBank
.YawPI
.Kp
= kp_o
;
1147 stabSettingsBank
.YawPI
.Ki
= ki_o
;
1153 // Librepilot might do something more with this some time
1154 // stabSettingsBank.DerivativeCutoff = 1.0f / (2.0f*M_PI_F*tau_d);
1155 // SystemIdentSettingsDerivativeCutoffSet(&systemIdentSettings.DerivativeCutoff);
1156 // then something to schedule saving this permanently to flash when disarmed
1158 // Save PIDs to UAVO RAM (not permanently yet)
1159 switch (systemIdentSettings
.DestinationPidBank
) {
1160 case SYSTEMIDENTSETTINGS_DESTINATIONPIDBANK_BANK1
:
1161 StabilizationSettingsBank1Set((void *)&stabSettingsBank
);
1163 case SYSTEMIDENTSETTINGS_DESTINATIONPIDBANK_BANK2
:
1164 StabilizationSettingsBank2Set((void *)&stabSettingsBank
);
1166 case SYSTEMIDENTSETTINGS_DESTINATIONPIDBANK_BANK3
:
1167 StabilizationSettingsBank3Set((void *)&stabSettingsBank
);
1173 // scale the damp and the noise to generate PIDs according to how a slider or other user specified ratio is set
1175 // when val is half way between min and max, it generates the default PIDs
1176 // when val is min, it generates the smoothest configured PIDs
1177 // when val is max, it generates the quickest configured PIDs
1179 // when val is between min and (min+max)/2, it scales val over the range [min, (min+max)/2] to generate PIDs between smoothest and default
1180 // when val is between (min+max)/2 and max, it scales val over the range [(min+max)/2, max] to generate PIDs between default and quickest
1182 // this is done piecewise because we are not guaranteed that default-min == max-default
1183 // but we are given that [smoothDamp,smoothNoise] [defaultDamp,defaultNoise] [quickDamp,quickNoise] are all good parameterizations
1184 // this code guarantees that we will get those exact parameterizations at (val =) min, (max+min)/2, and max
1185 static void ProportionPidsSmoothToQuick()
1187 float ratio
, damp
, noise
;
1189 float val
= smoothQuickValue
;
1192 // translate from range [min, max] to range [0, max-min]
1193 // that takes care of min < 0 case too
1198 if (ratio
<= 0.5f
) {
1199 // scale ratio in [0,0.5] to produce PIDs in [smoothest,default]
1201 damp
= (systemIdentSettings
.DampMax
* (1.0f
- ratio
)) + (systemIdentSettings
.DampRate
* ratio
);
1202 noise
= (systemIdentSettings
.NoiseMin
* (1.0f
- ratio
)) + (systemIdentSettings
.NoiseRate
* ratio
);
1204 // scale ratio in [0.5,1.0] to produce PIDs in [default,quickest]
1205 ratio
= (ratio
- 0.5f
) * 2.0f
;
1206 damp
= (systemIdentSettings
.DampRate
* (1.0f
- ratio
)) + (systemIdentSettings
.DampMin
* ratio
);
1207 noise
= (systemIdentSettings
.NoiseRate
* (1.0f
- ratio
)) + (systemIdentSettings
.NoiseMax
* ratio
);
1210 ComputeStabilizationAndSetPidsFromDampAndNoise(damp
, noise
);
1211 // save it to the system, but not yet written to flash
1212 SystemIdentSettingsSmoothQuickValueSet(&smoothQuickValue
);
1217 * Prediction step for EKF on control inputs to quad that
1218 * learns the system properties
1219 * @param X the current state estimate which is updated in place
1220 * @param P the current covariance matrix, updated in place
1221 * @param[in] the current control inputs (roll, pitch, yaw)
1222 * @param[in] the gyro measurements
1224 __attribute__((always_inline
)) static inline void AfPredict(float X
[AF_NUMX
], float P
[AF_NUMP
], const float u_in
[3], const float gyro
[3], const float dT_s
, const float t_in
)
1226 const float Ts
= dT_s
;
1227 const float Tsq
= Ts
* Ts
;
1228 const float Tsq3
= Tsq
* Ts
;
1229 const float Tsq4
= Tsq
* Tsq
;
1231 // for convenience and clarity code below uses the named versions of
1232 // the state variables
1233 float w1
= X
[0]; // roll rate estimate
1234 float w2
= X
[1]; // pitch rate estimate
1235 float w3
= X
[2]; // yaw rate estimate
1236 float u1
= X
[3]; // scaled roll torque
1237 float u2
= X
[4]; // scaled pitch torque
1238 float u3
= X
[5]; // scaled yaw torque
1239 const float e_b1
= expapprox(X
[6]); // roll torque scale
1240 const float b1
= X
[6];
1241 const float e_b2
= expapprox(X
[7]); // pitch torque scale
1242 const float b2
= X
[7];
1243 const float e_b3
= expapprox(X
[8]); // yaw torque scale
1244 const float b3
= X
[8];
1245 const float e_tau
= expapprox(X
[9]); // time response of the motors
1246 const float tau
= X
[9];
1247 const float bias1
= X
[10]; // bias in the roll torque
1248 const float bias2
= X
[11]; // bias in the pitch torque
1249 const float bias3
= X
[12]; // bias in the yaw torque
1251 // inputs to the system (roll, pitch, yaw)
1252 const float u1_in
= 4 * t_in
* u_in
[0];
1253 const float u2_in
= 4 * t_in
* u_in
[1];
1254 const float u3_in
= 4 * t_in
* u_in
[2];
1256 // measurements from gyro
1257 const float gyro_x
= gyro
[0];
1258 const float gyro_y
= gyro
[1];
1259 const float gyro_z
= gyro
[2];
1261 // update named variables because we want to use predicted
1263 w1
= X
[0] = w1
- Ts
* bias1
* e_b1
+ Ts
* u1
* e_b1
;
1264 w2
= X
[1] = w2
- Ts
* bias2
* e_b2
+ Ts
* u2
* e_b2
;
1265 w3
= X
[2] = w3
- Ts
* bias3
* e_b3
+ Ts
* u3
* e_b3
;
1266 u1
= X
[3] = (Ts
* u1_in
) / (Ts
+ e_tau
) + (u1
* e_tau
) / (Ts
+ e_tau
);
1267 u2
= X
[4] = (Ts
* u2_in
) / (Ts
+ e_tau
) + (u2
* e_tau
) / (Ts
+ e_tau
);
1268 u3
= X
[5] = (Ts
* u3_in
) / (Ts
+ e_tau
) + (u3
* e_tau
) / (Ts
+ e_tau
);
1269 // X[6] to X[12] unchanged
1271 /**** filter parameters ****/
1272 const float q_w
= 1e-3f
;
1273 const float q_ud
= 1e-3f
;
1274 const float q_B
= 1e-6f
;
1275 const float q_tau
= 1e-6f
;
1276 const float q_bias
= 1e-19f
;
1277 const float s_a
= 150.0f
; // expected gyro measurment noise
1279 const float Q
[AF_NUMX
] = { q_w
, q_w
, q_w
, q_ud
, q_ud
, q_ud
, q_B
, q_B
, q_B
, q_tau
, q_bias
, q_bias
, q_bias
};
1282 for (uint32_t i
= 0; i
< AF_NUMP
; i
++) {
1286 const float e_tau2
= e_tau
* e_tau
;
1287 const float e_tau3
= e_tau
* e_tau2
;
1288 const float e_tau4
= e_tau2
* e_tau2
;
1289 const float Ts_e_tau2
= (Ts
+ e_tau
) * (Ts
+ e_tau
);
1290 const float Ts_e_tau4
= Ts_e_tau2
* Ts_e_tau2
;
1292 // covariance propagation - D is stored copy of covariance
1293 P
[0] = D
[0] + Q
[0] + 2 * Ts
* e_b1
* (D
[3] - D
[28] - D
[9] * bias1
+ D
[9] * u1
)
1294 + Tsq
* (e_b1
* e_b1
) * (D
[4] - 2 * D
[29] + D
[32] - 2 * D
[10] * bias1
+ 2 * D
[30] * bias1
+ 2 * D
[10] * u1
- 2 * D
[30] * u1
1295 + D
[11] * (bias1
* bias1
) + D
[11] * (u1
* u1
) - 2 * D
[11] * bias1
* u1
);
1296 P
[1] = D
[1] + Q
[1] + 2 * Ts
* e_b2
* (D
[5] - D
[33] - D
[12] * bias2
+ D
[12] * u2
)
1297 + Tsq
* (e_b2
* e_b2
) * (D
[6] - 2 * D
[34] + D
[37] - 2 * D
[13] * bias2
+ 2 * D
[35] * bias2
+ 2 * D
[13] * u2
- 2 * D
[35] * u2
1298 + D
[14] * (bias2
* bias2
) + D
[14] * (u2
* u2
) - 2 * D
[14] * bias2
* u2
);
1299 P
[2] = D
[2] + Q
[2] + 2 * Ts
* e_b3
* (D
[7] - D
[38] - D
[15] * bias3
+ D
[15] * u3
)
1300 + Tsq
* (e_b3
* e_b3
) * (D
[8] - 2 * D
[39] + D
[42] - 2 * D
[16] * bias3
+ 2 * D
[40] * bias3
+ 2 * D
[16] * u3
- 2 * D
[40] * u3
1301 + D
[17] * (bias3
* bias3
) + D
[17] * (u3
* u3
) - 2 * D
[17] * bias3
* u3
);
1302 P
[3] = (D
[3] * (e_tau2
+ Ts
* e_tau
) + Ts
* e_b1
* e_tau2
* (D
[4] - D
[29]) + Tsq
* e_b1
* e_tau
* (D
[4] - D
[29])
1303 + D
[18] * Ts
* e_tau
* (u1
- u1_in
) + D
[10] * e_b1
* (u1
* (Ts
* e_tau2
+ Tsq
* e_tau
) - bias1
* (Ts
* e_tau2
+ Tsq
* e_tau
))
1304 + D
[21] * Tsq
* e_b1
* e_tau
* (u1
- u1_in
) + D
[31] * Tsq
* e_b1
* e_tau
* (u1_in
- u1
)
1305 + D
[24] * Tsq
* e_b1
* e_tau
* (u1
* (u1
- bias1
) + u1_in
* (bias1
- u1
))) / Ts_e_tau2
;
1306 P
[4] = (Q
[3] * Tsq4
+ e_tau4
* (D
[4] + Q
[3]) + 2 * Ts
* e_tau3
* (D
[4] + 2 * Q
[3]) + 4 * Q
[3] * Tsq3
* e_tau
1307 + Tsq
* e_tau2
* (D
[4] + 6 * Q
[3] + u1
* (D
[27] * u1
+ 2 * D
[21]) + u1_in
* (D
[27] * u1_in
- 2 * D
[21]))
1308 + 2 * D
[21] * Ts
* e_tau3
* (u1
- u1_in
) - 2 * D
[27] * Tsq
* u1
* u1_in
* e_tau2
) / Ts_e_tau4
;
1309 P
[5] = (D
[5] * (e_tau2
+ Ts
* e_tau
) + Ts
* e_b2
* e_tau2
* (D
[6] - D
[34])
1310 + Tsq
* e_b2
* e_tau
* (D
[6] - D
[34]) + D
[19] * Ts
* e_tau
* (u2
- u2_in
)
1311 + D
[13] * e_b2
* (u2
* (Ts
* e_tau2
+ Tsq
* e_tau
) - bias2
* (Ts
* e_tau2
+ Tsq
* e_tau
))
1312 + D
[22] * Tsq
* e_b2
* e_tau
* (u2
- u2_in
) + D
[36] * Tsq
* e_b2
* e_tau
* (u2_in
- u2
)
1313 + D
[25] * Tsq
* e_b2
* e_tau
* (u2
* (u2
- bias2
) + u2_in
* (bias2
- u2
))) / Ts_e_tau2
;
1314 P
[6] = (Q
[4] * Tsq4
+ e_tau4
* (D
[6] + Q
[4]) + 2 * Ts
* e_tau3
* (D
[6] + 2 * Q
[4]) + 4 * Q
[4] * Tsq3
* e_tau
1315 + Tsq
* e_tau2
* (D
[6] + 6 * Q
[4] + u2
* (D
[27] * u2
+ 2 * D
[22]) + u2_in
* (D
[27] * u2_in
- 2 * D
[22]))
1316 + 2 * D
[22] * Ts
* e_tau3
* (u2
- u2_in
) - 2 * D
[27] * Tsq
* u2
* u2_in
* e_tau2
) / Ts_e_tau4
;
1317 P
[7] = (D
[7] * (e_tau2
+ Ts
* e_tau
) + Ts
* e_b3
* e_tau2
* (D
[8] - D
[39])
1318 + Tsq
* e_b3
* e_tau
* (D
[8] - D
[39]) + D
[20] * Ts
* e_tau
* (u3
- u3_in
)
1319 + D
[16] * e_b3
* (u3
* (Ts
* e_tau2
+ Tsq
* e_tau
) - bias3
* (Ts
* e_tau2
+ Tsq
* e_tau
))
1320 + D
[23] * Tsq
* e_b3
* e_tau
* (u3
- u3_in
) + D
[41] * Tsq
* e_b3
* e_tau
* (u3_in
- u3
)
1321 + D
[26] * Tsq
* e_b3
* e_tau
* (u3
* (u3
- bias3
) + u3_in
* (bias3
- u3
))) / Ts_e_tau2
;
1322 P
[8] = (Q
[5] * Tsq4
+ e_tau4
* (D
[8] + Q
[5]) + 2 * Ts
* e_tau3
* (D
[8] + 2 * Q
[5]) + 4 * Q
[5] * Tsq3
* e_tau
1323 + Tsq
* e_tau2
* (D
[8] + 6 * Q
[5] + u3
* (D
[27] * u3
+ 2 * D
[23]) + u3_in
* (D
[27] * u3_in
- 2 * D
[23]))
1324 + 2 * D
[23] * Ts
* e_tau3
* (u3
- u3_in
) - 2 * D
[27] * Tsq
* u3
* u3_in
* e_tau2
) / Ts_e_tau4
;
1325 P
[9] = D
[9] - Ts
* e_b1
* (D
[30] - D
[10] + D
[11] * (bias1
- u1
));
1326 P
[10] = (D
[10] * (Ts
+ e_tau
) + D
[24] * Ts
* (u1
- u1_in
)) * (e_tau
/ Ts_e_tau2
);
1327 P
[11] = D
[11] + Q
[6];
1328 P
[12] = D
[12] - Ts
* e_b2
* (D
[35] - D
[13] + D
[14] * (bias2
- u2
));
1329 P
[13] = (D
[13] * (Ts
+ e_tau
) + D
[25] * Ts
* (u2
- u2_in
)) * (e_tau
/ Ts_e_tau2
);
1330 P
[14] = D
[14] + Q
[7];
1331 P
[15] = D
[15] - Ts
* e_b3
* (D
[40] - D
[16] + D
[17] * (bias3
- u3
));
1332 P
[16] = (D
[16] * (Ts
+ e_tau
) + D
[26] * Ts
* (u3
- u3_in
)) * (e_tau
/ Ts_e_tau2
);
1333 P
[17] = D
[17] + Q
[8];
1334 P
[18] = D
[18] - Ts
* e_b1
* (D
[31] - D
[21] + D
[24] * (bias1
- u1
));
1335 P
[19] = D
[19] - Ts
* e_b2
* (D
[36] - D
[22] + D
[25] * (bias2
- u2
));
1336 P
[20] = D
[20] - Ts
* e_b3
* (D
[41] - D
[23] + D
[26] * (bias3
- u3
));
1337 P
[21] = (D
[21] * (Ts
+ e_tau
) + D
[27] * Ts
* (u1
- u1_in
)) * (e_tau
/ Ts_e_tau2
);
1338 P
[22] = (D
[22] * (Ts
+ e_tau
) + D
[27] * Ts
* (u2
- u2_in
)) * (e_tau
/ Ts_e_tau2
);
1339 P
[23] = (D
[23] * (Ts
+ e_tau
) + D
[27] * Ts
* (u3
- u3_in
)) * (e_tau
/ Ts_e_tau2
);
1343 P
[27] = D
[27] + Q
[9];
1344 P
[28] = D
[28] - Ts
* e_b1
* (D
[32] - D
[29] + D
[30] * (bias1
- u1
));
1345 P
[29] = (D
[29] * (Ts
+ e_tau
) + D
[31] * Ts
* (u1
- u1_in
)) * (e_tau
/ Ts_e_tau2
);
1348 P
[32] = D
[32] + Q
[10];
1349 P
[33] = D
[33] - Ts
* e_b2
* (D
[37] - D
[34] + D
[35] * (bias2
- u2
));
1350 P
[34] = (D
[34] * (Ts
+ e_tau
) + D
[36] * Ts
* (u2
- u2_in
)) * (e_tau
/ Ts_e_tau2
);
1353 P
[37] = D
[37] + Q
[11];
1354 P
[38] = D
[38] - Ts
* e_b3
* (D
[42] - D
[39] + D
[40] * (bias3
- u3
));
1355 P
[39] = (D
[39] * (Ts
+ e_tau
) + D
[41] * Ts
* (u3
- u3_in
)) * (e_tau
/ Ts_e_tau2
);
1358 P
[42] = D
[42] + Q
[12];
1360 /********* this is the update part of the equation ***********/
1361 float S
[3] = { P
[0] + s_a
, P
[1] + s_a
, P
[2] + s_a
};
1362 X
[0] = w1
+ P
[0] * ((gyro_x
- w1
) / S
[0]);
1363 X
[1] = w2
+ P
[1] * ((gyro_y
- w2
) / S
[1]);
1364 X
[2] = w3
+ P
[2] * ((gyro_z
- w3
) / S
[2]);
1365 X
[3] = u1
+ P
[3] * ((gyro_x
- w1
) / S
[0]);
1366 X
[4] = u2
+ P
[5] * ((gyro_y
- w2
) / S
[1]);
1367 X
[5] = u3
+ P
[7] * ((gyro_z
- w3
) / S
[2]);
1368 X
[6] = b1
+ P
[9] * ((gyro_x
- w1
) / S
[0]);
1369 X
[7] = b2
+ P
[12] * ((gyro_y
- w2
) / S
[1]);
1370 X
[8] = b3
+ P
[15] * ((gyro_z
- w3
) / S
[2]);
1371 X
[9] = tau
+ P
[18] * ((gyro_x
- w1
) / S
[0]) + P
[19] * ((gyro_y
- w2
) / S
[1]) + P
[20] * ((gyro_z
- w3
) / S
[2]);
1372 X
[10] = bias1
+ P
[28] * ((gyro_x
- w1
) / S
[0]);
1373 X
[11] = bias2
+ P
[33] * ((gyro_y
- w2
) / S
[1]);
1374 X
[12] = bias3
+ P
[38] * ((gyro_z
- w3
) / S
[2]);
1376 // update the duplicate cache
1377 for (uint32_t i
= 0; i
< AF_NUMP
; i
++) {
1381 // This is an approximation that removes some cross axis uncertainty but
1382 // substantially reduces the number of calculations
1383 P
[0] = -D
[0] * (D
[0] / S
[0] - 1);
1384 P
[1] = -D
[1] * (D
[1] / S
[1] - 1);
1385 P
[2] = -D
[2] * (D
[2] / S
[2] - 1);
1386 P
[3] = -D
[3] * (D
[0] / S
[0] - 1);
1387 P
[4] = D
[4] - D
[3] * (D
[3] / S
[0]);
1388 P
[5] = -D
[5] * (D
[1] / S
[1] - 1);
1389 P
[6] = D
[6] - D
[5] * (D
[5] / S
[1]);
1390 P
[7] = -D
[7] * (D
[2] / S
[2] - 1);
1391 P
[8] = D
[8] - D
[7] * (D
[7] / S
[2]);
1392 P
[9] = -D
[9] * (D
[0] / S
[0] - 1);
1393 P
[10] = D
[10] - D
[3] * (D
[9] / S
[0]);
1394 P
[11] = D
[11] - D
[9] * (D
[9] / S
[0]);
1395 P
[12] = -D
[12] * (D
[1] / S
[1] - 1);
1396 P
[13] = D
[13] - D
[5] * (D
[12] / S
[1]);
1397 P
[14] = D
[14] - D
[12] * (D
[12] / S
[1]);
1398 P
[15] = -D
[15] * (D
[2] / S
[2] - 1);
1399 P
[16] = D
[16] - D
[7] * (D
[15] / S
[2]);
1400 P
[17] = D
[17] - D
[15] * (D
[15] / S
[2]);
1401 P
[18] = -D
[18] * (D
[0] / S
[0] - 1);
1402 P
[19] = -D
[19] * (D
[1] / S
[1] - 1);
1403 P
[20] = -D
[20] * (D
[2] / S
[2] - 1);
1404 P
[21] = D
[21] - D
[3] * (D
[18] / S
[0]);
1405 P
[22] = D
[22] - D
[5] * (D
[19] / S
[1]);
1406 P
[23] = D
[23] - D
[7] * (D
[20] / S
[2]);
1407 P
[24] = D
[24] - D
[9] * (D
[18] / S
[0]);
1408 P
[25] = D
[25] - D
[12] * (D
[19] / S
[1]);
1409 P
[26] = D
[26] - D
[15] * (D
[20] / S
[2]);
1410 P
[27] = D
[27] - D
[18] * (D
[18] / S
[0]) - D
[19] * (D
[19] / S
[1]) - D
[20] * (D
[20] / S
[2]);
1411 P
[28] = -D
[28] * (D
[0] / S
[0] - 1);
1412 P
[29] = D
[29] - D
[3] * (D
[28] / S
[0]);
1413 P
[30] = D
[30] - D
[9] * (D
[28] / S
[0]);
1414 P
[31] = D
[31] - D
[18] * (D
[28] / S
[0]);
1415 P
[32] = D
[32] - D
[28] * (D
[28] / S
[0]);
1416 P
[33] = -D
[33] * (D
[1] / S
[1] - 1);
1417 P
[34] = D
[34] - D
[5] * (D
[33] / S
[1]);
1418 P
[35] = D
[35] - D
[12] * (D
[33] / S
[1]);
1419 P
[36] = D
[36] - D
[19] * (D
[33] / S
[1]);
1420 P
[37] = D
[37] - D
[33] * (D
[33] / S
[1]);
1421 P
[38] = -D
[38] * (D
[2] / S
[2] - 1);
1422 P
[39] = D
[39] - D
[7] * (D
[38] / S
[2]);
1423 P
[40] = D
[40] - D
[15] * (D
[38] / S
[2]);
1424 P
[41] = D
[41] - D
[20] * (D
[38] / S
[2]);
1425 P
[42] = D
[42] - D
[38] * (D
[38] / S
[2]);
1427 // apply limits to some of the state variables
1430 } else if (X
[9] < -5.5f
) { /* 4ms */
1435 } else if (X
[10] < -0.5f
) {
1440 } else if (X
[11] < -0.5f
) {
1445 } else if (X
[12] < -0.5f
) {
1452 * Initialize the state variable and covariance matrix
1453 * for the system identification EKF
1455 static void AfInit(float X
[AF_NUMX
], float P
[AF_NUMP
])
1457 static const float qInit
[AF_NUMX
] = {
1460 0.05f
, 0.05f
, 0.005f
,
1465 // X[0] = X[1] = X[2] = 0.0f; // assume no rotation
1466 // X[3] = X[4] = X[5] = 0.0f; // and no net torque
1467 // X[6] = X[7] = 10.0f; // roll and pitch medium amount of strength
1468 // X[8] = 7.0f; // yaw strength
1469 // X[9] = -4.0f; // and 50 (18?) ms time scale
1470 // X[10] = X[11] = X[12] = 0.0f; // zero bias
1472 memset(X
, 0, AF_NUMX
* sizeof(X
[0]));
1473 // get these 10.0 10.0 7.0 -4.0 from default values of SystemIdent (.Beta and .Tau)
1474 // so that if they are changed there (mainly for future code changes), they will be changed here too
1475 memcpy(&X
[6], &u
.systemIdentState
.Beta
, sizeof(u
.systemIdentState
.Beta
));
1476 X
[9] = u
.systemIdentState
.Tau
;
1479 memset(P
, 0, AF_NUMP
* sizeof(P
[0]));