第二章:系统架构设计——地面站整体架构分层与模块划分原则

地面站这东西,说白了就是个“大脑”。它要跟飞机通信,要处理数据,要显示画面,还要响应你的操作。如果所有代码揉成一锅粥,那后期维护起来……嗯,我经历过那种痛苦。

我个人习惯,在动手写第一行代码之前,先把架构想清楚。今天我们就聊聊地面站的“骨架”——四层架构,以及怎么把各个功能模块合理地切分开。

2.1 为什么一定要分层?

你想想看,一个地面站要干多少事?

  • 从串口、网口、甚至蓝牙收数据
  • 把二进制流解析成遥测值
  • 根据飞行状态做逻辑判断(比如低电量报警)
  • 把姿态画在仪表盘上
  • 响应你点击“一键返航”按钮

如果这些代码全写在一个类里,那叫“大泥球”。我在项目中遇到过这种代码,改一个串口解析,结果地图显示崩了。为什么?因为耦合太紧,牵一发动全身。

分层的目的,就是让每一层只关心自己的事。通信层只管收发字节流,数据层只管解析和存储,业务层只管逻辑判断,UI层只管画界面。各层之间通过定义好的接口通信,互不干扰。

核心原则:上层依赖下层,下层不依赖上层。UI层可以调用业务层,但业务层不能直接操作UI控件。

2.2 四层架构详解

我习惯把地面站分成四层,从下往上依次是:

地面站四层架构 UI层(用户界面层) 地图显示 · 仪表盘 · 参数设置 · 飞行控制按钮 业务逻辑层(BLL) 飞行模式切换 · 航线规划 · 故障诊断 · 数据融合 数据层(DAL) 协议解析 · 数据缓存 · 日志记录 · 参数持久化 通信层(Comm Layer) 串口收发 · UDP/TCP · 蓝牙 · 数据链路管理 数据流向 ↑

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%的时间做架构设计。别急着写代码,先把图画清楚,把接口定义好。否则后期重构的成本,会让你后悔当初的偷懒。

最后说一句:架构不是一成不变的。随着项目演进,你可能会发现某些层需要合并,某些模块需要拆分。没关系,架构是活的,关键是要保持“高内聚低耦合”这个核心思想。


可可精品资料库