2. 划分飞控软件模块边界
任务定义
本案例针对eVTOL飞控软件,基于“高内聚、低耦合”原则完成模块划分,并定义模块间接口。目标产物为:一份模块划分清单(含职责边界)、一份接口定义表(含数据流方向与关键字段)、以及一份可编译的接口头文件(C语言),用于约束后续各模块的独立开发。
模块划分与职责边界
依据功能内聚原则,将飞控软件划分为六个模块,每个模块只处理单一领域职责,模块间通过显式接口通信,禁止跨模块直接访问内部数据。
| 模块名 | 职责边界(只做这些) | 不负责(边界外) |
|---|---|---|
| 传感器管理 | IMU/GPS/气压计/磁力计数据采集、时间戳对齐、异常值剔除、工程单位转换 | 姿态解算、位置滤波 |
| 导航 | 姿态/位置/速度估计(融合滤波)、航向计算 | 原始传感器驱动、轨迹规划 |
| 控制 | 姿态环/速度环/位置环控制律计算,输出期望推力和力矩 | 舵机/电机PWM生成 |
| 执行机构管理 | 混控分配(力矩→各执行机构指令)、执行机构健康反馈、PWM/总线输出 | 控制律计算 |
| 任务管理 | 飞行模式状态机(手动/定高/悬停/返航)、任务调度与模式切换逻辑 | 具体控制算法 |
| 健康监测 | 模块心跳监控、传感器数据合理性检查、执行机构卡死/超限检测、故障上报与降级策略触发 | 故障后的具体控制重构 |
模块间接口定义
接口设计遵循以下耦合约束:单向数据流为主,避免循环依赖;控制模块只依赖导航输出的状态估计,不直接读取传感器;任务管理只向控制模块发送模式指令,不干预控制律内部参数。接口数据均通过结构体传递,定义于公共头文件 fc_interface.h 中。
接口头文件实现
以下头文件定义了模块间传递的核心数据结构。每个结构体对应一条接口数据流,字段命名与单位在注释中明确标注,避免歧义。
// fc_interface.h — eVTOL飞控模块间接口定义
#ifndef FC_INTERFACE_H
#define FC_INTERFACE_H
#include <stdint.h>
// 传感器管理 → 导航:经过校准和异常剔除后的传感器数据
typedef struct {
uint32_t timestamp_ms; // 数据采集时间(单调时钟)
float accel_mps2[3]; // 加速度计,m/s²,机体坐标系
float gyro_radps[3]; // 陀螺仪,rad/s,机体坐标系
float mag_ut[3]; // 磁力计,μT,机体坐标系
float baro_alt_m; // 气压计高度,m
float gps_lat_deg; // 纬度,度(1e7缩放)
float gps_lon_deg; // 经度,度(1e7缩放)
float gps_vel_mps[2]; // 水平速度(北/东),m/s
uint8_t gps_fix_type; // 0=无, 2=2D, 3=3D
uint8_t sensor_status; // 位0:IMU有效 位1:GPS有效 位2:气压计有效
} sensor_data_t;
// 导航 → 控制:融合后的状态估计
typedef struct {
uint32_t timestamp_ms;
float quat[4]; // 姿态四元数 [w,x,y,z]
float ang_vel_radps[3]; // 角速度(机体)
float pos_m[3]; // 位置 NED,m
float vel_mps[3]; // 速度 NED,m/s
float vel_body_mps[3]; // 速度(机体坐标系)
uint8_t nav_mode; // 0=无效, 1=AHRS, 2=GPS融合
uint8_t nav_health; // 0=正常, 1=降级, 2=无效
} nav_state_t;
// 任务管理 → 控制:飞行模式指令
typedef struct {
uint32_t timestamp_ms;
uint8_t flight_mode; // 0=手动, 1=定高, 2=悬停, 3=返航
float target_pos_m[3]; // 目标位置 NED(悬停/返航时有效)
float target_yaw_rad; // 目标偏航角
uint8_t mode_ack_required; // 1=需要控制模块回执
} mode_cmd_t;
// 控制 → 执行机构管理:期望力矩与推力
typedef struct {
uint32_t timestamp_ms;
float thrust_N; // 总推力指令,N
float torque_Nm[3]; // 机体轴力矩指令,Nm
uint8_t ctrl_mode; // 当前控制模式(与mode_cmd一致)
uint8_t ctrl_health; // 0=正常, 1=饱和, 2=无效
} actuator_cmd_t;
// 各模块 → 健康监测:心跳与状态(统一结构)
typedef struct {
uint32_t timestamp_ms;
uint8_t module_id; // 0=传感器, 1=导航, 2=控制, 3=执行, 4=任务
uint8_t status; // 0=正常, 1=警告, 2=错误
uint16_t exec_time_us; // 最近周期执行耗时
uint16_t loop_period_us; // 模块运行周期
uint8_t error_code; // 模块自定义错误码
} module_health_t;
#endif // FC_INTERFACE_H
模块间调用关系约束
为保证低耦合,各模块只能调用下方接口函数(示意性声明,具体实现由各模块独立完成):
// 模块间允许的函数调用(供架构评审使用)
// 传感器管理模块提供:
void sensor_mgmt_get_data(sensor_data_t *out); // 导航模块调用
// 导航模块提供:
void nav_get_state(nav_state_t *out); // 控制模块调用
// 任务管理模块提供:
void mission_get_mode_cmd(mode_cmd_t *out); // 控制模块调用
// 控制模块提供:
void ctrl_get_actuator_cmd(actuator_cmd_t *out); // 执行机构管理调用
// 健康监测模块提供:
void health_monitor_report(module_health_t *report); // 所有模块周期调用
落地与验证方法
落地步骤:
- 新建
fc_interface.h,将上述头文件内容完整保存。 - 各模块开发时
#include "fc_interface.h",只通过结构体指针接收输入、通过函数接口输出结果。 - 在代码评审中,对照模块划分表检查:传感器管理是否只输出
sensor_data_t;控制模块是否只读取nav_state_t和mode_cmd_t,不直接访问传感器结构体。
验证方法与判定标准:
- 编译验证:编写一个空测试文件
#include "fc_interface.h",用gcc -fsyntax-only fc_interface.h编译,无报错即通过类型定义检查。 - 耦合规则检查:在代码评审中逐文件核对 include 关系——导航模块不得 include 执行机构管理头文件,控制模块不得 include 传感器驱动头文件。若出现反向依赖,判定架构违规。
- 数据流完整性测试:编写桩函数模拟传感器数据输入,调用
sensor_mgmt_get_data()后检查输出结构体字段是否与输入一致(仅验证接口通路,不验证算法精度)。 - 判定通过标准:上述编译通过、include 方向正确、桩函数数据通路无丢失,即视为模块边界划分有效。