Код для скользящего среднего

c

Ниже я написал код, чтобы найти скользящее среднее значение датчика температуры, подключенного к Arduino, и отобразить его на ЖК-дисплее. Моя проблема в том, что в первую минуту работы среднее значение неверно. Он начинается с 0, а затем увеличивается до нужного значения (~ 85F) с шагом примерно 3. После первой минуты все в порядке и работает так, как должно.

Я хочу, чтобы код просто брал среднее значение суммы значений, записанных за первую минуту.

Мои знания C довольно ограничены (у меня были занятия по нему только в течение семестра), поэтому все просто. Я студент-механик, и этот код предназначен для проекта во время моей стажировки.

#include <Adafruit_MLX90614.h>
Adafruit_MLX90614 tempSensor;

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

double array[30];
double rollingaverage=0;
int i=0, j=0;
double sum=0;
double newreading;
void setup() {

  Serial.begin(9600);
  lcd.begin(20,4);
  mlx.begin();
  Wire.begin();

}

void loop() {
  if(j=0){
      newreading = mlx.readObjectTempF();
      array[i] = newreading;
      sum += array[i];
      if(i>0){
          rollingaverage = sum/(i+1);
      }
      j=1;
  }
  else{
      newreading = mlx.readObjectTempF();
      array[i] = newreading;
      if(i < 29){ //оборачивает i обратно в 0, если оно равно 29, чтобы вычесть самое старое значение
          sum = sum + array[i] - array[i+1];
          rollingaverage = sum/30;
      }
      else{
          sum = sum + array[i] - array[0];
          rollingaverage = sum/30;
          i=0; //сбрасываем индекс на 0
      }
  }
  i++;

  lcd.clear();
  lcd.print("Belt: "); lcd.print(rollingaverage);
  lcd.setCursor(1,2);
  lcd.print("Air: "); lcd.print(mlx.readAmbientTempF());

delay(2000);
}

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

, 👍1

Обсуждение

Например, если у вас 25 допустимых значений и 5 нулевых, вам нужен счетчик для «действительных значений», измените rollingaverage = sum/30; и замените 30 значением счетчика., @MatsK

if(j==0) вместо (j=0), возможно, было бы лучше., @Szundi


3 ответа


2

Ваша проблема связана с тем, что вы начинаете с 30 значений 0, которые вы включаете в среднее значение. Вы должны принять это во внимание и игнорировать эти значения, разделив общую сумму только на количество фактических значений, которые у вас есть.

У меня есть библиотека, которая делает все это за вас и облегчит вам задачу:

  • https://github.com/MajkoLibraries/Average
,

1

Один из способов — инициализировать весь буфер первым значением. Ваше среднее значение не будет в некотором смысле «истинным», пока весь буфер не будет заменен считанными данными, но пока это первое значение не слишком отличается от истинной температуры, ваше среднее значение за этот период будет более разумным. . Т.е. ему не придется увеличиваться с нуля.

Другой способ — использовать экспоненциальное среднее вместо скользящего или товарного среднего. Экспоненциальное среднее не зависит от хранения старых образцов и может хорошо приспосабливаться - т.е. в пределах нескольких образцов, в зависимости от 'X' (см. ниже) - к внезапному изменению (чего температуры обычно не делают).

Новое среднее значение рассчитывается путем суммирования X процентов текущего среднего значения + (100-X) процентов следующей выборки — вы выбираете значение «X», которое лучше всего подходит для вашего приложения. У меня хорошие результаты при усреднении температуры даже по аналоговым датчикам при использовании X=75. Для чисел с фиксированной точкой, таких как 4 двоичных разряда, возвращаемых датчиками Max DS18b20, будет

// (3 * avg + 1 * sample) / 4
avg = (((avg<<2) - avg + sample) + 2) >> 2;

Для чисел с плавающей запятой это будет просто:

avg = (3. * avg + sample)/4.;

Обновление:

Инициализация экспоненциального среднего является такой же проблемой, как и со средним товарным вагоном.

Да, это так. Я инициализирую свои экспоненциальные усредняющие устройства с помощью первой выборки и обычно неплохо справляюсь. Это могло бы работать не так хорошо, если бы усреднение было слишком сильно демпфировано.

... товарный вагон шириной 30...

Экспоненциальное среднее по своей природе может придать недавним выборкам больший вес, чем более старым, поэтому придание столь малого веса следующей выборке, как вы предлагаете, сделает ее довольно жесткой! X=75% сработало для меня весьма хорошо для снижения шума без жесткого демпфирования. То, что хорошо работает для одной системы, может не работать для другой, но, проведя несколько испытаний сохраненного набора образцов на своем рабочем столе, вы быстро получите разумный результат X.

Мы не знаем, как была получена такая ширина (30); возможно, это необходимо? Но усреднение по своей природе является одновременно низкочастотным (обычно желательно) и задержкой (обычно нежелательным), расширяет внезапные особенности, такие как всплески, и одновременно ограничивает их амплитуду. Иногда я использую подавитель пиков перед более легким усредняющим фильтром, где альтернативный фильтр только для усреднения потребовал бы достаточного усреднения, чтобы слишком сильно подавить важные характеристики сигнала.

,

Инициализация является такой же проблемой для экспоненциального среднего, как и для товарного среднего. В частности, товарный вагон шириной 30 имеет примерно ту же « силу усреднения», что и экспоненциальное среднее с X = 100×(1-1/30). А последнему для полного урегулирования понадобится более 30 проб., @Edgar Bonet


0

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

Я бы использовал экспоненциальное сглаживание (которое проще кодировать, требует меньше данных и дает аналогичный результат). Это метод, который я использовал, когда отвечал за составление статистических отчетов о производительности. Если показания отображаются на графике, это приводит к более гладкому графику, который показывает тенденции и снижает шум.

Вкратце возьмите текущую выборку, умножьте ее на коэффициент дисконтирования и прибавьте долю к текущему значению.

s = x * α + (1-α) * s

Чтобы получить результаты с самого начала, инициализируйте образец до первого показания, а затем рассчитайте.

См. https://en.wikipedia.org/wiki/Exponential_smoothing

Я бы, наверное, начал с коэффициента сглаживания 0,3 — выбрал меньшее значение, чтобы придать больший вес прошлым значениям.

,

По сути, вы повторяете ответ Дж. Роберта., @Edgar Bonet