/WTR-Mavlink-Library

移植到 stm32 平台的 mavlink

Primary LanguagePython

WTR Mavlink Library

WTRobot, HITsz

介绍

移植到 stm32 平台的 mavlink

MAVLink 是一套消息传输协议。可以使用它方便地在单片机之间传输信息(变量、结构体等),并且有校验机制,可以检测并丢弃传输错误的包。

本库支持 MavLink V1 和 V2 版本的协议。

MAVLink 官网: https://mavlink.io/zh/

安装 mavlink

获取 mavlink

方法一:克隆或下载本仓库,里面的 mavlink_1.0.12 文件夹就是 1.0.12 版本的 mavlink 生成器和 2.4.36 版本的 pymavlink

方法二:从官方库中下载:

  1. clone 官方库
    git clone https://github.com/mavlink/mavlink.git --recursive
    
  2. 切换到 release 版本
    cd mavlink
    # 查看有哪些版本的 mavlink 库
    git tag
    # 切换到你希望的版本,例如:
    git checkout 1.0.12 
    
    cd pymavlink
    # 查看有哪些版本的 pymavlink 库
    git tag
    # 切换到你希望的版本。例如:
    git checkout 2.4.36

安装环境

使用 mavlink 生成消息前,需要安装 python 和一些模块

安装参考 https://mavlink.io/zh/getting_started/installation.html
上面的链接中,Windows 下只需要装好 python 和 future 模块就行了,Linux 下可能还要安装 TkInter

开始上手

生成自定义消息

消息就是你要发送或接收的一组变量。

消息在 xml 文件中定义。mavlink 会根据 xml 文件生成对应的结构体以及收发函数。

如果想在两个系统之间传输消息,那么这个消息必须在这两个系统中都定义(而且这个消息的id和内容要相同)

编写 .xml 文件

可以仿照以下文件编写 .xml

wtr_mavlink_demo.xml

test.xml

建议将你写的 xml 文件也放在你的工程目录中,以免以后修改时找不到之前写的文件

以下是 MavLink 官方关于如何选取消息 id 的建议:

对于 MAVLink 1:

  • 有效数字介于 0 到 255。
  • ID 0-149 和 230-255 为common.xml保留。 语支可以使用180-229 用于自定义消息 (除非这些信息没有被其他包括语支使用)。

对于 MAVLink 2 :

  • 有效数字介于0-1677215。
  • 255以下所有值都被认为是保留的,除非报文也打算用于 MAVLink 1。 注意 ID 在 MAVLink 1 中很宝贵!

mavlink 官方有一些预定义好的消息(放在这里 mavlink_1.0.12/message_definitions/v1.0)。这些消息是官方预设的无人机通信消息,如果不玩无人机就基本上用不到。

详细语法见官方文档:

https://mavlink.io/zh/guide/xml_schema.html

https://mavlink.io/zh/guide/define_xml_element.html

根据 xml 文件生成库文件

  1. 打开 mavlink/mavgenerate.py
    • XML 选择你刚刚编写的 XML 文件
    • Out 选择你想要存放库文件的目录
    • Language 选择 C
    • Protocol 选择 1.0 或 2.0(看你需求,版本区别官网上有说明)
  2. Generate

移植到 stm32

  1. 将生成的库文件添加到你的 stm32 工程中

  2. src/wtr_mavlink.csrc/wtr_mavlink.h 添加到你的 stm32 工程中

  3. 对于 MDK,可能要开启 GNU 拓展

  4. 根据实际需要,修改 wtr_mavlink.h 中的以下内容

    // 系统 ID 和 组件 ID
    static mavlink_system_t mavlink_system = {
        1, // System ID (1-255)
        1  // Component ID (1-255)
    };
    
    // MavLink 能使用的最大通道数
    #define MAVLINK_COMM_NUM_BUFFERS 4
    

    MAVLink 支持多个系统互相通信。系统可以是上位机、机器人、遥控器、无人机等。一般建议为每个系统分配唯一的 System ID。如果一个系统上有多个单片机,建议为每个单片机分配唯一的 Component ID

    这两个 ID 只是为了让接收方知道消息是谁发过来的。 MAVLink 原本设想的是为系统上的每个组件(比如说各种传感器)分配一个 Component ID,但这里的 mavlink_system 是全局变量,一个单片机不方便设置多个 Component ID,所以只能每个单片机分配一个 Component ID 了。

    MAVLINK_COMM_NUM_BUFFERS 是 MAVLink 最大可用的通道数。通常一个通道对应一个串口。请根据需要分配。设置得越大会占用越多内存。MAVLink 默认为单片机分配 4 个通道。

使用 WTR Mavlink Library

使用 mavlink 相关函数前,需要 #include "wtr_mavlink.h"

只需要包含 wtr_mavlink.h 这一个文件就行了,不用包含 mavlink 的其他头文件

绑定通道和串口

mavlink 支持多通道收发,使用 mavlink 的通道前需要先绑定串口,之后对这个通道的发送和接收操作就相当于对绑定的串口的操作(串口要在 CubeMX 里先配置好)

一般一个通道对应一个串口,一个通道可以同时收发,通道的数量取决于你在wtr_mavlink.h中定义的 MAVLINK_COMM_NUM_BUFFERS

使用 wtrMavlink_BindChannel() 函数绑定

examples:

wtrMavlink_BindChannel(&huart1, MAVLINK_COMM_0);
wtrMavlink_BindChannel(&huart2, MAVLINK_COMM_1);

发送消息

mavlink 将你自定义的消息都封装在了结构体里,一个消息对应一个结构体

如名字为 speed 的消息在 mavlink_speed_t

本库采用阻塞式发送,使用如下函数发送结构体(记得绑定通道和串口):

// 向通道X发送结构体,(X要改为对应的数字)
mavlink_msg_xxx_send_struct(MAVLINK_COMM_X, &StructToBeSend);

xxx 为要发送的消息名称

接收消息

本库采用中断接收模式,因此如果需要接收消息,必须在 CubeMX 里使能串口全局中断

接收消息需要如下操作:

  1. 确保已经绑定通道和串口

  2. 在代码中调用:

    // 启动通道X对应串口的中断接收
    wtrMavlink_StartReceiveIT(MAVLINK_COMM_X);
  3. 在中断回调函数中调用wtrMavlink_UARTRxCpltCallback()

    例如:

    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
        // 接收通道X的消息
        wtrMavlink_UARTRxCpltCallback(huart, MAVLINK_COMM_X);
    }
  4. 在你喜欢的位置(如 main.c)定义如下函数:

    /**
     * @brief 接收到完整消息且校验通过后会调用这个函数。在这个函数里调用解码函数就可以向结构体写入收到的消息
     *
     * @param msg 接收到的消息
     * @return
     */
    void wtrMavlink_MsgRxCpltCallback(mavlink_message_t *msg)
    {
        switch (msg->msgid) {
            case 1:
                // id = 1 的消息对应的解码函数(mavlink_msg_xxx_decode)
                mavlink_msg_xxx_decode(msg, &StructReceived);
                break;
            case 2:
                // id = 2 的消息对应的解码函数(mavlink_msg_xxx_decode)
                break;
            // ......
            default:
                break;
        }
    }

    以上代码通过 msg->msgid 判断是哪个消息,还可以通过 msg->sysidmsg->compid 判断消息是从哪里来的

更多 example

更多 example 可以参考 examples 下的工程

性能测试

stm32f103 在 arm-none-eabi-gcc-10.3.1 编译器下:

开启 O3 优化:2M 波特率成功收发,2.5M 波特率接收失败

不开优化:1M 波特率接收失败,115200 (0.1M) 波特率接收成功

作者

X. Y.

版本日志

1.2

  • 增加数组下标溢出判断
  • 修改文件注释

1.1.1

  • 更新 readme
  • 添加 .gitignore
  • 更新 pymavlink 脚本到 2.4.36 版本
  • 发现这个库直接支持 V2 协议(原本是在 V1 协议 mavlink 库上设计的)(😀)

1.1

  • 修复同时收发时可能的卡串口问题

1.0

  • 优化性能
  • 重构 API
  • 使用外部 mavlink_get_channel_status 和 mavlink_get_channel_buffer
  • 完善注释

0.2

上古代码,赶时间写出来的,有许多问题,和之后的版本差别巨大,不建议使用