Программная реализация движения Parrot Ar.Drone 2.0. по пользовательской траектории

Автор: Серебренникова А.А., Сначева К.Г.

Журнал: Теория и практика современной науки @modern-j

Рубрика: Основной раздел

Статья в выпуске: 11 (17), 2016 года.

Бесплатный доступ

Сегодня квадрокоптеры все больше и больше входят в нашу жизнь, и не только как детские и подростковые игрушки, но и как инструменты решения тех или иных проблем. Эта статья призвана помочь заинтересованным квадрокоптерами людям научиться самим реализовывать несложные программы взаимодействия с дроном. В статье подробно рассматривается учебный пример реализации движения квадрокоптера по заданной траектории: рассказано как правильно принимать и обрабатывать сетевые команды дрона, предложен и детально разобран алгоритм обработки движения дрона, разработан интерфейс программы для общения с пользователем. В качестве квадрокоптера была выбрана одна из современных моделей - AR.Drone 2.0 Parrot.

Еще

Квадрокоптеры, реализация движения квадрокоптера, алгоритм обработки движение дрона

Короткий адрес: https://sciup.org/140267595

IDR: 140267595

Текст научной статьи Программная реализация движения Parrot Ar.Drone 2.0. по пользовательской траектории

  •    Через порт 5554 квадрокоптера по протоколу UDP происходит инициализация канала связи для передачи по нему навигационных данных(статус работы, высота и скорость полета, направление и другое).

  •    С портов 5559 и 5555 дрона по протоколу TCP происходит передача конфигураций и видеопотока с одной из камер соответственно.

  •    Управление осуществляется через UDP-порт 5556. На него передаются так называемые AT-команды, в которых и заложены непосредственные инструкции для квадрокоптера.

Каждая AT-команда представляет собой строку, оканчивающуюся символом конца строки и имеющую следующую конструкцию:

AT*[имя команды]=[порядковый номер][параметр 1, параметр 2…]

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

  •    REF - базовые действия: взлет, посадка, аварийное выключение и перезапуск. Параметры: число, соответствующее конкретному действию.

  •    PCMD - передвижение дрона во время полета. Параметры: служебный флаг, крен, тангаж, рыскание, газ.

  •    FTRIM - сообщает дрону о том, что тот находится в горизонтальном положении, для его ориентации в пространстве во время дальнейшего полета. Параметры: нет.

Требуется следить за порядковыми номерами при передаче команд. Дрон обработает инструкцию только в том случае, если номер новой команды больше номера предыдущей.

Была разработана программа на С/С++, позволяющая отправить дрон в полет по нарисованной пользователем траектории. Цикл работы программы продемонстрирован на UML-диаграммe(рис.2).

Рис. 2. UML-диаграмма работы программы.

Для передачи AT-команд используются сокеты. Сокет открывается при инициализации объекта класса квадрокоптера. Для каждой команды предусмотрен отдельный метод, использующий ранее открытый сокет для передачи пакета.

Значения большинства параметров задаются с помощью IEEE-754 – стандарта, описывающего формат представления чисел с плавающей точкой.

В программе предусмотрены следующие действия с квадракоптером: Взлет:

Формат AT-команды: AT*REF= [порядковый номер],290718208.

290718208 – число, соответствующее взлету в команде REF.

Дрон поднимается на 1 метр над поверхностью. Для предотвращения конфликтов с последующими командами задается задержка программы на 7 секунд.

Листинг открытия сокета и метода взлета приведены ниже, а их алгоритм описан на рис. 3. Остальные методы реализованы схожим образом: class Drone{ public:

int i;

struct sockaddr_in receiver_addr;

int sock_fd;

char droneBuf[50];

Drone() { i = 1;

sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); receiver_addr.sin_family = AF_INET;

if (inet_aton( "192.168.1.1", &receiver_addr.sin_addr ) == 0) { close(sock_fd);

return;

} receiver_addr.sin_port = htons( PORT );

} void droneTakeOff() { sprintf(droneBuf,"AT*REF=%u,290718208\r",i++);

sendto(sock_fd, droneBuf, 50, 0,(struct sockaddr*)&receiver_addr, sizeof(receiver_addr));

std::cout << "away from land" << std::endl;

sleep(7);

}

// …

}

Рис. 3. Алгоритм открытия сокета и взлета.

Калибровка:

Формат АТ-комманды: AT*FTRIM=[порядковый номер].

Вызывается перед взлетом для правильной ориентации дрона в пространстве.

Посадка:

Формат АТ-команды: AT*REF= [порядковый номер],290717696.

290717696 – число, соответствующее посадке в команде REF.

Движение вперед:

Формат AT-команды:   AT*PCMD=[порядковый номер],1,0,-

1082130432,0,0.

Число -1082130432 соответствует исходному числу -1, что означает, что выставлен минимальный тангаж и дрон полетит вперед в горизонтальной плоскости. Служебный флаг установлен в 1, как и для всех действий, связанных с движением. Значения крена, рысканья и газа установлены в 0. Перед обращением к дрону вычисляется количество необходимых вызовов АТ-команды.

Повороты влево и вправо:

Форматы АТ-команд:

Поворот влево: AT*PCMD=[порядковый номер],1,0,0,0,-1090519040

Поворот вправо: AT*PCMD=[порядковый номер],1,0,0,0, 1056964608

Числа -1090519040 и 1056964608 соответствуют -0,5 и 0,5, т.е. команды поворота влево и вправо отличаются только значениями рыскания. Данные числа определяют угол поворота в 15 градусов. Значение тангажа, крена и газа установлены в 0.

На вход модулю, отвечающему за обработку координат, принятых от клиентского приложения, подаются координаты в таком виде: сначала количество координат, которые нужно обработать, затем сами координаты (первая по X, вторая по Y). После записи всех координат в массив struct point { data_t x;

data_t y;

};

typedef point point_t;

point_t* arr_crd = new point_t[qty_crd];

for (int pos = 0; pos < qty_crd; pos++) { std::cin >> arr_crd[pos].x >> arr_crd[pos].y;

} происходит взлет квадрокоптера с помощью функции droneTakeOff() из класса Drone.

Обработка координат. Как только дрон взлетел, все его дальнейшие перемещения будут описаны в двух координатных осях – ОХ и OY. Для работы с осями следует назначить дрону, после того как он взлетит, начальное значение координат и начальный основной вектор движения. Основной вектор движения – это направленный отрезок, находящийся под углом α относительно положительной полуоси абцисс единичной окружности. Для обозначения на плоскости вектора в данной работе достаточно знать только угл α , им и будем обозначать основной вектор движения. Однако вектор будет обозначен не всеми значениями угла α, а только кратными 15-ти. Данная погрешность допустима в рамках целей использования квадрокоптера.

Следовательно, обозначение вектора лежит в диапазоне [0, 15, 30, … , 330, 345, 360], где крайние, включенные в диапазон значения обозначают один и тот же вектор.

Начальные значения могут быть выбраны любыми, так как вся дальнейшая работа будет связана исключительно с их разностями (дельтами). Для удобства были выбраны координаты (0, 0) и основной вектор движения – ось координат OY.

Для корректной обработки каждой координаты нужно посчитать два измерения:

  • 1.    Как изменится основной вектор движения дрона относительно прошлой координаты.

  • 2.    Насколько дрон нужно переместить вперед в пространстве.

Изменение основного вектора движения дрона. Рассмотрим функцию:

void turn(int curent_drone_vector, int need_drone_vector, drone Ardrone) { int delta_angle = need_drone_vector - curent_drone_ vector;

Ardrone.droneRotateRight(pos);

}

Где curent_drone_vector – обозначение текущего вектора основного движения дрона, need_drone_vector – нужный вектор, delta_angle – угл, на который нужно повернуть дрон, чтобы изменить его основной вектор движения с текущего на нужный, droneRotateRight(х) – функция из класса Drone,   которая поворачивает дрон на х градусов направо, droneRotateLeft(х) – налево.

В функции turn() обрабатывается 5 событий:

  • 1.    Если delta_angle > 0 и delta_angle <= 180, то поворачиваем направо на delta_angle градусов.

  • 2.    Если delta_angle > 180, то поворачиваем налево на 360 -

  • abs(delta_angle), где abs(х) – функция взятия модуля от х.
  • 3.    Если delta_angle < 0 и delta_angle >= -180, то поворачиваем налево на abs( delta_angle).

  • 4.    Если delta_angle < -180, то поворачиваем направо на 360 -abs( delta_angle ).

  • 5.    Если curent_drone_vector = need_drone_vector , то не поворачиваем никуда.

Перемещение дрона вперед в пространстве. Существуют “простые” и “сложные” случаи. Под “простыми” подразумевается, что delta_x = 0 или delta_y = 0 , где delta_x – разность между текущей и следующей координатой дрона по оси ОХ, delta_y – по OY.

Рассмотрим 4 “простых” случая:

  • 1.    Если delta_x == 0 и delta_y > 0, то вызываем функции

  • 2.    Если delta_x == 0 и delta_y < 0, то turn(curent_drone_angle, 180 , Ardrone) и Ardrone.droneForward(abs( delta_y )).

  • 3.    Если delta_x > 0 и delta_y == 0, то turn(curent_drone_angle, 90 , Ardrone) и Ardrone.droneForward( delta_x ).

  • 4.    Если delta_x < 0 и delta_y == 0, то turn(curent_drone_angle, 270 , Ardrone) и Ardrone.droneForward(abs( delta_x) ).

turn(curent_drone_angle,          0 ,         Ardrone)         и

Ardrone.droneForward( delta_y ), где droneForward(х) - функция из класса Drone , которая двигает дрон на x единиц вперед.

Для рассмотрения “сложных” случаев понадобится функция, которая находит текущее положение вектора - find_drone_vector(int opposite_leg, int adjacent_leg). opposite_leg – противолежащий катет треугольника, образованного соединением текущей координаты со следующей и достраиванием delta_x и delta_y. adjacent_leg – прилежащий катет этого же треугольника. В зависимости от ситуации прилижащим катетом будет либо delta_x, либо delta_y, это же касается и противолежащего катета. Рассмотрим код функции:

int Find_drone_vector(int opposite_leg, int adjacent_leg) { double angle = asin(abs(opposite_leg) / sqrt(adjacent_leg * adjacent_leg + opposite_leg * opposite_leg));     // теорема пифагора angle = 180 * angle / pi;                     // перевод из радиан int integer_angle = angle / 15;                 // целая часть return (integer_angle + 1) * 15;

}

Рассмотрим 4 “сложных” случая:

  • 1.    Если delta_x > 0 и delta_y > 0, то

  • 2.    Если delta_x > 0 и delta_y < 0, то

  • 3.    Если delta_x < 0 и delta_y < 0, то turn(curent_drone_angle, Find_drone_vector( delta_x , delta_y) + 180 , Ardrone); int f = sqrt(delta_y * delta_y + delta_x * delta_x);

  • 4.    Если delta_x < 0 и delta_y > 0, то turn(curent_drone_angle, Find_drone_vector( delta_y , delta_x) + 270 , Ardrone); int f = sqrt(delta_y * delta_y + delta_x * delta_x);

turn(curent_drone_angle, Find_drone_vector( delta_x , delta_y), Ardrone);

int f = sqrt(delta_y * delta_y + delta_x * delta_x);      //Теорема пифагора

Ardrone.droneForward(f);

turn(curent_drone_angle, Find_drone_vector( delta_y , delta_x) + 90 , Ardrone); int f = sqrt(delta_y * delta_y + delta_x * delta_x);

Ardrone.droneForward(f);

Ardrone.droneForward(f);

Ardrone.droneForward(f);

Рассмотрим пример. Допустим, начальные координаты дрона указаны как (1, 3) (Рис. 4). Начальное обозначение основного вектора движения – 0.

Перваю смену координат (1,3) -> (2,3) описывает “простой” случай №3: Поворачиваем дрон на 90 градусов направо и двигаемся вперед на delta_x = 1.

Вторую смему координат (2,3) -> (3,1) описывает “сложный” случай №2: Find_drone_vector() вернет 75. Следовательно нужное обозначение вектора = 75 + 90 = 165. Функция turn() вызывается со следующими параметрами – turn(90, 165, Ardrone), что означает поворот направо на 75 градусов.

Далее двигаемся вперед на sqrt((-2) * (-2) + 1 * 1)

Рис. 4. Траектория движения дрона.

Взаимодействие с пользователем. Для создания интерфейса пользователя(Рис. 5) в качестве среды разработки был выбран Qt Designer. Библиотека Qt в числе прочих содержит множество графических элементов, удобных с точки зрения как разработки, так и эксплуатации программы.

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

С точка зрения пользователя, взаимодействие с дроном выглядит следующим образом:

  • 1.    Пользователь нажимает ЛКМ и, не отпуская ее, рисует произвольную линию.

  • 2.    Пользователь отпускает ЛКМ.

  • 3.    Пользователь нажимает кнопку “Start”.

  • 4.    Дрон поднимается в воздух и начинает движение по линии от первой нарисованной точки.

  • 5.    Каждый раз, когда дрон пролетает небольшой участок линии, соответствующая точка помечается красным, а ее координаты выводятся в текстовое поле рядом с полем для рисования.

  • 6.    Пролетев всю  траекторию, дрон опускается  на землю.

Пользователь может очистить поле для рисования с помощью кнопки “Clear” или отправить дрон по уже пройденной траектории.

Рис.5. Интерфейс пользователя.

Основной объект в интерфейсе – поле для рисования – создан на основе класса QGraphicsScene. Контейнером для данного поля является объект класса QGraphicsView.

Для определения актуальных размеров графической сцены при запуске программы и изменении размеров окна устанавливается таймер(объект QTimer) на 100 миллисекунд. Задержка необходима, так как непосредственно в момент события изменения размеров окна(resizeEvent) ширина и высота еще не принимают новых значений.

Для осуществления процесса рисования переопределены два события QGraphicsScene: mousePressEvent(событие нажатия ЛКМ) и mouseMoveEvent(событие, сообщающее о том, что мышь сдвинута при зажатой ЛКМ).

При нажатии ЛКМ перехватываются координаты точки, в которой пользователь нажал на кнопку, и вокруг них отрисовывается круг диаметром 2 пикселя. Координаты сохраняются в переменную previous_point. При движении мыши считываются координаты следующей точки, и проводится линия толщиной 2 пикселя между новой точкой и точкой с координатами из previous_point. В previousPoint записываются новые координаты. Данные действия повторяются, пока пользователь не закончит рисование.

Когда дрон достигает очередной точки из массива points, вызывается пользовательское событие pointReachEvent. Соответствующий слот pointReached обеспечивает уведомление пользователя о проделанном пути с помощью выделения достигнутой точки красным(рисуется красный круг диаметром 10 пикселей).

Рядом с полем для рисования находится текстовое поле(объект QTextView), в которое при возникновении pointReachEvent записываются координаты достигнутой точки.

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

Список литературы Программная реализация движения Parrot Ar.Drone 2.0. по пользовательской траектории

  • Stephane Piskorski, Nicolas Brulez, Pierre Eline, Frederic D`Haeyer. AR.Drone Developer Guide, 2012.
  • Официальный сайт QT. Режим доступа: http://doc.qt.io/qt-5/qtdesigner-manual.html (дата обращения 22.10.2016).
  • Robot App Store. Available at: http://www.robotappstore.com/Knowledge-Base/How-to-Program-ARDrone-Remotely-Over-WIFI/96.html (accessed 22.10.2016).
  • Wikipedia, The Free Encyclopedia. Available at: https://en.wikipedia.org/wiki/Parrot_AR.Drone (accessed 22.10.2016).
  • Stephane Piskorski, Nicolas Brulez, Pierre Eline, Frederic D`Haeyer. AR.Drone Developer Guide, 2012.
  • Официальный сайт QT. Режим доступа: http://doc.qt.io/qt-5/qtdesigner-manual.html (дата обращения 22.10.2016).
  • Robot App Store. Available at: http://www.robotappstore.com/Knowledge-Base/How-to-Program-ARDrone-Remotely-Over-WIFI/96.html (accessed 22.10.2016).
  • Wikipedia, The Free Encyclopedia. Available at: https://en.wikipedia.org/wiki/Parrot_AR.Drone (accessed 22.10.2016).
Статья научная