2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
28 #include "build/debug.h"
30 #include "common/axis.h"
31 #include "common/utils.h"
33 #include "config/config.h"
34 #include "config/feature.h"
36 #include "fc/controlrate_profile.h"
39 #include "fc/rc_controls.h"
40 #include "fc/rc_modes.h"
41 #include "fc/runtime_config.h"
43 #include "flight/failsafe.h"
44 #include "flight/imu.h"
45 #include "flight/gps_rescue.h"
46 #include "flight/pid.h"
47 #include "flight/pid_init.h"
53 #include "sensors/battery.h"
54 #include "sensors/gyro.h"
59 typedef float (applyRatesFn
)(const int axis
, float rcCommandf
, const float rcCommandfAbs
);
60 // note that rcCommand[] is an external float
62 static float rawSetpoint
[XYZ_AXIS_COUNT
];
64 static float setpointRate
[3], rcDeflection
[3], rcDeflectionAbs
[3]; // deflection range -1 to 1
65 static float maxRcDeflectionAbs
;
67 static bool reverseMotors
= false;
68 static applyRatesFn
*applyRates
;
70 static uint16_t currentRxIntervalUs
; // packet interval in microseconds
71 static float currentRxRateHz
; // packet rate in hertz
73 static bool isRxDataNew
= false;
74 static bool isRxIntervalValid
= false;
75 static float rcCommandDivider
= 500.0f
;
76 static float rcCommandYawDivider
= 500.0f
;
79 ROLL_FLAG
= 1 << ROLL
,
80 PITCH_FLAG
= 1 << PITCH
,
82 THROTTLE_FLAG
= 1 << THROTTLE
,
85 #ifdef USE_FEEDFORWARD
86 static float feedforwardSmoothed
[3];
87 static float feedforwardRaw
[3];
88 typedef struct laggedMovingAverageCombined_s
{
89 laggedMovingAverage_t filter
;
91 } laggedMovingAverageCombined_t
;
92 laggedMovingAverageCombined_t feedforwardDeltaAvg
[XYZ_AXIS_COUNT
];
94 float getFeedforward(int axis
)
96 #ifdef USE_RC_SMOOTHING_FILTER
97 return feedforwardSmoothed
[axis
];
99 return feedforwardRaw
[axis
];
102 #endif // USE_FEEDFORWARD
104 #ifdef USE_RC_SMOOTHING_FILTER
105 static FAST_DATA_ZERO_INIT rcSmoothingFilter_t rcSmoothingData
;
106 static float rcDeflectionSmoothed
[3];
107 #endif // USE_RC_SMOOTHING_FILTER
109 #define RX_INTERVAL_MIN_US 950 // 0.950ms to fit 1kHz without an issue
110 #define RX_INTERVAL_MAX_US 65500 // 65.5ms or 15.26hz
112 float getSetpointRate(int axis
)
114 #ifdef USE_RC_SMOOTHING_FILTER
115 return setpointRate
[axis
];
117 return rawSetpoint
[axis
];
121 static float maxRcRate
[3];
122 float getMaxRcRate(int axis
)
124 return maxRcRate
[axis
];
127 float getRcDeflection(int axis
)
129 #ifdef USE_RC_SMOOTHING_FILTER
130 return rcDeflectionSmoothed
[axis
];
132 return rcDeflection
[axis
];
136 float getRcDeflectionRaw(int axis
)
138 return rcDeflection
[axis
];
141 float getRcDeflectionAbs(int axis
)
143 return rcDeflectionAbs
[axis
];
146 float getMaxRcDeflectionAbs(void)
148 return maxRcDeflectionAbs
;
151 #define THROTTLE_LOOKUP_LENGTH 12
152 static int16_t lookupThrottleRC
[THROTTLE_LOOKUP_LENGTH
]; // lookup table for expo & mid THROTTLE
154 static int16_t rcLookupThrottle(int32_t tmp
)
156 const int32_t tmp2
= tmp
/ 100;
157 // [0;1000] -> expo -> [MINTHROTTLE;MAXTHROTTLE]
158 return lookupThrottleRC
[tmp2
] + (tmp
- tmp2
* 100) * (lookupThrottleRC
[tmp2
+ 1] - lookupThrottleRC
[tmp2
]) / 100;
161 #define SETPOINT_RATE_LIMIT_MIN -1998.0f
162 #define SETPOINT_RATE_LIMIT_MAX 1998.0f
163 STATIC_ASSERT(CONTROL_RATE_CONFIG_RATE_LIMIT_MAX
<= (uint16_t)SETPOINT_RATE_LIMIT_MAX
, CONTROL_RATE_CONFIG_RATE_LIMIT_MAX_too_large
);
165 #define RC_RATE_INCREMENTAL 14.54f
167 float applyBetaflightRates(const int axis
, float rcCommandf
, const float rcCommandfAbs
)
169 if (currentControlRateProfile
->rcExpo
[axis
]) {
170 const float expof
= currentControlRateProfile
->rcExpo
[axis
] / 100.0f
;
171 rcCommandf
= rcCommandf
* power3(rcCommandfAbs
) * expof
+ rcCommandf
* (1 - expof
);
174 float rcRate
= currentControlRateProfile
->rcRates
[axis
] / 100.0f
;
176 rcRate
+= RC_RATE_INCREMENTAL
* (rcRate
- 2.0f
);
178 float angleRate
= 200.0f
* rcRate
* rcCommandf
;
179 if (currentControlRateProfile
->rates
[axis
]) {
180 const float rcSuperfactor
= 1.0f
/ (constrainf(1.0f
- (rcCommandfAbs
* (currentControlRateProfile
->rates
[axis
] / 100.0f
)), 0.01f
, 1.00f
));
181 angleRate
*= rcSuperfactor
;
187 float applyRaceFlightRates(const int axis
, float rcCommandf
, const float rcCommandfAbs
)
189 // -1.0 to 1.0 ranged and curved
190 rcCommandf
= ((1.0f
+ 0.01f
* currentControlRateProfile
->rcExpo
[axis
] * (rcCommandf
* rcCommandf
- 1.0f
)) * rcCommandf
);
191 // convert to -2000 to 2000 range using acro+ modifier
192 float angleRate
= 10.0f
* currentControlRateProfile
->rcRates
[axis
] * rcCommandf
;
193 angleRate
= angleRate
* (1 + rcCommandfAbs
* (float)currentControlRateProfile
->rates
[axis
] * 0.01f
);
198 float applyKissRates(const int axis
, float rcCommandf
, const float rcCommandfAbs
)
200 const float rcCurvef
= currentControlRateProfile
->rcExpo
[axis
] / 100.0f
;
202 float kissRpyUseRates
= 1.0f
/ (constrainf(1.0f
- (rcCommandfAbs
* (currentControlRateProfile
->rates
[axis
] / 100.0f
)), 0.01f
, 1.00f
));
203 float kissRcCommandf
= (power3(rcCommandf
) * rcCurvef
+ rcCommandf
* (1 - rcCurvef
)) * (currentControlRateProfile
->rcRates
[axis
] / 1000.0f
);
204 float kissAngle
= constrainf(((2000.0f
* kissRpyUseRates
) * kissRcCommandf
), SETPOINT_RATE_LIMIT_MIN
, SETPOINT_RATE_LIMIT_MAX
);
209 float applyActualRates(const int axis
, float rcCommandf
, const float rcCommandfAbs
)
211 float expof
= currentControlRateProfile
->rcExpo
[axis
] / 100.0f
;
212 expof
= rcCommandfAbs
* (power5(rcCommandf
) * expof
+ rcCommandf
* (1 - expof
));
214 const float centerSensitivity
= currentControlRateProfile
->rcRates
[axis
] * 10.0f
;
215 const float stickMovement
= MAX(0, currentControlRateProfile
->rates
[axis
] * 10.0f
- centerSensitivity
);
216 const float angleRate
= rcCommandf
* centerSensitivity
+ stickMovement
* expof
;
221 float applyQuickRates(const int axis
, float rcCommandf
, const float rcCommandfAbs
)
223 const uint16_t rcRate
= currentControlRateProfile
->rcRates
[axis
] * 2;
224 const uint16_t maxDPS
= MAX(currentControlRateProfile
->rates
[axis
] * 10, rcRate
);
225 const float expof
= currentControlRateProfile
->rcExpo
[axis
] / 100.0f
;
226 const float superFactorConfig
= ((float)maxDPS
/ rcRate
- 1) / ((float)maxDPS
/ rcRate
);
232 if (currentControlRateProfile
->quickRatesRcExpo
) {
233 curve
= power3(rcCommandf
) * expof
+ rcCommandf
* (1 - expof
);
234 superFactor
= 1.0f
/ (constrainf(1.0f
- (rcCommandfAbs
* superFactorConfig
), 0.01f
, 1.00f
));
235 angleRate
= constrainf(curve
* rcRate
* superFactor
, SETPOINT_RATE_LIMIT_MIN
, SETPOINT_RATE_LIMIT_MAX
);
237 curve
= power3(rcCommandfAbs
) * expof
+ rcCommandfAbs
* (1 - expof
);
238 superFactor
= 1.0f
/ (constrainf(1.0f
- (curve
* superFactorConfig
), 0.01f
, 1.00f
));
239 angleRate
= constrainf(rcCommandf
* rcRate
* superFactor
, SETPOINT_RATE_LIMIT_MIN
, SETPOINT_RATE_LIMIT_MAX
);
245 static void scaleRawSetpointToFpvCamAngle(void)
247 //recalculate sin/cos only when rxConfig()->fpvCamAngleDegrees changed
248 static uint8_t lastFpvCamAngleDegrees
= 0;
249 static float cosFactor
= 1.0;
250 static float sinFactor
= 0.0;
252 if (lastFpvCamAngleDegrees
!= rxConfig()->fpvCamAngleDegrees
) {
253 lastFpvCamAngleDegrees
= rxConfig()->fpvCamAngleDegrees
;
254 cosFactor
= cos_approx(rxConfig()->fpvCamAngleDegrees
* RAD
);
255 sinFactor
= sin_approx(rxConfig()->fpvCamAngleDegrees
* RAD
);
258 float roll
= rawSetpoint
[ROLL
];
259 float yaw
= rawSetpoint
[YAW
];
260 rawSetpoint
[ROLL
] = constrainf(roll
* cosFactor
- yaw
* sinFactor
, SETPOINT_RATE_LIMIT_MIN
, SETPOINT_RATE_LIMIT_MAX
);
261 rawSetpoint
[YAW
] = constrainf(yaw
* cosFactor
+ roll
* sinFactor
, SETPOINT_RATE_LIMIT_MIN
, SETPOINT_RATE_LIMIT_MAX
);
264 void updateRcRefreshRate(timeUs_t currentTimeUs
)
266 static timeUs_t lastRxTimeUs
;
268 timeDelta_t frameAgeUs
;
269 timeDelta_t frameDeltaUs
= rxGetFrameDelta(&frameAgeUs
);
271 if (!frameDeltaUs
|| cmpTimeUs(currentTimeUs
, lastRxTimeUs
) <= frameAgeUs
) {
272 frameDeltaUs
= cmpTimeUs(currentTimeUs
, lastRxTimeUs
);
275 DEBUG_SET(DEBUG_RX_TIMING
, 0, MIN(frameDeltaUs
/ 10, INT16_MAX
));
276 DEBUG_SET(DEBUG_RX_TIMING
, 1, MIN(frameAgeUs
/ 10, INT16_MAX
));
278 lastRxTimeUs
= currentTimeUs
;
279 currentRxIntervalUs
= constrain(frameDeltaUs
, RX_INTERVAL_MIN_US
, RX_INTERVAL_MAX_US
);
280 isRxIntervalValid
= frameDeltaUs
== currentRxIntervalUs
;
282 currentRxRateHz
= 1e6f
/ currentRxIntervalUs
; // cannot be zero due to preceding constraint
283 DEBUG_SET(DEBUG_RX_TIMING
, 2, isRxIntervalValid
);
284 DEBUG_SET(DEBUG_RX_TIMING
, 3, MIN(currentRxIntervalUs
/ 10, INT16_MAX
));
287 uint16_t getCurrentRxIntervalUs(void)
289 return currentRxIntervalUs
;
292 #ifdef USE_RC_SMOOTHING_FILTER
294 // Initialize or update the filters base on either the manually selected cutoff, or
295 // the auto-calculated cutoff frequency based on detected rx frame rate.
296 FAST_CODE_NOINLINE
void rcSmoothingSetFilterCutoffs(rcSmoothingFilter_t
*smoothingData
)
298 // in auto mode, calculate the RC smoothing cutoff from the smoothed Rx link frequency
299 const uint16_t oldSetpointCutoff
= smoothingData
->setpointCutoffFrequency
;
300 const uint16_t oldFeedforwardCutoff
= smoothingData
->feedforwardCutoffFrequency
;
301 const uint16_t minCutoffHz
= 15; // don't let any RC smoothing filter cutoff go below 15Hz
302 if (smoothingData
->setpointCutoffSetting
== 0) {
303 smoothingData
->setpointCutoffFrequency
= MAX(minCutoffHz
, (uint16_t)(smoothingData
->smoothedRxRateHz
* smoothingData
->autoSmoothnessFactorSetpoint
));
305 if (smoothingData
->throttleCutoffSetting
== 0) {
306 smoothingData
->throttleCutoffFrequency
= MAX(minCutoffHz
, (uint16_t)(smoothingData
->smoothedRxRateHz
* smoothingData
->autoSmoothnessFactorThrottle
));
309 if (smoothingData
->feedforwardCutoffSetting
== 0) {
310 smoothingData
->feedforwardCutoffFrequency
= MAX(minCutoffHz
, (uint16_t)(smoothingData
->smoothedRxRateHz
* smoothingData
->autoSmoothnessFactorFeedforward
));
313 const float dT
= targetPidLooptime
* 1e-6f
;
314 if ((smoothingData
->setpointCutoffFrequency
!= oldSetpointCutoff
) || !smoothingData
->filterInitialized
) {
315 // note that cutoff frequencies are integers, filter cutoffs won't re-calculate until there is > 1hz variation from previous cutoff
316 // initialize or update the setpoint cutoff based filters
317 const float setpointCutoffFrequency
= smoothingData
->setpointCutoffFrequency
;
318 for (int i
= 0; i
< PRIMARY_CHANNEL_COUNT
; i
++) {
320 if (!smoothingData
->filterInitialized
) {
321 pt3FilterInit(&smoothingData
->filterSetpoint
[i
], pt3FilterGain(setpointCutoffFrequency
, dT
));
323 pt3FilterUpdateCutoff(&smoothingData
->filterSetpoint
[i
], pt3FilterGain(setpointCutoffFrequency
, dT
));
326 const float throttleCutoffFrequency
= smoothingData
->throttleCutoffFrequency
;
327 if (!smoothingData
->filterInitialized
) {
328 pt3FilterInit(&smoothingData
->filterSetpoint
[i
], pt3FilterGain(throttleCutoffFrequency
, dT
));
330 pt3FilterUpdateCutoff(&smoothingData
->filterSetpoint
[i
], pt3FilterGain(throttleCutoffFrequency
, dT
));
334 // initialize or update the RC Deflection filter
335 for (int i
= FD_ROLL
; i
< FD_YAW
; i
++) {
336 if (!smoothingData
->filterInitialized
) {
337 pt3FilterInit(&smoothingData
->filterRcDeflection
[i
], pt3FilterGain(setpointCutoffFrequency
, dT
));
339 pt3FilterUpdateCutoff(&smoothingData
->filterRcDeflection
[i
], pt3FilterGain(setpointCutoffFrequency
, dT
));
343 // initialize or update the Feedforward filter
344 if ((smoothingData
->feedforwardCutoffFrequency
!= oldFeedforwardCutoff
) || !smoothingData
->filterInitialized
) {
345 for (int i
= FD_ROLL
; i
<= FD_YAW
; i
++) {
346 const float feedforwardCutoffFrequency
= smoothingData
->feedforwardCutoffFrequency
;
347 if (!smoothingData
->filterInitialized
) {
348 pt3FilterInit(&smoothingData
->filterFeedforward
[i
], pt3FilterGain(feedforwardCutoffFrequency
, dT
));
350 pt3FilterUpdateCutoff(&smoothingData
->filterFeedforward
[i
], pt3FilterGain(feedforwardCutoffFrequency
, dT
));
355 DEBUG_SET(DEBUG_RC_SMOOTHING
, 1, smoothingData
->setpointCutoffFrequency
);
356 DEBUG_SET(DEBUG_RC_SMOOTHING
, 2, smoothingData
->feedforwardCutoffFrequency
);
359 // Determine if we need to caclulate filter cutoffs. If not then we can avoid
360 // examining the rx frame times completely
361 FAST_CODE_NOINLINE
bool rcSmoothingAutoCalculate(void)
363 // if any rc smoothing cutoff is 0 (auto) then we need to calculate cutoffs
364 if ((rcSmoothingData
.setpointCutoffSetting
== 0) || (rcSmoothingData
.feedforwardCutoffSetting
== 0) || (rcSmoothingData
.throttleCutoffSetting
== 0)) {
370 static FAST_CODE
void processRcSmoothingFilter(void)
372 static FAST_DATA_ZERO_INIT
float rxDataToSmooth
[4];
373 static FAST_DATA_ZERO_INIT
bool initialized
;
374 static FAST_DATA_ZERO_INIT
bool calculateCutoffs
;
376 // first call initialization
379 rcSmoothingData
.filterInitialized
= false;
380 rcSmoothingData
.smoothedRxRateHz
= 0.0f
;
381 rcSmoothingData
.sampleCount
= 0;
382 rcSmoothingData
.debugAxis
= rxConfig()->rc_smoothing_debug_axis
;
384 rcSmoothingData
.autoSmoothnessFactorSetpoint
= 1.5f
/ (1.0f
+ (rxConfig()->rc_smoothing_auto_factor_rpy
/ 10.0f
));
385 rcSmoothingData
.autoSmoothnessFactorFeedforward
= 1.5f
/ (1.0f
+ (rxConfig()->rc_smoothing_auto_factor_rpy
/ 10.0f
));
386 rcSmoothingData
.autoSmoothnessFactorThrottle
= 1.5f
/ (1.0f
+ (rxConfig()->rc_smoothing_auto_factor_throttle
/ 10.0f
));
388 rcSmoothingData
.setpointCutoffSetting
= rxConfig()->rc_smoothing_setpoint_cutoff
;
389 rcSmoothingData
.throttleCutoffSetting
= rxConfig()->rc_smoothing_throttle_cutoff
;
390 rcSmoothingData
.feedforwardCutoffSetting
= rxConfig()->rc_smoothing_feedforward_cutoff
;
392 rcSmoothingData
.setpointCutoffFrequency
= rcSmoothingData
.setpointCutoffSetting
;
393 rcSmoothingData
.feedforwardCutoffFrequency
= rcSmoothingData
.feedforwardCutoffSetting
;
394 rcSmoothingData
.throttleCutoffFrequency
= rcSmoothingData
.throttleCutoffSetting
;
396 if (rxConfig()->rc_smoothing_mode
) {
397 calculateCutoffs
= rcSmoothingAutoCalculate();
398 // if we don't need to calculate cutoffs dynamically then the filters can be initialized now
399 if (!calculateCutoffs
) {
400 rcSmoothingSetFilterCutoffs(&rcSmoothingData
);
401 rcSmoothingData
.filterInitialized
= true;
407 if (calculateCutoffs
) {
408 // for auto calculated filters, calculate the link interval and update the RC smoothing filters at regular intervals
409 // this is more efficient than monitoring for significant changes and making comparisons to decide whether to update the filter
410 const timeMs_t currentTimeMs
= millis();
412 const bool ready
= (currentTimeMs
> 1000) && (targetPidLooptime
> 0);
413 if (ready
) { // skip during FC initialization
414 // Wait 1000ms after power to let the PID loop stabilize before starting average frame rate calculation
415 if (rxIsReceivingSignal() && isRxIntervalValid
) {
417 static uint16_t previousRxIntervalUs
;
418 if (abs(currentRxIntervalUs
- previousRxIntervalUs
) < (previousRxIntervalUs
- (previousRxIntervalUs
/ 8))) {
419 // exclude large steps, eg after dropouts or telemetry
420 // by using interval here, we catch a dropout/telemetry where the inteval increases by 100%, but accept
421 // the return to normal value, which is only 50% different from the 100% interval of a single drop, and 66% of a return after a double drop.
422 static float prevRxRateHz
;
423 // smooth the current Rx link frequency estimates
424 const float kF
= 0.1f
; // first order lowpass smoothing filter coefficient
425 const float smoothedRxRateHz
= prevRxRateHz
+ kF
* (currentRxRateHz
- prevRxRateHz
);
426 prevRxRateHz
= smoothedRxRateHz
;
428 // recalculate cutoffs every 3 acceptable samples
429 if (rcSmoothingData
.sampleCount
) {
430 rcSmoothingData
.sampleCount
--;
433 rcSmoothingData
.smoothedRxRateHz
= smoothedRxRateHz
;
434 rcSmoothingSetFilterCutoffs(&rcSmoothingData
);
435 rcSmoothingData
.filterInitialized
= true;
436 rcSmoothingData
.sampleCount
= 3;
440 previousRxIntervalUs
= currentRxIntervalUs
;
442 // either we stopped receiving rx samples (failsafe?) or the sample interval is unreasonable
443 // require a full re-evaluation period after signal is restored
444 rcSmoothingData
.sampleCount
= 0;
448 DEBUG_SET(DEBUG_RC_SMOOTHING_RATE
, 0, currentRxIntervalUs
/ 10);
449 DEBUG_SET(DEBUG_RC_SMOOTHING_RATE
, 1, rcSmoothingData
.sampleCount
);
450 DEBUG_SET(DEBUG_RC_SMOOTHING_RATE
, 2, rcSmoothingData
.smoothedRxRateHz
); // value used by filters
451 DEBUG_SET(DEBUG_RC_SMOOTHING_RATE
, 3, sampleState
); // guard time = 1, guard time expired = 2
453 // Get new values to be smoothed
454 for (int i
= 0; i
< PRIMARY_CHANNEL_COUNT
; i
++) {
455 rxDataToSmooth
[i
] = i
== THROTTLE
? rcCommand
[i
] : rawSetpoint
[i
];
457 DEBUG_SET(DEBUG_RC_INTERPOLATION
, i
, lrintf(rxDataToSmooth
[i
]));
459 DEBUG_SET(DEBUG_RC_INTERPOLATION
, i
, ((lrintf(rxDataToSmooth
[i
])) - 1000));
464 DEBUG_SET(DEBUG_RC_SMOOTHING
, 0, rcSmoothingData
.smoothedRxRateHz
);
465 DEBUG_SET(DEBUG_RC_SMOOTHING
, 3, rcSmoothingData
.sampleCount
);
467 // each pid loop, apply the last received channel value to the filter, if initialised - thanks @klutvott
468 for (int i
= 0; i
< PRIMARY_CHANNEL_COUNT
; i
++) {
469 float *dst
= i
== THROTTLE
? &rcCommand
[i
] : &setpointRate
[i
];
470 if (rcSmoothingData
.filterInitialized
) {
471 *dst
= pt3FilterApply(&rcSmoothingData
.filterSetpoint
[i
], rxDataToSmooth
[i
]);
473 // If filter isn't initialized yet, as in smoothing off, use the actual unsmoothed rx channel data
474 *dst
= rxDataToSmooth
[i
];
478 for (int axis
= FD_ROLL
; axis
<= FD_YAW
; axis
++) {
479 // Feedforward smoothing
480 feedforwardSmoothed
[axis
] = pt3FilterApply(&rcSmoothingData
.filterFeedforward
[axis
], feedforwardRaw
[axis
]);
481 // Horizon mode smoothing of rcDeflection on pitch and roll to provide a smooth angle element
482 const bool smoothRcDeflection
= FLIGHT_MODE(HORIZON_MODE
) && rcSmoothingData
.filterInitialized
;
483 if (smoothRcDeflection
&& axis
< FD_YAW
) {
484 rcDeflectionSmoothed
[axis
] = pt3FilterApply(&rcSmoothingData
.filterRcDeflection
[axis
], rcDeflection
[axis
]);
486 rcDeflectionSmoothed
[axis
] = rcDeflection
[axis
];
490 #endif // USE_RC_SMOOTHING_FILTER
492 NOINLINE
void initAveraging(uint16_t feedforwardAveraging
)
494 for (int i
= 0; i
< XYZ_AXIS_COUNT
; i
++) {
495 laggedMovingAverageInit(&feedforwardDeltaAvg
[i
].filter
, feedforwardAveraging
+ 1, (float *)&feedforwardDeltaAvg
[i
].buf
[0]);
499 FAST_CODE_NOINLINE
void calculateFeedforward(const pidRuntime_t
*pid
, int axis
)
501 const float rxInterval
= currentRxIntervalUs
* 1e-6f
; // seconds
502 float rxRate
= currentRxRateHz
;
503 static float prevRxInterval
;
505 static float prevRcCommand
[3];
506 static float prevRcCommandDeltaAbs
[3]; // for duplicate interpolation
507 static float prevSetpoint
[3]; // equals raw unless interpolated
508 static float prevSetpointSpeed
[3];
509 static float prevAcceleration
[3]; // for duplicate interpolation
510 static bool prevDuplicatePacket
[3]; // to identify multiple identical packets
511 static uint16_t feedforwardAveraging
= 0;
513 if (feedforwardAveraging
!= pid
->feedforwardAveraging
) {
514 feedforwardAveraging
= pid
->feedforwardAveraging
;
515 initAveraging(feedforwardAveraging
);
518 const float rcCommandDeltaAbs
= fabsf(rcCommand
[axis
] - prevRcCommand
[axis
]);
519 prevRcCommand
[axis
] = rcCommand
[axis
];
521 float setpoint
= rawSetpoint
[axis
];
522 float setpointSpeed
= (setpoint
- prevSetpoint
[axis
]);
523 prevSetpoint
[axis
] = setpoint
;
524 float setpointAcceleration
= 0.0f
;
527 if (axis
== FD_ROLL
) {
528 DEBUG_SET(DEBUG_FEEDFORWARD
, 0, lrintf(setpoint
* 10.0f
)); // un-smoothed final feedforward
532 float zeroTheAcceleration
= 1.0f
;
533 float jitterAttenuator
= 1.0f
;
534 if (pid
->feedforwardJitterFactor
) {
535 if (rcCommandDeltaAbs
< pid
->feedforwardJitterFactor
) {
536 jitterAttenuator
= MAX(1.0f
- (rcCommandDeltaAbs
+ prevRcCommandDeltaAbs
[axis
]) * pid
->feedforwardJitterFactorInv
, 0.0f
);
537 // note that feedforwardJitterFactorInv includes a divide by 2 to average the two previous rcCommandDeltaAbs values
538 jitterAttenuator
= 1.0f
- jitterAttenuator
* jitterAttenuator
;
541 prevRcCommandDeltaAbs
[axis
] = rcCommandDeltaAbs
;
543 // interpolate setpoint if necessary
544 if (rcCommandDeltaAbs
) {
546 if (prevDuplicatePacket
[axis
] == true) {
547 rxRate
= 1.0f
/ (rxInterval
+ prevRxInterval
);
548 zeroTheAcceleration
= 0.0f
;
549 // don't add acceleration, empirically seems better on FrSky
551 setpointSpeed
*= rxRate
;
552 prevDuplicatePacket
[axis
] = false;
555 if (prevDuplicatePacket
[axis
] == false) {
556 // first duplicate after movement
557 setpointSpeed
= prevSetpointSpeed
[axis
];
558 if (fabsf(setpoint
) < 0.95f
* maxRcRate
[axis
]) {
559 setpointSpeed
+= prevAcceleration
[axis
];
561 zeroTheAcceleration
= 0.0f
; // force acceleration to zero
563 // second and subsequent duplicates after movement should be zeroed
564 setpointSpeed
= 0.0f
;
565 prevSetpointSpeed
[axis
] = 0.0f
;
566 zeroTheAcceleration
= 0.0f
; // force acceleration to zero
568 prevDuplicatePacket
[axis
] = true;
570 prevRxInterval
= rxInterval
;
572 // smooth the setpointSpeed value
573 setpointSpeed
= prevSetpointSpeed
[axis
] + pid
->feedforwardSmoothFactor
* (setpointSpeed
- prevSetpointSpeed
[axis
]);
575 // calculate acceleration and attenuate
576 setpointAcceleration
= (setpointSpeed
- prevSetpointSpeed
[axis
]) * rxRate
* 0.01f
;
577 prevSetpointSpeed
[axis
] = setpointSpeed
;
579 // smooth the acceleration element (effectively a second order filter) and apply jitter reduction
580 setpointAcceleration
= prevAcceleration
[axis
] + pid
->feedforwardSmoothFactor
* (setpointAcceleration
- prevAcceleration
[axis
]);
581 prevAcceleration
[axis
] = setpointAcceleration
* zeroTheAcceleration
;
582 setpointAcceleration
= setpointAcceleration
* pid
->feedforwardBoostFactor
* jitterAttenuator
* zeroTheAcceleration
;
584 if (axis
== FD_ROLL
) {
585 DEBUG_SET(DEBUG_FEEDFORWARD
, 1, lrintf(setpointSpeed
* 0.1f
)); // base feedforward without acceleration
588 float feedforward
= setpointSpeed
+ setpointAcceleration
;
590 if (axis
== FD_ROLL
) {
591 DEBUG_SET(DEBUG_FEEDFORWARD
, 2, lrintf(feedforward
* 0.1f
));
592 // un-smoothed feedforward including acceleration but before limiting, transition, averaging, and jitter reduction
595 // apply feedforward transition
596 const bool useTransition
= (pid
->feedforwardTransition
!= 0.0f
) && (rcDeflectionAbs
[axis
] < pid
->feedforwardTransition
);
598 feedforward
*= rcDeflectionAbs
[axis
] * pid
->feedforwardTransitionInv
;
602 if (feedforwardAveraging
) {
603 feedforward
= laggedMovingAverageUpdate(&feedforwardDeltaAvg
[axis
].filter
, feedforward
);
606 // apply jitter reduction
607 feedforward
*= jitterAttenuator
;
609 // apply max rate limiting
610 if (pid
->feedforwardMaxRateLimit
&& axis
< FD_YAW
) {
611 if (feedforward
* setpoint
> 0.0f
) { // in same direction
612 const float limit
= (maxRcRate
[axis
] - fabsf(setpoint
)) * pid
->feedforwardMaxRateLimit
;
613 feedforward
= (limit
> 0.0f
) ? constrainf(feedforward
, -limit
, limit
) : 0.0f
;
617 feedforwardRaw
[axis
] = feedforward
;
619 if (axis
== FD_ROLL
) {
620 DEBUG_SET(DEBUG_FEEDFORWARD
, 3, lrintf(feedforwardRaw
[axis
] * 0.1f
)); // un-smoothed final feedforward
621 DEBUG_SET(DEBUG_FEEDFORWARD_LIMIT
, 0, lrintf(jitterAttenuator
* 100.0f
)); // un-smoothed final feedforward
622 DEBUG_SET(DEBUG_FEEDFORWARD_LIMIT
, 1, lrintf(maxRcRate
[axis
])); // un-smoothed final feedforward
623 DEBUG_SET(DEBUG_FEEDFORWARD_LIMIT
, 2, lrintf(setpoint
)); // un-smoothed final feedforward
624 DEBUG_SET(DEBUG_FEEDFORWARD_LIMIT
, 3, lrintf(feedforwardRaw
[axis
])); // un-smoothed final feedforward
628 FAST_CODE
void processRcCommand(void)
631 maxRcDeflectionAbs
= 0.0f
;
632 for (int axis
= FD_ROLL
; axis
<= FD_YAW
; axis
++) {
636 #ifdef USE_GPS_RESCUE
637 if ((axis
== FD_YAW
) && FLIGHT_MODE(GPS_RESCUE_MODE
)) {
638 // If GPS Rescue is active then override the setpointRate used in the
639 // pid controller with the value calculated from the desired heading logic.
640 angleRate
= gpsRescueGetYawRate();
641 // Treat the stick input as centered to avoid any stick deflection base modifications (like acceleration limit)
642 rcDeflection
[axis
] = 0;
643 rcDeflectionAbs
[axis
] = 0;
647 // scale rcCommandf to range [-1.0, 1.0]
649 if (axis
== FD_YAW
) {
650 rcCommandf
= rcCommand
[axis
] / rcCommandYawDivider
;
652 rcCommandf
= rcCommand
[axis
] / rcCommandDivider
;
655 rcDeflection
[axis
] = rcCommandf
;
656 const float rcCommandfAbs
= fabsf(rcCommandf
);
657 rcDeflectionAbs
[axis
] = rcCommandfAbs
;
658 maxRcDeflectionAbs
= fmaxf(maxRcDeflectionAbs
, rcCommandfAbs
);
660 angleRate
= applyRates(axis
, rcCommandf
, rcCommandfAbs
);
663 rawSetpoint
[axis
] = constrainf(angleRate
, -1.0f
* currentControlRateProfile
->rate_limit
[axis
], 1.0f
* currentControlRateProfile
->rate_limit
[axis
]);
664 DEBUG_SET(DEBUG_ANGLERATE
, axis
, angleRate
);
666 #ifdef USE_FEEDFORWARD
667 calculateFeedforward(&pidRuntime
, axis
);
668 #endif // USE_FEEDFORWARD
671 // adjust unfiltered setpoint steps to camera angle (mixing Roll and Yaw)
672 if (rxConfig()->fpvCamAngleDegrees
&& IS_RC_MODE_ACTIVE(BOXFPVANGLEMIX
) && !FLIGHT_MODE(HEADFREE_MODE
)) {
673 scaleRawSetpointToFpvCamAngle();
677 #ifdef USE_RC_SMOOTHING_FILTER
678 processRcSmoothingFilter();
684 FAST_CODE_NOINLINE
void updateRcCommands(void)
688 for (int axis
= 0; axis
< 3; axis
++) {
689 // non coupled PID reduction scaler used in PID controller 1 and PID controller 2.
691 float tmp
= MIN(fabsf(rcData
[axis
] - rxConfig()->midrc
), 500.0f
);
692 if (axis
== ROLL
|| axis
== PITCH
) {
693 if (tmp
> rcControlsConfig()->deadband
) {
694 tmp
-= rcControlsConfig()->deadband
;
698 rcCommand
[axis
] = tmp
;
700 if (tmp
> rcControlsConfig()->yaw_deadband
) {
701 tmp
-= rcControlsConfig()->yaw_deadband
;
705 rcCommand
[axis
] = tmp
* -GET_DIRECTION(rcControlsConfig()->yaw_control_reversed
);
707 if (rcData
[axis
] < rxConfig()->midrc
) {
708 rcCommand
[axis
] = -rcCommand
[axis
];
713 if (featureIsEnabled(FEATURE_3D
)) {
714 tmp
= constrain(rcData
[THROTTLE
], PWM_RANGE_MIN
, PWM_RANGE_MAX
);
715 tmp
= (uint32_t)(tmp
- PWM_RANGE_MIN
);
717 tmp
= constrain(rcData
[THROTTLE
], rxConfig()->mincheck
, PWM_RANGE_MAX
);
718 tmp
= (uint32_t)(tmp
- rxConfig()->mincheck
) * PWM_RANGE_MIN
/ (PWM_RANGE_MAX
- rxConfig()->mincheck
);
721 if (getLowVoltageCutoff()->enabled
) {
722 tmp
= tmp
* getLowVoltageCutoff()->percentage
/ 100;
725 rcCommand
[THROTTLE
] = rcLookupThrottle(tmp
);
727 if (featureIsEnabled(FEATURE_3D
) && !failsafeIsActive()) {
728 if (!flight3DConfig()->switched_mode3d
) {
729 if (IS_RC_MODE_ACTIVE(BOX3D
)) {
730 fix12_t throttleScaler
= qConstruct(rcCommand
[THROTTLE
] - 1000, 1000);
731 rcCommand
[THROTTLE
] = rxConfig()->midrc
+ qMultiply(throttleScaler
, PWM_RANGE_MAX
- rxConfig()->midrc
);
734 if (IS_RC_MODE_ACTIVE(BOX3D
)) {
735 reverseMotors
= true;
736 fix12_t throttleScaler
= qConstruct(rcCommand
[THROTTLE
] - 1000, 1000);
737 rcCommand
[THROTTLE
] = rxConfig()->midrc
+ qMultiply(throttleScaler
, PWM_RANGE_MIN
- rxConfig()->midrc
);
739 reverseMotors
= false;
740 fix12_t throttleScaler
= qConstruct(rcCommand
[THROTTLE
] - 1000, 1000);
741 rcCommand
[THROTTLE
] = rxConfig()->midrc
+ qMultiply(throttleScaler
, PWM_RANGE_MAX
- rxConfig()->midrc
);
745 if (FLIGHT_MODE(HEADFREE_MODE
)) {
746 static t_fp_vector_def rcCommandBuff
;
748 rcCommandBuff
.X
= rcCommand
[ROLL
];
749 rcCommandBuff
.Y
= rcCommand
[PITCH
];
750 if ((!FLIGHT_MODE(ANGLE_MODE
) && (!FLIGHT_MODE(HORIZON_MODE
)) && (!FLIGHT_MODE(GPS_RESCUE_MODE
)))) {
751 rcCommandBuff
.Z
= rcCommand
[YAW
];
755 imuQuaternionHeadfreeTransformVectorEarthToBody(&rcCommandBuff
);
756 rcCommand
[ROLL
] = rcCommandBuff
.X
;
757 rcCommand
[PITCH
] = rcCommandBuff
.Y
;
758 if ((!FLIGHT_MODE(ANGLE_MODE
)&&(!FLIGHT_MODE(HORIZON_MODE
)) && (!FLIGHT_MODE(GPS_RESCUE_MODE
)))) {
759 rcCommand
[YAW
] = rcCommandBuff
.Z
;
764 void resetYawAxis(void)
767 setpointRate
[YAW
] = 0;
770 bool isMotorsReversed(void)
772 return reverseMotors
;
775 void initRcProcessing(void)
777 rcCommandDivider
= 500.0f
- rcControlsConfig()->deadband
;
778 rcCommandYawDivider
= 500.0f
- rcControlsConfig()->yaw_deadband
;
780 for (int i
= 0; i
< THROTTLE_LOOKUP_LENGTH
; i
++) {
781 const int16_t tmp
= 10 * i
- currentControlRateProfile
->thrMid8
;
784 y
= 100 - currentControlRateProfile
->thrMid8
;
786 y
= currentControlRateProfile
->thrMid8
;
787 lookupThrottleRC
[i
] = 10 * currentControlRateProfile
->thrMid8
+ tmp
* (100 - currentControlRateProfile
->thrExpo8
+ (int32_t) currentControlRateProfile
->thrExpo8
* (tmp
* tmp
) / (y
* y
)) / 10;
788 lookupThrottleRC
[i
] = PWM_RANGE_MIN
+ PWM_RANGE
* lookupThrottleRC
[i
] / 1000; // [MINTHROTTLE;MAXTHROTTLE]
791 switch (currentControlRateProfile
->rates_type
) {
792 case RATES_TYPE_BETAFLIGHT
:
794 applyRates
= applyBetaflightRates
;
796 case RATES_TYPE_RACEFLIGHT
:
797 applyRates
= applyRaceFlightRates
;
799 case RATES_TYPE_KISS
:
800 applyRates
= applyKissRates
;
802 case RATES_TYPE_ACTUAL
:
803 applyRates
= applyActualRates
;
805 case RATES_TYPE_QUICK
:
806 applyRates
= applyQuickRates
;
810 for (int i
= 0; i
< 3; i
++) {
811 maxRcRate
[i
] = applyRates(i
, 1.0f
, 1.0f
);
812 #ifdef USE_FEEDFORWARD
813 feedforwardSmoothed
[i
] = 0.0f
;
814 feedforwardRaw
[i
] = 0.0f
;
815 #endif // USE_FEEDFORWARD
818 #ifdef USE_YAW_SPIN_RECOVERY
819 const int maxYawRate
= (int)maxRcRate
[FD_YAW
];
820 initYawSpinRecovery(maxYawRate
);
824 // send rc smoothing details to blackbox
825 #ifdef USE_RC_SMOOTHING_FILTER
826 rcSmoothingFilter_t
*getRcSmoothingData(void)
828 return &rcSmoothingData
;
831 bool rcSmoothingInitializationComplete(void)
833 return rcSmoothingData
.filterInitialized
;
835 #endif // USE_RC_SMOOTHING_FILTER