Фильтр низких частот (low pass filter)
При снятии показаний с аналоговых датчиков или при измерении ширины
импульса ШИМ-сигнала часто возникает проблема некоторого непостоянства
сигнала. Так в статье Декодер PPM с RC-аппаратуры на Arduino
сигнал на каждом канале колеблется в небольших пределах около некоторой
величины, хотя все органы управления аппаратуры находятся в состоянии
покоя. Такой эффект вызывается погрешностями в электрических цепях
аппаратуры и микроконтроллера. Но сигнал с приемника снимается не просто
так, а для использования в виде параметра в некоторых расчетах.
Например, для управления квадрокоптером. А там своих погрешностей
хватает... Для того, чтобы избавиться от этого эффекта можно воспользоваться программным фильтром низких частот.
Велосипед тут изобрести сложно. В интернете есть масса информации на эту тему.
Итак, имеем некоторый сигнал канала Ch, полученный с приемника. В состоянии покоя, этот сигнал имеет "дрожание" +/-2 микросекунды. Чтобы сгладить эти мелкие колебания воспользуемся самым простым ФНЧ на основе интегратора. Для этого после каждого нового вычисления значения канала Ch применим к нему формулу Ch_f = (1-w) * Ch_f + w * Ch. Здесь Ch_f - сигнал на выходе фильтра, полученный при предыдущем вычислении, Ch - сигнал с приемника, w - некоторый весовой коэффициент. Значение w выбирается в диапазоне от 0 до 1 и определяет, какие колебания требуют подавления. Как следует из формулы, w указывает приоритет входного сигнала относительно ранее полученного. Чем оно ближе к 1, тем больше шумов будет пропускать фильтр. А чем ближе к нулю, тем больше будет "запаздывание" конечного сигнала и, как результат, более вялая реакция на изменение состояния органов управления аппаратуры. Весовой коэффициент можно подобрать эмпирическим путем или вычислить. В последнем случае воспользуемся следующей формулой: t = (1 - w) * Tp / w Здесь Tp - период вычислений или длинна пакета PPM-сигнала, t - период времени, который отделяет слишком быстрые изменения от требуемых. Отсюда w = Tp / (Tp + t). К примеру, пусть Tp = 20ms, а t = 50ms. Тогда весовой коэффициент будет равен примерно 0.286. На практике я использовал коэффициент 0.14.
Я доработал скетч из выше указанного примера:
#include <PinChangeInt.h>
#define PPM_PIN 5 #define MAX_PPM_CHANNELS 25 #define Kw 0.14
volatile uint16_t temp_time; volatile uint16_t up_time; volatile uint16_t d_time;
volatile uint16_t ChannelsCount; volatile float Channel[MAX_PPM_CHANNELS]; volatile uint8_t Curr_Channel;
void setup() { digitalWrite(19, HIGH); // включить резистор на выводе аналогового входа 0 pinMode(13, OUTPUT); ChannelsCount==0xff; Curr_Channel=0; Serial.begin(9600); Serial.println("Start"); TCCR1B = 0; //stop timer TCCR1A = 0; TCNT1 = 0; //setup TCCR1A = 0; up_time = 0; TCCR1B = 0<<CS12 | 1<<CS11 | 0<<CS10;//0x1A; //start timer with 1/8 PCintPort::attachInterrupt(PPM_PIN, CalcPPM, CHANGE); }
float Rssi; void loop() { Rssi = (1-0.05)*Rssi + 0.05*analogRead(5); delay(50); Serial.print("RSSI="); Serial.print(round(Rssi)); Serial.print("Channels="+String(ChannelsCount)); for(uint8_t i=0;i<ChannelsCount;i++){ Serial.print(" Ch[="+String(i)+"]="); Serial.print(round(Channel[i])); } Serial.println(" "); digitalWrite(13, !digitalRead(13)); }
void CalcPPM() { temp_time = TCNT1; if (!PCintPort::pinState) { if (up_time>temp_time) d_time=(0xffff-up_time+temp_time)>>1; d_time = (temp_time-up_time)>>1; up_time = temp_time; if (d_time>3500) { //Sync ChannelsCount=Curr_Channel; Curr_Channel = 0; } else { //Channel Channel[Curr_Channel]=(1-Kw)*Channel[Curr_Channel] + Kw * d_time; Curr_Channel++; } } }
|