Теория‎ > ‎

Фильтр низких частот

Под фильтрацией электрического сигнала обычно понимают выделение в сигнале желательных спектров, или подавление нежелательных. Тема фильтрации весьма обширна, и рассматривает множество различных подходов к обработке электрического сигнала. Существует масса различных классификаций фильтров, включая:
  • аппаратные и программные;
  • цифровые и аналоговые;
  • линейные и нелинейные;
  • фильтры средних, низких и высоких частот;
  • и т.п.
В рамках данной статьи будет рассмотрен один из самых распространенных электронных фильтров - фильтр низких частот (ФНЧ) (low pass filter), а именно, его программная реализация. Применительно к робототехнике, данный тип фильтров используется для сглаживания показаний различных датчиков и сигналов управления. Также, ФНЧ является составной частью более сложного, комплементарного фильтра.

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

Здесь, по оси Time отсчитывается время в секундах, а по оси Value показания 10-разрядного АЦП микроконтроллера, к которому подключен потенциометр. Несмотря на то, что я старался плавно поворачивать ручку прибора, график сигнала получился весьма неровный. Это объясняется двумя причинами. Во-первых, во время движения центрального контакта потенциометра по поверхности резистивного материала
возникает такое явление как "дребезг контактов". Во-вторых, человек существо "дрожащее", и любое его движение вызывает вибрацию, броски и прочее динамическое безобразие.

Шум на аналоговом входе микроконтроллера
Кроме того, даже когда ручка прибора не поворачивается, наблюдаются небольшие колебания сигнала. Это, по-видимому, результат воздействия различных наводок на тракте между АЦП и датчиком. Зачастую, именно эти мелкие колебания больше всего вредят работе системы. Например, если потенциометром регулируется тяга двигателей мультикоптера, такие паразитные колебания приведут к ошибкам подсистемы стабилизации.

Чтобы сгладить все эти неровности и устранить мелкие колебания, мы воспользуемся самым простым ФНЧ, на основе интегратора. Для этого, на каждой итерации глобального цикла микроконтроллера потребуется вычислить следующее выражение:

pot = (1-K)*pot + K*pot_raw

где pot - отфильтрованный сигнал, pot_raw - значение на аналоговом входе, и наконец K - коэффициент фильтра, который варьируется от 0.0 до 1.0.

Коэффициент K определяет, какие колебания считаются слишком высокими и требуют подавления, а какие следует пропустить с минимальными изменениями. Чем больше K, тем слабее фильтр, и тем более высокие колебания он может пропустить. Ниже представлен результат работы фильтра для разных значений K.

ФНЧ для сигнала потенциометра   ФНЧ для сигнала потенциометра

На левом графике, K = 0.1, а на правом K = 0.3. Видно, что при меньшем значении K, сигнал ведет себя более плавно. Мелкие колебания при K = 0.1 и вовсе отсутствуют.

Подбор коэффициента фильтра

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

t = (1-K) * delta / K

где t - период времени, который отделяет слишком быстрые изменения от требуемых; delta - время итерации глобального цикла.

Например, если в нашем случае с потенциометром, K = 0.1, а итерация длится 20мс, то время t = (1-0.1) * 0.02 / 0.1 = 0.18сек. То есть все изменения сигнала, которые длятся меньше 0.18 секунд будут подавляться. Во втором случае ( при K = 0.3), мы получим t = 0.047сек. 

На практике, все потенциометры в моем пульте управления квадрокоптером фильтруются с коэффициентом K = 0.05. При этом сигнал получается еще более плавным, чем на левом графике, но зато начисто подавляются всякого рода вибрации и паразитные наводки.

Фильтрация сигнала акселерометра

Еще одним удачным примером использования ФНЧ может служить обработка сигнала акселерометра. Ниже представлены графики сигнала до и после применения фильтра.

ФНЧ для сигнала акселерометра   ФНЧ для сигнала акселерометра

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

Также, даже если прибор находится казалось бы в покое, и не поворачивается вокруг осей, на него может действовать вибрация, например, от двигателей мультикоптера. Подобная вибрация (только не двигателей а руки ;) хорошо видна на правой части левого графика.

Чтобы устранить этот шум можно также воспользоваться фильтром низких частот. Такая фильтрация значительно подавит вибрацию и прочие сильные скачки сигнала, вызванные кратковременным воздействием. Результат работы ФНЧ изображен на правом графике голубой кривой. Видно, что показания акселерометра стали более пригодными для расчета положения датчика в пространстве.

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

Ниже представлен пример реализации ФНЧ для акселерометра, на Arduino или LaunchPad. Упоминаемая в примере библиотека SerialFlow, используется для визуализации показаний акселерометра на ПК, в приложении SFMonitor.

Фильтр низких частот для Arduino/LaunchPad

#include <SerialFlow.h>

SerialFlow rd;

#define BAUD_RATE 9600

const byte led_pin = P1_0;
const byte acc_x_pin = A5;;

short acc_x_raw, acc_x, acc_xf;

float FK = 0.1;

void setup() {
    pinMode(led_pin, OUTPUT);    
    rd.setPacketFormat(SerialFlow::COMPLEX_1, 2, 2);
    rd.begin(BAUD_RATE);
    acc_xf = 0;
}

void loop() {
    acc_x_raw = analogRead(acc_x_pin);
    acc_x = acc_x_raw - 1023/2.0;
    acc_xf = (1-FK)*acc_xf + FK*acc_x;

    rd.setPacketValue(acc_x);
    rd.setPacketValue(acc_xf);
    rd.sendPacket();
    delay(20);
}

Comments