第四章:地面站与指挥中心搭建
4.1 为什么地面站这么重要?
说实话,很多人觉得地面站就是个遥控器的大屏版。其实不是。
我参与的第一个风电巡检项目,团队花了两个月调飞控,结果第一次实地测试就翻车了——无人机在天上飞得好好的,地面站却显示「信号丢失」。后来发现,不是通信问题,是地面站软件对特定机型的MAVLink消息解析有bug。
从那天起我就明白:地面站不是附属品,它是整个系统的神经中枢。你想想看,无人机在天上飞,所有状态数据、控制指令、任务规划,全都要经过地面站。地面站挂了,无人机就是断了线的风筝。
4.2 QGroundControl二次开发:从入门到实战
QGroundControl(以下简称QGC)是目前最成熟的开源地面站之一。我选择它作为基础平台,原因有三:
- 协议支持全:MAVLink 1.0/2.0全覆盖,主流飞控都能接
- 架构清晰:Qt + C++,插件化设计,扩展方便
- 社区活跃:遇到问题基本都能找到答案
4.2.1 开发环境搭建
我个人习惯用Qt 5.15 + MSVC 2019这套组合。为什么不用Qt 6?嗯,QGC对Qt 6的支持还在完善中,有些插件会报错。别问我怎么知道的——我踩过这个坑。
# 克隆源码
git clone --recursive https://github.com/mavlink/qgroundcontrol.git
cd qgroundcontrol
# 安装依赖(Windows环境)
pip install -r requirements.txt
# 编译
mkdir build && cd build
cmake .. -G "Visual Studio 16 2019" -A x64
cmake --build . --config Release
--recursive 参数拉取子模块。我见过有人漏了这一步,结果编译到一半报错找不到MAVLink库,白白浪费半小时。
4.2.2 核心二次开发点
针对风电巡检场景,我们需要在QGC基础上做这些改造:
| 模块 | 改造内容 | 关键文件 |
|---|---|---|
| 地图引擎 | 替换为离线地图(风电场常无网络) | QmlMapEngine.cc |
| 任务规划 | 添加风机巡检航线模板 | MissionController.cc |
| 数据展示 | 增加风机状态叠加层 | Vehicle.cc |
| 通信模块 | 支持多机同时连接 | LinkManager.cc |
举个例子,添加风机巡检航线模板时,我写了个简单的航线生成函数:
// 生成风机巡检航线(三点环绕法)
QList<QGeoCoordinate> generateTurbineInspectionPath(
const QGeoCoordinate& turbineCenter,
double radius, // 环绕半径(米)
double altitude, // 飞行高度(米)
int pointsPerCircle // 每圈采样点
) {
QList<QGeoCoordinate> path;
double angleStep = 2 * M_PI / pointsPerCircle;
for (int i = 0; i < pointsPerCircle; ++i) {
double angle = i * angleStep;
double latOffset = radius * cos(angle) / 111320.0;
double lonOffset = radius * sin(angle) / (111320.0 * cos(turbineCenter.latitude() * M_PI / 180.0));
QGeoCoordinate point(
turbineCenter.latitude() + latOffset,
turbineCenter.longitude() + lonOffset,
altitude
);
path.append(point);
}
return path;
}
4.3 Web端指挥大屏设计
地面站是给飞手用的,指挥大屏是给项目经理看的。两者定位完全不同。
指挥大屏的核心需求就三个:全局态势感知、多机状态监控、异常告警。说白了,就是让领导一眼看清「现在几台机子在飞、飞得怎么样、有没有出问题」。
4.3.1 技术选型
我推荐用这套技术栈:
- 前端框架:Vue 3 + TypeScript(生态成熟,组件库丰富)
- 地图引擎:Leaflet + 离线瓦片(风电场网络差,你懂的)
- 实时通信:WebSocket(从QGC转发数据到Web端)
- 数据可视化:ECharts(画仪表盘、趋势图很顺手)
4.3.2 核心功能模块
我设计的指挥大屏通常包含这几个区域:
- 地图区:显示所有无人机实时位置、航线、风机点位
- 状态面板:每台无人机的电量、信号强度、飞行模式
- 告警列表:实时滚动显示异常事件(低电量、信号丢失、GPS丢星)
- 统计看板:今日巡检架次、完成率、平均耗时
这里有个关键点——数据流设计。我曾经见过一个方案,Web端直接连飞控,结果飞控的MAVLink数据量太大,浏览器直接卡死。正确的做法是:
无人机 → QGC(数据聚合) → WebSocket Server → 指挥大屏
QGC作为数据中继,把原始MAVLink消息解析成JSON格式,再推送给Web端。这样Web端只处理业务数据,压力小很多。
4.3.3 实时数据推送实现
在QGC里添加WebSocket服务端,代码大概这样:
// WebSocketServer.h
class WebSocketServer : public QObject {
Q_OBJECT
public:
explicit WebSocketServer(quint16 port = 9090);
public slots:
void onVehicleUpdated(const QJsonObject& vehicleData);
private:
QWebSocketServer* m_server;
QList<QWebSocket*> m_clients;
};
// WebSocketServer.cpp
void WebSocketServer::onVehicleUpdated(const QJsonObject& vehicleData) {
QByteArray message = QJsonDocument(vehicleData).toJson();
for (auto client : m_clients) {
client->sendTextMessage(message);
}
}
前端接收数据后,用ECharts实时更新仪表盘:
// 前端代码(Vue 3)
const ws = new WebSocket('ws://localhost:9090');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// 更新地图标记
updateMarker(data.vehicleId, data.lat, data.lng);
// 更新电量仪表
batteryGauge.setValue(data.batteryPercent);
// 检查告警
if (data.batteryPercent < 20) {
addAlert(`无人机 ${data.vehicleId} 电量不足`);
}
};
4.4 系统架构总览
说了这么多,咱们用一张图把整个体系串起来:
这张图我画了好几个版本才定下来。你看,数据从无人机出发,经过QGC地面站聚合处理,再通过WebSocket推送到指挥大屏。每一层各司其职,互不干扰。
4.5 避坑指南
最后分享几个我踩过的坑:
- 离线地图要提前准备:风电场通常在山沟里,4G信号时有时无。我建议用QGIS下载好18级瓦片,打包成MBTiles格式,QGC直接加载本地文件。
- WebSocket断线重连:网络不稳定是常态。前端一定要实现自动重连机制,我一般设置3秒重试间隔,最多重试10次。
- 数据频率控制:无人机位置数据每秒更新10次,全部推给Web端会撑爆带宽。我在QGC里做了降采样——位置数据每秒推2次,状态数据每秒推1次,告警数据实时推。
- 多机ID管理:每台无人机要有唯一标识。我习惯用飞控的MAVLink系统ID(sysid),配合自定义的机组编号,比如「WF-01-03」表示风场1的第3台机子。
嗯,地面站和指挥大屏这块,核心思路就是「分层解耦」。QGC负责和无人机打交道,Web端负责和人打交道。中间用WebSocket搭座桥,两边各干各的活,清爽又稳定。