4、QGC地面站二次开发环境:QGC源码下载、Qt环境配置、QGC插件机制、自定义UI面板

地面站是无人机系统的「眼睛」和「手」。没有它,你连飞机在飞什么都看不到。

QGC(QGroundControl)是目前PX4生态里最主流的开源地面站。我最早接触它的时候,第一反应是「这玩意儿代码量也太大了」。但后来摸透了它的插件机制,才发现——其实核心逻辑并不复杂。

这一章,我们就从零开始,把QGC的二次开发环境搭起来。顺便聊聊它的插件系统,最后手撸一个自定义UI面板。

4.1 QGC源码下载与版本选择

先搞定源码。QGC托管在GitHub上,地址是:https://github.com/mavlink/qgroundcontrol。

我个人习惯用 git clone 而不是直接下载ZIP。为什么?因为后续你要切分支、拉更新,用git方便得多。

版本选择建议

QGC的master分支更新很快,但有时候会引入不稳定的API改动。我做项目时一般选 Stable 标签版本,比如 v4.2.0 或 v4.3.0。这些版本经过充分测试,插件接口也相对稳定。

# 克隆最新源码
git clone https://github.com/mavlink/qgroundcontrol.git

# 切换到稳定版本(以v4.3.0为例)
cd qgroundcontrol
git checkout tags/v4.3.0 -b stable_v4.3.0

克隆完成后,你会看到这样的目录结构:

目录/文件 说明
src/ 核心源码,包括UI、通信、飞行逻辑
plugins/ 插件目录,我们二次开发的主战场
qgroundcontrol.pro Qt项目文件,编译入口
deploy/ 打包部署脚本

4.2 Qt环境配置——别踩这些坑

QGC基于Qt 5.15.x开发。我试过Qt 6,兼容性问题一堆,不建议在生产项目里用。

我的推荐配置:

  • Qt版本:5.15.2 LTS
  • 编译器:MSVC 2019(Windows)/ GCC 9+(Linux)/ Clang(macOS)
  • 额外组件:Qt Charts、Qt Location、Qt Serial Port、Qt WebEngine

注意:Qt 5.15.2 的离线安装包需要商业许可。个人开发可以用在线安装器,或者从 qt.io 下载开源版本。我遇到过有人用5.12编译,结果Qt Charts模块缺失,折腾了一整天。

安装完成后,用Qt Creator打开 qgroundcontrol.pro。第一次加载会有点慢,因为要解析所有依赖。别急,去泡杯咖啡。

编译前,检查一下 .qmake.conf 里的配置:

# 确保这些宏定义正确
CONFIG += qtquickcompiler
DEFINES += QT_NO_DEBUG_OUTPUT
DEFINES += QGC_DISABLE_UVC

然后按 Ctrl+R(或点击运行按钮)。如果一切顺利,你会看到QGC的启动界面。我第一次编译成功时,屏幕上弹出地图的那一刻,说实话,挺有成就感的。

4.3 QGC插件机制——为什么说它是「乐高」

QGC的插件系统,说白了就是一套「热插拔」的模块化架构。你不需要修改核心代码,只需要写一个插件,就能添加新功能。

它的核心接口是 QGCPlugin 类。每个插件都是一个独立的动态库(.dll / .so / .dylib)。

插件生命周期:

  1. QGC启动时扫描 plugins/ 目录
  2. 加载所有有效的 .so/.dll 文件
  3. 调用每个插件的 init() 方法
  4. 插件注册自己的UI面板、菜单项、通信接口

我画了一张图,帮你理解这个流程:

QGC 插件加载与注册流程 QGC 启动 扫描 plugins/ 目录 加载 .so/.dll 调用 init() 注册 关键说明 • 插件目录:plugins/ • 接口类:QGCPlugin • 注册方式:addPanel() • 生命周期:init→run→shutdown • 不修改核心代码 • 支持热加载(调试时) • 每个插件独立 .pro 文件

你看,整个流程很清晰。你只需要关注 init() 里注册了什么,其他事情QGC帮你搞定。

4.4 自定义UI面板——手写一个「测绘参数面板」

理论说完了,来点实战。我们写一个测绘专用的参数面板,显示当前测绘任务的覆盖面积、航点数量、预计飞行时间。

第一步:创建插件目录

在 plugins/ 下新建 SurveyPanel/ 文件夹。里面放两个文件:

  • SurveyPanel.pro —— 插件项目文件
  • SurveyPanel.h / SurveyPanel.cpp —— 插件实现

第二步:编写插件类

// SurveyPanel.h
#pragma once
#include "QGCPlugin.h"

class SurveyPanel : public QGCPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "com.qgroundcontrol.QGCPlugin" FILE "SurveyPanel.json")
    Q_INTERFACES(QGCPlugin)

public:
    SurveyPanel(QObject *parent = nullptr);
    void init() override;
};
// SurveyPanel.cpp
#include "SurveyPanel.h"
#include "SurveyPanelWidget.h"

SurveyPanel::SurveyPanel(QObject *parent) : QGCPlugin(parent) {}

void SurveyPanel::init()
{
    // 注册一个自定义UI面板
    registerPanel("测绘参数", [](QWidget *parent) -> QWidget* {
        return new SurveyPanelWidget(parent);
    });
}

第三步:实现UI界面

新建 SurveyPanelWidget,继承自 QWidget。我用QML写了一个简单的布局:

// SurveyPanelWidget.qml
import QtQuick 2.12
import QtQuick.Controls 2.12

Rectangle {
    width: 300
    height: 200
    color: "#f5f5f5"

    Column {
        anchors.centerIn: parent
        spacing: 10

        Text { text: "测绘参数面板"; font.bold: true; font.pixelSize: 16 }
        Text { text: "覆盖面积: 12.5 km²" }
        Text { text: "航点数量: 48" }
        Text { text: "预计飞行时间: 32 min" }
    }
}

第四步:注册到QGC菜单

在 init() 里,还可以添加菜单项:

void SurveyPanel::init()
{
    registerPanel("测绘参数", ...);
    registerMenuAction("工具", "打开测绘面板", this, SLOT(openSurveyPanel()));
}

小技巧:调试插件时,不用每次都重新编译整个QGC。在Qt Creator里,右键点击你的插件项目,选择「构建」即可。QGC支持动态加载插件,你甚至可以在运行中替换 .dll 文件——不过记得先停掉插件线程。

4.5 避坑指南——我踩过的几个坑

做插件开发,有几个地方特别容易翻车。我列出来,你注意一下:

问题 原因 解决方案
插件加载失败 Qt版本不匹配,插件编译用的Qt版本与QGC不同 确保插件和QGC使用同一套Qt库编译
UI面板不显示 未正确调用 registerPanel() 检查 init() 是否被调用,打印日志确认
崩溃在 Q_PLUGIN_METADATA 缺少 .json 文件或格式错误 确保 SurveyPanel.json 存在且内容正确
菜单项点击无反应 信号槽连接错误或SLOT函数未声明为 public slots 检查 Q_OBJECT 宏和函数声明

我曾经有一次,插件编译通过、加载成功,但面板就是不出来。折腾了两小时,最后发现是 registerPanel 的第二个参数——那个lambda表达式——捕获了错误的 this 指针。嗯,从那以后我写lambda都格外小心。

4.6 小结

这一章我们做了三件事:下载QGC源码、配好Qt环境、写了一个测绘参数面板插件。插件机制是QGC二次开发的灵魂,你想想看,以后不管加什么功能——测绘参数、电池监控、自定义航线——都可以用同样的套路。