QGC 固件升级与硬件适配
7.0 总体架构
QGC 4.0 将「固件烧录」与「运行时硬件/协议适配」分为两条相对独立的链路:
┌─────────────────────────────────────────────────────────────────┐
│ 离线烧录链路(USB 串口) │
│ FirmwareUpgrade.qml → FirmwareUpgradeController │
│ → PX4FirmwareUpgradeThread → Bootloader → QSerialPort │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 在线运行链路(MAVLink 连接后) │
│ MultiVehicleManager → Vehicle │
│ → FirmwarePluginManager → FirmwarePlugin(PX4/APM/Generic)│
│ → AutoPilotPlugin → VehicleComponent[](传感器/机架/电台...) │
│ → ParameterManager + *ParameterMetaData │
└─────────────────────────────────────────────────────────────────┘
设计原则:
FirmwarePlugin:飞行栈(PX4 / ArduPilot)相关的全部差异逻辑应集中在此AutoPilotPlugin+VehicleComponent:Setup 向导、传感器校准、机架选择等 UI/参数逻辑Vehicle与MissionManager等保持 MAVLink 通用,差异通过 Plugin 钩子注入
涉及的主要设计模式:
| 模式 | 体现 |
|---|---|
| Plugin 模式 | FirmwarePlugin / AutoPilotPlugin 抽象飞行栈差异 |
| Factory + 注册表 | FirmwarePluginFactory 构造时自注册到 FirmwarePluginFactoryRegister |
| Singleton(懒加载) | 各 Factory 内 _pluginInstance 单例复用 |
| MVC | FirmwareUpgradeController(C++)+ FirmwareUpgrade.qml(View) |
| Worker Thread | PX4FirmwareUpgradeThreadController 在独立线程执行串口 I/O |
| Strategy | 按 URI/板型/协议选择不同固件 URL、Bootloader 命令集 |
| Template Method | VehicleComponent 定义 setup 流程骨架,子类实现具体 QML/参数 |
| Adapter | adjustIncomingMavlinkMessage 修正 APM 与标准 MAVLink 差异 |
| Hash Map 配置 | _rgFMUV5Firmware 等 boardId → URL 映射表 |
涉及语法与技术:
- C++:
QObject、Q_PROPERTY、Q_INVOKABLE、Q_ENUM、signals/slots - Qt Serial:
QSerialPort、QSerialPortInfo - JSON/XML:
QJsonDocument(.px4/.apj/manifest)、QXmlStreamReader(参数元数据) - Intel Hex:
.ihx解析(SiK Radio) - MAVLink:
mavlink_message_t、MAV_AUTOPILOT、MAV_TYPE - QML:
SetupPage、FirmwareUpgradeController注册为QGroundControl.Controllers
7.1 飞控固件烧录流程开发
7.1.1 模块与文件索引
| 文件 | 职责 |
|---|---|
VehicleSetup/FirmwareUpgrade.qml |
固件升级 UI(Setup → Firmware) |
VehicleSetup/FirmwareUpgradeController.h/.cc |
MVC 控制器:选固件、下载、调度烧录 |
VehicleSetup/PX4FirmwareUpgradeThread.h/.cc |
独立线程:找板、同步 Bootloader、擦除/编程/校验 |
VehicleSetup/Bootloader.h/.cc |
PX4 Bootloader 二进制协议实现 |
VehicleSetup/FirmwareImage.h/.cc |
固件镜像解析(.bin/.px4/.apj/.ihx) |
comm/QGCSerialPortInfo.h/.cc |
USB VID/PID 识别板型 |
comm/USBBoardInfo.json |
板型 VID/PID 配置表 |
Settings/FirmwareUpgradeSettings.* |
APM ChibiOS、机型偏好等 |
7.1.2 用户操作流程(状态机)
Plug USB → startBoardSearch()
→ 扫描串口 (canFlash)
→ 发现板 (foundBoard) → UI 显示板名
→ 用户选 Stack(PX4/APM) + 版本(Stable/Beta/Dev) + 机型
→ flash() → 等待 Bootloader (foundBootloader)
→ 解析 board_id / flash_size
→ 查 Hash 得固件 URL → QGCFileDownload 下载
→ FirmwareImage::load() 校验 board_id、解压元数据
→ ThreadController::flash() → erase → program → verify → reboot
→ flashComplete → 恢复 LinkManager 连接
入口 startBoardSearch() 关键行为:
114:128:src/VehicleSetup/FirmwareUpgradeController.cc
void FirmwareUpgradeController::startBoardSearch(void)
{
LinkManager* linkMgr = qgcApp()->toolbox()->linkManager();
linkMgr->setConnectionsSuspended(tr("Connect not allowed during Firmware Upgrade."));
...
_threadController->startFindBoardLoop();
}
烧录期间禁止新建 MAVLink 连接,避免与 Bootloader 串口冲突。
7.1.3 USB 板型识别
QGCSerialPortInfo::getBoardInfo() 读取 :/json/USBBoardInfo.json:
8:17:src/comm/USBBoardInfo.json
"boardInfo": [
{ "vendorID": 9900, "productID": 16, "boardClass": "Pixhawk", "name": "PX4 FMU V1" },
{ "vendorID": 9900, "productID": 17, "boardClass": "Pixhawk", "name": "PX4 FMU V2" },
...
{ "vendorID": 9900, "productID": 50, "boardClass": "Pixhawk", "name": "PX4 FMU V5" },
匹配逻辑:先 VID+PID ,失败则 description/manufacturer 正则 fallback。
canFlash() 仅对三类板返回 true:
350:366:src/comm/QGCSerialPortInfo.cc
bool QGCSerialPortInfo::canFlash(void) const
{
...
switch(boardType){
case QGCSerialPortInfo::BoardTypePixhawk:
case QGCSerialPortInfo::BoardTypePX4Flow:
case QGCSerialPortInfo::BoardTypeSiKRadio:
return true;
新增可烧录硬件的第一步 :在 USBBoardInfo.json 增加 VID/PID 条目,并在 Bootloader.h 增加 boardID* 常量(若为新 PX4 板型)。
7.1.4 Bootloader 通信协议
Bootloader 类实现 PX4 官方 Bootloader 协议(也用于 3DR Radio 变体):
协议字节(节选):
| 符号 | 值 | 含义 |
|---|---|---|
PROTO_INSYNC |
0x12 | 同步前缀 |
PROTO_EOC |
0x20 | 命令结束 |
PROTO_OK / PROTO_FAILED |
0x10 / 0x11 | 响应 |
PROTO_GET_SYNC |
0x21 | 建立同步 |
PROTO_CHIP_ERASE |
0x23 | 全片擦除 |
PROTO_PROG_MULTI |
0x27 | 多块写入(最大 64 字节,4 字节对齐) |
PROTO_GET_CRC |
0x29 | CRC 校验(Bootloader ≥3) |
PROTO_BOOT |
0x30 | 重启进应用 |
烧录核心循环(.bin 格式):
193:232:src/VehicleSetup/Bootloader.cc
while (bytesSent < imageSize) {
...
if (_write(port, PROTO_PROG_MULTI)) {
if (_write(port, (uint8_t)bytesToSend)) {
if (_write(port, imageBuf, bytesToSend)) {
if (_write(port, PROTO_EOC)) {
if (_getCommandResponse(port)) {
failed = false;
}
}
}
}
}
...
_imageCRC = QGC::crc32((uint8_t *)imageBuf, bytesToSend, _imageCRC);
emit updateProgress(bytesSent, imageSize);
}
校验策略:
- Bootloader ≤2 或
.ihx:逐字节PROTO_READ_MULTI回读比对 - Bootloader ≥3 且
.bin:PROTO_GET_CRC与预计算 CRC 比较 - 完成后
reboot()发送PROTO_BOOT
Board ID 常量(与固件包内 board_id 对应):
64:85:src/VehicleSetup/Bootloader.h
static const int boardIDPX4FMUV2 = 9;
static const int boardIDPX4FMUV4 = 11;
static const int boardIDPX4FMUV5 = 50;
static const int boardIDPX4FMUV3 = 255; // V2 大 Flash 模拟 ID
static const int boardIDDurandalV1 = 139;
...
7.1.5 独立线程 Worker
PX4FirmwareUpgradeThreadController 将耗时串口操作移出 UI 线程:
Controller (主线程) Worker (子线程)
startFindBoardLoop ──signal──► _findBoardOnce()
flash(image) ──signal──► _flash(): erase→program→verify
cancel ──signal──► 关闭端口
◄── foundBootloader / updateProgress / flashComplete
找 Bootloader 流程:
- 枚举
QGCSerialPortInfo::availablePorts(),过滤canFlash() - 首次发现 emit
foundBoard(UI 等待用户确认/选固件) - 第二次循环(非 firstAttempt)打开串口 →
Bootloader::sync()→getPX4BoardInfo()读INFO_BL_REV / INFO_BOARD_ID / INFO_FLASH_SIZE - SiK Radio 特殊路径:先发
+++/AT&UPDATE强制进 Bootloader
完整烧录 _flash():
277:309:src/VehicleSetup/PX4FirmwareUpgradeThread.cc
if (_erase()) {
if (_bootloader->program(_bootloaderPort, _controller->image())) {
...
}
if (_bootloader->verify(_bootloaderPort, _controller->image())) {
...
}
}
emit _reboot();
emit flashComplete();
7.1.6 固件镜像格式(FirmwareImage)
| 扩展名 | 格式 | 用途 |
|---|---|---|
.bin |
原始二进制 | 直接编程 |
.px4 |
JSON 包装(PX4 传统) | 含 base64 镜像 + 压缩 parameter/airframe XML |
.apj |
JSON 包装(ArduPilot ChibiOS) | 同 .px4 结构,MAV_AUTOPILOT=APM |
.ihx |
Intel Hex | 3DR SiK Radio |
.px4 / .apj 加载流程:
218:264:src/VehicleSetup/FirmwareImage.cc
QJsonDocument doc = QJsonDocument::fromJson(bytes);
...
uint32_t firmwareBoardId = (uint32_t)px4Json.value(_jsonBoardIdKey).toInt();
if (!isCompatible(_boardId, firmwareBoardId)) {
emit statusMessage(...);
return false;
}
兼容规则示例:FMUv3(board_id=255)可烧 FMUv2(id=9)固件;AUAV X2.1 同理。
解压后的 parameter XML 写入缓存并注册到 ParameterManager::cacheMetaDataFile(),使烧录后 Setup 界面立即可用最新参数元数据,无需等飞控在线。
7.1.7 固件 URL 来源
(1)PX4 静态 Hash 表
_initFirmwareHash() 为每块板维护 QHash<FirmwareIdentifier, QString>:
- Key:
AutoPilotStackType+FirmwareBuildType+FirmwareVehicleType - Value:S3 URL,如
http://px4-travis.s3.amazonaws.com/Firmware/stable/px4fmu-v5_default.px4
FMUv2/V3/V4/V5、Durandal、KakuteF7 等各有独立 Hash。
(2)PX4 GitHub Releases 动态版本
_determinePX4StableVersion() 拉取 https://api.github.com/repos/PX4/Firmware/releases 解析 stable/beta 标签。
(3)ArduPilot manifest.json
856:868:src/VehicleSetup/FirmwareUpgradeController.cc
void FirmwareUpgradeController::_downloadArduPilotManifest(void)
{
...
downloader->download(QStringLiteral("http://firmware.ardupilot.org/manifest.json"));
}
解析字段:board_id、mav-type、url、mav-firmware-version-type、USBID、bootloader_str、brand_name 等,动态构建 _rgManifestFirmwareInfo,供 ChibiOS 板按名称列表选择固件。
(4)SingleFirmwareMode
Custom 构建可通过 QGCOptions::firmwareUpgradeSingleURL() 指定单一 URL,跳过用户选择。
7.1.8 Controller 与 QML 绑定
FirmwareUpgradeController 暴露给 QML 的属性示例:
boardPort/boardDescription/pixhawkBoardselectedFirmwareBuildType(Stable/Beta/Dev/Custom)apmFirmwareNames/apmFirmwareUrls(APM manifest 动态列表)progressBar/statusLog(QQuickItem 指针,C++ 直接setProperty("value", ...))
QML 调用:
qml
FirmwareUpgradeController {
onFlashComplete: ...
}
// Q_INVOKABLE:
controller.startBoardSearch()
controller.flash(AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware)
7.1.9 扩展烧录流程的开发指南
若需支持新 Bootloader 协议 或新文件格式:
- 扩展
FirmwareImage::load()增加解析分支 - 在
Bootloader中实现新命令集,或新建XxxBootloader类 - 修改
PX4FirmwareUpgradeThreadWorker::_findBootloader()的打开/同步逻辑 - 在
FirmwareUpgradeController::_initFirmwareHash()或 manifest 解析处增加 URL - 更新
USBBoardInfo.json与Bootloader::boardID*常量
若仅需支持新 PX4 兼容板(沿用 PX4 Bootloader):通常只需 JSON + Hash URL,无需改协议代码。
7.2 新增机型、外设传感器适配
7.2.1 运行时对象模型
Vehicle 创建
└─ _firmwarePlugin = FirmwarePluginManager::firmwarePluginForAutopilot(type, mavType)
└─ _firmwarePlugin->initializeVehicle(this)
└─ ParameterManager 就绪后
└─ AutoPilotPlugin::vehicleComponents()
└─ AirframeComponent / SensorsComponent / RadioComponent / ...
└─ 各 Component 绑定 setup QML + Fact 参数名
Vehicle 中获取 Plugin:
431:433:src/Vehicle/Vehicle.cc
void Vehicle::_commonInit()
{
_firmwarePlugin = _firmwarePluginManager->firmwarePluginForAutopilot(_firmwareType, _vehicleType);
7.2.2 FirmwarePluginFactory 注册机制
全局静态 Factory 对象在构造时自注册:
32:35:src/FirmwarePlugin/FirmwarePlugin.cc
FirmwarePluginFactory::FirmwarePluginFactory(void)
{
FirmwarePluginFactoryRegister::instance()->registerPluginFactory(this);
}
PX4 Factory:
25:28:src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc
QList<MAV_AUTOPILOT> PX4FirmwarePluginFactory::supportedFirmwareTypes(void) const
{
list.append(MAV_AUTOPILOT_PX4);
APM Factory(按 MAV_TYPE 分派):
37:71:src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc
switch (vehicleType) {
case MAV_TYPE_QUADROTOR:
case MAV_TYPE_HELICOPTER:
return _arduCopterPluginInstance;
case MAV_TYPE_FIXED_WING:
case MAV_TYPE_VTOL_QUADROTOR:
return _arduPlanePluginInstance;
case MAV_TYPE_GROUND_ROVER:
return _arduRoverPluginInstance;
case MAV_TYPE_SUBMARINE:
return _arduSubPluginInstance;
新增「机型」在 QGC 语义下通常指:
- 新的 MAV_TYPE 组合 → 新建或扩展
ArduXxxFirmwarePlugin - 新的 机架/airframe → PX4
AirframeComponent+PX4AirframeLoaderXML - 新的 Setup 步骤/外设 → 新建
VehicleComponent子类
7.2.3 AutoPilotPlugin 与 VehicleComponent
VehicleComponent 抽象接口:
46:64:src/VehicleSetup/VehicleComponent.h
virtual QString name(void) const = 0;
virtual bool requiresSetup(void) const = 0;
virtual bool setupComplete(void) const = 0;
virtual QUrl setupSource(void) const = 0; // 完整 Setup 面板 QML
virtual QUrl summaryQmlSource(void) const = 0; // 摘要卡片 QML
virtual QStringList setupCompleteChangedTriggerList(void) const = 0;
PX4 Setup 组件列表示例:
| Component | 职责 | 关键参数 Fact |
|---|---|---|
AirframeComponent |
机架/机型选择 | SYS_AUTOSTART, SYS_AUTOCONFIG |
SensorsComponent |
IMU/罗盘/气压计/空速计校准 | CAL_*, SENS_* |
PX4RadioComponent |
遥控校准 | RC_* |
FlightModesComponent |
飞行模式映射 | RC_MAP_* |
PowerComponent |
电池/电源 | BAT_* |
MotorComponent |
电机测试 | PWM_* |
SafetyComponent |
返航/地理围栏 | RTL_*, GF_* |
PX4TuningComponent |
PID 调参 | 各控制器增益 |
APM 对应: APMSensorsComponent、APMAirframeComponent、APMMotorComponent、APMSubFrameComponent(ROV 框架)、APMLightsComponent 等。
7.2.4 新增外设/传感器适配步骤(PX4 为例)
步骤 1:确认参数名与校准命令
PX4 传感器校准通过 MAVLink MAV_CMD_PREFLIGHT_CALIBRATION 或参数读写完成,QGC SensorsComponent 的 QML 触发 C++ 发送命令。新传感器需确认固件暴露的参数名(如 SENS_EN_XXX)和校准流程。
步骤 2:扩展 SensorsComponent
- 修改
setupCompleteChangedTriggerList()增加新参数 - 在
setupComplete()中检查校准状态参数(如CAL_ACC0_ID非零表示已校准) - 在
SensorsSetup.qml(或对应 QML)增加 UI 入口
步骤 3:参数元数据(Parameter MetaData)
PX4 使用 XML 描述参数短名、单位、枚举、增量:
29:38:src/FirmwarePlugin/PX4/PX4ParameterMetaData.h
void loadParameterFactMetaDataFile (const QString& metaDataFile);
FactMetaData* getMetaDataForFact (const QString& name, MAV_TYPE vehicleType);
void addMetaDataToFact (Fact* fact, MAV_TYPE vehicleType);
元数据来源:
- 飞控在线:
ParameterManager向飞控请求 - 离线/烧录后:
.px4包内parameter_xml解压缓存 - 内置:编译资源
:/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml
新增参数 UI 显示 :在固件 Parameter XML 中加入定义即可被 Fact 系统自动识别;若需特殊控件,扩展 FactMetaData 的 enumStrings 或自定义 QML FactControl。
步骤 4:机架/Airframe 元数据
PX4AutoPilotPlugin 构造时:
49:52:src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc
_airframeFacts = new PX4AirframeLoader(this, _vehicle->uas(), this);
PX4AirframeLoader::loadAirframeMetaData();
Airframe XML 定义 SYS_AUTOSTART ID 与机型名称映射。新增 PX4 机架 需在 PX4 固件侧 airframes/ 添加,QGC 通过烧录包或在线同步 XML 自动更新列表。
步骤 5:FactGroup 扩展(遥测显示)
新传感器若通过 MAVLink 消息上报(非参数),在 Vehicle 或专用 XxxFactGroup 中解析消息并暴露 Fact,供 Fly 视图仪表板使用。例如 _distanceSensorFactGroup、_battery1FactGroup。
7.2.5 APM 传感器与机型差异
APM 的 APMAutoPilotPlugin::vehicleComponents() 按 vehicleType + 参数存在性 动态组装:
- 直升机:
APMHeliComponent - 潜水器:
APMSubFrameComponent、APMLightsComponent - 相机:
APMCameraComponent(检测CAM1_TYPE等) - ESP8266 WiFi:
ESP8266Component
79:80:src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc
if ( _vehicle->supportsRadio() ) {
_radioComponent = new APMRadioComponent(_vehicle, this);
supportsRadio() 来自 FirmwarePlugin::supportsRadio() 虚函数,可按机型关闭 RC Setup。
7.2.6 USB 外设自动连接
LinkManager 扫描串口时调用 QGCSerialPortInfo::getBoardInfo(),对 RTK GPS、SiK Radio 等可自动创建连接。新增 USB 外设类型:
- 在
USBBoardInfo.json增加boardClass(如RTK GPS) - 在
LinkManager自动连接逻辑中增加对应处理
7.2.7 Custom 定制示例
custom-example/ 展示最小定制路径:
24:28:custom-example/src/FirmwarePlugin/CustomFirmwarePluginFactory.cc
QList<MAV_AUTOPILOT> CustomFirmwarePluginFactory::supportedFirmwareTypes() const
{
list.append(MAV_AUTOPILOT_PX4);
继承 PX4FirmwarePlugin / PX4AutoPilotPlugin,Override 飞行模式、任务命令、Setup 组件列表,无需 fork 整个 QGC。
7.2.8 新增机型适配检查清单
| 层次 | 修改位置 | 说明 |
|---|---|---|
| USB 识别 | USBBoardInfo.json |
VID/PID → 板名 |
| 烧录 | Bootloader.h board ID + Firmware Hash |
固件 URL |
| 协议 | XxxFirmwarePlugin |
飞行模式、GUIDED、任务 |
| Setup | VehicleComponent + QML |
校准/配置 UI |
| 参数 | PX4/APM Parameter XML | 名称/单位/枚举 |
| 机架 | Airframe XML | SYS_AUTOSTART |
| 任务 | MavCmdInfo*.json |
航线命令 UI |
| 连接 | LinkManager |
自动连接规则 |
7.3 不同飞控协议兼容改造
QGC 4.0 核心 MAVLink 处理保持栈无关;协议差异通过 FirmwarePlugin 钩子消化。
7.3.1 插件选择与 Fallback
58:77:src/FirmwarePlugin/FirmwarePluginManager.cc
FirmwarePlugin* FirmwarePluginManager::firmwarePluginForAutopilot(MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType)
{
FirmwarePluginFactory* factory = _findPluginFactory(firmwareType);
if (factory) {
plugin = factory->firmwarePluginForAutopilot(firmwareType, vehicleType);
}
if (!plugin) {
if (!_genericFirmwarePlugin) {
_genericFirmwarePlugin = new FirmwarePlugin;
}
plugin = _genericFirmwarePlugin;
}
return plugin;
}
未知 Autopilot 回退 Generic FirmwarePlugin,提供基础 MAVLink 能力,无 PX4/APM 特有 Guided 等功能。
7.3.2 MAVLink 消息适配钩子
Vehicle 收消息前:
683:683:src/Vehicle/Vehicle.cc
if (!_firmwarePlugin->adjustIncomingMavlinkMessage(this, &message)) {
返回 false 则丢弃该消息(用于过滤重复 STATUSTEXT 等)。
Vehicle 发消息前:
2000:2000:src/Vehicle/Vehicle.cc
_firmwarePlugin->adjustOutgoingMavlinkMessage(this, link, &message);
APM 入站适配(典型):
487:514:src/FirmwarePlugin/APM/APMFirmwarePlugin.cc
bool APMFirmwarePlugin::adjustIncomingMavlinkMessage(Vehicle* vehicle, mavlink_message_t* message)
{
if (message->msgid == MAVLINK_MSG_ID_HEARTBEAT) {
_handleIncomingHeartbeat(vehicle, message);
return true;
}
if (_ardupilotComponentMap[vehicle->id()][message->compid]) {
switch (message->msgid) {
case MAVLINK_MSG_ID_PARAM_VALUE:
_handleIncomingParamValue(vehicle, message);
break;
case MAVLINK_MSG_ID_STATUSTEXT:
return _handleIncomingStatusText(vehicle, message, false);
...
}
}
return true;
}
APM 兼容要点:
- 组件识别 :通过 HEARTBEAT 的
autopilot字段维护_ardupilotComponentMap,仅对 ArduPilot 组件做方言转换 - STATUSTEXT 严重级别 :旧版 APM 使用非标准 severity 编码,
_adjustSeverity()映射到 MAV_SEVERITY 标准值 - PARAM_VALUE :参数名可能带
@后缀或索引差异,_handleIncomingParamValue规范化 - RC_CHANNELS vs RC_CHANNELS_RAW:合并处理不同版本消息
- 出站 PARAM_SET :
_handleOutgoingParamSet处理 APM 参数名/类型差异
PX4 入站适配:
503:516:src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc
bool PX4FirmwarePlugin::adjustIncomingMavlinkMessage(Vehicle* vehicle, mavlink_message_t* message)
{
if (message->compid == MAV_COMP_ID_UDP_BRIDGE) {
return true;
}
switch (message->msgid) {
case MAVLINK_MSG_ID_AUTOPILOT_VERSION:
_handleAutopilotVersion(vehicle, message);
break;
}
return true;
}
主要处理版本过低警告、AUTOPILOT_VERSION 解析;PX4 较贴近标准 MAVLink,适配量小于 APM。
7.3.3 飞行模式映射
各栈实现 flightMode(base_mode, custom_mode) 与 setFlightMode(name, &base, &custom)。
APM Copter 使用 APMCopterMode 枚举 + APMCustomMode 映射表:
25:51:src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc
setEnumToStringMapping({
{ STABILIZE, "Stabilize"},
{ ACRO, "Acro"},
{ GUIDED, "Guided"},
{ LOITER, "Loiter"},
{ RTL, "RTL"},
...
});
PX4 使用 union px4_custom_mode 位域解析 custom_mode,支持 MAIN_MODE / SUB_MODE。
Fly 视图下拉列表来自 FirmwarePlugin::flightModes(vehicle),各子类按固件能力返回不同列表。
7.3.4 能力位(Capabilities)
45:52:src/FirmwarePlugin/FirmwarePlugin.h
typedef enum {
SetFlightModeCapability = 1 << 0,
PauseVehicleCapability = 1 << 1,
GuidedModeCapability = 1 << 2,
OrbitModeCapability = 1 << 3,
TakeoffVehicleCapability = 1 << 4,
ROIModeCapability = 1 << 5,
} FirmwareCapabilities;
UI 与 Joystick 通过 isCapable(vehicle, GuidedModeCapability) 决定是否显示 Guided Takeoff、Orbit 等按钮,避免对不支持栈发送无效命令。
7.3.5 参数名版本 remap(APM 重点)
APM 跨版本常重命名参数。FirmwarePlugin 定义三层 Map:
54:67:src/FirmwarePlugin/FirmwarePlugin.h
typedef QMap<QString, QString> remapParamNameMap_t;
typedef QMap<int, remapParamNameMap_t> remapParamNameMinorVersionRemapMap_t;
typedef QMap<int, remapParamNameMinorVersionRemapMap_t> remapParamNameMajorVersionMap_t;
ArduCopterFirmwarePlugin::paramNameRemapMajorVersionMap() 在静态初始化中填充,如 3.5 版本 "CHUKE_" → "CHUTE_" 类映射。ParameterManager 请求参数前查表转换,保证 QGC 内部统一使用新名称。
7.3.6 任务命令树(MissionCommandTree)
任务编辑器命令列表按 MAV_AUTOPILOT × MAV_TYPE 二维索引:
50:61:src/MissionManager/MissionCommandTree.cc
for (MAV_AUTOPILOT firmwareType: _toolbox->firmwarePluginManager()->supportedFirmwareTypes()) {
FirmwarePlugin* plugin = ...->firmwarePluginForAutopilot(firmwareType, MAV_TYPE_QUADROTOR);
for(MAV_TYPE vehicleType: vehicleTypes) {
QString overrideFile = plugin->missionCommandOverrides(vehicleType);
if (!overrideFile.isEmpty()) {
_staticCommandTree[firmwareType][vehicleType] = new MissionCommandList(overrideFile, ...);
}
}
}
各 Plugin 返回 JSON 路径如 :/json/MavCmdInfoMultiRotor.json,定义命令显示名、参数 Fact 元数据、是否支持。
新增协议命令 UI :在对应 MavCmdInfo*.json 增加条目,或在 Custom Plugin 中 override missionCommandOverrides()。
7.3.7 地理围栏 / Rally / Follow Me
GeoFenceManager/RallyPointManager:基类 MAVLink 通用,Plugin 提供supported()与格式差异FollowMe:PX4 支持Follow Me模式字符串;APM 部分版本通过 Plugin 扩展
7.3.8 编译期协议裁剪
96:99:src/VehicleSetup/FirmwareUpgradeController.cc
#if !defined(NO_ARDUPILOT_DIALECT)
_downloadArduPilotManifest();
#endif
NO_ARDUPILOT_DIALECT 宏可构建纯 PX4 版本,移除 APM manifest、APM 固件 URL 等,减小体积。
7.3.9 协议兼容改造实践指南
| 场景 | 推荐改造点 |
|---|---|
| 新 MAVLink 消息字段与标准不符 | adjustIncomingMavlinkMessage 解码后重写 |
| 发送命令被拒 | adjustOutgoingMavlinkMessage 或 Plugin 专用 send 方法 |
| 新飞行模式 | 子类 flightModes + setFlightMode + custom_mode 位定义 |
| 参数 rename | paramNameRemapMajorVersionMap |
| 新任务命令 | MavCmdInfo*.json + MissionController 验证逻辑 |
| 完全新飞控栈 | 新建 XxxFirmwarePluginFactory + XxxAutoPilotPlugin + 全局静态 Factory 实例 |
| 仅改品牌/隐藏 APM | Custom QGCCorePlugin + CustomFirmwarePlugin 继承 PX4 |
7.4 FirmwareUpgradeSettings 配置
| Fact | 作用 |
|---|---|
apmChibiOS |
优先 ChibiOS vs NuttX 固件路径 |
apmVehicleType |
默认 APM 机型过滤(Copter/Plane/Rover/Sub) |
变更时触发 _buildAPMFirmwareNames() 刷新 manifest 匹配列表。
7.5 关键类方法速查
| 类 | 方法 | 作用 |
|---|---|---|
FirmwareUpgradeController |
startBoardSearch() / flash() |
UI 入口 |
FirmwareUpgradeController |
_initFirmwareHash() |
PX4 URL 表 |
FirmwareUpgradeController |
_downloadArduPilotManifest() |
APM 动态固件 |
PX4FirmwareUpgradeThreadWorker |
_findBootloader() |
同步 Bootloader |
PX4FirmwareUpgradeThreadWorker |
_flash() |
擦写校验 |
Bootloader |
sync/erase/program/verify |
协议实现 |
FirmwareImage |
load() / isCompatible() |
镜像解析 |
QGCSerialPortInfo |
canFlash() / getBoardInfo() |
USB 识别 |
FirmwarePluginManager |
firmwarePluginForAutopilot() |
运行时选栈 |
FirmwarePlugin |
adjustIncomingMavlinkMessage() |
协议适配 |
AutoPilotPlugin |
vehicleComponents() |
Setup 组件 |
VehicleComponent |
setupComplete() |
校准完成判定 |
MissionCommandTree |
getMissionCommands() |
任务命令 UI |
7.6 本章小结
QGroundControl 4.0 的固件升级子系统采用 Controller + Worker Thread + Bootloader 协议 三层架构,通过 USB VID/PID(USBBoardInfo.json)识别 Pixhawk/PX4Flow/SiK 设备,经 PX4 Bootloader 二进制协议完成擦除、分块编程与 CRC/回读校验。固件来源包括 PX4 S3 静态表、GitHub Releases、ArduPilot manifest.json 动态清单;.px4/.apj 包同时携带参数与机架元数据,烧录后即可更新 Setup 界面。
机型与外设适配 依托 FirmwarePluginFactory → FirmwarePlugin → AutoPilotPlugin → VehicleComponent 插件链,参数/display 由 Fact 系统 + Parameter XML 驱动,传感器校准/机架选择通过 QML Setup 面板与 MAVLink 命令完成。新增硬件通常需同步修改 JSON 板型表、Parameter/Airframe 元数据及对应 Component。
协议兼容遵循「Vehicle 通用、Plugin 消化差异」原则,APM 侧通过 heartbeat 组件映射、STATUSTEXT 严重级别修正、参数 remap 等机制对齐 MAVLink 标准;PX4 侧适配较轻。任务命令、飞行模式、Guided 能力均通过 Plugin 虚函数与 JSON 配置扩展,Custom 示例提供了不修改主干的定制路径。