Jak działają kaskadowe sekcje biquad dla filtrów wyższego rzędu?

20

Próbuję zaimplementować filtr IIR 8. rzędu, a każda notka aplikacji i podręcznik, który przeczytałem, mówi, że najlepiej jest zaimplementować dowolny filtr rzędu więcej niż 2 jako sekcje drugiego rzędu. Użyłem tf2sosw MATLAB, aby uzyskać współczynniki dla sekcji drugiego rzędu, co dało mi współczynniki 6x4 dla 4 sekcji drugiego rzędu, zgodnie z oczekiwaniami. Przed wdrożeniem jako SOS filtr 8. rzędu wymagał zapisania 7 poprzednich wartości próbek (a także wartości wyjściowych). Teraz, kiedy wdrażam jako sekcje drugiego rzędu, w jaki sposób przepływ działa od wejścia do wyjścia, czy muszę przechowywać tylko 2 poprzednie wartości próbek? A może wyjście pierwszego filtra jest przesyłane jak x_indo drugiego filtra i tak dalej?

anasimtiaz
źródło
musisz zapamiętać poprzednie stany dla każdego etapu, w zależności od kolejności filtra na tym etapie, aby nie były to tylko 2, jak wspomniałeś

Odpowiedzi:

13

To ostatnia rzecz, którą powiedziałeś („A może wyjście pierwszego filtra przesyła się jako x_in do drugiego filtra i tak dalej?”). Pomysł jest prosty: traktujesz biquady jako osobne filtry drugiego rzędu, które są kaskadowo. Sygnał wyjściowy z pierwszego filtra jest wejściem do drugiego i tak dalej, więc linie opóźniające są rozłożone między filtry. Jeśli potrzebujesz zoptymalizować strukturę w środowisku ograniczonym pamięcią, możesz zauważyć, że sąsiednie biquady mają nadmiarową pamięć opóźnień (tj. Kilka ostatnich próbek wyjściowych etapu 1 jest takich samych, jak kilka ostatnich próbek wejściowych etapu 2, więc nie nie będą musiały przechowywać ich osobno, tak jak w przypadku samodzielnego wdrożenia filtrów).

Jason R.
źródło
Dzięki! Właśnie udało mi się szybko to zrobić w MATLAB. Przyczyną wcześniejszych nieporozumień było to, że zapomniałem pomnożyć zysk (ugh!) I dlatego zaczęły
wkradać się
Jeśli nie zawracasz sobie głowy pytaniem o wzmocnienie jako argument wyjściowy z tf2sos (jak w moim opublikowanym kodzie przykładowym), nie musisz zawracać sobie głowy mnożeniem go ponownie.
learnvst
9

Istnieją dwa sposoby implementacji sekcji drugiego rzędu: równoległy i szeregowy. W wersji szeregowej dane wyjściowe sekcji N są wejściami do sekcji N + 1. W wersji równoległej wszystkie sekcje mają takie same dane wejściowe (i tylko jedno rzeczywiste zero zamiast sprzężonej złożonej pary zer), a dane wyjściowe każdej sekcji są po prostu sumowane. Te dwie metody są powiązane przez częściowe rozszerzenie ułamka funkcji przenoszenia w domenie Z. OSTRZEŻENIE: jest to trudny numerycznie problem, a standardowa implementacja Matlaba „reszz” może mieć bardzo duże błędy numeryczne dla typowych filtrów audio, które mają bieguny blisko koła jednostki.

Hilmar
źródło
6

Oto trochę kodu demonstracyjnego, który pokazuje, dlaczego lepiej jest kaskadować sekcje drugiego rzędu.

clc

sr = 44100;
order = 13;

[b,a] = butter(order,1000/(sr/2),'low');
[sos] = tf2sos(b,a);

x = [1; zeros(299,1)]; %impulse


% all in one
Y = filter(b,a,x);

% cascaded biquads
Z = x;
for nn = 1:size(sos,1);
    Z = filter(sos(nn,1:3),sos(nn,4:6), Z );
end


cla; plot(Y, 'k'); hold on; plot(Z,':r'); hold off

W przypadku filtra dolnoprzepustowego podanego w powyższym przykładzie, rzędu około 12 do 13, błędy numeryczne narastają, aby dać wyraźnie inną odpowiedź impulsową dla implementacji, która nie wykorzystuje kaskadowych biquadów. W zależności od filtra twój przebieg będzie się różnić.

ZAMÓWIENIE = 10

wprowadź opis zdjęcia tutaj

ZAMÓWIENIE = 13

wprowadź opis zdjęcia tutaj

learnvst
źródło
@learvst Popraw mnie, jeśli się mylę, ale Twój kod traci zyski. Czy nie powinno być:[sos gain] = tf2sos(b,a); // Rest of code for nn = 1:size(sos,1); Z = filter(sos(nn,1:3),sos(nn,4:6), Z ); end Z = filter(gain,1,Z);
user915783