Sfat rapid Folosiți structura de date Buffer Ring pentru a obține valori netezite

În timp ce dezvoltați un joc, puteți găsi valori care sunt prea zgomotoase pentru nevoile dvs. Cazul comun este intrarea analogică a utilizatorului (mouse-ul, atingerea sau joystick-ul), dar zgomotul poate proveni și din sistemele de jocuri, cum ar fi comportamentul fizicii sau al direcției, unde soluții aproximative sau schimbări necontrolate conduc la zgomot. În acest tutorial, veți învăța o modalitate simplă de a netezi aceste valori zgomotoase. Exemplele de cod sunt în C #, dar sunt ușor de adaptat la orice altă limbă.


Ring Buffer

Cea mai simplă modalitate de a netezi valoarea variază este să luați un număr de probe anterioare și să le mediați. Vom folosi un număr constant de eșantioane, deci o matrice de dimensiuni fixe este o alegere naturală și eficientă pentru stocarea acestora. Apoi, pentru a evita schimbarea matricei, vom folosi un truc: structura de date "tampon inelar".

Să începem prin definirea datelor stocate în clasa noastră de utilitate:

 clasa publică SlidingAverage float [] buffer; sumă float; int lastIndex; public SlidingAverage (int num_samples, float initial_value) buffer = float nou [num_samples]; lastIndex = 0; reset (initial_value); 

Aici avem tamponul nostru de probe, suma probelor și ultimul indice utilizat în matrice. Constructorul alocă matricea tampon, seturi lastIndex la zero și sună la reset () metodă:

 public void reset (valoare float) sum = valoare * tampon.Lungime; pentru (int i = 0; i 

Aici, umplem tamponul cu valoarea inițială specificată și setăm suma pentru a se potrivi. Această metodă poate fi utilizată oricând trebuie să reporniți netezirea pentru a evita efectele de memorie ale probelor anterioare.

Acum, metoda principală: împingând o nouă valoare în tamponul nostru de inel:

 void public void pushValue (valoare float) sum- = buffer [lastIndex]; // se scade cel mai vechi eșantion din suma sumă + = valoare; // adăugați noul tampon de eșantion [lastIndex] = valoare; // păstrează noul exemplu // avansează indexul și îl înfășoară în jurul lui lastIndex + = 1; dacă (lastIndex> = buffer.Length) lastIndex = 0; 

Aici, înlocuim cel mai vechi eșantion la lastIndex cu cel nou, dar înainte de aceasta ajustăm suma scăzând eșantionul vechi și adăugând unul nou.

Apoi, avansăm lastIndex astfel încât să indice următorul eșantion (care este acum cel mai vechi). Dar dacă avansăm lastIndex vom iesi din matrice in cel mai scurt timp, asa ca atunci cand se iese din matrice legata, o inlaturam la zero.

De aceea e a inel tampon. Este, în esență, același lucru cu schimbarea matricei și adăugarea noului eșantion, dar mult mai rapid, deoarece, în loc să copiem valorile din memorie, înfășurăm în jurul indexului.

Singurul lucru care lipsește este obținerea valorii netezite:

 public float getSmoothedValue () retur sum / buffer.Length; 

Asta e; doar împărțim suma cu numărul de probe pentru a obține media. Dacă nu am depozitat suma, ar trebui să o calculam aici din eșantioane.


Rezultate

Să aruncăm o privire asupra rezultatelor:


Linia neagră este semnalul original (unde sinusoidală cu un zgomot), linia albă este netezită folosind două eșantioane și linia roșie este netezită folosind patru mostre.

După cum vedeți, chiar și câteva eșantioane fac ca acesta să fie considerabil mai fin, dar cu cât mai multe probe folosim, cu atât mai mult se află în spatele semnalului original. Așa se așteaptă, deoarece folosim doar probele anterioare în cazul în timp real. Dacă procesați ulterior, puteți schimba valori netezite în timp pentru a evita decalajul.


Concluzie

Acum aveți o clasă de utilitate simplă, care poate fi utilizată pentru a netezi orice valori de intrare zgomotoase, indiferent dacă este vorba de intrarea utilizatorului, de o trasată de obiect sau de un indicator de viteză.

Aceasta poate fi îmbunătățită suplimentar prin adăugarea greutăților de probă (am folosit o medie simplă cu o constantă 1 / N greutate), dar acesta este un subiect imens, procesarea semnalelor digitale și mai bine la un tutorial viitor!