adc_filter.txt 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. http://www.elcojacobs.com/eleminating-noise-from-sensor-readings-on-arduino-with-digital-filtering/
  2. выжимки из статьи
  3. Медианный фильтр:
  4. сортируем массив значений (например 100) от меньшего к большему (?) и берём среднее.
  5. автор предлагает скомбинировать медианный фильтр и фильтрацию по среднему:
  6. сортируем массив значений, берём 10 (из 100) средних и из них считаем среднее.
  7. The final code to read one sensor:
  8. #define NUM_READS 100
  9. float readTemperature(int sensorpin){
  10. // read multiple values and sort them to take the mode
  11. int sortedValues[NUM_READS];
  12. for(int i=0;i<NUM_READS;i++){
  13. int value = analogRead(sensorpin);
  14. int j;
  15. if(value<sortedValues[0] || i==0){
  16. j=0; //insert at first position
  17. }
  18. else{
  19. for(j=1;j<i;j++){
  20. if(sortedValues[j-1]<=value && sortedValues[j]>=value){
  21. // j is insert position
  22. break;
  23. }
  24. }
  25. }
  26. for(int k=i;k>j;k--){
  27. // move all values higher than current reading up one position
  28. sortedValues[k]=sortedValues[k-1];
  29. }
  30. sortedValues[j]=value; //insert current reading
  31. }
  32. //return scaled mode of 10 values
  33. float returnval = 0;
  34. for(int i=NUM_READS/2-5;i<(NUM_READS/2+5);i++){
  35. returnval +=sortedValues[i];
  36. }
  37. returnval = returnval/10;
  38. return returnval*1100/1023;
  39. }
  40. -=-=-=-=-=-=-=-=-=-
  41. По своей сути, это похоже на алгоритм, которым я когда-то фильтровал значения
  42. от счётчика гейгера:
  43. - считаем среднее в массиве измерений,
  44. - отбираем из массива только те значения, которые укладываются в +-5% от
  45. полученного среднего,
  46. - берём среднее от оставшихся значений.
  47. кажется я просто занулял неподходящие данные, а потом не учитывал их при
  48. следующем усреднении.
  49. какой алгоритм точнее, меньше, быстрее -- хз. можно попробовать на Unicharger-е.
  50. -=-=-=-=-=-=-=-=-=-
  51. Addional filtering: IIR Butterworth low pass filters
  52. (I have changed the filters from second order to third order...)
  53. для медленно меняющихся процессов он предлагает использовать фильтр нижних
  54. частот Баттервота 4-го порядка.
  55. Фильтр Баттервота -- это "infinite impulse response filter" (IIR)
  56. (а теперь гугльтранслейт)
  57. Это означает, что каждое выходное значение фильтра рассчитывается из истории
  58. входных значений и предыдущих выходных значений фильтра.
  59. Поскольку выходной фильтр используется для вычисления будущих значений,
  60. влияние любого входного значения на будущие выходные -- есть регрессия к
  61. бесконечности.
  62. (конец)
  63. The implementation of a software Butterworth filter is actually very easy, see
  64. the code snippet below.
  65. void updateTemperatures(void){ //called every 200 milliseconds
  66. fridgeTempFast[0] = fridgeTempFast[1];
  67. fridgeTempFast[1] = fridgeTempFast[2];
  68. fridgeTempFast[2] = fridgeTempFast[3];
  69. fridgeTempFast[3] = readTemperature(fridgePin);
  70. // Butterworth filter with cutoff frequency 0.033*sample frequency (FS=5Hz)
  71. fridgeTempFiltFast[0] = fridgeTempFiltFast[1];
  72. fridgeTempFiltFast[1] = fridgeTempFiltFast[2];
  73. fridgeTempFiltFast[2] = fridgeTempFiltFast[3];
  74. fridgeTempFiltFast[3] = (fridgeTempFast[0] + fridgeTempFast[3]
  75. + 3 * (fridgeTempFast[1] + fridgeTempFast[2]) )
  76. / 1.092799972e+03 + (0.6600489526 * fridgeTempFiltFast[0])
  77. + (-2.2533982563 * fridgeTempFiltFast[1])
  78. + ( 2.5860286592 * fridgeTempFiltFast[2]);
  79. fridgeTemperatureActual = fridgeTempFiltFast[3];
  80. beerTempFast[0] = beerTempFast[1];
  81. beerTempFast[1] = beerTempFast[2];
  82. beerTempFast[2] = beerTempFast[3];
  83. beerTempFast[3] = readTemperature(beerPin);
  84. // Butterworth filter with cutoff frequency 0.01*sample frequency (FS=5Hz)
  85. beerTempFiltFast[0] = beerTempFiltFast[1];
  86. beerTempFiltFast[1] = beerTempFiltFast[2];
  87. beerTempFiltFast[2] = beerTempFiltFast[3];
  88. beerTempFiltFast[3] = (beerTempFast[0] + beerTempFast[3]
  89. + 3 * (beerTempFast[1] + beerTempFast[2]) )
  90. / 3.430944333e+04 + (0.8818931306 * beerTempFiltFast[0])
  91. + (-2.7564831952 * beerTempFiltFast[1])
  92. + ( 2.8743568927 * beerTempFiltFast[2]);
  93. beerTemperatureActual = beerTempFiltFast[3];
  94. }
  95. void updateSlowFilteredTemperatures(void){ //called every 10 seconds
  96. // Input for filter
  97. fridgeTempSlow[0] = fridgeTempSlow[1];
  98. fridgeTempSlow[1] = fridgeTempSlow[2];
  99. fridgeTempSlow[2] = fridgeTempSlow[3];
  100. fridgeTempSlow[3] = fridgeTempFiltFast[3];
  101. // Butterworth filter with cutoff frequency 0.01*sample frequency (FS=0.1Hz)
  102. fridgeTempFiltSlow[0] = fridgeTempFiltSlow[1];
  103. fridgeTempFiltSlow[1] = fridgeTempFiltSlow[2];
  104. fridgeTempFiltSlow[2] = fridgeTempFiltSlow[3];
  105. fridgeTempFiltSlow[3] = (fridgeTempSlow[0] + fridgeTempSlow[3]
  106. + 3 * (fridgeTempSlow[1] + fridgeTempSlow[2]) )
  107. / 3.430944333e+04 + (0.8818931306 * fridgeTempFiltSlow[0])
  108. + (-2.7564831952 * fridgeTempFiltSlow[1])
  109. + ( 2.8743568927 * fridgeTempFiltSlow[2]);
  110. beerTempSlow[0] = beerTempSlow[1];
  111. beerTempSlow[1] = beerTempSlow[2];
  112. beerTempSlow[2] = beerTempSlow[3];
  113. beerTempSlow[3] = beerTempFiltFast[3];
  114. // Butterworth filter with cutoff frequency 0.01*sample frequency (FS=0.1Hz)
  115. beerTempFiltSlow[0] = beerTempFiltSlow[1];
  116. beerTempFiltSlow[1] = beerTempFiltSlow[2];
  117. beerTempFiltSlow[2] = beerTempFiltSlow[3];
  118. beerTempFiltSlow[3] = (beerTempSlow[0] + beerTempSlow[3]
  119. + 3 * (beerTempSlow[1] + beerTempSlow[2]) )
  120. / 3.430944333e+04 + (0.8818931306 * beerTempFiltSlow[0])
  121. + (-2.7564831952 * beerTempFiltSlow[1])
  122. + ( 2.8743568927 * beerTempFiltSlow[2]);
  123. }
  124. (это пиздец, считать на 8-мибитках такую вещественную арифметику...)
  125. Как видите, здесь использовано 2 быстрых и 2 медленных фильтра. Быстрый фильтр
  126. обновляется 5 раз в секунду и имеет угловые частоты 0.165 Гц и 0.05 Гц.
  127. Медленный фильтр обновляется раз в 10 сек и имеет угловую частоту 0.001 Гц.
  128. Медленный фильтр получает данные от быстрого.
  129. Почему быстрый и медленный фильтр?
  130. Каждый причинный фильтр (фильтр который не может заглянуть в будущее) вызывает
  131. задержку между входом и выходом: если брать среднее от К значений,
  132. потребуется К обновлений выхода чтобы полностью увидеть именения на входе.
  133. Каждый IIR low pass filter получает среднее от бесконечности предыдущих
  134. значений, но с уменьшающим множителем для более старых входных значений.
  135. Фильтр с меньшей угловой частотой усредняет больше значений и поэтому вызывает
  136. большую задержку.
  137. Дальше переводить особого смысла нет.
  138. Вобщем автора нужда заставила использовать два фильтра с разной угловой
  139. частотой.
  140. -=-=-=-=-
  141. Коэффициенты фильтра можно посчитать здесь:
  142. http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html