Algoritmo para comprimir un flujo de datos ADC de 12 bits en un microcontrolador Cortex M0

En mi aplicación, un microcontrolador Cortex M0 actualmente muestrea una señal ADC a 10 kHz y envía el resultado a través de un cable serial USB a una computadora.

Ahora quiero aumentar la frecuencia de muestreo a más de 100 kHz y agregar un segundo canal.

El enlace serie USB está limitado a 1-3 Mbps, por lo que es físicamente imposible transmitir a esta velocidad con un valor ADC de 12 bits y 2 bytes necesarios para transferir cada muestra. Para el muestreo de dos canales ADC a 100 KHz, la tasa de bits requerida es de aproximadamente 4 Mbps:

b i t _ r a t mi = 2 C h × 2 b y t mi s × 10 b i t s × 100 × 10 3 = 4 × 10 6 b pag s

Soy consciente de que podría colocar 2 muestras de ADC en 3 bytes, pero quiero comprimir un poco los datos de ADC.

¿Cuáles son algunos algoritmos de compresión que son adecuados para ejecutarse en un microcontrolador Cortex M0?

Hay docenas de algoritmos de compresión, pero estoy buscando algo que pueda ejecutarse en una plataforma con recursos limitados.

solo asegurándose: ¿la sobrecarga computacional aún le permitirá hacer todo lo suficientemente rápido?
No sé mucho sobre compresión, pero creo que con datos ya binarios, en una transmisión, tendrá problemas para comprimir esos datos sin perder información de manera significativa. ¿Son realmente los datos de 12 bits sin procesar 100000 veces por segundo lo que necesita? Creo que eso también pondrá de rodillas a su PC si desea calcular algo con los valores. ¿No hay posibilidad de combinar valores? Supongo que sus valores de 12 bits tendrán mucho ruido. ¿Por qué no hacer una media/RMS/lo que sea sobre varias muestras de datos y transferir el resultado?
Es una corteza M0 (punto fijo) que funciona a unos 64 MHz. Es capaz de alrededor de 60 DMIPS.
@jwsc Esta frecuencia de muestreo es necesaria para capturar transitorios rápidos.
¿Cuál es el ancho de banda de los datos en el transitorio rápido? ¿Tendrá suficiente "tiempo de silencio" para que el ancho de banda promedio sea menor que el cuello de botella del USB?
64MHz para transmisión de 4Mbit? ¿16 ciclos de reloj por cada bit enviado? Creo que tendrás que contar los ciclos de cada función que tengas. No soy un experto, tal vez alguien pueda confirmarlo, pero esto suena... problemático. La compresión de datos incluso en una PC tiene una barra de progreso visible. No es una tarea fácil.
¿Y si "envías" solo la "diferencia" en 1 byte? ¿Con una actualización "de vez en cuando"?
¿La entrada es un ruido blanco completamente arbitrario o tiene un dV/dt limitado? Si es lo último, ¿puede enviar un cuadro periódicamente y, a partir de eso, solo unos pocos bits por muestra, cuánto aumenta o disminuye a partir de ese valor? Similar a muchos algoritmos de compresión de video con fotogramas clave ocasionales y en el medio solo la diferencia.
Busque la compresión Lempel-Ziv si sus aplicaciones necesitan compresión de datos sin pérdidas. Hay una muy buena descripción histórica del desarrollo de ese algoritmo en la edición de mayo de 2021 de IEEE Spectrum.

Respuestas (4)

¿Es esto realmente una aplicación de audio? ¿Podría reemplazar todo con una interfaz de audio dedicada que resolverá todos estos problemas por usted, o necesita el nivel de CC preciso? El estéreo de 96 ksps es bastante alcanzable.

No tiene muchos MIPS con los que trabajar, así que voy a proponer el siguiente esquema de codificación. Es una tasa de bits técnicamente variable, por lo que es mejor que espere que los transitorios sean raros. Funciona para muestras de hasta 15 bits.

  • Realizar un seguimiento de la muestra anterior
  • Restar la siguiente muestra de la muestra anterior, llamar al resultado D
  • Examine los bits por encima de 7. O son todos unos, o todos ceros, o una combinación (lo que indica un cambio transitorio o grande).
  • Para los casos de todos unos o todos ceros: enmascare los 7 bits inferiores, transmita al host.
  • Para el caso transitorio: transmita el byte superior de la muestra (no D) con el bit superior establecido en "1", luego el byte inferior.
  • Periódicamente (por ejemplo, cada 256 muestras), envíe la muestra completa de todos modos con el bit superior establecido en 1. Esto permite la recuperación de errores.

La reconstrucción es simple:

  • si el bit superior de un byte es 0, extienda con signo los 7 bits restantes y agréguelos a la muestra anterior para obtener la siguiente muestra.
  • si el bit superior es 1, enmascararlo, leer el siguiente byte y ensamblar el resultado en una palabra.
Encuentro interesante esta idea. Esta no es una aplicación de audio sino una aplicación de muestreo actual. La mayoría de las veces, la diferencia entre una muestra y la siguiente sería de menos de 7 bits. Si entiendo su respuesta correctamente, ¿la mayoría de las muestras se representarían como un solo byte?
Sí, la mayoría de las muestras serían de 1 byte.
Pero repito la advertencia de tasa de bits variable: si hay muchos transitorios en una ráfaga, es posible que el enlace no se mantenga. En promedio, se pondrá al día. Esto puede ser importante si está intentando sincronizar con otra cosa.
Ok, esto básicamente reduce a la mitad la tasa de bits requerida de 4 Mbps a 2. Los transitorios agregarán un poco más, pero me gusta esta idea porque la sobrecarga es baja.
También es muy útil tener un marcador explícito de pérdida de sincronización/desbordamiento.

Hay algunos pasos "sencillos" que puede seguir para reducir la velocidad de transmisión necesaria:

  1. ¿Realmente necesitas una resolución de 12 bits? Si puede sacrificar los 4 bits menos significativos (que podrían ser solo ruido), reduce el ancho de banda requerido en un 33% y ahorra algunas operaciones al mismo tiempo.

  2. en qué cantidad la señal "generalmente" cambia entre 2 muestras sucesivas. Por ejemplo, si el 95% de las veces, la diferencia está solo en los 6 bits menos significativos, puede enviar:

  • si la diferencia es inferior a 6 bits, la diferencia se codifica como un entero con signo en 7 bits (con un 1 como el bit más significativo (8vo)); si la diferencia es superior a 6 bits, envía el valor completamente nuevo en 2 bytes (por lo que habrá 4 ceros de encabezado en el byte más significativo, lo que facilita su identificación)

Finalmente, una cosa interesante que dijiste en los comentarios es que necesitas una tasa de muestreo tan alta porque quieres observar transitorios.

Por lo tanto, veo 2 soluciones, un poco más complicadas, pero que podrían reducir significativamente la velocidad en baudios necesaria para su aplicación específica:

  1. Si solo necesita los transitorios: ¿por qué no "disparar" sobre ellos? Almacena todas sus medidas en un búfer rodante (usando DMA si puede). Luego, utiliza algún cálculo de software para definir un transitorio (por ejemplo, una diferencia mayor que X entre la medición N y N-N0). Si detecta uno, envía solo los valores alrededor de la medición N. Si sus transitorios son raros, su tasa de baudios requerida se vuelve realmente baja. NB: en esta versión, SOLO ve los transitorios, no el resto (puede o no satisfacer sus necesidades, si no, vea la solución 4)

  2. Otra solución es no enviar muestreos con intervalos de tiempo regulares: aún muestrea a 100 kHz (almacenado en un búfer interno con DMA), pero solo transmite parte de los datos: cuando los datos cambian rápidamente, envía muchos datos, cuando va cambiando poco a poco, te mandan pocos. Por ejemplo :

  • digamos que la última medida enviada es M[N0] (medida en el muestreo #N0, 12 bits)
  • llamemos a N la medida actual (valor=M[N]. Si N-N0 == 16, entonces X, con los 12 bits menos significativos de X siendo M[N], y los 4 más significativos siendo N-N0- 1 = 15 = 0b1111
  • de lo contrario, si abs(M[N]-M[N0]) > umbral: envió X (2 bytes), con M[N] para los 12 bits menos significativos y N-N0-1 para los 4 bits más significativos (es decir, el número de medidas que te saltaste)
  • otra cosa: no enviar nada.

Entonces, básicamente, en el lado receptor, recibe el valor en la forma A[0:3].M[0:12], siendo A el número de muestras omitidas (entonces (A+1) sampling_period el tiempo desde la última muestra ) , y M el valor de esa muestra. Si su señal está cambiando lo suficientemente lento y los transitorios son raros, tendrá un promedio de A entre 14 y 15 (seamos conservadores y digamos 14), por lo que tiene una tasa de bits promedio de 2 canales 16 bits * 100000 Hz/15 = 213 kb / s: eso se hace fácilmente en un UART de 1 Mb/s

oye, me gusta tu idea de detección de transitorios. Al igual que los datos de muestra, siempre a una velocidad alta, pero se envían más lentamente y en menor cantidad, solo se envían mucho cuando son transitorios. Esta es una solución ordenada basada en el propósito específico del dispositivo. Elegante.
En particular, SAC ADC generalmente tarda más tiempo en completar lecturas de 12 bits que lecturas de 8/10 bits, por lo que si sacrifica la resolución, podría significar automáticamente una frecuencia de muestreo más alta.

Si lo hace, tendrá una sobrecarga computacional significativa, debe asegurarse de que puede hacer todo a tiempo. Tendrá que revisar todos los datos varias veces, esto es del orden O(n). Porque obviamente no puede hacerlo sin revisar todos los datos al menos una vez y también necesita hacer algo con ellos. Tal vez pueda ahorrar algo de tiempo en la automatización del procedimiento de envío: puede hacer que un temporizador active las interrupciones regularmente y luego active la transmisión de los datos con DMA. Esto liberará algo de poder computacional. Si lo necesita, tal vez pueda manejarlo de la manera más simple y aún así hacerlo. (ni siquiera publicó MCU y la frecuencia en la que opera, al momento de escribir esto)

Puede tener un diccionario de compresión estático o dinámico (qué sustituye a qué). Si es estático, puede codificarlo en el extremo receptor para descomprimirlo. Si es dinámico, se necesitarán más cálculos en el lado de MCU para generarlo y también deberá transmitirlo como datos al lado receptor (PC), porque necesita decodificarlo.

Primero, haga una estimación de qué tipo de valores devuelve su ADC con más frecuencia. Tal vez quieras comprimir los bits más significativos. Tendrá que buscar algunos algoritmos específicos, pero creo que no hay forma de hacerlo sin renunciar a un poco de ancho de banda, porque lo que envía debe ser distinguible: ¿envía datos sin procesar reales o datos comprimidos o diccionario? Por ejemplo, puede codificar que cada 16 bytes enviará un byte que será un diccionario para los próximos 16 bytes (o más, pero luego su compresión podría empeorar debido a la variedad de datos). Lo que significa que su compresión debe ser lo suficientemente buena para superar esta sobrecarga y aun así aumentar la cantidad total de datos transmitidos.

Pero el paso número 1 siempre es estimar si hay algo en los datos para comprimir. Si espera que todos los valores posibles sean totalmente aleatorios todo el tiempo, no hay mucho que pueda hacer, entonces es posible que desee descartar algunas medidas. Esta es simplemente su limitación de hardware. Solo puedes obtener tanto con eso.

Haga su procesamiento en el ARM y envíe un resultado procesado

Solo necesita comunicaciones de ancho de banda completo con la PC si planea que el ARM sea solo un ADC USB tonto y todas las cosas inteligentes que suceden en la PC. Si coloca el procesamiento en el ARM, puede enviar cosas a la PC más lentamente.

Dices que estás buscando transitorios. Esencialmente, entonces estamos hablando de diferenciación (buscando una tasa de cambio rápida). Habiendo hecho esto recientemente con el trabajo (diferenciando la posición para dar velocidad y aceleración, y luego ejecutando el control de posición-velocidad-aceleración de circuito cerrado), puedo decirle que el muestreo es ruidoso y necesita niveles significativos de filtrado cuando busca a tasa de cambio. Si no lo hace, entonces los falsos disparadores del ruido van a matar su diseño. (Filtros de paso bajo Bessel FTW, debido a un sobreimpulso mínimo y un retraso de grupo sólido en la banda de paso; y también mire la transformada MZTi para obtener una mejor respuesta a medida que la constante de tiempo del filtro se acerca a Nyquist).

Ahora tenemos algo con más datos útiles para que funcione la PC. Pero también tenemos algo que se puede enviar de regreso a la PC más lentamente, porque si ha filtrado sus datos con un filtro de paso bajo, entonces no necesita enviar esos datos más rápido que el doble de su nuevo ancho de banda.