Friday 3 November 2017

Przenoszenie średnia klasa c


Próbuję obliczyć średnią ruchomą sygnału. Wartość sygnału (podwójna) jest aktualizowana losowo razy. Szukam skutecznego sposobu obliczania jego średniej ważonej w czasie w oknie czasowym, w czasie rzeczywistym. Mógłbym to zrobić sam, ale jest to trudniejsze, niż myślałem. Większość zasobów znalezionych w Internecie oblicza średnią ruchomą okresowego sygnału, ale aktualizacje w czasie losowym. Czy ktoś zna dobre zasoby do tego Podstęp jest następujący: Otrzymujesz aktualizacje losowo razy poprzez aktualizację void (czas int, wartość float). Musisz jednak również śledzić, kiedy aktualizacja spadnie z okna czasowego, więc ustawiasz alarm, który wywoływał w czasie N, który usuwa poprzednie uaktualnienie, aby ponownie nie był uwzględniany w obliczeniach. Jeśli dzieje się to w czasie rzeczywistym, możesz poprosić system operacyjny o wywołanie metody void dropoffoldestupdate (int time), która ma być wywołana w czasie N Jeśli jest to symulacja, nie możesz uzyskać pomocy z systemu operacyjnego i musisz rób to ręcznie. W symulacji można wywołać metody z czasem podanym jako argument (który nie koreluje z czasem rzeczywistym). Jednak rozsądnym założeniem jest to, że połączenia są gwarantowane tak, że argumenty czasu rosną. W takim przypadku musisz posortować listę wartości czasu alarmu, a dla każdej aktualizacji i wywołania odczytu sprawdzić, czy argument czasu jest większy niż nagłówek listy alarmów. Podczas gdy jest on większy, wykonujesz przetwarzanie związane z alarmem (usuwając najstarszą aktualizację), usuń głowicę i sprawdź ponownie, aż wszystkie alarmy przed danym czasem zostaną przetworzone. Następnie wykonaj wywołanie aktualizacji. Do tej pory zakładałem, że jest oczywiste, co zrobiłbyś dla rzeczywistych obliczeń, ale rozwiążę je na wszelki wypadek. Zakładam, że masz metodę float read (int time), której używasz do odczytu wartości. Celem jest uczynienie tego połączenia tak efektywnym, jak to tylko możliwe. Więc nie obliczasz średniej ruchomej za każdym razem, gdy wywoływana jest metoda read. Zamiast tego oblicz wstępną wartość z ostatniej aktualizacji lub ostatniego alarmu i dostosuj tę wartość kilkoma operacjami zmiennoprzecinkowymi, aby uwzględnić upływ czasu od ostatniej aktualizacji. (i. e. stałą liczbę operacji, z wyjątkiem prawdopodobnie przetwarzania listy alarmów ułożonych w stos). Mam nadzieję, że jest to jasne - powinien to być dość prosty algorytm i dość wydajny. Dalsza optymalizacja. Jednym z pozostałych problemów jest sytuacja, w której duża liczba aktualizacji odbywa się w ramach okna czasowego, a następnie jest długi czas, w którym nie ma ani odczytów, ani aktualizacji, a następnie pojawia się odczyt lub aktualizacja. W takim przypadku powyższy algorytm będzie nieskuteczny przy stopniowej aktualizacji wartości każdej z aktualizacji, która spada. Nie jest to konieczne, ponieważ dbamy tylko o ostatnią aktualizację poza przedziałem czasowym, więc jeśli istnieje sposób, aby skutecznie usunąć wszystkie starsze aktualizacje, pomogłoby to. Aby to zrobić, możemy zmodyfikować algorytm do binarnego wyszukiwania aktualizacji, aby znaleźć najnowszą aktualizację przed upływem okna czasowego. Jeśli istnieje stosunkowo niewielka liczba aktualizacji, które należy usunąć, można stopniowo zwiększać wartość każdej upuszczonej aktualizacji. Ale jeśli istnieje wiele aktualizacji, które należy usunąć, można ponownie obliczać wartość od zera po usunięciu starych aktualizacji. Dodatek do obliczeń przyrostowych: Powinienem wyjaśnić, co mam na myśli przez przyrostowe obliczenia powyżej w zdaniu, poprawić tę wartość o kilka operacji zmiennoprzecinkowych, aby uwzględnić upływ czasu od ostatniej aktualizacji. Początkowe obliczenia nieinżynierskie: następnie wykonaj iterację nad odpowiednimi aktualizacjami w kolejności rosnącego czasu: moveaverage (sum lastupdate timesincelastupdate) windowlength. Teraz, jeśli dokładnie jedna aktualizacja wypadnie z okna, ale nie pojawią się żadne nowe aktualizacje, dostosuj sumę jako: (zauważ, że jest to priorupdate, którego znacznik czasu został zmodyfikowany do rozpoczęcia ostatniego okna). I jeśli dokładnie jedna aktualizacja wchodzi do okna, ale żadne nowe aktualizacje nie wypadają, należy skorygować sumę jako: Jak powinno być oczywiste, jest to szkic szorstki, ale mam nadzieję, że pokazuje on, jak można utrzymać średnią taką, że jest to operacja O (1) na aktualizację zgodnie z amortyzacją. Ale zanotuj dalszą optymalizację w poprzednim paragrafie. Należy również zwrócić uwagę na problemy ze stabilnością, które pojawiają się w starszej odpowiedzi, co oznacza, że ​​błędy zmiennoprzecinkowe mogą kumulować się w dużej liczbie takich operacji przyrostowych, tak że istnieje rozbieżność w wyniku pełnego obliczenia, który jest istotny dla aplikacji. Jeśli przybliżenie jest w porządku i tam jest minimalny czas między próbkami, możesz spróbować super-próbkowania. Posiadaj tablicę, która reprezentuje równomiernie rozmieszczone interwały czasowe, które są krótsze niż minimum, i dla każdego okresu przechowuj najnowszą próbkę, która została odebrana. Im krótszy przedział czasu, tym bliżej wartości średniej do rzeczywistej. Okres nie powinien być większy niż połowa minimum lub istnieje szansa na brak próbki. odpowiedziała 15 grudnia 11 o 18:12 odpowiedział 15 grudnia 11 o 22:38 Dziękuję za odpowiedź. Jedna poprawa, która byłaby potrzebna do faktycznego cytowania wartości całkowitej średniej, więc nie robimy pętli przez cały czas. Co więcej, może to być drobna kwestia, ale czy nie byłoby bardziej wydajne korzystanie z rachunku lub listy do przechowywania wartości, ponieważ zakładamy, że aktualizacja zostanie wprowadzona we właściwej kolejności. Wstawianie będzie szybsze niż na mapie. ndash Arthur Dec 16 11 o 8:55 Tak, możesz buforować wartość sumy. Odejmij wartości wymazanych próbek, dodaj wartości wstawianych próbek. Ponadto, tak, DequeltpairltSample, Dategtgt może być bardziej efektywny. Wybrałem mapę dla czytelności i łatwość wywoływania mapy :: górne. Jak zawsze, najpierw napisz poprawny kod, a następnie zmień profil i zmień przyrostowe. ndash Rob Dec 16 11 o 15:00 Uwaga: Najwyraźniej to nie jest sposób, aby do tego podejść. Pozostawiając to tutaj dla odniesienia, co jest nie tak z tym podejściem. Sprawdź komentarze. AKTUALIZACJA - na podstawie komentarza Olisa. nie jestem jednak pewien niestabilności, o której on mówi. Użyj posortowanej mapy czasów przyjazdu względem wartości. Po nadejściu wartości dodaj czas przybycia do posortowanej mapy wraz z jej wartością i zaktualizuj średnią ruchomą. ostrzeżenie to jest pseudo-kod: There. Nie w pełni rozwiązany, ale masz pomysł. Rzeczy do zapamiętania. Jak już powiedziałem, powyższy kod to pseudo kod. Musisz wybrać odpowiednią mapę. Nie usuwaj par podczas iteracji, ponieważ unieważnisz iterator i będziesz musiał zacząć od nowa. Zobacz także komentarz Olis poniżej. odpowiedziała 15 grudnia o 12:22 To nie działa: nie bierze pod uwagę, jaka część okna ma długość każdej wartości. Również to podejście dodawania, a następnie odejmowania jest stabilne tylko dla typów całkowitych, a nie dla zmiennych. ndash Oliver Charlesworth 15 grudnia 11 o 12:29 OliCharlesworth - przepraszam, że przegapiłem kilka kluczowych punktów w opisie (podwójne i ważone czasem). Zaktualizuję. Dzięki. ndash Dennis 15 grudnia 11 o 12:33 Ważenie czasu to kolejny problem. Ale nie o tym mówię. Miałem na myśli fakt, że kiedy nowa wartość wchodzi najpierw w okno czasowe, jej udział w średniej jest minimalny. Jego wkład stale rośnie, dopóki nie pojawi się nowa wartość. ndash Oliver Charlesworth Dec 15 11 o 12: 35 Wiem, że jest to możliwe dzięki zwiększeniu liczby na: Ale naprawdę nie chciałbym używać boostu. Mam google i nie znalazłem żadnych odpowiednich lub czytelnych przykładów. Zasadniczo chcę śledzić średnią ruchomą bieżącego strumienia strumienia liczb zmiennoprzecinkowych z wykorzystaniem najnowszych 1000 liczb jako próbki danych. Jaki jest najłatwiejszy sposób, aby to osiągnąć? Eksperymentowałem z użyciem okrągłej tablicy, wykładniczej średniej kroczącej i prostszej średniej ruchomej, i odkryłem, że wyniki z okrągłej macie najlepiej pasują do moich potrzeb. Zapytany 12 czerwca 12 o 4:38 Jeśli twoje potrzeby są proste, możesz po prostu spróbować użyć wykładniczej średniej kroczącej. Mówiąc prościej, tworzysz zmienną akumulatora, a ponieważ twój kod wygląda na każdą próbkę, kod aktualizuje akumulator o nową wartość. Wybierasz stałą alfa, która jest pomiędzy 0 a 1, i obliczasz to: Musisz tylko znaleźć wartość alfa, gdzie efekt danej próbki trwa tylko około 1000 próbek. Hmm, nie jestem właściwie pewien, czy ci to pasuje, teraz, kiedy go tu umieściłem. Problem polega na tym, że 1000 to dość długie okno dla wykładniczej średniej kroczącej Nie jestem pewien, czy istnieje alfa, które rozłożyłoby średnią z ostatnich 1000 liczb, bez dolnego limitu w obliczeniach zmiennoprzecinkowych. Ale jeśli chcesz mieć mniejszą średnią, na przykład około 30 numerów, jest to bardzo łatwy i szybki sposób na zrobienie tego. odpowiedź 12 czerwca 12 o 4:44 1 na twój post. Wykładnicza średnia ruchoma może pozwolić na zmienną alfa. Dzięki temu można go wykorzystać do obliczenia średniej podstawy czasu (na przykład bajtów na sekundę). Jeśli czas od ostatniej aktualizacji akumulatora jest dłuższy niż 1 sekunda, zezwalasz alfa na 1.0. W przeciwnym razie możesz pozwolić na alfa (usecs od ostatniej aktualizacji1000000). ndash jxh 12 czerwca 12 o 6:21 Zasadniczo chcę śledzić średnią ruchomą ciągłego strumienia strumienia liczb zmiennoprzecinkowych z wykorzystaniem najnowszych 1000 liczb jako próbki danych. Zauważ, że poniższe aktualizacje aktualizują sumę jako elementy dodane z powrotem, unikając kosztownego przejścia przez O (N) w celu obliczenia sumy - potrzebnej dla średniej - na żądanie. Total otrzymuje inny parametr od T do obsługi np. użycie długiej długości, gdy suma wynosi 1000 długich s, int dla char s lub double do total float s. Jest to trochę wadliwe, ponieważ liczba poprawek może przekroczyć INTMAX - jeśli chcesz, możesz użyć długiej długości bez znaku. lub użyj dodatkowego elementu danych typu bool, aby zarejestrować, kiedy pojemnik jest pierwszy wypełniony, podczas gdy liczba próbkowania w cyklu wokół tablicy (najlepiej wtedy przemianowana na coś nieszkodliwego jak pos). odpowiedź 12 czerwca 12 o 5:19 zakłada się, że quotvoid operator (T sample) quot jest w rzeczywistości quotvoid operatorltlt (T sample) quot. ndash oPless cze 8 14 o 11:52 o Bez ahhh. dobrze zauważył. faktycznie miałem na celu unieważnienie operatora () (próbka T), ale oczywiście można użyć dowolnej notacji, którą lubisz. Naprawię, dzięki. ndash Tony D cze 8 14 o 14: 27 Jak wspomnieli inni, powinieneś rozważyć filtr IIR (nieskończona odpowiedź impulsowa) zamiast filtra FIR (skończona odpowiedź impulsowa), którego używasz teraz. Jest w tym coś więcej, ale na pierwszy rzut oka filtry FIR są implementowane w postaci jawnych zwojów i filtrów IIR z równaniami. Szczególny filtr IIR, którego używam w mikrokontrolerach, to jednobiegunowy filtr dolnoprzepustowy. Jest to cyfrowy odpowiednik prostego filtra analogowego R-C. W przypadku większości aplikacji będą one miały lepsze właściwości niż filtr skrzynkowy, którego używasz. Większość zastosowań filtra skrzynkowego, z jakim się zetknąłem, jest rezultatem tego, że ktoś nie zwraca uwagi w klasie przetwarzania sygnałów cyfrowych, a nie w wyniku potrzeby posiadania ich szczególnych cech. Jeśli chcesz tylko tłumić wysokie częstotliwości, o których wiesz, że są szumami, filtr jednoprzybiegowy dolnoprzepustowy jest lepszy. Najlepszym sposobem na implementację jednego cyfrowego w mikrokontrolerze jest zwykle: FILT lt-- FILT FF (NEW - FILT) FILT jest fragmentem trwałego stanu. Jest to jedyna trwała zmienna, którą musisz obliczyć. NOWOŚĆ jest nową wartością, którą filtr jest aktualizowany w tej iteracji. FF jest frakcją filtracyjną. który dostosowuje ciężkość filtra. Przyjrzyj się temu algorytmowi i zobacz, że dla FF 0 filtr jest nieskończenie ciężki, ponieważ jego wynik nigdy się nie zmienia. Dla FF 1, to naprawdę nie ma żadnego filtra, ponieważ wyjście jest zgodne z danymi wejściowymi. Przydatne wartości są pomiędzy. W małych systemach wybieramy FF na 12 N, aby pomnożenie przez FF można było wykonać jako przesunięcie w prawo o N bitów. Na przykład, FF może wynosić 116, a pomnożyć przez FF, zatem przesunięcie w prawo o 4 bity. W przeciwnym razie filtr ten wymaga tylko jednego odjęcia i jednego dodania, chociaż liczby zwykle muszą być szersze niż wartość wejściowa (więcej na temat dokładności liczbowej w osobnej sekcji poniżej). Zwykle odczyty AD są znacznie szybsze niż są potrzebne i stosuje się dwa z tych filtrów kaskadowo. Jest to cyfrowy odpowiednik dwóch filtrów R-C w serii i tłumi o 12 dBoctave powyżej częstotliwości wycofania. Jednak w przypadku odczytów AD jego zwykle bardziej istotne jest spojrzenie na filtr w dziedzinie czasu, biorąc pod uwagę jego skokową odpowiedź. Dzięki temu dowiesz się, jak szybko system zauważy zmianę, gdy zmienią się rzeczy, które mierzysz. Aby ułatwić projektowanie tych filtrów (co oznacza tylko wybieranie FF i decydowanie o tym, ile z nich ma być kaskadowane), używam mojego programu FILTBITS. Użytkownik określa liczbę bitów zmiany dla każdego FF w kaskadowej serii filtrów i oblicza odpowiedź skokową i inne wartości. Właściwie to zwykle uruchamiam to za pomocą mojego skryptu opakowania PLOTFILT. To uruchamia FILTBITS, który tworzy plik CSV, a następnie kreśli plik CSV. Na przykład tutaj jest wynik PLOTFILT 4 4: Dwa parametry dla PLOTFILT oznaczają, że będą dwa kaskady kaskadowe typu opisanego powyżej. Wartości 4 wskazują liczbę bitów zmiany, aby zrealizować pomnożenie przez FF. Dwie wartości FF wynoszą w tym przypadku 116. Czerwony ślad jest odpowiedzią jednostkową i jest najważniejszą rzeczą, na którą należy patrzeć. Na przykład informuje to, że jeśli dane wejściowe ulegną natychmiastowej zmianie, wynik połączonego filtru zostanie ustawiony na 90 nowej wartości w 60 iteracjach. Jeśli zależy Ci na 95 czasu rozstrzygania, to musisz czekać około 73 iteracji, a dla 50 czasu rozstrzygania tylko 26 iteracji. Zielony ślad pokazuje wynik z pojedynczego pełnego impulsu amplitudy. To daje wyobrażenie o losowym tłumieniu hałasu. Wygląda na to, że żadna pojedyncza próbka nie spowoduje więcej niż 2,5 zmiany w danych wyjściowych. Niebieski ślad daje subiektywne odczucie tego, co ten filtr robi z białym szumem. Nie jest to rygorystyczny test, ponieważ nie ma gwarancji, jaka dokładnie była zawartość losowych liczb wybieranych jako białe szumy wejściowe dla tego przebiegu PLOTFILT. To tylko po to, aby dać ci wrażenie, jak bardzo będzie zgnieciony i jak gładki jest. PLOTFILT, może FILTBITS, i wiele innych użytecznych rzeczy, szczególnie dla oprogramowania układowego PIC, jest dostępne w oprogramowaniu PIC Development Tools na mojej stronie Software downloads. Dodane o precyzję liczbową widzę z komentarzy, a teraz nową odpowiedź, że istnieje zainteresowanie omawianiem liczby bitów potrzebnych do wdrożenia tego filtru. Zwróć uwagę, że pomnożenie przez FF spowoduje utworzenie nowych bitów Log 2 (FF) poniżej punktu binarnego. W przypadku małych systemów wartość FF jest zwykle wybierana na 12 N, aby to pomnożenie było realizowane przez prawą zmianę N bitów. FILT jest więc zwykle stałą liczbą całkowitą. Zauważ, że to nie zmienia żadnej z matematyki z punktu widzenia procesorów. Na przykład, jeśli filtrujesz 10-bitowe odczyty AD i N 4 (FF 116), potrzebujesz 4 bitów frakcji poniżej 10-bitowych odczytów liczb całkowitych AD. Jeden z najbardziej procesorów wykona 16 bitowych operacji na liczbach całkowitych ze względu na 10-bitowe odczyty AD. W tym przypadku nadal możesz wykonywać dokładnie takie same 16-bitowe liczby całkowite, ale zacznij od odczytów AD przesuniętych o 4 bity w lewo. Procesor nie zna różnicy i nie musi tego robić. Wykonanie matematyki na całych 16-bitowych liczbach całkowitych działa niezależnie od tego, czy uważasz je za stałe 12 punktów, czy 16-bitowe liczby całkowite (16 punktów stałych). Ogólnie rzecz biorąc, musisz dodać N bitów dla każdego słupka filtra, jeśli nie chcesz dodawać szumu z powodu reprezentacji numerycznej. W powyższym przykładzie drugi filtr dwóch musiałby mieć 1044 18 bitów, aby nie stracić informacji. W praktyce na maszynie 8-bitowej oznacza to, że użyjesz 24-bitowych wartości. Technicznie tylko drugi biegun dwóch potrzebowałby szerszej wartości, ale dla prostoty oprogramowania zazwyczaj używam tej samej reprezentacji, a tym samym tego samego kodu, dla wszystkich biegunów filtra. Zwykle piszę podprogram lub makro, aby wykonać jedną operację na biegunie filtra, a następnie zastosować to do każdego bieguna. Czy podprogram lub makro zależy od tego, czy cykle lub pamięć programu są ważniejsze w danym projekcie. Tak czy inaczej, używam stanu scratch, aby przekazać NEW do subroutinemacro, który aktualizuje FILT, ale również ładuje go do tego samego stanu scratch NEW. Dzięki temu można łatwo zastosować wiele biegunów, ponieważ zaktualizowany FILT jednego bieguna jest NOWY następnego. Kiedy podprogram jest użyteczny, aby wskazywać na wskaźnik, wskaż FILT na wejściu, który jest aktualizowany tuż po FILT w drodze. W ten sposób podprogram automatycznie działa na kolejnych filtrach w pamięci, jeśli jest wywoływany wiele razy. W makrze nie potrzebujesz wskaźnika, ponieważ przekazujesz adres, aby działał w każdej iteracji. Przykłady kodu Oto przykład makra, jak opisano powyżej dla PIC 18: A tutaj jest podobne makro dla PIC 24 lub dsPIC 30 lub 33: Oba te przykłady są implementowane jako makra przy użyciu mojego preprocesora asemblera PIC. który jest bardziej wydajny niż którykolwiek z wbudowanych obiektów makro. clabacchio: Kolejną kwestią, o której powinienem wspomnieć, jest implementacja oprogramowania układowego. Możesz napisać jednobiegunowy podprogram z dolnoprzepustowym filtrem jeden raz, a następnie zastosować go kilka razy. Właściwie to zazwyczaj piszę taki podprogram, aby wprowadzić wskaźnik w pamięci do stanu filtra, a następnie przesunąć wskaźnik tak, aby można go było łatwo wywoływać w celu zrealizowania filtrów wielobiegunowych. ndash Olin Lathrop kwi 20 12 o 15:03 1. bardzo dziękuję za twoje odpowiedzi - wszystkie z nich. Postanowiłem użyć tego filtra IIR, ale ten filtr nie jest używany jako standardowy filtr LowPass, ponieważ potrzebuję uśredniać wartości przeciwstawne i porównywać je w celu wykrycia zmian w pewnym zakresie. ponieważ wartości te mają bardzo różne wymiary w zależności od sprzętu, chciałem wziąć średnią, aby móc automatycznie reagować na te zmiany specyficzne dla sprzętu. ndash sensslen 21 maja 12 o 12:06 Jeśli możesz żyć z ograniczeniem mocy dwóch liczb przedmiotów do średniej (tj. 2,4,8,16,32 itd.), wtedy podział można łatwo i skutecznie wykonać na niska wydajność mikro bez wydzielonego podziału, ponieważ można to zrobić jako odrobinę przesunięcia. Każda zmiana prawa jest jedną mocą dwóch np.: OP uważał, że ma dwa problemy, dzieli się na PIC16 i pamięć na swój bufor pierścieniowy. Ta odpowiedź pokazuje, że podział nie jest trudny. Wprawdzie nie rozwiązuje problemu pamięci, ale system SE umożliwia częściowe odpowiedzi, a użytkownicy mogą wziąć coś z każdej odpowiedzi dla siebie, a nawet edytować i łączyć inne odpowiedzi. Ponieważ niektóre inne odpowiedzi wymagają operacji dzielenia, są one podobnie niekompletne, ponieważ nie pokazują, jak skutecznie osiągnąć to na PIC16. ndash Martin Apr 20 12 at 13:01 Istnieje odpowiedź na prawdziwy filtr średniej ruchomej (zwany też filtrem wagonu towarowego) z mniejszą ilością pamięci, jeśli nie masz nic przeciwko zmniejszeniu częstotliwości próbkowania. Nazywany jest kaskadowym filtrem integrator-grzebień (CIC). Pomysł polega na tym, że posiadasz integratora, z którym będziesz różnił się przez pewien okres czasu, a kluczowym urządzeniem oszczędzającym pamięć jest to, że po zmniejszeniu częstotliwości nie musisz przechowywać każdej wartości integratora. Można go zaimplementować za pomocą następującego pseudokodu: Twoja efektywna średnia długość ruchu to decymacja Factorstatesize, ale musisz tylko przechowywać próbki ze stanem. Oczywiście można uzyskać lepszą wydajność, jeśli twój stateize i decimationFactor są potęgami 2, tak że operatorzy podziału i pozostałych zostają zastąpieni przez zmiany i maski. Postscript: Zgadzam się z Olinem, że zawsze powinieneś rozważyć proste filtry IIR przed filtrem średniej ruchomej. Jeśli nie potrzebujesz zerowej częstotliwości filtra wagonów, 1-biegunowy lub 2-biegunowy filtr dolnoprzepustowy prawdopodobnie działa dobrze. Z drugiej strony, jeśli filtrujesz w celu zdziesiątkowania (pobierając dane o wysokiej częstotliwości próbkowania i uśredniając je do użycia w procesie o niskiej szybkości), to filtr CIC może być właśnie tym, czego szukasz. (szczególnie, jeśli możesz użyć statesize1 i całkowicie uniknąć bufora pierścieniowego z tylko jedną poprzednią wartością integratora). Istnieje kilka dogłębnych analiz matematycznych za pomocą filtra IIR pierwszego rzędu, który Olin Lathrop już opisał na wymianie stosu Digital Signal Processing (zawiera mnóstwo ładnych zdjęć.) Równanie tego filtru IIR jest następujące: To może być zaimplementowane przy użyciu tylko liczb całkowitych i bez dzielenia przy użyciu następującego kodu (może wymagać trochę debugowania podczas pisania z pamięci). Filtr ten przybliża średnią ruchomą ostatnie K próbek, ustawiając wartość alfa na 1K. Zrób to w poprzednim kodzie, definiując BITS do LOG2 (K), tj. Dla K 16 ustaw BITS na 4, dla K4 ustaw BITS na 2, itd. (Potwierdzam kod tutaj wymieniony, gdy tylko otrzymam zmianę i edytuj tę odpowiedź w razie potrzeby.) Odpowiedź 23 czerwca 12 o 04:04 Oto jednobiegunowy filtr dolnoprzepustowy (średnia ruchoma, z częstotliwością odcięcia CutoffFrequency). Bardzo prosty, bardzo szybki, działa świetnie i prawie nie ma narzutów pamięci. Uwaga: Wszystkie zmienne mają zasięg wykraczający poza funkcję filtru, z wyjątkiem przekazanego w newInput Note: Jest to filtr jednostopniowy. Wiele etapów można połączyć kaskadowo, aby zwiększyć ostrość filtra. Jeśli używasz więcej niż jednego etapu, będziesz musiał dostosować DecayFactor (w odniesieniu do częstotliwości odcięcia), aby to zrekompensować. I oczywiście wszystko, czego potrzebujesz, to te dwie linie umieszczone gdziekolwiek, nie potrzebują swojej własnej funkcji. Ten filtr ma czas narastania, zanim średnia ruchoma reprezentuje wartość sygnału wejściowego. Jeśli chcesz ominąć ten czas rozruchu, możesz po prostu zainicjować MovingAverage na pierwszą wartość newInput zamiast 0 i mieć nadzieję, że pierwszy newInput nie będzie odstający. (CutoffFrequencySampleRate) ma zakres od 0 do 0,5. DecayFactor jest wartością z zakresu od 0 do 1, zwykle zbliżoną do 1. Pływaki o pojedynczej precyzji są wystarczające dla większości rzeczy, po prostu wolę podwójne. Jeśli musisz trzymać się liczb całkowitych, możesz przekształcić DecayFactor i Amplitude Factor w ułamkowe liczby całkowite, w których licznik jest zapisany jako liczba całkowita, a mianownik jest liczbą całkowitą równą 2 (tak, że możesz przesunąć bit w prawo, mianownik zamiast dzielenia podczas pętli filtra). Na przykład, jeśli DecayFactor 0.99 i chcesz używać liczb całkowitych, możesz ustawić DecayFactor 0,99 65536 64881. A potem, kiedy pomnożysz przez DecayFactor w pętli filtra, po prostu przesuń wynik 16. Więcej informacji na ten temat, doskonała książka online, rozdział 19 dotyczący filtrów rekursywnych: dspguidech19.htm PS W przypadku paradygmatu Moving Average, inne podejście do ustawiania DecayFactor i AmplitudeFactor, które mogą być bardziej odpowiednie do twoich potrzeb, powiedzmy, że chcesz poprzedni, około 6 elementów uśrednionych razem, robiąc to dyskretnie, dodałeś 6 pozycji i podzieliłeś o 6, więc możesz ustawić AmplitudeFactor na 16, a DecayFactor na (1.0 - AmplitudeFactor). Odpowiedzieli 14 maja 12 o 22:55 Wszyscy inni dokładnie skomentowali użyteczność IIR vs. FIR i podziału sił dwóch. Chciałbym podać pewne szczegóły dotyczące implementacji. Poniższe działa dobrze na małych mikrokontrolerach bez FPU. Nie ma mnożenia, a jeśli utrzymujesz N jako potęgę dwóch, cały podział jest przesunięciem bitów w jednym cyklu. Podstawowy bufor pierścieniowy FIR: zachowaj bufor bieżących ostatnich N wartości i działający SUM wszystkich wartości w buforze. Za każdym razem, gdy pojawia się nowa próbka, odejmij najstarszą wartość z buforu od SUM, zastąp ją nową próbką, dodaj nową próbkę do SUMY i wypisz SUMN. Zmodyfikowany bufor pierścieniowy IIR: zachowaj SUM z ostatnich N wartości. Za każdym razem, gdy pojawia się nowa próbka, SUM - SUMN, dodaj nową próbkę i wypisz SUMN. odpowiedział 28 sierpnia 13 o 13:45 Jeśli I39m czyta dobrze, to opisując filtr IIR pierwszego rzędu wartość odejmowania nie jest najstarszą wartością, która wypada, ale jest zamiast średniej z poprzednich wartości. Filtry IIR pierwszego rzędu z pewnością mogą się przydać, ale nie jestem pewien, co masz na myśli, gdy sugerujesz, że wynik jest taki sam dla wszystkich sygnałów okresowych. Przy częstotliwości próbkowania 10 kHz, podanie fali kwadratowej 100 Hz do 20-stopniowego filtra skrzynkowego da sygnał równomiernie rosnący dla 20 próbek, siedzi wysoko na 30, krople równomiernie na 20 próbek i siedzi na niskim poziomie przez 30. A first-order Filtr IIR. ndash supercat 28 sierpnia 13 o 15:31 przyniesie falę, która gwałtownie zacznie wzrastać i stopniowo spadnie blisko (ale nie na) maksymalnej wartości wejściowej, a następnie gwałtownie zacznie spadać i stopniowo spadnie w pobliżu (ale nie przy) minimalnej wartości wejściowej. Bardzo różne zachowanie. ndash supercat 28 sierpnia 13 o 15:32 Problem polega na tym, że prosta średnia krocząca może, ale nie musi być użyteczna. Dzięki filtrowi IIR możesz uzyskać ładny filtr o stosunkowo niewielkiej liczbie kalorii. FIR, które opisujesz, może dać ci tylko prostokąt w czasie - sinc in freq - i nie możesz zarządzać bocznymi płatami. Być może warto rzucić kilka mnożących liczb całkowitych, aby uczynić z niego symetryczny przestrajalny FIR, jeśli można oszczędzić tyknięcia zegara. ndash Scott Seidman Aug 29 13 at 13:50 ScottSeidman: Nie ma potrzeby mnożenia, jeśli jeden po prostu ma każdy etap FIR albo wyprowadza średnią z wejścia do tego etapu i jego poprzedniej zapisanej wartości, a następnie przechowuje dane wejściowe (jeśli zakres liczbowy, można użyć sumy zamiast średniej). To, czy jest lepsze niż filtr skrzynkowy, zależy od aplikacji (odpowiedź skokowa filtra skrzynkowego z całkowitym opóźnieniem 1 ms, na przykład, będzie miała nieprzyjemny skok d2dt, gdy zmiana wejścia, i ponownie 1 ms później, ale będzie miała minimum możliwe ddt dla filtra z całkowitym opóźnieniem 1ms). ndash supercat Aug 29 13 o 15:25 Jak powiedział mikeselectric, jeśli naprawdę potrzebujesz zmniejszyć zapotrzebowanie na pamięć i nie przejmujesz się tym, że odpowiedź impulsowa jest wykładnicza (zamiast impulsu prostokątnego), wybrałbym wykładniczy filtr średniej ruchomej . Używam ich szeroko. Z tego typu filtrem nie potrzebujesz żadnego bufora. Nie musisz przechowywać N ostatnich próbek. Tylko jeden. Tak więc, twoje wymagania dotyczące pamięci zostają zmniejszone o współczynnik N. Nie potrzebujesz do tego żadnego podziału. Tylko multiplikacje. Jeśli masz dostęp do arytmetyki zmiennoprzecinkowej, użyj zwielokrotnień zmiennoprzecinkowych. W przeciwnym razie wykonaj całkowitą multiplikację i przesuń w prawo. Jednak jesteśmy w 2017 roku i polecam korzystanie z kompilatorów (i mikrokontrolerów), które umożliwiają pracę z liczbami zmiennoprzecinkowymi. Poza tym, że jest to więcej pamięci wydajnej i szybszej (nie musisz aktualizować elementów w dowolnym buforze kołowym), powiedziałbym, że jest to również bardziej naturalne. ponieważ wykładnicza odpowiedź impulsowa lepiej pasuje do sposobu, w jaki zachowuje się natura, w większości przypadków. Odpowiedział 20 kwietnia o 9:59 Jedną z kwestii związanych z filtrem IIR, jako że prawie został dotknięty przez olin i supercat, ale najwyraźniej został zignorowany przez innych, jest to, że zaokrąglenie w dół wprowadza pewną niedokładność (i potencjalnie biastuncation). zakładając, że N jest potęgą dwóch, a używana jest tylko arytmetyczna liczba całkowita, prawo przesunięcia systematycznie eliminuje LSB nowej próbki. Oznacza to, że jak długo seria może być, średnia nigdy nie bierze ich pod uwagę. Załóżmy na przykład, że seria powoli się zmniejsza (8, 8, 8, 7, 7, 7, 7, 6, 6) i zakłada, że ​​średnia wynosi na początku 8. Pierwsza próbka 7 przyniesie średnią do 7, niezależnie od siły filtra. Tylko na jedną próbkę. Ta sama historia dla 6 itd. Teraz pomyśl o czymś przeciwnym. seria idzie w górę. Średnia pozostanie na 7 na zawsze, dopóki próbka nie będzie wystarczająco duża, aby mogła się zmienić. Oczywiście można poprawić błąd poprzez dodanie 12N2, ale to naprawdę nie rozwiąże problemu precyzji. w takim przypadku seria malejąca pozostanie na zawsze w punkcie 8, dopóki próbka nie osiągnie wartości 8-12 (N2). N4 na przykład, każda próbka powyżej zera utrzyma średnią bez zmian. Uważam, że rozwiązanie tego problemu oznaczałoby posiadanie akumulatora utraconych LSB. Ale nie zrobiłem tego wystarczająco daleko, aby mieć gotowy kod i nie jestem pewien, czy nie zaszkodziłoby to mocy IIR w niektórych innych seriach (na przykład, czy 7,9,7,9 będzie średnio wtedy 8). Olin, twoja dwustopniowa kaskada również potrzebuje wyjaśnienia. Czy masz na myśli trzymanie dwóch średnich wartości z wynikiem pierwszego podawanego do drugiego w każdej iteracji. Co za korzyść

No comments:

Post a Comment