этой методике, вы всегда будете иметь N последних замеров, где N — это размер буфера.
Следующий фрагмент реализует циклический буфер:
// sketch_13_01_averaging
const int samplePin = A1;
const int bufferSize = 10;
int buffer[bufferSize];
int index;
void setup()
{
Serial.begin(9600);
}
void loop()
{
int reading = analogRead(samplePin);
addReading(reading);
Serial.println(average());
delay(1000);
}
void addReading(int reading)
{
buffer[index] = reading;
index++;
if (index >= bufferSize) index = 0;
}
int average()
{
long sum = 0;
for (int i = 0; i < bufferSize; i++)
{
sum += buffer[i];
}
return (int)(sum / bufferSize);
}
Это решение дает неверный усредненный результат, пока буфер не заполнится. На практике это не должно быть большой проблемой, так как не составит труда заполнить буфер замерами перед тем, как начать запрашивать усредненные замеры.
Обратите внимание на то, что переменная sum для хранения суммы в функции average объявлена с типом long. Это особенно важно, если используется емкий буфер и есть вероятность, что сумма превысит максимальное положительное значение int, которое немногим больше 32 000. Отметьте также, что она безопасно может возвращать результат усреднения в виде значения int, потому что среднее значение будет находиться в диапазоне значений отдельных замеров.
Введение в фильтрацию
Как говорилось в разделе «Введение в цифровую обработку сигналов», любой сигнал обычно состоит из целого спектра гармоник. Иногда бывает желательно исключить некоторые из этих гармоник, и тогда следует использовать прием фильтрации.
Наиболее распространенный способ фильтрации в Arduino — низкочастотная фильтрация. Представьте, что у вас имеется датчик освещенности и вы пытаетесь определить общий уровень освещенности и поминутную динамику ее изменения, например, чтобы определить момент, когда станет достаточно темно, чтобы включить освещение. Но вам нужно устранить высокочастотные изменения освещенности, вызванные такими событиями, как быстрое перемещение вблизи датчика объектов, заслоняющих свет, или засветка датчика искусственными источниками света, которые в действительности мерцают с частотой напряжения питания (50 Гц, если вы живете в России). Если вас интересует только медленно изменяющаяся часть сигнала, то вам нужен низкочастотный фильтр. И наоборот, если требуется откликаться на скоротечные события и игнорировать более протяженные тенденции, используйте высокочастотный фильтр.
Вернемся к проблеме искажений, вызываемых частотой переменного тока в электросети. Если, к примеру, вам нужно исключить только паразитную гармонику с частотой 50 Гц и оставить гармоники с частотами выше и ниже этого значения, тогда простое отсечение низкочастотных гармоник не даст желаемого результата. Для решения этой задачи следует использовать полосовой фильтр, который удалит только гармонику с частотой 50 Гц или, что более вероятно, все гармоники с частотами от 49 до 51 Гц.
Простой низкочастотный фильтр
Часто в циклическом буфере нет никакой необходимости, если требуется всего лишь сгладить сигнал. Такое сглаживание можно рассматривать как низкочастотную фильтрацию, которая отсекает высокочастотные составляющие сигнала и оставляет только общую динамику. Подобного рода фильтрация часто используется при работе, например, с датчиками поворота, чувствительными к высокочастотным изменениям, которые могут не интересовать вас, или когда вам достаточно знать, на какой угол повернуто устройство.
Простой и эффективный способ решения этой задачи заключается в сохранении некоторого скользящего среднего по нескольким замерам. Скользящее среднее вычисляется как пропорция между текущим скользящим средним значением и значением нового замера:
Сглаженное значениеn = (Коэффициент х Сглаженное значениеn–1) + ((1 – Коэффициент) х Замерn).
Коэффициент — это константа между 0 и 1. Чем выше значение коэффициента, тем сильнее эффект сглаживания.
Такое определение выглядит сложнее, чем есть на самом деле, поэтому взгляните на следующий код, чтобы убедиться, насколько этот прием прост в реализации:
// sketch_13_02_simple_smoothing
const int samplePin = A1;
const float alpha = 0.9;
void setup()
{
Serial.begin(9600);
}
void loop()
{
static float smoothedValue = 0.0;
float newReading = (float)analogRead(samplePin);
smoothedValue = (alpha * smoothedValue) +
((1 — alpha) * newReading);
Serial.print(newReading); Serial.print(",");
Serial.println(smoothedValue);
delay(1000);