3、系统需求分析:飞控系统顶层需求分解、功能架构定义、接口信号定义
好,咱们进入第三讲。前面两讲我们聊了什么是基于模型的设计,以及它为什么能帮我们少走弯路。今天要聊的,是真正动手干活前的第一步——系统需求分析。
说白了,就是搞清楚两件事:飞控到底要干什么? 以及 它跟外界怎么打交道?
我见过不少团队,上来就写代码、调参数,结果飞到一半发现需求没对齐,推倒重来。嗯,这种坑我踩过不止一次。所以今天这部分,我建议你认真看。
3.1 顶层需求分解:从“能飞”到“怎么飞”
顶层需求,通常来自客户或者产品定义。比如:“我们要做一款物流无人机,载重5公斤,续航30分钟,能在6级风下稳定飞行。”
这句话听起来很美好,但直接拿给飞控工程师,他没法干活。为什么?因为太模糊了。你得把它拆成飞控系统能理解的语言。
我个人习惯,把顶层需求拆成三个维度:
- 性能需求:比如悬停精度、最大角速度、爬升率、抗风等级。
- 功能需求:比如自主起降、航线飞行、返航、故障保护。
- 接口需求:比如跟地面站怎么通信、跟舵机怎么连接、传感器数据怎么传。
举个例子,顶层需求里的“6级风下稳定飞行”,怎么拆?
性能需求分解示例:
- 最大抗风风速:13.8 m/s(6级风上限)
- 位置保持精度:水平 ±1.5m,垂直 ±0.8m(6级风条件下)
- 姿态角波动范围:滚转/俯仰 ≤ ±5°
- 控制带宽:至少 10 Hz(保证对风扰动的快速响应)
你看,这样一拆,飞控算法工程师就知道:哦,我需要设计一个鲁棒性够强的控制器,而且传感器更新率不能太低。
我的经验: 分解需求时,一定要跟测试团队对齐。我曾经遇到过,需求写的是“抗6级风”,但测试标准用的是瞬时风速,而实际飞行遇到的是阵风。结果就是,实验室过了,外场炸了。所以,需求分解要连带测试条件一起定义。
3.2 功能架构定义:飞控系统的“骨架”
需求拆完了,接下来要画功能架构。说白了,就是飞控系统由哪些模块组成,每个模块干什么活。
我习惯用一张功能框图来搞定这件事。飞控系统通常包含以下几个核心功能模块:
| 功能模块 | 职责描述 | 输入 | 输出 |
|---|---|---|---|
| 传感器管理 | 采集IMU、GPS、气压计、磁力计等数据,进行时间戳对齐和预处理 | 原始传感器数据 | 校准后的传感器数据 |
| 状态估计 | 融合多传感器数据,估算姿态、位置、速度 | 传感器数据 | 姿态角、位置、速度 |
| 导航与制导 | 根据目标航点,生成期望轨迹和期望姿态 | 目标航点、当前位置 | 期望姿态、期望推力 |
| 飞行控制 | 根据期望姿态和实际姿态,计算舵面/电机控制量 | 期望姿态、实际姿态 | PWM控制信号 |
| 任务管理 | 处理飞行模式切换、故障保护逻辑、指令解析 | 遥控器指令、地面站指令 | 模式切换信号、故障响应 |
| 通信管理 | 管理与地面站、遥控器、数传电台的数据收发 | 外部通信数据 | 遥测数据、状态信息 |
你想想看,这个架构图一旦画出来,整个团队的沟通成本就大大降低了。硬件工程师知道要给传感器留什么接口,算法工程师知道自己的模块输入输出是什么,测试工程师也知道该测哪些边界条件。
注意: 功能架构定义时,最容易犯的错误是模块耦合过紧。比如把状态估计和飞行控制写在一个函数里。当时觉得方便,后面想换一个估计算法,结果牵一发动全身。所以,模块间接口要清晰,耦合要松。
3.3 接口信号定义:把“连线”画清楚
架构定好了,接下来就是定义每个模块之间的接口信号。这一步看似琐碎,但往往是项目后期最头疼的地方。
我举个例子。状态估计模块输出给飞行控制模块的信号,至少应该包含:
- 姿态角:滚转、俯仰、偏航(单位:度或弧度)
- 角速度:滚转、俯仰、偏航速率(单位:度/秒)
- 位置:经纬度、高度(单位:度、米)
- 速度:北向、东向、地向速度(单位:米/秒)
- 时间戳:数据有效时间(单位:微秒)
这些信号,每个都要明确:
- 数据类型:float32 还是 int16?
- 取值范围:比如姿态角是 -180~180 还是 0~360?
- 更新频率:100Hz 还是 200Hz?
- 单位:统一用国际单位制,别混用。
我个人习惯,用一张接口定义表来管理所有信号。比如:
| 信号名称 | 源模块 | 目标模块 | 数据类型 | 范围 | 单位 | 频率 |
|---|---|---|---|---|---|---|
| roll_angle | 状态估计 | 飞行控制 | float32 | -180 ~ 180 | deg | 200 Hz |
| pitch_rate | 状态估计 | 飞行控制 | float32 | -500 ~ 500 | deg/s | 200 Hz |
| gps_lat | 传感器管理 | 状态估计 | double | -90 ~ 90 | deg | 10 Hz |
| pwm_output | 飞行控制 | 舵机/电机 | uint16 | 1000 ~ 2000 | us | 400 Hz |
你看,这样定义完,每个工程师拿到手就知道自己该发什么、收什么。不会出现“我以为你传的是度,结果你传的是弧度”这种低级错误。
避坑指南: 我曾经在一个项目里,因为接口信号的时间戳没对齐,导致状态估计和飞行控制之间差了20毫秒的延迟。飞起来就是高频抖动,查了三天才找到原因。所以,时间戳一定要在接口定义里明确写出来,而且最好用统一的系统时钟。
3.4 从需求到模型的桥梁
好了,需求分解了,架构画了,接口定义了。接下来干什么?
在基于模型的设计流程里,这些文档化的东西,最终要变成可执行的模型。比如在 Simulink 里,你会把每个功能模块建一个子系统,接口信号用总线(Bus)来定义。
我建议你,在建模之前,先把接口定义表做成一个 .m 文件或者数据字典。这样模型可以直接读取,避免手敲出错。
% 接口定义示例(MATLAB 数据字典)
% 状态估计 -> 飞行控制
est_to_ctrl.roll_angle = Simulink.BusElement;
est_to_ctrl.roll_angle.Name = 'roll_angle';
est_to_ctrl.roll_angle.DataType = 'single';
est_to_ctrl.roll_angle.Dimensions = 1;
est_to_ctrl.roll_angle.Unit = 'deg';
est_to_ctrl.roll_angle.Description = '滚转角';
est_to_ctrl.pitch_rate = Simulink.BusElement;
est_to_ctrl.pitch_rate.Name = 'pitch_rate';
est_to_ctrl.pitch_rate.DataType = 'single';
est_to_ctrl.pitch_rate.Dimensions = 1;
est_to_ctrl.pitch_rate.Unit = 'deg/s';
est_to_ctrl.pitch_rate.Description = '俯仰角速率';
% 创建总线对象
estToCtrlBus = Simulink.Bus;
estToCtrlBus.Elements = [est_to_ctrl.roll_angle, est_to_ctrl.pitch_rate];
嗯,这一步做好了,后面建模、仿真、代码生成都会顺畅很多。说白了,需求分析阶段多花点时间,后面调试阶段就能少熬几个夜。
我的习惯: 每次开始一个新项目,我都会先花一周时间专门做需求分析和接口定义。团队里有人觉得慢,但项目后期他们就知道值了。尤其是做基于模型的设计,模型本身就是需求的可执行表达,前期定义越清楚,模型越不容易跑偏。
好,这一讲就到这里。下一讲,我们会聊怎么把这些需求变成可执行的规格模型——也就是真正开始建模了。到时候见。