Есть большая и интересная тема мультикоптеров. Я не буду касаться вопросов постройки и выбора оборудования(двигателей, регуляторов и пр.), не буду публиковать видео полетов. В мультикоптерах меня больше интересуют "мозги" - микроконтроллер с набором датчиков, обеспечивающий стабилизацию аппарата в пространстве.
Опять же, в сети полно информации о том где скачать готовую прошивку, как ее загрузить в контроллер и настроить. Достаточно обратиться к гуглу.
Я хочу поговорить не об этом. Этот материал для тех, кому интересны внутренности, принципы и алгоритмы, заложенные в прошивку.
Если Уважаемое Сообщество сочтет, что этой теме тут не место - пишите в комментариях - статью уберу и новых писать не буду.
Вообще, материала много и я планирую сделать несколько статей на эту тему. К сожалению, дело не быстрое...
Начать предлагаю с самого простого: програмный фильтр низких частот (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++;
}
}

}

 

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