СПО пульта управления БПЛА - DRONE520/DRONE520-main GitHub Wiki

QGroundControl - PuppetMaster

В проекте DRONE520 в качестве пульта управления БПЛА используется приложение QGroundControl. Оно позволяет не только подавать команды дрону и получать данные телеметрии, но и загружать прошивку автопилота, задавать последовательность действий (миссии) и управлять в ручном режиме.

Цель

Модификация приложения QGroundControl под нужды проекта DRONE520: использование телеметрии для контроля лебёдки провода питания.

Для дрона предлагается вместо аккумуляторов использовать проводное питание. Чтобы предотвратить запутывание провода и предотвратимое зацепление за препятствия, а также контролировать натяжение, наземная станция должна иметь возможность управлять лебёдкой в соответствии с расстоянием до дрона. Эти данные телеметрии доступны в приложении QGroundControl для просмотра, но возможности их вывода и использования в других приложениях не предусмотрено. Следовательно, необходимо модифицировать программу; предпочтительно - интегрировать управление лебёдкой в функционал приложения.

Архитектура ПО

QGroundControl использует компонентно-ориентированную архитектуру с дополнительной абстракцией.

Поскольку приложение должно работать с прошивкой дрона, для которой определена только спецификация протокола связи MAVLink, привязываться к конкретной реализации прошивки недопустимо. Чтобы изолировать общий для всех прошивок код от специфического, используется модель плагинов. Это означает, что ядро программы общается с подключаемыми модулями через интерфейсы, которых для QGroundControl определено три вида:

  • FirmwarePlugin инкапсулирует функционал прошивки;
  • AutoPilotPlugin инкапсулирует функционал автопилота;
  • QGCCorePlugin инкапсулирует функционал всех остальных модулей программы, не связанных с управляемым аппаратом.

Сборка ПО

Для разработки приложения используется IDE Qt Creator версии 4.

Чтобы собрать приложение, нужно загрузить его (Clone), открыть проект qgroundcontrol.pro в Qt Creator и произвести сборку проекта (Build). Собранное приложение будет помещено в подпапку staging папки сборки: ../build-qgroundcontrol-[Название комплекта сборки]-[Debug/Release].

После изменения структуры или файлов разметки интерфейса (QML) и перед началом сборки следует запустить скрипт custom/updateqrc.py, чтобы зафиксировать эти изменения в файле ресурсов (custom/qgroundcontrol.qrc).

Системные требования

Комплект сборки в Qt Creator - набор инструментов сборки, используемых в конкретном проекте. Комплект включает в себя указание на устройство сборки (в случае удалённой сборки), компилятор, отладчик, используемый профиль Qt, программу сборки CMake и утилиту QMake.

Комплект сборки для QGroundControl должен иметь следующие параметры:

  • Профиль Qt: 5.15.2
    • Дополнительная библиотека Qt Charts (поставляется с установщиком Qt)
  • Платформа: amd64
  • Компилятор:
    • Для Windows: MSVC 2019 (устанавливается как часть Visual Studio 2019)
    • Для Linux: GCC 5 или новее

Дополнительно - для использования скрипта обновления ресурсов: Python 3

Заметки о версировании

Поскольку этот проект является ответвлением оригинального, Github предоставляет возможность заполучить последние обновления (Fetch Upstream).

Для сборки необходимо наличие всех подмодулей! Использование: git clone --recursive для полной загрузки или git submodule update --init --recursive для уже загруженного проекта.

Больше информации о сборке проекта: https://dev.qgroundcontrol.com/master/en/getting_started/

Способы расширения функциональности

Плагины

Архитектура QGroundControl подразумевает использование плагинов для индивидуальной настройки приложения. Для отображения функций самого приложения QGroundControl, которые не связаны с транспортным средством, через стандартный интерфейс, используется интерфейс QGCCorePlugin. Затем плагин используется пользовательскими сборками для настройки набора функций QGroundControl в соответствии с их потребностями. Каждый плагин в проекте представлен отдельным файлом включения .pri.

Для стандартизации и упрощения расширения функционала QGC использует систему фактов (Fact), каждый из которых представляет собой одно значение в системе. К факту привязан объект FactMetaData, хранящий детали о его смысле, предназначении и совместимости. Интерфейс Fact Control использует Fact и его FactMetaData, чтобы вывести информацию на экран пользователя. Самый простой способ получить информацию - воспользоваться функцией интерфейса прошивки getFact("factName").

Модификация интерфейса

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

Например, такой код в custom/res/CustomFlyViewOverlay.qml создаёт кнопку снизу слева на главном экране, нажатие на которую выводит в диалоговое окно (MessageDialog) координаты базы, текущее положение БПЛА и расстояние между этими двумя точками:

//-------------------------------------------------------------------------
//-- My button
Rectangle {
    id:                         buttonHell
    width:                      ScreenTools.defaultFontPixelHeight * 1.5
    height:                     ScreenTools.defaultFontPixelHeight * 1.5
    radius:                     2
    anchors.bottom:             parent.bottom
    anchors.bottomMargin:       ScreenTools.defaultFontPixelHeight * 1.5
    anchors.left:               parent.left
    anchors.leftMargin:         ScreenTools.defaultFontPixelHeight * 1.5
    Button {
        text:                   "Where"
        anchors.centerIn:       parent
        onClicked:              messageHell.open()

        MessageDialog {
            id:                 messageHell
            x:                  256
            y:                  -512
            title:              qsTr("Vehicle position")

            property var rtkSettings:               QGroundControl.settingsManager.rtkSettings
            property bool useFixedPosition:         rtkSettings.useFixedBasePosition.rawValue

            function vehiclePosition() {
                var lat = _activeVehicle.gps.getFact("lat").rawValue;
                var lon = _activeVehicle.gps.getFact("lon").rawValue;

                return "Vehicle position: " + (lat < 0 ? -lat : lat) + "\u00b0 " + (lat < 0 ? 'S' : 'N') + ' ' + (lon < 0 ? -lon : lon) + "\u00b0 " + (lon < 0 ? 'W' : 'E')
                        + "; alt. " + _activeVehicle.altitudeRelative.rawValue + ' ' + _activeVehicle.altitudeRelative.units;
            }
            function ditoBase(bLat, bLon, bAlt) {
                const degToMeters = 6.371e6 * Math.PI / 180;
                var y = (bLat - _activeVehicle.gps.getFact("lat").rawValue) * degToMeters;
                var x = (bLon - _activeVehicle.gps.getFact("lon").rawValue) * Math.cos(bLat) * degToMeters;
                var z = bAlt - _activeVehicle.altitudeRelative.rawValue;

                return "Distance to base: " + Math.sqrt(x * x + y * y + z * z) + " m";
            }
            function hellString() {
                var basePosition = "";
                var distanceToBase = "Distance unavaliable";
                if (useFixedPosition) {
                    var bLatF = rtkSettings.fixedBasePositionLatitude.rawValue;
                    var bLonF = rtkSettings.fixedBasePositionLongitude.rawValue;
                    var bAltF = rtkSettings.fixedBasePositionAltitude.rawValue;

                    basePosition = "Using fixed base position\nBase position: "
                            + (bLatF < 0 ? -bLatF : bLatF) + "\u00b0 " + (bLatF < 0 ? 'S' : 'N') + ' '
                            + (bLonF < 0 ? -bLonF : bLonF) + "\u00b0 " + (bLonF < 0 ? 'W' : 'E')
                            + "; alt. " + bAltF + ' ' + _activeVehicle.altitudeRelative.units;

                    distanceToBase = ditoBase(bLatF, bLonF, bAltF);
                }
                else if (!QGroundControl.gpsRtk.active.rawValue)    basePosition = "RTK GPS is inactive!";
                else if (!QGroundControl.gpsRtk.connected.rawValue) basePosition = "RTK GPS is disconnected!";
                else if (!QGroundControl.gpsRtk.valid.rawValue)     basePosition = "RTK GPS data is invalid!";
                else {
                    var bLatD = QGroundControl.gpsRtk.currentLatitude.rawValue;
                    var bLonD = QGroundControl.gpsRtk.currentLongitude.rawValue;
                    var bAltD = QGroundControl.gpsRtk.currentAltitude.rawValue;

                    basePosition = "Using GPS RTK survey\nBase position: "
                            + (bLatD < 0 ? -bLatD : bLatD) + "\u00b0 " + (bLatD < 0 ? 'S' : 'N') + ' '
                            + (bLonD < 0 ? -bLonD : bLonD) + "\u00b0 " + (bLonD < 0 ? 'W' : 'E')
                            + "; alt. " + bAltD + ' ' + _activeVehicle.altitudeRelative.units;

                    distanceToBase = ditoBase(bLatD, bLonD, bAltD);
                }

                return basePosition + "\n" + (_activeVehicle ? vehiclePosition() + "\n" + distanceToBase : "\nNo vehicle was found!");
            }

            text:               qsTr(hellString())
            standardButtons:    StandardButton.Ok
            onAccepted:         messageHell.close()
        }
    }
}

Как можно заметить, данные о БПЛА система получает методом getFact объекта _activeVehicle, который был получен заранее как QGroundControl.multiVehicleManager.activeVehicle. Так же и координаты базовой станции: данные о её положении содержатся в настройках приложения в разделе RTK GPS (QGroundControl.settingsManager.rtkSettings). Значение факта получается как rawValue.

Рабочее устройство

В качестве лебёдки предлагается использовать мотор-редуктор IG-42M (https://electroprivod.ru/ig-42gm.htm). При передаточном числе 504 и выше его крутящий момент достигает 30 кг•см с разумной скоростью вращения 13,5 об/мин.

Для управления мотором предлагается использовать контроллер BMSD (https://electroprivod.ru/bdc-driver_bmsd.htm). Регулировка скорости осуществляется с помощью встроенного в мотор энкодера на основе датчика Холла. Управление с компьютера осуществляется через интерфейс RS-485, который подключается через преобразователь NPort 5232 (https://moxa.pro/catalog/nport5232) в режиме Real COM к линии Ethernet. Таким образом, компьютер обменивается сообщениями с контроллером через виртуальный COM-порт.

Схема подключения

Протокол обмена контроллера BMSD предполагает использование сообщений длиной 5 байтов.

Байты управляющего сообщения (от компьютера к контроллеру):

  1. Заголовок - байт всегда равен 0xE6;
  2. Адрес блока - устанавливается перед началом работы;
  3. Код команды - см. ниже;
  4. Данные - см. ниже;
  5. CRC - помехозащитный байт.

Не более чем через 50 мс после получения сообщения "опрос состояния" (0x50) контроллер отправляет ответное сообщение. Биты ответного сообщения (от контроллера к компьютеру):

  • [0-7]. Адрес блока;
  • [8]. Режим работы с датчиком Холла:
    • 0 в асинхронном режиме (отсутствие импульсов от датчика Холла);
    • 1 в синхронном режиме - автоматическая стабилизация скорости;
  • [9]. Бит переполнения счётчика оборотов;
  • [10]. Режим установки параметров:
    • 0 - по умолчанию (ручная установка);
    • 1 - с компьютера;
  • [11]. Бит направления движения;
  • [12-23]. Счётчик оборотов;
  • [24-31]. Скорость вращения;
  • [32-39]. CRC - помехозащитный байт.

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

  1. Адрес блока;
  2. Сообщение;
  3. Данные 1;
  4. Данные 2;
  5. CRC - помехозащитный байт.

Список команд, посылаемых от ПК, и ответов блока:

Команда Ответ
Команда Код Данные Сообщение Данные 1 Данные 2
Установка адреса (только по адресу 0xFF) 0xA0 Новый адрес (0x00..0xFE) 0xA0 Новый адрес 0x00
Отбой установки адреса (только по адресу 0xFF) 0xA1 Любые --- Ответ не посылается ---
Число импульсов Холла на один оборот 0xA2 Число (0x00..0xFF) 0xA2 0x00 Число
Скорость, об./сек 0xA3 Скорость (0..250) 0xA3 0x00 Скорость
Максимальная скорость 0xA4 Макс. скорость (0..250) 0xA4 0x00 Макс. скорость
Ускорение 0xA5 Ускорение (1..24) 0xA5 0x00 Ускорение
Торможение 0xA6 Торможение (1..24) 0xA6 0x00 Торможение
Направление 0xA7 0 или 1 0xA7 0x00 Направление
Вход в режим регулировки от ПК (запуск) 0x51 Любые 0x51 0x00 Любые
Вход в дежурный режим (остановка) 0x52 Любые 0x52 0x00 Любые

Заключение

Индивидуальная настройка приложений - важная часть их адаптации в собственный проект, поскольку это позволяет получить от готового продукта новый, ранее недоступный функционал, который нужен именно в этом проекте. Благодаря открытой (Open Source) природе и модульной структуре QGroundControl является отличным приложением для реализации расширенного функционала наземной станции, оставаясь при этом совместимым со стандартом прошивок автопилота MavLink.