Moving Average Efficient Implementation


Como otros han mencionado, debe considerar un filtro IIR (respuesta de impulso infinito) en lugar del filtro FIR (respuesta de impulso finito) que está utilizando ahora. Hay más, pero a primera vista los filtros FIR se implementan como convoluciones explícitas y filtros IIR con ecuaciones. El filtro IIR particular que uso mucho en los microcontroladores es un filtro de paso simple de un solo paso. Este es el equivalente digital de un simple filtro analógico R-C. Para la mayoría de las aplicaciones, éstas tendrán mejores características que el filtro de caja que está utilizando. La mayoría de los usos de un filtro de caja que he encontrado son el resultado de alguien que no presta atención en la clase de procesamiento de señal digital, no como resultado de necesitar sus características particulares. Si sólo desea atenuar las altas frecuencias que usted sabe son el ruido, un filtro de un solo paso de paso bajo es mejor. La mejor manera de implementar uno digitalmente en un microcontrolador es generalmente: FILT lt-- FILT FF (NEW-FILT) FILT es una pieza de estado persistente. Esta es la única variable persistente que necesita para calcular este filtro. NUEVO es el nuevo valor que se está actualizando el filtro con esta iteración. FF es la fracción del filtro. Que ajusta la pesadez del filtro. Mire este algoritmo y vea que para FF 0 el filtro es infinitamente pesado ya que la salida nunca cambia. Para FF 1, su realmente ningún filtro en absoluto, ya que la salida sólo sigue la entrada. Los valores útiles están intermedios. En los sistemas pequeños, se selecciona FF para que sea 1/2 N de modo que la multiplicación por FF se pueda realizar como un desplazamiento a la derecha por N bits. Por ejemplo, FF puede ser 1/16 y multiplicar por FF por lo tanto un desplazamiento a la derecha de 4 bits. De lo contrario este filtro sólo necesita un substracto y un agregado, aunque los números generalmente necesitan ser más anchos que el valor de entrada (más en precisión numérica en una sección separada a continuación). Normalmente tomo lecturas de A / D mucho más rápido de lo que se necesitan y aplico dos de estos filtros en cascada. Este es el equivalente digital de dos filtros R-C en serie, y se atenúa por 12 dB / octava por encima de la frecuencia de rolloff. Sin embargo, para las lecturas de A / D su generalmente más relevante mirar el filtro en el dominio del tiempo considerando su respuesta del paso. Esto le indica cuán rápido su sistema verá un cambio cuando cambie la cosa que está midiendo. Para facilitar el diseño de estos filtros (que sólo significa escoger FF y decidir cuantos de ellos a la cascada), uso mi programa FILTBITS. Se especifica el número de bits de cambio para cada FF en la serie de filtros en cascada y se calcula la respuesta de paso y otros valores. En realidad, por lo general, ejecutar esto a través de mi script wrapper PLOTFILT. Esto ejecuta FILTBITS, que hace un archivo CSV, luego traza el archivo CSV. Por ejemplo, aquí está el resultado de PLOTFILT 4 4: Los dos parámetros de PLOTFILT significan que habrá dos filtros en cascada del tipo descrito anteriormente. Los valores de 4 indican el número de bits de cambio para realizar la multiplicación por FF. Los dos valores FF son por lo tanto 1/16 en este caso. El rastro rojo es la respuesta de la etapa de la unidad, y es la cosa principal a mirar. Por ejemplo, esto le dice que si la entrada cambia instantáneamente, la salida del filtro combinado se establecerá en 90 del nuevo valor en 60 iteraciones. Si te importa el tiempo de solución de 95, entonces usted tiene que esperar alrededor de 73 iteraciones, y por 50 tiempo de solución sólo 26 iteraciones. El rastro verde le muestra la salida de una sola espiga de amplitud completa. Esto le da una idea de la supresión de ruido aleatorio. Parece que ninguna muestra causará más de un cambio de 2.5 en la salida. El rastro azul es dar una sensación subjetiva de lo que hace este filtro con el ruido blanco. Esto no es una prueba rigurosa, ya que no hay garantía de que exactamente el contenido de los números aleatorios elegidos como el ruido blanco de entrada para esta ejecución de PLOTFILT. Es sólo para darle una sensación áspera de cuánto será aplastado y lo suave que es. PLOTFILT, tal vez FILTBITS, y muchas otras cosas útiles, especialmente para el desarrollo de firmware PIC está disponible en la versión de software PIC Development Tools en mi página de descargas de software. Agregado acerca de la precisión numérica veo de los comentarios y ahora una nueva respuesta que hay interés en discutir el número de bits necesarios para implementar este filtro. Tenga en cuenta que la multiplicación por FF creará Log 2 (FF) nuevos bits por debajo del punto binario. En sistemas pequeños, FF se elige generalmente para ser 1/2 N de modo que esta multiplicación se realice realmente por un desplazamiento a la derecha de N bits. FILT es por lo tanto un entero de punto fijo. Tenga en cuenta que esto no cambia ninguna de las matemáticas desde el punto de vista de los procesadores. Por ejemplo, si está filtrando lecturas A / D de 10 bits y N 4 (FF 1/16), entonces necesita 4 bits de fracción por debajo de las lecturas A / D de enteros de 10 bits. Uno de los procesadores más, youd estar haciendo operaciones enteras de 16 bits debido a las lecturas de 10 bits A / D. En este caso, todavía puede hacer exactamente las mismas operaciones enteras de 16 bits, pero comience con las lecturas A / D a la izquierda desplazadas por 4 bits. El procesador no sabe la diferencia y no necesita. Hacer la matemática en todo enteros de 16 bits funciona si usted los considera 12,4 puntos fijos o enteros verdaderos de 16 bits (16,0 puntos fijos). En general, es necesario agregar N bits cada polo de filtro si no desea añadir ruido debido a la representación numérica. En el ejemplo anterior, el segundo filtro de dos tendría que tener 1044 18 bits para no perder información. En la práctica en una máquina de 8 bits que significa youd utilizar valores de 24 bits. Técnicamente sólo el segundo polo de dos necesitaría el valor más amplio, pero para la simplicidad del firmware usualmente utilizo la misma representación, y por lo tanto el mismo código, para todos los polos de un filtro. Normalmente escribo una subrutina o macro para realizar una operación de polo de filtro, y luego aplicarla a cada polo. Si una subrutina o macro depende de si los ciclos o la memoria del programa son más importantes en ese proyecto en particular. De cualquier manera, utilizo un cierto estado del rasguño para pasar NUEVO en la subrutina / la macro, que pone al día FILT, pero también las cargas que en el mismo estado del rasguño NUEVO estaba pulg. Esto hace fácil aplicar múltiples polos puesto que el FILT actualizado de un poste es El NUEVO de la siguiente. Cuando una subrutina, es útil tener un puntero apuntan a FILT en el camino, que se actualiza justo después de FILT a la salida. De esta manera la subrutina opera automáticamente en filtros consecutivos en memoria si se llama varias veces. Con una macro usted no necesita un puntero puesto que usted pasa en la dirección para funcionar en cada iteración. Ejemplos de código Aquí hay un ejemplo de una macro como se describe anteriormente para un PIC 18: Y aquí hay una macro similar para un PIC 24 o dsPIC 30 o 33: Ambos ejemplos se implementan como macros utilizando mi preprocesador de ensamblador PIC. Que es más capaz que cualquiera de las instalaciones macro incorporadas. Clabacchio: Otro problema que debería haber mencionado es la implementación de firmware. Puede escribir una subrutina de filtro de paso bajo de un solo polo una vez, luego aplicarla varias veces. De hecho, por lo general escribo una subrutina de este tipo para tomar un puntero en la memoria al estado del filtro, a continuación, hacer avanzar el puntero para que pueda ser llamado en sucesión fácilmente para realizar filtros multipolar. Ndash Olin Lathrop Apr 20 12 at 15:03 1. Muchas gracias por sus respuestas - todas ellas. Decidí usar este filtro IIR, pero este filtro no se utiliza como un filtro LowPass estándar, ya que necesito valorar los valores promedio de los contadores y compararlos para detectar cambios en un determinado rango. Ya que estos Valores van de dimensiones muy diferentes dependiendo de Hardware que quería tomar un promedio para poder reaccionar a estos cambios específicos de hardware automáticamente. Ndash sensslen May 21 12 at 12:06 Si se puede vivir con la restricción de un poder de dos números de elementos a la media (es decir, 2,4,8,16,32 etc), entonces la división se puede hacer fácil y eficientemente en un De bajo rendimiento micro sin división dedicada, ya que se puede hacer como un cambio de bits. Cada turno a la derecha es una potencia de dos por ejemplo: El OP pensó que tenía dos problemas, dividiendo en un PIC16 y la memoria para su amortiguador de anillo. Esta respuesta muestra que la división no es difícil. Es cierto que no aborda el problema de la memoria, pero el sistema SE permite respuestas parciales, y los usuarios pueden tomar algo de cada respuesta por sí mismos, o incluso editar y combinar las respuestas de otros. Dado que algunas de las otras respuestas requieren una operación de división, son igualmente incompletas, ya que no muestran cómo lograr esto de manera eficiente en un PIC16. Ndash Martin Apr 20 12 at 13:01 Hay una respuesta para un verdadero filtro de media móvil (también conocido como filtro de caja) con menos requisitos de memoria, si no te importa el downsampling. Su llamado un filtro integrador-peine en cascada (CIC). La idea es que usted tiene un integrador que tomar las diferencias de más de un período de tiempo, y la clave de ahorro de memoria dispositivo es que mediante el muestreo, no tienes que almacenar todos los valores del integrador. Se puede implementar utilizando el pseudocódigo siguiente: Su longitud media móvil efectiva es decimationFactorstatesize, pero sólo necesita mantener alrededor de las muestras de estado. Obviamente, puede obtener un mejor rendimiento si su stateize y decimationFactor son potencias de 2, de modo que la división y los operadores de resto se sustituye por cambios y máscara-ands. Postscript: Estoy de acuerdo con Olin que siempre debe considerar simples filtros IIR antes de un filtro de media móvil. Si no necesita la frecuencia-nulos de un filtro de vagón, un filtro de paso bajo de 1 o 2 polos probablemente funcione bien. Por otro lado, si está filtrando para fines de decimación (tomando una entrada de alta tasa de muestreo y promediándola para su uso por un proceso de baja velocidad), entonces un filtro de CIC puede ser justo lo que está buscando. (Especialmente si se puede usar statesize1 y evitar el ringbuffer en conjunto con sólo un único valor de integrador anterior) Theres algunos análisis en profundidad de la matemática detrás de la utilización de la primera orden IIR filtro que Olin Lathrop ya ha descrito en el Digital Signal Processing stack exchange (Incluye muchas imágenes bonitas). La ecuación para este filtro IIR es: Esto se puede implementar usando sólo números enteros y sin división usando el siguiente código (podría necesitar un poco de depuración como estaba escribiendo desde la memoria.) Este filtro se aproxima a una media móvil de Los últimos K muestras estableciendo el valor de alfa a 1 / K. Hacer esto en el código precedente definiendo BITS a LOG2 (K), es decir para K 16 fijado BITS a 4, para K 4 fijado BITS a 2, etc. (Mal verificar el código enumerado aquí tan pronto como consiga un cambio y Edite esta respuesta si es necesario.) Respondió Jun 23 12 at 4:04 Heres un filtro de paso bajo de un solo polo (promedio móvil, con frecuencia de corte CutoffFrequency). Muy simple, muy rápido, funciona muy bien, y casi no hay sobrecarga de memoria. Nota: Todas las variables tienen un alcance más allá de la función de filtro, excepto la pasada en newInput Nota: Este es un filtro de una sola etapa. Múltiples etapas se pueden conectar en cascada para aumentar la nitidez del filtro. Si utiliza más de una etapa, tendrá que ajustar DecayFactor (en relación con la frecuencia de corte) para compensar. Y, obviamente, todo lo que necesita son las dos líneas colocadas en cualquier lugar, no necesitan su propia función. Este filtro tiene un tiempo de aceleración antes de que el promedio móvil represente el de la señal de entrada. Si necesita omitir ese tiempo de aceleración, sólo puede inicializar MovingAverage al primer valor de newInput en lugar de 0 y esperar que el primer newInput no sea un outlier. (CutoffFrequency / SampleRate) tiene un intervalo entre 0 y 0,5. DecayFactor es un valor entre 0 y 1, por lo general cerca de 1. Flotadores de precisión simple son lo suficientemente buenos para la mayoría de las cosas, sólo prefiero dobles. Si necesitas pegarte con números enteros, puedes convertir DecayFactor y Factor de Amplitud en enteros fraccionarios, en los que el numerador se almacena como el entero, y el denominador es una potencia entera de 2 (así puedes cambiar a la derecha como el número Denominador en vez de tener que dividir durante el bucle del filtro). Por ejemplo, si DecayFactor 0.99 y desea utilizar números enteros, puede establecer DecayFactor 0.99 65536 64881. Y luego, cada vez que multiplique por DecayFactor en su bucle de filtro, simplemente cambie el resultado 16. Para más información sobre esto, un excelente libro thats En línea, capítulo 19 sobre filtros recursivos: www. dspguide / ch19.htm PS Para el paradigma de la media móvil, un enfoque diferente para establecer DecayFactor y AmplitudeFactor que puede ser más relevante para sus necesidades, digamos que desea que el anterior, alrededor de 6 elementos promediados juntos, hacerlo discretamente, youd añadir 6 elementos y dividir por 6, por lo que Puede establecer el AmplitudeFactor a 1/6, y DecayFactor a (1.0 - AmplitudeFactor). Respondió May 14 12 at 22:55 Todo el mundo ha comentado a fondo sobre la utilidad de IIR vs FIR, y en la división de poder de dos. La identificación apenas tiene gusto de dar algunos detalles de la puesta en práctica. Lo siguiente funciona bien en pequeños microcontroladores sin FPU. No hay multiplicación, y si mantienes N una potencia de dos, toda la división es de un solo ciclo de desplazamiento de bits. Búfer de anillo FIR básico: guarda un buffer de ejecución de los últimos N valores, y una SUM corriente de todos los valores en el búfer. Cada vez que entra una nueva muestra, resta el valor más antiguo del buffer de SUM, reemplázalo por el nuevo, añada la nueva muestra a SUM y SUM / N. Búfer de anillo IIR modificado: mantener una SUM corriente de los últimos N valores. Cada vez que llega una nueva muestra, SUM - SUM / N, agregue la nueva muestra y la salida SUM / N. Si le estoy leyendo bien, usted está describiendo un filtro IIR de primer orden, el valor que está restar es el valor más antiguo que está cayendo, pero es el promedio de los valores anteriores. Los filtros IIR de primer orden pueden sin duda ser útiles, pero no estoy seguro de lo que quiere decir cuando sugiere que la salida es la misma para todas las señales periódicas. A una frecuencia de muestreo de 10KHz, alimentar una onda cuadrada de 100Hz en un filtro de caja de 20 etapas producirá una señal que se eleva uniformemente para 20 muestras, se sienta alto para 30, cae uniformemente para 20 muestras y se sienta bajo para 30. Un primer orden Filtro IIR. Ndash supercat Aug 28 13 a las 15:31 producirá una onda que comienza a subir bruscamente y gradualmente se nivela cerca (pero no en) el máximo de entrada, luego empieza a caer bruscamente y gradualmente se nivela cerca (pero no) del mínimo de entrada. Comportamiento muy diferente. Ndash supercat August 28 13 at 15:32 Un problema es que un simple promedio móvil puede o no ser útil. Con un filtro IIR, puede obtener un filtro agradable con relativamente pocos calcs. La FIR que usted describe sólo puede darle un rectángulo en el tiempo - un sinc en freq - y no puede administrar los lóbulos laterales. Puede ser bien vale la pena para lanzar en un número entero multiplica para que sea una buena sintonía sintonizable FIR si se puede ahorrar las garrapatas del reloj. Ndash Scott Seidman: No hay necesidad de multiplicar si uno simplemente tiene cada etapa de la FIR o la salida de la media de la entrada a esa etapa y su valor almacenado anterior, y luego almacenar la entrada (si se tiene El rango numérico, se podría usar la suma en lugar de la media). Si ese filtro es mejor que un filtro de caja depende de la aplicación (la respuesta de paso de un filtro de caja con un retardo total de 1ms, por ejemplo, tendrá un pico d2 / dt desagradable cuando el cambio de entrada, y 1ms más tarde, pero tendrá El mínimo posible d / dt para un filtro con un retraso total de 1ms). Ndash supercat Como dijo mikeselectricstuff, si realmente necesita reducir sus necesidades de memoria, y no te importa su respuesta al impulso que es un exponencial (en lugar de un pulso rectangular), me gustaría ir para un filtro de media móvil exponencial . Los uso ampliamente. Con ese tipo de filtro, usted no necesita ningún búfer. Usted no tiene que almacenar N muestras pasadas. Solo uno. Por lo tanto, sus requisitos de memoria se redujo por un factor de N. También, no necesita ninguna división para eso. Sólo multiplicaciones. Si tiene acceso a aritmética de punto flotante, use multiplicaciones de coma flotante. De lo contrario, haga multiplicaciones enteras y desplaza hacia la derecha. Sin embargo, estamos en 2012, y te recomiendo que utilices compiladores (y MCUs) que te permitan trabajar con números de coma flotante. Además de ser más eficiente de la memoria y más rápido (usted no tiene que actualizar los elementos en cualquier búfer circular), yo diría que es también más natural. Porque una respuesta de impulso exponencial coincide mejor con la forma en que se comporta la naturaleza, en la mayoría de los casos. Un problema con el filtro IIR como casi tocado por olin y supercat pero aparentemente ignorado por otros es que el redondeo abajo introduce cierta imprecisión (y potencialmente sesgo / truncamiento). Suponiendo que N es una potencia de dos, y sólo se utiliza la aritmética entera, el desplazamiento a la derecha elimina sistemáticamente los LSB de la nueva muestra. Eso significa que la duración de la serie nunca podría ser, el promedio nunca tendrá en cuenta. Por ejemplo, supongamos una serie que disminuye lentamente (8, 8, 8, 7, 7, 7, 7, 6, 6) y asuma que el promedio es realmente 8 al principio. La muestra del puño 7 llevará la media a 7, independientemente de la resistencia del filtro. Sólo para una muestra. La misma historia para 6, etc. Ahora piensa en lo opuesto. La serie sube. El promedio se mantendrá en 7 para siempre, hasta que la muestra es lo suficientemente grande como para que cambie. Por supuesto, puede corregir el sesgo añadiendo 1 / 2N / 2, pero eso no resolverá realmente el problema de precisión. En ese caso la serie decreciente permanecerá para siempre en 8 hasta que la muestra sea 8-1 / 2 (N / 2). Para N4 por ejemplo, cualquier muestra por encima de cero mantendrá el promedio sin cambios. Creo que una solución para eso implicaría mantener un acumulador de los LSB perdidos. Pero no lo hice lo suficientemente lejos para tener el código listo, y no estoy seguro de que no perjudicaría la potencia IIR en algunos otros casos de series (por ejemplo, si 7,9,7,9 promedio a 8 entonces). Olin, su cascada de dos etapas también necesitaría alguna explicación. ¿Se refiere a la celebración de dos valores medios con el resultado de la primera alimentado en el segundo en cada iteración. ¿Cuál es el beneficio de esto que esencialmente tienen una matriz de valores como este: La matriz anterior es simplificada, estoy coleccionando 1 valor por milisegundo en mi código real y necesito procesar la salida en un algoritmo que escribí para encontrar el pico más cercano antes de un punto en el tiempo. Mi lógica falla porque en mi ejemplo anterior, 0.36 es el pico real, pero mi algoritmo miraría hacia atrás y vería el último número 0.25 como el pico, pues hay una disminución a 0.24 antes de él. El objetivo es tomar estos valores y aplicarles un algoritmo que los suavice un poco para que tenga valores más lineales. (Es decir: la identificación como mis resultados para ser curvy, no jaggedy) se me ha dicho que aplique un filtro de media móvil exponencial a mis valores. ¿Cómo puedo hacer esto? Es realmente difícil para mí para leer las ecuaciones matemáticas, trato mucho mejor con el código. ¿Cómo procesar los valores en mi matriz, aplicando un cálculo de promedio móvil exponencial para igualarlos? Preguntado Feb 8 12 at 20:27 Para calcular una media móvil exponencial. Usted necesita mantener un poco de estado alrededor y usted necesita un parámetro de ajuste. Esto requiere una pequeña clase (asumiendo que está usando Java 5 o posterior): Instantiate con el parámetro de decadencia que desea (puede tomar la afinación debe estar entre 0 y 1) y luego use average () para filtrar. Al leer una página sobre alguna recurrencia matemática, todo lo que realmente necesita saber al convertirlo en código es que a los matemáticos les gusta escribir índices en matrices y secuencias con subíndices. Sin embargo, la EMA es bastante simple, ya que sólo es necesario recordar un valor antiguo que no requiere arrays de estado complicado. Respondió 8 Feb a las 20:42 TKKocheran: Bastante. No es bueno cuando las cosas pueden ser simples (si comienza con una nueva secuencia, obtenga un nuevo averager). Observe que los primeros términos de la secuencia promediada saltarán alrededor de un bit debido a efectos de límite, pero obtendrá aquellos con otras medias móviles también. Sin embargo, una buena ventaja es que usted puede envolver la lógica del promedio móvil en el averager y experimentar sin molestar el resto de su programa demasiado. Ndash Donal Fellows Feb 9 12 en 0:06 Estoy teniendo un rato difícil entender sus preguntas, pero intentaré contestar de todos modos. 1) Si su algoritmo encontró 0.25 en lugar de 0.36, entonces es incorrecto. Está mal porque asume un aumento o una disminución monotónica (que siempre sube o baja siempre). A menos que usted promedio TODOS sus datos, sus puntos de datos --- como usted los presenta --- son no lineales. Si realmente desea encontrar el valor máximo entre dos puntos en el tiempo, corte su matriz de tmin a tmax y busque el máximo de ese subarray. 2) Ahora, el concepto de promedios móviles es muy simple: imagina que tengo la siguiente lista: 1.4, 1.5, 1.4, 1.5, 1.5. Puedo suavizarlo tomando el promedio de dos números: 1.45, 1.45, 1.45, 1.5. Observe que el primer número es el promedio de 1,5 y 1,4 (segundo y primeros números), la segunda (nueva lista) es el promedio de 1,4 y 1,5 (tercera y segunda lista antigua) la tercera (nueva lista) el promedio de 1,5 y 1,4 (Cuarto y tercero), y así sucesivamente. Podría haberlo hecho el período tres o cuatro, o n. Observe cómo los datos son mucho más suaves. Una buena manera de ver los promedios móviles en el trabajo es ir a Google Finance, seleccionar una acción (probar Tesla Motors bastante volátil (TSLA)) y hacer clic en technicals en la parte inferior de la tabla. Seleccione Promedio móvil con un período determinado y Promedio móvil exponencial para comparar sus diferencias. La media móvil exponencial es sólo otra elaboración de esto, pero los pesos de los datos más antiguos menos de los nuevos datos de esta es una manera de sesgar el alisamiento hacia la parte posterior. Por favor, lea la entrada de Wikipedia. Por lo tanto, esto es más un comentario que una respuesta, pero el pequeño cuadro de comentarios era sólo a pequeño. Buena suerte. Si usted está teniendo apuro con la matemáticas, usted podría ir con una media móvil simple en vez de exponencial. Así que la salida que obtendrías serían los últimos x términos divididos por x. Pseudocódigo no comprobado: Tenga en cuenta que tendrá que manejar las partes inicial y final de los datos, ya que claramente no puede medirse los últimos 5 términos cuando está en su segundo punto de datos. Además, hay maneras más eficientes de calcular este promedio móvil (suma suma - más reciente más reciente), pero esto es para obtener el concepto de lo que está sucediendo a través de. Respondió 8 Feb a las 20:41 Actualmente estoy desarrollando un sistema gráfico LCD para mostrar temperaturas, flujos, voltajes, energía y energía en un sistema de bomba de calor. El uso de un LCD gráfico significa que la mitad de mi SRAM y 75 de mi flash se han utilizado por un buffer de pantalla y cadenas. Actualmente estoy mostrando valores mínimos / máximos / promedio de energía A medianoche, cuando se restablece la figura diaria, el sistema comprueba si el consumo del día está por encima o por debajo del mínimo o máximo anterior y almacena el valor. El promedio se calcula dividiendo el consumo acumulado de energía por el número de días. Me gustaría mostrar el promedio diario durante la última semana y el mes (4 semanas por simplicidad), es decir, un promedio móvil. En la actualidad, esto implica mantener una matriz de valores para los últimos 28 días y calcular un promedio en toda la matriz de mensual y los últimos 7 días para la semana. Al principio estaba haciendo esto usando una matriz de flotadores (como la energía está en la forma 12.12kWh), pero esto estaba usando 28 4 bytes 112 bytes (5.4 de SRAM). No me importa tener un solo punto decimal de resolución, así que cambié a usar uint16t y multiplicar la cifra por 100. Esto significa que 12.12 está representado como 1212, y divido por 100 para fines de visualización. El tamaño de la matriz es ahora de 56 bytes (mucho mejor). No hay manera trivial de reducir la figura a una uint8t que puedo ver. Podría tolerar la pérdida de un lugar decimal (12.1kWh en lugar de 12.12kWh), pero el consumo es frecuentemente superior a 25.5kWh (255 es el valor más alto representado por un entero sin signo de 8 bits). El consumo nunca ha estado por debajo de 10.0kWh o por encima de 35.0kWh, así que concebiblemente podría restar 10 de las cifras almacenadas, pero sé que un día superaremos estos límites. A continuación, probé el código para empaquetar valores de 9 bits en una matriz. Esto da un rango de 0-51.2kWh y usa 32 bytes en total. Sin embargo, acceder a una matriz como ésta es bastante lento, especialmente cuando tiene que iterar sobre todos los valores para calcular un promedio. Así que mi pregunta es - ¿hay una forma más eficiente de calcular una media móvil con tres ventanas - vida útil, 28 días y 7 días Eficiencia significa menor en términos de uso SRAM, pero sin la pena de código enorme. ¿Puedo evitar el almacenamiento de todos los valores que se me ha preguntado 7 de marzo a las 8:32 He estado pensando y tienes razón. Así que técnicamente hace mi respuesta incorrecta. I39m invertir más tiempo y paciencia en él. Tal vez algo fuera de la caja. Le haré saber si encuentro algo. Hacemos algo como esto mucho en mi lugar de trabajo. Déjame preguntar por ahí. Perdón por la confusión. Ndash Aditya Somani Mar 8 14 at 17:15 hay una manera más eficiente de calcular una media móvil con. 28 días y 7 días. Necesitando recordar 27 días de historia. En otras palabras, en lugar de almacenar todos los detalles de cada día durante los últimos 27 días, (a) almacenar 7 o más valores de información diaria detallada para el pasado 7 o así días, y también (b) almacenar 4 o así resumieron los valores de la información total o promedio para cada uno de los últimos 4 o así semanas. A Simple Moving Average Implementación en Java En varias ocasiones Ive quería calcular métricas simples en mi Java Aplicaciones, por ejemplo el número de visitas por hora, o errores a lo largo de un período de tiempo. Mientras que el cálculo de métricas simples no es terriblemente difícil, su solo trabajo extra e Id pasan bastante tiempo en el dominio del problema. Me sorprendió no encontrar soluciones ampliamente aceptadas para métricas en Java. Encontré Metrics, pero parecía un poco complicado y no estaba bien documentado. Lo único que quería era calcular un promedio móvil. Pensé en el problema un poco más y decidí que no era un problema difícil. Heres my solution Esto funciona creando una matriz de tamaño de ventana / actualización de frecuencia, a continuación, un hilo fija el recuento al siguiente índice en la matriz en la frecuencia de actualización. El recuento para el intervalo es simplemente arrayi - arrayi1, que es el recuento más reciente menos el recuento más antiguo. Para un intervalo de 10 minutos, el recuento más antiguo (i1) tiene exactamente 10 minutos. Para agregar una media móvil a nuestro código primero necesitamos un contador, usando AtomicLong. Este contador se debe incrementar en función de los eventos que esté interesado en el cálculo (por ejemplo, solicitudes POST para un servicio REST). Tenemos que proporcionar la implementación con acceso al contador y que se logra a través de la interfaz GetCount. Aquí Ill crear un promedio móvil con una ventana de 5 minutos que se actualiza cada segundo. Y para obtener el promedio actual simplemente llamamos al método getAverage: Un detalle clave de la implementación es cómo se determina el tamaño del array: dividiendo la ventana por la frecuencia de actualización. Así que una ventana grande con una frecuente frecuencia de actualización puede consumir una cantidad significativa de memoria. En este ejemplo, el tamaño de la matriz es razonable 300. Sin embargo, si creamos una media móvil de 24 horas con un intervalo de 1 segundo, el tamaño sería 86400. Una frecuencia de actualización más razonable para un período de 24 horas podría ser cada 5 minutos (tamaño de la matriz de 288 ). Otra consideración de elegir la ventana y la frecuencia de actualización es que la ventana debe ser divisible por la frecuencia. Por ejemplo una ventana de 2 minutos con una frecuencia de actualización de 6 segundos está bien, pero una frecuencia de actualización de 7 segundos no es, ya que no es divisible por 120. Una IllegalArgumentException se lanza si la frecuencia de actualización del módulo de ventana no es cero. Esta implementación requiere un hilo por promedio móvil, que no es muy eficiente. Una mejor solución sería compartir un hilo entre muchos promedios. Actualización. He actualizado el código para compartir un hilo aquí. Por último, theres un problema de estado inicial: no tenemos datos todavía para toda la ventana. Por ejemplo, si tiene una ventana de 5 minutos y sólo 15 segundos de datos. Esta implementación devuelve null hasta que tengamos 5 minutos de datos. Otro enfoque es estimar el promedio. Supongamos que tenemos un recuento de 10 en 30 segundos, entonces podemos estimar el promedio como 40 en 2 minutos. Sin embargo, existe el riesgo de error significativo al extrapolar los datos incompletos. Por ejemplo, si tuviéramos una explosión de 20 impactos en 2 segundos, estaríamos calculando 1200 por 2 minutos, lo cual con toda probabilidad está muy lejos. El científico y los ingenieros dirigen el procesamiento de señales digitales Por Steven W. Smith, Ph. D. Una tremenda ventaja del filtro de media móvil es que se puede implementar con un algoritmo que es muy rápido. Para entender este algoritmo, imagine pasar una señal de entrada, x, a través de un filtro de media móvil de siete puntos para formar una señal de salida, y. Ahora veamos cómo se calculan dos puntos de salida adyacentes, y 50 e y 51: Estos son casi los mismos puntos de cálculo x 48 a x 53 para y 50, y de nuevo para y 51. Si y 50 ya ha sido calculado , La forma más eficiente de calcular y 51 es: Una vez que se ha encontrado y 51 usando y 50, entonces y 52 se puede calcular a partir de la muestra y 51, y así sucesivamente. Después de que el primer punto se calcula en y, todos los demás puntos se pueden encontrar con sólo una sola suma y resta por punto. Esto se puede expresar en la ecuación: Observe que esta ecuación utiliza dos fuentes de datos para calcular cada punto en la salida: puntos de la entrada y puntos previamente calculados de la salida. Esto se llama una ecuación recursiva, lo que significa que el resultado de un cálculo se utiliza en los cálculos futuros. (El término recursivo también tiene otros significados, especialmente en informática). En el capítulo 19 se analizan más detalladamente diversos filtros recursivos. Tenga en cuenta que el filtro recursivo de media móvil es muy diferente de los filtros recursivos típicos. En particular, la mayoría de los filtros recursivos tienen una respuesta de impulso infinitamente larga (IIR), compuesta de sinusoides y exponenciales. La respuesta de impulso de la media móvil es un pulso rectangular (respuesta de impulso finito, o FIR). Este algoritmo es más rápido que otros filtros digitales por varias razones. En primer lugar, sólo hay dos cálculos por punto, independientemente de la longitud del núcleo del filtro. En segundo lugar, la suma y la resta son las únicas operaciones matemáticas necesarias, mientras que la mayoría de los filtros digitales requieren una multiplicación que consume tiempo. En tercer lugar, el esquema de indexación es muy simple. Cada índice de la ecuación 15-3 se encuentra añadiendo o restando constantes enteras que se pueden calcular antes de que comience el filtrado (es decir, p y q). Por último, todo el algoritmo puede realizarse con representación entera. Dependiendo del hardware utilizado, los enteros pueden ser más de un orden de magnitud más rápido que el punto flotante. Sorprendentemente, la representación entera funciona mejor que el punto flotante con este algoritmo, además de ser más rápido. El error de redondeo de aritmética de punto flotante puede producir resultados inesperados si no tiene cuidado. Por ejemplo, imagine una señal de muestra de 10.000 que se filtra con este método. La última muestra en la señal filtrada contiene el error acumulado de 10.000 adiciones y 10.000 sustracciones. Esto aparece en la señal de salida como un desplazamiento a la deriva. Los enteros no tienen este problema porque no hay error de redondeo en la aritmética. Si debe utilizar el punto flotante con este algoritmo, el programa de la Tabla 15-2 muestra cómo usar un acumulador de doble precisión para eliminar esta deriva.

Comments