2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
25 #include "blackbox/blackbox.h"
26 #include "blackbox/blackbox_fielddefs.h"
28 #include "build/debug.h"
30 #include "common/axis.h"
31 #include "common/maths.h"
32 #include "common/utils.h"
34 #include "drivers/time.h"
36 #include "config/feature.h"
37 #include "config/parameter_group.h"
38 #include "config/parameter_group_ids.h"
40 #include "fc/config.h"
41 #include "fc/controlrate_profile.h"
42 #include "fc/rc_controls.h"
43 #include "fc/rc_adjustments.h"
44 #include "fc/runtime_config.h"
45 #include "fc/settings.h"
47 #include "flight/pid.h"
49 #define AUTOTUNE_FIXED_WING_MIN_FF 10
50 #define AUTOTUNE_FIXED_WING_MAX_FF 255
51 #define AUTOTUNE_FIXED_WING_MIN_ROLL_PITCH_RATE 40
52 #define AUTOTUNE_FIXED_WING_MIN_YAW_RATE 10
53 #define AUTOTUNE_FIXED_WING_MAX_RATE 720
54 #define AUTOTUNE_FIXED_WING_CONVERGENCE_RATE 10
55 #define AUTOTUNE_FIXED_WING_SAMPLE_INTERVAL 20 // ms
56 #define AUTOTUNE_FIXED_WING_SAMPLES 1000 // Use average over the last 20 seconds of hard maneuvers
57 #define AUTOTUNE_FIXED_WING_MIN_SAMPLES 250 // Start updating tune after 5 seconds of hard maneuvers
59 PG_REGISTER_WITH_RESET_TEMPLATE(pidAutotuneConfig_t
, pidAutotuneConfig
, PG_PID_AUTOTUNE_CONFIG
, 2);
61 PG_RESET_TEMPLATE(pidAutotuneConfig_t
, pidAutotuneConfig
,
62 .fw_min_stick
= SETTING_FW_AUTOTUNE_MIN_STICK_DEFAULT
,
63 .fw_rate_adjustment
= SETTING_FW_AUTOTUNE_RATE_ADJUSTMENT_DEFAULT
,
64 .fw_max_rate_deflection
= SETTING_FW_AUTOTUNE_MAX_RATE_DEFLECTION_DEFAULT
,
78 float absDesiredRateAccum
;
79 float absReachedRateAccum
;
80 float absPidOutputAccum
;
84 #define AUTOTUNE_SAVE_PERIOD 5000 // Save interval is 5 seconds - when we turn off autotune we'll restore values from previous update at most 5 sec ago
86 #if defined(USE_AUTOTUNE_FIXED_WING) || defined(USE_AUTOTUNE_MULTIROTOR)
88 static pidAutotuneData_t tuneCurrent
[XYZ_AXIS_COUNT
];
89 static pidAutotuneData_t tuneSaved
[XYZ_AXIS_COUNT
];
90 static timeMs_t lastGainsUpdateTime
;
92 void autotuneUpdateGains(pidAutotuneData_t
* data
)
94 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
95 pidBankMutable()->pid
[axis
].FF
= lrintf(data
[axis
].gainFF
);
96 ((controlRateConfig_t
*)currentControlRateProfile
)->stabilized
.rates
[axis
] = lrintf(data
[axis
].rate
/10.0f
);
98 schedulePidGainsUpdate();
101 void autotuneCheckUpdateGains(void)
103 const timeMs_t currentTimeMs
= millis();
105 if ((currentTimeMs
- lastGainsUpdateTime
) < AUTOTUNE_SAVE_PERIOD
) {
109 // If pilot will exit autotune we'll restore values we are flying now
110 memcpy(tuneSaved
, tuneCurrent
, sizeof(pidAutotuneData_t
) * XYZ_AXIS_COUNT
);
111 autotuneUpdateGains(tuneSaved
);
112 lastGainsUpdateTime
= currentTimeMs
;
115 void autotuneStart(void)
117 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
118 tuneCurrent
[axis
].gainFF
= pidBank()->pid
[axis
].FF
;
119 tuneCurrent
[axis
].rate
= currentControlRateProfile
->stabilized
.rates
[axis
] * 10.0f
;
120 tuneCurrent
[axis
].initialRate
= currentControlRateProfile
->stabilized
.rates
[axis
] * 10.0f
;
121 tuneCurrent
[axis
].absDesiredRateAccum
= 0;
122 tuneCurrent
[axis
].absReachedRateAccum
= 0;
123 tuneCurrent
[axis
].absPidOutputAccum
= 0;
124 tuneCurrent
[axis
].updateCount
= 0;
127 memcpy(tuneSaved
, tuneCurrent
, sizeof(pidAutotuneData_t
) * XYZ_AXIS_COUNT
);
128 lastGainsUpdateTime
= millis();
131 void autotuneUpdateState(void)
133 if (IS_RC_MODE_ACTIVE(BOXAUTOTUNE
) && ARMING_FLAG(ARMED
)) {
134 if (!FLIGHT_MODE(AUTO_TUNE
)) {
136 ENABLE_FLIGHT_MODE(AUTO_TUNE
);
139 autotuneCheckUpdateGains();
142 if (FLIGHT_MODE(AUTO_TUNE
)) {
143 autotuneUpdateGains(tuneSaved
);
146 DISABLE_FLIGHT_MODE(AUTO_TUNE
);
150 static void blackboxLogAutotuneEvent(adjustmentFunction_e adjustmentFunction
, int32_t newValue
)
153 UNUSED(adjustmentFunction
);
156 if (feature(FEATURE_BLACKBOX
)) {
157 flightLogEvent_inflightAdjustment_t eventData
;
158 eventData
.adjustmentFunction
= adjustmentFunction
;
159 eventData
.newValue
= newValue
;
160 eventData
.floatFlag
= false;
161 blackboxLogEvent(FLIGHT_LOG_EVENT_INFLIGHT_ADJUSTMENT
, (flightLogEventData_t
*)&eventData
);
166 #if defined(USE_AUTOTUNE_FIXED_WING)
168 void autotuneFixedWingUpdate(const flight_dynamics_index_t axis
, float desiredRate
, float reachedRate
, float pidOutput
)
170 float maxRateSetting
= tuneCurrent
[axis
].rate
;
171 float gainFF
= tuneCurrent
[axis
].gainFF
;
172 float maxDesiredRate
= maxRateSetting
;
174 const float pidSumLimit
= (axis
== FD_YAW
) ? pidProfile()->pidSumLimitYaw
: pidProfile()->pidSumLimit
;
175 const float absDesiredRate
= fabsf(desiredRate
);
176 const float absReachedRate
= fabsf(reachedRate
);
177 const float absPidOutput
= fabsf(pidOutput
);
178 const bool correctDirection
= (desiredRate
>0) == (reachedRate
>0);
181 bool gainsUpdated
= false;
182 bool ratesUpdated
= false;
184 const timeMs_t currentTimeMs
= millis();
185 static timeMs_t previousSampleTimeMs
= 0;
186 const timeDelta_t timeSincePreviousSample
= currentTimeMs
- previousSampleTimeMs
;
188 // Use different max rate in ANLGE mode
189 if (FLIGHT_MODE(ANGLE_MODE
)) {
190 float maxDesiredRateInAngleMode
= DECIDEGREES_TO_DEGREES(pidProfile()->max_angle_inclination
[axis
] * 1.0f
) * pidBank()->pid
[PID_LEVEL
].P
/ FP_PID_LEVEL_P_MULTIPLIER
;
191 maxDesiredRate
= MIN(maxRateSetting
, maxDesiredRateInAngleMode
);
194 const float stickInput
= absDesiredRate
/ maxDesiredRate
;
196 if ((stickInput
> (pidAutotuneConfig()->fw_min_stick
/ 100.0f
)) && correctDirection
&& (timeSincePreviousSample
>= AUTOTUNE_FIXED_WING_SAMPLE_INTERVAL
)) {
197 // Record values every 20ms and calculate moving average over samples
198 tuneCurrent
[axis
].updateCount
++;
199 tuneCurrent
[axis
].absDesiredRateAccum
+= (absDesiredRate
- tuneCurrent
[axis
].absDesiredRateAccum
) / MIN(tuneCurrent
[axis
].updateCount
, (uint32_t)AUTOTUNE_FIXED_WING_SAMPLES
);
200 tuneCurrent
[axis
].absReachedRateAccum
+= (absReachedRate
- tuneCurrent
[axis
].absReachedRateAccum
) / MIN(tuneCurrent
[axis
].updateCount
, (uint32_t)AUTOTUNE_FIXED_WING_SAMPLES
);
201 tuneCurrent
[axis
].absPidOutputAccum
+= (absPidOutput
- tuneCurrent
[axis
].absPidOutputAccum
) / MIN(tuneCurrent
[axis
].updateCount
, (uint32_t)AUTOTUNE_FIXED_WING_SAMPLES
);
203 if ((tuneCurrent
[axis
].updateCount
& 25) == 0 && tuneCurrent
[axis
].updateCount
>= AUTOTUNE_FIXED_WING_MIN_SAMPLES
) {
204 if (pidAutotuneConfig()->fw_rate_adjustment
!= FIXED
&& !FLIGHT_MODE(ANGLE_MODE
)) { // Rate discovery is not possible in ANGLE mode
206 // Target 80% control surface deflection to leave some room for P and I to work
207 float pidSumTarget
= (pidAutotuneConfig()->fw_max_rate_deflection
/ 100.0f
) * pidSumLimit
;
209 // Theoretically achievable rate with target deflection
210 rateFullStick
= pidSumTarget
/ tuneCurrent
[axis
].absPidOutputAccum
* tuneCurrent
[axis
].absReachedRateAccum
;
213 if (rateFullStick
> (maxRateSetting
+ 10.0f
)) {
214 maxRateSetting
+= 10.0f
;
215 } else if (rateFullStick
< (maxRateSetting
- 10.0f
)) {
216 maxRateSetting
-= 10.0f
;
219 // Constrain to safe values
220 uint16_t minRate
= (axis
== FD_YAW
) ? AUTOTUNE_FIXED_WING_MIN_YAW_RATE
: AUTOTUNE_FIXED_WING_MIN_ROLL_PITCH_RATE
;
221 uint16_t maxRate
= (pidAutotuneConfig()->fw_rate_adjustment
== AUTO
) ? AUTOTUNE_FIXED_WING_MAX_RATE
: MAX(tuneCurrent
[axis
].initialRate
, minRate
);
222 tuneCurrent
[axis
].rate
= constrainf(maxRateSetting
, minRate
, maxRate
);
226 // Update FF towards value needed to achieve current rate target
227 gainFF
+= (tuneCurrent
[axis
].absPidOutputAccum
/ tuneCurrent
[axis
].absReachedRateAccum
* FP_PID_RATE_FF_MULTIPLIER
- gainFF
) * (AUTOTUNE_FIXED_WING_CONVERGENCE_RATE
/ 100.0f
);
228 tuneCurrent
[axis
].gainFF
= constrainf(gainFF
, AUTOTUNE_FIXED_WING_MIN_FF
, AUTOTUNE_FIXED_WING_MAX_FF
);
233 previousSampleTimeMs
= currentTimeMs
;
237 autotuneUpdateGains(tuneCurrent
);
241 blackboxLogAutotuneEvent(ADJUSTMENT_ROLL_FF
, tuneCurrent
[axis
].gainFF
);
245 blackboxLogAutotuneEvent(ADJUSTMENT_PITCH_FF
, tuneCurrent
[axis
].gainFF
);
249 blackboxLogAutotuneEvent(ADJUSTMENT_YAW_FF
, tuneCurrent
[axis
].gainFF
);
252 gainsUpdated
= false;
256 autotuneUpdateGains(tuneCurrent
);
260 blackboxLogAutotuneEvent(ADJUSTMENT_ROLL_RATE
, tuneCurrent
[axis
].rate
);
264 blackboxLogAutotuneEvent(ADJUSTMENT_PITCH_RATE
, tuneCurrent
[axis
].rate
);
268 blackboxLogAutotuneEvent(ADJUSTMENT_YAW_RATE
, tuneCurrent
[axis
].rate
);
273 DEBUG_SET(DEBUG_AUTOTUNE
, axis
, rateFullStick
);
274 ratesUpdated
= false;