4、常用模块库:数学运算、逻辑运算、连续与离散模块、端口与子系统

各位做飞控的朋友,大家好。今天咱们来聊聊Simulink里最常用的一批模块。说实话,我刚开始用Simulink做飞控代码生成时,面对满屏的模块库也是一脸懵。后来踩的坑多了,慢慢就摸清了门道。

这一章,我带你过一遍数学运算、逻辑运算、连续与离散模块,还有端口与子系统。这些都是飞控模型里的"砖瓦",你想想看,再复杂的飞控算法,拆开来看也就是这些基础模块的组合。

常用模块库 数学运算 加减乘除 · 三角函数 逻辑运算 与或非 · 比较器 连续与离散 积分器 · 延迟 · 滤波器 端口与子系统 In/Out · 子系统封装

4.1 数学运算模块:飞控算法的"算盘"

数学运算模块,说白了就是做计算用的。飞控里最常用的有加法、乘法、三角函数、绝对值这些。我个人习惯把数学运算模块分成两类:基本算术高级函数

核心要点:在飞控代码生成中,数学运算模块会直接映射为C代码中的算术表达式。比如一个加法模块,生成的就是 a + b。所以你在模型里怎么搭,代码里就怎么算。

举个例子,姿态解算里经常要用到四元数归一化。你需要计算 sqrt(q0^2 + q1^2 + q2^2 + q3^2),然后做除法。这时候就要用到 Math Function 模块,选择 sqrtreciprocal

我在项目中遇到过一个问题:用 Product 模块做矩阵乘法时,默认是元素乘(Element-wise),不是矩阵乘。如果你要做真正的矩阵乘法,得把 Product 模块的 Multiplication 属性改成 Matrix(*)。这个坑我踩过一次,仿真结果完全不对,查了半天才发现是乘法类型搞错了。

模块名称 用途 飞控典型场景
Add / Sum 加减法 PID控制器误差计算
Product 乘法/矩阵乘 坐标变换矩阵运算
Math Function sqrt, sin, cos, exp等 四元数归一化、三角函数计算
Gain 增益/比例系数 PID比例项、标度变换
Abs 绝对值 限幅保护、死区判断

小技巧:Gain 模块时,如果增益是常数,直接在模块里填数值就行。但如果增益需要在线调整(比如PID参数),我建议用 Inport 从外部传入,别写死在模块里。这样调试时方便多了。

4.2 逻辑运算模块:飞控的"决策大脑"

逻辑运算模块,说白了就是做判断用的。飞控里经常需要根据条件切换控制模式,比如手动模式切自动模式、故障保护触发等。

常用的逻辑模块有:Logical Operator(与或非)、Relational Operator(大于、小于、等于)、Switch(选择开关)。

嗯,这里要注意一点:Switch 模块的阈值判断逻辑。默认是 u2 >= Threshold 时输出 u1,否则输出 u3。但很多人容易搞反,以为大于阈值时输出 u3。我刚开始也犯过这个错,仿真时模式切换总是不对劲。

避坑指南:我曾经在一个飞控项目里用 Logical Operator 做模式切换条件判断,结果生成的代码里出现了大量的 if-else 嵌套。后来我改用 Switch 模块配合 Relational Operator,代码结构清晰多了。记住:逻辑模块用多了,代码的可读性会下降。

举个例子,你想实现一个简单的故障保护:当陀螺仪数据异常(比如角速度超过 500 deg/s)时,强制切换到安全模式。你可以这样搭:

1. Relational Operator: 角速度 > 500 → 输出 true/false
2. Switch: 用上面的结果控制,true 时输出安全模式指令,false 时输出正常指令

你想想看,这个逻辑是不是很直观?在Simulink里搭出来,生成的C代码也一目了然。

4.3 连续与离散模块:时间维度的"魔法"

飞控系统本质上是离散的——单片机按固定周期运行。但物理世界是连续的。所以Simulink里提供了连续模块和离散模块,用来模拟不同的时间行为。

连续模块:比如 Integrator(积分器)、Derivative(微分器)。这些模块在仿真时用连续时间步长计算,但在生成代码时,Simulink会自动把它们离散化。

离散模块:比如 Unit Delay(单位延迟)、Discrete-Time Integrator(离散积分器)。这些模块直接对应离散时间系统,生成代码时就是纯粹的差分方程。

核心要点:做飞控代码生成时,我强烈建议全部使用离散模块。为什么?因为连续模块在生成代码时,Simulink会帮你做离散化,但离散化的方法(比如欧拉法、梯形法)可能不是你想要的。用离散模块,你自己控制采样时间和积分方法,心里更有底。

我记得有一次做四旋翼的角速度环,用了连续积分器,仿真效果很好。但生成代码后,实际飞起来发现积分项有漂移。查了半天,原来是Simulink默认用的离散化方法不适合我的采样率。后来换成 Discrete-Time Integrator,手动设置采样时间和积分方法,问题就解决了。

模块名称 类型 说明
Integrator 连续 连续积分,代码生成时自动离散化
Derivative 连续 连续微分,对噪声敏感,慎用
Discrete-Time Integrator 离散 手动设置采样时间,推荐使用
Unit Delay 离散 延迟一个采样周期,相当于 z^{-1}
Memory 离散 保持上一个时刻的值,类似 Unit Delay

小技巧:Discrete-Time Integrator 时,记得设置 Initial condition(初始值)。比如积分项的初始值一般设为0。另外,Limit output 选项可以加积分限幅,防止积分饱和。我在做PID控制器时,这个功能帮了大忙。

4.4 端口与子系统:模块化的"乐高积木"

端口与子系统,说白了就是让模型变得整洁、可复用的工具。飞控模型通常很复杂,如果所有模块都堆在一个画布里,那画面太美我不敢看。

InportOutport:子系统的输入输出端口。你可以在子系统里放 Inport 和 Outport,然后在顶层模型里连线。这样每个子系统就像一个黑盒子,只关心输入输出,内部逻辑封装起来。

Subsystem:子系统。你可以把一组相关的模块打包成一个子系统。比如把姿态解算、位置控制、电机混控分别做成子系统。

Atomic Subsystem:原子子系统。这个比较特殊,它会强制生成一个独立的函数。我建议在代码生成时,把关键功能模块用 Atomic Subsystem 封装,这样生成的C代码函数边界清晰,便于调试和测试。

核心要点:用子系统做模块化设计,不仅让模型好看,更重要的是代码可读性和可维护性。我见过一个飞控模型,所有逻辑都平铺在顶层,足足有200多个模块。后来我花了一天时间重构,拆成10个子系统,调试效率提升了一倍。

举个例子,你想做一个姿态控制器子系统:

1. 创建 Subsystem,命名为 "Attitude_Controller"
2. 添加 Inport: 期望姿态角 (roll_ref, pitch_ref, yaw_ref)
3. 添加 Inport: 实际姿态角 (roll, pitch, yaw)
4. 内部放 PID 控制器逻辑
5. 添加 Outport: 控制输出 (roll_cmd, pitch_cmd, yaw_cmd)

这样,顶层模型里只需要连几根线,清晰明了。而且这个子系统可以复用到其他项目里,改改参数就能用。

避坑指南:我曾经在一个项目里用了 Virtual Subsystem(虚拟子系统),结果生成的代码里所有函数都内联到了主循环里,代码量爆炸。后来换成 Atomic Subsystem,每个子系统生成独立的函数,代码结构瞬间清爽了。记住:做代码生成,尽量用 Atomic Subsystem。

好了,这一章的内容就到这里。数学运算、逻辑运算、连续与离散模块、端口与子系统,这四个模块库是飞控模型的基础。你把这些玩熟了,后面搭复杂的飞控算法就会顺手很多。


专注资料整理