Ayuda con el código de suavizado retrasado

Estoy obsesionado con cómo suavizar los datos del sensor analógico de una manera específica. Matemáticas/código no es mi experiencia, así que gracias de antemano.

Mi boceto necesita escuchar un AnalogPin y suavizar/eliminar cualquier valor atípico a medida que cambian los valores, aunque se asiente suavemente en el valor final de AnalogPin.

Ex. si el sensor es un micrófono en una habitación vacía y durante 30 a 60 segundos la habitación se llena de gente, necesitaría un MicValue que crezca lentamente hasta el nuevo volumen pero omita los pasos iniciales o los picos importantes.

Ex. si el pin AnalogRead es un potenciómetro, girar rápidamente la perilla durante 5 segundos no tendría mucho efecto, pero donde termina la perilla, el valor se asentará lentamente.

Cualquier ayuda para definir lo que estoy buscando o ayuda con el código sería muy apreciada. ¡Gracias!

Respuestas (4)

Se puede obtener un estimador suavizado simple simplemente mediante una combinación lineal del valor recién muestreado y el valor suavizado existente:

smoothed_value := (1 - alpha) * smoothed_value + alpha * new_value

Por ejemplo, si alfa es 0.20 (20%), entonces tenemos:

smoothed_value = 0.8 * smoothed_value + 0.2 * new_value

En otras palabras, tome el 80% del valor existente y el 20% de la nueva muestra.

Haciendo alguna manipulación algebraica, podemos mostrar que también se puede calcular de esta manera:

smoothed_value := smoothed_value + alpha * (new_value - smoothed_value)

En otras palabras, actualice el valor suavizado agregándole solo la alphafracción del paso entre este y el nuevo valor.

Si hace que todo sea un número entero y usa una potencia de dos alphao un múltiplo de una potencia de dos, puede hacerlo de manera muy eficiente incluso en procesadores poco sofisticados que no admiten la multiplicación o la división. Por ejemplo, supongamos que queremos hacer alpha3/16. En C, podemos multiplicar por 3 y dividir por 16 con turnos y sumas:

delta = new_value - smoothed_value
smoothed_value += ((delta << 1) + delta) >> 4;

Este tipo de suavizado exhibe un decaimiento exponencial. Es similar a los procesos de la naturaleza, como la forma en que un circuito RC se carga o descarga para rastrear los cambios en el voltaje aplicado.

El alphaparámetro está en el rango [0, 1]. Si es cero, los nuevos valores se ignoran; no hay convergencia. Si es 1, entonces smoothed_valueno se suaviza en absoluto; sigue new_valueal instante. Los valores bajos cercanos a cero proporcionan una convergencia lenta.

Este algoritmo, o algo muy similar, se utiliza en el protocolo TCP para establecer una base para el tiempo de espera de retransmisión en función de las mediciones del tiempo de ida y vuelta de la conexión.

Llame a esto en algún intervalo bastante lento:

int ReadValue() {
  static int value = 0;
  int inputValue = ReadAnalogPin();

  if (inputValue > value) value++;
  if (inputValue < value) value--;

  return value;
}

Al muestrear sistemas ruidosos, es una práctica normal tomar múltiples muestras y promediarlas. Podría hacer algo similar aquí para evitar picos menores en la señal que afecten significativamente su medición.

Además de eso, puede mantener un historial de 'hitos' de medición con, digamos, un período de 1 segundo. Si este historial de mediciones tuviera un tamaño finito (quizás 10 mediciones), entonces podría ingresar su último hito de medición cada segundo y luego tomar un promedio de su historial de mediciones al mismo tiempo. De esta manera, cuando el volumen promedio en la habitación aumente constantemente, el valor promedio de su historial de mediciones también crecerá constantemente. Lo contrario sería cierto si la habitación estuviera constantemente más tranquila.

Lo que describe a menudo se implementa como un filtro de promedio móvil. Puede ser útil hacer que el número de valores en la matriz del historial del filtro sea una potencia par de dos para que el cálculo del valor promedio sea simplemente una operación de cambio binario. Existe un esquema inteligente que se puede usar con un filtro de promedio móvil en el que realiza un seguimiento de la suma de los elementos del historial. Cuando llega un nuevo valor, resta el valor más antiguo del valor de la suma, luego agrega el nuevo valor a la suma y coloca el nuevo valor en la matriz de historial reemplazando el valor más antiguo.
Mantener la suma como se describe anteriormente elimina la sobrecarga de tener que sumar los 8, 16 o 32 elementos de la matriz de historial cada vez que necesita el nuevo valor promedio móvil.

Muy útil, gracias a todos. Aquí hay una prueba para cualquiera que la necesite. Hacer rodar la olla afectará lentamente el brillo de un LED:

int sensorPin = A0;  //pot dial to test
int led = 9;

int brightness = 0;
int smooth_return = 0;
int smoothed = 0;
int new_value;

void setup() {
  pinMode(led, OUTPUT);  
  pinMode(sensorPin, INPUT);
  Serial.begin(9600);
}

void loop() {
  smoothed = Smooth(); 
  brightness = map(smoothed, 0, 1023, 0, 255); 
  analogWrite(led, brightness);    

  Serial.print("new smoothed: ");
  Serial.print(smoothed);
  Serial.print(" vs raw data: ");
  Serial.print(new_value);
  Serial.println();                              
} 

int Smooth(){
  new_value = analogRead(A0);

  smooth_return = 0.95 * smooth_return + 0.05 * new_value;
  return smooth_return;
}