Фильтрация помех

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

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

Нас окружают источники помех: электромагнитных, контактных и т.д. Помехи могут вызвать ложные срабатывания разрабатываемых нами устройств. При этом устранить причину бывает довольно сложно. Ложные сигналы с датчика движения или датчика освещённости могут вызвать включение ламп освещения, что может стать неприятным сюрпризом.

Аналоговые значения можно фильтровать с помощью радиоэлементов, к примеру – RC цепей. Со случайными значениями получаемыми от цифровых датчиков бороться сложнее.

Предлагаю разобрать алгоритм цифровой фильтрации. Алгоритм построен на базе экспоненциального сглаживания. В данном примере мы обрабатываем данные получаемые (на каждом цикле) с аналогового датчика освещённости. На экран выводим разность между реальным и фильтрованным значением. Интересные результаты получаются при включении диммируемого источника света.

#include <Arduino.h>

int nSize = 20; //размер массива
int n = 0;
double *arVal;  //указатель на массив
uint32_t tTime = 0; 
double Alpha = 0.1; //Чем меньше Alpha, тем в большей степени фильтруются, подавляются колебания исходного ряда и шума.
uint16_t valueA0;   //промежуточное значение, для наглядности

void setup() {
  arVal = new double(nSize);
  //проверить, что не NULL
  Serial.begin(9600);
}

double func(double *ar, int& nSize){ //вычисляем среднее значение массива накопленных данных
  double sum = 0;
  for(int i=0; i < nSize; ++i){      //суммируем все элементы массива
    sum += ar[i];
  }
  return sum / nSize;   //возвращаем среднее значение
}

void loop() {
  valueA0 = analogRead(A0);       //данные с датчика
  if(millis() - tTime > 300){     //выводим с задержкой, что бы успеть разглядеть результат
    Serial.println((long)arVal[n] - valueA0); //выводим разность между фильтрованным значением и текущим значением с датчика освещенности.
    tTime = millis();
  }
  arVal[n] = valueA0 * Alpha + (1-Alpha) * func(arVal, nSize);  //расчет очередного значения
  if(++n == nSize)n = 0;    //массив обрабатывается циклически.
}

Код с классами:

#include <Arduino.h>
#include <ArduinoSTL.h>

using namespace std;

class FilterES {
    const double Alpha;
    int nSize;
    std::vector<double> arVect;
    std::vector<double>::iterator it, end1, begin1;
  public:
    FilterES(int, double);
    double FilterAnalog(double);
    void outArray();
    double getValue();
};
//********************************************
void FilterES::outArray(){
  for(auto& iT: arVect){
    Serial.print(iT);
    Serial.print(" ");
  }
    Serial.println(" -----");
}
//********************************************
FilterES::FilterES(int size, double Alpha = 0.1):nSize(size), Alpha(Alpha){
  arVect.resize(nSize);
  begin1 = it = arVect.begin();
  end1 = arVect.end();
  if(arVect.empty())Serial.println("ERROR: Vector is empty.");
}
//********************************************
double FilterES::FilterAnalog(double val){
  double sum = 0.;
  for(auto& itT:arVect)sum += itT;  //сумма всех элементов массива
  *it = Alpha * val + (1-Alpha) * sum / nSize;
  it++;
  if(it == end1) it = begin1;
  return *it;
}
//********************************************
double FilterES::getValue(){return *it;}
//+++++++++++++++++++++++++++++++++++++++++++++
FilterES *fes;
uint32_t tTime = 0;
double valueA0;

void setup() {
  Serial.begin(9600);
  fes = new FilterES(20, 0.1);
}
//------------------------------------------------
void loop() {
    valueA0 = analogRead(A0);
    fes->FilterAnalog(valueA0);
  if(millis() - tTime > 100){
    tTime = millis();
    Serial.println(fes->getValue());
//    Serial.println(fes->getValue() - valueA0);
//    fes->outArray();
  }
}