做信号处理这行MATLAB里那几十个函数真正每天抓在手里用的其实就那么十来个。这篇是这个系列的第三篇我打算把平时处理振动、音频、生物电这类实测信号时用得最频繁的函数挨个过一遍不按教科书目录念定义只讲这些函数在什么场景下选、参数怎么给、出来的结果怎么判。如果你刚开始用MATLAB处理信号或者已经被fft的幅值换算、滤波器的相位问题折腾过一两次这篇文章应该能帮你省下不少时间。1. 测试信号生成先把“干净的输入”造出来信号处理的一切排查都要从信号本身开始。我在处理实测数据之前有个习惯先用已知函数构造一段参考信号把整个流程跑通再换真实数据。这一步看起来简单却是后面所有分析的基准基准错了后面全部白搭。1.1 基础波形生成函数MATLAB里造测试信号最常用的是sin、cos、chirp、square、sawtooth和randn这几个。sin和cos不多说但有两点容易被忽略第一参数单位是弧度采样率、频率、点数三者之间要换算清楚第二生成的结果只是数组不携带任何采样率信息后面做fft时所有频率刻度都得自己手动换算。举个例子生成一段1kHz正弦波采样率16kHz时长0.1秒fs 16000; % 采样率单位Hz t 0:1/fs:0.1-1/fs; % 时间轴单位秒 x sin(2*pi*1000*t); % 1kHz正弦波这段代码本身没问题但我在实际使用中一般会顺手写成一个带参数的函数把fs、f0、时长、幅值全作为入参后面改频率和采样率时不用到处改代码。chirp是扫频信号常用于测系统频响特性它的参数里t、f0、t1、f1需要按调用方式对应清楚。square和sawtooth生成方波和锯齿波这类周期信号在频谱上包含丰富的高次谐波很适合用来验证滤波器是否真的把谐波压掉了。1.2 加噪、拼接与量纲处理实际工作中很少有人只需要一条干净正弦。更多时候我们要模拟带噪声的测量信号最常见的就是高斯白噪声和均匀分布噪声。如果纯粹是叠加随机扰动用randn乘上一个幅度系数就行但如果要做信噪比相关的实验我更推荐直接awgn它可以根据目标信噪比自动计算噪声功率省去自己换算的麻烦SNR 20; % 目标信噪比 20dB y awgn(x, SNR, measured);这里有个细节awgn第三个参数用measured表示按输入信号的实际功率来加噪声比手动指定功率更稳尤其适合信号幅度随场景变化的情况。另一个经常遇到的需求是多段信号拼接。比如前5秒是正常状态信号、后5秒是故障特征信号。直接x [x1; x2]会带来一个阶跃跳变这个跳变在后面做fft时会大幅展宽频谱产生很多虚假分量。正确处理是在拼接处加一小段过渡窗做交叉淡化或者构造一个余弦渐变段让两段信号平滑衔接。量纲问题是最容易被新手忽略的。MATLAB里所有波形都是纯数值没有单位。你处理的是加速度传感器数据幅值的物理含义是m/s²还是g完全取决于换算系数。我习惯在构造测试信号时严格给幅值赋予物理含义这样后面做频谱分析或者阈值标定时才不会出错。提示测试信号生成是整个信号处理链路中最容易偷懒的环节但也是最该认真的环节。我这几年排查过大量“算法不收敛”“滤波结果不对”的问题最后发现有一半以上是构造参考信号时埋的雷。2. 时频域转换FFT分析的正确姿势FFT是信号处理绕不开的核心操作但MATLAB的fft函数太容易上手反而让很多人忽视了它背后的细节。这一节把调用方式、频谱规范化和窗函数一并说清。2.1 fft的调用参数与频谱规范化fft最基本的调用是X fft(x)默认取信号的样本点数作为变换长度。我几乎从不用这种写法而是显式指定点数N length(x); X fft(x, N);当你希望频谱更密一些可以用fft(x, N2)其中N2 N。但必须说清楚补零只能在现有频谱上增加插值密度不能提高真实频率分辨率。真实分辨率取决于信号时长表达式是fs / N。我见过不少人花大力气补十几万个零以为能看到更细的谱线分离实际上只是把原来的谱走势画得更平滑频率分辨率并没有改变。频谱画出来之后有几件事是默认要做的X是复数必须取模abs(X)才得到幅值谱幅值谱关于尼奎斯特频率对称通常只画前半段前半段除了直流分量外所有幅值要乘以2/N才是真实的单频幅值。这个系数是新手最容易错的地方很多人画出来的幅值和理论值差了一倍多半就是没乘这个系数。X fft(x); N length(x); mag abs(X(1:N/21)); mag(2:end-1) 2 * mag(2:end-1); % 直流分量不乘2 f (0:N/2) * fs / N; % 频率轴换算 plot(f, mag);注意横轴频率不是自动生成的值必须用索引乘以fs/N换算成Hz否则画出来的图只在“点数”这个维度上没法对标物理频率。这是我被问得最多的问题之一。2.2 泄漏、窗函数与fftshiftfft假设输入信号在截取时长内是周期的。如果你的信号截取长度不是信号周期的整数倍频谱就会发生泄漏——本应集中在某个频点的能量散到了旁边许多频点。解决的通用手段是加窗给时域信号乘一个窗函数让首尾趋向于零。MATLAB里hann、hamming、blackman、kaiser都提供。我说说自己的选择习惯做普通频谱分析hann或者hamming够用如果两个频率离得很近希望压低旁瓣干扰可以用blackman但主瓣会变宽如果需要旁瓣水平可调用kaiserbeta参数越大旁瓣越低。加窗之后幅值会变化做幅值校正时需要参考窗函数增益这个细节一般文档里不会写需要自己实测标定。fftshift是另一种常见操作它把零频分量从数组起点搬到数组正中画出以0Hz为中心的对称频谱图。有时候为了做频域滤波也会先fftshift、再针对对称频率分量处理、最后ifftshift还原。但一定要记住fftshift是纯数组搬移不改变数值大小只是重新排列顺序。注意fftshift不是为了解决“频率算不对”而存在的它只负责把横轴排列方式从“从0到正频”改成“从负频到正频”。如果发现频率刻度不是想要的先检查f (0:N/2) * fs / N这段换算不要拿fftshift来擦屁股。3. 滤波器设计从butter到filtfilt滤波器的设计函数MATLAB里一大堆但工程上反复使用的其实就那几个。这节的目的是把设计、执行、选型几个关键节点理清楚帮助你少走弯路。3.1 经典滤波器设计函数butter可能是被使用最多的一个它设计巴特沃斯滤波器特点是通带最大平坦、没有波纹但高阶数时过渡带滚降速度一般。调用方式是[b, a] butter(n, Wn, low)其中Wn是归一化截止频率算法是截止频率 / (fs/2)。注意Wn不是Hz是相对尼奎斯特频率的比例。新手经常把500Hz直接写进Wn得到的滤波器实际截止频率完全不是预期结果。如果设计带通Wn是包含两个元素的向量写成[f_low/(fs/2), f_high/(fs/2)]第三个参数bandpass可以写也可以省略。切比雪夫I型cheby1在通带内有波纹但同阶数下过渡带比巴特沃斯陡椭圆滤波器ellip通带和阻带都有波纹但阶数相同时过渡带最窄。如果想尽量减小相位失真还有人会用贝塞尔滤波器但它也同样存在模拟原型和数字转换的问题工程中不如前几种常见。另一个很实用的是fir1它基于窗函数法设计FIR滤波器。FIR最大的优点是严格线性相位不存在相位失真代价是达到同样的滚降速度所需要的阶数远高于IIR。我用fir1的典型场景是做离线数据的带限滤波如果允许一定的群延迟FIR的线性相位特性可以避免波形畸变。3.2 filter与filtfilt的取舍设计完成后要执行滤波。文档一般推荐filter(b, a, x)这是因果滤波输出在时间上相对原始信号有明显相位延迟。如果做实时处理只能用filter因为未来信号不可知因果性是硬约束。但在离线数据分析中我强烈推荐用filtfilt。filtfilt把信号正向滤波一遍再反向滤波一遍两个方向产生的相位移正好抵消实现零相位滤波。代价是它引入了非因果性信号首尾会有额外的过渡处理边界效应比因果滤波更明显实际等效阶数也翻了一倍计算量更大。[b, a] butter(4, 0.2, low); y1 filter(b, a, x); % 因果滤波有相位延迟 y2 filtfilt(b, a, x); % 零相位滤波离线分析推荐我处理离线振动信号提取包络特征时几乎必然会用filtfilt因为相位延迟会让特征位置发生平移影响故障定位的准确性。如果是实时监测系统filter就是唯一选项。还有一类坑是滤波器的数值稳定性。对高阶IIR滤波器直接用[b,a]形式在某些极端频率配比下会出现严重的数值误差。现在MATLAB更推荐用designfilt生成滤波器对象或者借助zp2sos把零极点转换为二阶节形式再用sosfilt执行滤波。我自己在处理高阶带通时一律用二阶节形式能避免很多难以排查的数值问题。4. 功率谱估计与重采样工程里的高频实用函数除了fft直接做频谱分析功率谱密度估计和重采样也是工程中的高频需求。这两类函数选对了效率和分析效果都会好很多。4.1 pwelch与其他谱估计方法pwelch在当前工程分析中基本是标配它基于Welch平均法把长信号分成多段每段加窗、做FFT、计算功率谱再对所有段取平均。这种方式以损失频率分辨率为代价换来了更平滑、方差更小的谱估计。调用时最关键的是几个参数[pxx, f] pwelch(x, window, noverlap, nfft, fs);window可以是整数表示每段长度也可以直接传窗函数向量noverlap是相邻两段重叠的样本数常用比例是50%到75%nfft是FFT点数和窗长一致即可fs用于指定频率轴单位。如果只追求最高分辨率直接fft更合适但如果信号很长、谱波动明显pwelch能压制随机噪声让稳定的谱峰凸现出来。我处理长期采集的轴承振动数据时经常用pwelch观察整体频带能量分布比直接看单帧FFT清晰得多。相关函数里periodogram是pwelch在只分一段时的特例方差大工程实用价值低。spectrogram做短时傅里叶变换输出时间-频率-功率三维结果适合非平稳信号分析但使用逻辑和pwelch不一样后面有机会单独展开。4.2 resample与重采样实操实际工程常遇到采样率不统一的信号比如两个数据集一个采1kHz、另一个采2kHz直接对齐时间轴很麻烦。resample(x, p, q)可以实现带抗混叠滤波的重采样把信号按p/q比例转换。例如从2kHz降到1kHz写resample(x, 1, 2)。它内部会先做插值再做抗混叠低通滤波整体比较稳妥。如果只是单纯降采样downsample很直接但不带滤波高频混叠风险大。正确的做法是先经过低通抗混叠滤波再抽取。MATLAB里decimate自带抗混叠滤波但它内置的滤波器设计在某些过渡带要求高的场景下不够锐利必要时需要手动设计低通滤波器再配合downsample。重采样还需要注意群延迟问题。重采样后的信号起点和末尾会有一段过渡区如果做时间精确对齐建议在重采样后单独估计延迟并补偿否则后续做互相关时会出现时间偏移。5. 常见坑位与排查技巧实录这一节集中整理平时答疑中被问得最多的问题做成速查表再讲几个容易反复踩的细节。5.1 频域分析常见问题速查表现象常见原因解决思路频率轴刻度完全不对fs计算错误或忘记乘以fs/N换算显式计算f (0:N/2)*fs/N核对fs数值幅值比理论值小一半未乘单边谱幅值校正系数除直流外幅值乘以2/N频谱出现很多毛刺信号截断导致泄漏或信号含噪声加窗抑制旁瓣或改用pwelch平均0Hz处有巨大尖峰信号含直流分量或未去均值对x减去mean(x)后再做fft两个近频分量分不开数据长度不够分辨率不足增加信号时长而不是盲目补零滤波器截止频率不对Wn忘记除以fs/2记住Wn是归一化频率不是Hz滤波后波形开头跳变初始状态未设置用filter的初始条件参数zi或接受filtfilt边界效应5.2 容易被反复问到的细节第一个细节是fft结果是复数取模后不要忘了对称性。有人直接画plot(abs(fft(x)))图是对称的但横轴一半信息重复浪费显示区域。标准做法是取前半段或者先fftshift再画对称谱。第二个细节是findpeaks找谱峰时返回的是索引而不是频率值。很多人直接把索引当频率用结果相差几十倍。索引换频率一定要乘fs/N这个步骤看着小错了就是整体偏差。第三个细节是不要盲目追求高阶滤波器。阶数越高过渡带越陡但IIR的相位失真更明显数值稳定性也变差。一般工程先给一个中低阶数看效果不够再逐步提高而不是一上来就设计20阶带通。第四个细节是处理长信号时的内存和速度问题。不要一次性把几十万甚至几百万样本丢给filter建议分块处理用初始状态zi在块之间传递滤波状态。filter(b, a, x, zi)这个带初始条件的形式配合循环分块既能保证滤波连续性又控制了内存开销。我从实际经验里总结出来的排查习惯是每写完一个滤波或FFT段落立刻画图看中间结果不要等整个流程跑完再回头看。信号处理里错误大多是链条式的越早发现越好改。等到最后一步才发现前面某段出了问题定位成本会成倍增加。最后说一个我自己的经验习惯只要发现连续三个参数调完结果仍然怪就回退一步先用一条已知幅值和频率的正弦信号把当前函数跑通确认尺度因子和频率轴没问题再继续。这个小习惯帮我避开了大量低级错误也让代码的可维护性高了不少。下一篇可以接着展开振动特征提取或者音频降噪的具体场景等实际做完再回来分享更多现场细节。