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 OpenPilot Team, http://www.openpilot.org Copyright (C) 2011.
12 * @brief Optional module to tune PID settings using R/C transmitter.
14 * @see The GNU Public License (GPL) Version 3
16 *****************************************************************************/
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
28 * You should have received a copy of the GNU General Public License along
29 * with this program; if not, write to the Free Software Foundation, Inc.,
30 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 * Output object: StabilizationSettings
36 * This module will periodically update values of stabilization PID settings
37 * depending on configured input control channels. New values of stabilization
38 * settings are not saved to flash, but updated in RAM. It is expected that the
39 * module will be enabled only for tuning. When desired values are found, they
40 * can be read via GCS and saved permanently. Then this module should be
43 * UAVObjects are automatically generated by the UAVObjectGenerator from
44 * the object definition XML file.
46 * Modules have no API, all communication to other modules is done through UAVObjects.
47 * However modules may use the API exposed by shared libraries.
48 * See the OpenPilot wiki for more details.
49 * http://wiki.openpilot.org/display/Doc/OpenPilot+Architecture
53 #include "openpilot.h"
54 #include "txpidsettings.h"
55 #include "accessorydesired.h"
56 #include "manualcontrolcommand.h"
57 #include "stabilizationsettings.h"
58 #include "attitudesettings.h"
60 #include "altitudeholdsettings.h"
62 #include "stabilizationbank.h"
63 #include "stabilizationsettingsbank1.h"
64 #include "stabilizationsettingsbank2.h"
65 #include "stabilizationsettingsbank3.h"
66 #include "flightstatus.h"
67 #include "txpidstatus.h"
68 #include "hwsettings.h"
73 #define SAMPLE_PERIOD_MS 200
74 #define TELEMETRY_UPDATE_PERIOD_MS 0 // 0 = update on change (default)
77 #if (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_INPUTS_NUMELEM) || \
78 (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_MINPID_NUMELEM) || \
79 (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_MAXPID_NUMELEM)
80 #error Invalid TxPID UAVObject definition (inconsistent number of field elements)
88 static void updatePIDs(UAVObjEvent
*ev
);
89 static uint8_t update(float *var
, float val
);
90 static uint8_t updateUint8(uint8_t *var
, float val
);
91 static uint8_t updateInt8(int8_t *var
, float val
);
92 static float scale(float val
, float inMin
, float inMax
, float outMin
, float outMax
);
95 * Initialise the module, called on startup
96 * \returns 0 on success or -1 if initialisation failed
98 int32_t TxPIDInitialize(void)
101 HwSettingsOptionalModulesData optionalModules
;
104 AltitudeHoldSettingsInitialize();
107 HwSettingsInitialize();
108 HwSettingsOptionalModulesGet(&optionalModules
);
110 if (optionalModules
.TxPID
== HWSETTINGS_OPTIONALMODULES_ENABLED
) {
113 txPIDEnabled
= false;
117 TxPIDSettingsInitialize();
118 TxPIDStatusInitialize();
119 AccessoryDesiredInitialize();
122 .obj
= AccessoryDesiredHandle(),
125 .lowPriority
= false,
127 EventPeriodicCallbackCreate(&ev
, updatePIDs
, SAMPLE_PERIOD_MS
/ portTICK_RATE_MS
);
129 #if (TELEMETRY_UPDATE_PERIOD_MS != 0)
130 // Change StabilizationSettings update rate from OnChange to periodic
131 // to prevent telemetry link flooding with frequent updates in case of
132 // control channel jitter.
133 // Warning: saving to flash with this code active will change the
134 // StabilizationSettings update rate permanently. Use Metadata via
135 // browser to reset to defaults (telemetryAcked=true, OnChange).
136 UAVObjMetadata metadata
;
137 StabilizationSettingsInitialize();
138 StabilizationSettingsGetMetadata(&metadata
);
139 metadata
.telemetryAcked
= 0;
140 metadata
.telemetryUpdateMode
= UPDATEMODE_PERIODIC
;
141 metadata
.telemetryUpdatePeriod
= TELEMETRY_UPDATE_PERIOD_MS
;
142 StabilizationSettingsSetMetadata(&metadata
);
144 AttitudeSettingsInitialize();
145 AttitudeSettingsGetMetadata(&metadata
);
146 metadata
.telemetryAcked
= 0;
147 metadata
.telemetryUpdateMode
= UPDATEMODE_PERIODIC
;
148 metadata
.telemetryUpdatePeriod
= TELEMETRY_UPDATE_PERIOD_MS
;
149 AttitudeSettingsSetMetadata(&metadata
);
150 #endif /* if (TELEMETRY_UPDATE_PERIOD_MS != 0) */
158 /* stub: module has no module thread */
159 int32_t TxPIDStart(void)
164 MODULE_INITCALL(TxPIDInitialize
, TxPIDStart
);
167 * Update PIDs callback function
169 static void updatePIDs(UAVObjEvent
*ev
)
171 if (ev
->obj
!= AccessoryDesiredHandle()) {
175 TxPIDSettingsData inst
;
176 TxPIDSettingsGet(&inst
);
178 if (inst
.UpdateMode
== TXPIDSETTINGS_UPDATEMODE_NEVER
) {
183 FlightStatusArmedGet(&armed
);
184 if ((inst
.UpdateMode
== TXPIDSETTINGS_UPDATEMODE_WHENARMED
) &&
185 (armed
== FLIGHTSTATUS_ARMED_DISARMED
)) {
189 StabilizationBankData bank
;
190 switch (inst
.BankNumber
) {
192 StabilizationSettingsBank1Get((StabilizationSettingsBank1Data
*)&bank
);
196 StabilizationSettingsBank2Get((StabilizationSettingsBank2Data
*)&bank
);
200 StabilizationSettingsBank3Get((StabilizationSettingsBank3Data
*)&bank
);
206 StabilizationSettingsData stab
;
207 StabilizationSettingsGet(&stab
);
209 AttitudeSettingsData att
;
210 AttitudeSettingsGet(&att
);
213 AltitudeHoldSettingsData altitude
;
214 AltitudeHoldSettingsGet(&altitude
);
216 AccessoryDesiredData accessory
;
218 TxPIDStatusData txpid_status
;
219 TxPIDStatusGet(&txpid_status
);
221 uint8_t needsUpdateBank
= 0;
222 uint8_t needsUpdateStab
= 0;
223 uint8_t needsUpdateAtt
= 0;
225 uint8_t needsUpdateAltitude
= 0;
228 // Loop through every enabled instance
229 for (uint8_t i
= 0; i
< TXPIDSETTINGS_PIDS_NUMELEM
; i
++) {
230 if (TxPIDSettingsPIDsToArray(inst
.PIDs
)[i
] != TXPIDSETTINGS_PIDS_DISABLED
) {
232 if (TxPIDSettingsInputsToArray(inst
.Inputs
)[i
] == TXPIDSETTINGS_INPUTS_THROTTLE
) {
233 ManualControlCommandThrottleGet(&value
);
235 inst
.ThrottleRange
.Min
,
236 inst
.ThrottleRange
.Max
,
237 TxPIDSettingsMinPIDToArray(inst
.MinPID
)[i
],
238 TxPIDSettingsMaxPIDToArray(inst
.MaxPID
)[i
]);
239 } else if (AccessoryDesiredInstGet(
240 TxPIDSettingsInputsToArray(inst
.Inputs
)[i
] - TXPIDSETTINGS_INPUTS_ACCESSORY0
,
242 value
= scale(accessory
.AccessoryVal
, -1.0f
, 1.0f
,
243 TxPIDSettingsMinPIDToArray(inst
.MinPID
)[i
],
244 TxPIDSettingsMaxPIDToArray(inst
.MaxPID
)[i
]);
249 TxPIDStatusCurPIDToArray(txpid_status
.CurPID
)[i
] = value
;
251 switch (TxPIDSettingsPIDsToArray(inst
.PIDs
)[i
]) {
252 case TXPIDSETTINGS_PIDS_ROLLRATEKP
:
253 needsUpdateBank
|= update(&bank
.RollRatePID
.Kp
, value
);
255 case TXPIDSETTINGS_PIDS_ROLLRATEKI
:
256 needsUpdateBank
|= update(&bank
.RollRatePID
.Ki
, value
);
258 case TXPIDSETTINGS_PIDS_ROLLRATEKD
:
259 needsUpdateBank
|= update(&bank
.RollRatePID
.Kd
, value
);
261 case TXPIDSETTINGS_PIDS_ROLLRATEILIMIT
:
262 needsUpdateBank
|= update(&bank
.RollRatePID
.ILimit
, value
);
264 case TXPIDSETTINGS_PIDS_ROLLRATERESP
:
265 needsUpdateBank
|= update(&bank
.ManualRate
.Roll
, value
);
267 case TXPIDSETTINGS_PIDS_ROLLATTITUDEKP
:
268 needsUpdateBank
|= update(&bank
.RollPI
.Kp
, value
);
270 case TXPIDSETTINGS_PIDS_ROLLATTITUDEKI
:
271 needsUpdateBank
|= update(&bank
.RollPI
.Ki
, value
);
273 case TXPIDSETTINGS_PIDS_ROLLATTITUDEILIMIT
:
274 needsUpdateBank
|= update(&bank
.RollPI
.ILimit
, value
);
276 case TXPIDSETTINGS_PIDS_ROLLATTITUDERESP
:
277 needsUpdateBank
|= updateUint8(&bank
.RollMax
, value
);
279 case TXPIDSETTINGS_PIDS_PITCHRATEKP
:
280 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kp
, value
);
282 case TXPIDSETTINGS_PIDS_PITCHRATEKI
:
283 needsUpdateBank
|= update(&bank
.PitchRatePID
.Ki
, value
);
285 case TXPIDSETTINGS_PIDS_PITCHRATEKD
:
286 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kd
, value
);
288 case TXPIDSETTINGS_PIDS_PITCHRATEILIMIT
:
289 needsUpdateBank
|= update(&bank
.PitchRatePID
.ILimit
, value
);
291 case TXPIDSETTINGS_PIDS_PITCHRATERESP
:
292 needsUpdateBank
|= update(&bank
.ManualRate
.Pitch
, value
);
294 case TXPIDSETTINGS_PIDS_PITCHATTITUDEKP
:
295 needsUpdateBank
|= update(&bank
.PitchPI
.Kp
, value
);
297 case TXPIDSETTINGS_PIDS_PITCHATTITUDEKI
:
298 needsUpdateBank
|= update(&bank
.PitchPI
.Ki
, value
);
300 case TXPIDSETTINGS_PIDS_PITCHATTITUDEILIMIT
:
301 needsUpdateBank
|= update(&bank
.PitchPI
.ILimit
, value
);
303 case TXPIDSETTINGS_PIDS_PITCHATTITUDERESP
:
304 needsUpdateBank
|= updateUint8(&bank
.PitchMax
, value
);
306 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKP
:
307 needsUpdateBank
|= update(&bank
.RollRatePID
.Kp
, value
);
308 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kp
, value
);
310 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKI
:
311 needsUpdateBank
|= update(&bank
.RollRatePID
.Ki
, value
);
312 needsUpdateBank
|= update(&bank
.PitchRatePID
.Ki
, value
);
314 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKD
:
315 needsUpdateBank
|= update(&bank
.RollRatePID
.Kd
, value
);
316 needsUpdateBank
|= update(&bank
.PitchRatePID
.Kd
, value
);
318 case TXPIDSETTINGS_PIDS_ROLLPITCHRATEILIMIT
:
319 needsUpdateBank
|= update(&bank
.RollRatePID
.ILimit
, value
);
320 needsUpdateBank
|= update(&bank
.PitchRatePID
.ILimit
, value
);
322 case TXPIDSETTINGS_PIDS_ROLLPITCHRATERESP
:
323 needsUpdateBank
|= update(&bank
.ManualRate
.Roll
, value
);
324 needsUpdateBank
|= update(&bank
.ManualRate
.Pitch
, value
);
326 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKP
:
327 needsUpdateBank
|= update(&bank
.RollPI
.Kp
, value
);
328 needsUpdateBank
|= update(&bank
.PitchPI
.Kp
, value
);
330 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKI
:
331 needsUpdateBank
|= update(&bank
.RollPI
.Ki
, value
);
332 needsUpdateBank
|= update(&bank
.PitchPI
.Ki
, value
);
334 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEILIMIT
:
335 needsUpdateBank
|= update(&bank
.RollPI
.ILimit
, value
);
336 needsUpdateBank
|= update(&bank
.PitchPI
.ILimit
, value
);
338 case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDERESP
:
339 needsUpdateBank
|= updateUint8(&bank
.RollMax
, value
);
340 needsUpdateBank
|= updateUint8(&bank
.PitchMax
, value
);
342 case TXPIDSETTINGS_PIDS_YAWRATEKP
:
343 needsUpdateBank
|= update(&bank
.YawRatePID
.Kp
, value
);
345 case TXPIDSETTINGS_PIDS_YAWRATEKI
:
346 needsUpdateBank
|= update(&bank
.YawRatePID
.Ki
, value
);
348 case TXPIDSETTINGS_PIDS_YAWRATEKD
:
349 needsUpdateBank
|= update(&bank
.YawRatePID
.Kd
, value
);
351 case TXPIDSETTINGS_PIDS_YAWRATEILIMIT
:
352 needsUpdateBank
|= update(&bank
.YawRatePID
.ILimit
, value
);
354 case TXPIDSETTINGS_PIDS_YAWRATERESP
:
355 needsUpdateBank
|= update(&bank
.ManualRate
.Yaw
, value
);
357 case TXPIDSETTINGS_PIDS_YAWATTITUDEKP
:
358 needsUpdateBank
|= update(&bank
.YawPI
.Kp
, value
);
360 case TXPIDSETTINGS_PIDS_YAWATTITUDEKI
:
361 needsUpdateBank
|= update(&bank
.YawPI
.Ki
, value
);
363 case TXPIDSETTINGS_PIDS_YAWATTITUDEILIMIT
:
364 needsUpdateBank
|= update(&bank
.YawPI
.ILimit
, value
);
366 case TXPIDSETTINGS_PIDS_YAWATTITUDERESP
:
367 needsUpdateBank
|= updateUint8(&bank
.YawMax
, value
);
369 case TXPIDSETTINGS_PIDS_ROLLEXPO
:
370 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Roll
, value
);
372 case TXPIDSETTINGS_PIDS_PITCHEXPO
:
373 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Pitch
, value
);
375 case TXPIDSETTINGS_PIDS_ROLLPITCHEXPO
:
376 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Roll
, value
);
377 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Pitch
, value
);
379 case TXPIDSETTINGS_PIDS_YAWEXPO
:
380 needsUpdateBank
|= updateInt8(&bank
.StickExpo
.Yaw
, value
);
382 case TXPIDSETTINGS_PIDS_GYROTAU
:
383 needsUpdateStab
|= update(&stab
.GyroTau
, value
);
385 case TXPIDSETTINGS_PIDS_ACROPLUSFACTOR
:
386 needsUpdateBank
|= update(&bank
.AcroInsanityFactor
, value
);
388 case TXPIDSETTINGS_PIDS_ACCELTAU
:
389 needsUpdateAtt
|= update(&att
.AccelTau
, value
);
391 case TXPIDSETTINGS_PIDS_ACCELKP
:
392 needsUpdateAtt
|= update(&att
.AccelKp
, value
);
394 case TXPIDSETTINGS_PIDS_ACCELKI
:
395 needsUpdateAtt
|= update(&att
.AccelKi
, value
);
399 case TXPIDSETTINGS_PIDS_ALTITUDEPOSKP
:
400 needsUpdateAltitude
|= update(&altitude
.VerticalPosP
, value
);
402 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKP
:
403 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Kp
, value
);
405 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKI
:
406 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Ki
, value
);
408 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYKD
:
409 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Kd
, value
);
411 case TXPIDSETTINGS_PIDS_ALTITUDEVELOCITYBETA
:
412 needsUpdateAltitude
|= update(&altitude
.VerticalVelPID
.Beta
, value
);
420 if (needsUpdateStab
) {
421 StabilizationSettingsSet(&stab
);
423 if (needsUpdateAtt
) {
424 AttitudeSettingsSet(&att
);
427 if (needsUpdateAltitude
) {
428 AltitudeHoldSettingsSet(&altitude
);
431 if (needsUpdateBank
) {
432 switch (inst
.BankNumber
) {
434 StabilizationSettingsBank1Set((StabilizationSettingsBank1Data
*)&bank
);
438 StabilizationSettingsBank2Set((StabilizationSettingsBank2Data
*)&bank
);
442 StabilizationSettingsBank3Set((StabilizationSettingsBank3Data
*)&bank
);
450 if (needsUpdateStab
||
453 needsUpdateAltitude
||
454 #endif /* REVOLUTION */
456 TxPIDStatusSet(&txpid_status
);;
461 * Scales input val from [inMin..inMax] range to [outMin..outMax].
462 * If val is out of input range (inMin <= inMax), it will be bound.
463 * (outMin > outMax) is ok, in that case output will be decreasing.
465 * \returns scaled value
467 static float scale(float val
, float inMin
, float inMax
, float outMin
, float outMax
)
477 // normalize input value to [0..1]
478 if (inMax
<= inMin
) {
481 val
= (val
- inMin
) / (inMax
- inMin
);
484 // update output bounds
485 if (outMin
> outMax
) {
492 return (outMax
- outMin
) * val
+ outMin
;
496 * Updates var using val if needed.
497 * \returns 1 if updated, 0 otherwise
499 static uint8_t update(float *var
, float val
)
501 /* FIXME: this is not an entirely correct way
502 * to check if the two floating point
503 * numbers are 'not equal'.
504 * Epsilon of 1e-9 is probably okay for the range
505 * of numbers we see here*/
506 if (fabsf(*var
- val
) > 1e-9f
) {
514 * Updates var using val if needed.
515 * \returns 1 if updated, 0 otherwise
517 static uint8_t updateUint8(uint8_t *var
, float val
)
519 uint8_t roundedVal
= (uint8_t)roundf(val
);
521 if (*var
!= roundedVal
) {
529 * Updates var using val if needed.
530 * \returns 1 if updated, 0 otherwise
532 static uint8_t updateInt8(int8_t *var
, float val
)
534 int8_t roundedVal
= (int8_t)roundf(val
);
536 if (*var
!= roundedVal
) {