在STM32工程中使用Mavlink与飞控通信

需求来源:

Mavlink协议的优势为什么不自定义协议

1、通用性强,绝大部分飞控和地面站都是使用Mavlink协议,兼容性好;相比于自定义协议,在不同的飞控和地面站之间切换时代价更小;

2、避免了重复造轮子,降低了总体工作量,方便快速实现功能;

3、基于XML文件定义消息,可读性强,然后使用Python自动生成代码,并且可以生成常见的大部分语言的代码(C、C++、Java、Python、C#等),这极大地降低了飞控与地面站联调的工作量(如APM飞控使用的C++,而MissionPlanner地面站使用的是C#),也就是说跨平台性非常好;

4、支持多主机和多节点(System ID、Component ID)。

Mavlink的帧结构

# 以下为Mavlink2的帧结构:
uint8_t magic;              // 固定帧头,Mavlink2为0xFD
uint8_t len;                // 帧数据长度
uint8_t incompat_flags;     // 不兼容标识
uint8_t compat_flags;       // 兼容标识
uint8_t seq;                // 帧序号
uint8_t sysid;              // 系统ID,飞行器、地面站等各自算一个系统。
                            // 此值为0表示广播;飞控的此值一般为1;地面站的此值一般为255
uint8_t compid;             // 组件ID,此值为0表示广播
uint8_t msgid 0:7;          // 消息ID,bit0 ~ bit7
uint8_t msgid 8:15;         // 消息ID,bit8 ~ bit15
uint8_t msgid 16:23;        // 消息ID,bit0 ~ bit7
uint8_t payload[max 255];   // 帧数据,最多255个字节
uint16_t checksum;          // CRC-16/MCRF4XX

安装Mavlink生成器

1、安装Python3

Microsoft Store中搜索Python3,安装大于Python3.7的版本(是否含Python3.7不确定)。

2、获取Mavlink代码

# 无需切换分支
# 下面的“--recursive”已经包含了更新子模块的功能
git clone https://github.com/mavlink/mavlink.git --recursive

我下载好放到了百度网盘上:
链接https://pan.baidu.com/s/1iCCTlAM7B-VMutP9TDFwsg
提取码:nfcy

3、安装依赖项

# 在mavlink文件夹,右键,“在终端中打开”

python3 -m pip install -r pymavlink/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

# 注意,有黄色warning不用管

生成Mavlink头文件

# 在mavlink文件夹,右键,“在终端中打开”
python3 -m mavgenerate

# XML选择mavlink/message_definitions/v1.0/ardupilotmega.xml
# Out选择一个自己建立的文件夹

# Language选择C
# Protocol选择2.0,即使用mavlink2

认识Mavlink的XML文件

参考链接:https://mavlink.io/zh/guide/xml_schema.html

认识生成的头文件

将Mavlink头文件集成到STM32工程中

1、硬件介绍

演示电路板原理图在STM32工程目录的Doc文件夹下:

2、飞控端设置

  • SERIAL2_PROTOCOL:2,飞控串口2的通信协议,mavlink2
  • SERIAL2_BAUD:460,飞控串口2的波特率,460指的是460800
  • SERIAL2_OPTIONS:0,飞控串口2的额外选型,设置为默认值

3、将生成的代码复制到STM32工程中

4、在Keil中添加include路径

5、在代码中包含“.h”文件

#include <ardupilotmega/mavlink.h>

6、Mavlink解帧示例(详见视频教程:链接

7、Mavlink组帧发送示例(详见视频教程:链接

8、消除编译时的warning

原理:32位的int的最大值为2^31(去掉一个符号位)- 1 = 2147483647,而mavlink生成的代码里最大值已经超过了这个值。

解决方法:

MAV_SYS_STATUS_EXTENSION_USED=2147483646,
MAV_SYS_STATUS_SENSOR_ENUM_END=2147483647,

HIL_SENSOR_UPDATED_RESET=2147483646,
HIL_SENSOR_UPDATED_FLAGS_ENUM_END=2147483647,

9、mavlink中“数据流”(data stream)的概念

配置SERIAL2(参数名中简称SR2)的各个数据流的频率的参数:

进阶:添加自定义Mavlink帧

注意,此压缩包里的代码已经切换好分支并且更新好子模块,可以直接编译。

1、修改飞控端XML文件

飞控中的xml文件位置:modules/mavlink/message_defines/v1.0/

视频中的示例中我们修改的是modules/mavlink/message_defines/v1.0/cubepilot.xml,添加的内容如下:

    <message id="50006" name="NFCY_TEST_MAVLINK">
      <description>test mavlink.</description>
      <field type="uint8_t" name="test1">Test field 1.</field>
      <field type="uint16_t" name="test2">Test field 2.</field>
      <field type="uint32_t" name="test3">Test field 3.</field>
    </message>

2、编译飞控固件,产生对应的头文件

注意,xml文件夹中的v1.0和头文件的v2.0含义不同(应该是历史遗留问题,这里我们生成的的确是mavlink2)。

3、在飞控中添加自定义mavlink的发送程序

在ArduCopter/GCS_Mavlink.h中,添加发送函数的声明(注意,对于固定翼,就是在ArduPlane/GCS_Mavlink.h中,也就是说每种载具里都有其独有的GCS_Mavlink,然后在libraries中有各种载具共有的GCS_Mavlink

    void send_nfcy_test_mavlink() const;

在ArduCopter/GCS_Mavlink.cpp中添加函数定义:

void GCS_MAVLINK_Copter::send_nfcy_test_mavlink() const
{
    mavlink_msg_nfcy_test_mavlink_send(  # 发送函数的名字都为“mavlink_msg_消息名_send”
        chan, 1, 2, 3);
}

在libraries/GCS_MAVLink/ap_message.h中添加自定义的ID:

MSG_NFCY_TEST,  // 注意,这个必须放在MSG_LAST前面

在libraries/GCS_MAVLink/GCS_Common.cpp中添加两种ID的对应关系:

// 前面一个为Mavlink自动生成的ID,后面一个为上一步添加的ID
// 前面的ID格式都是“MAVLINK_MSG_ID_消息名”
{ MAVLINK_MSG_ID_NFCY_TEST_MAVLINK, MSG_NFCY_TEST},

在ArduCopter/GCS_Mavlink.cpp中添加:

// bool GCS_MAVLINK_Copter::try_send_message(enum ap_message id)函数中:

 case MSG_NFCY_TEST:
        send_nfcy_test_mavlink();
        break;

在ArduCopter/Copter.cpp中添加:

// void Copter::three_hz_loop()函数中添加:
gcs().send_message(MSG_NFCY_TEST);

4、在飞控中添加解析自定义mavlink的程序

请按照视频教程中的步骤操作:链接

5、为STM32工程重新生成Mavlink头文件(先修改XML文件)

请按照视频教程中的步骤操作:链接

6、在STM32中添加自定义mavlink的发送程序

7、在STM32中添加自定义mavlink的接收处理程序

疑难解答

后续大家遇到的问题的解决方法会更新在此处。

京ICP备19049723号   |   京公网安备 11010502039327号