尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
QGC二次开发:从自定义面板到MAVLink消息
简介这是一份面向初学者的QGCQGroundControl二次开发入门项目代码适合具备少量C与QT基础、希望掌握无人机地面站定制与功能扩展的开发者。压缩包内共3个文件包含inscode项目配置、html说明页面及gitignore文件整体仅5KB虽体积小巧但聚焦核心源码与配置。教程内容覆盖软件名称与图标修改、官方中文版BUG修复与汉化、商标更换、信号与槽添加、QML与C交互、MAVLINK解析与发送、地图更换及自定义MAVLINK消息等关键环节同时涉及多机轨迹显示与地图画点功能。通过这份资料读者可以了解QGC二次开发的整体流程获得可直接运行的修改后QGC源码、开发环境配置虚拟机以及独家汉化版QGC的下载指引特别适合用于快速验证开发思路或作为项目起步参考。目前已有432人浏览学习也印证了其对入门者有切实的参考价值。1. QGC二次开发到底在改什么值不值得动这个手手里那台自研载荷通过串口把数据喂给了飞控链路里数据明明是通的可打开地面站界面上只有电池、卫星数和飞行模式这些通用仪表属于我们自己的传感器读数一个都看不到。QGC二次开发解决的就是这一类需求在开源的 QGC 地面站基础上往界面里加自己的面板往通信里加自己的协议让地面站真正贴合你手上的设备而不是套一个通用模板。它适合懂一点 MAVLink、想掌握全链路的开发者也适合要交付定制地面站的团队。这篇文章会从工程结构、编译跑通、挂自定义面板、排查踩坑到扩展自定义消息把一次完整二开的路子走通。真心建议先花半天跑通一个能编译的 QGC后面所有的改动都建立在它之上别在半成品上反复抠。2. 读懂QGC工程布局二开最该先认准的几块源码2.1 Qt与QML混合架构为什么这个结构对二次开发格外友好QGC 是一个典型的 Qt 应用界面层大量使用 QML底层逻辑用 C 实现。这个混合架构对二次开发的价值在于改界面的时候你不需要跟重型 C 编译流程死磕QML 文件写错了通常只是界面显示异常不会让整个程序直接崩溃而消息收发、设备状态管理这些核心逻辑放在 C 侧性能和可靠性有保障。两层之间通过注册机制和模型/视图方式连接界面层拿到的是 C 暴露出来的属性不会直接把底层数据铺到 QML 里。一开始扎进这个工程很多人会觉得自己在黑匣子里转圈这很正常。你先记住一条主线界面找 QML逻辑找 C两者通过“注册 QML 类型”和“属性绑定”沟通。入门阶段你的大部分改动只需要落在 QML 层C 层能看懂信号槽和属性注册就够了。我见过不少开发者一上来就啃 Vehicle 的通信代码结果界面还没跑通就先被消息流劝退这个顺序是反的。2.2 四个高频修改区UI、Vehicle、MAVLink 与 Utilities源码目录很多但二开真正高频进出的是四个区域。第一块是 ui 目录以及资源文件里的 QML地面站的工具栏、飞行仪表、参数设置界面全在这你想加按钮、改布局、塞新面板主要跟这里打交道。第二块是 src/Vehicle它差不多是飞行器在软件里的投影所有 MAVLink 消息进来之后的第一站就是这里收发消息相关的行为改动都集中在它身上。第三块是 MAVLink 协议库它负责消息的编解码。如果你只是调用现有协议可以不碰它可一旦要自定义消息就必须从这里下手后面第 6 章会专门展开。第四块是 Utilities 目录日志、坐标转换、格式化这类通用能力都在这。我通常会在二开第一天就把日志工具摸清楚因为排查消息丢失、面板不显示这类问题时日志是第一个要开的门。这四个区域的边界并不绝对但按这个地图去找代码比漫无目的地全库搜“voltage”省时间得多。2.3 启动链路从 main 函数到你的第一个 QML 窗口弄明白程序怎么启动能帮你判断自己的改动该放在哪一步。QGC 的启动流程去掉平台分支后可以简化为下面这个样子// QGC 启动流程精简示意省略了平台初始化和单例清理 int main(int argc, char *argv[]) { QGCApplication* app new QGCApplication(argc, argv); // toolBox 负责集中创建各类 Manager // 自定义 QML 类型的注册也放在这个阶段完成 app-toolBox()-load(); // 主窗口本体是 QMLC 只负责窗口外壳和生命周期 app-showMainWindow(); return app-exec(); }这段代码说明了两个关键点。一是 toolBox 是全局管理器的集中注册点你要注册一个新的 C 服务或一个新的 QML 类型通常要在它完成加载之前准备好二是主窗口本质是一个 QML 界面C 只提供窗口壳子。找到那个被资源文件引用的主窗口 QML你就找到了几乎所有界面的挂载点。后续往里加自定义面板本质上都是在这个 QML 组件树上找一个合适的位置插进去。3. 从拉源码到首跑把QGC编译环境的全过程走一遍3.1 环境准备两条主流路线的版本对齐建议二开最忌讳的就是环境没对齐就开跑结果编译报错全跟版本有关。Ubuntu 和 Windows 是两条主流路线。Ubuntu 下我习惯用 Qt 官方安装器装完整开发套件再配 GCC 和必要的构建工具Windows 下常见的组合是 MSVC 编译器加 Qt 对应位的开发库也可以用 MinGW 组合但迭代时注意别混用。无论哪条路线位数必须和编译目标一致64 位环境配 64 位 Qt这个不齐后面链接阶段全是怪错。QGC 对 Qt 版本有明确要求以官方仓库说明为准我个人会选 Qt 5 的稳定分支并锁住版本因为二开团队一般不希望地面站的界面框架频繁迁移。安装完成后记得确认 qmake 命令指向的是你刚装的那套 Qt而不是系统自带的旧版本这一步用qmake -v验证就行。很多人的编译翻车根子就出在 PATH 里混进了两个 Qt 版本。3.2 拉取源码、同步子模块与编译命令源码一定要带着子模块一起拉。QGC 依赖 MAVLink 库等子模块只克隆主仓不拉子模块后续生成代码时必报错。推荐的做法是第一次克隆就直接带子模块# 从官方仓库或团队内部分发渠道获取源码务必携带子模块 git clone --recurse-submodules 你的QGC源码仓库地址 cd qgroundcontrol # 如果克隆时没带子模块执行下面这条补拉 git submodule update --init --recursive # 使用传统 qmake 方式构建 mkdir -p build cd build qmake ../qgroundcontrol.pro make -j4参数说明--recurse-submodules让 git 在克隆主仓时一并拉取所有子模块省去后续手动同步qmake ../qgroundcontrol.pro是让 Qt 的构建系统解析工程文件并生成 Makefilemake -j4表示用 4 个并行任务编译机器内存不够时可以降到-j2。第一次编译会消耗比较久的时间源码体积大、QML 资源多半小时到一小时都是正常的看到滚动刷屏不要慌。3.3 首次编译的三个高频报错与对策第一个高频报错是“缺少 Qt 头文件”现象是 make 打印 fatal error 然后找不到某个 QtXXX 头文件。原因基本是装了精简版 Qt 或者没装对应开发模块解决方法是回到安装器补全开发套件而不是手动下载头文件硬塞。第二个是子模块相关报错比如找不到 mavlink 头文件这通常是克隆时漏了子模块回到仓库根目录执行一次git submodule update --init --recursive就能解决。第三个比较隐蔽发生在 MAVLink 头文件生成环节报错指向 Python 环境。QGC 构建过程会用 Python 脚本预先生成部分协议代码系统缺 Python 或缺相关库就会失败。解决方法是补装 Python 3 并安装依赖库然后在命令行里确认python3 --version可执行。每次遇到这类报错先把完整报错信息读一遍再搜很多新人栽在只看最后一行实际根因在中间几行。编译这东西没有太多玄学报错信息基本都指到了该改的位置。4. 第一个实战把一个自定义状态面板挂进QGC主界面4.1 选挂载点主界面布局与自定义组件的插入位置主界面是一个由多个 QML 组件拼起来的布局树侧边栏、工具栏、仪表区各有各的容器。二开最常见的两种做法一种是在侧边栏区域预留一个容器把自定义面板放进去好处是常驻可见坏处是改动原有布局结构出错时影响面大另一种是注册一个新的工具按钮点击后以对话框或抽屉方式弹出面板好处是不碰原有界面出问题时只删掉自己加的东西就恢复原样。对第一次做二开的人来说我强烈建议走第二种。先做一个能独立弹出的小面板验证“QML 能加载、数据能绑定”再考虑常驻布局。这个顺序能帮你把“我的代码有问题”和“主布局被破坏了”这两个问题分开排查。挂载点确定之后下一步就是真正把面板画出来这不需要改动 C 侧第一步可以先做纯静态版本。4.2 用QML把状态面板画出来下面这个面板示例用来展示自定义载荷的在线状态和电池电压纯 QML 实现不依赖复杂控件放在哪个 QGC 版本上都能跑// PayloadStatusPanel.qml // 自定义载荷状态面板显示在线状态与电池电压 import QtQuick 2.15 import QtQuick.Controls 2.15 Item { id: payloadPanel width: 220 height: 140 // 对外暴露属性由父级组件或 C 侧赋值绑定 property real batteryVolt: 0.0 property bool payloadOk: false Rectangle { anchors.fill: parent radius: 6 color: payloadOk ? #f4f7f4 : #fdecea border.color: #bbbbbb } Column { anchors { left: parent.left; top: parent.top; margins: 10 } spacing: 6 Text { text: 载荷状态 font.pixelSize: 16 font.bold: true } Text { text: 电压 batteryVolt.toFixed(1) V } Text { text: 链路 (payloadOk ? 正常 : 异常) } } }逻辑说明面板本身是一个 Item尺寸固定为 220 乘 140背景 Rectangle 的颜色会根据payloadOk切换在线是浅灰绿离线是浅红用于快速目检Column 里放了三个 Text分别显示标题、电压和链路状态。batteryVolt和payloadOk是暴露出来的属性父级组件或 C 逻辑可以直接赋值面板自身不关心数据来源。这个设计符合 QGC 界面层只做展示、数据由上层注入的习惯。4.3 从C把实时数据接进来QML 面板本身不会产生数据真正的数据来源是飞控或载荷设备。二开里常见的做法是写一个 C 管理类持有实时状态通过信号槽把数据推给 QML。下面这个 PayloadStateManager 是一个最小可用的状态容器// PayloadStateManager.h #pragma once #include QObject class PayloadStateManager : public QObject { Q_OBJECT public: explicit PayloadStateManager(QObject* parent nullptr) : QObject(parent), _batteryVolt(0.0), _payloadOk(false) { } Q_INVOKABLE double batteryVolt() const { return _batteryVolt; } Q_INVOKABLE bool payloadOk() const { return _payloadOk; } public slots: void updateState(double volt, bool ok) { _batteryVolt volt; _payloadOk ok; emit stateChanged(); } signals: void stateChanged(); private: double _batteryVolt; bool _payloadOk; };这个类暴露了两个只读接口给 QML更新数据走updateState这个槽。要让 QML 能识别它需要在启动阶段注册类型// main.cpp 或 toolBox 初始化阶段 qmlRegisterTypePayloadStateManager(QGroundControl, 1, 0, PayloadStateManager);注册之后在 QML 里就能用PayloadStateManager这个类型创建实例把它的属性绑定到面板上。实践中的常见做法是收到 MAVLink 消息后在接收处理分支里调用updateStateQML 面板立即刷新。这里的关键在于 C 只管更新状态UI 只做展示中间靠信号stateChanged触发刷新这是 Qt/QML 混合开发的经典套路。5. 二次开发避坑指南最容易翻车的五个位置5.1 QML改了没反应资源缓存和路径不一致现象修改了一个 QML 文件重新编译启动界面还是老样子仿佛改了个寂寞。原因通常是两个一是 QML 文件被打到了 Qt 的资源系统里增量编译时资源可能没有重新打包二是改错了文件编辑器里打开的是工作副本而工程资源加载的是另一个路径下的同名文件。解决方式先搜索确认你改的文件确实在加载路径里然后清空 build 目录重新 qmake 再编译。别第一时间怀疑缓存玄学先排除路径问题这一条我说过太多次几乎占了界面改动问题的六成。5.2 自定义面板不显示先怀疑尺寸和挂载点现象代码没有报错运行起来界面却找不到自己加的面板。原因排下来最常见的是宽高为 0尤其当面板被塞进 Column 或 Row 时如果没有设置Layout.fillWidth或明确宽度组件可能被压缩成不可见的一根线其次是挂载点本身不在当前显示的页面中比如放进了某个默认不打开的 Tab 页里。解决办法是先给面板一个固定宽高和一条高亮边框背景让它强制可见再逐步调整挂载位置。这个排错方法能帮你快速把“面板代码的问题”和“布局容器的问题”分开。5.3 自定义MAVLink消息收不到dialect没对上现象飞控端明明在发自定义消息地面站里绑定的槽函数一直不触发。原因九成出在协议方言不一致上QGC 编译时用的是某一组 MAVLink 方言生成的头文件你的自定义消息 ID 不在其中消息进入分发模块后没有对应的处理分支直接被丢弃。解决步骤是先抓包确认消息确实到达了地面站端口这一步我习惯看 UDP 14550 端口上的报文再检查生成头文件时用的 XML 里是否包含了你的消息定义最后在接收分发处补上处理 case。顺序一定不能反先链路后代码否则容易白改一通。5.4 中文显示乱码Windows下的编码问题现象界面或代码注释里的中文变成乱码和方块Linux 上少见Windows 上经常出现。原因是 MSVC 编译器对源文件编码的处理和 UTF-8 不一致Qt 5 的字符串又默认按 UTF-8 解析两边一冲突就乱。解决办法是给 MSVC 编译选项加上/utf-8同时把 QML 文件统一保存为 UTF-8 格式字符串内容习惯性套上tr()或QStringLiteral。这个坑不难绕但第一次踩到会怀疑是自己代码写错实际上就是编码约定问题属于二开团队的默认必修课。5.5 版本漂移升级后老代码编译不过现象QGC 源码从旧版本切到新版本之前的二开代码报出一堆类型错误和找不到成员。原因很直接QGC 本身迭代很快C 类名、QML 模块路径、接口签名都可能变化二次开发代码还没来得及同步就是这副样子。解决方式分两层团队层面建立一个二开分支并锁定基线版本不随便追官方 release个人层面每次改动记录清单升级前先跑一遍全量编译看哪些是纯 API 改名、哪些是结构性变化。这个坑我栽过不止一次后来养成了“版本锁死 变更清单”的习惯二开工程量越大这两个习惯越值钱。6. 进阶让自定义MAVLink消息从定义到上机验证走通走到这一步你已经能在 QGC 里加自己的界面了。真正的二开项目通常还需要自定义消息把载荷数据从飞控链路里捞出来。常见做法是新增一个自定义 dialect XML在里面描述消息格式再通过 MAVLink 工具链重新生成 C 头文件让 QGC 的协议层认识这条消息。!-- 自定义消息示例仅演示结构ID 取自定义区间 -- message id42000 namePAYLOAD_STATE description自定义载荷状态上报/description field typeuint8_t namestate0离线 1在线/field field typefloat namebattery_volt载荷电池电压/field /message生成头文件后把新的协议文件放回 MAVLink 库目录重新编译工程然后在消息分发处为PAYLOAD_STATE增加一个处理分支调用你自定义状态管理类的updateState接口。验证时先开模拟器或直接连飞控用抓包工具确认报文抵达再看程序日志里是否打印了解析结果。整个链路里最容易出错的是生成头文件的 XML 和你实际发送的字节结构不一致代码没问题数据也对不上。二开做到这个深度真正考验你的已经不是某个具体类怎么调而是能不能把“链路、协议、界面、状态管理”这几层串起来排查问题。我自己的习惯是每改一步都保留一个能回滚的提交点尤其涉及自定义消息时前后端都留好版本对应关系否则线上跑半个小时后客户端闪退连回滚都找不到后悔药吃。希望这篇教程能帮你少走几段弯路也把你从只会改界面的入门段位往上推一截。本文还有配套的精品资源点击获取
RELATED

相关推荐

BrowserSkill:Chrome 浏览器原生 CLI 编程工具

BrowserSkill:Chrome 浏览器原生 CLI 编程工具

1. 项目概述:BrowserSkill 是什么,它解决的不是“浏览器自动化”,而是开发者工作流的物理层重构BrowserSkill(常缩写为 bsk)不是又一个 Selenium 封装库,也不是 Puppeteer 的 CLI 包装器。我第一次在内部工…

📅 2026/10/18 16:53:51
智能风控AI决策引擎架构实战:从0到1搭建实时拦截系统

智能风控AI决策引擎架构实战:从0到1搭建实时拦截系统

智能风控AI决策引擎的架构实战:拆解一个实时拦截系统的设计与落地做风控架构这行久了,你会发现一个很残酷的现实:市面上讲风控模型算法的文章车载斗量,但真正把“决策引擎”从0到1搭起来、还能稳定扛住高并发和业务迭代的实战经验…

📅 2026/10/18 16:53:50
飞控计算机硬实时操作系统核心机制与工程选型全解析

飞控计算机硬实时操作系统核心机制与工程选型全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/18 16:53:50
MORE NEWS

更多资讯

📰

CoCa模型深度解析:对比学习与生成式预训练的融合之路

今年以来多模态这块最不缺的就是新模型,但每次有朋友问我“想快速跟上CoCa这条路子该看什么”,我都会把这篇列为首选。倒不是因为它参数最大或者刷榜最猛,而是因为CoCa把对比学习和生成式学习这两条原本有点“互相对着干”的技术路线&#xf…

📰

原生双端音视频社交源码:架构、难点与部署避坑指南

简介:这套资源定位于快速搭建一对一音视频社交应用的原生双端源码方案,覆盖Android(Java)与iOS(Objective-C)客户端及ThinkPHP后台,适合有移动端开发基础、希望直接获得可扩展通信框架的开发者。…

📰

十月AI前端岗面试策略:从被动应答到主动引导

1. 十月AI前端岗面试的真实生态:为什么“老实”反而成了减分项十月这个时间节点,在招聘市场里有个很微妙的位置。金九银十的说法流传了很多年,但真正在一线做技术招聘的人都知道,十月其实是“补录季”和“捡漏季”的叠加态。大厂的…

📰

JavaScript计算器入门:解决input值类型转换与四则运算陷阱

1. 这个看似简单的计算器&#xff0c;为什么90%的初学者第一次写就卡在“值没传进去”&#xff1f;你有没有试过&#xff0c;在HTML里放三个<input>&#xff0c;两个用来输数字&#xff0c;一个用来显示结果&#xff0c;然后信心满满地写上document.getElementById(a).va…

📰

ChatGPT智能UI逆向拆解:协议驱动的流式组件渲染与前端架构

1. 项目概述&#xff1a;为什么我要拆解一个智能UI如果你经常跟各类AI对话产品打交道&#xff0c;会发现界面里有一套很少被单独讨论的“智能UI层”&#xff1a;对话还在流式输出时&#xff0c;底部已经冒出一个可点击的“运行代码”按钮&#xff1b;你问天气&#xff0c;回答还…

📰

大模型Agent记忆系统实战指南:上下文管理、向量检索与遗忘机制

我不太喜欢用“记忆”这个词来描述大模型应用里的缓存机制&#xff0c;因为大部分人第一次给自己的Agent加记忆时&#xff0c;都把问题想得太简单了——不就是把聊天记录存起来&#xff0c;下次再塞回去吗&#xff1f;我一开始也是这么干的&#xff0c;结果做出来的东西不仅回答…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬