Skip to content

Commit 2fe9cc2

Browse files
authored
Merge pull request #10177 from iNavFlight/dzikuvx-lulu-improvements
Gyro LULU smoother as gyro filter alternative
2 parents 5553190 + b1b787d commit 2fe9cc2

File tree

10 files changed

+248
-18
lines changed

10 files changed

+248
-18
lines changed

docs/Settings.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1782,6 +1782,16 @@ Specifies the type of the software LPF of the gyro signals.
17821782

17831783
---
17841784

1785+
### gyro_lulu_sample_count
1786+
1787+
Gyro lulu sample count, in number of samples.
1788+
1789+
| Default | Min | Max |
1790+
| --- | --- | --- |
1791+
| 3 | | 15 |
1792+
1793+
---
1794+
17851795
### gyro_main_lpf_hz
17861796

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

23192329
| Default | Min | Max |
23202330
| --- | --- | --- |
2321-
| 1000 | | 9000 |
2331+
| 500 | | 9000 |
23222332

23232333
---
23242334

src/main/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ main_sources(COMMON_SRC
3131
common/gps_conversion.h
3232
common/log.c
3333
common/log.h
34+
common/lulu.c
35+
common/lulu.h
3436
common/maths.c
3537
common/maths.h
3638
common/memory.c

src/main/common/filter.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
#include "platform.h"
2424

2525
#include "common/filter.h"
26+
#include "common/lulu.h"
2627
#include "common/maths.h"
2728
#include "common/utils.h"
2829
#include "common/time.h"
29-
3030
// NULL filter
3131
float nullFilterApply(void *filter, float input)
3232
{
@@ -326,6 +326,8 @@ void initFilter(const uint8_t filterType, filter_t *filter, const float cutoffFr
326326
pt2FilterInit(&filter->pt2, pt2FilterGain(cutoffFrequency, dT));
327327
} if (filterType == FILTER_PT3) {
328328
pt3FilterInit(&filter->pt3, pt3FilterGain(cutoffFrequency, dT));
329+
} if (filterType == FILTER_LULU) {
330+
luluFilterInit(&filter->lulu, cutoffFrequency);
329331
} else {
330332
biquadFilterInitLPF(&filter->biquad, cutoffFrequency, refreshRate);
331333
}
@@ -341,8 +343,10 @@ void assignFilterApplyFn(uint8_t filterType, float cutoffFrequency, filterApplyF
341343
*applyFn = (filterApplyFnPtr) pt2FilterApply;
342344
} if (filterType == FILTER_PT3) {
343345
*applyFn = (filterApplyFnPtr) pt3FilterApply;
346+
} if (filterType == FILTER_LULU) {
347+
*applyFn = (filterApplyFnPtr) luluFilterApply;
344348
} else {
345349
*applyFn = (filterApplyFnPtr) biquadFilterApply;
346350
}
347351
}
348-
}
352+
}

src/main/common/filter.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#pragma once
1919

20+
#include "lulu.h"
21+
2022
typedef struct rateLimitFilter_s {
2123
float state;
2224
} rateLimitFilter_t;
@@ -50,13 +52,15 @@ typedef union {
5052
pt1Filter_t pt1;
5153
pt2Filter_t pt2;
5254
pt3Filter_t pt3;
55+
luluFilter_t lulu;
5356
} filter_t;
5457

5558
typedef enum {
5659
FILTER_PT1 = 0,
5760
FILTER_BIQUAD,
5861
FILTER_PT2,
59-
FILTER_PT3
62+
FILTER_PT3,
63+
FILTER_LULU
6064
} filterType_e;
6165

6266
typedef enum {
@@ -134,4 +138,4 @@ void alphaBetaGammaFilterInit(alphaBetaGammaFilter_t *filter, float alpha, float
134138
float alphaBetaGammaFilterApply(alphaBetaGammaFilter_t *filter, float input);
135139

136140
void initFilter(uint8_t filterType, filter_t *filter, float cutoffFrequency, uint32_t refreshRate);
137-
void assignFilterApplyFn(uint8_t filterType, float cutoffFrequency, filterApplyFnPtr *applyFn);
141+
void assignFilterApplyFn(uint8_t filterType, float cutoffFrequency, filterApplyFnPtr *applyFn);

src/main/common/lulu.c

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#include "lulu.h"
2+
3+
#include <stdbool.h>
4+
#include <stdint.h>
5+
#include <string.h>
6+
#include <math.h>
7+
8+
#include "platform.h"
9+
10+
#include "common/filter.h"
11+
#include "common/maths.h"
12+
#include "common/utils.h"
13+
14+
#ifdef __ARM_ACLE
15+
#include <arm_acle.h>
16+
#endif /* __ARM_ACLE */
17+
#include <fenv.h>
18+
19+
void luluFilterInit(luluFilter_t *filter, int N)
20+
{
21+
filter->N = constrain(N, 1, 15);
22+
filter->windowSize = filter->N * 2 + 1;
23+
filter->windowBufIndex = 0;
24+
25+
memset(filter->luluInterim, 0, sizeof(float) * (filter->windowSize));
26+
memset(filter->luluInterimB, 0, sizeof(float) * (filter->windowSize));
27+
}
28+
29+
FAST_CODE float fixRoad(float *series, float *seriesB, int index, int filterN, int windowSize)
30+
{
31+
register float curVal = 0;
32+
register float curValB = 0;
33+
for (int N = 1; N <= filterN; N++)
34+
{
35+
int indexNeg = (index + windowSize - 2 * N) % windowSize;
36+
register int curIndex = (indexNeg + 1) % windowSize;
37+
register float prevVal = series[indexNeg];
38+
register float prevValB = seriesB[indexNeg];
39+
register int indexPos = (curIndex + N) % windowSize;
40+
for (int i = windowSize - 2 * N; i < windowSize - N; i++)
41+
{
42+
if (indexPos >= windowSize)
43+
{
44+
indexPos = 0;
45+
}
46+
if (curIndex >= windowSize)
47+
{
48+
curIndex = 0;
49+
}
50+
// curIndex = (2 - 1) % 3 = 1
51+
curVal = series[curIndex];
52+
curValB = seriesB[curIndex];
53+
register float nextVal = series[indexPos];
54+
register float nextValB = seriesB[indexPos];
55+
// onbump (s, 1, 1, 3)
56+
// if(onBump(series, curIndex, N, windowSize))
57+
if (prevVal < curVal && curVal > nextVal)
58+
{
59+
float maxValue = MAX(prevVal, nextVal);
60+
61+
series[curIndex] = maxValue;
62+
register int k = curIndex;
63+
for (int j = 1; j < N; j++)
64+
{
65+
if (++k >= windowSize)
66+
{
67+
k = 0;
68+
}
69+
series[k] = maxValue;
70+
}
71+
}
72+
73+
if (prevValB < curValB && curValB > nextValB)
74+
{
75+
float maxValue = MAX(prevValB, nextValB);
76+
77+
curVal = maxValue;
78+
seriesB[curIndex] = maxValue;
79+
register int k = curIndex;
80+
for (int j = 1; j < N; j++)
81+
{
82+
if (++k >= windowSize)
83+
{
84+
k = 0;
85+
}
86+
seriesB[k] = maxValue;
87+
}
88+
}
89+
prevVal = curVal;
90+
prevValB = curValB;
91+
curIndex++;
92+
indexPos++;
93+
}
94+
95+
curIndex = (indexNeg + 1) % windowSize;
96+
prevVal = series[indexNeg];
97+
prevValB = seriesB[indexNeg];
98+
indexPos = (curIndex + N) % windowSize;
99+
for (int i = windowSize - 2 * N; i < windowSize - N; i++)
100+
{
101+
if (indexPos >= windowSize)
102+
{
103+
indexPos = 0;
104+
}
105+
if (curIndex >= windowSize)
106+
{
107+
curIndex = 0;
108+
}
109+
// curIndex = (2 - 1) % 3 = 1
110+
curVal = series[curIndex];
111+
curValB = seriesB[curIndex];
112+
register float nextVal = series[indexPos];
113+
register float nextValB = seriesB[indexPos];
114+
115+
if (prevVal > curVal && curVal < nextVal)
116+
{
117+
float minValue = MIN(prevVal, nextVal);
118+
119+
curVal = minValue;
120+
series[curIndex] = minValue;
121+
register int k = curIndex;
122+
for (int j = 1; j < N; j++)
123+
{
124+
if (++k >= windowSize)
125+
{
126+
k = 0;
127+
}
128+
series[k] = minValue;
129+
}
130+
}
131+
132+
if (prevValB > curValB && curValB < nextValB)
133+
{
134+
float minValue = MIN(prevValB, nextValB);
135+
curValB = minValue;
136+
seriesB[curIndex] = minValue;
137+
register int k = curIndex;
138+
for (int j = 1; j < N; j++)
139+
{
140+
if (++k >= windowSize)
141+
{
142+
k = 0;
143+
}
144+
seriesB[k] = minValue;
145+
}
146+
}
147+
prevVal = curVal;
148+
prevValB = curValB;
149+
curIndex++;
150+
indexPos++;
151+
}
152+
}
153+
return (curVal - curValB) / 2;
154+
}
155+
156+
FAST_CODE float luluFilterPartialApply(luluFilter_t *filter, float input)
157+
{
158+
// This is the value N of the LULU filter.
159+
register int filterN = filter->N;
160+
// This is the total window size for the rolling buffer
161+
register int filterWindow = filter->windowSize;
162+
163+
register int windowIndex = filter->windowBufIndex;
164+
register float inputVal = input;
165+
register int newIndex = (windowIndex + 1) % filterWindow;
166+
filter->windowBufIndex = newIndex;
167+
filter->luluInterim[windowIndex] = inputVal;
168+
filter->luluInterimB[windowIndex] = -inputVal;
169+
return fixRoad(filter->luluInterim, filter->luluInterimB, windowIndex, filterN, filterWindow);
170+
}
171+
172+
FAST_CODE float luluFilterApply(luluFilter_t *filter, float input)
173+
{
174+
// This is the UL filter
175+
float resultA = luluFilterPartialApply(filter, input);
176+
// We use the median interpretation of this filter to remove bias in the output
177+
return resultA;
178+
}

src/main/common/lulu.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#pragma once
2+
3+
// Max N = 15
4+
typedef struct
5+
{
6+
int windowSize;
7+
int windowBufIndex;
8+
int N;
9+
float luluInterim[32] __attribute__((aligned(128)));
10+
float luluInterimB[32];
11+
} luluFilter_t;
12+
13+
void luluFilterInit(luluFilter_t *filter, int N);
14+
float luluFilterApply(luluFilter_t *filter, float input);

src/main/fc/config.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ void validateAndFixConfig(void)
192192
{
193193

194194
#ifdef USE_ADAPTIVE_FILTER
195-
// gyroConfig()->adaptiveFilterMinHz has to be at least 5 units lower than gyroConfig()->gyro_main_lpf_hz
196-
if (gyroConfig()->adaptiveFilterMinHz + 5 > gyroConfig()->gyro_main_lpf_hz) {
195+
// gyroConfig()->adaptiveFilterMinHz has to be at least 5 units lower than gyroConfig()->gyro_main_lpf_hz
196+
if (gyroConfig()->adaptiveFilterMinHz + 5 > gyroConfig()->gyro_main_lpf_hz) {
197197
gyroConfigMutable()->adaptiveFilterMinHz = gyroConfig()->gyro_main_lpf_hz - 5;
198198
}
199199
//gyroConfig()->adaptiveFilterMaxHz has to be at least 5 units higher than gyroConfig()->gyro_main_lpf_hz

src/main/fc/settings.yaml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ tables:
119119
- name: filter_type
120120
values: ["PT1", "BIQUAD"]
121121
- name: filter_type_full
122-
values: ["PT1", "BIQUAD", "PT2", "PT3"]
122+
values: ["PT1", "BIQUAD", "PT2", "PT3", "LULU"]
123123
- name: log_level
124124
values: ["ERROR", "WARNING", "INFO", "VERBOSE", "DEBUG"]
125125
- name: iterm_relax
@@ -192,7 +192,7 @@ tables:
192192
values: ["SHARED_LOW", "SHARED_HIGH", "LOW", "HIGH"]
193193
enum: led_pin_pwm_mode_e
194194
- name: gyro_filter_mode
195-
values: ["STATIC", "DYNAMIC", "ADAPTIVE"]
195+
values: ["STATIC", "DYNAMIC", "ADAPTIVE", "LULU"]
196196
enum: gyroFilterType_e
197197
- name: headtracker_dev_type
198198
values: ["NONE", "SERIAL", "MSP"]
@@ -219,13 +219,18 @@ groups:
219219
members:
220220
- name: looptime
221221
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."
222-
default_value: 1000
222+
default_value: 500
223223
max: 9000
224224
- name: gyro_anti_aliasing_lpf_hz
225225
description: "Gyro processing anti-aliasing filter cutoff frequency. In normal operation this filter setting should never be changed. In Hz"
226226
default_value: 250
227227
field: gyro_anti_aliasing_lpf_hz
228228
max: 1000
229+
- name: gyro_lulu_sample_count
230+
description: "Gyro lulu sample count, in number of samples."
231+
default_value: 3
232+
field: gyroLuluSampleCount
233+
max: 15
229234
- name: gyro_main_lpf_hz
230235
description: "Software based gyro main lowpass filter. Value is cutoff frequency (Hz)"
231236
default_value: 60

0 commit comments

Comments
 (0)