拓冰建站拓冰建站
首页 / 资讯中心 / 正文

卷积在数字控制中的核心原理与工程实现

1. 卷积的“翻转直觉”与物理前提1.1 卷积公式到底在描述什么如果你去看一本教材卷积的定义通常是这样的y(t) ∫ x(τ) h(t-τ) dτ很多学控制、学电气的人第一次看到这个式子时都会本能地想“这个式子是怎么来的”而不是“这个式子要我怎么算”。这其实是学卷积最容易卡住的地方。我建议你别盯着积分符号看先在脑子里建立一个画面一个输入信号是一连串“事件”系统是一块“有记忆的材料”当前时刻的输出是当前事件和所有历史事件共同造成的叠加结果。不是当前输入单独决定输出而是系统把过去所有输入的“余威”都保留了下来并在当前时刻一起释放。举个例子。你往水面上丢石子水面会泛起涟漪涟漪会逐渐衰减。假设你每隔几秒丢一颗大小不同的石子那某一时刻水面最高的波纹绝对不只是“刚刚那颗石子”造成的而是前面所有石子留下的涟漪在那个时刻的叠加状态。每颗石子激起的涟漪形状都差不多只是激发时刻不同、幅度不同。这个“涟漪形状”就是系统的冲激响应 h(t)而实际水面波动就是输入和冲激响应的卷积。有了这个画面你再看卷积公式就不会慌了x(τ) 表示 τ 时刻那颗石子有多大h(t-τ) 表示 τ 时刻的事件传播到当前 t 时刻还剩下多少影响把所有 τ 都积分起来就是 t 时刻的总响应。这件事儿成立有个大前提系统得是线性的、时不变的。线性保证了多个输入可以分开计算再叠加时不变保证了每次丢石子激起的涟漪形状都一样。如果系统是时变的或者里面藏着非线性环节卷积公式就不再适用这是一线工程里最容易忽略的先决条件。1.2 冲激响应系统给输入的“记忆盖章”冲激响应 h(t) 不是随便一个函数它是系统对单位冲激信号的完整响应。它本质上是系统“记忆特征”的完整记录哪些历史时刻影响大哪些影响小影响能持续多久中间有没有振荡全都写在 h(t) 的形状里。在数字控制里你经常听到“系统的脉冲响应”“看响应曲线”这类说法。给实际系统加一个窄脉冲记录输出得到的就是该系统在离散状态下的冲激响应。拿到冲激响应之后系统对任意输入的响应都可以用卷积算出来这是控制理论里“黑箱辨识”的一块基石。所以冲激响应不是只在教科书里出现的东西它是系统给自己输入的“记忆盖章”。你给系统什么样的历史系统就按这个章往回翻阅并决定当前输出。1.3 翻转的含义因果性和时间箭头很多动图演示卷积时都会把 h(τ) 先翻转成 h(-τ)再平移成 h(t-τ)。很多人不理解为什么要多此一举。我换个说法你就懂了一个系统若是物理可实现的它就必须是因果的也就是说现在的输出只能依赖过去和当前的输入不能依赖未来的输入。计算 t 时刻的输出时能参与贡献的输入都在 τ ≤ t 这一侧。h(t-τ) 里的负号正是为了把“过去的输入”映射到 h 的正时间轴方向让每一个历史输入都能对应到“它经过多长时间传播、衰减了多少”的正确系数上。换句话说翻转不是数学上的花架子它是在强制表达“时间箭头”过去影响现在未来不影响现在。你在做数字控制实现时这个“翻转”在代码里表现为从缓冲区里从新到旧倒序取数据而不是正序取。这个细节我在后面讲实现时会再提因为很多初写滤波器的工程师就是栽在这里。2. 数字控制里的卷积从连续积分离散成实时求和2.1 离散卷积和数字控制器的实时计算数字控制器里的所有信号都是采样后的序列。连续卷积的积分变成求和就得到离散卷积y[n] ∑_{k0}^{N-1} h[k] · x[n-k]这里 h[k] 就是离散系统的冲激响应序列x[n-k] 是当前时刻往前数第 k 个采样周期的输入值y[n] 是当前输出。控制工程师看到这个公式第一反应应该是“这不就是一个加权滑动平均吗”。对它本质上就是把历史上 N 个输入值分别乘以各自的权重再相加权重正好是冲激响应 h[k]。数字控制里最常见的 FIR 滤波器、各种平滑滤波器、陷波器本质都是这个公式。实现上它也很朴素每来一个新采样把它存进历史缓冲区把缓冲区里从新到旧的样本和 h[0], h[1], …, h[N-1] 对应相乘并累加输出一个 y[n]。这个过程在实时控制的中断服务函数里跑每个采样周期执行一次。2.2 差分方程、冲激响应、Z传递函数是一棵树的三个分支同一个线性时不变离散系统可以用三种方式描述差分方程、冲激响应、Z传递函数。很多人学的时候把它们当三种东西其实它们是同一棵树上的三个分支。差分方程直接描述输入输出序列之间的递推关系写代码最方便。冲激响应系统对单个脉冲的完整响应序列描述系统“记忆”的全貌。Z传递函数把所有序列变换到复频域把差分方程变成代数方程把时域卷积变成复频域乘法。这里最关键的就是卷积定理时域里做卷积Z域里做乘法。也就是说 Y(z) H(z) · X(z)。你设计控制器时用的传递函数本质就是在频域里把输入信号变换成输出信号。到实现时再通过差分方程或离散卷积回到时域。这也是为什么做控制的人经常在传递函数、冲激响应、差分方程之间来回切换因为它们描述的是同一个东西。你在一个扭矩环里加的低通滤波写出来可以是一阶差分方程可以是一串冲激响应系数也可以是一个关于 z 的传递函数三者完全等价。2.3 手算一个一阶低通把纸上公式变活为了把上面的概念落到看得见的地方我们算一个数字控制中最常见的一阶低通滤波器。假设速度环采样周期 Ts1ms想要一个时间常数 τ10ms 的低通用一阶后向差分离散化得到差分方程y[n] a·x[n] (1-a)·y[n-1]其中 a Ts / (τ Ts) 1/11 ≈ 0.0909。它的冲激响应是h[n] a·(1-a)^nn≥0这个冲激响应是无限长的所以对应的滤波器是 IIR 结构。但如果把它截断成前面 M 个系数就变成了一个 FIR 滤波器也就是直接做离散卷积y[n] ≈ ∑_{k0}^{M-1} a·(1-a)^k·x[n-k]当 M 取得足够大时截断误差小到可以忽略。这就很直观地解释了数字控制里两个滤波器家族的关系IIR 是有反馈的无限冲激响应FIR 是纯卷积的有限冲激响应在工程上二者可以互相近似。区别只在计算量、相位特性、稳定性边界这些实际指标上下面会详细说。3. 从公式到MCU环形缓冲区、定点化与FIR/IIR取舍3.1 环形缓冲区实现FIR卷积代码要怎么写真正写 FIR 滤波器时最忌讳每来一个新样本就把数组整体移动一位那样既慢又容易出错。正确做法是维护一个环形缓冲区每个采样周期只覆盖最老的那个样本然后用一个指针倒退遍历历史数据。这段代码是我在嵌入式电机控制里常用的形式#define N_TAPS 64 typedef struct { float buf[N_TAPS]; int idx; } ring_buffer_t; float fir_process(ring_buffer_t *rb, const float coeff[N_TAPS], float x) { rb-buf[rb-idx] x; int pos rb-idx; float y 0.0f; for (int k 0; k N_TAPS; k) { y coeff[k] * rb-buf[pos]; pos (pos 0) ? N_TAPS - 1 : pos - 1; } rb-idx (rb-idx 0) ? N_TAPS - 1 : rb-idx - 1; return y; }注意几个关键点pos 是倒着走的因为卷积公式里取的是 x[n-k]k 越大取的历史样本越老。这个“倒着取”就是我在第 1.3 节说的“时间翻转”在代码里的体现。环形缓冲区长度必须严格等于 N_TAPS多一个少一个都会读越界。每次调用传入系数数组时要确保系数按 h[0], h[1], …, h[N-1] 的顺序排列不要倒序传进去。这个代码很简单但很多从 Simulink 自动生成代码转过来的同事第一次看都觉得绕。你自己写一遍、在内存里把每个采样时刻的 idx 和 pos 演变画出来就再也忘不了。3.2 FIR与IIR卷积家族的两种形态搞数字控制总要在 FIR 和 IIR 之间做选择。我见过不少工程师要么迷信 FIR“稳定”要么觉得 IIR“省事”。实际选型要看具体环路的指标。维度FIR非递归卷积IIR递归差分方程冲激响应有限长无限长稳定性始终BIBO稳定极点需在单位圆内相位可以做成线性相位通常为非线性相位计算量与抽头数N正相关通常低阶即可存储量需要N个历史样本只需几个历史样本典型控制场景振动抑制、高精度前馈电流环、速度环的常规滤波在电流环和速度环里一阶或二阶 IIR 低通几乎成了标配因为计算量小、相位滞后可控、实现简单。但如果你要抑制某个特定频率的机械谐振用 FIR 陷波器或带通滤波器反而更容易设计出对称的幅频响应也不会因为极点位置不当而产生数值不稳定。还有一个容易被忽略的点在级联系统里FIR 的群延迟是固定的 (N-1)/2 个采样周期便于你做相位补偿。IIR 的相位曲线是非线性的尤其在转折频率附近会吃掉更多相位裕量。所以设计高带宽控制器时我一般会用 FIR 做前馈路径上的整形用 IIR 做反馈路径里的平滑。3.3 定点平台上的溢出、截断和群延迟很多电机控制器用的 MCU 不一定有 FPU或者 FPU 频率受限这时候 FIR 就要定点化。定点卷积的常规做法是把浮点系数乘以 2^Q 四舍五入成有符号整数。每个采样周期做乘累加时用 64 位中间变量防止累加溢出。最终结果右移 Q 位并做饱和处理而不是简单截断。简单截断会产生直流偏置。比如输入信号一直是正数你滤波出来的平均值会偏低这在速度环里可能表现为静态误差。饱和也是必须的否则右移后如果超出 int16 范围直接截断成错误数值在压缩机电控里可能会让占空比跳变。另外群延迟对数字控制的相位裕量影响非常直接。一个 N 抽头 FIR 的群延迟约是 (N-1)/2 个采样周期对 1kHz 采样、3 抽头 FIR延迟只有 1ms但如果为了滤波效果用了 64 抽头延迟就是 31.5ms这在电机速度环里已经是不可接受的了。所以做控制滤波设计时我总是先算一笔延迟账再定抽头数而不是只看幅频曲线。4. 卷积的“变种家族”转置、空洞、可分离、可变形、门控等一网打尽4.1 步长与填充一维重采样和边界处理的推广近几年卷积神经网络火起来之后卷积这个词被大量包装出现了转置卷积、空洞卷积、深度可分离卷积、可变形卷积、门控卷积一堆名词。很多人误以为这是新发明其实它们都是同一个“滑窗乘加”思想在不同维度和不同约束下的变体。先从最基本的二维卷积说起。图像上的卷积核实际上是在空间维度上滑动的乘法窗口把一个局部邻域的像素乘以核系数并求和得到输出像素。步长(stride)就是窗口每次滑动的格数等价于一维信号处理里的降采样——先算卷积再每隔若干个点取一个输出。填充(padding)是为了处理图像边缘不够算的情况相当于给边界人为补值类似于我们处理有限长历史数据时默认“没有数据就补零”。这里有个很有意思的点深度学习框架里叫“卷积”的操作大多数实现其实是“互相关”也就是默认不做核翻转。在数字控制里你如果把卷积和互相关混用结果会差很多但在深度学习里大家默认这个命名习惯。这个差异在跨领域交流时特别容易引起误解我每次跟算法团队对齐都要先确认“你们说的卷积到底翻不翻转”。4.2 空洞卷积与控制里的“长记忆”滤波空洞卷积也叫膨胀卷积是往核的每个采样点之间插入若干个零从而在不增加参数量的情况下扩大感受野。它的效果相当于用同一个核去“跳过”部分输入位置做乘加等价于一维信号处理里对输入做间隔采样后与短核做卷积。如果类比数字控制空洞卷积有点像“长记忆滤波器”的稀疏版本。比如你想让滤波器覆盖过去 100 个采样周期的信息但不想真的用 100 个抽头那就可以每隔 4 个点抽一个样做加权等效影响时间窗口更长了但计算量和抽头数不涨。代价是某些高频分量会以混叠的方式混进来因为跳过了中间样本等于隐含了欠采样。这个混叠效应在控制里买不到便宜。4.3 转置卷积一个输入点扩散成一片输出转置卷积听起来很高深实际它就是普通卷积的“反方向”操作。普通卷积是把输入上的局部多点聚合到一个输出点转置卷积则是把一个输入点扩散到输出上的局部区域。在图像任务里它常用于上采样或生成模型例如从低分辨率特征恢复高分辨率图像。放在控制场景里最贴近的类比是零阶保持器控制器在低采样率下计算出一个控制量通过保持器把它映射到高采样率执行器上每个输出时刻都收到同一个控制量。转置卷积也是类似的思想只不过它的“保持”函数不是一个矩形而是一个可学习的核可以把它理解成“带形状的零阶保持”。需要澄清一点转置卷积不是数学意义上的逆卷积也不能恢复出卷积前的原始信号。它只是把数据的空间尺寸变大里面的信息被重新分配所以工程上不要指望用它来精确还原信号。4.4 深度可分离卷积可分离核的现代版本深度可分离卷积把标准卷积拆成两步先对每个输入通道单独做空间卷积再用一个 1×1 卷积对通道间做线性混合。这样计算量远小于标准卷积因为标准卷积的参数量是输入通道数×输出通道数×核宽×核高而深度可分离卷积把通道混合和空间卷积拆开了。这个思想在图像滤波里一点都不新鲜。二维高斯核本来就是行和列两个一维高斯核的乘法分离所以你可以先按行卷积一次再按列卷积一次把 O(N²) 的计算量降成 O(2N)。在数字控制的多变量系统里也一样如果控制通道之间的耦合比较弱你可以先把每个通道单独滤波再做通道间混合效果近似于全耦合矩阵滤波但计算量和调试复杂度都大幅下降。4.5 可变形卷积和门控卷积从固定结构到自适应结构可变形卷积在标准卷积基础上为每个采样位置额外学习一组偏移量让卷积核的采样网格随内容变形。这样卷积核不再是固定形状而是能绕开不感兴趣的区域、聚焦到关键区域。但从系统角度来看一旦核的形状本身随输入变化系统就不再是线性时不变的不能用传统控制理论去分析它的频率响应和稳定性。这一点做算法和做控制的工程师要心里有数。门控卷积是在卷积结果外面乘一个由另一个分支算出来的门控信号相当于根据输入内容动态调节每个位置的权重。这个思路在控制工程里有点像“增益调度”不同工况下系统使用不同的控制器增益只不过门控卷积把增益做成了逐点连续变化的形式。它逼真但也失去线性系统的理论保障工程落地时需要用大量仿真和实验去验证闭环性能。这些变种听起来五花八门实际上你只要抓住“滑动窗口、对应位置相乘、累加输出”这个核心识别特征就能一眼看出来它们都属于卷积家族区别无非是要不要翻转、步长多大、怎么处理边界、核里插不插零、核的形状变不变、最终输出和输入的维度是否一致。5. 数字控制工程中卷积的实战避坑指南5.1 边界效应启动时的那一下跳动用卷积做实时滤波最容易被忽略的是初始阶段的历史数据不足。缓冲区在启动时全是零滤波输出会从零慢慢爬升到真实值这在控制回路里就表现为开机瞬间输出的一次扰动。很多时候电机一上电、速度环轻微往前窜一下就有这个原因。解决办法不难启动时不要把缓冲区初始化为零而是把当前采到的第一个实际值填满整个缓冲区或者滤波器启动后延迟一拍投入给 PID 一个稳定的初始化时间。如果做的是离线分析也要明确自己用的是 full、same 还是 valid 模式不同模式在边界处的结果差异很大别拿默认输出当最终结论。5.2 别把互相关当卷积互相关和卷积长得像只差一个翻转。互相关公式里没有翻转常用于时延估计、模式匹配。系统辨识里也常用它给系统输入白噪声计算输入和输出的互相关可以得到与冲激响应成正比的结果。问题在于很多代码库把“卷积”和“互相关”实现混在一起名字也一样。你和别人对接口时如果一边默认翻转一边默认不翻转最后估计出的冲激响应是镜像的。这在辨识结果曲线上非常明显本来应该在正时间轴出现的峰值跑到了负方向或者响应形状左右颠倒。遇到这种情况先检查是不是把卷积库当互相关用了。5.3 滤波核设计先看频域再回时域抄数字设计控制器里的 FIR 卷积核时不要上来就手动填抽头数值。正确流程是先在频域里明确你想要的通带、阻带、过渡带然后用窗函数法或等波纹设计法生成一组抽头系数最后用 freqz 之类的工具检查幅频和相频曲线是否满足指标。举个例子采样率 1kHz想在 500Hz 以上做衰减过渡带大约 100Hz用 Hamming 窗设计低通 FIR抽头数大约可以按 α·Fs/Δf 估算Hamming 窗对应的常数 α 一般在 3 左右。这样估算出来 N 约 30 左右再实际设计并用频率响应验证。具体 α 取值不同文献略有差异但设计完成后一定要用一维频率响应工具核对一下而不是只看时域系数长什么样。抽头数的选择会直接影响群延迟和计算量所以这是一次三方权衡频域指标、延迟预算、算力预算。这方面最容易翻车的是只按幅频曲线看效果不看相位曲线结果滤波器把相位裕量吃掉闭环稳定性突然变差。5.4 几个值得写进评审清单的细节最后列几个我踩过、也给同事 review 时经常揪出来的点采样周期千万别改。卷积抽头对应的是采样周期个数采样周期一变同一组系数对应的等效时间常数就变了滤波器频率响应整体跑偏。插值后的数据不要用原采样率设计的核直接处理否则相位滞后比预期更糟信号看着平滑闭环反而更容易振。控制律里有饱和非线性时不能简单把所有环节都当成线性卷积来算需要把饱和模块单独建模处理。环形缓冲区长度和抽头数必须严格一致最好在编译期加静态断言避免上线后出现越界读。代码里凡是用了卷积库都要在注释里写清楚这个库内部做了翻转还是没做翻转互相关和卷积混用的问题基本都是从这里引发的。我在实际项目里吃过不少亏后来养成一个习惯新接手一套控制系统源码第一步先搜索滤波器实现把缓冲区、系数顺序、翻转逻辑全部核对一遍再往下看控制回路。很多隐性问题都藏在这里。正文完
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门