Técnicas de promedios móviles más rápidos con una huella mínima

5

He probado algunas técnicas de promedio en ejecución para suavizar el cambio en los datos de ADC en AtMega48 para controlar las luces (PWM) al girar un potenciómetro (ADC).

Los filtros (pseudo códigos):

Media móvil:

adc_avg += new adc_raw; 
adc_avg >>= 1;

Filtro de media móvil de 8 puntos :

fill adc_raw_array[i] = adc_raw; 
adc_raw_array[8] <-- delete left most, move left, push adc_raw. 
adc_avg = sum(adc_raw_array); 
adc_avg >>=3 ; // divide by 8

Observé que los filtros son muy bonitos. Pero lento en respuesta lo que se espera.

Estoy buscando técnicas como Promedio móvil exponencial . Se dice que es más receptivo. ¿Hay otro como este? Como dice:

adc_Eavg = α * adc_raw + ( 1 - α ) * adc_Eavg ;

donde α está entre 0 y 1.

Cómo codificar y optimizar esos; código sabio (sin usar flotadores) ? O ¿Cómo convertiría los flotantes en enteros correspondientes para hacer código pequeño, rápido y sensible?

Lo intenté como

adc_Eavg  += α * ( adc_raw - adc_Eavg );

y seguí     α = 1;

Aparte de eso, no funcionará como se esperaba. Porque tendría que cambiar todas las variables para flotar.

Por favor, no se concentre en la siguiente declaración por el momento, pero tenga en cuenta. Mantener flotantes en mi base de código está llenando la memoria del programa del 45% al 137%, en caso de

adc_avg = adc_avg * 0.8f + adc_raw * 0.2f;
    
pregunta Rick2047

3 respuestas

5

Su filtro de promedio móvil es bastante ineficiente; no hay necesidad de cambiar los datos, ni de calcular el total cada vez. En lugar de un FIFO implementarlo como round robin :

*i points to the oldest sample in the list*   
total -= samples[i]  
total += new_sample  
samples[i] = new_sample  
i = (i+1) mod 8  
average = total >> 3

Entonces, reemplaza la muestra más antigua por la nueva en la lista, luego de restar la más antigua del total y agregar la nueva. Luego deje que el índice apunte a la siguiente posición, que ahora es la más antigua, para ser reemplazada por la siguiente muestra.

FIR vs IIR
El filtro de promedio móvil es un filtro FIR (Finite Impulse Response) con todos los coeficientes iguales. Un filtro FIR es inherentemente estable, y su principal desventaja es que necesita más almacenamiento que el filtro IIR típico. En el caso de la media móvil, es solo la matriz de muestras, pero para un filtro FIR genérico se necesita una segunda matriz con los coeficientes.

Su otra solución es un filtro IIR (Respuesta de Impulso Infinito); utiliza una combinación de la salida anterior y la nueva entrada para crear el nuevo valor de salida. No es necesariamente estable (aunque el tuyo lo es), pero a menudo necesita menos almacenamiento: el tuyo solo necesita el valor de salida anterior. Un filtro bi-quad solo necesita un almacenamiento de 4 muestras (Formulario directo 1, que se muestra a continuación) o solo 2 muestras en el Formulario directo 2.

    
respondido por el stevenvh
2

Puedes implementar

adc_Eavg = α * adc_raw + ( 1 - α ) * adc_Eavg ;

con una sobrecarga mínima al limitar α a las fracciones binarias. He usado esto con buenos resultados.

Toma el resultado existente,
Desplazar N coloca derecho a dividir por 2 ^ N
Réstelo del resultado existente.
Añadir nuevos datos

Hecho.

Esto no es tan rápido para cambiar con un cambio de paso en los datos de entrada como puede desear, pero en muchos casos es fácil de implementar y es lo suficientemente efectivo como filtro.

Puede acelerar su respuesta tomando decisiones informales sobre su comportamiento en casos que son muy diferentes. por ejemplo, mantener un recuento de entradas secuenciales que son más que un límite diferente al resultado existente. Si este recuento excede algún umbral, entonces cambie la razón de división de N por algún factor.

por ejemplo, N es generalmente 4- > los resultados se desplazan a la derecha 4 veces = 16 división. Si la entrada está a más de xxx de la respuesta, haga solo dos turnos a la derecha y multiplique la nueva muestra por 4 antes de agregar.

    
respondido por el Russell McMahon
2

Puede optimizar un filtro exponencial si α = 1 / (2 ^ n). P.EJ. si α = 1/4, entonces:

adc_Eavg = (adc_raw>>2) + adc_Eavg - (adc_Eavg>>2);

O, para mayor precisión, pero en riesgo de desbordamiento:

adc_Eavg = (adc_raw + (adc_Eavg<<2) - adc_Eavg) >> 2;

O, si tu MCU tiene una multiplicación más rápida que shift:

adc_Eavg = (adc_raw + adc_Eavg*3) >> 2;
    
respondido por el Rocketmagnet

Lea otras preguntas en las etiquetas