Arduino / [Из песочницы] Использование инерциальной навигационной системы (ИНС) с несколькими датчиками на примере задачи стабилизации высоты квадрокоптера

в 23:24, , рубрики: arduino, акселерометр, гироскоп, квадрокоптер, метки: , , ,

Arduino / [Из песочницы] Использование инерциальной навигационной системы (ИНС) с несколькими датчиками на примере задачи стабилизации высоты квадрокоптера
В данной статье я постараюсь рассказать о своем опыте создания и реализации алгоритма для обработки сигналов с нескольких стандартных датчиков, входящих в состав ИНС (в английской версии IMU), для решения задачи стабилизации высоты многороторного летательного аппарата (в моем случае — квадрокоптера). На хабре уже был ряд статей, описывающих, что это за игрушка и как её сделать самому. Как программисту по профессии, мне было интересно не только его собрать, но и поковыряться в «мозгах» и сделать что-то полезное для сообщества. В качестве «мозгов» я выбрал Arduino и замечательный проект MultiWii. Он полностью открытый, динамично развивается, но в нем пока есть «белые пятна». Например, неудовлетворительно работает стабилизация положения по высоте. И я решил разобраться, можно ли с имеющимся оборудованием улучшить эту часть системы.
Для начала немного вводной информации, чтобы прояснить, с чем предстоит иметь дело.
Мультиротор — аппарат с несколькими (3, 4, 6, 8) моторами с пропеллерами, каждый из которых создает вертикальную регулируемую тягу. В отличие от вертолета, стабилизация тут полностью электронная, и занимается ей микропроцессор при помощи ИНС (полетный контроллер).
Какие задачи необходимо решать в полете?
Определение ориентации (углов по трем осям относительно земли) и стабилизация по ним

Определение высоты и стабилизация по ней

Определение координат и полет по заданным точкам

Прием команд с пульта управления и выдача управляющих сигналов на моторы

Что мы имеем в распоряжении?

На данный момент в легкой доступности имеем стандартный набор сенсоров:довольно хорошие 3-х осевые гироскопы.

средние по качеству 3-х осевые акселерометры

средний по качеству 3-х осевой магнетометр

средний или плохой барометр

Такой набор в сборе с Arduino-подобным процессором или в виде отдельной платки можно найти за сумму 70-100$
У каждого датчика свои возможности и слабые стороны. По отдельности ни один из них не может решить ни одну и перечисленных выше задач, поэтому системы ИНС всегда строятся из комбинации датчиков, и самое интересное тут — это вычислительные алгоритмы, позволяющие соединить сильные стороны каждого из датчиков для устранения их недостатков.
Первая задача — стабилизация ориентации — довольно успешно решается гироскопами. Гироскопы очень точно меряют угловую скорость и после интегрирования можно получить углы. Но у них есть проблема — показания уплывают со временем. Для коррекции этого дрифта применяется акселерометр, который всегда (ну или почти всегда в долгосрочной перспективе) знает, где земля. Но акселерометр ничего не почувствует, если его крутить вокруг оси Z, поэтому нам нужен магнетометр, который всегда знает, где север.
Вторая задача — нахождение высоты — частично решается барометром. Если обнулить показания на земле, то при подъеме на каждый метр мы знаем, насколько изменятся его показания (естественно, если мы не летаем 12 часов и не начала меняться погода). Но по условию задачи барометр у нас плохой, и он выдает высоту +-1м с диким шумом амплитудой примерно в этих же пределах. В реальности мой датчик показывает следующее (на 10-й секунде перемещен на 1метр):
На помощь барометру может прийти сонар, который меряет высоту с очень высокой точностью (даже тот, что я приобрел за 5$, выдает точность ± 3мм по заявлению производителя). Но сонар способен работать только невысоко над землей (2-10м), меряет долго (до 200мс), чувствителен к качеству поверхности, к углу наклона, и может терять сигнал.
Третья задача — определение координат — не решается никак указанными выше датчиками. Акселерометр в комбинации с гироскопом может выдать линейные горизонтальные ускорения, но тут есть две проблемы: постоянно действующий огромный (по сравнению с тем, что будем измерять) вектор 1G, и отсутствие привязок для коррекции. Так что определение координат остается прерогативой GPS-сенсора, и на высокую точность тут рассчитывать не приходится.
Во всех любительских полетных контроллерах задача нахождения ориентации решена хорошо и на ней останавливаться не буду. Задача довольно простая и расписана в интернете (один, два). В MultiWii используется красивое решение без сложностей типа кватернионов и матриц DCM (не забываем, что считать все это будет простенький 16-Мегагерцовый процессор), на основе упрощений для малых углов и комплементарного фильтра.
Итак, ориентацию аппарата в пространстве мы знаем с высокой степенью точности. Теперь можно перейти к основной теме статьи, т. е. постараться улучшить результаты, которые выдает барометр (или сонар), чтобы их можно было скормить ПИД-регулятору. Для этого показания должны поступать без задержек, быть точными в короткой перспективе и не сильно уплывать со временем. Тема ПИД-регуляторов заслуживает отдельного пристального изучения, так как он широко используются в системах управления процессами. Я рекомендую сначала ознакомиться с их определением, чтобы лучше понять рассуждения, изложенные ниже.
Сглаживаем

Чем нам не подходят показания барометра в текущем виде? Ну во первых, сильная зашумленность сигнала будет вызывать лишние управляющие воздействия на моторы. Применив фильтр низких частот, мы уменьшим шум, но потеряем быстроту измерения. А это значит, что любые кратковременные возмущения останутся без внимания, резкие возмущения отработаются с большой задержкой, и самое главное, мы не получим дифференциальную составляющую (D) для ПИД-регулятора. А как следует из теории, регулятор без этой составляющей склонен к слабозатухающим осцилляциям вокруг целевой величины, что и наблюдается на практике.
Интегрируем

Хорошо, оставим барометр и возьмем акселерометр. Вроде все просто — из значения по оси Z вычтем константу 1G, получим линейное вертикальное ускорение. Дважды проинтегрируем его (фактически просуммируем в измерительном цикле) и получим скорость и относительное смещение. Для ПИД-регулятора это лакомые показатели, с ними можно построить хорошую динамическую модель. Но и тут не все так хорошо, как хотелось бы. Наклон аппарата вызовет изменение проекции вектора ускорения А на ось Z. Вибрации от мотора или изменение температуры могут вызвать «сдвиг» чувствительности, и наша константа 1G уже не будет соответствовать реальности. Но даже в случае идеально неподвижного аппарата и точно выставленной 1G, любой сенсор выдает шум. А ведь даже крохотная ошибка в течение десятка секунд двойного интегрирования вырастает до размера слона, и вот мы видим скорость 10м/с и высоту 20м (хотя от земли ещё даже не оторвались).
Комплементарный фильтр

Если объяснять по простому — этот фильтр применяется к двум величинам, измеряемых разными датчиками, и корректирует одну из них так, что она медленно стремится ко второй. В измерительном цикле фильтр реализуется простой формулой:
При этом влияние величины A2 на A1 пропорционально разнице между ними и определяется коэффициентом k (чем больше, тем слабее влияние).
Если применить этот фильтр к высоте, найденной акселерометром, и показаниям барометра, получится интересная штука: дрифт акселерометра будет постоянно корректироваться барометром, а показания барометра будут сглаживаться (так как для A2 этот фильтр работает как фильтр низких частот). Но корректироваться будет только второй интеграл, а первый по прежнему спокойно «дрейфовать» в бесконечность, и в итоге из-за малого коэффициента k барометр просто не сможет повлиять на ситуацию.
Почему этот фильтр прекрасно работает для пары гироскоп + акселерометр? Потому что там мы корректируем первый интеграл, и он в конце концов перестает «уплывать», когда величина коррекции за время одного цикла сравняется с величиной ошибки гироскопа, прибавляемой в этом же цикле при интегрировании.
ПИД-регулятор приходит на помощь

Но и из пары барометр+акселерометр можно извлечь нечто полезное, если применить к ним ПИД-регулятор (да-да, область их применения крайне обширна).
Итак, в чем главное слабое место нашего интегратора ускорения? В микро-ошибке, которая может возникнуть по разным причинам, описанным выше, при вычитании константы 1G. Если записать искомое ускорение в виде:
то, регулируя величину bias, можно управлять и первым интегралом (скоростью), и вторым (смещением). Итак, цель нашего ПИД-регулятора найдена. Но надо ещё знать ошибку. Сделаем допущение, что bias зафиксируется после наступлении некоей стабилизации параметров системы (температурных, вибрационных и т. д.). Когда bias будет найден, показания акселерометра станут очень близки к истине и можно применить комплементарный фильтр, скрестив их с барометром. Величина коррекции этого фильтра и будет ошибкой, от которой будет отталкиваться ПИД-регулятор (он стремится свести ошибку к 0 за счет регулирования целевой переменной).
Дальше находим все три составляющие ПИД-регулятора. Пропорциональная (P) — это сама ошибка. Интегральная (I) — просто интегрируем её. Дифференциальная (D) — по теории надо дифференцировать ошибку. Но в ней сидят ужасные шумы барометра и акселерометра. Такое дифференцировать страшно, поэтому применим хитрость — возьмем за D-составляющую найденную акселерометром скорость с отрицательным знаком. Так как D призвана ввести затухание в регулятор, скорость тут вполне сгодится — чем она больше, тем больше надо её «погасить».
Умножим каждый из компонентов на свои коэффициенты, сложим и получим bias. Но тут применим вторую хитрость — не будем прибавлять bias напрямую к ускорению, а прибавим только I-часть. Именно она описывает величину постоянно действующей ошибки, что соответствует нашему допущению о медленном изменении bias.
P и D части прибавим к скорости, умножив на dT (так как позаимствовали их из ускорения).
Так как основная задача регулятора — найти постоянную составляющую ошибки, я решил настроить его достаточно «мягко», чтобы по минимуму влиять на кратковременные изменения. Но тут остается широкое поле для экспериментов, и все буде определяться поведением реальных датчиков.
А как же гироскоп?

Выше я упомянул, что для определения высоты нам понадобится ещё и гироскоп. Действительно, описанный выше алгоритм будет работать, только если вектор A (в локальной системе) смотрит точно вдоль оси Z. Как только аппарат наклонится, произойдут две неприятные вещи: проекция A на ось Z изменится и ПИД-регулятор начнет заново медленно и мучительно искать bias. И вторая — любое горизонтальное ускорение начнет давать ненулевую проекцию на локальную ось Z. При углах наклона в 45° уже и не поймешь, где какое ускорение.
Но так как мы знаем точную ориентацию локальной системы относительно глобальной, нет ничего сложного восстановить справедливость — просто спроектируем локальный вектор A на локальный вектор G (изначально найденный акселерометром и заботливо вращаемый гироскопом), который всегда смотрит в землю.
Операция эта простая и вытекает из определения векторного произведения:
Сделать это надо до вычитания 1G.
Теперь можно посмотреть на код и результаты.
#define ACC_BARO_CMPF 300.0f
#define ACC_BARO_P 30.0f
#define ACC_BARO_I (ACC_BARO_P * 0.001f)
#define ACC_BARO_D (ACC_BARO_P * 0.001f)
#define VEL_SCALE ((1.0f — 1.0f/ACC_BARO_CMPF)/1000000.0f)
#define ACC_SCALE 9.80665f / acc_1G / 10000.0f

err = (alt - BaroAlt)/ACC_BARO_CMPF; // P term of error
errI+= err * ACC_BARO_I; // I term of error
accZ = (accADC[0]*EstG.V.X + accADC[1]*EstG.V.Y + accADC[2]*EstG.V.Z) *
InvSqrt(fsq(EstG.V.X) + fsq(EstG.V.Y) + fsq(EstG.V.Z))
- errI - acc_1G;
// Integrator - velocity, cm/sec
vel+= (accZ - err*ACC_BARO_P - vel*ACC_BARO_D) * cycleTime * ACC_SCALE;

// Integrator - altitude, cm
alt+= vel * cycleTime * VEL_SCALE;

// Apply ACC->BARO complementary filter
alt-= err;
errPrev = err;

Данные сенсоров приходят в следующих переменных:
EstG.V – вектор G (он получен ранее при нахождении ориентации)
accADC – чистые данные с акселерометра
BaroAlt – данные с барометра, конвертированные в cm
На выходе получаем accZ, vel, alt.
Как видно, вычислительная сложность алгоритма довольно простая и Arduino его «переварит» легко (особенно если перевести в целочисленную арифметику, но тогда код станет плохо читаем).PS: в видео есть фраза об отключении алгоритма при определенном угле наклона, и и-за этого возникает выброс ошибки. На самом деле это ограничение не нужно — алгоритм работает стабильно при любом угле от 0° до 360°
Если к этому алгоритму подключить сонар (вмето BaroAlt взять SonarAlt), то кривая высоты выглядит практически идеально. Таким образом, на низкой высоте используем сонар, при появлении ошибок или близко к пределу измерений переключаемся на барометр (предварительно согласовав высоты в период поступления достоверных данных с сонара).
К сожалению, погода пока не дает провести полетные испытания нового алгоритма. Как только появятся результаты, выложу графики сонара, отлаженный исходный код всего проекта и полетное видео.

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js