Forum: Digitale Signalverarbeitung / DSP / Machine Learning Butterworth Filter in C++, benötige Hilfe bei Feinabstimmung und Fehlersuche


von Michael D. (michael86)


Angehängte Dateien:

Lesenswert?

Ich versuche mich gerade an einem Butterworth Filter in C++ um 
Positions-Daten zu filtern.
Den Filter-Algorithmus habe ich von verschiedenen Github-Seiten und 
selbst daran rummodifiziert.

Es scheint so, als würde der funktionieren, garantieren kann ich es aber 
auch nicht.
Jedenfalls filtert er nicht wie gewünscht. Bei mir ist Input = Output, 
jedenfalls meistens oder ziemlich nahe dran.
Habe schon die Variablen cutoff_frequency_ und sampling_rate_ verändert 
aber hier scheint es mir so zu sein, dass diese nahezu keine Auswirkung 
haben.
Die Messwerte sollten konstant sein (also keine Schwankungen), da das 
Objekt nicht bewegt wurde.
1
std::vector<double> butterworth_filter(const std::vector<double>& input, double cutoff_frequency, double sampling_rate, int order)
2
{
3
    int N = input.size();
4
    std::vector<double> output(N, 0.0);
5
6
    // cf, init
7
    double nyquist = 0.5 * sampling_rate;
8
    double normalized_cutoff = cutoff_frequency / nyquist;
9
10
    std::vector<double> a(order + 1, 0.0);
11
    std::vector<double> b(order + 1, 0.0);
12
13
    double theta = M_PI * normalized_cutoff;
14
    double d = 1.0 / std::tan(theta);
15
    double d_squared = d * d;
16
17
    a[0] = 1.0 / (1.0 + std::sqrt(2.0) * d + d_squared);
18
    a[1] = 2.0 * a[0];
19
    a[2] = a[0];
20
    b[1] = 2.0 * (1.0 - d_squared) * a[0];
21
    b[2] = (1.0 - std::sqrt(2.0) * d + d_squared) * a[0];
22
23
    for (int i = 2; i < N; ++i)
24
    {
25
#ifdef NDEBUG
26
#else
27
        printf("\nFiltering index: %i\n",i);
28
#endif
29
        output[i] = a[0] * input[i] + a[1] * input[i - 1] + a[2] * input[i - 2] - b[1] * output[i - 1] - b[2] * output[i - 2];
30
    }
31
32
    return output;
33
}
34
35
class Filter
36
{
37
public:
38
    Filter() {}
39
40
    void set_history_size(size_t new_size)
41
    {
42
        history_size_ = new_size;
43
        if (x_history_.size() > history_size_) x_history_.resize(history_size_);
44
        if (y_history_.size() > history_size_) y_history_.resize(history_size_);
45
        if (z_history_.size() > history_size_) z_history_.resize(history_size_);
46
    }
47
48
    geometry_msgs::msg::TransformStamped apply_filter(const geometry_msgs::msg::TransformStamped& transform)
49
    {
50
        x_history_.push_back(transform.transform.translation.x);
51
        y_history_.push_back(transform.transform.translation.y);
52
        z_history_.push_back(transform.transform.translation.z);
53
54
        if (x_history_.size() > history_size_) x_history_.pop_front();
55
        if (y_history_.size() > history_size_) y_history_.pop_front();
56
        if (z_history_.size() > history_size_) z_history_.pop_front();
57
58
#ifdef NDEBUG
59
#else
60
        printf("\nHistory Size: %zu", history_size_);
61
        printf("\nDeque x_history_ size: %zu", x_history_.size());
62
        printf("\nDeque y_history_ size: %zu", y_history_.size());
63
        printf("\nDeque z_history_ size: %zu", z_history_.size());
64
#endif
65
        geometry_msgs::msg::TransformStamped filtered_transform = transform;
66
67
        if (x_history_.size() >= history_size_)
68
        {
69
            std::vector<double> x_filtered = butterworth_filter(
70
                std::vector<double>(x_history_.begin(), x_history_.end()), cutoff_frequency_, sampling_rate_, order_);
71
            std::vector<double> y_filtered = butterworth_filter(
72
                std::vector<double>(y_history_.begin(), y_history_.end()), cutoff_frequency_, sampling_rate_, order_);
73
            std::vector<double> z_filtered = butterworth_filter(
74
                std::vector<double>(z_history_.begin(), z_history_.end()), cutoff_frequency_, sampling_rate_, order_);
75
76
            filtered_transform.transform.translation.x = x_filtered.back();
77
            filtered_transform.transform.translation.y = y_filtered.back();
78
            filtered_transform.transform.translation.z = z_filtered.back();
79
        }
80
81
        return filtered_transform;
82
    }
83
84
private:
85
    std::deque<double> x_history_;
86
    std::deque<double> y_history_;
87
    std::deque<double> z_history_;
88
    size_t history_size_ = 20;
89
    double cutoff_frequency_ = 10.0;
90
    double sampling_rate_ = 100.0;
91
    int order_ = 2;
92
};

Wo genau liegt der Fehler?
Im Anhang der vollständige Code und ein Mitschnitt der Messdaten.

von Detlef _. (detlef_a)


Lesenswert?

Hi. Sieht doch eigentlich schick aus. Der Fehler wird in der Berechnung 
der coff. a und b liegen. Für ein cutoff und samplerate a und b händisch 
berechnen und vergleichen. Btw überall heißen die vorwärtskoeffizienten 
b und die Rückkoppelkoeffizienten a, bei dir umgekehrt.
Cheers
Detlef

von The A. (the_a343)


Lesenswert?

Hi,

immer schwer zu sagen, woran es fehlt ... :-O

ich benutze die CMSIS Funktionen
https://arm-software.github.io/CMSIS-DSP/main/group__groupFilters.html

und zum Berechnen der Koeffizienten den Micromodeler
https://www.micromodeler.com/dsp/

der kann auch Code ausgeben.

PS: als Testdaten kannst du ja mal ein Sprung auf 1 oder verschiedene 
Frequenzen benutzen ..

HTH, Adib

von Falk S. (falk_s831)


Lesenswert?

Deine Funktion ist ja auch recht großzügig bezüglich möglicher 
Eingabeparameter, würd ich sagen: schreib dir doch 'ne kleine Testsuite, 
welche Deine Funktion benutzt und wo Du Deine Berechnungs-Ergebnisse für 
die gegebenen Input-Stimuli gegen plausibilisieren kannst.

Der Code ist ja mit deinen STL-Containern ziemlich portabel, sollte also 
nix dagegen sprechen, mal GTest oder boost::test ne Chance zu geben.

von Michal S. (michal_s869)


Lesenswert?

I will do work on it after completing my project, which is related to 
Garage Door Services https://doorandroll.com.au/garage-door-services/.

: Bearbeitet durch User
von Rolf (audiorolf)


Lesenswert?

Detlef _. schrieb:
> Der Fehler wird in der Berechnung
> der coff. a und b liegen.
Möglicherweise ein mismatch zwischen der normierten Zeit und der, die 
sich durch die fs ergibt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.