1. 系统架构概览
做多无人机地面站系统,第一件事不是写代码,而是把架构想清楚。我见过太多团队上来就撸界面、调串口,结果做到一半发现模块之间耦合得跟麻花似的,改一个地方崩一片。说白了,地面站就是一个“大脑”,它得同时管好几架飞机,还要保证不出乱子。
我个人习惯,先画一张顶层架构图,把每个模块的边界定死。这样后面开发时,大家各管各的,互不干扰。下面这张图,就是我这几年反复迭代后沉淀下来的核心框架。
1.1 整体架构:分层解耦
这个架构我把它分成五层。你想想看,每一层只干自己的事,通过定义好的接口跟上下层打交道。这样做的好处是——哪一层出了问题,直接换掉就行,不用动其他代码。
- 用户交互层:说白了就是操作界面。显示地图、飞机状态、任务进度,接收操作员的点击和指令。我建议用 Qt 或 Web 技术做,跨平台方便。
- 核心业务逻辑层:这是大脑。它不关心你用什么界面,也不管底层是串口还是网口。它只处理“业务”——比如航线冲突检测、多机协同逻辑、应急切换策略。
- 功能模块层:任务规划、飞行控制、状态监控。这三个模块是并行的,各自独立运行。
- 数据与通信层:负责把业务数据打包成协议报文,或者把收到的报文解析成业务数据。我习惯在这里做一层“协议适配”,这样换通信方式时不用改上层代码。
- 硬件设备层:电台、4G 模块、Wi-Fi 网卡……这些东西只管收发字节流,不关心内容。
我的经验:曾经有个项目,客户要求从数传电台换成 4G 模块。因为我在通信层做了抽象,只改了硬件抽象层的驱动代码,上层一行没动,两天就搞定了。要是当初把通信逻辑跟业务逻辑写在一起……嗯,那估计得重构半个月。
1.2 核心功能模块划分
功能模块这块,我重点说说三个核心模块。它们之间的协作关系,直接决定了系统能不能稳定跑起来。
| 模块名称 | 核心职责 | 关键数据 | 我踩过的坑 |
|---|---|---|---|
| 任务规划模块 | 生成航线、分配任务、冲突检测 | 航点列表、任务队列、禁飞区 | 多机航线交叉时没做碰撞检测,差点撞机 |
| 飞行控制模块 | 指令下发、模式切换、应急处理 | 控制指令、心跳包、状态机 | 心跳超时处理太慢,导致飞机失控 |
| 状态监控模块 | 遥测解析、告警生成、日志记录 | GPS、电池、IMU、链路质量 | 日志写得太频繁,把 SD 卡写坏了 |
这三个模块之间怎么通信?我用的是一种“发布-订阅”模式。任务规划模块发布一条新航线,飞行控制模块订阅后自动执行,状态监控模块订阅后开始记录相关数据。谁也不直接调用谁,全靠消息总线。
注意:千万不要让模块之间直接函数调用。比如任务规划模块直接调用飞行控制模块的“起飞”函数。一旦模块需要升级或替换,所有调用它的地方都得改。用消息总线,模块之间只认消息格式,不认对方是谁。
1.3 数据流与通信链路设计
数据流这块,我把它分成两条主线:上行链路和下行链路。上行是地面站给飞机发指令,下行是飞机给地面站回报数据。
上行链路(指令下发)
流程很简单:操作员在界面上点“起飞” → 业务层生成 MAVLink 指令 → 通信层打包 → 硬件层通过电台发出去。但这里有个坑——多机并发时,指令不能同时发,得排队。我曾经遇到过同时给 5 架飞机发起飞指令,结果电台缓冲区溢出,丢包了。
下行链路(遥测上报)
每架飞机每秒会发回几十条数据:GPS 坐标、高度、速度、电池电压、IMU 数据……如果地面站不做处理,直接往界面上塞,UI 会卡死。我的做法是在通信层做一个“数据整形器”,把高频数据降采样,只把关键变化推送给界面。
数据流关键设计原则:
- 上行指令必须带序列号和确认机制,防止重复执行
- 下行遥测必须区分“重要告警”和“普通状态”,重要告警优先处理
- 通信链路必须做心跳检测,超过 3 秒没收到心跳,自动触发应急流程
通信链路选型
实际项目中,我常用这几种链路:
| 链路类型 | 带宽 | 延迟 | 距离 | 适用场景 |
|---|---|---|---|---|
| 数传电台(433MHz/915MHz) | ~100kbps | 10-50ms | 5-30km | 视距内多机编队 |
| 4G/5G 模块 | ~10Mbps | 20-100ms | 不限(有信号) | 超视距、城市环境 |
| Wi-Fi(2.4GHz/5.8GHz) | ~50Mbps | 5-20ms | ~1km | 近距离测试、室内 |
我个人习惯,主链路用数传电台,因为它稳定、抗干扰强。同时保留一条 4G 备份链路,一旦主链路断了,自动切换。这个切换逻辑我写在硬件抽象层里,上层完全无感。
避坑指南:我曾经在项目里只用了一条 4G 链路,结果飞到一个基站覆盖盲区,直接失联。飞机按失控保护逻辑自动返航了,但地面站这边完全不知道发生了什么,干着急。从那以后,我所有项目都强制要求双链路冗余。