Bei der Entwicklung eines Spiels finden Sie möglicherweise Werte, die für Ihre Bedürfnisse zu laut sind. Der übliche Fall ist analoge Benutzereingaben (Maus, Touch oder Joystick), aber das Geräusch könnte auch von den Spielsystemen stammen, wie etwa von Physik oder Lenkverhalten, bei denen angenäherte Lösungen oder nicht zusammenhängende Änderungen zu Geräuschen führen. In diesem Lernprogramm erfahren Sie eine einfache Möglichkeit, diese Störgeräusche zu glätten. Die Codebeispiele sind in C #, sie können jedoch leicht an andere Sprachen angepasst werden.
Der einfachste Weg, den variierenden Wert zu glätten, besteht darin, eine Anzahl der vergangenen Proben zu nehmen und diese zu mitteln. Wir verwenden eine konstante Anzahl von Samples, daher ist ein Array mit fester Größe eine natürliche und effiziente Wahl, um diese zu speichern. Um das Array nicht zu verschieben, verwenden wir einen Trick: die Datenstruktur "Ring Buffer".
Beginnen wir mit der Definition der Daten, die in unserer Utility-Klasse gespeichert werden sollen:
öffentliche Klasse SlidingAverage float [] buffer; Float-Summe; int lastIndex; public SlidingAverage (int num_samples, float initial_value) buffer = new float [num_samples]; lastIndex = 0; Reset (Anfangswert);
Hier haben wir unseren Samples-Puffer, die Summe der Samples und den zuletzt verwendeten Index in das Array. Der Konstruktor ordnet die Pufferarraysätze zu lastIndex
auf null und ruft die zurücksetzen ()
Methode:
public void reset (float-Wert) sum = wert * buffer.Length; für (int i = 0; iHier füllen wir den Puffer mit dem angegebenen Anfangswert und setzen die Summe entsprechend. Diese Methode kann jedes Mal verwendet werden, wenn Sie die Glättung erneut starten müssen, um Speichereffekte der letzten Samples zu vermeiden.
Nun die Hauptmethode: Einen neuen Wert in unseren Ringpuffer stecken:
public void pushValue (Gleitkommawert) sum- = buffer [lastIndex]; // subtrahiere den ältesten Abtastwert von der Summe Summe + = Wert; // füge den neuen Beispielpuffer hinzu [lastIndex] = value; // das neue Beispiel speichern // den Index vorrücken und ihn umwickeln lastIndex + = 1; if (lastIndex> = buffer.Length) lastIndex = 0;Hier überschreiben wir das älteste Sample bei
lastIndex
mit dem neuen, aber vorher passen wir die Summe an, indem wir das alte Sample abziehen und das neue hinzufügen.Dann kommen wir voran
lastIndex
damit zeigt es auf die nächste Probe (die jetzt die älteste ist). Aber wenn wir nur weiter kommenlastIndex
Das Array wird in kürzester Zeit ausgehen. Wenn es also aus der Array-Bindung herauskommt, wird es auf Null gesetzt.Deshalb ist es eine Ring Puffer. Es ist im Wesentlichen dasselbe wie das Verschieben des Arrays und das Anhängen des neuen Samples, aber viel schneller, da wir die Werte im Speicher nicht kopieren, sondern nur den Index umschließen.
Jetzt fehlt nur noch der geglättete Wert:
public float getSmoothedValue () return sum / buffer.Length;Das ist es; Wir teilen die Summe einfach durch die Anzahl der Proben, um den Durchschnitt zu erhalten. Wenn wir die Summe nicht speichern würden, müssten wir sie hier aus den Samples berechnen.
Ergebnisse
Schauen wir uns die Ergebnisse an:
Die schwarze Linie ist das ursprüngliche Signal (Sinuswelle mit etwas Rauschen), die weiße Linie wird mit zwei Samples geglättet und die rote Linie wird mit vier Samples geglättet.Wie Sie sehen, machen es sogar einige Samples merklich weicher, aber je mehr Samples wir verwenden, desto mehr bleibt es hinter dem ursprünglichen Signal zurück. Das ist zu erwarten, da wir in der Vergangenheit nur Samples aus der Vergangenheit verwenden. Wenn Sie nachbearbeiten, können Sie die geglätteten Werte rechtzeitig verschieben, um die Verzögerung zu vermeiden.
Fazit
Sie verfügen jetzt über eine einfache Dienstprogrammklasse, mit der alle geräuschintensiven eingehenden Werte geglättet werden können, unabhängig davon, ob es sich um Benutzereingaben, eine Objektspur oder eine Geschwindigkeitsanzeige handelt.
Sie kann durch Hinzufügen von Stichprobengewichten weiter verbessert werden (wir haben einen einfachen Mittelwert mit Konstante verwendet
1 / N
gewicht), aber das ist ein riesiges thema, digitale signalverarbeitung und überlassen wir es einem zukünftigen Tutorial!