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

Комментарии 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 коммита.

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

Публикации

Истории