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_modes.h"
44 #include "fc/rc_adjustments.h"
45 #include "fc/runtime_config.h"
46 #include "fc/settings.h"
48 #include "flight/pid.h"
50 #define AUTOTUNE_FIXED_WING_MIN_FF 10
51 #define AUTOTUNE_FIXED_WING_MAX_FF 255
52 #define AUTOTUNE_FIXED_WING_MIN_ROLL_PITCH_RATE 40
53 #define AUTOTUNE_FIXED_WING_MIN_YAW_RATE 10
54 #define AUTOTUNE_FIXED_WING_MAX_RATE 720
55 #define AUTOTUNE_FIXED_WING_CONVERGENCE_RATE 10
56 #define AUTOTUNE_FIXED_WING_SAMPLE_INTERVAL 20 // ms
57 #define AUTOTUNE_FIXED_WING_SAMPLES 1000 // Use average over the last 20 seconds of hard maneuvers
58 #define AUTOTUNE_FIXED_WING_MIN_SAMPLES 250 // Start updating tune after 5 seconds of hard maneuvers
60 PG_REGISTER_WITH_RESET_TEMPLATE(pidAutotuneConfig_t
, pidAutotuneConfig
, PG_PID_AUTOTUNE_CONFIG
, 2);
62 PG_RESET_TEMPLATE(pidAutotuneConfig_t
, pidAutotuneConfig
,
63 .fw_min_stick
= SETTING_FW_AUTOTUNE_MIN_STICK_DEFAULT
,
64 .fw_rate_adjustment
= SETTING_FW_AUTOTUNE_RATE_ADJUSTMENT_DEFAULT
,
65 .fw_max_rate_deflection
= SETTING_FW_AUTOTUNE_MAX_RATE_DEFLECTION_DEFAULT
,
79 float absDesiredRateAccum
;
80 float absReachedRateAccum
;
81 float absPidOutputAccum
;
85 #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
87 #if defined(USE_AUTOTUNE_FIXED_WING) || defined(USE_AUTOTUNE_MULTIROTOR)
89 static pidAutotuneData_t tuneCurrent
[XYZ_AXIS_COUNT
];
90 static pidAutotuneData_t tuneSaved
[XYZ_AXIS_COUNT
];
91 static timeMs_t lastGainsUpdateTime
;
93 void autotuneUpdateGains(pidAutotuneData_t
* data
)
95 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
96 pidBankMutable()->pid
[axis
].FF
= lrintf(data
[axis
].gainFF
);
97 ((controlRateConfig_t
*)currentControlRateProfile
)->stabilized
.rates
[axis
] = lrintf(data
[axis
].rate
/10.0f
);
99 schedulePidGainsUpdate();
102 void autotuneCheckUpdateGains(void)
104 const timeMs_t currentTimeMs
= millis();
106 if ((currentTimeMs
- lastGainsUpdateTime
) < AUTOTUNE_SAVE_PERIOD
) {
110 // If pilot will exit autotune we'll restore values we are flying now
111 memcpy(tuneSaved
, tuneCurrent
, sizeof(pidAutotuneData_t
) * XYZ_AXIS_COUNT
);
112 autotuneUpdateGains(tuneSaved
);
113 lastGainsUpdateTime
= currentTimeMs
;
116 void autotuneStart(void)
118 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
119 tuneCurrent
[axis
].gainFF
= pidBank()->pid
[axis
].FF
;
120 tuneCurrent
[axis
].rate
= currentControlRateProfile
->stabilized
.rates
[axis
] * 10.0f
;
121 tuneCurrent
[axis
].initialRate
= currentControlRateProfile
->stabilized
.rates
[axis
] * 10.0f
;
122 tuneCurrent
[axis
].absDesiredRateAccum
= 0;
123 tuneCurrent
[axis
].absReachedRateAccum
= 0;
124 tuneCurrent
[axis
].absPidOutputAccum
= 0;
125 tuneCurrent
[axis
].updateCount
= 0;
128 memcpy(tuneSaved
, tuneCurrent
, sizeof(pidAutotuneData_t
) * XYZ_AXIS_COUNT
);
129 lastGainsUpdateTime
= millis();
132 void autotuneUpdateState(void)
134 if (isFwAutoModeActive(BOXAUTOTUNE
) && STATE(AIRPLANE
) && ARMING_FLAG(ARMED
)) {
135 if (!FLIGHT_MODE(AUTO_TUNE
)) {
137 ENABLE_FLIGHT_MODE(AUTO_TUNE
);
140 autotuneCheckUpdateGains();
143 if (FLIGHT_MODE(AUTO_TUNE
)) {
144 autotuneUpdateGains(tuneSaved
);
147 DISABLE_FLIGHT_MODE(AUTO_TUNE
);
151 static void blackboxLogAutotuneEvent(adjustmentFunction_e adjustmentFunction
, int32_t newValue
)
154 UNUSED(adjustmentFunction
);
157 if (feature(FEATURE_BLACKBOX
)) {
158 flightLogEvent_inflightAdjustment_t eventData
;
159 eventData
.adjustmentFunction
= adjustmentFunction
;
160 eventData
.newValue
= newValue
;
161 eventData
.floatFlag
= false;
162 blackboxLogEvent(FLIGHT_LOG_EVENT_INFLIGHT_ADJUSTMENT
, (flightLogEventData_t
*)&eventData
);
167 #if defined(USE_AUTOTUNE_FIXED_WING)
169 void autotuneFixedWingUpdate(const flight_dynamics_index_t axis
, float desiredRate
, float reachedRate
, float pidOutput
)
171 float maxRateSetting
= tuneCurrent
[axis
].rate
;
172 float gainFF
= tuneCurrent
[axis
].gainFF
;
173 float maxDesiredRate
= maxRateSetting
;
175 const float pidSumLimit
= getPidSumLimit(axis
);
176 const float absDesiredRate
= fabsf(desiredRate
);
177 const float absReachedRate
= fabsf(reachedRate
);
178 const float absPidOutput
= fabsf(pidOutput
);
179 const bool correctDirection
= (desiredRate
>0) == (reachedRate
>0);
182 bool gainsUpdated
= false;
183 bool ratesUpdated
= false;
185 const timeMs_t currentTimeMs
= millis();
186 static timeMs_t previousSampleTimeMs
= 0;
187 const timeDelta_t timeSincePreviousSample
= currentTimeMs
- previousSampleTimeMs
;
189 // Use different max rate in ANLGE mode
190 if (FLIGHT_MODE(ANGLE_MODE
)) {
191 float maxDesiredRateInAngleMode
= DECIDEGREES_TO_DEGREES(pidProfile()->max_angle_inclination
[axis
] * 1.0f
) * pidBank()->pid
[PID_LEVEL
].P
* FP_PID_LEVEL_P_MULTIPLIER
;
192 maxDesiredRate
= MIN(maxRateSetting
, maxDesiredRateInAngleMode
);
195 const float stickInput
= absDesiredRate
/ maxDesiredRate
;
197 if ((stickInput
> (pidAutotuneConfig()->fw_min_stick
/ 100.0f
)) && correctDirection
&& (timeSincePreviousSample
>= AUTOTUNE_FIXED_WING_SAMPLE_INTERVAL
)) {
198 // Record values every 20ms and calculate moving average over samples
199 tuneCurrent
[axis
].updateCount
++;
200 tuneCurrent
[axis
].absDesiredRateAccum
+= (absDesiredRate
- tuneCurrent
[axis
].absDesiredRateAccum
) / MIN(tuneCurrent
[axis
].updateCount
, (uint32_t)AUTOTUNE_FIXED_WING_SAMPLES
);
201 tuneCurrent
[axis
].absReachedRateAccum
+= (absReachedRate
- tuneCurrent
[axis
].absReachedRateAccum
) / MIN(tuneCurrent
[axis
].updateCount
, (uint32_t)AUTOTUNE_FIXED_WING_SAMPLES
);
202 tuneCurrent
[axis
].absPidOutputAccum
+= (absPidOutput
- tuneCurrent
[axis
].absPidOutputAccum
) / MIN(tuneCurrent
[axis
].updateCount
, (uint32_t)AUTOTUNE_FIXED_WING_SAMPLES
);
204 if ((tuneCurrent
[axis
].updateCount
& 25) == 0 && tuneCurrent
[axis
].updateCount
>= AUTOTUNE_FIXED_WING_MIN_SAMPLES
) {
205 if (pidAutotuneConfig()->fw_rate_adjustment
!= FIXED
&& !FLIGHT_MODE(ANGLE_MODE
)) { // Rate discovery is not possible in ANGLE mode
207 // Target 80% control surface deflection to leave some room for P and I to work
208 float pidSumTarget
= (pidAutotuneConfig()->fw_max_rate_deflection
/ 100.0f
) * pidSumLimit
;
210 // Theoretically achievable rate with target deflection
211 rateFullStick
= pidSumTarget
/ tuneCurrent
[axis
].absPidOutputAccum
* tuneCurrent
[axis
].absReachedRateAccum
;
214 if (rateFullStick
> (maxRateSetting
+ 10.0f
)) {
215 maxRateSetting
+= 10.0f
;
216 } else if (rateFullStick
< (maxRateSetting
- 10.0f
)) {
217 maxRateSetting
-= 10.0f
;
220 // Constrain to safe values
221 uint16_t minRate
= (axis
== FD_YAW
) ? AUTOTUNE_FIXED_WING_MIN_YAW_RATE
: AUTOTUNE_FIXED_WING_MIN_ROLL_PITCH_RATE
;
222 uint16_t maxRate
= (pidAutotuneConfig()->fw_rate_adjustment
== AUTO
) ? AUTOTUNE_FIXED_WING_MAX_RATE
: MAX(tuneCurrent
[axis
].initialRate
, minRate
);
223 tuneCurrent
[axis
].rate
= constrainf(maxRateSetting
, minRate
, maxRate
);
227 // Update FF towards value needed to achieve current rate target
228 gainFF
+= (tuneCurrent
[axis
].absPidOutputAccum
/ tuneCurrent
[axis
].absReachedRateAccum
* FP_PID_RATE_FF_MULTIPLIER
- gainFF
) * (AUTOTUNE_FIXED_WING_CONVERGENCE_RATE
/ 100.0f
);
229 tuneCurrent
[axis
].gainFF
= constrainf(gainFF
, AUTOTUNE_FIXED_WING_MIN_FF
, AUTOTUNE_FIXED_WING_MAX_FF
);
234 previousSampleTimeMs
= currentTimeMs
;
238 autotuneUpdateGains(tuneCurrent
);
242 blackboxLogAutotuneEvent(ADJUSTMENT_ROLL_FF
, tuneCurrent
[axis
].gainFF
);
246 blackboxLogAutotuneEvent(ADJUSTMENT_PITCH_FF
, tuneCurrent
[axis
].gainFF
);
250 blackboxLogAutotuneEvent(ADJUSTMENT_YAW_FF
, tuneCurrent
[axis
].gainFF
);
253 gainsUpdated
= false;
257 autotuneUpdateGains(tuneCurrent
);
261 blackboxLogAutotuneEvent(ADJUSTMENT_ROLL_RATE
, tuneCurrent
[axis
].rate
);
265 blackboxLogAutotuneEvent(ADJUSTMENT_PITCH_RATE
, tuneCurrent
[axis
].rate
);
269 blackboxLogAutotuneEvent(ADJUSTMENT_YAW_RATE
, tuneCurrent
[axis
].rate
);
274 DEBUG_SET(DEBUG_AUTOTUNE
, axis
, rateFullStick
);
275 ratesUpdated
= false;