2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
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.
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
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
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
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"
61 #include "altitudeholdsettings.h"
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"
74 #define SAMPLE_PERIOD_MS 200
75 #define TELEMETRY_UPDATE_PERIOD_MS 0 // 0 = update on change (default)
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)
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
);
97 * Initialise the module, called on startup
98 * \returns 0 on success or -1 if initialisation failed
100 int32_t TxPIDInitialize(void)
104 HwSettingsOptionalModulesData optionalModules
;
106 HwSettingsOptionalModulesGet(&optionalModules
);
108 #ifdef MODULE_TXPID_BUILTIN
110 optionalModules
.TxPID
= HWSETTINGS_OPTIONALMODULES_ENABLED
;
111 HwSettingsOptionalModulesSet(&optionalModules
);
113 if (optionalModules
.TxPID
== HWSETTINGS_OPTIONALMODULES_ENABLED
) {
116 txPIDEnabled
= false;
121 TxPIDStatusInitialize();
122 AccessoryDesiredInitialize();
125 .obj
= AccessoryDesiredHandle(),
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) */
159 /* stub: module has no module thread */
160 int32_t TxPIDStart(void)
165 MODULE_INITCALL(TxPIDInitialize
, TxPIDStart
);
168 * Update PIDs callback function
170 static void updatePIDs(UAVObjEvent
*ev
)
172 if (ev
->obj
!= AccessoryDesiredHandle()) {
176 TxPIDSettingsData inst
;
177 TxPIDSettingsGet(&inst
);
179 if (inst
.UpdateMode
== TXPIDSETTINGS_UPDATEMODE_NEVER
) {
184 FlightStatusArmedGet(&armed
);
185 if ((inst
.UpdateMode
== TXPIDSETTINGS_UPDATEMODE_WHENARMED
) &&
186 (armed
== FLIGHTSTATUS_ARMED_DISARMED
)) {
190 StabilizationBankData bank
;
191 switch (inst
.BankNumber
) {
193 StabilizationSettingsBank1Get((StabilizationSettingsBank1Data
*)&bank
);
197 StabilizationSettingsBank2Get((StabilizationSettingsBank2Data
*)&bank
);
201 StabilizationSettingsBank3Get((StabilizationSettingsBank3Data
*)&bank
);
207 StabilizationSettingsData stab
;
208 StabilizationSettingsGet(&stab
);
210 AttitudeSettingsData att
;
211 AttitudeSettingsGet(&att
);
214 AltitudeHoldSettingsData altitude
;
215 AltitudeHoldSettingsGet(&altitude
);
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;
228 uint8_t needsUpdateAltitude
= 0;
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
) {
235 if (TxPIDSettingsInputsToArray(inst
.Inputs
)[i
] == TXPIDSETTINGS_INPUTS_THROTTLE
) {
236 ManualControlCommandThrottleGet(&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
,
245 value
= scale(accessory
.AccessoryVal
, -1.0f
, 1.0f
,
246 TxPIDSettingsMinPIDToArray(inst
.MinPID
)[i
],
247 TxPIDSettingsMaxPIDToArray(inst
.MaxPID
)[i
]);
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
);
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
);
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
);
270 case TXPIDSETTINGS_PIDS_ROLLRATEKI
:
271 needsUpdateBank
|= update(&bank
.RollRatePID
.Ki
, value
);
273 case TXPIDSETTINGS_PIDS_ROLLRATEKD
:
274 needsUpdateBank
|= update(&bank
.RollRatePID
.Kd
, value
);
276 case TXPIDSETTINGS_PIDS_ROLLRATEILIMIT
:
277 needsUpdateBank
|= update(&bank
.RollRatePID
.ILimit
, value
);
279 case TXPIDSETTINGS_PIDS_ROLLRATERESP
:
280 needsUpdateBank
|= updateUint16(&bank
.ManualRate
.Roll
, value
);
282 case TXPIDSETTINGS_PIDS_ROLLATTITUDEKP
:
283 needsUpdateBank
|= update(&bank
.RollPI
.Kp
, value
);
285 case TXPIDSETTINGS_PIDS_ROLLATTITUDEKI
:
286 needsUpdateBank
|= update(&bank
.RollPI
.Ki
, value
);
288 case TXPIDSETTINGS_PIDS_ROLLATTITUDEILIMIT
:
289 needsUpdateBank
|= update(&bank
.RollPI
.ILimit
, value
);
291 case TXPIDSETTINGS_PIDS_ROLLATTITUDERESP
:
292 needsUpdateBank
|= updateUint8(&bank
.RollMax
, value
);
294 case TXPIDSETTINGS_PIDS_PITCHRATEKP
:
295 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kp
, value
);
297 case TXPIDSETTINGS_PIDS_PITCHRATEKI
:
298 needsUpdateBank
|= update(&bank
.PitchRatePID
.Ki
, value
);
300 case TXPIDSETTINGS_PIDS_PITCHRATEKD
:
301 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kd
, value
);
303 case TXPIDSETTINGS_PIDS_PITCHRATEILIMIT
:
304 needsUpdateBank
|= update(&bank
.PitchRatePID
.ILimit
, value
);
306 case TXPIDSETTINGS_PIDS_PITCHRATERESP
:
307 needsUpdateBank
|= updateUint16(&bank
.ManualRate
.Pitch
, value
);
309 case TXPIDSETTINGS_PIDS_PITCHATTITUDEKP
:
310 needsUpdateBank
|= update(&bank
.PitchPI
.Kp
, value
);
312 case TXPIDSETTINGS_PIDS_PITCHATTITUDEKI
:
313 needsUpdateBank
|= update(&bank
.PitchPI
.Ki
, value
);
315 case TXPIDSETTINGS_PIDS_PITCHATTITUDEILIMIT
:
316 needsUpdateBank
|= update(&bank
.PitchPI
.ILimit
, value
);
318 case TXPIDSETTINGS_PIDS_PITCHATTITUDERESP
:
319 needsUpdateBank
|= updateUint8(&bank
.PitchMax
, value
);
321 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKP
:
322 needsUpdateBank
|= update(&bank
.RollRatePID
.Kp
, value
);
323 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kp
, value
);
325 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKI
:
326 needsUpdateBank
|= update(&bank
.RollRatePID
.Ki
, value
);
327 needsUpdateBank
|= update(&bank
.PitchRatePID
.Ki
, value
);
329 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKD
:
330 needsUpdateBank
|= update(&bank
.RollRatePID
.Kd
, value
);
331 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kd
, value
);
333 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEILIMIT
:
334 needsUpdateBank
|= update(&bank
.RollRatePID
.ILimit
, value
);
335 needsUpdateBank
|= update(&bank
.PitchRatePID
.ILimit
, value
);
337 case TXPIDSETTINGS_PIDS_ROLLPITCHRATERESP
:
338 needsUpdateBank
|= updateUint16(&bank
.ManualRate
.Roll
, value
);
339 needsUpdateBank
|= updateUint16(&bank
.ManualRate
.Pitch
, value
);
341 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKP
:
342 needsUpdateBank
|= update(&bank
.RollPI
.Kp
, value
);
343 needsUpdateBank
|= update(&bank
.PitchPI
.Kp
, value
);
345 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKI
:
346 needsUpdateBank
|= update(&bank
.RollPI
.Ki
, value
);
347 needsUpdateBank
|= update(&bank
.PitchPI
.Ki
, value
);
349 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEILIMIT
:
350 needsUpdateBank
|= update(&bank
.RollPI
.ILimit
, value
);
351 needsUpdateBank
|= update(&bank
.PitchPI
.ILimit
, value
);
353 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDERESP
:
354 needsUpdateBank
|= updateUint8(&bank
.RollMax
, value
);
355 needsUpdateBank
|= updateUint8(&bank
.PitchMax
, value
);
357 case TXPIDSETTINGS_PIDS_YAWRATEKP
:
358 needsUpdateBank
|= update(&bank
.YawRatePID
.Kp
, value
);
360 case TXPIDSETTINGS_PIDS_YAWRATEKI
:
361 needsUpdateBank
|= update(&bank
.YawRatePID
.Ki
, value
);
363 case TXPIDSETTINGS_PIDS_YAWRATEKD
:
364 needsUpdateBank
|= update(&bank
.YawRatePID
.Kd
, value
);
366 case TXPIDSETTINGS_PIDS_YAWRATEILIMIT
:
367 needsUpdateBank
|= update(&bank
.YawRatePID
.ILimit
, value
);
369 case TXPIDSETTINGS_PIDS_YAWRATERESP
:
370 needsUpdateBank
|= updateUint16(&bank
.ManualRate
.Yaw
, value
);
372 case TXPIDSETTINGS_PIDS_YAWATTITUDEKP
:
373 needsUpdateBank
|= update(&bank
.YawPI
.Kp
, value
);
375 case TXPIDSETTINGS_PIDS_YAWATTITUDEKI
:
376 needsUpdateBank
|= update(&bank
.YawPI
.Ki
, value
);
378 case TXPIDSETTINGS_PIDS_YAWATTITUDEILIMIT
:
379 needsUpdateBank
|= update(&bank
.YawPI
.ILimit
, value
);
381 case TXPIDSETTINGS_PIDS_YAWATTITUDERESP
:
382 needsUpdateBank
|= updateUint8(&bank
.YawMax
, value
);
384 case TXPIDSETTINGS_PIDS_ROLLEXPO
:
385 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Roll
, value
);
387 case TXPIDSETTINGS_PIDS_PITCHEXPO
:
388 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Pitch
, value
);
390 case TXPIDSETTINGS_PIDS_ROLLPITCHEXPO
:
391 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Roll
, value
);
392 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Pitch
, value
);
394 case TXPIDSETTINGS_PIDS_YAWEXPO
:
395 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Yaw
, value
);
397 case TXPIDSETTINGS_PIDS_GYROTAU
:
398 needsUpdateStab
|= update(&stab
.GyroTau
, value
);
400 case TXPIDSETTINGS_PIDS_ACROROLLFACTOR
:
401 needsUpdateBank
|= updateUint8(&bank
.AcroInsanityFactor
.Roll
, value
);
403 case TXPIDSETTINGS_PIDS_ACROPITCHFACTOR
:
404 needsUpdateBank
|= updateUint8(&bank
.AcroInsanityFactor
.Pitch
, value
);
406 case TXPIDSETTINGS_PIDS_ACROROLLPITCHFACTOR
:
407 needsUpdateBank
|= updateUint8(&bank
.AcroInsanityFactor
.Roll
, value
);
408 needsUpdateBank
|= updateUint8(&bank
.AcroInsanityFactor
.Pitch
, value
);
410 case TXPIDSETTINGS_PIDS_ACCELTAU
:
411 needsUpdateAtt
|= update(&att
.AccelTau
, value
);
413 case TXPIDSETTINGS_PIDS_ACCELKP
:
414 needsUpdateAtt
|= update(&att
.AccelKp
, value
);
416 case TXPIDSETTINGS_PIDS_ACCELKI
:
417 needsUpdateAtt
|= update(&att
.AccelKi
, value
);
421 case TXPIDSETTINGS_PIDS_ALTITUDEPOSKP
:
422 needsUpdateAltitude
|= update(&altitude
.VerticalPosP
, value
);
424 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKP
:
425 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Kp
, value
);
427 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKI
:
428 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Ki
, value
);
430 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKD
:
431 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Kd
, value
);
433 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYBETA
:
434 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Beta
, value
);
442 if (needsUpdateStab
) {
443 StabilizationSettingsSet(&stab
);
445 if (needsUpdateAtt
) {
446 AttitudeSettingsSet(&att
);
449 if (needsUpdateAltitude
) {
450 AltitudeHoldSettingsSet(&altitude
);
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
) {
462 StabilizationSettingsBank1Set((StabilizationSettingsBank1Data
*)&bank
);
466 StabilizationSettingsBank2Set((StabilizationSettingsBank2Data
*)&bank
);
470 StabilizationSettingsBank3Set((StabilizationSettingsBank3Data
*)&bank
);
478 if (needsUpdateStab
||
481 needsUpdateAltitude
||
482 #endif /* REVOLUTION */
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
)
505 // normalize input value to [0..1]
506 if (inMax
<= inMin
) {
509 val
= (val
- inMin
) / (inMax
- inMin
);
512 // update output bounds
513 if (outMin
> outMax
) {
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
) {
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
) {
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
) {
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
) {