Skip to content

Commit

Permalink
Merge pull request #10177 from iNavFlight/dzikuvx-lulu-improvements
Browse files Browse the repository at this point in the history
Gyro LULU smoother as gyro filter alternative
  • Loading branch information
DzikuVx authored Jun 24, 2024
2 parents 5553190 + b1b787d commit 2fe9cc2
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 18 deletions.
12 changes: 11 additions & 1 deletion docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,16 @@ Specifies the type of the software LPF of the gyro signals.

---

### gyro_lulu_sample_count

Gyro lulu sample count, in number of samples.

| Default | Min | Max |
| --- | --- | --- |
| 3 | | 15 |

---

### gyro_main_lpf_hz

Software based gyro main lowpass filter. Value is cutoff frequency (Hz)
Expand Down Expand Up @@ -2318,7 +2328,7 @@ This is the main loop time (in us). Changing this affects PID effect with some P

| Default | Min | Max |
| --- | --- | --- |
| 1000 | | 9000 |
| 500 | | 9000 |

---

Expand Down
2 changes: 2 additions & 0 deletions src/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ main_sources(COMMON_SRC
common/gps_conversion.h
common/log.c
common/log.h
common/lulu.c
common/lulu.h
common/maths.c
common/maths.h
common/memory.c
Expand Down
8 changes: 6 additions & 2 deletions src/main/common/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
#include "platform.h"

#include "common/filter.h"
#include "common/lulu.h"
#include "common/maths.h"
#include "common/utils.h"
#include "common/time.h"

// NULL filter
float nullFilterApply(void *filter, float input)
{
Expand Down Expand Up @@ -326,6 +326,8 @@ void initFilter(const uint8_t filterType, filter_t *filter, const float cutoffFr
pt2FilterInit(&filter->pt2, pt2FilterGain(cutoffFrequency, dT));
} if (filterType == FILTER_PT3) {
pt3FilterInit(&filter->pt3, pt3FilterGain(cutoffFrequency, dT));
} if (filterType == FILTER_LULU) {
luluFilterInit(&filter->lulu, cutoffFrequency);
} else {
biquadFilterInitLPF(&filter->biquad, cutoffFrequency, refreshRate);
}
Expand All @@ -341,8 +343,10 @@ void assignFilterApplyFn(uint8_t filterType, float cutoffFrequency, filterApplyF
*applyFn = (filterApplyFnPtr) pt2FilterApply;
} if (filterType == FILTER_PT3) {
*applyFn = (filterApplyFnPtr) pt3FilterApply;
} if (filterType == FILTER_LULU) {
*applyFn = (filterApplyFnPtr) luluFilterApply;
} else {
*applyFn = (filterApplyFnPtr) biquadFilterApply;
}
}
}
}
8 changes: 6 additions & 2 deletions src/main/common/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

#pragma once

#include "lulu.h"

typedef struct rateLimitFilter_s {
float state;
} rateLimitFilter_t;
Expand Down Expand Up @@ -50,13 +52,15 @@ typedef union {
pt1Filter_t pt1;
pt2Filter_t pt2;
pt3Filter_t pt3;
luluFilter_t lulu;
} filter_t;

typedef enum {
FILTER_PT1 = 0,
FILTER_BIQUAD,
FILTER_PT2,
FILTER_PT3
FILTER_PT3,
FILTER_LULU
} filterType_e;

typedef enum {
Expand Down Expand Up @@ -134,4 +138,4 @@ void alphaBetaGammaFilterInit(alphaBetaGammaFilter_t *filter, float alpha, float
float alphaBetaGammaFilterApply(alphaBetaGammaFilter_t *filter, float input);

void initFilter(uint8_t filterType, filter_t *filter, float cutoffFrequency, uint32_t refreshRate);
void assignFilterApplyFn(uint8_t filterType, float cutoffFrequency, filterApplyFnPtr *applyFn);
void assignFilterApplyFn(uint8_t filterType, float cutoffFrequency, filterApplyFnPtr *applyFn);
178 changes: 178 additions & 0 deletions src/main/common/lulu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#include "lulu.h"

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

#include "platform.h"

#include "common/filter.h"
#include "common/maths.h"
#include "common/utils.h"

#ifdef __ARM_ACLE
#include <arm_acle.h>
#endif /* __ARM_ACLE */
#include <fenv.h>

void luluFilterInit(luluFilter_t *filter, int N)
{
filter->N = constrain(N, 1, 15);
filter->windowSize = filter->N * 2 + 1;
filter->windowBufIndex = 0;

memset(filter->luluInterim, 0, sizeof(float) * (filter->windowSize));
memset(filter->luluInterimB, 0, sizeof(float) * (filter->windowSize));
}

FAST_CODE float fixRoad(float *series, float *seriesB, int index, int filterN, int windowSize)
{
register float curVal = 0;
register float curValB = 0;
for (int N = 1; N <= filterN; N++)
{
int indexNeg = (index + windowSize - 2 * N) % windowSize;
register int curIndex = (indexNeg + 1) % windowSize;
register float prevVal = series[indexNeg];
register float prevValB = seriesB[indexNeg];
register int indexPos = (curIndex + N) % windowSize;
for (int i = windowSize - 2 * N; i < windowSize - N; i++)
{
if (indexPos >= windowSize)
{
indexPos = 0;
}
if (curIndex >= windowSize)
{
curIndex = 0;
}
// curIndex = (2 - 1) % 3 = 1
curVal = series[curIndex];
curValB = seriesB[curIndex];
register float nextVal = series[indexPos];
register float nextValB = seriesB[indexPos];
// onbump (s, 1, 1, 3)
// if(onBump(series, curIndex, N, windowSize))
if (prevVal < curVal && curVal > nextVal)
{
float maxValue = MAX(prevVal, nextVal);

series[curIndex] = maxValue;
register int k = curIndex;
for (int j = 1; j < N; j++)
{
if (++k >= windowSize)
{
k = 0;
}
series[k] = maxValue;
}
}

if (prevValB < curValB && curValB > nextValB)
{
float maxValue = MAX(prevValB, nextValB);

curVal = maxValue;
seriesB[curIndex] = maxValue;
register int k = curIndex;
for (int j = 1; j < N; j++)
{
if (++k >= windowSize)
{
k = 0;
}
seriesB[k] = maxValue;
}
}
prevVal = curVal;
prevValB = curValB;
curIndex++;
indexPos++;
}

curIndex = (indexNeg + 1) % windowSize;
prevVal = series[indexNeg];
prevValB = seriesB[indexNeg];
indexPos = (curIndex + N) % windowSize;
for (int i = windowSize - 2 * N; i < windowSize - N; i++)
{
if (indexPos >= windowSize)
{
indexPos = 0;
}
if (curIndex >= windowSize)
{
curIndex = 0;
}
// curIndex = (2 - 1) % 3 = 1
curVal = series[curIndex];
curValB = seriesB[curIndex];
register float nextVal = series[indexPos];
register float nextValB = seriesB[indexPos];

if (prevVal > curVal && curVal < nextVal)
{
float minValue = MIN(prevVal, nextVal);

curVal = minValue;
series[curIndex] = minValue;
register int k = curIndex;
for (int j = 1; j < N; j++)
{
if (++k >= windowSize)
{
k = 0;
}
series[k] = minValue;
}
}

if (prevValB > curValB && curValB < nextValB)
{
float minValue = MIN(prevValB, nextValB);
curValB = minValue;
seriesB[curIndex] = minValue;
register int k = curIndex;
for (int j = 1; j < N; j++)
{
if (++k >= windowSize)
{
k = 0;
}
seriesB[k] = minValue;
}
}
prevVal = curVal;
prevValB = curValB;
curIndex++;
indexPos++;
}
}
return (curVal - curValB) / 2;
}

FAST_CODE float luluFilterPartialApply(luluFilter_t *filter, float input)
{
// This is the value N of the LULU filter.
register int filterN = filter->N;
// This is the total window size for the rolling buffer
register int filterWindow = filter->windowSize;

register int windowIndex = filter->windowBufIndex;
register float inputVal = input;
register int newIndex = (windowIndex + 1) % filterWindow;
filter->windowBufIndex = newIndex;
filter->luluInterim[windowIndex] = inputVal;
filter->luluInterimB[windowIndex] = -inputVal;
return fixRoad(filter->luluInterim, filter->luluInterimB, windowIndex, filterN, filterWindow);
}

FAST_CODE float luluFilterApply(luluFilter_t *filter, float input)
{
// This is the UL filter
float resultA = luluFilterPartialApply(filter, input);
// We use the median interpretation of this filter to remove bias in the output
return resultA;
}
14 changes: 14 additions & 0 deletions src/main/common/lulu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

// Max N = 15
typedef struct
{
int windowSize;
int windowBufIndex;
int N;
float luluInterim[32] __attribute__((aligned(128)));
float luluInterimB[32];
} luluFilter_t;

void luluFilterInit(luluFilter_t *filter, int N);
float luluFilterApply(luluFilter_t *filter, float input);
4 changes: 2 additions & 2 deletions src/main/fc/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ void validateAndFixConfig(void)
{

#ifdef USE_ADAPTIVE_FILTER
// gyroConfig()->adaptiveFilterMinHz has to be at least 5 units lower than gyroConfig()->gyro_main_lpf_hz
if (gyroConfig()->adaptiveFilterMinHz + 5 > gyroConfig()->gyro_main_lpf_hz) {
// gyroConfig()->adaptiveFilterMinHz has to be at least 5 units lower than gyroConfig()->gyro_main_lpf_hz
if (gyroConfig()->adaptiveFilterMinHz + 5 > gyroConfig()->gyro_main_lpf_hz) {
gyroConfigMutable()->adaptiveFilterMinHz = gyroConfig()->gyro_main_lpf_hz - 5;
}
//gyroConfig()->adaptiveFilterMaxHz has to be at least 5 units higher than gyroConfig()->gyro_main_lpf_hz
Expand Down
11 changes: 8 additions & 3 deletions src/main/fc/settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ tables:
- name: filter_type
values: ["PT1", "BIQUAD"]
- name: filter_type_full
values: ["PT1", "BIQUAD", "PT2", "PT3"]
values: ["PT1", "BIQUAD", "PT2", "PT3", "LULU"]
- name: log_level
values: ["ERROR", "WARNING", "INFO", "VERBOSE", "DEBUG"]
- name: iterm_relax
Expand Down Expand Up @@ -192,7 +192,7 @@ tables:
values: ["SHARED_LOW", "SHARED_HIGH", "LOW", "HIGH"]
enum: led_pin_pwm_mode_e
- name: gyro_filter_mode
values: ["STATIC", "DYNAMIC", "ADAPTIVE"]
values: ["STATIC", "DYNAMIC", "ADAPTIVE", "LULU"]
enum: gyroFilterType_e
- name: headtracker_dev_type
values: ["NONE", "SERIAL", "MSP"]
Expand All @@ -219,13 +219,18 @@ groups:
members:
- name: looptime
description: "This is the main loop time (in us). Changing this affects PID effect with some PID controllers (see PID section for details). A very conservative value of 3500us/285Hz should work for everyone. Setting it to zero does not limit loop time, so it will go as fast as possible."
default_value: 1000
default_value: 500
max: 9000
- name: gyro_anti_aliasing_lpf_hz
description: "Gyro processing anti-aliasing filter cutoff frequency. In normal operation this filter setting should never be changed. In Hz"
default_value: 250
field: gyro_anti_aliasing_lpf_hz
max: 1000
- name: gyro_lulu_sample_count
description: "Gyro lulu sample count, in number of samples."
default_value: 3
field: gyroLuluSampleCount
max: 15
- name: gyro_main_lpf_hz
description: "Software based gyro main lowpass filter. Value is cutoff frequency (Hz)"
default_value: 60
Expand Down
Loading

0 comments on commit 2fe9cc2

Please sign in to comment.