第4章:源码获取与结构分析

好,咱们开始动手了。这一章我带你干两件事:第一,把MAVLink官方C库从GitHub上拽下来;第二,把它的目录结构扒开看看,搞清楚每个文件夹、每个文件是干嘛的。

说实话,我第一次接触MAVLink源码时,也被它的目录结构搞得有点懵。文件夹套文件夹,头文件满天飞。但别怕,摸清规律后你会发现,这玩意儿设计得其实挺精巧。

4.1 从GitHub获取官方C库

MAVLink官方维护了两个主要的C语言仓库:

  • c_library_v2:这是MAVLink 2.0协议的C语言实现,也是我们课程的主角
  • c_library_v1:MAVLink 1.0的老版本,现在基本不用了

我个人习惯直接克隆仓库到本地,这样方便后续查看提交历史和版本切换。命令很简单:

git clone https://github.com/mavlink/c_library_v2.git

如果你只是临时用用,也可以直接下载ZIP压缩包。但我建议你学会用git,因为后面裁剪时,我们需要基于某个特定版本做修改,git的分支管理会帮大忙。

小提示: 我建议你克隆后切到 master 分支的最新提交。MAVLink协议还在演进,不同版本之间可能有细微差异。咱们课程基于2024年初的版本,如果你用的更新,遇到问题可以回退看看。

4.2 目录结构解析

克隆完成后,我们来看看它的目录长什么样。用 tree -L 2 命令可以看得比较清楚:

c_library_v2/
├── ardupilotmega/
├── ASLUAV/
├── autoquad/
├── common/
├── icarous/
├── minimal/
├── python_array_test/
├── standard/
├── test/
├── uAvionix/
├── README.md
├── mavlink_helpers.h
├── mavlink_types.h
├── mavlink_conversions.h
└── checksum.h

嗯,一眼看去,大部分是文件夹,每个文件夹代表一个消息集(message set)。说白了,就是不同厂商或组织定义的MAVLink消息集合。

我来给你拆解一下:

目录/文件 说明
common/ 核心消息集,所有飞控基本都会用到。比如心跳、姿态、GPS数据都在这里
minimal/ 最小消息集,只包含HEARTBEAT、PROTOCOL_VERSION等基础消息
standard/ 标准消息集,比minimal多一些通用消息
ardupilotmega/ ArduPilot专用的扩展消息集
ASLUAV/ 某款固定翼无人机厂商的私有消息集
autoquad/ AutoQuad飞控的私有消息集
icarous/ NASA的ICAROUS项目消息集
test/ 测试用的消息集,开发时调试用
python_array_test/ 测试Python数组序列化的,嵌入式开发一般用不到
uAvionix/ 某家ADS-B设备厂商的私有消息集
核心要点: 我们做嵌入式移植时,common/minimal/ 是必须的。其他厂商的私有消息集,除非你的项目需要跟特定设备通信,否则统统可以砍掉。

4.3 关键文件说明

根目录下有几个头文件,它们是整个MAVLink协议栈的骨架。我一个个说:

4.3.1 mavlink_types.h

这个文件定义了MAVLink协议的基础数据类型。比如 mavlink_message_t 结构体,它代表一条完整的MAVLink消息。还有 mavlink_status_t,用来记录解析状态。

我记得第一次看这个文件时,最让我困惑的是 mavlink_message_t 里那个 payload64 数组。为什么用64位整数数组来存负载?后来才明白,这是为了对齐和序列化效率。说白了,就是让CPU处理起来更快。

4.3.2 mavlink_helpers.h

辅助函数的大本营。这里面有:

  • mavlink_msg_*_pack():把结构体数据打包成MAVLink消息
  • mavlink_msg_*_decode():从MAVLink消息中解包出结构体数据
  • mavlink_parse_char():逐字节解析MAVLink帧
  • mavlink_finalize_message():计算并填充校验和

这些函数是咱们移植时的核心。你想想看,飞控和地面站之间来回传数据,全靠这些函数在背后干活。

4.3.3 mavlink_conversions.h

坐标转换和数学工具函数。比如:

  • 欧拉角转四元数
  • 四元数转欧拉角
  • 经纬度转地心地固坐标系

嗯,这里要注意,这些函数不是MAVLink协议必须的。如果你飞控里已经有自己的数学库,完全可以不用这个文件。我一般只在做地面站软件时才会用到它。

4.3.4 checksum.h

CRC16校验的实现。MAVLink 2.0使用CRC16-CCITT算法来保证数据完整性。这个文件很小,就一个函数 crc_calculate()。但别小看它,校验错了,整条消息就废了。

我曾经在一个项目里遇到过诡异的问题:地面站偶尔收不到飞控的心跳包。查了两天才发现,是CRC计算时用了错误的初始值。从那以后,我每次移植都会先写个测试用例,专门验证CRC计算对不对。

4.4 消息集内部结构

我们随便进一个消息集目录看看,比如 common/

common/
├── common.h
├── mavlink_msg_attitude.h
├── mavlink_msg_gps_raw_int.h
├── mavlink_msg_heartbeat.h
├── ... (几百个消息头文件)
└── version.h

每个 mavlink_msg_*.h 文件对应一条MAVLink消息。比如 mavlink_msg_heartbeat.h 里定义了心跳消息的打包、解包函数。

common.h 是总入口,它把所有消息头文件都include进来。你移植时,如果只需要部分消息,可以自己写一个精简版的 common.h,只包含你需要的消息。

version.h 记录了该消息集的版本号。不同版本的MAVLink协议,消息ID和字段定义可能不同。所以,保持版本一致很重要。

4.5 知识体系总览

说了这么多,我画张图帮你理清思路:

MAVLink C库结构总览 c_library_v2/ 核心头文件 消息集目录 辅助工具 mavlink_types.h - 基础数据类型 mavlink_helpers.h - 打包/解包/解析 checksum.h - CRC16校验 mavlink_conversions.h - 坐标转换 common/ - 核心消息集 minimal/ - 最小消息集 ardupilotmega/ - ArduPilot扩展 其他厂商私有消息集 mavlink_helpers.h - 解析引擎 mavlink_conversions.h - 数学工具 移植要点: 保留 common/ 和 minimal/,砍掉私有消息集 核心头文件一个都不能少,但可以按需裁剪内部函数

4.6 移植前的准备工作

在开始移植之前,我建议你做三件事:

  1. 确定你需要的消息集:如果你的飞控只跟QGroundControl通信,那 common/ 就够了。如果还要跟ArduPilot交互,加上 ardupilotmega/
  2. 检查编译器支持:MAVLink C库用了C99标准的一些特性,比如 stdint.h 里的定长整数类型。如果你的MCU编译器比较老,可能需要做适配。
  3. 准备测试环境:我习惯先在PC上用gcc编译一遍,确认没问题了再往嵌入式平台移植。这样能快速排除语法错误。
避坑指南: 我曾经在一个项目里直接拿整个c_library_v2往STM32上怼,结果编译报错一大堆。后来才发现,很多私有消息集里用了C99的变长数组,而我的编译器不支持。所以,一定要先裁剪再移植,别偷懒。

好了,源码已经到手,目录结构也摸清了。下一章我们就要开始动手裁剪了——把那些用不上的消息集和函数统统砍掉,只留下我们需要的部分。嗯,这才是嵌入式开发的精髓:够用就好,绝不浪费一个字节


公众号:蓝海资料掘金营,微信deep3321