LP-500 HoTT Bridge Module ported from TauLabs
[librepilot.git] / flight / modules / AutoTune / autotune.c
blobd84933c22206763123fed8260b6aaaeeaff117e6
1 /**
2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
4 * @{
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"
9 * @{
11 * @file AutoTune/autotune.c
12 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
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
30 * for more details.
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>
38 #include <pios.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>
51 #include <taskinfo.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
62 #else
63 #define powapprox powf
64 #define expapprox expf
65 #endif /* defined(PIOS_EXCLUDE_ADVANCED_FEATURES) */
68 // Private constants
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)
75 #define AF_NUMX 13
76 #define AF_NUMP 43
78 #if !defined(AT_QUEUE_NUMELEM)
79 #define AT_QUEUE_NUMELEM 18
80 #endif
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
95 #define TAU_NAN 1
96 #define BETA_NAN 2
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 // smooth-quick modes
105 #define SMOOTH_QUICK_DISABLED 0
106 #define SMOOTH_QUICK_ACCESSORY_BASE 10
107 #define SMOOTH_QUICK_TOGGLE_BASE 20
110 // Private types
111 enum AUTOTUNE_STATE { AT_INIT, AT_INIT_DELAY, AT_INIT_DELAY2, AT_START, AT_RUN, AT_FINISHED, AT_WAITING };
112 struct at_queued_data {
113 float y[3]; /* Gyro measurements */
114 float u[3]; /* Actuator desired */
115 float throttle; /* Throttle desired */
116 uint32_t gyroStateCallbackTimestamp; /* PIOS_DELAY_GetRaw() time of GyroState callback */
117 uint32_t sensorReadTimestamp; /* PIOS_DELAY_GetRaw() time of sensor read */
121 // Private variables
122 static SystemIdentSettingsData systemIdentSettings;
123 // save memory because metadata is only briefly accessed, when normal data struct is not being used
124 // unnamed union issues a warning
125 static union {
126 SystemIdentStateData systemIdentState;
127 UAVObjMetadata systemIdentStateMetaData;
128 } u;
129 static StabilizationBankManualRateData manualRate;
130 static xTaskHandle taskHandle;
131 static xQueueHandle atQueue;
132 static float gX[AF_NUMX] = { 0 };
133 static float gP[AF_NUMP] = { 0 };
134 static float gyroReadTimeAverage;
135 static float gyroReadTimeAverageAlpha;
136 static float gyroReadTimeAverageAlphaAlpha;
137 static float alpha;
138 static float smoothQuickValue;
139 static volatile uint32_t atPointsSpilled;
140 static uint32_t throttleAccumulator;
141 static uint8_t rollMax, pitchMax;
142 static int8_t accessoryToUse;
143 static int8_t flightModeSwitchTogglePosition;
144 static bool moduleEnabled;
147 // Private functions
148 static void AtNewGyroData(UAVObjEvent *ev);
149 static bool AutoTuneFoundInFMS();
150 static void AutoTuneTask(void *parameters);
151 static void AfInit(float X[AF_NUMX], float P[AF_NUMP]);
152 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);
153 static bool CheckFlightModeSwitchForPidRequest(uint8_t flightMode);
154 static uint8_t CheckSettings();
155 static uint8_t CheckSettingsRaw();
156 static void ComputeStabilizationAndSetPidsFromDampAndNoise(float damp, float noise);
157 static void FlightModeSettingsUpdatedCb(UAVObjEvent *ev);
158 static void InitSystemIdent(bool loadDefaults);
159 static void ProportionPidsSmoothToQuick();
160 static void UpdateSystemIdentState(const float *X, const float *noise, float dT_s, uint32_t predicts, uint32_t spills, float hover_throttle);
161 static void UpdateStabilizationDesired(bool doingIdent);
165 * Initialise the module, called on startup
166 * \returns 0 on success or -1 if initialisation failed
168 int32_t AutoTuneInitialize(void)
170 // do this here since module can become disabled for several reasons
171 // even for MODULE_AutoTune_BUILTIN
172 FlightModeSettingsInitialize();
174 #if defined(MODULE_AutoTune_BUILTIN)
175 moduleEnabled = true;
176 #else
177 // HwSettings is only used right here, so init here
178 HwSettingsInitialize();
179 HwSettingsOptionalModulesData optionalModules;
180 HwSettingsOptionalModulesGet(&optionalModules);
181 if (optionalModules.AutoTune == HWSETTINGS_OPTIONALMODULES_ENABLED) {
182 // even though the AutoTune module is automatically enabled
183 // (below, when the flight mode switch is configured to use autotune)
184 // there are use cases where the user may even want it enabled without being on the FMS
185 // that allows PIDs to be adjusted in flight
186 moduleEnabled = true;
187 } else {
188 // if the user did not manually enable the autotune module
189 // do it for them if they have autotune on their flight mode switch
190 moduleEnabled = AutoTuneFoundInFMS();
192 #endif /* defined(MODULE_AutoTune_BUILTIN) */
194 if (moduleEnabled) {
195 AccessoryDesiredInitialize();
196 ActuatorDesiredInitialize();
197 FlightStatusInitialize();
198 GyroStateInitialize();
199 ManualControlCommandInitialize();
200 StabilizationBankInitialize();
201 StabilizationSettingsBank1Initialize();
202 StabilizationSettingsBank2Initialize();
203 StabilizationSettingsBank3Initialize();
204 SystemIdentSettingsInitialize();
205 SystemIdentStateInitialize();
207 atQueue = xQueueCreate(AT_QUEUE_NUMELEM, sizeof(struct at_queued_data));
208 if (!atQueue) {
209 moduleEnabled = false;
212 if (!moduleEnabled) {
213 // only need to watch for enabling AutoTune in FMS if AutoTune module is _not_ running
214 FlightModeSettingsConnectCallback(FlightModeSettingsUpdatedCb);
217 return 0;
222 * Initialise the module, called on startup
223 * \returns 0 on success or -1 if initialisation failed
225 int32_t AutoTuneStart(void)
227 // Start main task if it is enabled
228 if (moduleEnabled) {
229 GyroStateConnectCallback(AtNewGyroData);
230 xTaskCreate(AutoTuneTask, "AutoTune", STACK_SIZE_BYTES / 4, NULL, TASK_PRIORITY, &taskHandle);
231 PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_AUTOTUNE, taskHandle);
233 return 0;
237 MODULE_INITCALL(AutoTuneInitialize, AutoTuneStart);
241 * Module thread, should not return.
243 static void AutoTuneTask(__attribute__((unused)) void *parameters)
245 float noise[3] = { 0 };
246 float dT_s = 0.0f;
247 uint32_t lastUpdateTime = 0; // initialization is only for compiler warning
248 uint32_t lastTime = 0;
249 uint32_t measureTime = 0;
250 uint32_t updateCounter = 0;
251 enum AUTOTUNE_STATE state = AT_INIT;
252 bool saveSiNeeded = false;
253 bool savePidNeeded = false;
255 // wait for the accessory values to stabilize
256 // otherwise they come up as zero, then change to their real value
257 // and that causes the PIDs to be re-exported (if smoothquick is active), which the user may not want
258 vTaskDelay(TASK_STARTUP_DELAY_MS / portTICK_RATE_MS);
260 // get max attitude / max rate
261 // for use in generating Attitude mode commands from this module
262 // note that the values could change when they change flight mode (and the associated bank)
263 StabilizationBankRollMaxGet(&rollMax);
264 StabilizationBankPitchMaxGet(&pitchMax);
265 StabilizationBankManualRateGet(&manualRate);
266 // correctly set accessoryToUse and flightModeSwitchTogglePosition
267 // based on what is in SystemIdent
268 // so that the user can use the PID smooth->quick slider in flights following the autotune flight
269 InitSystemIdent(false);
270 smoothQuickValue = systemIdentSettings.SmoothQuickValue;
272 while (1) {
273 uint32_t diffTime;
274 bool doingIdent = false;
275 bool canSleep = true;
276 FlightStatusData flightStatus;
277 FlightStatusGet(&flightStatus);
279 if (flightStatus.Armed == FLIGHTSTATUS_ARMED_DISARMED) {
280 if (saveSiNeeded) {
281 saveSiNeeded = false;
282 // Save SystemIdentSettings to permanent settings
283 UAVObjSave(SystemIdentSettingsHandle(), 0);
285 if (savePidNeeded) {
286 savePidNeeded = false;
287 // Save PIDs to permanent settings
288 switch (systemIdentSettings.DestinationPidBank) {
289 case 1:
290 UAVObjSave(StabilizationSettingsBank1Handle(), 0);
291 break;
292 case 2:
293 UAVObjSave(StabilizationSettingsBank2Handle(), 0);
294 break;
295 case 3:
296 UAVObjSave(StabilizationSettingsBank3Handle(), 0);
297 break;
302 // if using flight mode switch "quick toggle 3x" to "try smooth -> quick PIDs" is enabled
303 // and user toggled into and back out of AutoTune three times in the last two seconds
304 // and the autotune data gathering is complete
305 // and the autotune data gathered is good
306 // note: CheckFlightModeSwitchForPidRequest(mode) only returns true if current mode is not autotune
307 if (flightModeSwitchTogglePosition != -1 && CheckFlightModeSwitchForPidRequest(flightStatus.FlightMode)
308 && systemIdentSettings.Complete && !CheckSettings()) {
309 if (flightStatus.Armed == FLIGHTSTATUS_ARMED_ARMED) {
310 // if user toggled while armed set PID's to next in sequence
311 // if you assume that smoothest is -1 and quickest is +1
312 // this corresponds to 0,+.50,+1.00,-1.00,-.50 (for 5 position toggle)
313 smoothQuickValue += 1.0f / (float)flightModeSwitchTogglePosition;
314 if (smoothQuickValue > 1.001f) {
315 smoothQuickValue = -1.0f;
317 } else {
318 // if they did the 3x FMS toggle while disarmed, set PID's back to the middle of smoothquick
319 smoothQuickValue = 0.0f;
321 // calculate PIDs based on new smoothQuickValue and save to the PID bank
322 ProportionPidsSmoothToQuick();
323 // save new PIDs permanently when / if disarmed
324 savePidNeeded = true;
325 // we also save the new knob/toggle value for startup next time
326 // this keeps the PIDs in sync with the toggle position
327 saveSiNeeded = true;
330 //////////////////////////////////////////////////////////////////////////////////////
331 // if configured to use a slider for smooth-quick and the autotune module is running
332 // (note that the module can be automatically or manually enabled)
333 // then the smooth-quick slider is always active (when not actually in autotune mode)
335 // when the slider is active it will immediately change the PIDs
336 // and it will schedule the PIDs to be written to permanent storage
338 // if the FC is disarmed, the perm write will happen on next loop
339 // but if the FC is armed, the perm write will only occur when the FC goes disarmed
340 //////////////////////////////////////////////////////////////////////////////////////
342 // we don't want it saving to permanent storage many times
343 // while the user is moving the knob once, so wait till the knob stops moving
344 static uint8_t savePidDelay;
345 // any time we are not in AutoTune mode:
346 // - the user may be using the accessory0-3 knob/slider to request PID changes
347 // - the state machine needs to be reset
348 // - the local version of Attitude mode gets skipped
349 if (flightStatus.FlightMode != FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE) {
350 // if accessory0-3 is configured as a PID changing slider/knob over the smooth to quick range
351 // and FC is not currently running autotune
352 // and accessory0-3 changed by at least 1/85 of full range (2)
353 // (don't bother checking to see if the requested accessory# is configured properly
354 // if it isn't, the value will be 0 which is the center of [-1,1] anyway)
355 if (accessoryToUse != -1 && systemIdentSettings.Complete && !CheckSettings()) {
356 AccessoryDesiredData accessoryValue;
357 AccessoryDesiredInstGet(accessoryToUse, &accessoryValue);
358 // if the accessory changed more than some percent of total range
359 // some old PPM receivers use a low resolution chip which only allows about 180 steps out of a range of 2.0
360 // a test Taranis transmitter knob has about 0.0233 slop out of a range of 2.0
361 // what we are doing here does not need any higher precision than that
362 // user must move the knob more than 1/85th of the total range (of 2.0) for it to register as changed
363 if (fabsf(smoothQuickValue - accessoryValue.AccessoryVal) > (2.0f / 85.0f)) {
364 smoothQuickValue = accessoryValue.AccessoryVal;
365 // calculate PIDs based on new smoothQuickValue and save to the PID bank
366 ProportionPidsSmoothToQuick();
367 // this schedules the first possible write of the PIDs to occur a fraction of a second or so from now
368 // and changes the scheduled time if it is already scheduled
369 savePidDelay = SMOOTH_QUICK_FLUSH_TICKS;
370 } else if (savePidDelay && --savePidDelay == 0) {
371 // this flags that the PIDs can be written to permanent storage right now
372 // but they will only be written when the FC is disarmed
373 // so this means immediate (after NOT_AT_MODE_DELAY_MS) or wait till FC is disarmed
374 savePidNeeded = true;
375 // we also save the new knob/toggle value for startup next time
376 // this avoids rewriting the PIDs at each startup
377 // because knob is unknown / not where it is expected / looks like knob moved
378 saveSiNeeded = true;
380 } else {
381 savePidDelay = 0;
383 state = AT_INIT;
384 vTaskDelay(NOT_AT_MODE_DELAY_MS / portTICK_RATE_MS);
385 continue;
386 } else {
387 savePidDelay = 0;
390 switch (state) {
391 case AT_INIT:
392 // beware that control comes here every time the user toggles the flight mode switch into AutoTune
393 // and it isn't appropriate to reset the main state here
394 // init must wait until after a delay has passed:
395 // - to make sure they intended to stay in this mode
396 // - to wait for the stab bank to get populated with the new bank info
397 // This is a race. It is possible that flightStatus.FlightMode has been changed,
398 // but the stab bank hasn't been changed yet.
399 state = AT_INIT_DELAY;
400 lastUpdateTime = xTaskGetTickCount();
401 break;
403 case AT_INIT_DELAY:
404 diffTime = xTaskGetTickCount() - lastUpdateTime;
405 // after a small delay, get the stab bank values and SystemIdentSettings in case they changed
406 // this is a very small delay (100ms), so "quick 3x fms toggle" gets in here
407 if (diffTime > INIT_TIME_DELAY_MS) {
408 // do these here so the user has at most a 1/10th second
409 // with controls that use the previous bank's rates
410 StabilizationBankRollMaxGet(&rollMax);
411 StabilizationBankPitchMaxGet(&pitchMax);
412 StabilizationBankManualRateGet(&manualRate);
413 // load SystemIdentSettings so that they can change it
414 // and do smooth-quick on changed values
415 InitSystemIdent(false);
416 // wait for FC to arm in case they are doing this without a flight mode switch
417 // that causes the 2+ second delay that follows to happen after arming
418 // which gives them a chance to take off before the shakes start
419 // the FC must be armed and if we check here it also allows switchless setup to use autotune
420 if (flightStatus.Armed == FLIGHTSTATUS_ARMED_ARMED) {
421 state = AT_INIT_DELAY2;
422 lastUpdateTime = xTaskGetTickCount();
425 break;
427 case AT_INIT_DELAY2:
428 // delay for 2 seconds before actually starting the SystemIdent flight mode and AutoTune.
429 // that allows the user to get his fingers on the sticks
430 // and avoids starting the AutoTune if the user is toggling the flight mode switch
431 // to select other PIDs on the "simulated Smooth Quick slider".
432 // or simply "passing through" this flight mode to get to another flight mode
433 diffTime = xTaskGetTickCount() - lastUpdateTime;
434 // after 2 seconds start systemident flight mode
435 if (diffTime > SYSTEMIDENT_TIME_DELAY_MS) {
436 // load default tune and clean up any NANs from previous tune
437 InitSystemIdent(true);
438 AfInit(gX, gP);
439 // and write it out to the UAVO so innerloop can see the default values
440 UpdateSystemIdentState(gX, NULL, 0.0f, 0, 0, 0.0f);
441 // before starting SystemIdent stabilization mode
442 doingIdent = true;
443 state = AT_START;
445 break;
447 case AT_START:
448 diffTime = xTaskGetTickCount() - lastUpdateTime;
449 doingIdent = true;
450 // after an additional short delay, start capturing data
451 if (diffTime > INIT_TIME_DELAY2_MS) {
452 // Reset save status
453 // save SI data even if partial or bad, aids in diagnostics
454 saveSiNeeded = true;
455 // don't save PIDs until data gathering is complete
456 // and the complete data has been sanity checked
457 savePidNeeded = false;
458 // get the tuning duration in case the user just changed it
459 measureTime = (uint32_t)systemIdentSettings.TuningDuration * (uint32_t)1000;
460 // init the "previous packet timestamp"
461 lastTime = PIOS_DELAY_GetRaw();
462 /* Drain the queue of all current data */
463 xQueueReset(atQueue);
464 /* And reset the point spill counter */
465 updateCounter = 0;
466 atPointsSpilled = 0;
467 throttleAccumulator = 0;
468 alpha = 0.0f;
469 state = AT_RUN;
470 lastUpdateTime = xTaskGetTickCount();
472 break;
474 case AT_RUN:
475 diffTime = xTaskGetTickCount() - lastUpdateTime;
476 doingIdent = true;
477 canSleep = false;
478 // 4 gyro samples per cycle
479 // 2ms cycle time
480 // that is 500 gyro samples per second if it sleeps each time
481 // actually less than 500 because it cycle time is processing time + 2ms
482 for (int i = 0; i < MAX_PTS_PER_CYCLE; i++) {
483 struct at_queued_data pt;
484 /* Grab an autotune point */
485 if (xQueueReceive(atQueue, &pt, 0) != pdTRUE) {
486 /* We've drained the buffer fully */
487 canSleep = true;
488 break;
490 /* calculate time between successive points */
491 dT_s = PIOS_DELAY_DiffuS2(lastTime, pt.gyroStateCallbackTimestamp) * 1.0e-6f;
492 /* This is for the first point, but
493 * also if we have extended drops */
494 if (dT_s > 5.0f / PIOS_SENSOR_RATE) {
495 dT_s = 5.0f / PIOS_SENSOR_RATE;
497 lastTime = pt.gyroStateCallbackTimestamp;
498 // original algorithm handles time from GyroStateGet() to detected motion
499 // this algorithm also includes the time from raw gyro read to GyroStateGet()
500 gyroReadTimeAverage = gyroReadTimeAverage * alpha
501 + PIOS_DELAY_DiffuS2(pt.sensorReadTimestamp, pt.gyroStateCallbackTimestamp) * 1.0e-6f * (1.0f - alpha);
502 alpha = alpha * gyroReadTimeAverageAlphaAlpha + gyroReadTimeAverageAlpha * (1.0f - gyroReadTimeAverageAlphaAlpha);
503 AfPredict(gX, gP, pt.u, pt.y, dT_s, pt.throttle);
504 for (int j = 0; j < 3; ++j) {
505 const float NOISE_ALPHA = 0.9997f; // 10 second time constant at 300 Hz
506 noise[j] = NOISE_ALPHA * noise[j] + (1 - NOISE_ALPHA) * (pt.y[j] - gX[j]) * (pt.y[j] - gX[j]);
508 // This will work up to 8kHz with an 89% throttle position before overflow
509 throttleAccumulator += 10000 * pt.throttle;
510 // Update uavo every 256 cycles to avoid
511 // telemetry spam
512 if (((++updateCounter) & 0xff) == 0) {
513 float hoverThrottle = ((float)(throttleAccumulator / updateCounter)) / 10000.0f;
514 UpdateSystemIdentState(gX, noise, dT_s, updateCounter, atPointsSpilled, hoverThrottle);
517 if (diffTime > measureTime) { // Move on to next state
518 // permanent flag that AT is complete and PIDs can be calculated
519 state = AT_FINISHED;
521 break;
523 case AT_FINISHED:
524 // update with info from the last few data points
525 if ((updateCounter & 0xff) != 0) {
526 float hoverThrottle = ((float)(throttleAccumulator / updateCounter)) / 10000.0f;
527 UpdateSystemIdentState(gX, noise, dT_s, updateCounter, atPointsSpilled, hoverThrottle);
529 // data is automatically considered bad if FC was disarmed at the time AT completed
530 if (flightStatus.Armed == FLIGHTSTATUS_ARMED_ARMED) {
531 // always calculate and save PIDs if disabling sanity checks
532 if (!CheckSettings()) {
533 ProportionPidsSmoothToQuick();
534 savePidNeeded = true;
535 // mark these results as good in the log settings so they can be viewed in playback
536 u.systemIdentState.Complete = true;
537 SystemIdentStateCompleteSet(&u.systemIdentState.Complete);
538 // mark these results as good in the permanent settings so they can be used next flight too
539 // this is written to the UAVO below, outside of the ARMED and CheckSettings() checks
540 systemIdentSettings.Complete = true;
542 // always raise an alarm if sanity checks failed
543 // even if disabling sanity checks
544 // that way user can still see that they failed
545 uint8_t failureBits = CheckSettingsRaw();
546 if (failureBits) {
547 // raise a warning that includes failureBits to indicate what failed
548 ExtendedAlarmsSet(SYSTEMALARMS_ALARM_SYSTEMCONFIGURATION, SYSTEMALARMS_ALARM_WARNING,
549 SYSTEMALARMS_EXTENDEDALARMSTATUS_AUTOTUNE, failureBits);
552 // need to save UAVO after .Complete gets potentially set
553 // SystemIdentSettings needs the whole UAVO saved so it is saved outside the previous checks
554 SystemIdentSettingsSet(&systemIdentSettings);
555 state = AT_WAITING;
556 break;
558 case AT_WAITING:
559 default:
560 // after tuning, wait here till user switches to another flight mode
561 // or disarms
562 break;
565 // fly in Attitude mode or in SystemIdent mode
566 UpdateStabilizationDesired(doingIdent);
568 if (canSleep) {
569 vTaskDelay(YIELD_MS / portTICK_RATE_MS);
575 // FlightModeSettings callback
576 // determine if autotune is enabled in the flight mode switch
577 static bool AutoTuneFoundInFMS()
579 bool found = false;
580 FlightModeSettingsFlightModePositionOptions fms[FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_NUMELEM];
581 uint8_t num_flightMode;
583 FlightModeSettingsFlightModePositionGet(fms);
584 ManualControlSettingsFlightModeNumberGet(&num_flightMode);
585 for (uint8_t i = 0; i < num_flightMode; ++i) {
586 if (fms[i] == FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_AUTOTUNE) {
587 found = true;
588 break;
591 return found;
595 // gyro sensor callback
596 // get gyro data and actuatordesired into a packet
597 // and put it in the queue for later processing
598 static void AtNewGyroData(UAVObjEvent *ev)
600 static struct at_queued_data q_item;
601 static bool last_sample_unpushed = false;
602 GyroStateData gyro;
603 ActuatorDesiredData actuators;
604 uint32_t timestamp;
606 if (!ev || !ev->obj || ev->instId != 0 || ev->event != EV_UPDATED) {
607 return;
610 // object will at times change asynchronously so must copy data here, with locking
611 // and do it as soon as possible
612 timestamp = PIOS_DELAY_GetRaw();
613 GyroStateGet(&gyro);
614 ActuatorDesiredGet(&actuators);
616 if (last_sample_unpushed) {
617 /* Last time we were unable to queue up the gyro data.
618 * Try again, last chance! */
619 if (xQueueSend(atQueue, &q_item, 0) != pdTRUE) {
620 atPointsSpilled++;
624 q_item.gyroStateCallbackTimestamp = timestamp;
625 q_item.y[0] = q_item.y[0] * stabSettings.gyro_alpha + gyro.x * (1 - stabSettings.gyro_alpha);
626 q_item.y[1] = q_item.y[1] * stabSettings.gyro_alpha + gyro.y * (1 - stabSettings.gyro_alpha);
627 q_item.y[2] = q_item.y[2] * stabSettings.gyro_alpha + gyro.z * (1 - stabSettings.gyro_alpha);
628 q_item.u[0] = actuators.Roll;
629 q_item.u[1] = actuators.Pitch;
630 q_item.u[2] = actuators.Yaw;
631 q_item.throttle = actuators.Thrust;
632 q_item.sensorReadTimestamp = gyro.SensorReadTimestamp;
634 if (xQueueSend(atQueue, &q_item, 0) != pdTRUE) {
635 last_sample_unpushed = true;
636 } else {
637 last_sample_unpushed = false;
642 // this callback is only enabled if the AutoTune module is not running
643 // if it sees that AutoTune was added to the FMS it issues BOOT and ? alarms
644 static void FlightModeSettingsUpdatedCb(__attribute__((unused)) UAVObjEvent *ev)
646 if (AutoTuneFoundInFMS()) {
647 ExtendedAlarmsSet(SYSTEMALARMS_ALARM_BOOTFAULT, SYSTEMALARMS_ALARM_CRITICAL, SYSTEMALARMS_EXTENDEDALARMSTATUS_REBOOTREQUIRED, 0);
652 // check for the user quickly toggling the flight mode switch
653 // into and out of AutoTune, 3 times
654 // that is a signal that the user wants to try the next PID settings
655 // on the scale from smooth to quick
656 // when it exceeds the quickest setting, it starts back at the smoothest setting
657 static bool CheckFlightModeSwitchForPidRequest(uint8_t flightMode)
659 static uint32_t lastUpdateTime;
660 static uint8_t flightModePrev;
661 static uint8_t counter;
662 uint32_t updateTime;
664 // only count transitions into and out of autotune
665 if ((flightModePrev == FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE) ^ (flightMode == FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE)) {
666 flightModePrev = flightMode;
667 updateTime = xTaskGetTickCount();
668 // if it has been over 2 seconds, reset the counter
669 if (updateTime - lastUpdateTime > 2000) {
670 counter = 0;
672 // if the counter is reset, start a new time period
673 if (counter == 0) {
674 lastUpdateTime = updateTime;
676 // if flight mode has toggled into autotune 3 times but is currently not autotune
677 if (++counter >= 5 && flightMode != FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE) {
678 counter = 0;
679 return true;
683 return false;
687 // read SystemIdent uavos, update the local structures
688 // and set some flags based on the values
689 // it is used two ways:
690 // - on startup it reads settings so the user can reuse an old tune with smooth-quick
691 // - at tune time, it inits the state to default values of uavo xml file, in preparation for tuning
692 static void InitSystemIdent(bool loadDefaults)
694 SystemIdentSettingsGet(&systemIdentSettings);
695 if (loadDefaults) {
696 // get these 10.0 10.0 7.0 -4.0 from default values of SystemIdent (.Beta and .Tau)
697 // so that if they are changed there (mainly for future code changes), they will be changed here too
698 // save metadata from being changed by the following SetDefaults()
699 SystemIdentStateGetMetadata(&u.systemIdentStateMetaData);
700 SystemIdentStateSetDefaults(SystemIdentStateHandle(), 0);
701 SystemIdentStateSetMetadata(&u.systemIdentStateMetaData);
702 SystemIdentStateGet(&u.systemIdentState);
703 // Tau, GyroReadTimeAverage, Beta, and the Complete flag get default values
704 // in preparation for running AutoTune
705 systemIdentSettings.Tau = u.systemIdentState.Tau;
706 systemIdentSettings.GyroReadTimeAverage = u.systemIdentState.GyroReadTimeAverage;
707 memcpy(&systemIdentSettings.Beta, &u.systemIdentState.Beta, sizeof(SystemIdentSettingsBetaData));
708 systemIdentSettings.Complete = u.systemIdentState.Complete;
709 } else {
710 // Tau, GyroReadTimeAverage, Beta, and the Complete flag get stored values
711 // so the user can fly another battery to select and test PIDs with the slider/knob
712 u.systemIdentState.Tau = systemIdentSettings.Tau;
713 u.systemIdentState.GyroReadTimeAverage = systemIdentSettings.GyroReadTimeAverage;
714 memcpy(&u.systemIdentState.Beta, &systemIdentSettings.Beta, sizeof(SystemIdentStateBetaData));
715 u.systemIdentState.Complete = systemIdentSettings.Complete;
717 SystemIdentStateSet(&u.systemIdentState);
719 // (1.0f / PIOS_SENSOR_RATE) is gyro period
720 // the -1/10 makes it converge nicely, the other values make it converge the same way if the configuration is changed
721 // gyroReadTimeAverageAlphaAlpha is 0.9996 when the tuning duration is the default of 60 seconds
722 gyroReadTimeAverageAlphaAlpha = expapprox(-1.0f / PIOS_SENSOR_RATE / ((float)systemIdentSettings.TuningDuration / 10.0f));
723 if (!IS_REAL(gyroReadTimeAverageAlphaAlpha)) {
724 gyroReadTimeAverageAlphaAlpha = expapprox(-1.0f / 500.0f / (60 / 10)); // basically 0.9996
726 // 0.99999988f is as close to 1.0f as possible to make final average as smooth as possible
727 gyroReadTimeAverageAlpha = 0.99999988f;
728 gyroReadTimeAverage = u.systemIdentState.GyroReadTimeAverage;
730 uint8_t SmoothQuickSource = systemIdentSettings.SmoothQuickSource;
731 switch (SmoothQuickSource) {
732 case SMOOTH_QUICK_ACCESSORY_BASE + 0: // use accessory0
733 case SMOOTH_QUICK_ACCESSORY_BASE + 1: // use accessory1
734 case SMOOTH_QUICK_ACCESSORY_BASE + 2: // use accessory2
735 case SMOOTH_QUICK_ACCESSORY_BASE + 3: // use accessory3
736 // leave smoothQuickValue alone since it is always controlled by knob
737 // disable PID changing with flight mode switch
738 flightModeSwitchTogglePosition = -1;
739 // enable PID changing with accessory0-3
740 accessoryToUse = SmoothQuickSource - SMOOTH_QUICK_ACCESSORY_BASE;
741 break;
742 case SMOOTH_QUICK_TOGGLE_BASE + 3: // use flight mode switch toggle with 3 points
743 case SMOOTH_QUICK_TOGGLE_BASE + 5: // use flight mode switch toggle with 5 points
744 case SMOOTH_QUICK_TOGGLE_BASE + 7: // use flight mode switch toggle with 7 points
745 // don't allow init of current toggle position in the middle of 3x fms toggle
746 if (loadDefaults) {
747 // set toggle to middle of range
748 smoothQuickValue = 0.0f;
750 // enable PID changing with flight mode switch
751 flightModeSwitchTogglePosition = (SmoothQuickSource - 1 - SMOOTH_QUICK_TOGGLE_BASE) / 2;
752 // disable PID changing with accessory0-3
753 accessoryToUse = -1;
754 break;
755 case SMOOTH_QUICK_DISABLED:
756 default:
757 // leave smoothQuickValue alone so user can set it to a different value and have it stay that value
758 // disable PID changing with flight mode switch
759 flightModeSwitchTogglePosition = -1;
760 // disable PID changing with accessory0-3
761 accessoryToUse = -1;
762 break;
767 // update the gain and delay with current calculated value
768 // these are stored in the settings for use with next battery
769 // and also in the state for logging purposes
770 static void UpdateSystemIdentState(const float *X, const float *noise,
771 float dT_s, uint32_t predicts, uint32_t spills, float hover_throttle)
773 u.systemIdentState.Beta.Roll = X[6];
774 u.systemIdentState.Beta.Pitch = X[7];
775 u.systemIdentState.Beta.Yaw = X[8];
776 u.systemIdentState.Bias.Roll = X[10];
777 u.systemIdentState.Bias.Pitch = X[11];
778 u.systemIdentState.Bias.Yaw = X[12];
779 u.systemIdentState.Tau = X[9];
780 if (noise) {
781 u.systemIdentState.Noise.Roll = noise[0];
782 u.systemIdentState.Noise.Pitch = noise[1];
783 u.systemIdentState.Noise.Yaw = noise[2];
785 u.systemIdentState.Period = dT_s * 1000.0f;
786 u.systemIdentState.NumAfPredicts = predicts;
787 u.systemIdentState.NumSpilledPts = spills;
788 u.systemIdentState.HoverThrottle = hover_throttle;
789 u.systemIdentState.GyroReadTimeAverage = gyroReadTimeAverage;
791 // 'settings' tau, beta, and GyroReadTimeAverage have same value as 'state' versions
792 // the state version produces a GCS log
793 // the settings version is remembered after power off/on
794 systemIdentSettings.Tau = u.systemIdentState.Tau;
795 memcpy(&systemIdentSettings.Beta, &u.systemIdentState.Beta, sizeof(SystemIdentSettingsBetaData));
796 systemIdentSettings.GyroReadTimeAverage = u.systemIdentState.GyroReadTimeAverage;
797 systemIdentSettings.SmoothQuickValue = smoothQuickValue;
799 SystemIdentStateSet(&u.systemIdentState);
803 // when running AutoTune mode, this bypasses manualcontrol.c / stabilizedhandler.c
804 // to control whether the multicopter should be in Attitude mode vs. SystemIdent mode
805 static void UpdateStabilizationDesired(bool doingIdent)
807 StabilizationDesiredData stabDesired;
808 ManualControlCommandData manualControlCommand;
810 ManualControlCommandGet(&manualControlCommand);
812 stabDesired.Roll = manualControlCommand.Roll * rollMax;
813 stabDesired.Pitch = manualControlCommand.Pitch * pitchMax;
814 stabDesired.Yaw = manualControlCommand.Yaw * manualRate.Yaw;
815 stabDesired.Thrust = manualControlCommand.Thrust;
817 if (doingIdent) {
818 stabDesired.StabilizationMode.Roll = STABILIZATIONDESIRED_STABILIZATIONMODE_SYSTEMIDENT;
819 stabDesired.StabilizationMode.Pitch = STABILIZATIONDESIRED_STABILIZATIONMODE_SYSTEMIDENT;
820 stabDesired.StabilizationMode.Yaw = STABILIZATIONDESIRED_STABILIZATIONMODE_SYSTEMIDENT;
821 } else {
822 stabDesired.StabilizationMode.Roll = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE;
823 stabDesired.StabilizationMode.Pitch = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE;
824 stabDesired.StabilizationMode.Yaw = STABILIZATIONDESIRED_STABILIZATIONMODE_RATE;
826 stabDesired.StabilizationMode.Thrust = STABILIZATIONDESIRED_STABILIZATIONMODE_MANUAL;
828 StabilizationDesiredSet(&stabDesired);
832 // check the completed autotune state (mainly gain and delay)
833 // to see if it is reasonable
834 // return a bit mask of errors detected
835 static uint8_t CheckSettingsRaw()
837 uint8_t retVal = 0;
839 // inverting the comparisons then negating the bool result should catch the nans but it doesn't
840 // so explictly check for nans
841 if (!IS_REAL(expapprox(u.systemIdentState.Tau))) {
842 retVal |= TAU_NAN;
844 if (!IS_REAL(expapprox(u.systemIdentState.Beta.Roll))) {
845 retVal |= BETA_NAN;
847 if (!IS_REAL(expapprox(u.systemIdentState.Beta.Pitch))) {
848 retVal |= BETA_NAN;
850 if (!IS_REAL(expapprox(u.systemIdentState.Beta.Yaw))) {
851 retVal |= BETA_NAN;
854 // Check the axis gains
855 // Extreme values: Your roll or pitch gain was lower than expected. This will result in large PID values.
856 if (u.systemIdentState.Beta.Roll < 6) {
857 retVal |= ROLL_BETA_LOW;
859 if (u.systemIdentState.Beta.Pitch < 6) {
860 retVal |= PITCH_BETA_LOW;
862 // yaw gain is no longer checked, because the yaw options only include:
863 // - not calculating yaw
864 // - limiting yaw gain between two sane values (default)
865 // - ignoring errors and accepting the calculated yaw
867 // Check the response speed
868 // Extreme values: Your estimated response speed (tau) is slower than normal. This will result in large PID values.
869 if (expapprox(u.systemIdentState.Tau) > 0.1f) {
870 retVal |= TAU_TOO_LONG;
872 // Extreme values: Your estimated response speed (tau) is faster than normal. This will result in large PID values.
873 else if (expapprox(u.systemIdentState.Tau) < 0.008f) {
874 retVal |= TAU_TOO_SHORT;
877 // Sanity check: CPU is too slow compared to gyro rate
878 if (gyroReadTimeAverage > (1.0f / PIOS_SENSOR_RATE)) {
879 retVal |= CPU_TOO_SLOW;
882 return retVal;
886 // check the completed autotune state (mainly gain and delay)
887 // to see if it is reasonable
888 // override bad yaw values if configured that way
889 // return a bit mask of errors detected
890 static uint8_t CheckSettings()
892 uint8_t retVal = CheckSettingsRaw();
894 if (systemIdentSettings.DisableSanityChecks) {
895 retVal = 0;
897 return retVal;
901 // given Tau"+"GyroReadTimeAverage(delay) and Beta(gain) from the tune (and user selection of smooth to quick) calculate the PIDs
902 // this code came from dRonin GCS and has been converted from double precision math to single precision
903 static void ComputeStabilizationAndSetPidsFromDampAndNoise(float dampRate, float noiseRate)
905 _Static_assert(sizeof(StabilizationSettingsBank1Data) == sizeof(StabilizationBankData), "sizeof(StabilizationSettingsBank1Data) != sizeof(StabilizationBankData)");
906 StabilizationBankData volatile stabSettingsBank;
907 switch (systemIdentSettings.DestinationPidBank) {
908 case 1:
909 StabilizationSettingsBank1Get((void *)&stabSettingsBank);
910 break;
911 case 2:
912 StabilizationSettingsBank2Get((void *)&stabSettingsBank);
913 break;
914 case 3:
915 StabilizationSettingsBank3Get((void *)&stabSettingsBank);
916 break;
919 // These three parameters define the desired response properties
920 // - rate scale in the fraction of the natural speed of the system
921 // to strive for.
922 // - damp is the amount of damping in the system. higher values
923 // make oscillations less likely
924 // - ghf is the amount of high frequency gain and limits the influence
925 // of noise
926 const float ghf = noiseRate / 1000.0f;
927 const float damp = dampRate / 100.0f;
929 float tau = expapprox(u.systemIdentState.Tau) + systemIdentSettings.GyroReadTimeAverage;
930 float exp_beta_roll_times_ghf = expapprox(u.systemIdentState.Beta.Roll) * ghf;
931 float exp_beta_pitch_times_ghf = expapprox(u.systemIdentState.Beta.Pitch) * ghf;
933 float wn = 1.0f / tau;
934 float tau_d = 0.0f;
935 for (int i = 0; i < 30; i++) {
936 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);
937 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);
938 // Select the slowest filter property
939 tau_d = (tau_d_roll > tau_d_pitch) ? tau_d_roll : tau_d_pitch;
940 wn = (tau + tau_d) / (tau * tau_d) / (2.0f * damp + 2.0f);
943 // Set the real pole position. The first pole is quite slow, which
944 // prevents the integral being too snappy and driving too much
945 // overshoot.
946 const float a = ((tau + tau_d) / tau / tau_d - 2.0f * damp * wn) / 20.0f;
947 const float b = ((tau + tau_d) / tau / tau_d - 2.0f * damp * wn - a);
949 // Calculate the gain for the outer loop by approximating the
950 // inner loop as a single order lpf. Set the outer loop to be
951 // critically damped;
952 const float zeta_o = 1.3f;
953 float kp_o = 1.0f / 4.0f / (zeta_o * zeta_o) / (1.0f / wn);
955 // Except, if this is very high, we may be slew rate limited and pick
956 // up oscillation that way. Fix it with very soft clamping.
957 // (dRonin) MaximumRate defaults to 350, 6.5 corresponds to where we begin
958 // clamping rate ourselves. ESCs, etc, it depends upon gains
959 // and any pre-emphasis they do. Still give ourselves partial credit
960 // for inner loop bandwidth.
962 // In dRonin, MaximumRate defaults to 350 and they begin clamping at outer Kp 6.5
963 // To avoid oscillation, find the minimum rate, calculate the ratio of that to 350,
964 // and scale (linearly) with that. Skip yaw. There is no outer yaw in the GUI.
965 const uint16_t minRate = MIN(stabSettingsBank.MaximumRate.Roll, stabSettingsBank.MaximumRate.Pitch);
966 const float kp_o_clamp = systemIdentSettings.OuterLoopKpSoftClamp * ((float)minRate / 350.0f);
967 if (kp_o > kp_o_clamp) {
968 kp_o = kp_o_clamp - sqrtf(kp_o_clamp) + sqrtf(kp_o);
970 kp_o *= 0.95f; // Pick up some margin.
971 // Add a zero at 1/15th the innermost bandwidth.
972 const float ki_o = 0.75f * kp_o / (2.0f * M_PI_F * tau * 15.0f);
974 float kpMax = 0.0f;
975 float betaMinLn = 1000.0f;
976 StabilizationBankRollRatePIDData volatile *rollPitchPid = NULL; // satisfy compiler warning only
978 for (int i = 0; i < ((systemIdentSettings.CalculateYaw != SYSTEMIDENTSETTINGS_CALCULATEYAW_FALSE) ? 3 : 2); i++) {
979 float betaLn = SystemIdentStateBetaToArray(u.systemIdentState.Beta)[i];
980 float beta = expapprox(betaLn);
981 float ki;
982 float kp;
983 float kd;
985 switch (i) {
986 case 0: // Roll
987 case 1: // Pitch
988 ki = a * b * wn * wn * tau * tau_d / beta;
989 kp = tau * tau_d * ((a + b) * wn * wn + 2.0f * a * b * damp * wn) / beta - ki * tau_d;
990 kd = (tau * tau_d * (a * b + wn * wn + (a + b) * 2.0f * damp * wn) - 1.0f) / beta - kp * tau_d;
991 if (betaMinLn > betaLn) {
992 betaMinLn = betaLn;
993 // RollRatePID PitchRatePID YawRatePID
994 // form an array of structures
995 // point to one
996 // this pointer arithmetic no longer works as expected in a gcc 64 bit test program
997 // rollPitchPid = &(&stabSettingsBank.RollRatePID)[i];
998 if (i == 0) {
999 rollPitchPid = &stabSettingsBank.RollRatePID;
1000 } else {
1001 rollPitchPid = (StabilizationBankRollRatePIDData *)&stabSettingsBank.PitchRatePID;
1004 break;
1005 case 2: // Yaw
1006 // yaw uses a mixture of yaw and the slowest axis (pitch) for it's beta and thus PID calculation
1007 // calculate the ratio to use when converting from the slowest axis (pitch) to the yaw axis
1008 // as (e^(betaMinLn-betaYawLn))^0.6
1009 // which is (e^betaMinLn / e^betaYawLn)^0.6
1010 // which is (betaMin / betaYaw)^0.6
1011 // which is betaMin^0.6 / betaYaw^0.6
1012 // now given that kp for each axis can be written as kpaxis = xp / betaaxis
1013 // for xp that is constant across all axes
1014 // then kpmin (probably kppitch) was xp / betamin (probably betapitch)
1015 // which we multiply by betaMin^0.6 / betaYaw^0.6 to get the new Yaw kp
1016 // so the new kpyaw is (xp / betaMin) * (betaMin^0.6 / betaYaw^0.6)
1017 // which is (xp / betaMin) * (betaMin^0.6 / betaYaw^0.6)
1018 // which is (xp * betaMin^0.6) / (betaMin * betaYaw^0.6)
1019 // which is xp / (betaMin * betaYaw^0.6 / betaMin^0.6)
1020 // which is xp / (betaMin^0.4 * betaYaw^0.6)
1021 // hence the new effective betaYaw for Yaw P is (betaMin^0.4)*(betaYaw^0.6)
1022 beta = expapprox(0.6f * (betaMinLn - u.systemIdentState.Beta.Yaw));
1023 // this casting assumes that RollRatePID is the same as PitchRatePID
1024 kp = rollPitchPid->Kp * beta;
1025 ki = 0.8f * rollPitchPid->Ki * beta;
1026 kd = 0.8f * rollPitchPid->Kd * beta;
1027 break;
1030 if (i < 2) {
1031 if (kpMax < kp) {
1032 kpMax = kp;
1034 } else {
1035 // use the ratio with the largest roll/pitch kp to limit yaw kp to a reasonable value
1036 // use largest roll/pitch kp because it is the axis most slowed by rotational inertia
1037 // and yaw is also slowed maximally by rotational inertia
1038 // note that kp, ki, kd are all proportional in beta
1039 // so reducing them all proportionally is the same as changing beta
1040 float min = 0.0f;
1041 float max = 0.0f;
1042 switch (systemIdentSettings.CalculateYaw) {
1043 case SYSTEMIDENTSETTINGS_CALCULATEYAW_TRUELIMITTORATIO:
1044 max = kpMax * systemIdentSettings.YawToRollPitchPIDRatioMax;
1045 min = kpMax * systemIdentSettings.YawToRollPitchPIDRatioMin;
1046 break;
1047 case SYSTEMIDENTSETTINGS_CALCULATEYAW_TRUEIGNORELIMIT:
1048 default:
1049 max = 1000.0f;
1050 min = 0.0f;
1051 break;
1054 float ratio = 1.0f;
1055 if (min > 0.0f && kp < min) {
1056 ratio = kp / min;
1057 } else if (max > 0.0f && kp > max) {
1058 ratio = kp / max;
1060 kp /= ratio;
1061 ki /= ratio;
1062 kd /= ratio;
1065 // reduce kd if so configured
1066 // both of the quads tested for d term oscillation exhibit some degree of it with the stock autotune PIDs
1067 // if may be that adjusting stabSettingsBank.DerivativeCutoff would have a similar affect
1068 // reducing kd requires that kp and ki be reduced to avoid ringing
1069 // the amount to reduce kp and ki is taken from ZN tuning
1070 // specifically kp is parameterized based on the ratio between kp(PID) and kp(PI) as the D factor varies from 1 to 0
1071 // https://en.wikipedia.org/wiki/PID_controller
1072 // Kp Ki Kd
1073 // -----------------------------------
1074 // P 0.50*Ku - -
1075 // PI 0.45*Ku 1.2*Kp/Tu -
1076 // PID 0.60*Ku 2.0*Kp/Tu Kp*Tu/8
1078 // so Kp is multiplied by (.45/.60) if Kd is reduced to 0
1079 // and Ki is multiplied by (1.2/2.0) if Kd is reduced to 0
1080 #define KP_REDUCTION (.45f / .60f)
1081 #define KI_REDUCTION (1.2f / 2.0f)
1083 // this link gives some additional ratios that are different
1084 // the reduced overshoot ratios are invalid for this purpose
1085 // https://en.wikipedia.org/wiki/Ziegler%E2%80%93Nichols_method
1086 // Kp Ki Kd
1087 // ------------------------------------------------
1088 // P 0.50*Ku - -
1089 // PI 0.45*Ku Tu/1.2 -
1090 // PD 0.80*Ku - Tu/8
1091 // classic PID 0.60*Ku Tu/2.0 Tu/8 #define KP_REDUCTION (.45f/.60f) #define KI_REDUCTION (1.2f/2.0f)
1092 // 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)
1093 // some overshoot 0.33*Ku Tu/2.0 Tu/3 #define KP_REDUCTION (.45f/.33f) #define KI_REDUCTION (1.2f/2.0f)
1094 // no overshoot 0.20*Ku Tu/2.0 Tu/3 #define KP_REDUCTION (.45f/.20f) #define KI_REDUCTION (1.2f/2.0f)
1096 // reduce roll and pitch, but not yaw
1097 // yaw PID is entirely based on roll or pitch PIDs which have already been reduced
1098 if (i < 2) {
1099 kp = kp * KP_REDUCTION + kp * systemIdentSettings.DerivativeFactor * (1.0f - KP_REDUCTION);
1100 ki = ki * KI_REDUCTION + ki * systemIdentSettings.DerivativeFactor * (1.0f - KI_REDUCTION);
1101 kd *= systemIdentSettings.DerivativeFactor;
1104 switch (i) {
1105 case 0: // Roll
1106 stabSettingsBank.RollRatePID.Kp = kp;
1107 stabSettingsBank.RollRatePID.Ki = ki;
1108 stabSettingsBank.RollRatePID.Kd = kd;
1109 stabSettingsBank.RollPI.Kp = kp_o;
1110 stabSettingsBank.RollPI.Ki = ki_o;
1111 break;
1112 case 1: // Pitch
1113 stabSettingsBank.PitchRatePID.Kp = kp;
1114 stabSettingsBank.PitchRatePID.Ki = ki;
1115 stabSettingsBank.PitchRatePID.Kd = kd;
1116 stabSettingsBank.PitchPI.Kp = kp_o;
1117 stabSettingsBank.PitchPI.Ki = ki_o;
1118 break;
1119 case 2: // Yaw
1120 stabSettingsBank.YawRatePID.Kp = kp;
1121 stabSettingsBank.YawRatePID.Ki = ki;
1122 stabSettingsBank.YawRatePID.Kd = kd;
1123 #if 0
1124 // if we ever choose to use these
1125 // (e.g. mag yaw attitude)
1126 // here they are
1127 stabSettingsBank.YawPI.Kp = kp_o;
1128 stabSettingsBank.YawPI.Ki = ki_o;
1129 #endif
1130 break;
1134 // Librepilot might do something more with this some time
1135 // stabSettingsBank.DerivativeCutoff = 1.0f / (2.0f*M_PI_F*tau_d);
1136 // SystemIdentSettingsDerivativeCutoffSet(&systemIdentSettings.DerivativeCutoff);
1137 // then something to schedule saving this permanently to flash when disarmed
1139 // Save PIDs to UAVO RAM (not permanently yet)
1140 switch (systemIdentSettings.DestinationPidBank) {
1141 case 1:
1142 StabilizationSettingsBank1Set((void *)&stabSettingsBank);
1143 break;
1144 case 2:
1145 StabilizationSettingsBank2Set((void *)&stabSettingsBank);
1146 break;
1147 case 3:
1148 StabilizationSettingsBank3Set((void *)&stabSettingsBank);
1149 break;
1154 // scale the damp and the noise to generate PIDs according to how a slider or other user specified ratio is set
1156 // when val is half way between min and max, it generates the default PIDs
1157 // when val is min, it generates the smoothest configured PIDs
1158 // when val is max, it generates the quickest configured PIDs
1160 // 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
1161 // 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
1163 // this is done piecewise because we are not guaranteed that default-min == max-default
1164 // but we are given that [smoothDamp,smoothNoise] [defaultDamp,defaultNoise] [quickDamp,quickNoise] are all good parameterizations
1165 // this code guarantees that we will get those exact parameterizations at (val =) min, (max+min)/2, and max
1166 static void ProportionPidsSmoothToQuick()
1168 float ratio, damp, noise;
1169 float min = -1.0f;
1170 float val = smoothQuickValue;
1171 float max = 1.0f;
1173 // translate from range [min, max] to range [0, max-min]
1174 // that takes care of min < 0 case too
1175 val -= min;
1176 max -= min;
1177 ratio = val / max;
1179 if (ratio <= 0.5f) {
1180 // scale ratio in [0,0.5] to produce PIDs in [smoothest,default]
1181 ratio *= 2.0f;
1182 damp = (systemIdentSettings.DampMax * (1.0f - ratio)) + (systemIdentSettings.DampRate * ratio);
1183 noise = (systemIdentSettings.NoiseMin * (1.0f - ratio)) + (systemIdentSettings.NoiseRate * ratio);
1184 } else {
1185 // scale ratio in [0.5,1.0] to produce PIDs in [default,quickest]
1186 ratio = (ratio - 0.5f) * 2.0f;
1187 damp = (systemIdentSettings.DampRate * (1.0f - ratio)) + (systemIdentSettings.DampMin * ratio);
1188 noise = (systemIdentSettings.NoiseRate * (1.0f - ratio)) + (systemIdentSettings.NoiseMax * ratio);
1191 ComputeStabilizationAndSetPidsFromDampAndNoise(damp, noise);
1192 // save it to the system, but not yet written to flash
1193 SystemIdentSettingsSmoothQuickValueSet(&smoothQuickValue);
1198 * Prediction step for EKF on control inputs to quad that
1199 * learns the system properties
1200 * @param X the current state estimate which is updated in place
1201 * @param P the current covariance matrix, updated in place
1202 * @param[in] the current control inputs (roll, pitch, yaw)
1203 * @param[in] the gyro measurements
1205 __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)
1207 const float Ts = dT_s;
1208 const float Tsq = Ts * Ts;
1209 const float Tsq3 = Tsq * Ts;
1210 const float Tsq4 = Tsq * Tsq;
1212 // for convenience and clarity code below uses the named versions of
1213 // the state variables
1214 float w1 = X[0]; // roll rate estimate
1215 float w2 = X[1]; // pitch rate estimate
1216 float w3 = X[2]; // yaw rate estimate
1217 float u1 = X[3]; // scaled roll torque
1218 float u2 = X[4]; // scaled pitch torque
1219 float u3 = X[5]; // scaled yaw torque
1220 const float e_b1 = expapprox(X[6]); // roll torque scale
1221 const float b1 = X[6];
1222 const float e_b2 = expapprox(X[7]); // pitch torque scale
1223 const float b2 = X[7];
1224 const float e_b3 = expapprox(X[8]); // yaw torque scale
1225 const float b3 = X[8];
1226 const float e_tau = expapprox(X[9]); // time response of the motors
1227 const float tau = X[9];
1228 const float bias1 = X[10]; // bias in the roll torque
1229 const float bias2 = X[11]; // bias in the pitch torque
1230 const float bias3 = X[12]; // bias in the yaw torque
1232 // inputs to the system (roll, pitch, yaw)
1233 const float u1_in = 4 * t_in * u_in[0];
1234 const float u2_in = 4 * t_in * u_in[1];
1235 const float u3_in = 4 * t_in * u_in[2];
1237 // measurements from gyro
1238 const float gyro_x = gyro[0];
1239 const float gyro_y = gyro[1];
1240 const float gyro_z = gyro[2];
1242 // update named variables because we want to use predicted
1243 // values below
1244 w1 = X[0] = w1 - Ts * bias1 * e_b1 + Ts * u1 * e_b1;
1245 w2 = X[1] = w2 - Ts * bias2 * e_b2 + Ts * u2 * e_b2;
1246 w3 = X[2] = w3 - Ts * bias3 * e_b3 + Ts * u3 * e_b3;
1247 u1 = X[3] = (Ts * u1_in) / (Ts + e_tau) + (u1 * e_tau) / (Ts + e_tau);
1248 u2 = X[4] = (Ts * u2_in) / (Ts + e_tau) + (u2 * e_tau) / (Ts + e_tau);
1249 u3 = X[5] = (Ts * u3_in) / (Ts + e_tau) + (u3 * e_tau) / (Ts + e_tau);
1250 // X[6] to X[12] unchanged
1252 /**** filter parameters ****/
1253 const float q_w = 1e-3f;
1254 const float q_ud = 1e-3f;
1255 const float q_B = 1e-6f;
1256 const float q_tau = 1e-6f;
1257 const float q_bias = 1e-19f;
1258 const float s_a = 150.0f; // expected gyro measurment noise
1260 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 };
1262 float D[AF_NUMP];
1263 for (uint32_t i = 0; i < AF_NUMP; i++) {
1264 D[i] = P[i];
1267 const float e_tau2 = e_tau * e_tau;
1268 const float e_tau3 = e_tau * e_tau2;
1269 const float e_tau4 = e_tau2 * e_tau2;
1270 const float Ts_e_tau2 = (Ts + e_tau) * (Ts + e_tau);
1271 const float Ts_e_tau4 = Ts_e_tau2 * Ts_e_tau2;
1273 // covariance propagation - D is stored copy of covariance
1274 P[0] = D[0] + Q[0] + 2 * Ts * e_b1 * (D[3] - D[28] - D[9] * bias1 + D[9] * u1)
1275 + 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
1276 + D[11] * (bias1 * bias1) + D[11] * (u1 * u1) - 2 * D[11] * bias1 * u1);
1277 P[1] = D[1] + Q[1] + 2 * Ts * e_b2 * (D[5] - D[33] - D[12] * bias2 + D[12] * u2)
1278 + 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
1279 + D[14] * (bias2 * bias2) + D[14] * (u2 * u2) - 2 * D[14] * bias2 * u2);
1280 P[2] = D[2] + Q[2] + 2 * Ts * e_b3 * (D[7] - D[38] - D[15] * bias3 + D[15] * u3)
1281 + 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
1282 + D[17] * (bias3 * bias3) + D[17] * (u3 * u3) - 2 * D[17] * bias3 * u3);
1283 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])
1284 + 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))
1285 + D[21] * Tsq * e_b1 * e_tau * (u1 - u1_in) + D[31] * Tsq * e_b1 * e_tau * (u1_in - u1)
1286 + D[24] * Tsq * e_b1 * e_tau * (u1 * (u1 - bias1) + u1_in * (bias1 - u1))) / Ts_e_tau2;
1287 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
1288 + Tsq * e_tau2 * (D[4] + 6 * Q[3] + u1 * (D[27] * u1 + 2 * D[21]) + u1_in * (D[27] * u1_in - 2 * D[21]))
1289 + 2 * D[21] * Ts * e_tau3 * (u1 - u1_in) - 2 * D[27] * Tsq * u1 * u1_in * e_tau2) / Ts_e_tau4;
1290 P[5] = (D[5] * (e_tau2 + Ts * e_tau) + Ts * e_b2 * e_tau2 * (D[6] - D[34])
1291 + Tsq * e_b2 * e_tau * (D[6] - D[34]) + D[19] * Ts * e_tau * (u2 - u2_in)
1292 + D[13] * e_b2 * (u2 * (Ts * e_tau2 + Tsq * e_tau) - bias2 * (Ts * e_tau2 + Tsq * e_tau))
1293 + D[22] * Tsq * e_b2 * e_tau * (u2 - u2_in) + D[36] * Tsq * e_b2 * e_tau * (u2_in - u2)
1294 + D[25] * Tsq * e_b2 * e_tau * (u2 * (u2 - bias2) + u2_in * (bias2 - u2))) / Ts_e_tau2;
1295 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
1296 + Tsq * e_tau2 * (D[6] + 6 * Q[4] + u2 * (D[27] * u2 + 2 * D[22]) + u2_in * (D[27] * u2_in - 2 * D[22]))
1297 + 2 * D[22] * Ts * e_tau3 * (u2 - u2_in) - 2 * D[27] * Tsq * u2 * u2_in * e_tau2) / Ts_e_tau4;
1298 P[7] = (D[7] * (e_tau2 + Ts * e_tau) + Ts * e_b3 * e_tau2 * (D[8] - D[39])
1299 + Tsq * e_b3 * e_tau * (D[8] - D[39]) + D[20] * Ts * e_tau * (u3 - u3_in)
1300 + D[16] * e_b3 * (u3 * (Ts * e_tau2 + Tsq * e_tau) - bias3 * (Ts * e_tau2 + Tsq * e_tau))
1301 + D[23] * Tsq * e_b3 * e_tau * (u3 - u3_in) + D[41] * Tsq * e_b3 * e_tau * (u3_in - u3)
1302 + D[26] * Tsq * e_b3 * e_tau * (u3 * (u3 - bias3) + u3_in * (bias3 - u3))) / Ts_e_tau2;
1303 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
1304 + Tsq * e_tau2 * (D[8] + 6 * Q[5] + u3 * (D[27] * u3 + 2 * D[23]) + u3_in * (D[27] * u3_in - 2 * D[23]))
1305 + 2 * D[23] * Ts * e_tau3 * (u3 - u3_in) - 2 * D[27] * Tsq * u3 * u3_in * e_tau2) / Ts_e_tau4;
1306 P[9] = D[9] - Ts * e_b1 * (D[30] - D[10] + D[11] * (bias1 - u1));
1307 P[10] = (D[10] * (Ts + e_tau) + D[24] * Ts * (u1 - u1_in)) * (e_tau / Ts_e_tau2);
1308 P[11] = D[11] + Q[6];
1309 P[12] = D[12] - Ts * e_b2 * (D[35] - D[13] + D[14] * (bias2 - u2));
1310 P[13] = (D[13] * (Ts + e_tau) + D[25] * Ts * (u2 - u2_in)) * (e_tau / Ts_e_tau2);
1311 P[14] = D[14] + Q[7];
1312 P[15] = D[15] - Ts * e_b3 * (D[40] - D[16] + D[17] * (bias3 - u3));
1313 P[16] = (D[16] * (Ts + e_tau) + D[26] * Ts * (u3 - u3_in)) * (e_tau / Ts_e_tau2);
1314 P[17] = D[17] + Q[8];
1315 P[18] = D[18] - Ts * e_b1 * (D[31] - D[21] + D[24] * (bias1 - u1));
1316 P[19] = D[19] - Ts * e_b2 * (D[36] - D[22] + D[25] * (bias2 - u2));
1317 P[20] = D[20] - Ts * e_b3 * (D[41] - D[23] + D[26] * (bias3 - u3));
1318 P[21] = (D[21] * (Ts + e_tau) + D[27] * Ts * (u1 - u1_in)) * (e_tau / Ts_e_tau2);
1319 P[22] = (D[22] * (Ts + e_tau) + D[27] * Ts * (u2 - u2_in)) * (e_tau / Ts_e_tau2);
1320 P[23] = (D[23] * (Ts + e_tau) + D[27] * Ts * (u3 - u3_in)) * (e_tau / Ts_e_tau2);
1321 P[24] = D[24];
1322 P[25] = D[25];
1323 P[26] = D[26];
1324 P[27] = D[27] + Q[9];
1325 P[28] = D[28] - Ts * e_b1 * (D[32] - D[29] + D[30] * (bias1 - u1));
1326 P[29] = (D[29] * (Ts + e_tau) + D[31] * Ts * (u1 - u1_in)) * (e_tau / Ts_e_tau2);
1327 P[30] = D[30];
1328 P[31] = D[31];
1329 P[32] = D[32] + Q[10];
1330 P[33] = D[33] - Ts * e_b2 * (D[37] - D[34] + D[35] * (bias2 - u2));
1331 P[34] = (D[34] * (Ts + e_tau) + D[36] * Ts * (u2 - u2_in)) * (e_tau / Ts_e_tau2);
1332 P[35] = D[35];
1333 P[36] = D[36];
1334 P[37] = D[37] + Q[11];
1335 P[38] = D[38] - Ts * e_b3 * (D[42] - D[39] + D[40] * (bias3 - u3));
1336 P[39] = (D[39] * (Ts + e_tau) + D[41] * Ts * (u3 - u3_in)) * (e_tau / Ts_e_tau2);
1337 P[40] = D[40];
1338 P[41] = D[41];
1339 P[42] = D[42] + Q[12];
1341 /********* this is the update part of the equation ***********/
1342 float S[3] = { P[0] + s_a, P[1] + s_a, P[2] + s_a };
1343 X[0] = w1 + P[0] * ((gyro_x - w1) / S[0]);
1344 X[1] = w2 + P[1] * ((gyro_y - w2) / S[1]);
1345 X[2] = w3 + P[2] * ((gyro_z - w3) / S[2]);
1346 X[3] = u1 + P[3] * ((gyro_x - w1) / S[0]);
1347 X[4] = u2 + P[5] * ((gyro_y - w2) / S[1]);
1348 X[5] = u3 + P[7] * ((gyro_z - w3) / S[2]);
1349 X[6] = b1 + P[9] * ((gyro_x - w1) / S[0]);
1350 X[7] = b2 + P[12] * ((gyro_y - w2) / S[1]);
1351 X[8] = b3 + P[15] * ((gyro_z - w3) / S[2]);
1352 X[9] = tau + P[18] * ((gyro_x - w1) / S[0]) + P[19] * ((gyro_y - w2) / S[1]) + P[20] * ((gyro_z - w3) / S[2]);
1353 X[10] = bias1 + P[28] * ((gyro_x - w1) / S[0]);
1354 X[11] = bias2 + P[33] * ((gyro_y - w2) / S[1]);
1355 X[12] = bias3 + P[38] * ((gyro_z - w3) / S[2]);
1357 // update the duplicate cache
1358 for (uint32_t i = 0; i < AF_NUMP; i++) {
1359 D[i] = P[i];
1362 // This is an approximation that removes some cross axis uncertainty but
1363 // substantially reduces the number of calculations
1364 P[0] = -D[0] * (D[0] / S[0] - 1);
1365 P[1] = -D[1] * (D[1] / S[1] - 1);
1366 P[2] = -D[2] * (D[2] / S[2] - 1);
1367 P[3] = -D[3] * (D[0] / S[0] - 1);
1368 P[4] = D[4] - D[3] * (D[3] / S[0]);
1369 P[5] = -D[5] * (D[1] / S[1] - 1);
1370 P[6] = D[6] - D[5] * (D[5] / S[1]);
1371 P[7] = -D[7] * (D[2] / S[2] - 1);
1372 P[8] = D[8] - D[7] * (D[7] / S[2]);
1373 P[9] = -D[9] * (D[0] / S[0] - 1);
1374 P[10] = D[10] - D[3] * (D[9] / S[0]);
1375 P[11] = D[11] - D[9] * (D[9] / S[0]);
1376 P[12] = -D[12] * (D[1] / S[1] - 1);
1377 P[13] = D[13] - D[5] * (D[12] / S[1]);
1378 P[14] = D[14] - D[12] * (D[12] / S[1]);
1379 P[15] = -D[15] * (D[2] / S[2] - 1);
1380 P[16] = D[16] - D[7] * (D[15] / S[2]);
1381 P[17] = D[17] - D[15] * (D[15] / S[2]);
1382 P[18] = -D[18] * (D[0] / S[0] - 1);
1383 P[19] = -D[19] * (D[1] / S[1] - 1);
1384 P[20] = -D[20] * (D[2] / S[2] - 1);
1385 P[21] = D[21] - D[3] * (D[18] / S[0]);
1386 P[22] = D[22] - D[5] * (D[19] / S[1]);
1387 P[23] = D[23] - D[7] * (D[20] / S[2]);
1388 P[24] = D[24] - D[9] * (D[18] / S[0]);
1389 P[25] = D[25] - D[12] * (D[19] / S[1]);
1390 P[26] = D[26] - D[15] * (D[20] / S[2]);
1391 P[27] = D[27] - D[18] * (D[18] / S[0]) - D[19] * (D[19] / S[1]) - D[20] * (D[20] / S[2]);
1392 P[28] = -D[28] * (D[0] / S[0] - 1);
1393 P[29] = D[29] - D[3] * (D[28] / S[0]);
1394 P[30] = D[30] - D[9] * (D[28] / S[0]);
1395 P[31] = D[31] - D[18] * (D[28] / S[0]);
1396 P[32] = D[32] - D[28] * (D[28] / S[0]);
1397 P[33] = -D[33] * (D[1] / S[1] - 1);
1398 P[34] = D[34] - D[5] * (D[33] / S[1]);
1399 P[35] = D[35] - D[12] * (D[33] / S[1]);
1400 P[36] = D[36] - D[19] * (D[33] / S[1]);
1401 P[37] = D[37] - D[33] * (D[33] / S[1]);
1402 P[38] = -D[38] * (D[2] / S[2] - 1);
1403 P[39] = D[39] - D[7] * (D[38] / S[2]);
1404 P[40] = D[40] - D[15] * (D[38] / S[2]);
1405 P[41] = D[41] - D[20] * (D[38] / S[2]);
1406 P[42] = D[42] - D[38] * (D[38] / S[2]);
1408 // apply limits to some of the state variables
1409 if (X[9] > -1.5f) {
1410 X[9] = -1.5f;
1411 } else if (X[9] < -5.5f) { /* 4ms */
1412 X[9] = -5.5f;
1414 if (X[10] > 0.5f) {
1415 X[10] = 0.5f;
1416 } else if (X[10] < -0.5f) {
1417 X[10] = -0.5f;
1419 if (X[11] > 0.5f) {
1420 X[11] = 0.5f;
1421 } else if (X[11] < -0.5f) {
1422 X[11] = -0.5f;
1424 if (X[12] > 0.5f) {
1425 X[12] = 0.5f;
1426 } else if (X[12] < -0.5f) {
1427 X[12] = -0.5f;
1433 * Initialize the state variable and covariance matrix
1434 * for the system identification EKF
1436 static void AfInit(float X[AF_NUMX], float P[AF_NUMP])
1438 static const float qInit[AF_NUMX] = {
1439 1.0f, 1.0f, 1.0f,
1440 1.0f, 1.0f, 1.0f,
1441 0.05f, 0.05f, 0.005f,
1442 0.05f,
1443 0.05f, 0.05f, 0.05f
1446 // X[0] = X[1] = X[2] = 0.0f; // assume no rotation
1447 // X[3] = X[4] = X[5] = 0.0f; // and no net torque
1448 // X[6] = X[7] = 10.0f; // roll and pitch medium amount of strength
1449 // X[8] = 7.0f; // yaw strength
1450 // X[9] = -4.0f; // and 50 (18?) ms time scale
1451 // X[10] = X[11] = X[12] = 0.0f; // zero bias
1453 memset(X, 0, AF_NUMX * sizeof(X[0]));
1454 // get these 10.0 10.0 7.0 -4.0 from default values of SystemIdent (.Beta and .Tau)
1455 // so that if they are changed there (mainly for future code changes), they will be changed here too
1456 memcpy(&X[6], &u.systemIdentState.Beta, sizeof(u.systemIdentState.Beta));
1457 X[9] = u.systemIdentState.Tau;
1459 // P initialization
1460 memset(P, 0, AF_NUMP * sizeof(P[0]));
1461 P[0] = qInit[0];
1462 P[1] = qInit[1];
1463 P[2] = qInit[2];
1464 P[4] = qInit[3];
1465 P[6] = qInit[4];
1466 P[8] = qInit[5];
1467 P[11] = qInit[6];
1468 P[14] = qInit[7];
1469 P[17] = qInit[8];
1470 P[27] = qInit[9];
1471 P[32] = qInit[10];
1472 P[37] = qInit[11];
1473 P[42] = qInit[12];
1477 * @}
1478 * @}