Как стать автором
Обновить

Комментарии 13

Добрый день, а на практике какой-то беспилотник использовался?
По тексту: поле Component ID на рисунка названо COMP, а в тексте COM, может быть на рисунке лучше использовать поля, как названо в исходнике?
Спасибо, поправил. На практике собираю самосборку в корпусе Skywalker X8 с Raspbery Pi3 * NAVIO2.
Видео полёта в ручном режиме

НЛО прилетело и опубликовало эту надпись здесь
По ссылке главная страница, на сайте MAVLink нет биндинга к заголовкам. Но я добавил ссылки в конце статьи
НЛО прилетело и опубликовало эту надпись здесь
Из предложений: возможно имеет смысл написать за что отвечает System ID и Component ID.
Также имеет смысл сразу сделать вместо класса AbstractHandler/HeartbeatHandler абстрактный класс для каждого MAV, который бы отвечал за обработку принятых сообщений и упаковку сообщений для каждого MAV отдельно:

class AbstractMav: public QObject
{
    Q_OBJECT
    
public:
    AbstractMav(QObject* parent = nullptr, AbstractLink* link);

public slots:
    void sendHeartbeat(unit8_t compid);
    void arm();
    void disarm();
    ...

signals:
    void messageReady(const mavlink_message_t& message, const AbstractLink* link);
    void heartheatReceived(unit8_t compid);
    void imuReceived(unit8_t compid, uint64_t timestamp,
                     const QVector3D& accel, const QVector3D& gyro, const QVector3D& mag,
                     double pressure, double temperature);
    ...

private slots:
    void processMessage(const mavlink_message_t& message);

private:
    void commandLong(uint8_t compid,
                     uint16_t command,
                     uint8_t confirmation,
                     float param1, float param2, float param3, float param4,
                     float param5, float param6, float param7 );
    ...

    uint8_t m_sysid;
    AbstractLink* m_link;
};


слот AbstractMav::processMessage соединятеся с сигналом MavLinkCommunicator::messageReceived, и отфильтровыват сообщенния принадлежащие только ему:
void AbstractMav::processMessage(const mavlink_message_t& message) {
    if (message.sysid != m_sysid) {
        return;
    }
    
    switch(message.msgid) {
        case MAVLINK_MSG_ID_HEARTBEAT: {
            emit heartheatReceived(message.compid);
            break;
        }
        case MAVLINK_MSG_ID_HIGHRES_IMU: {
            ...
        }
    }
}


а сигнал AbstractMav::messageReady со слотом MavLinkCommunicator::sendMessage. Каждая функция, которая отправляет данные на MAV, должна активировать сигнал messageReady:
void AbstractMav::sendHeartbeat(unit8_t compid) {
    mavlink_message_t message;
    mavlink_heartbeat_t heartbeat;
    
    mavlink_msg_heartbeat_encode(m_sysd, compid, &message, &heartbeat);

    emit messageReady(message, m_link)
}


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

И из мелочей: имеет смысл использовать QHash вместо QMap для члена MavLinkCommunicator:: m_linkChannels, так как упорядоченности по ключу нам не важна.
согласен по поводу работы с несколькими девайсами, но для подавляющего большинства это не самый важный момент, и все оставляют на потом.

 if (message.sysid != m_sysid) {
        return;
    }

тут не совсем корректно, тк общие сообщения будут игнорироваться.
Спасибо за советы, в следующей статье постараюсь учесть замечания.
В конечной архитектуре у меня есть класс Vehicle, который является моделью дрона, но наполнение его происходит всё равно через наследников AbstractHandler. Это упрощённый вариант паттерна цепочка обязанностей, реализованный через механизм сигналов и слотов Qt. Его я ввёл, чтобы избавиться от switch(message.msgid). Heartbeat тогда бы выглядел так:
void HeartbeatHandler::processMessage(const mavlink_message_t& message)
{
    if (message.msgid != MAVLINK_MSG_ID_HEARTBEAT) return;

    Vehicle* vehicle = m_vehicleService->requestVehicle(message.sysid);

    mavlink_heartbeat_t heartbeat;
    mavlink_msg_heartbeat_decode(&message, &heartbeat);

    vehicle->setType(::vehicleTypeFromMavLinkType(heartbeat.type));
    vehicle->setState(::vehicleStateFromMavLinkSystemStatus(heartbeat.system_status));
    // ...
}

К тому же, при такой реализации Vehicle не завязан на типы MAVLink, и в будущем можно добавить другой протокол. В следующей статье, как приведу код в порядок, нарисую диаграмму классов.

QMap использовал т.к. на прогнозируемым количестве элементов он должен быть быстрее QHash. Основывался на этой статье.

Сейчас уже лучше писать про версию 2.0, т.к. она совместима с 1.0 (выбирается через флаги).
2.0 уже поддерживается APM'ом и PX4. QGC пока в процессе перехода.


И еще, генератор поддерживает куда больше языков: Java, JavaScript, C#, Python, Object C, Swift.


А для C++ мне надоели неудобства C-lib и я написал C++11 генератор (вот только не знаю когда замержат).

Спасибо за уточнения, если мой APM правда съест версию 2.0, вторую часть напишу на ней. Про генераторы подправил в статье. C++11 генератор интересно, можно посмотреть где-нибудь?)

Не уверен, что APM 2.6 (что на atmega) уже поддерживает, проверь свежайшую прошивку.


https://github.com/mavlink/mavlink/tree/mavlink2-cxx11 но его нужно теперь переносить в pymavlink и слить в 3-4 коммита.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории