mirror of
https://github.com/iNavFlight/inav.git
synced 2025-07-25 17:25:18 +03:00
Added notch filter from betaflight (#592)
This commit is contained in:
parent
c806a7e568
commit
daf9ba7dde
6 changed files with 94 additions and 57 deletions
|
@ -20,59 +20,18 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "common/axis.h"
|
|
||||||
#include "common/filter.h"
|
#include "common/filter.h"
|
||||||
#include "common/maths.h"
|
#include "common/maths.h"
|
||||||
|
|
||||||
#include "drivers/gyro_sync.h"
|
#define BIQUAD_BANDWIDTH 1.9f /* bandwidth in octaves */
|
||||||
|
#define BIQUAD_Q 1.0f / sqrtf(2.0f) /* quality factor - butterworth*/
|
||||||
#define BIQUAD_Q (1.0f / 1.41421356f) /* quality factor - butterworth (1 / sqrt(2)) */
|
|
||||||
#define M_PI_FLOAT 3.14159265358979323846f
|
|
||||||
|
|
||||||
void biquadFilterInit(biquadFilter_t *filter, uint32_t filterCutFreq, uint32_t refreshRate)
|
|
||||||
{
|
|
||||||
// setup variables
|
|
||||||
const float sampleRate = 1.0f / ((float)refreshRate * 0.000001f);
|
|
||||||
const float omega = 2 * M_PIf * ((float)filterCutFreq) / sampleRate;
|
|
||||||
const float sn = sin_approx(omega);
|
|
||||||
const float cs = cos_approx(omega);
|
|
||||||
const float alpha = sn / (2 * BIQUAD_Q);
|
|
||||||
|
|
||||||
float a0, a1, a2, b0, b1, b2;
|
|
||||||
|
|
||||||
b0 = (1 - cs) / 2;
|
|
||||||
b1 = 1 - cs;
|
|
||||||
b2 = (1 - cs) / 2;
|
|
||||||
a0 = 1 + alpha;
|
|
||||||
a1 = -2 * cs;
|
|
||||||
a2 = 1 - alpha;
|
|
||||||
|
|
||||||
// precompute the coefficients
|
|
||||||
filter->b0 = b0 / a0;
|
|
||||||
filter->b1 = b1 / a0;
|
|
||||||
filter->b2 = b2 / a0;
|
|
||||||
filter->a1 = a1 / a0;
|
|
||||||
filter->a2 = a2 / a0;
|
|
||||||
|
|
||||||
// zero initial samples
|
|
||||||
filter->d1 = filter->d2 = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Computes a biquad_t filter on a sample */
|
|
||||||
float biquadFilterApply(biquadFilter_t *filter, float input)
|
|
||||||
{
|
|
||||||
const float result = filter->b0 * input + filter->d1;
|
|
||||||
filter->d1 = filter->b1 * input - filter->a1 * result + filter->d2;
|
|
||||||
filter->d2 = filter->b2 * input - filter->a2 * result;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// PT1 Low Pass filter
|
// PT1 Low Pass filter
|
||||||
|
|
||||||
// f_cut = cutoff frequency
|
// f_cut = cutoff frequency
|
||||||
void pt1FilterInit(pt1Filter_t *filter, uint8_t f_cut, float dT)
|
void pt1FilterInit(pt1Filter_t *filter, uint8_t f_cut, float dT)
|
||||||
{
|
{
|
||||||
filter->RC = 1.0f / ( 2.0f * M_PI_FLOAT * f_cut );
|
filter->RC = 1.0f / ( 2.0f * M_PIf * f_cut );
|
||||||
filter->dT = dT;
|
filter->dT = dT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,14 +41,15 @@ float pt1FilterApply(pt1Filter_t *filter, float input)
|
||||||
return filter->state;
|
return filter->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
float pt1FilterApply4(pt1Filter_t *filter, float input, float f_cut, float dT)
|
float pt1FilterApply4(pt1Filter_t *filter, float input, uint16_t f_cut, float dT)
|
||||||
{
|
{
|
||||||
// Pre calculate and store RC
|
// Pre calculate and store RC
|
||||||
if (!filter->RC) {
|
if (!filter->RC) {
|
||||||
filter->RC = 1.0f / ( 2.0f * (float)M_PI * f_cut );
|
filter->RC = 1.0f / ( 2.0f * M_PIf * f_cut );
|
||||||
|
filter->dT = dT;
|
||||||
}
|
}
|
||||||
|
|
||||||
filter->state = filter->state + dT / (filter->RC + dT) * (input - filter->state);
|
filter->state = filter->state + filter->dT / (filter->RC + filter->dT) * (input - filter->state);
|
||||||
return filter->state;
|
return filter->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +77,67 @@ float rateLimitFilterApply4(rateLimitFilter_t *filter, float input, float rate_l
|
||||||
return filter->state;
|
return filter->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIR filter
|
float filterGetNotchQ(uint16_t centerFreq, uint16_t cutoff)
|
||||||
|
{
|
||||||
|
const float octaves = log2f((float)centerFreq / (float)cutoff) * 2;
|
||||||
|
return sqrtf(powf(2, octaves)) / (powf(2, octaves) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets up a biquad Filter
|
||||||
|
void biquadFilterInitLPF(biquadFilter_t *filter, uint16_t filterFreq, uint32_t refreshRate)
|
||||||
|
{
|
||||||
|
biquadFilterInit(filter, filterFreq, refreshRate, BIQUAD_Q, FILTER_LPF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void biquadFilterInit(biquadFilter_t *filter, uint16_t filterFreq, uint32_t refreshRate, float Q, biquadFilterType_e filterType)
|
||||||
|
{
|
||||||
|
// setup variables
|
||||||
|
const float sampleRate = 1.0f / ((float)refreshRate * 0.000001f);
|
||||||
|
const float omega = 2.0f * M_PIf * ((float)filterFreq) / sampleRate;
|
||||||
|
const float sn = sin_approx(omega);
|
||||||
|
const float cs = cos_approx(omega);
|
||||||
|
const float alpha = sn / (2 * Q);
|
||||||
|
|
||||||
|
float b0, b1, b2;
|
||||||
|
switch (filterType) {
|
||||||
|
case FILTER_LPF:
|
||||||
|
b0 = (1 - cs) / 2;
|
||||||
|
b1 = 1 - cs;
|
||||||
|
b2 = (1 - cs) / 2;
|
||||||
|
break;
|
||||||
|
case FILTER_NOTCH:
|
||||||
|
b0 = 1;
|
||||||
|
b1 = -2 * cs;
|
||||||
|
b2 = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const float a0 = 1 + alpha;
|
||||||
|
const float a1 = -2 * cs;
|
||||||
|
const float a2 = 1 - alpha;
|
||||||
|
|
||||||
|
// precompute the coefficients
|
||||||
|
filter->b0 = b0 / a0;
|
||||||
|
filter->b1 = b1 / a0;
|
||||||
|
filter->b2 = b2 / a0;
|
||||||
|
filter->a1 = a1 / a0;
|
||||||
|
filter->a2 = a2 / a0;
|
||||||
|
|
||||||
|
// zero initial samples
|
||||||
|
filter->d1 = filter->d2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes a biquad_t filter on a sample
|
||||||
|
float biquadFilterApply(biquadFilter_t *filter, float input)
|
||||||
|
{
|
||||||
|
const float result = filter->b0 * input + filter->d1;
|
||||||
|
filter->d1 = filter->b1 * input - filter->a1 * result + filter->d2;
|
||||||
|
filter->d2 = filter->b2 * input - filter->a2 * result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIR filter
|
||||||
|
*/
|
||||||
void firFilterInit2(firFilter_t *filter, float *buf, uint8_t bufLength, const float *coeffs, uint8_t coeffsLength)
|
void firFilterInit2(firFilter_t *filter, float *buf, uint8_t bufLength, const float *coeffs, uint8_t coeffsLength)
|
||||||
{
|
{
|
||||||
filter->buf = buf;
|
filter->buf = buf;
|
||||||
|
@ -127,6 +147,10 @@ void firFilterInit2(firFilter_t *filter, float *buf, uint8_t bufLength, const fl
|
||||||
memset(filter->buf, 0, sizeof(float) * filter->bufLength);
|
memset(filter->buf, 0, sizeof(float) * filter->bufLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIR filter initialisation
|
||||||
|
* If FIR filter is just used for averaging, coeffs can be set to NULL
|
||||||
|
*/
|
||||||
void firFilterInit(firFilter_t *filter, float *buf, uint8_t bufLength, const float *coeffs)
|
void firFilterInit(firFilter_t *filter, float *buf, uint8_t bufLength, const float *coeffs)
|
||||||
{
|
{
|
||||||
firFilterInit2(filter, buf, bufLength, coeffs, bufLength);
|
firFilterInit2(filter, buf, bufLength, coeffs, bufLength);
|
||||||
|
@ -138,7 +162,7 @@ void firFilterUpdate(firFilter_t *filter, float input)
|
||||||
filter->buf[0] = input;
|
filter->buf[0] = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
float firFilterApply(firFilter_t *filter)
|
float firFilterApply(const firFilter_t *filter)
|
||||||
{
|
{
|
||||||
float ret = 0.0f;
|
float ret = 0.0f;
|
||||||
for (int ii = 0; ii < filter->coeffsLength; ++ii) {
|
for (int ii = 0; ii < filter->coeffsLength; ++ii) {
|
||||||
|
|
|
@ -33,6 +33,17 @@ typedef struct biquadFilter_s {
|
||||||
float d1, d2;
|
float d1, d2;
|
||||||
} biquadFilter_t;
|
} biquadFilter_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FILTER_PT1 = 0,
|
||||||
|
FILTER_BIQUAD,
|
||||||
|
FILTER_FIR,
|
||||||
|
} filterType_e;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FILTER_LPF,
|
||||||
|
FILTER_NOTCH
|
||||||
|
} biquadFilterType_e;
|
||||||
|
|
||||||
typedef struct firFilter_s {
|
typedef struct firFilter_s {
|
||||||
float *buf;
|
float *buf;
|
||||||
const float *coeffs;
|
const float *coeffs;
|
||||||
|
@ -41,17 +52,19 @@ typedef struct firFilter_s {
|
||||||
} firFilter_t;
|
} firFilter_t;
|
||||||
|
|
||||||
float pt1FilterApply(pt1Filter_t *filter, float input);
|
float pt1FilterApply(pt1Filter_t *filter, float input);
|
||||||
float pt1FilterApply4(pt1Filter_t *filter, float input, float f_cut, float dt);
|
float pt1FilterApply4(pt1Filter_t *filter, float input, uint16_t f_cut, float dt);
|
||||||
void pt1FilterReset(pt1Filter_t *filter, float input);
|
void pt1FilterReset(pt1Filter_t *filter, float input);
|
||||||
|
|
||||||
void rateLimitFilterInit(rateLimitFilter_t *filter);
|
void rateLimitFilterInit(rateLimitFilter_t *filter);
|
||||||
float rateLimitFilterApply4(rateLimitFilter_t *filter, float input, float rate_limit, float dT);
|
float rateLimitFilterApply4(rateLimitFilter_t *filter, float input, float rate_limit, float dT);
|
||||||
|
|
||||||
void biquadFilterInit(biquadFilter_t *filter, uint32_t filterCutFreq, uint32_t refreshRate);
|
void biquadFilterInitLPF(biquadFilter_t *filter, uint16_t filterFreq, uint32_t refreshRate);
|
||||||
|
void biquadFilterInit(biquadFilter_t *filter, uint16_t filterFreq, uint32_t refreshRate, float Q, biquadFilterType_e filterType);
|
||||||
float biquadFilterApply(biquadFilter_t *filter, float sample);
|
float biquadFilterApply(biquadFilter_t *filter, float sample);
|
||||||
|
float filterGetNotchQ(uint16_t centerFreq, uint16_t cutoff);
|
||||||
|
|
||||||
void firFilterInit(firFilter_t *filter, float *buf, uint8_t bufLength, const float *coeffs);
|
void firFilterInit(firFilter_t *filter, float *buf, uint8_t bufLength, const float *coeffs);
|
||||||
void firFilterInit2(firFilter_t *filter, float *buf, uint8_t bufLength, const float *coeffs, uint8_t coeffsLength);
|
void firFilterInit2(firFilter_t *filter, float *buf, uint8_t bufLength, const float *coeffs, uint8_t coeffsLength);
|
||||||
void firFilterUpdate(firFilter_t *filter, float input);
|
void firFilterUpdate(firFilter_t *filter, float input);
|
||||||
float firFilterApply(firFilter_t *filter);
|
float firFilterApply(const firFilter_t *filter);
|
||||||
|
|
||||||
|
|
|
@ -499,7 +499,7 @@ void filterRc(bool isRXDataNew)
|
||||||
|
|
||||||
// Calculate average cycle time (1Hz LPF on cycle time)
|
// Calculate average cycle time (1Hz LPF on cycle time)
|
||||||
if (!filterInitialised) {
|
if (!filterInitialised) {
|
||||||
biquadFilterInit(&filteredCycleTimeState, 1, gyro.targetLooptime);
|
biquadFilterInitLPF(&filteredCycleTimeState, 1, gyro.targetLooptime);
|
||||||
filterInitialised = true;
|
filterInitialised = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -921,7 +921,7 @@ void filterServos(void)
|
||||||
// Initialize servo lowpass filter (servos are calculated at looptime rate)
|
// Initialize servo lowpass filter (servos are calculated at looptime rate)
|
||||||
if (!servoFilterIsSet) {
|
if (!servoFilterIsSet) {
|
||||||
for (servoIdx = 0; servoIdx < MAX_SUPPORTED_SERVOS; servoIdx++) {
|
for (servoIdx = 0; servoIdx < MAX_SUPPORTED_SERVOS; servoIdx++) {
|
||||||
biquadFilterInit(&servoFitlerState[servoIdx], mixerConfig->servo_lowpass_freq, gyro.targetLooptime);
|
biquadFilterInitLPF(&servoFitlerState[servoIdx], mixerConfig->servo_lowpass_freq, gyro.targetLooptime);
|
||||||
}
|
}
|
||||||
|
|
||||||
servoFilterIsSet = true;
|
servoFilterIsSet = true;
|
||||||
|
|
|
@ -58,7 +58,7 @@ void accInit(uint32_t targetLooptime)
|
||||||
accTargetLooptime = targetLooptime;
|
accTargetLooptime = targetLooptime;
|
||||||
if (accLpfCutHz) {
|
if (accLpfCutHz) {
|
||||||
for (int axis = 0; axis < 3; axis++) {
|
for (int axis = 0; axis < 3; axis++) {
|
||||||
biquadFilterInit(&accFilter[axis], accLpfCutHz, accTargetLooptime);
|
biquadFilterInitLPF(&accFilter[axis], accLpfCutHz, accTargetLooptime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,7 @@ void setAccelerationFilter(uint8_t initialAccLpfCutHz)
|
||||||
accLpfCutHz = initialAccLpfCutHz;
|
accLpfCutHz = initialAccLpfCutHz;
|
||||||
if (accTargetLooptime) {
|
if (accTargetLooptime) {
|
||||||
for (int axis = 0; axis < XYZ_AXIS_COUNT; axis++) {
|
for (int axis = 0; axis < XYZ_AXIS_COUNT; axis++) {
|
||||||
biquadFilterInit(&accFilter[axis], accLpfCutHz, accTargetLooptime);
|
biquadFilterInitLPF(&accFilter[axis], accLpfCutHz, accTargetLooptime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ void gyroInit(void)
|
||||||
{
|
{
|
||||||
if (gyroSoftLpfHz && gyro.targetLooptime) { // Initialisation needs to happen once samplingrate is known
|
if (gyroSoftLpfHz && gyro.targetLooptime) { // Initialisation needs to happen once samplingrate is known
|
||||||
for (int axis = 0; axis < 3; axis++) {
|
for (int axis = 0; axis < 3; axis++) {
|
||||||
biquadFilterInit(&gyroFilterLPF[axis], gyroSoftLpfHz, gyro.targetLooptime);
|
biquadFilterInitLPF(&gyroFilterLPF[axis], gyroSoftLpfHz, gyro.targetLooptime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue