Snabbtips Använd datastrukturen Ringbuffert till jämna värden

När du utvecklar ett spel kan du hitta värden som är för bullriga för dina behov. Det vanliga fallet är analog användarinmatning (mus, beröring eller joystick), men bruset kan också komma från spelsystemen, som fysik eller styrningsbeteenden, där approximativa lösningar eller oavhängiga ändringar leder till brus. I denna handledning lär du dig ett enkelt sätt att släta bort de högljudda värdena. Kodsexemplen finns i C #, men de är lätta att anpassa till något annat språk.


Ringbuffert

Det enklaste sättet att släta det varierande värdet är att ta ett antal av de tidigare proverna och genomsnittsa dem. Vi använder ett konstant antal prover, så en fast storlek är ett naturligt och effektivt val för lagring av dessa. Då, för att undvika att flytta den matrisen, använder vi ett knep: "ringbuffert" datastrukturen.

Låt oss börja med att definiera data som ska lagras i vår verktygsklass:

 offentlig klass SlidingAverage float [] buffert; float summa; int lastIndex; offentlig SlidingAverage (int num_samples, float initial_value) buffer = ny float [num_samples]; lastindex = 0; reset (initial_value); 

Här har vi vår proverbuffert, summan av proverna och det senast använda indexet i matrisen. Konstruktorn allokerar buffertuppsättningen, uppsättningar lastindex till noll och ringer till återställa() metod:

 public void reset (float värde) sum = värde * buffer.Length; för (int i = 0; i 

Här fyller vi bufferten med det angivna initialvärdet och anger summan som matchar den. Den här metoden kan användas när du behöver starta om utjämningen för att undvika minneseffekter från tidigare prover.

Nu är den viktigaste metoden: att trycka ett nytt värde i vår ringbuffert:

 public void pushValue (float värde) sum- = buffert [lastIndex]; // subtrahera det äldsta provet från summan summan + = värdet; // lägg till den nya provbufferten [lastIndex] = värde; // lagra det nya samplet // förbi indexet och linda det runt sistIndex + = 1; om (lastIndex> = buffer.Length) lastIndex = 0; 

Här skriver vi över det äldsta provet på lastindex med den nya, men innan det justerar vi summan genom att subtrahera det gamla provet och lägga till det nya.

Sedan går vi framåt lastindex så att den pekar på nästa prov (som nu är den äldsta). Men om vi bara går vidare lastindex vi rinner ut ur matrisen på nolltid, så när det kommer ut ur matrisen binds vi om det till noll.

Det är därför det är en ringa buffert. Det är i grunden detsamma som att flytta arrayen och lägga till det nya provet, men mycket snabbare eftersom istället för att kopiera värdena i minnet vi bara sätter sig runt indexet.

Nu är det enda som saknas får det jämnda värdet:

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

Det är allt; vi delar bara summan av antalet prover för att få genomsnittet. Om vi ​​inte lagrade summan skulle vi behöva beräkna det här från proverna.


Resultat

Låt oss ta en titt på resultaten:


Den svarta linjen är den ursprungliga signalen (sinusvåg med lite ljud), den vita linjen slätas med två prov och den röda linjen släpas med fyra prov.

Som du ser, gör det även ett fåtal prov märkbart mjukare, men ju fler prover vi använder desto mer ligger det bakom den ursprungliga signalen. Det förväntas eftersom vi bara använder tidigare prover i realtidsfallet. Om du är efterbehandling kan du flytta de jämnda värdena i tid för att undvika lagret.


Slutsats

Du har nu en enkel verktygsklass som kan användas för att släta alla bullriga inkommande värden, oavsett om det är användarinmatning, ett objektspår eller en hastighetsindikator.

Det kan förbättras ytterligare genom att lägga till provvikter (vi använde ett enkelt medelvärde med konstant 1 / N vikt), men det är ett stort ämne, digital signalbehandling och bättre kvar till en framtida handledning!