Algorithmus zum Komprimieren eines Stroms von 12-Bit-ADC-Daten in einem Cortex M0-Mikrocontroller

In meiner Anwendung tastet ein Cortex M0-Mikrocontroller derzeit ein ADC-Signal mit 10 kHz ab und sendet das Ergebnis über ein serielles USB-Kabel an einen Computer.

Ich möchte jetzt die Abtastrate auf über 100 kHz erhöhen und einen zweiten Kanal hinzufügen.

Die USB-Seriell-Verbindung ist auf 1–3 Mbit/s begrenzt, daher ist es physikalisch unmöglich, mit dieser Rate mit einem 12-Bit-ADC-Wert und 2 Bytes zu streamen, die für die Übertragung jedes Samples benötigt werden. Für zwei ADC-Kanäle, die bei 100 kHz abtasten, beträgt die erforderliche Bitrate ungefähr 4 Mbit/s:

B ich T _ R A T e = 2 C H × 2 B j T e S × 10 B ich T S × 100 × 10 3 = 4 × 10 6 B P S

Mir ist bewusst, dass ich 2 ADC-Samples in 3 Bytes einbauen könnte, aber ich möchte die ADC-Daten etwas komprimieren.

Welche Komprimierungsalgorithmen eignen sich für die Ausführung in einem Cortex M0-Mikrocontroller?

Es gibt Dutzende von Komprimierungsalgorithmen, aber ich suche nach etwas, das auf einer ressourcenbeschränkten Plattform ausgeführt werden kann.

Nur um sicherzugehen: Durch den Rechenaufwand können Sie immer noch alles schnell genug erledigen?
Ich weiß nicht viel über Komprimierung, aber ich denke, dass Sie bei bereits binären Daten in einem Stream dennoch Probleme haben werden, diese Daten zu komprimieren, ohne Informationen auf sinnvolle Weise zu verlieren. Sind 12-Bit-Rohdaten 100.000 Mal pro Sekunde wirklich das, was Sie brauchen? Ich denke, das wird auch deinen PC in die Knie zwingen, wenn du etwas mit den Werten rechnen willst. Gibt es keine Möglichkeit, Werte zu kombinieren? Ich denke, Ihre 12-Bit-Werte werden viel Rauschen haben. Warum nicht einen Mittelwert/RMS/was auch immer über mehrere Datenproben machen und das Ergebnis übertragen?
Es ist ein Kortex M0 (Fixpunkt), der mit etwa 64 MHz läuft. Es ist in der Lage, etwa 60 DMIPS zu erreichen.
@jwsc Diese Abtastrate wird benötigt, um schnelle Transienten zu erfassen.
Wie groß ist die Bandbreite der Daten bei der schnellen Transiente? Haben Sie genug "ruhige Zeit", damit die durchschnittliche Bandbreite geringer ist als der USB-Engpass?
64MHz für 4Mbit Übertragung? 16 Taktzyklen pro gesendetem Bit? Ich denke, Sie müssen die Zyklen jeder Funktion zählen, die Sie haben. Ich bin kein Experte, vielleicht kann das jemand bestätigen, aber das klingt ... problematisch. Das Komprimieren von Daten sogar auf einem PC hat einen sichtbaren Fortschrittsbalken. Es ist keine leichte Aufgabe.
Und wenn Sie nur die "Differenz" in 1 Byte "senden"? Mit einem Update etwas "von Zeit zu Zeit" ?
Ist der Eingang vollständig willkürliches weißes Rauschen oder hat er begrenzte dV/dt? Wenn letzteres der Fall ist, können Sie regelmäßig einen Frame senden und von diesem Wert nur ein paar Bits pro Abtastung um wie viel erhöhen oder verringern? Ähnlich wie bei vielen Videokomprimierungsalgorithmen mit gelegentlichen Keyframes und dazwischen nur der Unterschied.
Suchen Sie nach der Lempel-Ziv-Komprimierung, wenn Ihre Anwendungen eine verlustfreie Datenkomprimierung benötigen. Es gibt einen wirklich guten historischen Überblick über die Entwicklung dieses Algorithmus in der Mai2021-Ausgabe von IEEE Spectrum.

Antworten (4)

Ist das eigentlich eine Audioanwendung? Könnten Sie das Ganze durch ein dediziertes Audio-Interface ersetzen, das all diese Probleme für Sie löst, oder benötigen Sie den genauen DC-Pegel? 96ksps Stereo sind durchaus erreichbar.

Sie haben nicht viele MIPS, mit denen Sie arbeiten können, daher werde ich das folgende Codierungsschema vorschlagen. Es ist technisch gesehen eine variable Bitrate, also sollten Sie besser hoffen, dass Transienten selten sind. Es funktioniert für Samples bis zu 15 Bit.

  • Verfolgen Sie die vorherige Probe
  • Subtrahieren Sie das nächste Sample vom vorherigen Sample, nennen Sie das Ergebnis D
  • Untersuchen Sie Bits über 7. Entweder sind sie nur Einsen oder nur Nullen oder eine Mischung (was auf einen Übergang oder einen großen Anstieg hinweist).
  • Für die Fälle, die nur aus Einsen oder aus Nullen bestehen: Maskiere die unteren 7 Bits und übertrage sie an den Host.
  • Für den transienten Fall: Übertragen Sie das obere Byte des Samples (nicht D) mit dem oberen Bit auf "1", dann das untere Byte.
  • Senden Sie in regelmäßigen Abständen (z. B. alle 256 Abtastwerte) trotzdem den gesamten Abtastwert, wobei das obere Bit auf 1 gesetzt ist. Dies ermöglicht eine Wiederherstellung nach Fehlern.

Der Umbau ist einfach:

  • Wenn das oberste Bit eines Bytes 0 ist, erweitere die verbleibenden 7 Bits vorzeichenweise und füge sie dem vorherigen Sample hinzu, um das nächste Sample zu erhalten.
  • wenn das oberste Bit 1 ist, maskiere es, lese das nächste Byte und setze das Ergebnis zu einem Wort zusammen.
Ich finde diese Idee interessant. Dies ist keine Audio-Anwendung, sondern eine aktuelle Sampling-Anwendung. Meistens beträgt der Unterschied von einem Sample zum nächsten weniger als 7 Bit. Wenn ich Ihre Antwort richtig verstehe, werden die meisten Samples als einzelnes Byte dargestellt?
Ja, die meisten Samples wären 1 Byte.
Aber ich wiederhole den Vorbehalt der variablen Bitrate – wenn es viele Transienten in einem Burst gibt, ist es möglich, dass die Verbindung nicht mithält. Im Durchschnitt wird es aufholen. Dies kann eine Rolle spielen, wenn Sie versuchen, mit etwas anderem zu synchronisieren.
Ok, das halbiert im Grunde die erforderliche Bitrate von 4 Mbps auf 2. Transienten werden ein bisschen mehr hinzufügen, aber ich mag diese Idee, da der Overhead gering ist.
Es ist auch sehr nützlich, einen expliziten Loss-of-Sync-/Overflow-Marker zu haben.

Es gibt ein paar "einfache" Schritte, die Sie unternehmen können, um die benötigte Baudrate zu reduzieren:

  1. Benötigen Sie wirklich eine Auflösung von 12 Bit? Wenn Sie die 4 niederwertigsten Bits (die möglicherweise nur Rauschen sind) opfern können, reduzieren Sie die erforderliche Bandbreite um 33 % und sparen gleichzeitig einige Operationen ein.

  2. um welchen Betrag ändert sich das Signal "normalerweise" zwischen 2 aufeinanderfolgenden Samples. Wenn zum Beispiel in 95 % der Fälle der Unterschied nur bei den 6 niederwertigsten Bits liegt, können Sie Folgendes senden:

  • Wenn die Differenz weniger als 6 Bit beträgt, wird die Differenz als vorzeichenbehaftete Ganzzahl auf 7 Bit codiert (mit einer 1 als höchstwertigem (8.) Bit). Wenn die Differenz mehr als 6 Bit beträgt, senden Sie den gesamten neuen Wert auf 2 Bytes (also gibt es 4 Überschriften-Nullen auf dem höchstwertigen Byte, wodurch es leicht zu identifizieren ist)

Eine interessante Sache, die Sie in den Kommentaren gesagt haben, ist schließlich, dass Sie eine so hohe Abtastrate benötigen, weil Sie Transienten beobachten möchten.

Daher sehe ich 2 Lösungen, die etwas komplizierter sind, aber die für Ihre spezifische Anwendung erforderliche Baudrate erheblich reduzieren können:

  1. Wenn Sie nur die Transienten benötigen: Warum nicht auf sie "triggern"? Sie speichern alle Ihre Messungen in einem rollierenden Puffer (unter Verwendung von DMA, wenn Sie können). Dann verwenden Sie eine Softwareberechnung, um eine Transiente zu definieren (z. B. Differenz größer als X zwischen Messung N und N-N0). Wenn Sie einen erkennen, senden Sie nur die Werte um die Messung N herum. Wenn Ihre Transienten selten sind, wird Ihre erforderliche Baudrate sehr niedrig. NB: In dieser Version sehen Sie NUR die Transienten, nicht den Rest (kann Ihren Anforderungen entsprechen oder nicht, wenn nicht, siehe Lösung 4)

  2. Eine andere Lösung besteht darin, keine Abtastungen in regelmäßigen Zeitintervallen zu senden: Sie tasten immer noch mit 100 kHz ab (mit DMA im internen Puffer gespeichert), aber Sie übertragen nur einen Teil der Daten: Wenn sich Daten schnell ändern, senden Sie viele Daten ändert sich langsam, Sie senden wenige. Zum Beispiel :

  • Nehmen wir an, die letzte gesendete Messung ist M[N0] (Messung bei Abtastung #N0, 12 Bit)
  • Nennen wir N die aktuelle Messung (Wert=M[N]. Wenn N-N0 == 16, dann X, wobei die 12 niedrigstwertigen Bits von X M[N] sind und die 4 höchstwertigen N-N0- 1 = 15 = 0b1111
  • sonst, wenn abs(M[N]-M[N0]) > Schwelle: X (2 Bytes) gesendet, mit M[N] für die 12 niedrigstwertigen Bits und N-N0-1 für die 4 höchstwertigen Bits (dh die Anzahl der übersprungenen Messungen)
  • sonst: nichts senden.

Auf der Empfangsseite erhalten Sie also im Grunde einen Wert in der Form A[0:3].M[0:12], wobei A die Anzahl der übersprungenen Samples ist (also (A+1) sample_period die Zeit seit dem letzten Sample ) , und M den Wert dieser Probe. Wenn sich Ihr Signal langsam genug ändert und Transienten selten sind, haben Sie einen Durchschnitt von A zwischen 14 und 15 (lassen Sie uns konservativ sein und 14 sagen), also haben Sie eine durchschnittliche Bitrate von 2ch 16bits*100000Hz/15=213 kb / s : Das ist auf einem 1-Mbit / s-UART problemlos möglich

Hey, ich mag deine transiente Erkennungsidee. Wie Beispieldaten immer mit hoher Rate, aber langsamer und weniger senden, nur viel senden, wenn Transienten. Dies ist eine nette Lösung, die auf dem spezifischen Zweck des Geräts basiert. Schlau.
Insbesondere benötigt der SAC-ADC normalerweise länger, um 12-Bit-Lesevorgänge abzuschließen als 8/10-Bit-Lesevorgänge. Wenn Sie also die Auflösung opfern, kann dies automatisch eine höhere Abtastrate bedeuten.

Dies hat einen erheblichen Rechenaufwand zur Folge, Sie müssen sicherstellen, dass Sie alles rechtzeitig erledigen können. Sie müssen die gesamten Daten mehrmals durchgehen, dies ist von der Größenordnung O(n). Denn natürlich geht es nicht, ohne alle Daten mindestens einmal durchzugehen und man muss auch etwas damit anfangen. Vielleicht können Sie Zeit sparen, indem Sie den Sendevorgang automatisieren - Sie können einen Timer regelmäßig Interrupts auslösen lassen und dann die Übertragung der Daten mit DMA auslösen. Dadurch wird etwas Rechenleistung frei. Wenn Sie es brauchen, können Sie es vielleicht einfacher handhaben und es trotzdem erledigen. (Sie haben die MCU und die Frequenz, mit der sie arbeitet, nicht einmal gepostet, als Sie dies geschrieben haben.)

Sie können entweder ein statisches oder ein dynamisches Komprimierungswörterbuch haben (was ersetzt durch was). Wenn es statisch ist, können Sie es auf der Empfängerseite fest codieren, um es zu dekomprimieren. Wenn es dynamisch ist, erfordert es mehr Berechnung auf der MCU-Seite, um es zu generieren, und Sie müssen es auch als Daten an die Empfangsseite (PC) übertragen, da es dekodiert werden muss.

Machen Sie zunächst eine Schätzung darüber, welche Art von Werten Ihr ADC häufiger zurückgibt. Vielleicht möchten Sie die wichtigsten Bits komprimieren. Sie müssen nach bestimmten Algorithmen suchen, aber ich denke, es gibt keine Möglichkeit, dies zu tun, ohne etwas Bandbreite aufzugeben, da das, was Sie senden, unterscheidbar sein muss - senden Sie tatsächliche Rohdaten oder komprimierte Daten oder ein Wörterbuch? Sie können beispielsweise fest codieren, dass Sie alle 16 Bytes ein Byte senden, das ein Wörterbuch für die nächsten 16 Bytes ist (oder mehr, aber dann könnte Ihre Komprimierung aufgrund der unterschiedlichen Daten schlechter werden). Das bedeutet, dass Ihre Komprimierung gut genug sein muss, um diesen Overhead zu überwinden und dennoch die Gesamtmenge der übertragenen Daten zu erhöhen.

Aber Schritt Nummer 1 schätzt immer, ob es irgendetwas in den Daten zu komprimieren gibt. Wenn Sie davon ausgehen, dass alle möglichen Werte die ganze Zeit völlig zufällig sind, können Sie nicht viel tun, Sie möchten dann vielleicht einige Messungen verwerfen. Dies ist einfach Ihre Hardwarebeschränkung. Damit kann man nur so viel erreichen.

Führen Sie Ihre Verarbeitung auf dem ARM durch und senden Sie ein verarbeitetes Ergebnis

Sie benötigen nur Kommunikation mit voller Bandbreite mit dem PC, wenn Sie vorhaben, dass der ARM nur ein dummer USB-ADC ist und all die cleveren Dinge, die auf dem PC passieren. Wenn Sie die Verarbeitung auf den ARM legen, können Sie Daten langsamer an den PC zurücksenden.

Sie sagen, Sie suchen nach Durchreisenden. Im Wesentlichen sprechen wir dann von Differenzierung (auf der Suche nach einer schnellen Änderungsrate). Nachdem ich dies kürzlich mit Arbeit gemacht habe (Differenzieren der Position, um Geschwindigkeit und Beschleunigung anzugeben, und anschließendes Ausführen einer Positions-Geschwindigkeits-Beschleunigungssteuerung mit geschlossenem Regelkreis), kann ich Ihnen sagen, dass das Abtasten verrauscht ist und Sie bei der Suche ein erhebliches Maß an Filterung benötigen bei Änderungsrate. Wenn Sie dies nicht tun, werden Fehlauslöser durch Rauschen Ihr Design zerstören. (Bessel-Tiefpassfilter FTW wegen minimalem Überschwingen und solider Gruppenverzögerung im Durchlassbereich; und schauen Sie sich auch die MZTi-Transformation an, um eine bessere Reaktion zu erzielen, wenn sich Ihre Filterzeitkonstante Nyquist nähert.)

Jetzt haben wir etwas mit nützlicheren Daten, mit denen der PC arbeiten kann. Aber wir haben auch etwas, das langsamer an den PC zurückgesendet werden kann, denn wenn Sie Ihre Daten tiefpassgefiltert haben, müssen Sie diese Daten nicht schneller als mit der doppelten neuen Bandbreite senden.