3、QGC架构概览:整体架构与核心模块
好,咱们进入第三章。这一章我打算把QGC的骨架给你拆开看看。说白了,就是让你知道这个地面站软件到底由哪些部分组成,它们之间怎么通信,以及你后续要改的代码大概在哪个文件夹里。
我记得刚接触QGC那会儿,打开源码一看,好家伙,几百个文件。当时真有点懵。后来做项目做多了,慢慢就摸清了门道——其实它的架构设计得很清晰,只是入口有点多。
3.1 整体架构:三层分离
QGC的架构,我习惯把它分成三层:
- UI层:用户看到的一切,地图、按钮、设置面板。基于Qt Quick(QML)实现。
- 业务逻辑层:C++写的核心控制逻辑,比如航点计算、参数管理、MAVLink消息解析。
- 通信层:负责与无人机、数传电台、模拟器之间的数据收发。
你想想看,这三层各干各的,互不干扰。UI层只负责展示和接收点击事件,业务层处理数据,通信层只管收发。这就是典型的前后端分离思路,只不过前后端都在同一个进程里。
核心要点:QGC的UI层用QML,业务层用C++。两者通过Qt的信号-槽机制绑定。你在QML里写的onClicked,背后可能连着一个C++函数。
我曾经在一个项目中,需要把QGC的UI全部换成触摸屏适配版。当时只改了QML文件,C++核心逻辑一行没动。这就是分层架构的好处——改界面不影响飞控逻辑。
3.2 核心模块介绍
QGC有四个主要视图,也是你日常开发最常打交道的地方。我按使用频率给你排个序:
| 模块名称 | 功能定位 | 主要文件路径(相对源码根目录) |
|---|---|---|
| FlyView | 飞行控制主界面,显示地图、飞行数据、遥测状态 | src/FlightDisplay/ |
| PlanView | 任务规划,航点编辑、航线生成 | src/PlanView/ |
| SettingsView | 地面站参数、无人机参数、通信设置 | src/Settings/ |
| AnalyzeView | 日志分析、MAVLink消息监控、调试工具 | src/AnalyzeView/ |
3.2.1 FlyView:飞行控制的核心
FlyView是你飞无人机时眼睛一直盯着的地方。它包含:
- 地图组件:显示飞机位置、航点、禁飞区。底层用Qt Location或自定义地图引擎。
- 仪表盘:高度、速度、电池电压、GPS卫星数。这些数据每秒刷新几十次。
- 虚拟摇杆:用于手动控制无人机,适合调试模式。
- 状态栏:显示飞行模式、连接状态、报错信息。
我个人习惯,每次改FlyView的UI时,先开一个模拟器(比如SITL),然后一边改一边看效果。这样效率最高。
3.2.2 PlanView:任务规划器
PlanView用来画航线。你在地图上点几个点,设置高度、速度,它就生成一条完整的任务航线。
这里有个坑,我曾经踩过:航点坐标系的转换。QGC内部用WGS84经纬度,但地图组件可能用墨卡托投影坐标。如果你直接拿地图上的像素坐标去算航点,飞机会偏到姥姥家去。正确的做法是用QGC提供的QGeoCoordinate类做转换。
注意:PlanView生成的航线数据,最终通过MAVLink的MISSION_ITEM协议上传给飞控。如果你要自定义航点类型(比如加一个“拍照”动作),需要同时修改QGC的PlanView和飞控端的任务解析器。
3.2.3 SettingsView:参数配置中心
SettingsView分两部分:
- 地面站设置:语言、单位制、地图源、遥测速率。
- 无人机参数:PID值、IMU校准、电池类型。这些参数通过MAVLink的
PARAM_SET/PARAM_GET协议读写。
嗯,这里要注意:参数读写是异步的。你发一个PARAM_READ请求,飞控可能几百毫秒后才回复。如果你连续发一堆请求,要做好超时重试机制。我在一个项目中就因为这个原因,导致参数列表加载不全,排查了半天才发现是请求队列满了。
3.3 MAVLink通信机制
MAVLink是QGC和无人机之间的“语言”。它本质上是一个轻量级的消息协议,定义了几百种消息类型。
3.3.1 消息结构
一条MAVLink消息长这样:
起始标志(0xFE) | 载荷长度 | 序列号 | 系统ID | 组件ID | 消息ID | 载荷数据 | 校验和
每个字段都有固定含义。比如系统ID用来区分不同的无人机(地面站通常是255),组件ID用来区分同一架飞机上的不同设备(飞控是1,相机是100)。
3.3.2 常用消息类型
| 消息ID | 名称 | 用途 |
|---|---|---|
| 0 | HEARTBEAT | 心跳包,每秒1次,表示设备在线 |
| 30 | ATTITUDE | 姿态数据(横滚、俯仰、偏航) |
| 33 | GLOBAL_POSITION_INT | GPS经纬度、海拔、地面速度 |
| 74 | COMMAND_LONG | 发送指令(解锁、起飞、返航) |
| 76 | COMMAND_ACK | 指令执行结果确认 |
3.3.3 QGC中的通信流程
QGC内部有一个MAVLinkProtocol类,负责解析和封装消息。流程是这样的:
- 接收:串口或UDP收到原始字节流,
MAVLinkProtocol按协议拆包、校验、解析成结构体。 - 分发:解析后的消息通过信号-槽机制发给对应的模块。比如
HEARTBEAT发给连接管理器,ATTITUDE发给FlyView的仪表盘。 - 发送:业务层构造消息结构体,
MAVLinkProtocol序列化成字节流,通过通信链路发出去。
开发技巧:调试MAVLink通信时,可以用QGC自带的AnalyzeView里的MAVLink Inspector。它能实时显示收发的每一条消息,包括原始字节和解析后的字段值。我排查通信问题基本都靠它。
3.4 架构关系图
下面这张图展示了QGC各模块之间的调用关系和数据流向。我特意把MAVLink通信层放在中间,因为它连接了UI和飞控两端。
这张图你看懂了吗?UI层只管画界面,业务层处理数据,通信层负责收发。每一层都只和相邻层打交道,不跨层调用。这就是QGC能支持多种飞控、多种通信链路的原因——换飞控只需要改通信层的解析逻辑,UI和业务层基本不动。
3.5 开发中的常见坑
我曾经踩过的坑:
- QML和C++的线程问题:QML运行在主线程,C++业务逻辑可能在后台线程。如果你在C++里直接操作QML对象,程序会崩溃。正确的做法是用信号-槽跨线程通信。
- MAVLink消息频率过高:默认遥测速率是10Hz,但有些传感器数据(比如IMU)能到100Hz。如果你不加限制地全部转发到UI,界面会卡死。我一般会在通信层加一个节流阀,只保留最近一次的数据。
- 参数缓存不同步:QGC本地会缓存一份无人机参数。如果你在飞控端改了参数(比如通过命令行),QGC这边不会自动更新。需要手动点“刷新”或重启地面站。
3.6 小结
这一章我们把QGC的骨架摸了一遍。你知道了:
- 三层架构:UI层(QML)、业务层(C++)、通信层(MAVLink)。
- 四个核心视图:FlyView、PlanView、SettingsView、AnalyzeView。
- MAVLink消息的结构和常用类型。
- 数据从飞控到UI的完整流向。