my #define MAX(a, b) ((a) > (b)) ? (a) : (b) #define MIN(a, b) ((a) < (b)) ? (a) : (b) #define MIDDLE(a, b, c) MAX(MAX(MIN(a, b), MIN(a, c)), MIN(b, c)) func (val) { static a1, a2 == 0 //middle = MIDDLE(val, a1, a2) if val < a1 and val < a2 middle = MIDDLE(val, a1, a2) else if val > a1 and a2 middle = MIDDLE(val, a1, a2) else middle = val a2 = a1 a1 = val return middle } === http://chipenable.ru/index.php/embedded-programming/item/203-mediannyy-filtr.html uint16_t middle_of_3(uint16_t a, uint16_t b, uint16_t c) { uint16_t middle; if ((a <= b) && (a <= c)){ middle = (b <= c) ? b : c; } else{ if ((b <= a) && (b <= c)){ middle = (a <= c) ? a : c; } else{ middle = (a <= b) ? a : b; } } return middle; } === http://www.elcojacobs.com/eleminating-noise-from-sensor-readings-on-arduino-with-digital-filtering/ выжимки из статьи Медианный фильтр: сортируем массив значений (например 100) от меньшего к большему (?) и берём среднее. автор предлагает скомбинировать медианный фильтр и фильтрацию по среднему: сортируем массив значений, берём 10 (из 100) средних и из них считаем среднее. The final code to read one sensor: #define NUM_READS 100 float readTemperature(int sensorpin){ // read multiple values and sort them to take the mode int sortedValues[NUM_READS]; for(int i=0;i=value){ // j is insert position break; } } } for(int k=i;k>j;k--){ // move all values higher than current reading up one position sortedValues[k]=sortedValues[k-1]; } sortedValues[j]=value; //insert current reading } //return scaled mode of 10 values float returnval = 0; for(int i=NUM_READS/2-5;i<(NUM_READS/2+5);i++){ returnval +=sortedValues[i]; } returnval = returnval/10; return returnval*1100/1023; } -=-=-=-=-=-=-=-=-=- По своей сути, это похоже на алгоритм, которым я когда-то фильтровал значения от счётчика гейгера: - считаем среднее в массиве измерений, - отбираем из массива только те значения, которые укладываются в +-5% от полученного среднего, - берём среднее от оставшихся значений. кажется я просто занулял неподходящие данные, а потом не учитывал их при следующем усреднении. какой алгоритм точнее, меньше, быстрее -- хз. можно попробовать на Unicharger-е. -=-=-=-=-=-=-=-=-=- Addional filtering: IIR Butterworth low pass filters (I have changed the filters from second order to third order...) для медленно меняющихся процессов он предлагает использовать фильтр нижних частот Баттервота 4-го порядка. Фильтр Баттервота -- это "infinite impulse response filter" (IIR) (а теперь гугльтранслейт) Это означает, что каждое выходное значение фильтра рассчитывается из истории входных значений и предыдущих выходных значений фильтра. Поскольку выходной фильтр используется для вычисления будущих значений, влияние любого входного значения на будущие выходные -- есть регрессия к бесконечности. (конец) The implementation of a software Butterworth filter is actually very easy, see the code snippet below. void updateTemperatures(void){ //called every 200 milliseconds fridgeTempFast[0] = fridgeTempFast[1]; fridgeTempFast[1] = fridgeTempFast[2]; fridgeTempFast[2] = fridgeTempFast[3]; fridgeTempFast[3] = readTemperature(fridgePin); // Butterworth filter with cutoff frequency 0.033*sample frequency (FS=5Hz) fridgeTempFiltFast[0] = fridgeTempFiltFast[1]; fridgeTempFiltFast[1] = fridgeTempFiltFast[2]; fridgeTempFiltFast[2] = fridgeTempFiltFast[3]; fridgeTempFiltFast[3] = (fridgeTempFast[0] + fridgeTempFast[3] + 3 * (fridgeTempFast[1] + fridgeTempFast[2]) ) / 1.092799972e+03 + (0.6600489526 * fridgeTempFiltFast[0]) + (-2.2533982563 * fridgeTempFiltFast[1]) + ( 2.5860286592 * fridgeTempFiltFast[2]); fridgeTemperatureActual = fridgeTempFiltFast[3]; beerTempFast[0] = beerTempFast[1]; beerTempFast[1] = beerTempFast[2]; beerTempFast[2] = beerTempFast[3]; beerTempFast[3] = readTemperature(beerPin); // Butterworth filter with cutoff frequency 0.01*sample frequency (FS=5Hz) beerTempFiltFast[0] = beerTempFiltFast[1]; beerTempFiltFast[1] = beerTempFiltFast[2]; beerTempFiltFast[2] = beerTempFiltFast[3]; beerTempFiltFast[3] = (beerTempFast[0] + beerTempFast[3] + 3 * (beerTempFast[1] + beerTempFast[2]) ) / 3.430944333e+04 + (0.8818931306 * beerTempFiltFast[0]) + (-2.7564831952 * beerTempFiltFast[1]) + ( 2.8743568927 * beerTempFiltFast[2]); beerTemperatureActual = beerTempFiltFast[3]; } void updateSlowFilteredTemperatures(void){ //called every 10 seconds // Input for filter fridgeTempSlow[0] = fridgeTempSlow[1]; fridgeTempSlow[1] = fridgeTempSlow[2]; fridgeTempSlow[2] = fridgeTempSlow[3]; fridgeTempSlow[3] = fridgeTempFiltFast[3]; // Butterworth filter with cutoff frequency 0.01*sample frequency (FS=0.1Hz) fridgeTempFiltSlow[0] = fridgeTempFiltSlow[1]; fridgeTempFiltSlow[1] = fridgeTempFiltSlow[2]; fridgeTempFiltSlow[2] = fridgeTempFiltSlow[3]; fridgeTempFiltSlow[3] = (fridgeTempSlow[0] + fridgeTempSlow[3] + 3 * (fridgeTempSlow[1] + fridgeTempSlow[2]) ) / 3.430944333e+04 + (0.8818931306 * fridgeTempFiltSlow[0]) + (-2.7564831952 * fridgeTempFiltSlow[1]) + ( 2.8743568927 * fridgeTempFiltSlow[2]); beerTempSlow[0] = beerTempSlow[1]; beerTempSlow[1] = beerTempSlow[2]; beerTempSlow[2] = beerTempSlow[3]; beerTempSlow[3] = beerTempFiltFast[3]; // Butterworth filter with cutoff frequency 0.01*sample frequency (FS=0.1Hz) beerTempFiltSlow[0] = beerTempFiltSlow[1]; beerTempFiltSlow[1] = beerTempFiltSlow[2]; beerTempFiltSlow[2] = beerTempFiltSlow[3]; beerTempFiltSlow[3] = (beerTempSlow[0] + beerTempSlow[3] + 3 * (beerTempSlow[1] + beerTempSlow[2]) ) / 3.430944333e+04 + (0.8818931306 * beerTempFiltSlow[0]) + (-2.7564831952 * beerTempFiltSlow[1]) + ( 2.8743568927 * beerTempFiltSlow[2]); } (это пиздец, считать на 8-мибитках такую вещественную арифметику...) Как видите, здесь использовано 2 быстрых и 2 медленных фильтра. Быстрый фильтр обновляется 5 раз в секунду и имеет угловые частоты 0.165 Гц и 0.05 Гц. Медленный фильтр обновляется раз в 10 сек и имеет угловую частоту 0.001 Гц. Медленный фильтр получает данные от быстрого. Почему быстрый и медленный фильтр? Каждый причинный фильтр (фильтр который не может заглянуть в будущее) вызывает задержку между входом и выходом: если брать среднее от К значений, потребуется К обновлений выхода чтобы полностью увидеть именения на входе. Каждый IIR low pass filter получает среднее от бесконечности предыдущих значений, но с уменьшающим множителем для более старых входных значений. Фильтр с меньшей угловой частотой усредняет больше значений и поэтому вызывает большую задержку. Дальше переводить особого смысла нет. Вобщем автора нужда заставила использовать два фильтра с разной угловой частотой. -=-=-=-=- Коэффициенты фильтра можно посчитать здесь: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html