第二章:系统架构设计——地面站整体架构分层与模块划分原则
地面站这东西,说白了就是个“大脑”。它要跟飞机通信,要处理数据,要显示画面,还要响应你的操作。如果所有代码揉成一锅粥,那后期维护起来……嗯,我经历过那种痛苦。
我个人习惯,在动手写第一行代码之前,先把架构想清楚。今天我们就聊聊地面站的“骨架”——四层架构,以及怎么把各个功能模块合理地切分开。
2.1 为什么一定要分层?
你想想看,一个地面站要干多少事?
- 从串口、网口、甚至蓝牙收数据
- 把二进制流解析成遥测值
- 根据飞行状态做逻辑判断(比如低电量报警)
- 把姿态画在仪表盘上
- 响应你点击“一键返航”按钮
如果这些代码全写在一个类里,那叫“大泥球”。我在项目中遇到过这种代码,改一个串口解析,结果地图显示崩了。为什么?因为耦合太紧,牵一发动全身。
分层的目的,就是让每一层只关心自己的事。通信层只管收发字节流,数据层只管解析和存储,业务层只管逻辑判断,UI层只管画界面。各层之间通过定义好的接口通信,互不干扰。
核心原则:上层依赖下层,下层不依赖上层。UI层可以调用业务层,但业务层不能直接操作UI控件。
2.2 四层架构详解
我习惯把地面站分成四层,从下往上依次是:
2.2.1 通信层(最底层)
这一层负责“物理连接”。不管是串口、UDP、TCP还是蓝牙,通信层只做一件事:收发原始字节流。它不关心字节流里是什么内容,只管把数据扔给上层。
我在项目中遇到过一个问题:串口数据偶尔会丢包。后来发现是通信层没有做超时重传。嗯,这里要注意,通信层虽然简单,但一定要处理好异常情况——断线重连、缓冲区溢出、校验失败,这些都得考虑进去。
小技巧:通信层建议设计成“可插拔”的。今天用串口,明天换网口,只需要换一个通信层实现,上层代码完全不用动。
2.2.2 数据层
数据层拿到通信层的字节流,开始“翻译”。MAVLink协议也好,自定义协议也罢,都在这一层解析。解析完的数据,比如经纬度、高度、电池电压,会缓存起来,供上层使用。
数据层还要负责持久化——把关键参数存到本地文件或数据库里。这样下次启动地面站,参数还在。
2.2.3 业务逻辑层
这是地面站的“大脑”。它根据数据层提供的遥测数据,做出决策。比如:
- 检测到电池电压低于3.7V → 触发低电量报警
- GPS信号丢失超过5秒 → 自动切换为悬停模式
- 用户点击“一键返航” → 生成返航航线并下发
业务层不关心数据怎么来的,也不关心界面怎么画。它只做逻辑判断。我曾经犯过一个错:在业务层里直接写了UI弹窗代码。结果后来想换UI框架,改得我头皮发麻……
避坑指南:业务层永远不要引用任何UI相关的命名空间。它应该是一个纯逻辑层,可以独立测试。
2.2.4 UI层
UI层是用户直接看到的东西。地图、仪表盘、参数列表、控制按钮……它负责把数据“画”出来,并把用户的操作(比如点击按钮)传递给业务层。
UI层要做的,是“好看”和“好用”。但注意,UI层不要做任何业务判断。比如“一键返航”按钮,UI层只管调用业务层的ExecuteReturnToLaunch()方法,至于能不能返航、怎么返航,那是业务层的事。
2.3 模块划分原则
分层是纵向的切分,模块是横向的切分。一个地面站可以拆成哪些模块?我一般按功能来分:
| 模块名称 | 所属层级 | 职责描述 |
|---|---|---|
| 通信管理模块 | 通信层 | 管理串口/网络连接,收发数据 |
| 协议解析模块 | 数据层 | 解析MAVLink等协议,提取遥测数据 |
| 数据缓存模块 | 数据层 | 缓存最新遥测值,提供快速查询 |
| 飞行控制模块 | 业务层 | 处理飞行模式切换、航线指令 |
| 故障诊断模块 | 业务层 | 监控异常状态,触发报警 |
| 地图显示模块 | UI层 | 显示飞机位置、轨迹、航点 |
| 仪表盘模块 | UI层 | 显示姿态、速度、高度等实时数据 |
模块划分有几个原则,我总结一下:
- 高内聚:一个模块只做一件事,并且把它做好。比如协议解析模块,只负责解析,不负责存储。
- 低耦合:模块之间通过接口通信,不要直接访问对方的内部数据。我习惯用事件或回调来解耦。
- 可替换:每个模块都应该可以独立替换。比如今天用百度地图,明天换高德地图,地图模块换掉就行,不影响其他模块。
我的经验:模块划分不要太细,也不要太粗。太细了管理成本高,太粗了复用性差。一般一个地面站拆成7~10个模块比较合适。
2.4 一个简单的代码示例
说了这么多,来点实际的。下面是一个简化的C#代码片段,展示各层如何协作:
// 通信层:接收原始数据
public class SerialCommLayer
{
public event Action<byte[]> OnDataReceived;
public void Start(string portName)
{
// 打开串口,启动接收线程
// 收到数据后触发 OnDataReceived 事件
}
}
// 数据层:解析协议
public class MavlinkParser
{
public void Parse(byte[] rawData)
{
// 解析MAVLink帧
// 提取姿态、位置等信息
// 存入 DataCache
}
}
// 业务层:逻辑判断
public class FlightController
{
public void CheckBattery(float voltage)
{
if (voltage < 3.7f)
{
// 触发低电量报警事件
OnLowBattery?.Invoke();
}
}
}
// UI层:显示和交互
public class MainForm
{
private void OnLowBatteryHandler()
{
// 在界面上显示报警弹窗
MessageBox.Show("电池电量低,请尽快降落!");
}
}
你看,每一层各司其职。通信层只管收数据,数据层只管解析,业务层只管判断,UI层只管显示。改任何一层,其他层都不受影响。
2.5 总结
地面站的架构设计,说白了就是“分而治之”。四层架构把纵向的依赖关系理清了,模块划分把横向的功能边界划清了。这两件事做好了,后面的开发会顺畅很多。
我个人习惯,在项目初期花30%的时间做架构设计。别急着写代码,先把图画清楚,把接口定义好。否则后期重构的成本,会让你后悔当初的偷懒。
最后说一句:架构不是一成不变的。随着项目演进,你可能会发现某些层需要合并,某些模块需要拆分。没关系,架构是活的,关键是要保持“高内聚低耦合”这个核心思想。