Skocz do zawartości

Czujnik dźwięku pracujący z Arduino


Ciamek007

Pomocna odpowiedź

Witam! Jestem bardzo początkującym elektronikiem, od niedawna próbuję zdziałać coś w Arduino i aktualnie próbuję wykorzystać ten czujnik, by powstało coś w rodzaju sensora dźwięku.

Wszystko zatem cacy, podłączam Vin do napięcia 3,3V, GND do masy i DC do analogowego wejścia Arduino. Krótki program badający napięcie na wejściu pokazuje, że praktycznie cały czas napięcie wynosi ok 0,7V - i prawidłowo, bo takie według instrukcji powinno być w przypadku ciszy. Problem natomiast w tym, że ciężko wykrzesać z mikrofoniku jakąś sensowną zmianę - powiedzenie czegoś głośniej czy dmuchnięcie w mikrofon powoduje wprawdzie zmianę odczytywanego napięcia, ale po ustaniu źródła dźwięku utrzymuje się ono dłużej na tym nowym poziomie, czasem bardzo powoli opadając. Innym razem natomiast pojawiają się jakieś zawichrowania, napięcie skacze i ląduje nagle nawet na wartości ok. 0,4V. Chyba nie do końca powinno tak być, prawda? Czy robię coś źle? A może mój sposób pojmowania działania tego czujnika z mikrofonem mija się z prawdą?[/center]

Link do komentarza
Share on other sites

Sygnał audio, który dostaniesz z mikrofonu będzie elektrycznym odwzorowaniem fali akustycznej. Ta nigdy nie będzie stałym napięciem, bo składa się z mnóstwa drgań - to właśnie słyszysz jako dźwięki. Nie wiem czym jest "coś w rodzaju sensora dźwięku", bo samym czujnikiem jest mikrofon. Możesz coś z sygnałem elektrycznym robić, ale aby go wciągnąć do procesora musisz bardzo często mierzyć napięcie.

No bo wyobraź sobie bardzo poplątany sygnał np. mowy: są w nim górki i dołki, szczyty i doliny w zupełnie nieprzewidywalny sposób następujące po sobie, a każde "coś" trwa np. 1/1000s. A teraz Ty bierzesz swój przetwornik i mierzysz z tego wszystkiego powiedzmy dwa punkty i dostajesz jakieś dwa napięcia. Czy one w jakikolwiek sposób świadczą o sygnale? Absolutnie nie. Musisz mieć cały sygnał żeby móc go oceniać.

Zrób więc na początek taki eksperyment: zadeklaruj tablicę o długości kilkuset int-ów (żeby zmieściła się w mikroskopijnej pamięci RAM Arduino) a potem w pętli szybko zapisuj do niej wyniki pomiarów z ADC wykonywanych najczęściej jak się da. Dopiero gdy tablica się zapełni, w drugiej pętli wypisz jej zawartość na monitorze portu szeregowego. Zobaczysz ciąg kilkuset liczb - to właśnie jest sygnał dźwiękowy. Gdy będziesz próbę powtarzał - raz w w ciszy, raz głośno gwiżdżąc a innym razem mówiąc cokolwiek zobaczysz, że ciągi liczb są zupełnie różne. Czasem - gdy sygnał jest prosty typu cisza - liczby powinny być w miarę podobne, gdy jest to prosty dźwięk (samogłoska aaaaaa.. lub gwizd) powinieneś dostrzec jakiś okres górek i dołków itd. Co dalej?

W tym miejscu zaczyna się tzw. cyfrowe przetwarzanie sygnałów (DSP) - bardzo rozległa dziedzina, bardzo ściśle związana z matematyką, pozwalająca na obróbkę takich ciągów liczb i wyciąganie z nich potrzebnych nam cech. Co zatem konkretnie chcesz zrobić?

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Do pomiaru napięcia wykorzystuję najprostszy program z samouczków i analogowe wejścia bezpośrednio na Arduino (tak, wiem, że to się słabo nadaje i jest mało warte, ale nie zależy mi na super dokładności), ale odpowiedź marka1707 faktycznie dała mi do myślenia, jak to wygląda, co jest w porządku, a co nie 🙂 To, co chciałbym w swoich eksperymentach póki co uzyskać, to jedynie informacja, gdy poziom głośności dźwięku przekroczy jakąś ustaloną wartość.

Link do komentarza
Share on other sites

To teraz zastanów się co znaczy "poziom głośności" w kontekście tego co napisałem. Nie jest to na pewno wysokość jednego "szczytu" sygnału. Już raczej suma modułów amplitud liczona za jakiś czas, np. z ostatniej 1/10 sekundy - to trywialne i może zadziałać. Możesz też zacząć odróżniać dźwięki wybuchowe (klaśnięcia, strzały) od ciągłych - właśnie przez badanie zachowania się kolejno liczonych głośności w czasie. No, masz pole do popisu..

Napisz jakie decyzje podjąłeś (tj. jak chcesz to zrobić) i jakie próby już wykonałeś. Wejścia analogowe Arduino nadają się, ale najpierw musisz oprogramować ADC tak, by co dokładnie ten sam czas (to ważne - inaczej nici z przetwarzania sygnału) pobierał próbkę i składował ją w pamięci. Możesz też "obrabiać" ją od razu, bez zapamiętywania (np. doliczać do sumy modułów), ale wtedy operacja musi być bardzo prosta.

EDIT: I jeszcze jedno: sygnał z tego mikrofonu jest dopasowany do możliwości ADC w Arduino więc jest nałożony na pewną składową stałą - sam o tym pisałeś. Wszystkie pomiary są więc dodatnie w sensie znaku liczb. Dźwięk nie ma składowej stałej, bo ciśnienie powietrza w pokoju nie rośnie w trakcie mówienia tylko oscyluje wokół jakiejś zupełnie nieważnej wartości. A i sam przetwornik ADC procesora mierzy tylko napięcia dodatnie. Dlatego pierwsze co musisz zrobić po złapaniu kompletu próbek do tablicy, to policzyć ich wartość średnią i odjąć to od każdej z nich. Wtedy dostaniesz liczby dodatnie i ujemne, ale ich suma będzie zerowa - jak w prawdziwym dźwięku. Dopiero teraz możesz badać amplitudy itd. Mógłbyś też użyć cyfrowego filtru górnoprzepustowego, ale na to chyba jeszcze za wcześnie...

Link do komentarza
Share on other sites

Wielkie dzięki za naprowadzenie!

Wykorzystując wiedzę na temat tego, w jaki sposób dźwięk jest faktycznie interpretowany przez elektronikę, napisałem prosty program wykorzystujący wskazówki, które otrzymałem i póki co spełnia moje potrzeby - pytanie, czy nie jest zbyt toporny i czy na pewno dobrze zastosowałem się do rad:

 int tab[100];
 int cisza=0;
 int granica=100;  // wartość "głośności"
 int czas=20;      // czas występowania "głośności"

void setup() {
 Serial.begin(9600);

 for (int i=0;i<100;i++) //pobranie próbki "ciszy"
 tab[i]=analogRead(A5);

 for(int i=0;i<100;i++)  //wyznaczenie wartości średniej "ciszy"
 cisza=cisza+tab[i];
 cisza=cisza/100;
}

void loop() {
 for (int i=0;i<100;i++) 
 tab[i]=analogRead(A5)-cisza; //zebranie sygnałów do tablicy z odjęciem "ciszy"

 int licznik=0;
 for (int i=0;i<100;i++){
   if(abs(tab[i])>granica) licznik++;   // sprawdzenie, czy któraś wartość przekroczyła granicę
   if(licznik>czas){                   // warunek, żeby nie reagowało na zbyt krótki czas wystąpienia dźwięku
     Serial.println("Słyszę!");
     break;
   }
 }
}

Ogólnie rzecz biorąc, mocno uprościłem sprawę, bo skoro głównie chodzi mi o wykrywanie samego istnienia dźwięku, zacząłem sprawdzać, czy wartości nie zaczęły podejrzanie często przekraczać wcześniej ustalonej doświadczalnie granicy.

Zauważyłem też, że gdy w czasie programowania wypisywałem jeszcze wszystkie wartości zmniejszone o "ciszę", to faktycznie oscylowały one w okolicy zera. Po dmuchnięciu w mikrofon i pojawieniu się fali różnych odchyleń, wszystkie wartości co prawda ponownie zaczęły być do siebie zbliżone, ale były to na przykład nawet 30 czy -40 i powolutku schodziły do zera - i to chyba to samo, o czym pisałem już wcześniej. Czy ktoś zna może przyczynę takiego zjawiska?

Link do komentarza
Share on other sites

Dobrze, że zaczynasz łapać choć moim zdaniem w powyższym kodzie popełniasz jeden istotny błąd: nie możesz zakładać, że wartość średnia jednego kompletu próbek jest taka sama jak innego. To co zrobiłeś bardziej przypomina jednorazową kalibrację toru: łapiesz komplet próbek "wzorcowych" i zakładając, że to cisza uczysz w ten sposób program co ma robić z następnymi zestawami danych. Jeżeli ten wzorzec będzie zły, to potem wszystko dalej będzie złe.

Spróbuj prościej:

1. Łapiesz próbki do tablicy.

2. Liczysz ich średnią.

3. Odejmujesz tę średnią od każdej próbki tego samego zestawu próbek.

4. Teraz dopiero robisz potrzebne obliczenia (np. poszukiwanie przekroczeń progów) na "uzdatnionym" zestawie.

5. Wracasz do pkt. 1.

Dzięki takiemu podejściu "kalibracja zera" działa zawsze od nowa, na najnowszym zestawie sampli.

Nie mamy schematu modułu mikrofonu więc trudno powiedzieć jakie są jego charakterystyki częstotliwościowe. Od nich zależy m.in. sposób powrotu napięcia do "średniej" np. po niesymetrycznych impulsach typu klaśnięcie. Łapiesz bardzo mało próbek (tylko 100) więc trudno powiedzieć które fragmenty dźwięku odbierasz a których nie słyszysz robiąc wtedy obliczenia. Jeżeli typowa konwersja taka jaką wykonuje analogRead() trwa ok. 100us to cały okres "słuchania" to tylko 10ms. Stałe czasowe toru są z 10 razy dłuższe a poza tym dmuchanie w mikrofon na pewno go przesterowuje. Wtedy to co dostajesz już zupełnie nie ma to nic wspólnego z nadchodzącym dźwiękiem. Możesz zrobić tak: zadeklaruj większą tablicę, np. o długości 800, wykonaj kroki 1-5 algorytmu usuwania składowej stałej a potem wypisz sobie wszystkie wartości, zapamiętaj w pliku tekstowym i wrzuć do jakiegoś arkusza kalkulacyjnego. Wtedy zobaczysz "naocznie" jak ten sygnał wygląda i kiedy zaczyna być z dołu lub z góry obcinany na płasko przez ograniczenia analogowe toru. To taki oscyloskop dla cierpliwych 🙂

Acha i jeszcze jedno: przy sumowaniu wartości wielu próbek możesz bardzo łatwo przepełnić 16-bitowego int'a (zakres -32768..+32767) a wtedy cała policzona średnia jest błędna. Lepiej używaj w tym miejscu typu int32_t.

Zamiast liczenia liczby przekroczeń progu po prostu sumuj moduły wszystkich złapanych próbek i dopiero tę wartość porównuj z jakoś eksperymentalnie dobranym progiem. Możesz zawsze sobie tę sumę wypisać - będziesz widział jak silny był sygnał w czasie próbkowania.

W prawdziwym DSP wartość energii w sygnale liczy się przez sumowanie kwadratów wartości próbek - po pierwsze kwadraty zawsze są dodatnie 🙂 a po drugie energia to całka z mocy a ta jest kwadratem wielkości sygnału. Gdybyś chciał to na próbę zrobić i przekonać się czy lepiej działa suma próbek czy suma ich kwadratów, uważaj na zakresy liczb i rozszerz typy odpowiednich zmiennych do 32 bitów.

------------------------------------

EDIT: Przetwornik ADC mierzy sygnał wejściowy w pewnym zakresie napięć. Zwykle 0V to wynik 0, ale już najwyższe napięcie które odpowiada wynikowi 1023 (tzw. full scale) jest w pewnym sensie programowalne. Zasadą działania ADC jest bowiem porównywanie mierzonego napięcia z tzw. napięciem odniesienia (VREF). W procesorze mega328 jest to domyślnie napięcie zasilania - i tu w zależności od typu Arduino może to być 5V lub 3.3V, ale w prosty sposób jako VREF możesz wybrać wejście (pin) AREF lub wewnętrzne, stabilne źródło odniesienia 1.1V. W zależności od wybranego VREF to samo napięcie podane na wejście będzie zamieniane w ADC na inną liczbę.

Mając dajmy na to Vin = 0.7V:

- przy VREF = 5V dostajesz wynik (0.7V/5V) * 1024 = 143

- przy VREF = 3.3V będzie to (0.7V/3.3V) * 1024 = 217

- a przy 1.1V już 651.

Tak więc obniżanie VREF powoduje jakby zwiększanie "wzmocnienia" przetwornika. Nie można tego robić bez ograniczeń, producent zwykle podaje minimalne VREF przy jakim przetwornik jeszcze zachowuje swoje parametry i mierzy sensownie.

Tory analogowe a w szczególności akustyczne są projektowane tak, by były prawie symetryczne - w sensie dopuszczalnego "odchylania" napięcia od wartości średniej. Robi się tak dlatego, że i dźwięk jest w dłuższym okresie czasu symetryczny. W dużym uproszczeniu: jeżeli mamy do dyspozycji np. zasilanie 3.3V to głupotą jest ustalanie wartości średniej napięcia w torze na poziomie 0.5V, bo wtedy poprawnie będą przenoszone sygnały tylko od 0.0V do 1.0V - symetrycznie wokół 0.5V. Reszta zakresu (od 1 do 3.3V) będzie zmarnowana. Jeżeli masz źródło, które ma wartość średnią właśnie 0.7V to można oczekiwać, że na wyjściu poprawne będą jedynie sygnały z zakresu 0-1.4V. W tym przypadku ADC pracujący domyślnie z VREF=5V jest stratą precyzji, bo większa część jego zakresu (od 1.4V aż do 5V) jest niewykorzystana. W Arduino jest funkcja:

https://www.arduino.cc/en/Reference/AnalogReference

"podłączająca" różne źródła napięcia odniesienia do ADC. W Twoim przypadku najlepiej byłoby "stworzyć" z dwóch oporników i kondensatora dzielnik z 5V oddający 1.4V (na pewno umiesz je policzyć) i podłączyć to do pinu VREF. Wtedy wystarczy wywołać:

analogReference(EXTERNAL);

i nagle masz dużo czulszy ADC. Możesz też sprawdzić jak zadziała dużo prostsze, bo bez żadnych elementów dodatkowych:

analogReference(INTERNAL);

ale wtedy VREF=1.1V (w mega328, bo różne procki mają różnie) i to może być nawet trochę za mało.

Spróbuj i napisz jak zmieniły się wartości sumowanej średniej i samych próbek dźwięku. Powinny znacznie urosnąć co będzie oznaką większej precyzji pomiarów 🙂 Sprawdź, czy dla głośniejszych dźwięków i VREF=1.1V nie podchodzą pod 1023 bo to z kolei będzie oznaczać "obijanie się o sufit" nowego zakresu ADC.

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...