Soluții

“Murdarire” audio cu amprenta microfonului

Am creat acest script pentru a rezolva o problemă fundamentală: modelul AI era antrenat pe sunete curate, înregistrate profesional, dar pe dispozitiv primea sunete „murdărite” de caracteristicile microfonului intern al ESP32. Microfonul acesta nu este unul de studio — are o răspuns în frecvență limitat, introduce un zgomot de fond propriu și colorează sunetul într-un mod specific. Din cauza asta, aceeași împușcătură sau sirenă suna diferit în datele de antrenare față de ce auzea dispozitivul în realitate, iar modelul nu le mai recunoștea corect.

Soluția: am „murdărit” datele de antrenare ca să semene cu ce aude microfonul

1. Am calculat amprenta acustică a microfonului

Am redat un semnal de test cunoscut (un sweep de frecvențe) prin difuzor și l-am înregistrat cu microfonul ESP32. Comparând semnalul original curat cu cel înregistrat, am extras prin deconvoluție ceea ce se numește „răspuns la impuls” — practic amprenta acustică a microfonului, care descrie exact cum modifică el sunetul (ce frecvențe le atenuează, ce frecvențe le amplifică, ce distorsiuni introduce).

2. Am aplicat această amprentă peste toate datele de antrenare

Am luat fiecare fișier audio din setul de date și l-am trecut prin convoluție cu acest răspuns la impuls. Efectul este ca și cum acel sunet ar fi fost înregistrat direct cu microfonul ESP32, chiar dacă inițial era un fișier curat descărcat de pe internet.

3. Am limitat banda de frecvențe

Microfonul ESP32 nu captează fidel frecvențe foarte joase sau foarte înalte. Am aplicat un filtru care păstrează doar frecvențele între 100 Hz și 7000 Hz, simulând limitările reale ale hardware-ului.

4. Am aplicat compresie

Microfonul și circuitul analogic al dispozitivului introduc o ușoară compresie naturală — sunetele foarte puternice nu cresc proporțional, ci se „saturează” ușor. Am simulat acest efect printr-o funcție matematică de compresie.

5. Am adăugat zgomot de fond real

Am înregistrat liniștea cu ESP32 — chiar și când nu e niciun sunet, microfonul produce un zgomot de fond slab, caracteristic. Am amestecat acest zgomot peste fiecare fișier audio de antrenare, la intensități variate, pentru ca modelul să învețe să ignore acest zgomot de fond și să se concentreze pe sunetele importante.

6. Am variat volumul

În realitate, sunetele ajung la microfon la volume diferite în funcție de distanță și mediu. Am aplicat o amplificare aleatorie pentru fiecare fișier, astfel încât modelul să nu depindă de un anumit nivel de volum.

De ce este nevoie de acest pas

Fără el, modelul ar fi fost antrenat într-o „lume ideală” a sunetelor curate, dar ar fi funcționat pe un dispozitiv care aude lumea printr-un microfon imperfect. Este ca și cum ai antrena pe cineva să recunoască mașini uitându-se la fotografii profesionale, dar apoi îi ceri să le recunoască printr-o cameră de supraveghere neclară și cu zgomot.

Prin aplicarea acestor transformări, am adus datele de antrenare cât mai aproape de condițiile reale, iar modelul a învățat să recunoască sunete exact așa cum le aude dispozitivul — cu toate imperfecțiunile incluse. Acest lucru a îmbunătățit semnificativ acuratețea clasificării pe dispozitiv.

[mai mult...]

Echivalenta matematica Python-Arduino

Am asigurat echivalența matematică dintre calculul spectrogramei mel din Python și cel de pe microcontroler prin mai multe strategii concrete:

1. Parametri identici

Am pornit de la aceiași parametri exacti în ambele medii: rată de eșantionare de 16kHz, FFT de 512 puncte, fereastră de 30ms (480 eșantioane), hop de 10ms (160 eșantioane) și 40 de benzi mel.

2. Filterbank-ul mel — exportat direct din Python

În loc să reimplementez manual formulele de calcul al benzilor mel pe microcontroler (unde orice diferență mică de rotunjire ar fi alterat rezultatele), am generat filterbank-ul direct în Python folosind librosa, cu setările sale implicite. Rezultatul — o matrice de 40×257 valori — l-am salvat cu 8 zecimale de precizie într-un format pe care microcontrolerul îl poate citi direct. Astfel, dispozitivul folosește exact aceiași coeficienți numerici ca Python, fără nicio aproximare.

3. Fereastra Hann — aceeași formulă

Am replicat pe microcontroler aceeași formulă matematică de fereastră Hann simetrică pe care o folosește librosa, cu același divisor, producând valori identice.

4. FFT și spectrul de putere — aceeași secvență de operații

Pe microcontroler, pentru fiecare cadru: aplic fereastra Hann, completez cu zerouri, rulez transformata Fourier, apoi calculez spectrul de putere ca suma pătratelor părților reale și imaginare. În Python, am configurat librosa să facă exact același lucru, inclusiv dezactivarea padding-ului simetric la capetele semnalului — un detaliu critic, pentru că altfel Python ar fi generat cadre decalate și un număr diferit de frame-uri.

5. Conversia în decibeli și normalizarea — aceeași formulă, aceleași praguri

Atât pe microcontroler cât și în Python, aplic aceeași secvență: convertesc puterea în decibeli cu același epsilon mic pentru a preveni logaritmul din zero, limitez valorile la intervalul de la -80 la 0 decibeli, apoi normalizez totul în intervalul 0–1 prin aceeași formulă liniară.

6. Verificare pas cu pas

Am scris un script Python care reproduce pas cu pas exact ce face microcontrolerul: încarcă audio, convertește la virgulă mobilă, calculează volumul, aplică filtrul de liniște, calculează spectrograma cadru cu cadru prin aceeași buclă manuală. Fiecare pas intermediar este afișat în același format ca pe dispozitiv, astfel încât pot compara linie cu linie valorile și identifica orice divergență.

7. Antrenarea modelului pe date procesate identic

Ca să mă asigur că modelul AI a fost antrenat pe aceleași tipuri de spectrograme pe care le va primi pe dispozitiv, am preprocesat toate datele de antrenare în Python cu exact aceiași parametri și aceeași formulă de conversie și normalizare. Astfel, modelul a „văzut” în antrenare exact același tip de reprezentare pe care o primește și pe dispozitiv.

Pe scurt: nu am reimplementat independent matematica pe microcontroler, ci am exportat componentele critice direct din Python, am replicat doar operațiile aritmetice simple cu aceleași formule și constante, și am construit un sistem de verificare care compară valoare cu valoare rezultatele intermediare dintre cele două medii.

[mai mult...]

Clasificare sunet 3 clase Arduino

Am construit un sistem unificat de gestionare audio care funcționează ca „urechile” dispozitivului Sentinel — un modul care ascultă non-stop mediul înconjurător și identifică în timp real sunete potențial periculoase sau demne de atenție.

Captura audio: Am configurat microfonul intern al dispozitivului M5Stack CoreS3 să captureze audio mono la o rată de eșantionare de 16kHz. La fiecare ciclu, citesc un fragment de 2048 de eșantioane de la microfon. Imediat după citire, aplic o curățare a semnalului: mai întâi elimin offset-ul DC printr-un filtru simplu de ordinul întâi (care scoate componenta continuă pe care microfonul o poate introduce), apoi aplic un noise gate — dacă un eșantion este sub un prag de 250, îl setez la zero, eliminând astfel zgomotul de fond foarte slab care nu are relevanță.

La final, limitez valorile pentru a preveni depășirile. Fragmentele curățate sunt scrise într-un buffer circular alocat în PSRAM, care reține ultimele 5 secunde de audio — când se umple, datele cele mai vechi sunt suprascrise automat.

Procesarea AI: O dată pe secundă, verific dacă am suficiente eșantioane acumulate (16.000 de eșantioane = 1 secundă) și extrag un clip de 1 secundă din buffer. Primul lucru pe care îl fac este să calculez nivelul de volum (RMS) al clipului. Dacă volumul este sub un prag foarte mic, consider că este liniște — în acest caz, raportez direct „Background” cu încredere zero și nu mai rulez AI-ul deloc. Asta economisește resurse semnificative pe un microcontroler cu putere de calcul limitată.

Spectrograma mel: Dacă clipul conține sunet real, trec la etapa de extragere a trăsăturilor. Convert eșantioanele din format întreg (int16) în virgulă mobilă (float, intervalul -1.0 la 1.0), apoi calculez o spectrogramă mel — aceasta transformă sunetul brut într-o reprezentare vizuală compactă a frecvențelor în timp, exact cum face și biblioteca Python librosa. Procesul implică: aplicarea unei ferestre Hann pe fiecare cadru, calculul FFT (transformata Fourier rapidă) pentru a obține componentele de frecvență, apoi multiplicarea cu un filterbank mel care grupează frecvențele în benzi perceptuale (cum aude urechea umană).

Am optimizat acest pas folosind un filterbank mel „sparse” — în loc să stochez toți cei ~10.000 de coeficienți (dintre care majoritatea sunt zero), stochez doar cele ~200 de valori nenule, ceea ce reduce memoria folosită și accelerează calculul. La final, convertesc valorile de putere în decibeli, le limitez la un prag de -80 dB și le normalizez în intervalul 0–1.

Inferența AI: Spectrograma mel rezultată (o matrice de 63 cadre × 40 benzi mel) este trimisă unui model TensorFlow Lite care rulează direct pe dispozitiv. Modelul clasifică sunetul în una din 13 categorii: Background, Gunshot (împușcătură), Explosion (explozie), Screaming (țipăt), Siren (sirenă), Alarm (alarmă), Glass_breaking (spargere de geam), Fire (foc), Dog_alert (lătrat de alertă), Knock (bătaie în ușă), Vehicle_horn (claxon), Screech (scrâșnet) și Slam (trântire).

Aleg categoria cu scorul cel mai mare și stochez atât predicția, cât și nivelul de încredere, pe care le expun prin funcții getter pentru ca restul sistemului (interfața grafică, modulul de alertare etc.) să le poată accesa.

Modul de debug: Am adăugat o funcție specială care rulează întregul pipeline — de la conversia int16→float, la calculul RMS, spectrograma mel și inferența AI — pe un clip WAV de test încorporat direct în firmware. Asta îmi permite să verific că fiecare etapă produce rezultate corecte, comparându-le cu valorile obținute de scripturile Python echivalente, fără să depind de un microfon live sau de condiții acustice reale.

Înregistrarea pe SD: Inițial, sistemul suporta și salvarea audio pe card SD. Am eliminat complet această funcționalitate pentru a optimiza performanța — pe un microcontroler ESP32, operațiile de scriere pe SD introduceau întârzieri care afectau captura audio și inferența. Am lăsat doar funcții stub care returnează valori implicite, pentru a nu strica codul existent care le apelează.

Gestionarea memoriei: Am avut grijă să aloc toate bufferele mari (audio float, features, FFT) în PSRAM (memoria externă), păstrând în DRAM (memoria internă rapidă) doar bufferele FFT necesare bibliotecii ArduinoFFT și coeficienții mel sparse — aceștia din urmă fiind suficient de mici (~800 bytes) pentru a încăpea fără probleme. Această separare este esențială pe ESP32-S3, unde DRAM-ul este limitat dar PSRAM-ul oferă megabyți de spațiu suplimentar.

[mai mult...]