BetaFlight模块设计之二十九:滤波模块分析
Posted lida2003
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BetaFlight模块设计之二十九:滤波模块分析相关的知识,希望对你有一定的参考价值。
BetaFlight模块设计之二十九:滤波模块分析
基于BetaFlight开源代码框架简介的框架设计,逐步分析内部模块功能设计。
滤波模块
前面分析BetaFlight模块设计主要基于其任务代码框架来展开,因为这样从理解以及代码上来说更容易切入。随着总体任务的熟悉,其实每个任务细节部分都是需要软件模块来逐一完成的。这里我们主要针对BetaFlight的滤波函数进行整理归纳,期望得到一个相对完成的模块分析,同时也对这里的历史问题展开一定的讨论。有兴趣的朋友可以留言或者联系我,可以深入探讨!
GYRO_FILTER_FUNCTION_NAME是一个整体的数据滤波实现,实际在代码中因宏定义差别会有debug和release版本。从概念及逻辑的角度来说都一样,debug会有一些开发人员感兴趣的打点位置。
关于引入这个函数的任务,详见BetaFlight模块设计之十三:Gyro过滤任务分析
注:有一点需要注意的,数字滤波必定导致数据延迟。
GYRO_FILTER_FUNCTION_NAME
├──> <loop axis> // downsample the individual gyro samples, XYZ_AXIS_COUNT=3
│ ├──> <gyro.downsampleFilterEnabled> // using gyro lowpass 2 filter for downsampling
│ │ └──> gyroADCf = gyro.sampleSum[axis];
│ ├──> <!gyro.downsampleFilterEnabled> // using simple average for downsampling
│ │ └──> gyroADCf = gyro.sampleSum[axis] / gyro.sampleCount;
│ ├──> <USE_RPM_FILTER> //主要用于过滤电机带来的扰动,后续可以在黑匣子数据上分析(gyro/motor随油门的频谱图)
│ │ └──> rpmFilterGyro
│ ├──> // apply static notch filters and software lowpass filters
│ │ ├──> gyroADCf = gyro.notchFilter1ApplyFn((filter_t *)&gyro.notchFilter1[axis], gyroADCf); // 一阶带通
│ │ ├──> gyroADCf = gyro.notchFilter2ApplyFn((filter_t *)&gyro.notchFilter2[axis], gyroADCf); // 二阶带通
│ │ └──> gyroADCf = gyro.lowpassFilterApplyFn((filter_t *)&gyro.lowpassFilter[axis], gyroADCf); // 低通滤波
│ ├──> <USE_DYN_NOTCH_FILTER><isDynNotchActive()>
│ │ ├──> dynNotchPush(axis, gyroADCf);
│ │ ├──> gyroADCf = dynNotchFilter(axis, gyroADCf); //动态带通滤波
│ └──> gyro.gyroADCf[axis] = gyroADCf;
└──> gyro.sampleCount = 0;
gyro_filter_impl.c
├──> rpm_filter.c/h
├──> filter.c/h
├──> dyn_notch.c/h
└──> dyn_notch_filter.c/h
滤波类型
- 【单一滤波方法】slewFilter <from filter.c/h>
- 【单一滤波方法】simpleLowpassFilter <from filter.c/h>
- 【单一滤波方法】laggedMovingAverage <from filter.c/h>
- 【单一滤波方法】pt1Filter <from filter.c/h>
- 【单一滤波方法】pt2Filter <from filter.c/h>
- 【单一滤波方法】pt3Filter <from filter.c/h>
- 【支持多种滤波方法】 biquadFilter <from filter.c/h>
- 【支持多种滤波混合】dynNotchFilter <from dyn_notch_filter.c/h>
- 【支持多种滤波混合】rpmFilter <from rpm_filter.c/h>
1. slewFilter
中文翻译:回转过滤器
起作用就是当input参数超过threshold,过滤掉变化量不大于slewLimit的input(正反向等效)。
typedef struct slewFilter_s
float state;
float slewLimit;
float threshold;
slewFilter_t;
void slewFilterInit(slewFilter_t *filter, float slewLimit, float threshold);
float slewFilterApply(slewFilter_t *filter, float input);
2. simpleLowpassFilter
中文翻译:简单低通滤波器
Low-pass_filter的概念来自物理学基尔霍夫定律。通常滤波需要采用否点运算方法,如下表达式:
// LPF: Y(n) = (1-ß)*Y(n-1) + (ß*X(n))) = Y(n-1) - (ß*(Y(n-1)-X(n)));
// ß = 0.125
而在很多不带浮点运算协处理的CPU,尤其是MCU上,我们需要简化算法(尽量采用整形的移位),详见Fixed-point_arithmetic。
这里的重点是将变量范围尽量放在合适的区间(Fixed Point/Range),以免丢失一些细节。BetaFlight的simpleLowpassFilter就是采用这种处理方式。
对细节比较纠结的朋友可以看下以下两个链接,会更加容易理解!
【1】A simple digital low-pass filter in C
【2】A lowpass filter algorithm for small controllers without multiplier
【3】Fixed Point Scaling – Low Pass Filter example
typedef struct simpleLowpassFilter_s
int32_t fp;
int32_t beta;
int32_t fpShift;
simpleLowpassFilter_t;
int32_t simpleLPFilterUpdate(simpleLowpassFilter_t *filter, int32_t newVal);
void simpleLPFilterInit(simpleLowpassFilter_t *filter, int32_t beta, int32_t fpShift);
3. laggedMovingAverage
中文翻译:滞后移动平均
lagged Moving Average filter 主要基于之前的数据,做线性平滑,去除类似白噪声等干扰。
typedef struct laggedMovingAverage_s
uint16_t movingWindowIndex;
uint16_t windowSize;
float movingSum;
float *buf;
bool primed;
laggedMovingAverage_t;
void laggedMovingAverageInit(laggedMovingAverage_t *filter, uint16_t windowSize, float *buf);
float laggedMovingAverageUpdate(laggedMovingAverage_t *filter, float input);
4. pt1Filter
来自Low-pass_filter公式:
y i = x i ( Δ T R C + Δ T ) ⏞ Input Contribution + y i − 1 ( R C R C + Δ T ) ⏞ Inertia from previous Output y_i = x_i\\overbrace(\\cfrac\\varDelta TRC + \\varDelta T)^\\textInput Contribution + y_i-1\\overbrace(\\cfracRCRC + \\varDelta T)^\\textInertia from previous Output yi=xi(RC+ΔTΔT) Input Contribution+yi−1(RC+ΔTRC) Inertia from previous Output
其中pt1FilterGain函数根据下面表达式解决pt1FilterInit和pt1FilterUpdateCutoff的k值。
R C = 1 2 π f c RC = \\cfrac12\\pi f_c RC=2πfc1
α = Δ T R C + Δ T \\alpha = \\cfrac\\varDelta TRC + \\varDelta T α=RC+ΔTΔT
typedef struct pt1Filter_s
float state;
float k;
pt1Filter_t;
float pt1FilterGain(float f_cut, float dT);
void pt1FilterInit(pt1Filter_t *filter, float k);
void pt1FilterUpdateCutoff(pt1Filter_t *filter, float k);
float pt1FilterApply(pt1Filter_t *filter, float input);
5. pt2Filter
来自Passive Low Pass RC Filters公式:
f ( − 3 d B ) = f c 2 ( 1 / 2 ) − 1 f_(-3dB) = f_c \\sqrt2^(1/2)-1 f(−3dB)=fc2(1/2)−1
R
C
=
1
2
π
f
(
−
3
d
B
)
RC = \\cfrac12\\pi f_(-3dB)
RC=2πf(−3dB)1
typedef struct pt2Filter_s
float state;
float state1;
float k;
pt2Filter_t;
float pt2FilterGain(float f_cut, float dT);
void pt2FilterInit(pt2Filter_t *filter, float k);
void pt2FilterUpdateCutoff(pt2Filter_t *filter, float k);
float pt2FilterApply(pt2Filter_t *filter, float input);
6. pt3Filter
与pt2Filter类似,当n=3时,就是三阶RC滤波。
f ( − 3 d B ) = f c 2 ( 1 / n ) − 1 f_(-3dB) = f_c \\sqrt2^(1/n)-1 f(−3dB)=fc2(1/n)−1
R C = 1 2 π f ( − 3 d B ) RC = \\cfrac12\\pi f_(-3dB) RC=2πf(−3dB)1
typedef struct pt3Filter_s
float state;
float state1;
float state2;
float k;
pt3Filter_t;
float pt3FilterGain(float f_cut, float dT);
void pt3FilterInit(pt3Filter_t *filter, float k);
void pt3FilterUpdateCutoff(pt3Filter_t *filter, float k);
float pt3FilterApply(pt3Filter_t *filter, float input);
7. biquadFilter
7.1 biquad类型
biquad Filter在BetaFlight代码实现上,有三种类型:
- 低通滤波(FILTER_LPF)
- 高通滤波(FILTER_BPF)
- 带通滤波(FILTER_NOTCH)
typedef enum
FILTER_LPF, // 2nd order Butterworth section
FILTER_NOTCH,
FILTER_BPF,
biquadFilterType_e;
注:提供一个可以看波形的链接,感受下不同类型的效果图。
7.2 biquad滤波方式
- biquadFilterApplyDF1采用Direct form 1
- biquadFilterApplyDF1Weighted采用Direct form 1 + Weight(类似simpleLowpassFilter)
注:Wiki中描述到“higher-order filters are typically implemented as serially-cascaded biquad sections (and a first-order filter if necessary)”。 - biquadFilterApply采用Transposed direct form 2
Direct form 1
Transposed direct form 2
7.3 biquad滤波接口
/* this holds the data required to update samples thru a filter */
typedef struct biquadFilter_s
float b0, b1, b2, a1, a2;
float x1, x2, y1, y2;
float weight;
biquadFilter_t;
void biquadFilterInitLPF(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate);
void biquadFilterInit(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate, float Q, biquadFilterType_e filterType, float weight);
void biquadFilterUpdate(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate, float Q, biquadFilterType_e filterType, float weight);
void biquadFilterUpdateLPF(biquadFilter_t *filter, float filterFreq, uint32_t refreshRate);
float biquadFilterApplyDF1(biquadFilter_t *filter, float input);
float biquadFilterApplyDF1Weighted(biquadFilter_t *filter, float input);
float biquadFilterApply(biquadFilter_t *filter, float input);
float filterGetNotchQ(float centerFreq, float cutoffFreq);
8. dynNotchFilter
其采用biquadFilter + FILTER_NOTCH(带通滤波) + biquadFilterApplyDF1(Direct form 1)的方式对gyro数据进行带通滤波。主要应用在BetaFlight模块设计之十三:Gyro过滤任务分析。
typedef struct dynNotchConfig_s
uint16_t dyn_notch_min_hz;
uint16_t dyn_notch_max_hz;
uint16_t dyn_notch_q;
uint8_t dyn_notch_count;
dynNotchConfig_t;
void dynNotchInit(const dynNotchConfig_t *config, const timeUs_t targetLooptimeUs);
void dynNotchPush(const int axis, const float sample);
void dynNotchUpdate(void);
float dynNotchFilter(const int axis, float value);
bool isDynNotchActive(void);
int getMaxFFT(void);
void resetMaxFFT(void);
9. rpmFilter
rpmFilter滤波器里面使用到多种滤波技术
- pt1Filter
- rpmNotchFilter:biquadFilter + FILTER_NOTCH(带通滤波) + biquadFilterApplyDF1Weighted(Direct form 1 + Weight)的方式对motor数据进行带通滤波。
上述rpmFilter的主要目的是过滤掉电机固有振动频率,以及振动随油门变化而变化的振动频率带来的干扰。
注:因为本机的电调没有这部分功能,从平铺图上很容易发现机架和电机的振动干扰。
typedef struct rpmFilterConfig_s
uint8_t rpm_filter_harmonics; // how many harmonics should be covered with notches? 0 means filter off
uint8_t rpm_filter_min_hz; // minimum frequency of the notches
uint16_t rpm_filter_fade_range_hz; // range in which to gradually turn off notches down to minHz
uint16_t rpm_filter_q; BetaFlight模块设计之二十六:接收机任务分析
BetaFlight模块设计之二十八:MainPidLoop任务分析