QGC整体架构与代码目录解析

QGroundControl 4.0 工程架构与代码目录技术文档


前言

QGroundControl(以下简称 QGC)是由 QGroundControl 开发团队维护的开源无人机地面站(Ground Control Station, GCS)软件。它基于 Qt 5.11+ / C++14 / QML 构建,通过 MAVLink 协议与 PX4、ArduPilot 等飞控栈通信,提供飞行监控、任务规划、参数配置、固件升级、视频流接收等完整 GCS 能力。

本文从工程实践角度,按 分层架构 → 目录结构 → 类体系与信号槽 → 多线程调度 四个维度,对 QGC 4.0 代码库进行系统性解析。文中代码引用均来自当前工程源码。


一、分层架构设计

QGC 并非严格意义上的四层 MVC,而是采用 "工具箱(Toolbox)+ 插件(Plugin)+ 数据绑定(Fact)" 的混合架构。若从职责划分角度,可映射为以下四层:

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    UI 表现层 (Presentation)                  │
│  QML 视图 + C++ Controller + QGroundControlQmlGlobal        │
├─────────────────────────────────────────────────────────────┤
│                    业务逻辑层 (Business)                     │
│  Vehicle / MissionManager / FirmwarePlugin / AutoPilotPlugin│
├─────────────────────────────────────────────────────────────┤
│                    通信协议层 (Communication)                │
│  LinkManager / LinkInterface / MAVLinkProtocol              │
├─────────────────────────────────────────────────────────────┤
│                    数据持久层 (Data)                         │
│  FactSystem / SettingsManager / ParameterManager / 缓存文件  │
└─────────────────────────────────────────────────────────────┘
         ↑ libs/mavlink、Eigen、ShapeLib 等第三方库 ↑

1.1 UI 表现层

UI 层以 QML 声明式界面 为主,C++ 控制器为辅,通过 Qt 属性绑定实现数据驱动渲染。

核心入口:

  • src/ui/MainRootWindow.qml:应用顶层 ApplicationWindow,定义全局属性(如 activeVehicle),承载 Fly View / Plan View / Setup View 等视图切换。
  • src/FlightDisplay/:飞行显示界面(HUD、仪表盘、视频叠加)。
  • src/FlightMap/ + src/PlanView/:地图与任务规划界面。
  • src/AutoPilotPlugins/:各飞控栈的设置向导 QML 页面。
  • src/QmlControls/:可复用 QML 控件库(按钮、对话框、参数编辑器等)。

C++ 与 QML 的桥梁:

QGroundControlQmlGlobal 是 QML 侧访问 C++ 后端的全局单例入口,将 LinkManagerMultiVehicleManagerSettingsManager 等核心服务以 Q_PROPERTY 形式暴露给 QML:

69:77:src/QmlControls/QGroundControlQmlGlobal.h 复制代码
    Q_PROPERTY(LinkManager*         linkManager         READ linkManager            CONSTANT)
    Q_PROPERTY(MultiVehicleManager* multiVehicleManager READ multiVehicleManager    CONSTANT)
    Q_PROPERTY(QGCMapEngineManager* mapEngineManager    READ mapEngineManager       CONSTANT)
    Q_PROPERTY(QGCPositionManager*  qgcPositionManger   READ qgcPositionManger      CONSTANT)
    Q_PROPERTY(MissionCommandTree*  missionCommandTree  READ missionCommandTree     CONSTANT)
    Q_PROPERTY(VideoManager*        videoManager        READ videoManager           CONSTANT)
    Q_PROPERTY(MAVLinkLogManager*   mavlinkLogManager   READ mavlinkLogManager      CONSTANT)
    Q_PROPERTY(QGCCorePlugin*       corePlugin          READ corePlugin             CONSTANT)
    Q_PROPERTY(SettingsManager*     settingsManager     READ settingsManager        CONSTANT)

QML 中通过 QGroundControl.multiVehicleManager.activeVehicle 即可绑定当前活动飞行器的状态,实现 UI 与业务数据的自动同步。

插件化 UI 扩展:

QGCCorePluginsrc/api/)允许自定义构建替换默认行为,包括设置页列表、分析页、工具栏 URL、品牌资源等,是 OEM 定制 QGC 的核心扩展点。

1.2 通信协议层

通信层负责物理链路的建立、字节流收发、MAVLink 帧解析与分发,是 GCS 与飞行器之间的唯一数据通道。

三层结构:

组件 职责
LinkManager 链路生命周期管理:创建/连接/断开、自动连接、MAVLink 通道分配
LinkInterface 抽象链路接口:串口/UDP/TCP/蓝牙/HIL 仿真的统一基类
MAVLinkProtocol MAVLink 帧解析、序列号丢包统计、遥测日志、消息广播

数据流(接收方向):

复制代码
mermaid
sequenceDiagram
    participant HW as 物理设备/网络
    participant Link as LinkInterface<br/>(独立线程)
    participant LM as LinkManager
    participant MP as MAVLinkProtocol
    participant MVM as MultiVehicleManager
    participant V as Vehicle

    HW->>Link: 原始字节流
    Link->>MP: bytesReceived(link, data)
    MP->>MP: mavlink_parse_char()
    MP->>MVM: vehicleHeartbeatInfo (首次心跳)
    MVM->>V: 创建 Vehicle 实例
    MP->>V: messageReceived(link, msg)
    V->>V: _mavlinkMessageReceived()

关键连接在 LinkManager::_addLink() 中建立:

200:202:src/comm/LinkManager.cc 复制代码
    connect(link, &LinkInterface::communicationError,   _app,               &QGCApplication::criticalMessageBoxOnMainThread);
    connect(link, &LinkInterface::bytesReceived,        _mavlinkProtocol,   &MAVLinkProtocol::receiveBytes);
    connect(link, &LinkInterface::bytesSent,            _mavlinkProtocol,   &MAVLinkProtocol::logSentBytes);

MAVLinkProtocol::receiveBytes() 逐字节调用 mavlink_parse_char(),完整帧解析后通过 messageReceived 信号广播:

352:355:src/comm/MAVLinkProtocol.cc 复制代码
            // The packet is emitted as a whole, as it is only 255 - 261 bytes short
            // kind of inefficient, but no issue for a groundstation pc.
            // It buys as reentrancy for the whole code over all threads
            emit messageReceived(link, _message);

发送方向 采用线程安全写法:外部调用 writeBytesSafe() → 发射 _invokeWriteBytes 信号 → 在链路线程内执行 _writeBytes(),避免跨线程直接操作串口/套接字。

1.3 业务逻辑层

业务层封装无人机相关的领域逻辑,与具体 UI 和通信细节解耦。

核心实体:Vehicle

Vehiclesrc/Vehicle/Vehicle.h/.cc)是整个业务层的中心对象,代表一架已连接的 MAVLink 飞行器。每个 Vehicle 实例聚合:

  • ParameterManager:飞控参数读写
  • MissionManager / GeoFenceManager / RallyPointManager:任务/围栏/集结点
  • FirmwarePlugin / AutoPilotPlugin:固件特定行为与设置 UI
  • 多个 FactGroup 子组:GPS、电池、振动、风速等遥测数据

多机管理:MultiVehicleManager

监听 MAVLinkProtocol::vehicleHeartbeatInfo,在收到 Autopilot 组件(MAV_COMP_ID_AUTOPILOT1)的心跳时创建 Vehicle:

66:66:src/Vehicle/MultiVehicleManager.cc 复制代码
    connect(_mavlinkProtocol, &MAVLinkProtocol::vehicleHeartbeatInfo, this, &MultiVehicleManager::_vehicleHeartbeatInfo);

固件插件体系:

作用
FirmwarePlugin 抽象基类:飞行模式字符串、MAVLink 命令映射、任务命令树
FirmwarePluginManager 工厂:按 MAV_AUTOPILOT + MAV_TYPE 返回对应插件
PX4FirmwarePlugin / APMFirmwarePlugin PX4 / ArduPilot 具体实现
AutoPilotPlugin 设置向导 UI 组件列表(传感器、遥控器、安全等)

这种设计使 QGC 主体代码保持 MAVLink 通用,固件差异全部收敛到 Plugin 目录。

任务规划业务链:

复制代码
PlanView.qml (UI)
    ↓ 绑定
PlanMasterController (协调 Mission/GeoFence/Rally)
    ↓
MissionController (视觉任务项编辑、路径计算)
    ↓ sendToVehicle()
PlanManager (MAVLink Mission Protocol v2 状态机)
    ↓ MAVLink MISSION_* 消息
Vehicle → 飞控

PlanManager 实现了 MAVLink v2 任务协议的状态机,含超时重试、ACK 等待、进度百分比等:

74:77:src/MissionManager/PlanManager.h 复制代码
    static const int _ackTimeoutMilliseconds = 1500;
    // When actively retrying to request mission items, use a shorter timeout instead.
    static const int _retryTimeoutMilliseconds = 250;
    static const int _maxRetryCount = 5;

1.4 数据持久层

数据层统一管理参数、设置、缓存三类数据。

FactSystem --- 统一数据模型

Fact 是 QGC 中最基础的数据单元,表示一个带元数据(类型、单位、枚举、范围)的可绑定值:

27:28:src/FactSystem/Fact.h 复制代码
/// @brief A Fact is used to hold a single value within the system.
class Fact : public QObject

FactGroup 将多个 Fact 组织成树形结构,支持批量更新速率限制(避免高频遥测导致 UI 卡顿):

23:24:src/FactSystem/FactGroup.h 复制代码
/// Used to group Facts together into an object hierarachy.
class FactGroup : public QObject

ParameterManager --- 飞控参数同步

每个 Vehicle 持有一个 ParameterManager,负责:

  • 向飞控请求全量参数列表(PARAM_REQUEST_LIST
  • 本地磁盘缓存(parameterCacheDir()
  • 参数写入队列与 pendingWrites 状态跟踪

SettingsManager --- 应用配置

SettingsManager 作为 QGCToolbox 中第一个创建的工具,管理所有持久化应用设置:

55:56:src/QGCToolbox.cc 复制代码
    // SettingsManager must be first so settings are available to any subsequent tools
    _settingsManager        = new SettingsManager           (app, this);

下设 AppSettingsAutoConnectSettingsVideoSettingsFlightMapSettings 等子模块,均基于 QSettings 持久化。

离线/在线地理数据:

  • Terrain/:地形高度查询(在线 API + 离线瓦片)
  • QtLocationPlugin/:地图瓦片引擎与离线缓存
  • Geo/:坐标系转换(UTM、MGRS 等)

二、核心源码文件夹作用详解

2.1 工程根目录

目录/文件 功能
src/ 全部核心 C++/QML 源码
libs/ 第三方依赖(MAVLink、Eigen、ShapeLib 等)
resources/ 静态资源:SVG 图标、FlightGear HIL 飞机模型
cmake/ CMake 构建模块(Git 版本、Qt 配置、Android 工具链)
deploy/ 打包脚本(AppImage、NSIS 安装包、PPA)
tools/ 构建辅助(版本号更新、Qt 打包、ccache)
android/ / ios/ 移动端平台配置
qgroundcontrol.pro qmake 主工程文件
CMakeLists.txt CMake 构建入口
QGCExternalLibs.pri 外部库路径与 MAVLink 方言配置

2.2 src/ 模块一览

应用框架
目录 说明
main.cc 程序入口:初始化 QApplication、注册 QML 类型、启动 UI
QGCApplication.* 应用单例:语言、主题、消息框、主窗口引用
QGCToolbox.* 服务注册中心:统一创建和管理所有顶层工具
api/ 插件 API:QGCCorePluginQGCOptionsQGCSettings
Settings/ 应用设置分组管理
QmlControls/ QML 控件库与 C++ 控制器
通信子系统 (comm/)
文件/类 说明
LinkManager 链路 CRUD、自动连接、MAVLink 通道池
LinkInterface 链路抽象基类(继承 QThread
SerialLink / UDPLink / TCPLink 具体链路实现
MAVLinkProtocol MAVLink 解析与日志
MockLink 单元测试用仿真链路
LogReplayLink 遥测日志回放
QGCJSBSimLink / QGCXPlaneLink HIL 仿真链路
车辆与数据 (Vehicle/, FactSystem/, uas/)
目录 说明
Vehicle/ Vehicle 实体、MultiVehicleManager、MAVLink 日志管理
FactSystem/ FactFactGroupParameterManagerFactMetaData
uas/ 遗留 UAS 接口、UASMessageHandler(STATUSTEXT 消息)、FileManager(MAVLink FTP)
固件插件 (FirmwarePlugin/, AutoPilotPlugins/)
子目录 说明
FirmwarePlugin/PX4/ PX4 飞行模式、参数元数据 XML、MAVLink 命令 JSON
FirmwarePlugin/APM/ ArduPilot 各机型插件(Copter/Plane/Rover/Sub)
AutoPilotPlugins/PX4/ PX4 设置页(机架、传感器、安全、调参)
AutoPilotPlugins/APM/ ArduPilot 设置页(电机、罗盘校准等)
AutoPilotPlugins/Common/ 通用组件(遥控器、电机测试)
AutoPilotPlugins/Generic/ 通用 MAVLink 飞控插件
任务规划 (MissionManager/, PlanView/)
类/目录 说明
MissionItem 单个 MAVLink 任务项
VisualMissionItem / SimpleMissionItem / ComplexMissionItem 可视化任务项抽象
SurveyComplexItem / CorridorScanComplexItem / StructureScanComplexItem 复杂任务模式
MissionController Plan View 任务编辑控制器
PlanMasterController 任务+围栏+集结点总控
PlanManager MAVLink Mission Protocol 状态机
PlanView/ 任务规划 QML 界面
地图与显示
目录 说明
FlightMap/ 地图控件、MapItems(车辆/航点/围栏可视化)
FlightDisplay/ Fly View HUD 与仪表盘
QtLocationPlugin/ 自定义 Qt Location 插件(多地图源)
Geo/ 坐标转换数学库
Terrain/ 地形高度查询接口
PositionManager/ 地面站自身 GPS 位置
外设与扩展
目录 说明
GPS/ 外接 RTK GPS(GPSProvider 独立线程 + Drivers/ 驱动)
Joystick/ 游戏手柄输入(SDL / Android)
Camera/ MAVLink 相机控制
VideoStreaming/ GStreamer 视频接收
Audio/ 语音告警播报
ADSB/ ADS-B 交通感知
FollowMe/ "跟随我"模式
AirspaceManagement/ / Airmap/ 空域管理
AnalyzeView/ 日志分析、MAVLink 控制台、GeoTag
VehicleSetup/ 固件升级、摇杆校准
ViewWidgets/ 可扩展自定义视图
UI 层 (ui/)
子目录 说明
MainRootWindow.qml 主窗口
toolbar/ 顶部工具栏(连接、GPS、电池、模式指示器)
preferences/ 设置对话框(链路、MAVLink、串口等)
测试
目录 说明
qgcunittest/ 单元测试框架与测试用例
SingletonTest/ 本工程自定义的单例测试模块

2.3 libs/ 第三方库

用途
mavlink/ MAVLink v2.0 协议头文件(默认 ardupilotmega 方言)
eigen/ 线性代数(坐标变换、姿态计算)
shapelib/ ESRI Shapefile 读写(地理数据导入)
qtandroidserialport/ Android 串口支持
gst-plugins-good/ GStreamer 视频插件
OpenSSL/ 加密(配对、AirMap)
sdl2/ 摇杆输入(桌面端)

三、类结构与信号槽通信机制

3.1 应用启动与工具箱初始化

程序从 main.cc 启动,创建 QGCApplication 单例,后者持有 QGCToolbox
main.cc
QGCApplication
QGCToolbox 两阶段初始化
Phase 1: new 所有 QGCTool
Phase 2: setToolbox 注入依赖
QQmlApplicationEngine
加载 MainRootWindow.qml
QGroundControlQmlGlobal 单例

两阶段构造模式是 QGC 依赖注入的核心约定:

129:140:src/QGCToolbox.h 复制代码
class QGCTool : public QObject {
    Q_OBJECT
public:
    // All tools must be parented to the QGCToolbox and go through a two phase creation. In the constructor the toolbox
    // should only be passed to QGCTool constructor for correct parenting. It should not be referenced or set in the
    // protected member. Then in the second phase of setToolbox calls is where you can reference the toolbox.
    QGCTool(QGCApplication* app, QGCToolbox* toolbox);
    virtual void setToolbox(QGCToolbox* toolbox);
protected:
    QGCApplication* _app;
    QGCToolbox*     _toolbox;
};

Phase 1 仅做 new,Phase 2 的 setToolbox() 中才通过 _toolbox->xxx() 获取其他工具引用并建立信号连接。这避免了工具间循环依赖和未初始化访问。

工具创建顺序(节选):

  1. SettingsManager(必须最先,供后续工具读取配置)
  2. QGCCorePlugin(自定义/OEM 插件)
  3. LinkManagerMAVLinkProtocolMultiVehicleManager(通信链)
  4. FactSystemFirmwarePluginManagerMissionCommandTree
  5. VideoManagerGPSManagerJoystickManager

3.2 核心类继承关系

复制代码
QObject
├── QGCTool (工具基类)
│   ├── LinkManager
│   ├── MAVLinkProtocol
│   ├── MultiVehicleManager
│   ├── SettingsManager
│   ├── FirmwarePluginManager
│   ├── GPSManager
│   ├── VideoManager
│   └── QGroundControlQmlGlobal
│
├── QThread
│   └── LinkInterface (链路基类)
│       ├── SerialLink
│       ├── UDPLink
│       ├── TCPLink
│       └── MockLink
│
├── Vehicle (核心业务实体)
│   ├── 持有 ParameterManager
│   ├── 持有 MissionManager / GeoFenceManager / RallyPointManager
│   └── 持有多个 FactGroup 子对象
│
├── FactGroup
│   └── Fact (单个数据点)
│
├── PlanManager (任务协议状态机)
├── MissionController (Plan View 控制器)
└── AutoPilotPlugin (设置向导抽象)
    ├── PX4AutoPilotPlugin
    └── APM 各机型 Plugin

3.3 信号槽通信模式

QGC 中信号槽承担 跨模块解耦跨线程安全调度 两大职责。主要模式如下:

复制代码
LinkInterface::bytesReceived
    → MAVLinkProtocol::receiveBytes (DirectConnection, 同线程或链路线程)
        → emit messageReceived(link, msg)
            → Vehicle::_mavlinkMessageReceived (每个 Vehicle 独立连接)
            → LinkManager::_mavlinkMessageReceived
            → PlanManager::_mavlinkMessageReceived

Vehicle 在构造时订阅全局 MAVLink 消息:

228:228:src/Vehicle/Vehicle.cc 复制代码
    connect(_mavlink, &MAVLinkProtocol::messageReceived,        this, &Vehicle::_mavlinkMessageReceived);

Vehicle 内部按 msg.sysid 过滤,只处理属于自己的消息。这种 单点解析、多点订阅 的模式避免了在 Link 层做路由逻辑。

模式二:属性绑定(QML ↔ C++)

C++ 侧通过 Q_PROPERTY + NOTIFY 信号驱动 QML 自动刷新:

41:46:src/Vehicle/MultiVehicleManager.h 复制代码
    Q_PROPERTY(bool                 activeVehicleAvailable          READ activeVehicleAvailable                                         NOTIFY activeVehicleAvailableChanged)
    Q_PROPERTY(bool                 parameterReadyVehicleAvailable  READ parameterReadyVehicleAvailable                                 NOTIFY parameterReadyVehicleAvailableChanged)
    /// The current, active vehicle
    Q_PROPERTY(Vehicle*             activeVehicle                   READ activeVehicle                  WRITE setActiveVehicle          NOTIFY activeVehicleChanged)
    /// The list of all connected vehicles
    Q_PROPERTY(QmlObjectListModel*  vehicles                        READ vehicles                                                       CONSTANT)

QML 侧:

qml 复制代码
property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
property bool communicationLost: activeVehicle ? activeVehicle.connectionLost : false

当 Vehicle 的 connectionLost 变化时,工具栏、地图、HUD 等所有绑定该属性的 QML 组件自动更新,无需手动刷新。

模式三:Fact 值变更链(遥测 → UI)
复制代码
MAVLink MSG (如 GLOBAL_POSITION_INT)
    → Vehicle::_mavlinkMessageReceived
        → _gpsFactGroup._handleGpsRawInt(msg)
            → Fact::_containerSetRawValue(value)
                → emit valueChanged (若启用)
                    → QML Text 绑定 fact.value 自动刷新

FactGroup 支持 _updateRateMSecs 批量更新,将高频遥测(如 50Hz 姿态)节流到 UI 可接受频率(如 10Hz),避免 QML 重绘风暴。

模式四:跨线程 QueuedConnection

Vehicle 发送 MAVLink 消息时使用队列连接确保线程安全:

233:233:src/Vehicle/Vehicle.cc 复制代码
    connect(this, &Vehicle::_sendMessageOnLinkOnThread, this, &Vehicle::_sendMessageOnLink, Qt::QueuedConnection);

链路断开等需要异步处理的场景:

212:212:src/comm/LinkManager.cc 复制代码
    connect(link, &LinkInterface::connectionRemoved,    this, &LinkManager::_linkConnectionRemoved, Qt::QueuedConnection);

UI 弹窗从工作线程触发时使用主线程槽:

98:106:src/QGCApplication.h 复制代码
public slots:
    /// You can connect to this slot to show an information message box from a different thread.
    void informationMessageBoxOnMainThread(const QString& title, const QString& msg);
    /// You can connect to this slot to show a warning message box from a different thread.
    void warningMessageBoxOnMainThread(const QString& title, const QString& msg);
    /// You can connect to this slot to show a critical message box from a different thread.
    void criticalMessageBoxOnMainThread(const QString& title, const QString& msg);
模式五:状态机 + 定时器(任务上传/下载)

PlanManager 使用 QTimer 实现 ACK 超时重试状态机:

91:93:src/MissionManager/PlanManager.h 复制代码
private slots:
    void _mavlinkMessageReceived(const mavlink_message_t& message);
    void _ackTimeout(void);

流程:发送 MISSION_COUNT → 启动 1500ms 定时器 → 收到 MISSION_REQUEST 则推进状态 / 超时则重试(最多 5 次)→ 完成后 emit sendComplete(error)

模式六:插件工厂

FirmwarePluginManager::firmwarePluginForAutopilot() 根据心跳中的 MAV_AUTOPILOTMAV_TYPE 返回单例插件实例,Vehicle 和 MissionCommandTree 通过插件获取固件特定的命令名称、参数元数据、飞行模式列表等。

3.4 QML 类型注册

QGCApplication 构造函数中大量调用 qmlRegisterType / qmlRegisterUncreatableType,将 C++ 控制器注册到 QML 引擎。例如:

  • QGroundControl.Vehicle --- Vehicle 类型(不可直接创建,由 MultiVehicleManager 管理)
  • QGroundControl.FactSystem --- Fact 相关类型
  • QGroundControl.FlightMap --- 地图组件

QGroundControlQmlGlobal 通过 qmlRegisterSingletonType 注册为 QML 全局单例 QGroundControl,是所有 QML 页面的后端入口。


四、多线程任务调度设计

QGC 的线程模型遵循 "IO 密集任务独立线程,业务逻辑主线程" 原则,利用 Qt 事件循环和信号槽实现线程间协作。

4.1 线程全景

复制代码
┌──────────────────────────────────────────────────────────────┐
│                     主线程 (GUI Thread)                       │
│  QGCApplication 事件循环                                      │
│  ├── QGCToolbox 所有 QGCTool (LinkManager, MAVLinkProtocol...) │
│  ├── Vehicle / ParameterManager / MissionController          │
│  ├── QML 渲染引擎                                            │
│  └── QTimer 驱动的定时任务 (心跳、超时、自动连接)              │
├──────────────────────────────────────────────────────────────┤
│              Link 线程 (每条链路一个 QThread)                  │
│  LinkInterface 子类 (SerialLink / UDPLink / TCPLink)          │
│  ├── run() 或 moveToThread(this) 事件循环                    │
│  ├── 阻塞/非阻塞 IO 读写                                     │
│  └── emit bytesReceived → 跨线程到 MAVLinkProtocol           │
├──────────────────────────────────────────────────────────────┤
│              专用工作线程                                      │
│  ├── GPSProvider (QThread) --- GPS/RTK 串口读取               │
│  ├── PX4FirmwareUpgradeThreadWorker --- 固件刷写              │
│  ├── VideoReceiver (GStreamer pipeline) --- 视频解码           │
│  ├── LogReplayLink --- 日志回放定时读取                         │
│  ├── HIL Links (JSBSim/XPlane) --- 仿真通信                    │
│  └── QThreadPool (QRunnable) --- 视频初始化等一次性任务          │
└──────────────────────────────────────────────────────────────┘

4.2 链路线程模型

LinkInterface 继承 QThread,是所有物理链路的基类:

31:32:src/comm/LinkInterface.h 复制代码
class LinkInterface : public QThread
{
    Q_OBJECT

UDP 链路在构造时将自己移到独立线程:

cpp 复制代码
// UDPLink 构造函数中
moveToThread(this);

线程安全写操作通过信号中转:

152:155:src/comm/LinkInterface.h 复制代码
    void writeBytesSafe(const char *bytes, int length)
    {
        emit _invokeWriteBytes(QByteArray(bytes, length));
    }
68:68:src/comm/LinkInterface.cc 复制代码
    QObject::connect(this, &LinkInterface::_invokeWriteBytes, this, &LinkInterface::_writeBytes);

任何线程调用 writeBytesSafe() 都是安全的:信号会被 Qt 事件系统投递到 Link 所在线程执行 _writeBytes()

线程安全读操作 :Link 线程收到数据后直接 emit bytesReceived(this, data)。由于 MAVLinkProtocol::receiveBytes 连接为默认 AutoConnection,当发送方和接收方在不同线程时,Qt 自动使用 QueuedConnection,将字节数组拷贝到主线程队列中异步处理。

MAVLinkProtocol 作为 QGCTool 运行在主线程。虽然 receiveBytes() 可能从 Link 线程被调用(QueuedConnection),但 mavlink_parse_char()emit messageReceived() 最终在主线程执行。源码注释明确说明了设计意图:

"It buys us reentrancy for the whole code over all threads"

即:所有 MAVLink 消息的处理(Vehicle 状态更新、参数同步、任务协议)都在主线程完成,避免复杂的锁机制。代价是高频链路下主线程负载增加,但 GCS 场景下(通常 <100Hz 有效消息)完全可接受。

4.4 GPS/RTK 独立线程

GPSProvider 继承 QThread,在 run() 中阻塞读取 GPS 串口:

28:29:src/GPS/GPSProvider.h 复制代码
class GPSProvider : public QThread
{
    Q_OBJECT
57:61:src/GPS/GPSProvider.h 复制代码
signals:
    void positionUpdate(GPSPositionMessage message);
    void satelliteInfoUpdate(GPSSatelliteMessage message);
    void RTCMDataUpdate(QByteArray message);
    void surveyInStatus(float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active);

GPSManager 在主线程接收这些信号,更新 RTK FactGroup,并通过 RTCMMavlink 将 RTCM 差分数据转发给飞行器。

停止 GPS 线程使用 std::atomic_bool _requestGpsStop 协作式退出,避免强制 terminate()

4.5 固件升级工作线程

PX4 固件升级涉及串口 Bootloader 通信,耗时且可能阻塞,因此采用 Worker-Object + QThread 模式:

35:35:src/VehicleSetup/PX4FirmwareUpgradeThread.h 复制代码
class PX4FirmwareUpgradeThreadWorker : public QObject
{
    Q_OBJECT

Worker 对象被 moveToThread() 到专用 QThread,通过信号报告进度:

43:54:src/VehicleSetup/PX4FirmwareUpgradeThread.h 复制代码
signals:
    void updateProgress(int curr, int total);
    void foundBoard(bool firstAttempt, const QGCSerialPortInfo& portInfo, int type, QString boardName);
    void noBoardFound(void);
    void boardGone(void);
    void foundBootloader(int bootloaderVersion, int boardID, int flashSize);
    void bootloaderSyncFailed(void);
    void error(const QString& errorString);
    void status(const QString& statusText);
    void eraseStarted(void);
    void eraseComplete(void);
    void flashComplete(void);

UI 层的 FirmwareUpgradeController 在主线程连接这些信号更新 QML 进度条。

4.6 视频流线程

VideoManager 管理 VideoReceiver 实例。GStreamer pipeline 在 GStreamer 内部线程运行,帧回调通过 Qt 信号传递到主线程渲染。视频初始化被投递到线程池避免阻塞启动:

134:147:src/QGCApplication.cc 复制代码
class FinishVideoInitialization : public QRunnable
{
public:
  FinishVideoInitialization(VideoManager* manager)
      : _manager(manager)
  {}
  void run () {
      _manager->_initVideo();
  }
private:
  VideoManager* _manager;
};

4.7 定时器驱动的周期性任务

QGC 大量使用 QTimer 实现轻量级调度,无需额外线程:

定时器 所属类 周期 用途
GCS Heartbeat MultiVehicleManager ~1s 向所有链路广播 GCS 心跳
AutoConnect LinkManager ~1s 扫描串口/USB 自动连接
Link Active Check LinkManager 可变 检测 USB Direct 链路活动
MAVLink Message Timer LinkInterface 3s 检测 Vehicle 通信丢失
PlanManager ACK Timeout PlanManager 250-1500ms 任务协议超时重试
MAV Command ACK Vehicle 可变 命令重发
PreArm Error Vehicle 单次 PreArm 错误自动清除
FactGroup Update FactGroup 可配置 遥测 UI 刷新节流
Log Replay LogReplayLink 按日志时间戳 遥测回放

MultiVehicleManager 的 GCS 心跳定时器示例:

47:52:src/Vehicle/MultiVehicleManager.cc 复制代码
    _gcsHeartbeatTimer.setInterval(_gcsHeartbeatRateMSecs);
    _gcsHeartbeatTimer.setSingleShot(false);
    connect(&_gcsHeartbeatTimer, &QTimer::timeout, this, &MultiVehicleManager::_sendGCSHeartbeat);
    if (_gcsHeartbeatEnabled) {
        _gcsHeartbeatTimer.start();
    }

单元测试用的 MockLink 使用多频率定时器模拟飞控行为:

cpp 复制代码
QTimer timer1HzTasks;    // 1Hz 任务
QTimer timer10HzTasks;   // 10Hz 任务
QTimer timer500HzTasks;  // 500Hz 任务

这展示了 QGC 如何用 Qt 定时器模拟不同频率的 MAVLink 消息流,而无需真实硬件。

4.9 线程安全设计原则总结

  1. IO 隔离:所有串口/网络 IO 在 Link 独立线程,不阻塞 GUI。
  2. 消息归主:MAVLink 解析和业务处理在主线程,简化同步。
  3. 信号中转 :跨线程通信用 emit signal + QueuedConnection,禁止跨线程直接访问 QObject。
  4. 写操作保护writeBytesSafe() + _invokeWriteBytes 模式确保链路写线程安全。
  5. 互斥锁辅助 :Link 的数据速率统计使用 QMutexLocker_dataRateMutex),UDP 目标列表使用 _sessionTargetsMutex
  6. 原子变量 :GPS 线程停止标志使用 std::atomic_bool
  7. 主线程 UI :所有 QML 弹窗、消息框通过 xxxOnMainThread 槽函数触发。

五、典型业务流程串联

5.1 从启动到连接飞行器

复制代码
1. main() → QGCApplication 构造
2. new QGCToolbox → 创建所有 QGCTool
3. setChildToolboxes() → 注入依赖、建立信号连接
4. QQmlApplicationEngine 加载 MainRootWindow.qml
5. LinkManager 自动连接 / 用户手动添加链路
6. LinkInterface 线程建立连接 → emit connected
7. Link 收到 Heartbeat → bytesReceived → MAVLinkProtocol 解析
8. emit vehicleHeartbeatInfo → MultiVehicleManager 创建 Vehicle
9. Vehicle 构造:连接 messageReceived、创建 ParameterManager/UAS/Plugins
10. ParameterManager 请求参数 → parametersReady → UI 可用
11. QML activeVehicle 绑定生效 → Fly View 显示遥测

5.2 任务上传到飞行器

复制代码
1. 用户在 PlanView 编辑航点 → MissionController 维护 VisualMissionItem 列表
2. 点击 Upload → PlanMasterController.sendToVehicle()
3. MissionController 生成 MissionItem 列表
4. PlanManager.writeMissionItems() 启动 MAVLink Mission Protocol 状态机
5. 逐条发送 MISSION_ITEM → 等待 MISSION_REQUEST / MISSION_ACK
6. QTimer 超时重试机制保障可靠性
7. emit sendComplete(false) → UI 显示上传成功

5.3 参数修改

复制代码
1. QML ParameterEditor 修改 Fact.cookedValue
2. Fact emit valueChanged → ParameterManager 检测到用户写入
3. 发送 PARAM_SET MAVLink 消息
4. 等待 PARAM_VALUE 确认
5. 更新本地 Fact 值 + 磁盘缓存

六、扩展与定制指南

6.1 添加新飞控支持

  1. FirmwarePlugin/ 下创建新插件类,继承 FirmwarePlugin + FirmwarePluginFactory
  2. AutoPilotPlugins/ 下创建对应 AutoPilotPlugin
  3. qgroundcontrol.pro 中添加编译配置
  4. 提供参数元数据 XML 和 MAVLink 命令 JSON

6.2 OEM 定制

  1. 创建 custom/custom.pri,定义 CUSTOMCLASSCUSTOMHEADER
  2. 继承 QGCCorePlugin,覆盖 settingsPagesoptionsbrandImageIndoor
  3. 替换 custom/qgroundcontrol.qrc 中的资源

6.3 添加新 QML 页面

  1. src/ 相应目录创建 QML + Controller
  2. QGCApplication.ccqmlRegisterType
  3. 通过 QGCCorePlugin::analyzePagessettingsPages 注册入口

七、总结

QGroundControl 4.0 是一个 Qt 原生、插件化、MAVLink 通用 的地面站架构:

维度 设计要点
分层 UI(QML) → 业务(Vehicle/Plugin) → 通信(Link/MAVLink) → 数据(Fact/Settings)
组织 QGCToolbox 统一管理 20+ 顶层服务,两阶段构造避免循环依赖
扩展 FirmwarePlugin + AutoPilotPlugin 双插件体系隔离飞控差异
数据 Fact/FactGroup 统一建模,Q_PROPERTY 驱动 QML 绑定
通信 Link 线程 IO + 主线程 MAVLink 解析 + 信号广播分发
线程 IO 隔离、消息归主、信号中转、定时器调度

理解以上架构后,可按 "从 QGCToolbox 找到服务 → 从 Vehicle 找到业务 → 从 Fact 找到数据 → 从信号找到流程" 的路径快速定位任意功能的实现代码。

相关推荐
yeflx13 小时前
点云场景树架构-详细设计
架构
一个数据大开发13 小时前
大模型时代的数据中台架构演进:从数据仓库到认知引擎
数据仓库·架构
Rain50913 小时前
架构解密:mini-cc 的核心设计思路
前端·架构·开源·node.js·ai编程
mohaoyuan13 小时前
软考架构师知识点汇总
开发语言·架构
GISer_Jing13 小时前
现代分布式系统架构全链路解析
后端·架构
mydeman13 小时前
智能体工程化演进:架构收敛、协议标准化与安全边界下沉
人工智能·架构·软件工程·ai编程
Maimai1080813 小时前
用 TanStack Table、React Query 和 shadcn/ui 搭一个可维护的数据表格架构
前端·javascript·react.js·ui·架构·前端框架·reactjs
song50113 小时前
多模态模型在昇腾上的部署架构
人工智能·分布式·深度学习·架构·transformer·交互
小O的算法实验室13 小时前
2026年SEVC,层级分解协同演化算法+带有无人机的车辆路径路径规划
算法·无人机