☰
QGroundControl 通信架构类层次解析:从 LinkManager 到 Vehicle 与 FirmwarePlugin
2026/10/14 16:49:31 网站建设 项目流程
  • 无人机
  • 智能硬件

【免费下载链接】qgroundcontrol

Cross-platform ground control station for drones (Android, iOS, Mac OS, Linux, Windows)

项目地址:https://gitcode.com/gh_mirrors/qg/qgroundcontrol
点击查看免费下载

本文以 QGroundControl 官方开发指南中“高层类层次结构”(Class Hierarchy)一篇文档为主体,结合当前仓库的 C++ 源码实现,梳理地面站(GCS)与飞行器之间数据通路上的五个核心角色:LinkManager/LinkInterface(通信链路层)、MAVLinkProtocol(协议解析层)、MultiVehicleManager(多机管理层)、Vehicle(飞行器对象)以及FirmwarePlugin/FirmwarePluginManager(固件插件层)。读完后你能掌握 QGC 内部“字节流 → MAVLink 消息 → 飞行器对象 → UI 接口”的完整数据流向,以及多机场景下车载端如何被发现、创建、切换和移除。

一、整体数据流:一条链路如何变成一架飞行器

按原文档的描述,QGC 中一条数据链路的处理流程可以概括为四个阶段:

  1. Link 层:一条 “Link” 是与飞行器之间某种具体的通信管道(串口、WiFi 上的 UDP 等)。所有 Link 的基类是LinkInterface,每条 Link 运行在自己的线程上,把接收到的字节流发送给MAVLinkProtocol。
  2. MAVLinkProtocol 层:系统中只有一个MAVLinkProtocol对象,负责把链路字节流翻译成 MAVLink 消息。其中 HEARTBEAT 消息被路由给MultiVehicleManager,其余所有 MAVLink 消息被路由给与该链路关联的Vehicle。
  3. MultiVehicleManager 层:系统中同样只有一个MultiVehicleManager对象。当它在某条链路上收到“此前未见过的” HEARTBEAT 时,就创建一个Vehicle对象;它还负责维护系统中所有 Vehicle、处理活动飞行器(active vehicle)的切换,以及飞行器被移除时的清理工作。
  4. Vehicle 层:Vehicle对象是 QGC 代码与物理飞行器通信的主要接口。

下面逐层结合源码展开。

二、LinkManager 与 LinkInterface:通信链路层

2.1 LinkManager:链路注册中心与自动连接管理器

LinkManager 的头文件注释给出了与原文档一致的定位:

Manage communication links. The Link Manager organizes the physical Links. It can manage arbitrary links and takes care of connecting them as well assigning the correct protocol instance to transport the link data into the application.

从源码结构看,LinkManager的职责远不止“记录已打开的链路”,它同时管理:

  • 链路配置的增删改:createConfiguration、startConfigurationEditing、endConfigurationEditing、removeConfiguration等Q_INVOKABLE方法供 QML 侧的链路配置界面(如 SerialSettings.qml、UdpSettings.qml)调用;
  • 自动连接:私有成员AutoConnectSettings *_autoConnectSettings、_addUDPAutoConnectLink()、_addMAVLinkForwardingLink()、_reconnectAutoConnectLinks()以及 1 秒周期的_autoconnectUpdateTimerMSecs定时器(见 LinkManager.h),这正是原文档所说“LinkManageralso manages automatic connections through serial and UDP links”的实现;串口端则由SerialAutoConnect(SerialAutoConnect.h)配合QTimer *_portListTimer周期性刷新串口列表;
  • MAVLink 通道分配:allocateMavlinkChannel()/freeMavlinkChannel()基于位掩码_mavlinkChannelsUsedBitMask管理每条链路占用的 MAVLink 通道;
  • 连接挂起:setConnectionsSuspended(reason)允许应用级地暂停所有新连接(例如固件升级场景);
  • 日志回放:startLogReplay()基于LogReplayLink把历史日志作为虚拟链路喂入系统。

LinkManager以单例形式存在(static LinkManager *instance()),并以QML_ELEMENT暴露给 QML,但标记为QML_UNCREATABLE——即 QML 只能读取、不能自行创建,体现了“链路只能由 LinkManager 产生”的设计约束。

2.2 LinkInterface:所有链路的抽象基类

LinkInterface 是所有链路类型的基类,其头文件明确写道:“The link interface defines the interface for all links used to communicate with the ground station application.”。仓库中它的直接子类覆盖了 QGC 支持的全部物理与虚拟链路形态:

子类文件链路类型
SerialLinkSerialLink.h串口(含 USB 直连飞控)
UDPLinkUDPLink.hUDP(WiFi/以太网)
TCPLinkTCPLink.hTCP
BluetoothLinkBluetoothLink.h蓝牙经典/BLE
MockLinkMockLink.h模拟链路(PX4 仿真接入)
LogReplayLinkLogReplayLink.h日志回放

从源码结构看,原文档中“每条 Link 运行在自己的线程上”这一说法在实现上成立:例如 UDPLink.h 持有QThread *_workerThread,数据接收在独立线程完成后,通过bytesReceived(LinkInterface*, const QByteArray&)信号把字节流投递给MAVLinkProtocol(信号定义见 LinkInterface.h)。

LinkInterface还提供了不少与协议细节强相关的设施,值得注意:

  • 单点发送咽喉:sendMessageThreadSafe(mavlink_message_t&)的注释写明它是“Single message-level send chokepoint: re-signs (if signing is active), serializes, then writes”,即所有出站 MAVLink 消息必须经过这里,以保证 MAVLink 2.4 签名(SigningController)无法被绕过;
  • MAVLink v1/v2 兼容告警:reportMavlinkV1Traffic()配合默认 10 秒的宽限期(kMavlinkV1TrafficGraceMsecsDefault = 10000)处理 ArduPilot “先 v1 启动、收到 QGC 首个 v2 消息后升级 v2”的场景;
  • 链路属性判定:isSecureConnection()(USB/有线以太网等可信链路)、isRadioStatusSeen/isRadioLink()(从 RADIO_STATUS 流量识别 SiK/RFD 电台链路,用于约 252 字节空口帧长的提示);
  • 构造函数为 protected,注释说明“Links are only created by LinkManager”,与QML_UNCREATABLE形成双重约束。

三、MAVLinkProtocol:唯一的协议解析单例

原文档说:“There is a singleMAVLinkProtocolobject in the system. It's job is to take incoming bytes from a link and translate them into MAVLink messages.” 在 src/Comms/MAVLinkProtocol.h 中,MAVLinkProtocol提供static MAVLinkProtocol* instance()单例访问器,receiveBytes(LinkInterface*, const QByteArray&)是公开的 public slot,直接对接每条 Link 的bytesReceived信号。

结合 MAVLinkProtocol.cc 的实现,可以确认原文档所述的两条消息路由规则:

规则一:HEARTBEAT(及高低延迟心跳)路由给 MultiVehicleManager。在receiveBytes处理消息的switch分支中(MAVLinkProtocol.cc):

case MAVLINK_MSG_ID_HEARTBEAT: { _startLogging(); mavlink_heartbeat_t heartbeat{}; mavlink_msg_heartbeat_decode(&message, &heartbeat); emit vehicleHeartbeatInfo(link, message.sysid, message.compid, heartbeat.autopilot, heartbeat.type); break; } case MAVLINK_MSG_ID_HIGH_LATENCY: { // HIGH_LATENCY does not provide autopilot or type information, generic is our safest bet emit vehicleHeartbeatInfo(link, message.sysid, message.compid, MAV_AUTOPILOT_GENERIC, MAV_TYPE_GENERIC); break; } case MAVLINK_MSG_ID_HIGH_LATENCY2: { mavlink_high_latency2_t highLatency2{}; mavlink_msg_high_latency2_decode(&message, &highLatency2); emit vehicleHeartbeatInfo(link, message.sysid, message.compid, highLatency2.autopilot, highLatency2.type); break; }

注意一个超出原文档细节的实现事实:不仅标准HEARTBEAT,HIGH_LATENCY/HIGH_LATENCY2(高延迟链路卫星遥测协议)同样会触发vehicleHeartbeatInfo信号,保证星链类飞行器也能被识别为 Vehicle。

规则二:所有 MAVLink 消息路由给链路关联的 Vehicle。_updateStatus()中统一发出messageReceived(link, message)信号(MAVLinkProtocol.cc);每个Vehicle在构造时连接该信号——Vehicle.cc:

connect(MAVLinkProtocol::instance(), &MAVLinkProtocol::messageReceived, this, &Vehicle::_mavlinkMessageReceived);

也就是说,“消息路由给链路上所有关联的 Vehicle”是通过广播 + Vehicle 侧自行过滤(sysid/compid 匹配)实现的。

此外,MAVLinkProtocol还承担原文档未提及但紧密相关的职责,均可见于 MAVLinkProtocol.h:

  • GCS 身份标识:getSystemId()与static int getComponentId() { return MAV_COMP_ID_MISSIONPLANNER; },GCS 在地面站侧固定使用 MISSIONPLANNER 组件 ID;
  • 遥测丢包统计:按(channel, sysid, compid)维度维护_lastIndex序列号数组与收/丢包计数器,周期性发出mavlinkMessageStatus信号——这就是链路状态页面上丢包率数据的来源;
  • 遥测日志记录:_startLogging()/_stopLogging()围绕飞行器解锁状态(通过 HEARTBEAT 的MAV_MODE_FLAG_DECODE_POSITION_SAFETY位判断_vehicleWasArmed)自动开启/关闭.mavlink日志文件;
  • 消息转发:_forward()/_forwardSupport()支撑LinkManager的 MAVLink 转发链路(createMavlinkForwardingSupportLink())。

四、MultiVehicleManager:车辆发现、创建与生命周期

原文档对MultiVehicleManager的描述是:“When it receives a HEARTBEAT on a link which has not been previously seen it creates a Vehicle object. MultiVehicleManager also keeps tracks of all Vehicles in the system and handles switching from one active vehicle to another and correctly handling a vehicle being removed.”

MultiVehicleManager 同样是单例(static MultiVehicleManager *instance()),在init()中连接了MAVLinkProtocol::vehicleHeartbeatInfo信号(MultiVehicleManager.cc)。车辆创建逻辑完整体现在_vehicleHeartbeatInfo()(MultiVehicleManager.cc)中,其中包含若干原文档没有展开的判定细节:

  1. 只认飞控组件:componentId != MAV_COMP_ID_AUTOPILOT1的心跳直接忽略——摄像头、GCS、机载控制器等组件不会生成 Vehicle;
  2. 类型兜底:新烧录的 ArduCopter 若未上报 FRAME_CLASS(MAV_TYPE_GENERIC+MAV_AUTOPILOT_ARDUPILOTMEGA),默认按四旋翼(MAV_TYPE_QUADROTOR)处理;
  3. 非飞行器类型排除:MAV_TYPE_GCS、MAV_TYPE_ONBOARD_CONTROLLER、MAV_TYPE_GIMBAL、MAV_TYPE_ADSB不创建 Vehicle;
  4. 多机开关:_vehicles->count() > 0且未启用多机模式(QGCCorePlugin::instance()->options()->multiVehicleEnabled(),对应 QGCOptions.h 的multiVehicleEnabled属性)时直接返回——即单 VEHICLE 模式下后接入的飞行器会被丢弃;
  5. 去重与校验:vehicleId已在忽略列表、已存在,或vehicleId == 0时不创建;若飞行器 ID 与 QGC 自身getSystemId()冲突则弹出警告;
  6. 创建后动作:向QmlObjectListModel *_vehicles追加、立即_sendGCSHeartbeat()(注释说明这是为了让 PX4 尽快接受命令)、发出vehicleAdded信号;第一个 Vehicle 自动setActiveVehicle(),多机时仅提示“Connected to Vehicle %1”;
  7. 移除处理:Vehicle 的VehicleLinkManager::allLinksRemoved信号连接到两阶段删除槽_deleteVehiclePhase1/_deleteVehiclePhase2(MultiVehicleManager.h),对应原文档说的“correctly handling a vehicle being removed”。

另外两点与原文档互补的实现细节:

  • GCS 心跳速率常量为kGCSHeartbeatRateMSecs = 1000(1 秒一次),由_gcsHeartbeatTimer周期触发_sendGCSHeartbeat()(MultiVehicleManager.h);
  • 除了实时列表,还持有一个offlineEditingVehicle(以MAV_AUTOPILOT_TRACK/MAV_TYPE_TRACK哨兵值构造的离线 Vehicle,见 MultiVehicleManager.cc 与 Vehicle.h),用于无连接状态下的离线任务/参数编辑。

MultiVehicleManager还以 QML 属性暴露activeVehicle、vehicles、selectedVehicles等(MultiVehicleManager.h),QML 侧的活动飞行器切换(selectVehicle/setActiveVehicle)即走这条通道。

五、Vehicle:QGC 与物理飞行器之间的主要接口

原文档对 Vehicle 的表述是:“The Vehicle object is the main interface through which the QGC code communicates with the physical vehicle.”

Vehicle 类同时继承VehicleFactGroup与VehicleTypes,构造函数签名为:

Vehicle(LinkInterface* link, int vehicleId, int defaultComponentId, MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType, QObject* parent = nullptr);

与MultiVehicleManager._vehicleHeartbeatInfo()中的创建调用完全对应(MultiVehicleManager.cc)。从源码结构看,一个 Vehicle 聚合了大量子系统对象,例如:

  • VehicleLinkManager:管理该 Vehicle 关联的所有链路(一条链路可能对应多架同 ID 不同链路的飞行器,支持链路聚合/切换);
  • ParameterManager:参数下载与就绪状态(其parametersReadyChanged信号被MultiVehicleManager用于驱动parameterReadyVehicleAvailable属性);
  • FactGroup家族(VehicleGPSFactGroup、VehicleBatteryFactGroup等,见 Vehicle.h 的Q_MOC_INCLUDE列表):把遥测消息整理为 QML 可直接绑定的 Fact;
  • 以及MavCommandQueue、GimbalController、FTPManager、Autotune、RemoteIDManager等(见同目录 src/Vehicle 下的Vehicle.cc/Vehicle.h)。

关于原文档中 UAS 的说明需要更新:原文档指出“每个 Vehicle 还关联一个 UAS 对象,属于正在逐步淘汰的弃用类,所有功能正迁移到 Vehicle 类,不应再向其中添加新代码”。在当前仓库中搜索不到任何class UAS定义(src/下无匹配),说明 UAS 类已被完全移除,该迁移在现有代码中已经完成——新代码一律面向Vehicle类编程。

六、FirmwarePlugin 与 FirmwarePluginManager:固件差异的隔离层

原文档最后一段:“The FirmwarePlugin class is the base class for firmware plugins. A firmware plugin contains the firmware specific code, such that the Vehicle object is clean with respect to it supporting a single standard interface to the UI. FirmwarePluginManager is a factory class which creates a FirmwarePlugin instance based on the MAV_AUTOPILOT/MAV_TYPE combination for the Vehicle.”

6.1 FirmwarePlugin:飞行栈差异的“唯一合法居所”

FirmwarePlugin.h 中的类注释把原文档的设计意图说得更绝对:

This is theonly placewhere flight stack specific code should reside in QGroundControl. The remainder of the QGroundControl source is generic to a common mavlink implementation. The implementation in the base class supports mavlink generic firmware. Override the base class virtuals to create your own firmware specific plugin.

基类同时定义了能力位图FirmwareCapabilities(FirmwarePlugin.h),例如:

enum FirmwareCapabilities { SetFlightModeCapability = 1 << 0, ///< FirmwarePlugin::setFlightMode method is supported PauseVehicleCapability = 1 << 1, ///< Vehicle supports pausing at current location GuidedModeCapability = 1 << 2, ///< Vehicle supports guided mode commands OrbitModeCapability = 1 << 3, ///< Vehicle supports orbit mode TakeoffVehicleCapability = 1 << 4, ///< Vehicle supports taking off ROIModeCapability = 1 << 5, ///< Vehicle supports ROI (both in Fly guided mode and from Plan creation) ChangeHeadingCapability = 1 << 6, ///< Vehicle supports changing heading at current location GuidedTakeoffCapability = 1 << 7, ///< Vehicle supports guided takeoff VTOLMulticopterTakeoffCapability = 1 << 8, ///< VTOL supports MAV_CMD_NAV_TAKEOFF while remaining in multirotor mode };

UI(FlyView 的引导操作、PlanView 的任务项等)通过查询这些能力位来呈现/隐藏功能,而不是硬编码判断固件类型。基类还负责飞行模式列表(FirmwareFlightMode结构:mode_name、standard_mode、custom_mode、canBeSet、advanced、fixedWing/multiRotor标志)以及参数名跨版本重映射表(remapParamNameMap_t,用于处理如 4.0 版本TUNE_LOW→TUNE_MIN这类固件改参名的情况)。

6.2 FirmwarePluginManager:按 MAV_AUTOPILOT/MAV_TYPE 选择插件

FirmwarePluginManager 头文件注释:“a singleton which is used to return the correct FirmwarePlugin for a MAV_AUTOPILOT type”。核心工厂方法:

/// Returns appropriate plugin for autopilot type. /// @param firmwareType Type of firmware to return plugin for. /// @param vehicleType Vehicle type to return plugin for. /// @return Singleton FirmwarePlugin instance for the specified MAV_AUTOPILOT. FirmwarePlugin *firmwarePluginForAutopilot(MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType);

参数正是原文档所说的 “MAV_AUTOPILOT/MAV_TYPE combination”——两者都来自 HEARTBEAT,与MultiVehicleManager创建Vehicle时保存的字段一一对应。管理器内部通过FirmwarePluginFactory注册表(_findPluginFactory)按固件类查找工厂,找不到时回退到_genericFirmwarePlugin(MAVLink 通用固件插件,位于 src/FirmwarePlugin)。仓库内置的具体插件实现包括:

  • PX4FirmwarePlugin/PX4FirmwarePluginFactory(src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h)
  • APMFirmwarePlugin/APMFirmwarePluginFactory(src/FirmwarePlugin/APM/APMFirmwarePlugin.h)
  • GenericFirmwarePlugin(src/FirmwarePlugin/Generic)

此外管理器还提供supportedFirmwareClasses()、firmwareClassSupported()、singleFirmwareSupport()、singleVehicleSupport()等查询接口,QGC 可通过编译期配置裁剪为“仅支持单一固件类/机型”的精简构建(custom-example/下的定制插件即展示了如何按此机制扩展固件支持)。

七、端到端串讲:一条链路到一架飞行器

把前六节串起来,当前仓库中的完整调用链为:

  1. LinkManager按LinkConfiguration创建LinkInterface子类实例(如UDPLink),分配 MAVLink 通道并启动;链路工作线程收到字节后发出bytesReceived;
  2. 唯一的MAVLinkProtocol实例在receiveBytes中组包解析出mavlink_message_t;HEARTBEAT/HIGH_LATENCY 类消息额外发出vehicleHeartbeatInfo;
  3. 唯一的MultiVehicleManager实例根据心跳中的vehicleId(sysid)判定是否为新飞行器:是则new Vehicle(...)并加入_vehicles模型、设置活动飞行器;
  4. 新建的Vehicle连接MAVLinkProtocol::messageReceived,开始消费链路上发给自己的全部遥测消息,并通过FirmwarePluginManager::firmwarePluginForAutopilot(MAV_AUTOPILOT, MAV_TYPE)获得对应固件插件;
  5. QML 界面通过multiVehicleManager(QML 单例)的activeVehicle属性拿到当前活动 Vehicle,再经 Vehicle 的 FactGroup/属性树读取遥测、经FirmwarePlugin的能力位与飞行模式列表驱动 Fly/Plan/Setup 等视图。

八、阅读源码的起点

  • 链路层:src/Comms/LinkManager.h、src/Comms/LinkInterface.h、src/Comms/Serial/SerialLink.h、src/Comms/UDPLink.h、src/Comms/MockLink/MockLink.h
  • 协议层:src/Comms/MAVLinkProtocol.h、src/Comms/MAVLinkProtocol.cc
  • 车辆管理层:src/Vehicle/MultiVehicleManager.h、src/Vehicle/MultiVehicleManager.cc
  • 飞行器对象:src/Vehicle/Vehicle.h、src/Vehicle/Vehicle.cc
  • 固件插件:src/FirmwarePlugin/FirmwarePlugin.h、src/FirmwarePlugin/FirmwarePluginManager.h、src/FirmwarePlugin/PX4、src/FirmwarePlugin/APM
  • 测试佐证:链路/车辆/协议相关单测集中在 test/Comms、test/Vehicle、test/MAVLink 目录,可用于验证上述调用行为

需要说明的适用前提:本文所有结论基于当前仓库代码快照;原文档中提到的 UAS 类在当前代码中已不存在,相关“弃用迁移”说明仅对旧版本仓库有意义。原文档提示可查阅的 Doxygen 在线类文档(api.qgroundcontrol.com)为站外资源,此处不再给出链接。

  • 无人机
  • 智能硬件

【免费下载链接】qgroundcontrol

Cross-platform ground control station for drones (Android, iOS, Mac OS, Linux, Windows)

项目地址:https://gitcode.com/gh_mirrors/qg/qgroundcontrol
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询