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)
103 HwSettingsOptionalModulesData optionalModules
;
106 AltitudeHoldSettingsInitialize();
109 HwSettingsInitialize();
110 HwSettingsOptionalModulesGet(&optionalModules
);
112 if (optionalModules
.TxPID
== HWSETTINGS_OPTIONALMODULES_ENABLED
) {
115 txPIDEnabled
= false;
119 TxPIDSettingsInitialize();
120 TxPIDStatusInitialize();
121 AccessoryDesiredInitialize();
124 .obj
= AccessoryDesiredHandle(),
127 .lowPriority
= false,
129 EventPeriodicCallbackCreate(&ev
, updatePIDs
, SAMPLE_PERIOD_MS
/ portTICK_RATE_MS
);
131 #if (TELEMETRY_UPDATE_PERIOD_MS != 0)
132 // Change StabilizationSettings update rate from OnChange to periodic
133 // to prevent telemetry link flooding with frequent updates in case of
134 // control channel jitter.
135 // Warning: saving to flash with this code active will change the
136 // StabilizationSettings update rate permanently. Use Metadata via
137 // browser to reset to defaults (telemetryAcked=true, OnChange).
138 UAVObjMetadata metadata
;
139 StabilizationSettingsInitialize();
140 StabilizationSettingsGetMetadata(&metadata
);
141 metadata
.telemetryAcked
= 0;
142 metadata
.telemetryUpdateMode
= UPDATEMODE_PERIODIC
;
143 metadata
.telemetryUpdatePeriod
= TELEMETRY_UPDATE_PERIOD_MS
;
144 StabilizationSettingsSetMetadata(&metadata
);
146 AttitudeSettingsInitialize();
147 AttitudeSettingsGetMetadata(&metadata
);
148 metadata
.telemetryAcked
= 0;
149 metadata
.telemetryUpdateMode
= UPDATEMODE_PERIODIC
;
150 metadata
.telemetryUpdatePeriod
= TELEMETRY_UPDATE_PERIOD_MS
;
151 AttitudeSettingsSetMetadata(&metadata
);
152 #endif /* if (TELEMETRY_UPDATE_PERIOD_MS != 0) */
160 /* stub: module has no module thread */
161 int32_t TxPIDStart(void)
166 MODULE_INITCALL(TxPIDInitialize
, TxPIDStart
);
169 * Update PIDs callback function
171 static void updatePIDs(UAVObjEvent
*ev
)
173 if (ev
->obj
!= AccessoryDesiredHandle()) {
177 TxPIDSettingsData inst
;
178 TxPIDSettingsGet(&inst
);
180 if (inst
.UpdateMode
== TXPIDSETTINGS_UPDATEMODE_NEVER
) {
185 FlightStatusArmedGet(&armed
);
186 if ((inst
.UpdateMode
== TXPIDSETTINGS_UPDATEMODE_WHENARMED
) &&
187 (armed
== FLIGHTSTATUS_ARMED_DISARMED
)) {
191 StabilizationBankData bank
;
192 switch (inst
.BankNumber
) {
194 StabilizationSettingsBank1Get((StabilizationSettingsBank1Data
*)&bank
);
198 StabilizationSettingsBank2Get((StabilizationSettingsBank2Data
*)&bank
);
202 StabilizationSettingsBank3Get((StabilizationSettingsBank3Data
*)&bank
);
208 StabilizationSettingsData stab
;
209 StabilizationSettingsGet(&stab
);
211 AttitudeSettingsData att
;
212 AttitudeSettingsGet(&att
);
215 AltitudeHoldSettingsData altitude
;
216 AltitudeHoldSettingsGet(&altitude
);
218 AccessoryDesiredData accessory
;
220 TxPIDStatusData txpid_status
;
221 TxPIDStatusGet(&txpid_status
);
223 bool easyTuneEnabled
= false;
225 uint8_t needsUpdateBank
= 0;
226 uint8_t needsUpdateStab
= 0;
227 uint8_t needsUpdateAtt
= 0;
229 uint8_t needsUpdateAltitude
= 0;
232 // Loop through every enabled instance
233 for (uint8_t i
= 0; i
< TXPIDSETTINGS_PIDS_NUMELEM
; i
++) {
234 if (TxPIDSettingsPIDsToArray(inst
.PIDs
)[i
] != TXPIDSETTINGS_PIDS_DISABLED
) {
236 if (TxPIDSettingsInputsToArray(inst
.Inputs
)[i
] == TXPIDSETTINGS_INPUTS_THROTTLE
) {
237 ManualControlCommandThrottleGet(&value
);
239 inst
.ThrottleRange
.Min
,
240 inst
.ThrottleRange
.Max
,
241 TxPIDSettingsMinPIDToArray(inst
.MinPID
)[i
],
242 TxPIDSettingsMaxPIDToArray(inst
.MaxPID
)[i
]);
243 } else if (AccessoryDesiredInstGet(
244 TxPIDSettingsInputsToArray(inst
.Inputs
)[i
] - TXPIDSETTINGS_INPUTS_ACCESSORY0
,
246 value
= scale(accessory
.AccessoryVal
, -1.0f
, 1.0f
,
247 TxPIDSettingsMinPIDToArray(inst
.MinPID
)[i
],
248 TxPIDSettingsMaxPIDToArray(inst
.MaxPID
)[i
]);
253 TxPIDStatusCurPIDToArray(txpid_status
.CurPID
)[i
] = value
;
255 switch (TxPIDSettingsPIDsToArray(inst
.PIDs
)[i
]) {
256 case TXPIDSETTINGS_PIDS_ROLLRATEKP
:
257 needsUpdateBank
|= update(&bank
.RollRatePID
.Kp
, value
);
259 case TXPIDSETTINGS_PIDS_EASYTUNERATEROLL
:
260 easyTuneEnabled
= true;
261 needsUpdateBank
|= update(&bank
.RollRatePID
.Kp
, value
);
262 needsUpdateBank
|= update(&bank
.RollRatePID
.Ki
, value
* inst
.EasyTunePitchRollRateFactors
.I
);
263 needsUpdateBank
|= update(&bank
.RollRatePID
.Kd
, value
* inst
.EasyTunePitchRollRateFactors
.D
);
265 case TXPIDSETTINGS_PIDS_EASYTUNERATEPITCH
:
266 easyTuneEnabled
= true;
267 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kp
, value
);
268 needsUpdateBank
|= update(&bank
.PitchRatePID
.Ki
, value
* inst
.EasyTunePitchRollRateFactors
.I
);
269 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kd
, value
* inst
.EasyTunePitchRollRateFactors
.D
);
271 case TXPIDSETTINGS_PIDS_ROLLRATEKI
:
272 needsUpdateBank
|= update(&bank
.RollRatePID
.Ki
, value
);
274 case TXPIDSETTINGS_PIDS_ROLLRATEKD
:
275 needsUpdateBank
|= update(&bank
.RollRatePID
.Kd
, value
);
277 case TXPIDSETTINGS_PIDS_ROLLRATEILIMIT
:
278 needsUpdateBank
|= update(&bank
.RollRatePID
.ILimit
, value
);
280 case TXPIDSETTINGS_PIDS_ROLLRATERESP
:
281 needsUpdateBank
|= updateUint16(&bank
.ManualRate
.Roll
, value
);
283 case TXPIDSETTINGS_PIDS_ROLLATTITUDEKP
:
284 needsUpdateBank
|= update(&bank
.RollPI
.Kp
, value
);
286 case TXPIDSETTINGS_PIDS_ROLLATTITUDEKI
:
287 needsUpdateBank
|= update(&bank
.RollPI
.Ki
, value
);
289 case TXPIDSETTINGS_PIDS_ROLLATTITUDEILIMIT
:
290 needsUpdateBank
|= update(&bank
.RollPI
.ILimit
, value
);
292 case TXPIDSETTINGS_PIDS_ROLLATTITUDERESP
:
293 needsUpdateBank
|= updateUint8(&bank
.RollMax
, value
);
295 case TXPIDSETTINGS_PIDS_PITCHRATEKP
:
296 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kp
, value
);
298 case TXPIDSETTINGS_PIDS_PITCHRATEKI
:
299 needsUpdateBank
|= update(&bank
.PitchRatePID
.Ki
, value
);
301 case TXPIDSETTINGS_PIDS_PITCHRATEKD
:
302 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kd
, value
);
304 case TXPIDSETTINGS_PIDS_PITCHRATEILIMIT
:
305 needsUpdateBank
|= update(&bank
.PitchRatePID
.ILimit
, value
);
307 case TXPIDSETTINGS_PIDS_PITCHRATERESP
:
308 needsUpdateBank
|= updateUint16(&bank
.ManualRate
.Pitch
, value
);
310 case TXPIDSETTINGS_PIDS_PITCHATTITUDEKP
:
311 needsUpdateBank
|= update(&bank
.PitchPI
.Kp
, value
);
313 case TXPIDSETTINGS_PIDS_PITCHATTITUDEKI
:
314 needsUpdateBank
|= update(&bank
.PitchPI
.Ki
, value
);
316 case TXPIDSETTINGS_PIDS_PITCHATTITUDEILIMIT
:
317 needsUpdateBank
|= update(&bank
.PitchPI
.ILimit
, value
);
319 case TXPIDSETTINGS_PIDS_PITCHATTITUDERESP
:
320 needsUpdateBank
|= updateUint8(&bank
.PitchMax
, value
);
322 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKP
:
323 needsUpdateBank
|= update(&bank
.RollRatePID
.Kp
, value
);
324 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kp
, value
);
326 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKI
:
327 needsUpdateBank
|= update(&bank
.RollRatePID
.Ki
, value
);
328 needsUpdateBank
|= update(&bank
.PitchRatePID
.Ki
, value
);
330 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKD
:
331 needsUpdateBank
|= update(&bank
.RollRatePID
.Kd
, value
);
332 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kd
, value
);
334 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEILIMIT
:
335 needsUpdateBank
|= update(&bank
.RollRatePID
.ILimit
, value
);
336 needsUpdateBank
|= update(&bank
.PitchRatePID
.ILimit
, value
);
338 case TXPIDSETTINGS_PIDS_ROLLPITCHRATERESP
:
339 needsUpdateBank
|= updateUint16(&bank
.ManualRate
.Roll
, value
);
340 needsUpdateBank
|= updateUint16(&bank
.ManualRate
.Pitch
, value
);
342 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKP
:
343 needsUpdateBank
|= update(&bank
.RollPI
.Kp
, value
);
344 needsUpdateBank
|= update(&bank
.PitchPI
.Kp
, value
);
346 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKI
:
347 needsUpdateBank
|= update(&bank
.RollPI
.Ki
, value
);
348 needsUpdateBank
|= update(&bank
.PitchPI
.Ki
, value
);
350 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEILIMIT
:
351 needsUpdateBank
|= update(&bank
.RollPI
.ILimit
, value
);
352 needsUpdateBank
|= update(&bank
.PitchPI
.ILimit
, value
);
354 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDERESP
:
355 needsUpdateBank
|= updateUint8(&bank
.RollMax
, value
);
356 needsUpdateBank
|= updateUint8(&bank
.PitchMax
, value
);
358 case TXPIDSETTINGS_PIDS_YAWRATEKP
:
359 needsUpdateBank
|= update(&bank
.YawRatePID
.Kp
, value
);
361 case TXPIDSETTINGS_PIDS_YAWRATEKI
:
362 needsUpdateBank
|= update(&bank
.YawRatePID
.Ki
, value
);
364 case TXPIDSETTINGS_PIDS_YAWRATEKD
:
365 needsUpdateBank
|= update(&bank
.YawRatePID
.Kd
, value
);
367 case TXPIDSETTINGS_PIDS_YAWRATEILIMIT
:
368 needsUpdateBank
|= update(&bank
.YawRatePID
.ILimit
, value
);
370 case TXPIDSETTINGS_PIDS_YAWRATERESP
:
371 needsUpdateBank
|= updateUint16(&bank
.ManualRate
.Yaw
, value
);
373 case TXPIDSETTINGS_PIDS_YAWATTITUDEKP
:
374 needsUpdateBank
|= update(&bank
.YawPI
.Kp
, value
);
376 case TXPIDSETTINGS_PIDS_YAWATTITUDEKI
:
377 needsUpdateBank
|= update(&bank
.YawPI
.Ki
, value
);
379 case TXPIDSETTINGS_PIDS_YAWATTITUDEILIMIT
:
380 needsUpdateBank
|= update(&bank
.YawPI
.ILimit
, value
);
382 case TXPIDSETTINGS_PIDS_YAWATTITUDERESP
:
383 needsUpdateBank
|= updateUint8(&bank
.YawMax
, value
);
385 case TXPIDSETTINGS_PIDS_ROLLEXPO
:
386 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Roll
, value
);
388 case TXPIDSETTINGS_PIDS_PITCHEXPO
:
389 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Pitch
, value
);
391 case TXPIDSETTINGS_PIDS_ROLLPITCHEXPO
:
392 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Roll
, value
);
393 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Pitch
, value
);
395 case TXPIDSETTINGS_PIDS_YAWEXPO
:
396 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Yaw
, value
);
398 case TXPIDSETTINGS_PIDS_GYROTAU
:
399 needsUpdateStab
|= update(&stab
.GyroTau
, value
);
401 case TXPIDSETTINGS_PIDS_ACROROLLFACTOR
:
402 needsUpdateBank
|= updateUint8(&bank
.AcroInsanityFactor
.Roll
, value
);
404 case TXPIDSETTINGS_PIDS_ACROPITCHFACTOR
:
405 needsUpdateBank
|= updateUint8(&bank
.AcroInsanityFactor
.Pitch
, value
);
407 case TXPIDSETTINGS_PIDS_ACROROLLPITCHFACTOR
:
408 needsUpdateBank
|= updateUint8(&bank
.AcroInsanityFactor
.Roll
, value
);
409 needsUpdateBank
|= updateUint8(&bank
.AcroInsanityFactor
.Pitch
, value
);
411 case TXPIDSETTINGS_PIDS_ACCELTAU
:
412 needsUpdateAtt
|= update(&att
.AccelTau
, value
);
414 case TXPIDSETTINGS_PIDS_ACCELKP
:
415 needsUpdateAtt
|= update(&att
.AccelKp
, value
);
417 case TXPIDSETTINGS_PIDS_ACCELKI
:
418 needsUpdateAtt
|= update(&att
.AccelKi
, value
);
422 case TXPIDSETTINGS_PIDS_ALTITUDEPOSKP
:
423 needsUpdateAltitude
|= update(&altitude
.VerticalPosP
, value
);
425 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKP
:
426 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Kp
, value
);
428 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKI
:
429 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Ki
, value
);
431 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKD
:
432 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Kd
, value
);
434 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYBETA
:
435 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Beta
, value
);
443 if (needsUpdateStab
) {
444 StabilizationSettingsSet(&stab
);
446 if (needsUpdateAtt
) {
447 AttitudeSettingsSet(&att
);
450 if (needsUpdateAltitude
) {
451 AltitudeHoldSettingsSet(&altitude
);
454 if (easyTuneEnabled
&& (inst
.EasyTuneRatePIDRecalculateYaw
!= TXPIDSETTINGS_EASYTUNERATEPIDRECALCULATEYAW_FALSE
)) {
455 float newKp
= (bank
.RollRatePID
.Kp
+ bank
.PitchRatePID
.Kp
) * .5f
* inst
.EasyTuneYawRateFactors
.P
;
456 needsUpdateBank
|= update(&bank
.YawRatePID
.Kp
, newKp
);
457 needsUpdateBank
|= update(&bank
.YawRatePID
.Ki
, newKp
* inst
.EasyTuneYawRateFactors
.I
);
458 needsUpdateBank
|= update(&bank
.YawRatePID
.Kd
, newKp
* inst
.EasyTuneYawRateFactors
.D
);
460 if (needsUpdateBank
) {
461 switch (inst
.BankNumber
) {
463 StabilizationSettingsBank1Set((StabilizationSettingsBank1Data
*)&bank
);
467 StabilizationSettingsBank2Set((StabilizationSettingsBank2Data
*)&bank
);
471 StabilizationSettingsBank3Set((StabilizationSettingsBank3Data
*)&bank
);
479 if (needsUpdateStab
||
482 needsUpdateAltitude
||
483 #endif /* REVOLUTION */
485 TxPIDStatusSet(&txpid_status
);;
490 * Scales input val from [inMin..inMax] range to [outMin..outMax].
491 * If val is out of input range (inMin <= inMax), it will be bound.
492 * (outMin > outMax) is ok, in that case output will be decreasing.
494 * \returns scaled value
496 static float scale(float val
, float inMin
, float inMax
, float outMin
, float outMax
)
506 // normalize input value to [0..1]
507 if (inMax
<= inMin
) {
510 val
= (val
- inMin
) / (inMax
- inMin
);
513 // update output bounds
514 if (outMin
> outMax
) {
521 return (outMax
- outMin
) * val
+ outMin
;
525 * Updates var using val if needed.
526 * \returns 1 if updated, 0 otherwise
528 static uint8_t update(float *var
, float val
)
530 /* FIXME: this is not an entirely correct way
531 * to check if the two floating point
532 * numbers are 'not equal'.
533 * Epsilon of 1e-9 is probably okay for the range
534 * of numbers we see here*/
535 if (fabsf(*var
- val
) > 1e-9f
) {
543 * Updates var using val if needed.
544 * \returns 1 if updated, 0 otherwise
546 static uint8_t updateUint16(uint16_t *var
, float val
)
548 uint16_t roundedVal
= (uint16_t)roundf(val
);
550 if (*var
!= roundedVal
) {
558 * Updates var using val if needed.
559 * \returns 1 if updated, 0 otherwise
561 static uint8_t updateUint8(uint8_t *var
, float val
)
563 uint8_t roundedVal
= (uint8_t)roundf(val
);
565 if (*var
!= roundedVal
) {
573 * Updates var using val if needed.
574 * \returns 1 if updated, 0 otherwise
576 static uint8_t updateInt8(int8_t *var
, float val
)
578 int8_t roundedVal
= (int8_t)roundf(val
);
580 if (*var
!= roundedVal
) {