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.

: Bearbeitet durch User
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

: Bearbeitet durch User
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.

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.