update credits
[librepilot.git] / flight / modules / TxPID / txpid.c
blob47569bed33cb8d646418b607acad192f820119b3
1 /**
2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
4 * @{
5 * @addtogroup TxPIDModule TxPID Module
6 * @brief Optional module to tune PID settings using R/C transmitter.
7 * Updates PID settings in RAM in real-time using configured Accessory channels as controllers.
8 * @{
10 * @file txpid.c
11 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
12 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2011.
13 * @brief Optional module to tune PID settings using R/C transmitter.
15 * @see The GNU Public License (GPL) Version 3
17 *****************************************************************************/
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
26 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 * for more details.
29 * You should have received a copy of the GNU General Public License along
30 * with this program; if not, write to the Free Software Foundation, Inc.,
31 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 /**
35 * Output object: StabilizationSettings
37 * This module will periodically update values of stabilization PID settings
38 * depending on configured input control channels. New values of stabilization
39 * settings are not saved to flash, but updated in RAM. It is expected that the
40 * module will be enabled only for tuning. When desired values are found, they
41 * can be read via GCS and saved permanently. Then this module should be
42 * disabled again.
44 * UAVObjects are automatically generated by the UAVObjectGenerator from
45 * the object definition XML file.
47 * Modules have no API, all communication to other modules is done through UAVObjects.
48 * However modules may use the API exposed by shared libraries.
49 * See the OpenPilot wiki for more details.
50 * http://wiki.openpilot.org/display/Doc/OpenPilot+Architecture
54 #include "openpilot.h"
55 #include "txpidsettings.h"
56 #include "accessorydesired.h"
57 #include "manualcontrolcommand.h"
58 #include "stabilizationsettings.h"
59 #include "attitudesettings.h"
60 #ifdef REVOLUTION
61 #include "altitudeholdsettings.h"
62 #endif
63 #include "stabilizationbank.h"
64 #include "stabilizationsettingsbank1.h"
65 #include "stabilizationsettingsbank2.h"
66 #include "stabilizationsettingsbank3.h"
67 #include "flightstatus.h"
68 #include "txpidstatus.h"
69 #include "hwsettings.h"
72 // Configuration
74 #define SAMPLE_PERIOD_MS 200
75 #define TELEMETRY_UPDATE_PERIOD_MS 0 // 0 = update on change (default)
77 // Sanity checks
78 #if (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_INPUTS_NUMELEM) || \
79 (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_MINPID_NUMELEM) || \
80 (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_MAXPID_NUMELEM)
81 #error Invalid TxPID UAVObject definition (inconsistent number of field elements)
82 #endif
84 // Private types
86 // Private variables
88 // Private functions
89 static void updatePIDs(UAVObjEvent *ev);
90 static uint8_t update(float *var, float val);
91 static uint8_t updateUint16(uint16_t *var, float val);
92 static uint8_t updateUint8(uint8_t *var, float val);
93 static uint8_t updateInt8(int8_t *var, float val);
94 static float scale(float val, float inMin, float inMax, float outMin, float outMax);
96 /**
97 * Initialise the module, called on startup
98 * \returns 0 on success or -1 if initialisation failed
100 int32_t TxPIDInitialize(void)
102 bool txPIDEnabled;
104 HwSettingsOptionalModulesData optionalModules;
106 HwSettingsOptionalModulesGet(&optionalModules);
108 #ifdef MODULE_TXPID_BUILTIN
109 txPIDEnabled = true;
110 optionalModules.TxPID = HWSETTINGS_OPTIONALMODULES_ENABLED;
111 HwSettingsOptionalModulesSet(&optionalModules);
112 #else
113 if (optionalModules.TxPID == HWSETTINGS_OPTIONALMODULES_ENABLED) {
114 txPIDEnabled = true;
115 } else {
116 txPIDEnabled = false;
118 #endif
120 if (txPIDEnabled) {
121 TxPIDStatusInitialize();
122 AccessoryDesiredInitialize();
124 UAVObjEvent ev = {
125 .obj = AccessoryDesiredHandle(),
126 .instId = 0,
127 .event = 0,
128 .lowPriority = false,
130 EventPeriodicCallbackCreate(&ev, updatePIDs, SAMPLE_PERIOD_MS / portTICK_RATE_MS);
132 #if (TELEMETRY_UPDATE_PERIOD_MS != 0)
133 // Change StabilizationSettings update rate from OnChange to periodic
134 // to prevent telemetry link flooding with frequent updates in case of
135 // control channel jitter.
136 // Warning: saving to flash with this code active will change the
137 // StabilizationSettings update rate permanently. Use Metadata via
138 // browser to reset to defaults (telemetryAcked=true, OnChange).
139 UAVObjMetadata metadata;
140 StabilizationSettingsGetMetadata(&metadata);
141 metadata.telemetryAcked = 0;
142 metadata.telemetryUpdateMode = UPDATEMODE_PERIODIC;
143 metadata.telemetryUpdatePeriod = TELEMETRY_UPDATE_PERIOD_MS;
144 StabilizationSettingsSetMetadata(&metadata);
146 AttitudeSettingsGetMetadata(&metadata);
147 metadata.telemetryAcked = 0;
148 metadata.telemetryUpdateMode = UPDATEMODE_PERIODIC;
149 metadata.telemetryUpdatePeriod = TELEMETRY_UPDATE_PERIOD_MS;
150 AttitudeSettingsSetMetadata(&metadata);
151 #endif /* if (TELEMETRY_UPDATE_PERIOD_MS != 0) */
153 return 0;
156 return -1;
159 /* stub: module has no module thread */
160 int32_t TxPIDStart(void)
162 return 0;
165 MODULE_INITCALL(TxPIDInitialize, TxPIDStart);
168 * Update PIDs callback function
170 static void updatePIDs(UAVObjEvent *ev)
172 if (ev->obj != AccessoryDesiredHandle()) {
173 return;
176 TxPIDSettingsData inst;
177 TxPIDSettingsGet(&inst);
179 if (inst.UpdateMode == TXPIDSETTINGS_UPDATEMODE_NEVER) {
180 return;
183 uint8_t armed;
184 FlightStatusArmedGet(&armed);
185 if ((inst.UpdateMode == TXPIDSETTINGS_UPDATEMODE_WHENARMED) &&
186 (armed == FLIGHTSTATUS_ARMED_DISARMED)) {
187 return;
190 StabilizationBankData bank;
191 switch (inst.BankNumber) {
192 case 0:
193 StabilizationSettingsBank1Get((StabilizationSettingsBank1Data *)&bank);
194 break;
196 case 1:
197 StabilizationSettingsBank2Get((StabilizationSettingsBank2Data *)&bank);
198 break;
200 case 2:
201 StabilizationSettingsBank3Get((StabilizationSettingsBank3Data *)&bank);
202 break;
204 default:
205 return;
207 StabilizationSettingsData stab;
208 StabilizationSettingsGet(&stab);
210 AttitudeSettingsData att;
211 AttitudeSettingsGet(&att);
213 #ifdef REVOLUTION
214 AltitudeHoldSettingsData altitude;
215 AltitudeHoldSettingsGet(&altitude);
216 #endif
217 AccessoryDesiredData accessory;
219 TxPIDStatusData txpid_status;
220 TxPIDStatusGet(&txpid_status);
222 bool easyTuneEnabled = false;
224 uint8_t needsUpdateBank = 0;
225 uint8_t needsUpdateStab = 0;
226 uint8_t needsUpdateAtt = 0;
227 #ifdef REVOLUTION
228 uint8_t needsUpdateAltitude = 0;
229 #endif
231 // Loop through every enabled instance
232 for (uint8_t i = 0; i < TXPIDSETTINGS_PIDS_NUMELEM; i++) {
233 if (TxPIDSettingsPIDsToArray(inst.PIDs)[i] != TXPIDSETTINGS_PIDS_DISABLED) {
234 float value;
235 if (TxPIDSettingsInputsToArray(inst.Inputs)[i] == TXPIDSETTINGS_INPUTS_THROTTLE) {
236 ManualControlCommandThrottleGet(&value);
237 value = scale(value,
238 inst.ThrottleRange.Min,
239 inst.ThrottleRange.Max,
240 TxPIDSettingsMinPIDToArray(inst.MinPID)[i],
241 TxPIDSettingsMaxPIDToArray(inst.MaxPID)[i]);
242 } else if (AccessoryDesiredInstGet(
243 TxPIDSettingsInputsToArray(inst.Inputs)[i] - TXPIDSETTINGS_INPUTS_ACCESSORY0,
244 &accessory) == 0) {
245 value = scale(accessory.AccessoryVal, -1.0f, 1.0f,
246 TxPIDSettingsMinPIDToArray(inst.MinPID)[i],
247 TxPIDSettingsMaxPIDToArray(inst.MaxPID)[i]);
248 } else {
249 continue;
252 TxPIDStatusCurPIDToArray(txpid_status.CurPID)[i] = value;
254 switch (TxPIDSettingsPIDsToArray(inst.PIDs)[i]) {
255 case TXPIDSETTINGS_PIDS_ROLLRATEKP:
256 needsUpdateBank |= update(&bank.RollRatePID.Kp, value);
257 break;
258 case TXPIDSETTINGS_PIDS_EASYTUNERATEROLL:
259 easyTuneEnabled = true;
260 needsUpdateBank |= update(&bank.RollRatePID.Kp, value);
261 needsUpdateBank |= update(&bank.RollRatePID.Ki, value * inst.EasyTunePitchRollRateFactors.I);
262 needsUpdateBank |= update(&bank.RollRatePID.Kd, value * inst.EasyTunePitchRollRateFactors.D);
263 break;
264 case TXPIDSETTINGS_PIDS_EASYTUNERATEPITCH:
265 easyTuneEnabled = true;
266 needsUpdateBank |= update(&bank.PitchRatePID.Kp, value);
267 needsUpdateBank |= update(&bank.PitchRatePID.Ki, value * inst.EasyTunePitchRollRateFactors.I);
268 needsUpdateBank |= update(&bank.PitchRatePID.Kd, value * inst.EasyTunePitchRollRateFactors.D);
269 break;
270 case TXPIDSETTINGS_PIDS_ROLLRATEKI:
271 needsUpdateBank |= update(&bank.RollRatePID.Ki, value);
272 break;
273 case TXPIDSETTINGS_PIDS_ROLLRATEKD:
274 needsUpdateBank |= update(&bank.RollRatePID.Kd, value);
275 break;
276 case TXPIDSETTINGS_PIDS_ROLLRATEILIMIT:
277 needsUpdateBank |= update(&bank.RollRatePID.ILimit, value);
278 break;
279 case TXPIDSETTINGS_PIDS_ROLLRATERESP:
280 needsUpdateBank |= updateUint16(&bank.ManualRate.Roll, value);
281 break;
282 case TXPIDSETTINGS_PIDS_ROLLATTITUDEKP:
283 needsUpdateBank |= update(&bank.RollPI.Kp, value);
284 break;
285 case TXPIDSETTINGS_PIDS_ROLLATTITUDEKI:
286 needsUpdateBank |= update(&bank.RollPI.Ki, value);
287 break;
288 case TXPIDSETTINGS_PIDS_ROLLATTITUDEILIMIT:
289 needsUpdateBank |= update(&bank.RollPI.ILimit, value);
290 break;
291 case TXPIDSETTINGS_PIDS_ROLLATTITUDERESP:
292 needsUpdateBank |= updateUint8(&bank.RollMax, value);
293 break;
294 case TXPIDSETTINGS_PIDS_PITCHRATEKP:
295 needsUpdateBank |= update(&bank.PitchRatePID.Kp, value);
296 break;
297 case TXPIDSETTINGS_PIDS_PITCHRATEKI:
298 needsUpdateBank |= update(&bank.PitchRatePID.Ki, value);
299 break;
300 case TXPIDSETTINGS_PIDS_PITCHRATEKD:
301 needsUpdateBank |= update(&bank.PitchRatePID.Kd, value);
302 break;
303 case TXPIDSETTINGS_PIDS_PITCHRATEILIMIT:
304 needsUpdateBank |= update(&bank.PitchRatePID.ILimit, value);
305 break;
306 case TXPIDSETTINGS_PIDS_PITCHRATERESP:
307 needsUpdateBank |= updateUint16(&bank.ManualRate.Pitch, value);
308 break;
309 case TXPIDSETTINGS_PIDS_PITCHATTITUDEKP:
310 needsUpdateBank |= update(&bank.PitchPI.Kp, value);
311 break;
312 case TXPIDSETTINGS_PIDS_PITCHATTITUDEKI:
313 needsUpdateBank |= update(&bank.PitchPI.Ki, value);
314 break;
315 case TXPIDSETTINGS_PIDS_PITCHATTITUDEILIMIT:
316 needsUpdateBank |= update(&bank.PitchPI.ILimit, value);
317 break;
318 case TXPIDSETTINGS_PIDS_PITCHATTITUDERESP:
319 needsUpdateBank |= updateUint8(&bank.PitchMax, value);
320 break;
321 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKP:
322 needsUpdateBank |= update(&bank.RollRatePID.Kp, value);
323 needsUpdateBank |= update(&bank.PitchRatePID.Kp, value);
324 break;
325 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKI:
326 needsUpdateBank |= update(&bank.RollRatePID.Ki, value);
327 needsUpdateBank |= update(&bank.PitchRatePID.Ki, value);
328 break;
329 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKD:
330 needsUpdateBank |= update(&bank.RollRatePID.Kd, value);
331 needsUpdateBank |= update(&bank.PitchRatePID.Kd, value);
332 break;
333 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEILIMIT:
334 needsUpdateBank |= update(&bank.RollRatePID.ILimit, value);
335 needsUpdateBank |= update(&bank.PitchRatePID.ILimit, value);
336 break;
337 case TXPIDSETTINGS_PIDS_ROLLPITCHRATERESP:
338 needsUpdateBank |= updateUint16(&bank.ManualRate.Roll, value);
339 needsUpdateBank |= updateUint16(&bank.ManualRate.Pitch, value);
340 break;
341 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKP:
342 needsUpdateBank |= update(&bank.RollPI.Kp, value);
343 needsUpdateBank |= update(&bank.PitchPI.Kp, value);
344 break;
345 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKI:
346 needsUpdateBank |= update(&bank.RollPI.Ki, value);
347 needsUpdateBank |= update(&bank.PitchPI.Ki, value);
348 break;
349 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEILIMIT:
350 needsUpdateBank |= update(&bank.RollPI.ILimit, value);
351 needsUpdateBank |= update(&bank.PitchPI.ILimit, value);
352 break;
353 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDERESP:
354 needsUpdateBank |= updateUint8(&bank.RollMax, value);
355 needsUpdateBank |= updateUint8(&bank.PitchMax, value);
356 break;
357 case TXPIDSETTINGS_PIDS_YAWRATEKP:
358 needsUpdateBank |= update(&bank.YawRatePID.Kp, value);
359 break;
360 case TXPIDSETTINGS_PIDS_YAWRATEKI:
361 needsUpdateBank |= update(&bank.YawRatePID.Ki, value);
362 break;
363 case TXPIDSETTINGS_PIDS_YAWRATEKD:
364 needsUpdateBank |= update(&bank.YawRatePID.Kd, value);
365 break;
366 case TXPIDSETTINGS_PIDS_YAWRATEILIMIT:
367 needsUpdateBank |= update(&bank.YawRatePID.ILimit, value);
368 break;
369 case TXPIDSETTINGS_PIDS_YAWRATERESP:
370 needsUpdateBank |= updateUint16(&bank.ManualRate.Yaw, value);
371 break;
372 case TXPIDSETTINGS_PIDS_YAWATTITUDEKP:
373 needsUpdateBank |= update(&bank.YawPI.Kp, value);
374 break;
375 case TXPIDSETTINGS_PIDS_YAWATTITUDEKI:
376 needsUpdateBank |= update(&bank.YawPI.Ki, value);
377 break;
378 case TXPIDSETTINGS_PIDS_YAWATTITUDEILIMIT:
379 needsUpdateBank |= update(&bank.YawPI.ILimit, value);
380 break;
381 case TXPIDSETTINGS_PIDS_YAWATTITUDERESP:
382 needsUpdateBank |= updateUint8(&bank.YawMax, value);
383 break;
384 case TXPIDSETTINGS_PIDS_ROLLEXPO:
385 needsUpdateBank |= updateInt8(&bank.StickExpo.Roll, value);
386 break;
387 case TXPIDSETTINGS_PIDS_PITCHEXPO:
388 needsUpdateBank |= updateInt8(&bank.StickExpo.Pitch, value);
389 break;
390 case TXPIDSETTINGS_PIDS_ROLLPITCHEXPO:
391 needsUpdateBank |= updateInt8(&bank.StickExpo.Roll, value);
392 needsUpdateBank |= updateInt8(&bank.StickExpo.Pitch, value);
393 break;
394 case TXPIDSETTINGS_PIDS_YAWEXPO:
395 needsUpdateBank |= updateInt8(&bank.StickExpo.Yaw, value);
396 break;
397 case TXPIDSETTINGS_PIDS_GYROTAU:
398 needsUpdateStab |= update(&stab.GyroTau, value);
399 break;
400 case TXPIDSETTINGS_PIDS_ACROROLLFACTOR:
401 needsUpdateBank |= updateUint8(&bank.AcroInsanityFactor.Roll, value);
402 break;
403 case TXPIDSETTINGS_PIDS_ACROPITCHFACTOR:
404 needsUpdateBank |= updateUint8(&bank.AcroInsanityFactor.Pitch, value);
405 break;
406 case TXPIDSETTINGS_PIDS_ACROROLLPITCHFACTOR:
407 needsUpdateBank |= updateUint8(&bank.AcroInsanityFactor.Roll, value);
408 needsUpdateBank |= updateUint8(&bank.AcroInsanityFactor.Pitch, value);
409 break;
410 case TXPIDSETTINGS_PIDS_ACCELTAU:
411 needsUpdateAtt |= update(&att.AccelTau, value);
412 break;
413 case TXPIDSETTINGS_PIDS_ACCELKP:
414 needsUpdateAtt |= update(&att.AccelKp, value);
415 break;
416 case TXPIDSETTINGS_PIDS_ACCELKI:
417 needsUpdateAtt |= update(&att.AccelKi, value);
418 break;
420 #ifdef REVOLUTION
421 case TXPIDSETTINGS_PIDS_ALTITUDEPOSKP:
422 needsUpdateAltitude |= update(&altitude.VerticalPosP, value);
423 break;
424 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKP:
425 needsUpdateAltitude |= update(&altitude.VerticalVelPID.Kp, value);
426 break;
427 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKI:
428 needsUpdateAltitude |= update(&altitude.VerticalVelPID.Ki, value);
429 break;
430 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKD:
431 needsUpdateAltitude |= update(&altitude.VerticalVelPID.Kd, value);
432 break;
433 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYBETA:
434 needsUpdateAltitude |= update(&altitude.VerticalVelPID.Beta, value);
435 break;
436 #endif
437 default:
438 PIOS_Assert(0);
442 if (needsUpdateStab) {
443 StabilizationSettingsSet(&stab);
445 if (needsUpdateAtt) {
446 AttitudeSettingsSet(&att);
448 #ifdef REVOLUTION
449 if (needsUpdateAltitude) {
450 AltitudeHoldSettingsSet(&altitude);
452 #endif
453 if (easyTuneEnabled && (inst.EasyTuneRatePIDRecalculateYaw != TXPIDSETTINGS_EASYTUNERATEPIDRECALCULATEYAW_FALSE)) {
454 float newKp = (bank.RollRatePID.Kp + bank.PitchRatePID.Kp) * .5f * inst.EasyTuneYawRateFactors.P;
455 needsUpdateBank |= update(&bank.YawRatePID.Kp, newKp);
456 needsUpdateBank |= update(&bank.YawRatePID.Ki, newKp * inst.EasyTuneYawRateFactors.I);
457 needsUpdateBank |= update(&bank.YawRatePID.Kd, newKp * inst.EasyTuneYawRateFactors.D);
459 if (needsUpdateBank) {
460 switch (inst.BankNumber) {
461 case 0:
462 StabilizationSettingsBank1Set((StabilizationSettingsBank1Data *)&bank);
463 break;
465 case 1:
466 StabilizationSettingsBank2Set((StabilizationSettingsBank2Data *)&bank);
467 break;
469 case 2:
470 StabilizationSettingsBank3Set((StabilizationSettingsBank3Data *)&bank);
471 break;
473 default:
474 return;
478 if (needsUpdateStab ||
479 needsUpdateAtt ||
480 #ifdef REVOLUTION
481 needsUpdateAltitude ||
482 #endif /* REVOLUTION */
483 needsUpdateBank) {
484 TxPIDStatusSet(&txpid_status);;
489 * Scales input val from [inMin..inMax] range to [outMin..outMax].
490 * If val is out of input range (inMin <= inMax), it will be bound.
491 * (outMin > outMax) is ok, in that case output will be decreasing.
493 * \returns scaled value
495 static float scale(float val, float inMin, float inMax, float outMin, float outMax)
497 // bound input value
498 if (val > inMax) {
499 val = inMax;
501 if (val < inMin) {
502 val = inMin;
505 // normalize input value to [0..1]
506 if (inMax <= inMin) {
507 val = 0.0f;
508 } else {
509 val = (val - inMin) / (inMax - inMin);
512 // update output bounds
513 if (outMin > outMax) {
514 float t = outMin;
515 outMin = outMax;
516 outMax = t;
517 val = 1.0f - val;
520 return (outMax - outMin) * val + outMin;
524 * Updates var using val if needed.
525 * \returns 1 if updated, 0 otherwise
527 static uint8_t update(float *var, float val)
529 /* FIXME: this is not an entirely correct way
530 * to check if the two floating point
531 * numbers are 'not equal'.
532 * Epsilon of 1e-9 is probably okay for the range
533 * of numbers we see here*/
534 if (fabsf(*var - val) > 1e-9f) {
535 *var = val;
536 return 1;
538 return 0;
542 * Updates var using val if needed.
543 * \returns 1 if updated, 0 otherwise
545 static uint8_t updateUint16(uint16_t *var, float val)
547 uint16_t roundedVal = (uint16_t)roundf(val);
549 if (*var != roundedVal) {
550 *var = roundedVal;
551 return 1;
553 return 0;
557 * Updates var using val if needed.
558 * \returns 1 if updated, 0 otherwise
560 static uint8_t updateUint8(uint8_t *var, float val)
562 uint8_t roundedVal = (uint8_t)roundf(val);
564 if (*var != roundedVal) {
565 *var = roundedVal;
566 return 1;
568 return 0;
572 * Updates var using val if needed.
573 * \returns 1 if updated, 0 otherwise
575 static uint8_t updateInt8(int8_t *var, float val)
577 int8_t roundedVal = (int8_t)roundf(val);
579 if (*var != roundedVal) {
580 *var = roundedVal;
581 return 1;
583 return 0;
587 * @}
591 * @}