尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

QGC源码二次开发全解析:从编译环境到MAVLink协议与模式定制

QGC源码二次开发全解析:从编译环境到MAVLink协议与模式定制 简介开源地面站软件是无人机开发中的重要一环QGroundControlQGC作为跨平台地面站其源码深度决定了定制边界。理解MAVLink协议是掌握QGC通信链路的基础它承载着遥测、指令与参数传输。源码编译则是二次开发的前提通过Ubuntu环境搭建可摆脱Release包限制。QGC基于Qt与QML构建分层结构清晰FirmwarePlugin提供固件适配层支持自定义飞行模式与参数交互。实际工程中品牌替换、私有协议对接、参数扩展等需求都需要直接修改源码。从通用技术概念出发掌握QGC源码结构、编译流程与MAVLink机制即可针对自研飞控或特定业务进行地面站定制。1. 先问能不能直接用Release啃QGC源码的三种典型场景我第一次正经翻开QGCQGroundControl的源代码是在一个周五晚上。当时项目里给一台装了自研飞控的无人机做地面站定制直接用官网下载的QGC Release包连上后飞行模式下拉框只有一个手动参数面板里一堆自定义字段根本显示不出来。换Mission Planner也一样因为飞控协议不是标准的ArduPilot/PX4所有现成地面站都不认。折腾到半夜才认命地打开GitHub把qgroundcontrol仓库clone下来。从那天起地面站这块我再也没用过现成安装包。1.1 现成安装包解决不了的三类需求先说结论如果你是新手只是想用地面站连一下无人机、看看姿态、规划航线那直接用官网Release包和App就够了没必要碰源码。但以下三类需求一定会把你逼到源码层面第一类界面和业务定制。公司项目要出自己的地面站品牌改Logo改应用名甚至要裁剪掉航测、跟随、日志分析等功能模块只保留任务规划、传感器校准、参数读写。Release包是一个编译好的黑盒你连里面一个按钮的文字都改不了。第二类非标飞控或私有协议对接。市面上很多飞控基于STM32方案对外通信用的不是标准MAVLink而是私有串口协议。国内玩匿名飞控的人应该很熟悉匿名地面站所谓的参数读写本质就是把协议栈写死在它自己的控制器里。你想在新的地面站里复现类似能力就必须顺着QGC的通信层源码一路改下去。第三类行为细节的掌控。比如QGC默认的飞行模式列表来自FirmwarePlugin里的硬编码映射遇到自研飞控或深度魔改的PX4固件模式的枚举值就对不上。又如QGC之前的建图插件、离线地图源、遥测刷新频率都有大量可用但不够符合需求的地方。想掌控这些细节源码是唯一的参考。1.2 源码编译和Release包的本质差别很多人担心编译源码很难其实难的不是编译本身而是搞清楚源码仓库里哪些东西能去掉、哪些不能动。QGC的Release包是用Stable分支在CI流水线上构建出来的通用版本相当于一辆官方调校好的量产车。你自己从源码编译等于拿到了一台裸车API全部裸露行为全部可控代价是你需要承担版本匹配、依赖安装、模块裁剪这些原本官方替你打理好的事。另外要提醒一句QGC采用GPL v3协议。这意味着如果你把定制版分发给第三方就需要按GPL要求开放你的改动代码。很多公司选择的做法是内部自用或仅供自家硬件配套不对外分发软件本身这样协议上的压力会小很多。但具体怎么处理建议让公司法务参与判断别等产品落地了再补合规。基于这个前提这篇内容更像是我踩过坑之后的整理。下面直接进环境搭建、源码结构、二次开发和排错这几块最实际的内容。2. Ubuntu 20.04从拉代码到跑起来依赖、命令与三个逃不掉的编译坑2.1 依赖清单不要一上来就装Qt6QGC的编译环境要求比较固定尤其是Qt版本必须用Qt 5.15.x。我见过最典型的失败案例就是先装了Ubuntu 20.04软件源里的Qt 5.12.8又折腾Qt 6最后编译时各种Cannot mix incompatible Qt library报错。QGC对C标准、Qt模块、编译器版本都有隐含要求版本不对后面每一步都是煎熬。我整理了一套在Ubuntu 20.04上实测稳定的依赖安装命令# 基础工具 sudo apt update sudo apt install -y build-essential git cmake ninja-build # Qt相关 sudo apt install -y qtbase5-dev qtdeclarative5-dev qt5-qmake sudo apt install -y libqt5serialport5-dev libqt5svg5-dev sudo apt install -y libqt5location5-dev libqt5multimedia5-plugins sudo apt install -y libqt5waylandclient5 qtwayland5 # 其他依赖 sudo apt install -y libsdl2-dev libgstreamer1.0-dev sudo apt install -y libgstreamer-plugins-base1.0-dev sudo apt install -y libssl-dev clang这里有个关键点apt源的Qt 5.12.8不一定够用。Ubuntu 20.04官方源里的Qt偏老QGC官方构建脚本要求至少5.15.2所以稳妥做法是从Qt官方下载离线安装包单独装5.15.2然后在CMake里用-DQT_DIR指过去。如果你apt装完能编译通过那说明你的分支版本对Qt要求不算严但如果遇到Qt 5.12.8 is too old之类的报错别犹豫直接去Qt官网下载5.15.2的Linux离线包装到~/Qt/5.15.2。2.2 完整的从零编译流程代码拉取和编译命令如下可以直接抄# 拉取稳定分支注意一定要带 --recursive git clone --recursive -b Stable https://github.com/mavlink/qgroundcontrol.git cd qgroundcontrol # 如果clone时忘记拉子模块补一次 git submodule update --init --recursive # 编译 mkdir build cd build cmake .. -DCMAKE_BUILD_TYPEDebug -DQT_DIR~/Qt/5.15.2/gcc_64 make -j$(nproc)首次编译时间比较长视机器性能从20分钟到1小时不等。编译产物会在build/staging目录下运行./staging/qgroundcontrol/qgroundcontrol 21 | tee run.log如果一切顺利QGC主界面会弹出来左侧出现连接按钮这里的QGC至少证明你的工具链没问题可以进入二次开发了。2.3 最常卡的三个编译错误及对策我整理了一个高频问题对照表都是编译时最常见的坑错误现象真正原因解决办法Could not find a package configuration file provided by Qt6系统里或CMake路径中混入了Qt6确保只用Qt 5.15.x清理CMake缓存后加-DQT_DIR指定fatal error: mavlink/xxx.h: No such file or directory子模块没有拉取完整执行git submodule update --init --recursivelibQt5Xxx.so: cannot open shared object file缺Qt模块或系统库对照上面的apt列表补齐尤其是libqt5location5-dev和libqt5serialport5-devCould NOT find SDL2缺游戏控制库QGC摇杆功能依赖sudo apt install -y libsdl2-dev补充一个小经验不要用root用户编译QGC否则运行时会产生一堆目录权限问题另外如果你在虚拟机里跑建议给编译分配至少4GB内存make -j$(nproc)在高配置机器上可能爆发内存不足可以把-j降到-j4。3. 源码地图三个核心链路看懂QGC的骨架编译通过只是开始真正难的是拿到源码不知道从哪下手。QGC的代码量相当大全看一遍不现实但它的分层逻辑非常清晰。我建议无论你后续想干什么先沿着三个链路走一遍目录结构、MAVLink通信链路、飞行模式映射链路。3.1 顶层目录一张表看懂源码结构QGC源码核心目录和职责如下目录职责二次开发常改场景src/Comm通信链路管理串口、UDP、TCP、蓝牙等底层连接新增通信方式、修改波特率策略、串口权限处理src/MAVLinkMAVLink协议栈子模块一般不动除非要解析私有MAVLink扩展消息src/Vehicle飞行器对象姿态、状态、参数、遥测的封装层新增自定义状态字段、遥测展示逻辑src/FirmwarePlugin固件差异适配层PX4、ArduPilot等都在这里自定义飞行模式、修改模式映射、固件特定参数src/MissionEditor/src/PlanView航线规划界面与任务编辑器自定义航点动作、任务类型扩展src/QmlControlsQML通用控件库参数编辑、仪表盘等新增界面控件、改参数面板样式src/UI历史遗留的QWidget界面一般不用src/Analyzer日志分析模块做飞行日志后处理时参考看到这个表格你应该能猜到一个规律大部分改行为的需求集中在FirmwarePlugin和Vehicle大部分加界面的需求集中在QmlControls。3.2 遥测上行与下行MAVLink消息怎么在代码里流动QGC和飞控之间的通信可以简化成一条流水线下行遥测串口/网口数据 →SerialLinksrc/Comm中→MAVLinkProtocol把二进制帧解析成结构体 →Vehicle更新内部状态 → QML界面通过属性绑定刷新显示。上行指令QML界面比如点击解锁按钮→ 调用Vehicle的某个方法 → 该方法构造MAVLink消息如MAV_CMD_COMPONENT_ARM_DISARM→LinkManager交给LinkInterface发出去。实际调试时这条链路最关键的是MAVLinkProtocol。它决定了QGC和飞控用MAVLink 1还是MAVLink 2、支持哪些扩展消息。老飞控连不上新QGC大概率就是这里协议版本没对齐。3.3 飞行模式设置FirmwarePlugin与状态机映射很多人在搜QGC飞行模式设置都有什么其实这个问题在源码里非常直接每种飞控固件都对应一个FirmwarePlugin子类比如PX4Plugin、ArduCopterPlugin。这些子类里实现了模式的枚举映射把地面站里显示的字符串比如定高自稳任务翻译成各固件认识的MAVLink模式值。当你在地面站切换模式时QGC调用的核心逻辑大概是// FirmwarePlugin 中核心虚函数 virtual QStringList getFlightModes() 0; virtual bool setFlightMode(const QString mode) 0;实际实现里PX4Plugin会把字符串映射到PX4的MAV_MODE_FLAG和PX4_CUSTOM_MAIN_MODE组合ArduCopterPlugin则对应到ArduPilot的COPTER_MODE。如果你的飞控不是标准固件想加入新的飞行模式这里就是第一修改点。4. 二次开发下手点换品牌、扩展参数、自定义飞行模式源码攒在手里下面聊几个最常被问到的改造方向。4.1 换品牌信息与默认参数最小改动快速见效如果你只是想做一个自己的地面站品牌半小时内能完成替换资源文件里的Logo图标路径一般在resources/目录下的图标文件。修改应用显示名称和版本号全局搜索QGCApplication或APP_NAME宏一般在src/QGCApplication.cc或qgroundcontrol.pro/CMakeLists.txt里定义。如果需要连上飞控后隐藏部分默认页面可以在QGroundControl.qml里裁剪导航栏的按钮项。这一步基本不碰逻辑纯粹是资源和界面配置。但做完你会发现自己的地面站和QGC在行为上还是没什么区别这很正常真正的差异要从参数和模式入手。4.2 参数读写扩展从QGC参数面板说起参数读写是地面站最核心的功能之一。QGC参数面板默认展示飞控通过MAVLink上报的所有参数参数列表自动发现编辑后通过PARAM_SET消息写回。如果你在STM32飞控上跑的是私有协议想复现类似QGC这种左侧参数树右侧编辑的体验有两条路一是在飞控端实现MAVLink参数协议。这种办法最省事参数名、类型、最小值、默认值都能用MAVLink标准字段描述QGC自动就认了。前提是你的底层通信协议能承载MAVLink帧哪怕是把MAVLink封装在私有串口帧里也能凑合。国内有些匿名飞控的MODBUS式参数读写本质上就是在协议栈上做了一个参数表和MAVLink思路类似。二是在QGC侧新增私有参数通道。比如在MAVLinkProtocol里拦截自定义消息ID或在Vehicle里新增一个FactSystem的FactGroup把私有协议的参数映射进去。这条路灵活但需要你对FactSystem有一定理解。我的建议是能用MAVLink标准参数协议就别自己造界面。它不仅省事还能白嫖QGC的参数搜索、分组、导入导出功能。4.3 自定义飞行模式改完源码后飞控端要配合什么如果你改了FirmwarePlugin里的模式列表但飞控端不认等于白改。这里有一个容易被忽略的配合关系地面站侧负责把模式字符串翻译成MAVLink指令发给飞控。飞控侧必须实现了对应的模式并能够响应MAV_CMD_DO_SET_MODE或PX4风格的MAV_CMD_DO_SET_MODE消息。如果你的飞控是自研的推荐做法是先在飞控端定义一套模式枚举比如MODE_HOLD10、MODE_BRAKE11然后在QGC的FirmwarePlugin子类里加映射if (modeString 悬停) { mavCmdDoSetMode.customMode 10; } else if (modeString 刹车) { mavCmdDoSetMode.customMode 11; }另外QGC有些界面元素如模式选择下拉框是从FirmwarePlugin::getFlightModes()动态读取的所以加完映射后记得把这个新模式也加进返回列表。改完代码重新编译连接真机测试前先确认数传链路正常能收发消息否则模式切换指令发不出去排查起来会非常混乱。4.4 私有数传协议接入从链路层还是协议层下手经常有人在搜3DR数传电台连接QGC、雷迅V6X用QGC调试。先说结论3DR数传和雷迅V6X都是标准MAVLink设备QGC直接就能连不需要改源码重点是选对串口和波特率。3DR数传电脑端串口一般是/dev/ttyUSB0波特率常见57600Radio默认或921600Telemetry2。雷迅V6X通常是Type-C直连或数传模块PX4固件QGC连接后按PX4流程校准即可。真正需要碰私有协议的是那些给自制飞控写地面站的人。这类需求建议先在src/Comm新增一个LinkInterface实现处理字节流的读写然后在协议层决定是解析MAVLink还是单独起一个私有协议解析器。链路层和协议层一定要分开否则后期排错会很痛苦——我在项目里见过有人把私有协议解析写在SerialLink的readBytes回调里结果一改波特率整个协议栈就崩根本没法调试。5. 源码级排错实录五个让我熬夜过的问题5.1 日志先行QGC的日志体系怎么用很多人在QGC里遇到问题喜欢漫天改代码。我的第一建议永远不是改代码而是先开日志。用--logging参数启动QGC会输出非常详细的调试信息./staging/qgroundcontrol/qgroundcontrol --logging 21 | tee run.log日志文件默认存在用户目录下的~/.qgroundcontrol/logs里面包含遥测帧率、串口连接状态、MAVLink错误计数等。遇到连接异常、指令失败第一条线索基本都是日志里的打印。QGC还支持按模块过滤日志级别核心类都挂在了自己的日志系统上写完自定义代码之后善用日志是你定位自己bug最快的路。5.2 串口连不上权限、ModemManager与波特率在Ubuntu上连不上数传90%的原因是串口权限或ModemManager抢占了USB串口。权限sudo usermod -a -G dialout $USER加完必须重新登录才生效。ModemManager这个服务默认会把USB串口当成调制解调器去探测导致QGC打不开端口。直接sudo systemctl stop ModemManager或者写udev规则让数传设备不被它接管。波特率检查数传两端是否一致QGC每个串口连接项都可以单独设置波特率老版本默认57600。还有一个很容易被忽略的点Type-C直连飞控时部分飞控默认进入Bootloader模式或CDC枚举冲突QGC根本看不到端口。这时候先看dmesg | grep tty确认设备是否存在再用串口助手手动发0xFE之类的心跳包测一下链路通不通一秒就知道。5.3 连接后无数据MAVLink协议版本对不上有些老飞控只发MAVLink 1而新版QGC默认会尝试MAVLink 2。现象是QGC显示已连接但姿态、电池、GPS统统没有数据。在线路调试里如果飞控回复的消息带FE开头就是MAVLink 1帧FD开头是MAVLink 2帧。你可以在QGC的连接参数里强制指定MAVLink协议版本或者反过来在代码里找到MAVLinkProtocol的getCurrentVersion()让它只发送旧版本协议。项目里遇到这种问题我一般优先在QGC界面设置里改协议版本实在不行再改代码。5.4 参数写不进参数名、长度与写保护参数写不进飞控是个高频问题。除了飞控本身处于写保护比如PX4的param set权限、参数被锁定之外最常见的原因是参数名不匹配。QGC的Fact系统会根据飞控端上报的参数元数据在校验参数名和类型后才发送PARAM_SET。如果飞控端参数定义里没有这个参数或者类型对不上QGC会静默失败或者显示写入失败。排查这类问题建议在Vehicle中日志里过滤PARAM关键字看到底有没有发出PARAM_SET飞控回没回PARAM_VALUE。如果消息层面一切正常但飞控还是没反应那就是飞控端参数表的问题要用飞控自己的底层调试方式解决。5.5 界面卡顿与崩溃QML调试思路QGC的界面是QML加C控制器。界面卡顿、崩溃这类问题在源码层级上最常见的原因是C侧数据更新触发QML属性的高频重绘。我处理过的一个典型例子自研数传把姿态数据刷到50Hz按遥感惯例这不算高但QGC默认的仪表盘控件绑定了过于复杂的属性计算导致界面掉帧。解决办法是限流界面刷新频率在C侧用一个定时器聚合更新而不是每个MAVLink消息都触发一次propertyChanged。崩溃的话用gdb跑QGCbt命令看调用栈十次有八次能定位到是空指针或生命周期管理问题尤其是Vehicle对象在断开连接后被UI继续引用的场景很值得注意。6. 最后说几句个人体会QGC的源代码是一个典型的工业级开源项目代码量大、结构清晰、文档稀缺。与其说读懂QGC源码不如说会用QGC源码解决问题。我个人的核心体会就一句话先在源码里找到你问题的入口而不是试图了解所有代码。你只需要沿着一条数据链路比如从串口到界面把每个环节的名字和职责搞清楚后面所有定制化需求都会变得顺理成章。还有一个小技巧分享给大家在git log里看历史提交很多棘手功能都可以通过查看提交信息找到作者当时的思路和测试方法这个比任何教程都直接。希望这篇内容能帮你在QGC源码这片森林里少走几段弯路。本文还有配套的精品资源点击获取
返回列表