Усреднение значений, считываемых с аналогового входа

Пример чтения сигнала на аналоговом входе analogRead и сглаживание значение с помощью скользящей средней. Данный пример может быть полезен при чтение зашумленного сигнала с датчика.

Берем:

  • плата Arduino
  • 10k ом переменный резистор

Подключение


Подключаем один контакт переменного резитора к выходу 5В, средний к аналоговому выходу 0 и последний контакт земле.
Схема

Код
Программа хранит последние 10 значений в массиве, каждое новое значение записывается на место самого старого и рассчитывается среднее значение.
Размер массива может быть увеличен для большего усреднения или уменьшен с помощью переменной numReadings.


//задаем размер массива
const int numReadings = 10;

int readings[numReadings];  // массив для хранения значений полученных на аналоговом входе 
int readIndex = 0;          // индекс последнего значения
int total = 0;              // сумма значений
int average = 0;            // скользящее среднее

int inputPin = A0;

void setup() {
  // устанавливаем связь:
  Serial.begin(9600);
  //устанавливаем все значения в 0
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}

void loop() {
  // вычитанем последнее значение из суммы
  total = total - readings[readIndex];
  // считываем значение 
  readings[readIndex] = analogRead(inputPin);
  // добавляем значение к сумме
  total = total + readings[readIndex];
  // переставляем индекс на следующую позицию
  readIndex = readIndex + 1;

  // проверяем если мы выскочили индексом за пределы массива
  if (readIndex >= numReadings) {
    // если да, то индекс на ноль
    readIndex = 0;
  }

  // считаем среднее:
  average = total / numReadings;
  // выводим
  Serial.println(average);
  delay(1);  // задержка перед следующим циклом
}
2 лайка

x = (x * 9 + analogRead(A0)) / 10

3 лайка

Только если х float.

1 лайк

нет. int пойдет

Ну ну. Если int то по этой формуле никогда не дойдёт до установившегося значения. Проверь сам.

1 лайк

арифметика не врёт:

974
976
978
980
982
983
984
985
986
987
988
989
990
991
991
991
991
991
991
991
991
991

int x = 500;
int y = 1000;

void setup() {
  Serial.begin(115200);
}

void loop() {
x = (x * 9 + y) / 10;
Serial.println(x);
delay(300);
}

Чего-то я не пойму это к чему?

Я бы усреднял 2^n значений. На них делить легче !

Плюс надо отбросить самое маленькое и самое большое значения …

2 лайка

Сдвиг вправо?

Да … с вариациями …

это самый распространенный фильтр. значение = (значение*n + новое) /(n+1)
Гораздо лучше обычного “скользящего” и памяти меньше занимает.

1 лайк

Скажите, « скользящее среднее» с математической точки зрения, тоже самое что и конволюция , но для одномерного массива?

Ещё раз. Он правильно работает только на float. А на
float все преимущества по скорости и памяти теряются.

Какой то своеобразный фильтр, или рандом такой же.


По идее около 500, а на графике “амплитуды 505-487”.

На старом форуме выкладывал пример скользящего среднего на целых числах

word  prevMillis, intervalMs = 50;
int avgAdc, errorVal,  averageFactor = 8;
    
void setup() { 
  Serial.begin(115200);
  avgAdc = analogRead(A1);    
}
void loop() {  
  if((word)millis() - prevMillis >= intervalMs){
    prevMillis += intervalMs;      
    int valAdc = analogRead(A1); 
    int delta = valAdc - avgAdc;
    int sum = errorVal + delta;
    avgAdc += sum / averageFactor;
    errorVal = sum % averageFactor;//накапливаем погрешность
    Serial.print(valAdc); Serial.print('\t');
    Serial.print(errorVal); Serial.print('\t');
    Serial.println(avgAdc);  
  }    
}
259	-6	259
259	-6	259
259	-6	259
259	-6	259
259	-6	259
258	-7	259
258	0	258
256	-2	258
252	0	257
247	-2	256
241	-1	254
233	-6	252
225	-1	248
216	-1	244
207	-6	240
197	-1	234
185	-2	228
171	-3	221
157	-3	213
144	0	204
135	-5	196
127	-2	187
119	-6	179
112	-1	170
104	-3	162
96	-5	154
88	-7	146
77	-4	137
72	-5	129
71	-7	122
71	-2	115
71	-6	110
71	-5	105
71	-7	101
71	-5	97
71	-7	94
71	-6	91
71	-2	88
70	-4	86
71	-3	84
71	0	82
71	-3	81
71	-5	80
71	-6	79
71	-6	78
71	-5	77
71	-3	76
71	0	75
71	-4	75
70	-1	74
70	-5	74
71	0	73
71	-2	73
70	-5	73
71	-7	73
71	-1	72
71	-2	72
71	-3	72
71	-4	72
71	-5	72
71	-6	72
71	-7	72
71	0	71
71	0	71
71	0	71
70	-1	71
71	-1	71
71	-1	71
71	-1	71
71	-1	71
71	-1	71
71	-1	71
71	-1	71
71	-1	71
70	-2	71

Хмм. По арифметике среднее значение равно старое значение плюс новое и делённое на два.
Пробовал такую методику на индикаторе топлива. Работало нормально. Но с резкими сменами показаний данный метод плохо работает. Данные на дуине долго усреднялись. Тактовая частота низкая.

Почему именно “по арифметике” и какое именно среднее?

Сводка

Это намек, во-первых, что средних много больше, чем одно, а во-вторых, что среднее может быть далеко не только из двух чисел.

Скользящие средние для усреднения использовать это плохая идея. Поскольку при этом возникает запаздывание. Гораздо лучше использовать AMA Кауфмана. При этом методе усреднения если присутствует полезный сигнал то постоянная времени усреднения маленькая. Если присутствует только шум, то постоянная времени усреднения становится большой.