news 2026/9/21 2:35:23

连续小波变换原理详解:从傅里叶死穴到Python时频图实操

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
连续小波变换原理详解:从傅里叶死穴到Python时频图实操

站在信号处理这个行当里摸爬滚打这些年,我越来越觉得“连续小波变换(CWT)”是个被低估的工具。很多人一听“时频局部分析”就觉得高深,其实它解决的是一个特别接地气的问题:傅里叶变换能告诉你信号里有什么频率,却说不清这些频率是什么时候出现的。而CWT就是把“频率随时间变化”这张图给画出来。这篇东西写给正在做故障诊断、生物电信号分析、地震勘探,或者单纯被时频分析搞得头大的朋友,我会从傅里叶的死穴讲起,把CWT的原理、参数、代码和坑一次说清楚。


1. 为什么傅里叶变换做不了“局部”分析

1.1 傅里叶变换的本质:把时间抹平掉的频谱

傅里叶变换的核心思想,是把一段信号分解成无穷多个正弦波的叠加。数学上长这样:

[ X(f) = \int_{-\infty}^{+\infty} x(t) e^{-j2\pi ft} dt ]

这个公式干了一件很“暴力”的事:它把时间变量 t 直接积分掉了。积分意味着什么?意味着信号在每一个时刻的大小,都被叠加到了同一个结果里。比如一段10秒的信号,前9秒是50Hz的纯音,最后1秒突然变成了200Hz的纯音。傅里叶变换看过去,只会告诉你“哦,这个信号里有50Hz和200Hz的成分”,至于200Hz是出现在开头还是结尾,它一概不知。

我经常跟人打比方:傅里叶变换像是把一整首交响乐搅拌成一份“食材清单”,清单上写着“有小提琴、有鼓、有钢琴”,但谁先响、谁后响、谁在哪一秒独奏,完全看不出来。对于平稳信号,这根本不是问题,反正频率成分从头到尾都一个样。可现实世界的信号——语音、机械振动、心电、地震波——几乎没有一个是平稳的。轴承磨损初期的高频冲击,只会在特定转角出现;癫痫发作时的棘波,也只是偶尔蹦出来。这些瞬态信息才是诊断的关键,而傅里叶变换恰恰把它们“平均”没了。

这就引出了时频分析的核心诉求:我们既要频率信息,又要时间信息,而且还要知道“某个频率成分到底在什么时刻活跃”。

1.2 短时傅里叶变换(STFT)和它的窗口困境

傅里叶做不了局部,那直接加个窗口不就行了?这就是短时傅里叶变换(STFT)的思路:用一个小窗口把信号切成一段一段,对每一段分别做傅里叶变换,然后把结果按时间顺序排成一幅“时间-频率”图。公式是:

[ STFT(t, f) = \int_{-\infty}^{+\infty} x(\tau) w(\tau - t) e^{-j2\pi f\tau} d\tau ]

窗口 w 的宽度决定了时间分辨率和频率分辨率的平衡。窗口越短,时间定位越准(能看到瞬态发生在哪一刻),但频率分辨率越差(频谱被拉宽,两个靠近的频率分不开);窗口越长,频率分辨得越清楚,但时间上就模糊了,短时间内发生的突变会被“摊”进整个窗口里。这个矛盾就是海森堡测不准原理在信号处理里的表现,一旦窗口长度固定,整个时频图上所有位置的频率分辨率都一样,不管信号是低频还是高频。

这种固定分辨率的做法非常不划算。现实问题里,低频成分往往持续很长时间(比如机器的转频),需要频率精度高一点;高频冲击往往只存在一瞬间(比如轴承点蚀的脉冲),需要时间精度高一点。STFT用一个固定窗口,等于逼你在“看整体”和“看细节”之间二选一,选完了还不能反悔。我当时第一次用STFT分析轴承振动信号时,为了看那个高频脉冲,窗函数选短了,结果转频附近的两个谐波糊成了一个;窗加长了,脉冲时间点又对不准,来回试了十几组窗口参数,最后只能折中。这就是STFT的先天缺陷——窗口长度是死的。

那有没有一种办法,让窗口自己会伸缩:看高频时自动变窄,看低频时自动变宽?有,这就是连续小波变换。


2. 连续小波变换的核心思路

2.1 小波到底是什么:有限长的波

“小波”(wavelet)这个词本身就挺直白——“小的波”。傅里叶变换用的基函数是正弦波,从负无穷延伸到正无穷,永不衰减。小波不一样,它是一个有限长度、平均值为零的波形,能量集中在一小段区间内,这段区间之外的振幅迅速衰减到零。

这个“有限长”是革命性的。正弦波像一盏永不熄灭的灯,照得整个房间都很亮,却分不清光是从哪个方向来的;小波像手电筒,光束集中,指哪打哪。用小波去做分解,就能在“某个时间位置”上看到“某个频率成分”的活跃程度。

常见的母小波有几种:Morlet小波是一个复值小波,形状是复正弦乘以高斯包络,适合分析振荡型信号,也是使用最广的;墨西哥帽小波(Ricker小波)是高斯函数的二阶导数,形状像一顶倒扣的帽子,适合检测脉冲和突变;Haar小波是个方波,虽然简单,但光滑性差,时频图很难看。选哪种小波没有绝对标准,要看信号形态:信号里是持续振荡就用Morlet,信号里是尖锐冲击就试试墨西哥帽,这个选择直接决定时频图的质量,后面我会详细讲。

2.2 CWT的数学定义:尺度与平移

连续小波变换的公式是:

[ CWT_x(a, b) = \frac{1}{\sqrt{|a|}} \int_{-\infty}^{+\infty} x(t) \psi^*\left(\frac{t-b}{a}\right) dt ]

这里的 \psi(t) 是母小波,a 是尺度参数(scale),b 是平移参数(shift),星号表示共轭。这个公式的直觉是:把母小波 \psi(t) 在时间轴上挪到位置 b,再拉伸或压缩成尺度 a,然后和信号 x(t) 做内积。内积越大,说明在这个位置、这个尺度下,信号和小波的相似度越高。

尺度参数 a 和频率是反向关系:a 越大,小波在时间上被拉得越宽,频率越低;a 越小,小波被压得越窄,频率越高。这正是“自适应窗口”的来由——当你在高频段扫查时,小波的等效窗口自然就变窄了。

这里有个关键点值得掰开揉碎:CWT的小波是对同一个母小波做“缩放+平移”得到的,所以本质上它的多分辨率是“内嵌”在基函数里的。STFT的窗口长度固定,频率分辨率和时间分辨率全局一致;CWT的窗口自动随尺度变化,低频段窗口宽、高频段窗口窄。这意味着同一幅时频图上,低频端的频率精度高,高频端的时间精度高——这正是现实信号分析最需要的分配方式。我第一次跑出CWT时频图时,看到低频段几条谱线清楚分开、高频段脉冲位置精确对应,真有那种“这玩意早该用上”的感慨。

2.3 尺度参数到频率的换算

用CWT最大的一个困惑就是:我把尺度 a 从1取到128,画出来的图横坐标是尺度,不是频率,怎么和实际故障特征频率对上?这中间隔着一个换算公式。

对于复数Morlet小波,中心频率为 fc(母小波本身的频率,通常在0.8到1.5之间),采样率为 fs,尺度 a 对应的“伪频率”是:

[ f_a = \frac{f_c \cdot f_s}{a} ]

举个例子,如果 fs = 1000 Hz,Morlet的中心频率 fc = 1,那么尺度 a = 100 对应的频率是 10 Hz,尺度 a = 10 对应的是 100 Hz。也就是说,如果你关心的是 1 Hz 到 100 Hz 的低频范围,尺度就要取 10 到 1000;如果你关心的是 100 Hz 到 1000 Hz,尺度就要取 1 到 10。

换算逻辑想清楚之后,选尺度范围就变成了一道简单的除法题:先确定目标频率区间下限 f_min 和上限 f_max,再反推尺度范围:

[ a_{max} = \frac{f_c \cdot f_s}{f_{min}}, \quad a_{min} = \frac{f_c \cdot f_s}{f_{max}} ]

很多人懒得算这步,直接用默认的尺度范围跑,结果画出来的图频率轴根本对不上实际物理频率,这是CWT入门最常见的翻车点。在这个问题上,花两分钟算一下尺度范围,能让后面省下几个小时的分析时间。


3. CWT实操:用Python把时频图跑出来

3.1 环境准备和信号构造

工欲善其事必先利其器。CWT在Python里有两条路:一条是用 SciPy 的scipy.signal.cwt,底层是自己传小波函数;另一条是用 PyWavelets(pywt)库,内置各种小波和连续小波变换的封装。我个人习惯用 pywt,因为内置小波种类多,且直接支持返回复数小波的结果,比手写小波函数省事。

先造一段测试信号。我建议用“chirp+突变”组合:一个频率线性扫频的chirp信号,中间叠加一个短时高频脉冲。这种信号是检验时频分析算法最好的试金石——chirp能看出频率分辨率,脉冲能看出时间分辨率。

import numpy as np import matplotlib.pyplot as plt import pywt fs = 1000.0 t = np.linspace(0, 1, int(fs), endpoint=False) # 频率从10Hz线性扫到100Hz的chirp信号 f0, f1 = 10, 100 chirp = np.sin(2 * np.pi * (f0 * t + 0.5 * (f1 - f0) * t**2)) # 在0.5秒处叠加一个200Hz的短时脉冲 pulse = np.zeros_like(t) idx_pulse = int(0.5 * fs) pulse[idx_pulse:idx_pulse+30] = np.sin(2 * np.pi * 200 * np.arange(30) / fs) * np.hanning(30) signal = chirp + pulse

这段信号的第一个成分是平滑扫频,第二个成分是0.5秒处的一个极短振荡。拿它去跑STFT和CWT,能非常直观地看出两种算法在一个固定窗口下有多尴尬,而CWT又怎么避开这个坑。

3.2 用pywt实现CWT的核心代码

pywt的cwt函数用法很直接:传入信号、尺度数组、小波名,返回小波系数矩阵和对应的频率数组。

scales = np.arange(1, 128) coeffs, freqs = pywt.cwt(signal, scales, 'cmor1.5-1.0', sampling_period=1/fs) # 画时频图 plt.figure(figsize=(10, 6)) plt.imshow(np.abs(coeffs), extent=[0, 1, freqs[-1], freqs[0]], aspect='auto', cmap='jet') plt.colorbar(label='Magnitude') plt.ylabel('Frequency (Hz)') plt.xlabel('Time (s)') plt.title('CWT Scalogram') plt.ylim(0, 250) plt.show()

'cmor1.5-1.0'这个字符串值得多说几句:cmor代表复数Morlet小波,1.5是带宽参数,1.0是中心频率。pywt里小波命名的格式是“名字+带宽-中心频率”,带宽越大,小波在频域的支撑越宽,频率分辨率越高,但时间分辨率变差;中心频率就是前文公式里的 fc,直接参与尺度到频率的换算。

这里有个特别容易踩的坑:pywt.cwt的返回值里,freqs是按尺度从小到大排列的,但小波系数的行顺序也是尺度从小到大。如果用extent参数画图,freqs的顺序必须和矩阵行对齐。freqs[0]对应的是最小尺度,也就是最高频率;freqs[-1]对应最大尺度、最低频率。如果你照抄网上的代码却把extent写成[0, 1, freqs[0], freqs[-1]],图就会上下颠倒,频率轴直接反了。我见过不止一个同事在这种细节上浪费了半天,就为了查“为什么时频图和想象的不一样”。

3.3 小波选择与参数调节经验

小波类型和参数的选择,我总结了几个原则,基本能覆盖绝大多数分析场景:

先看信号形态。如果你分析的是振动、声音、脑电这类振荡信号,首选复Morlet小波;如果你分析的是冲击脉冲、雷击、地层反射这类瞬态信号,墨西哥帽小波('mexh')往往更合适。复Morlet的好处是能同时给出幅值和相位信息,做包络分析很方便;墨西哥帽是实值小波,只有幅值,但对脉冲敏感,定位更锐利。

再看带宽参数。Morlet的带宽参数决定小波在频域里的宽度。带宽越大,频率分辨率越高,适合需要区分相邻频率分量的场景;带宽越小,时间分辨率越高,适合定位突变时刻。经验上做机械故障诊断时带宽取1.5比较均衡,做语音分析时可以试着调高到2.0以上,但这个参数没有通用最优值,必须结合信号特征反复试。

最后记住一个原则:时频图不是越“亮”越好。很多人看到结果里一片红色亮带就兴奋,其实那往往是小波参数不匹配导致的能量扩散,真正的特征应该是“清晰、局部、可解释的”。


4. CWT与STFT的对比实验

4.1 同一条信号、两种算法的效果对比

拿上面那段chirp+脉冲信号,分别用STFT和CWT跑一遍,差异非常明显。STFT选一个居中的窗口长度(比如128个点),跑出来的时频图里,chirp谱线是中段的直线,但在低频段和高频段两端都可能发糊,而那个0.5秒处的脉冲在图上被拉成一个竖直的宽线,时间上没法精确定位到0.5秒这个点。

同样信号跑CWT(尺度从1到128、Morlet小波),chirp谱线从头到尾都清晰连续,频率越高谱线越细;脉冲则在时频图上变成一个尖锐的垂直短线,时间位置正好卡在0.5秒处。这就是CWT的“自适应”带来的直接收益:高频细节用高时间分辨率去看,低频趋势用高频率分辨率去看,不用人为选窗口。

对比代码也很简单,STFT可以用SciPy的signal.stft

from scipy.signal import stft f_stft, t_stft, Zxx = stft(signal, fs, nperseg=128, window='hann', noverlap=96) plt.pcolormesh(t_stft, f_stft, np.abs(Zxx), cmap='jet', shading='gouraud') plt.ylim(0, 250) plt.colorbar(label='Magnitude') plt.title('STFT Spectrogram')

把两张图放一起看,STFT的固定窗口问题就暴露无遗了:窗口长则脉冲糊,窗口短则低频线糊。你没法让一个窗口同时照顾好两端,但CWT天然就做到了。

4.2 分辨率取舍的直观理解

我常跟人解释,时频分辨率的取舍本质上就是一个“亮度”问题。任何时频分析都不可能无限同时提高时间和频率精度,这是信息论决定的规律。STFT的选择是“全局统一”:整个时频平面上,所有位置的分辨率都一样,像一个固定焦距的镜头,你只能选聚焦中景还是聚焦特写。CWT的选择是“按需分配”:频率越低镜头焦距越长,频率越高镜头焦距越短,所有位置看起来都是清楚的。

这种自适应特性对工程分析意义很大。举个例子,诊断一台齿轮箱时,转频大概在20Hz上下,齿啮合频率可能在1000Hz上下,两者之间隔了几十倍。用STFT去分析,窗口得把20Hz的几级谐波分清楚,窗口就必须很长,结果高频冲击的时间精度就完蛋了;反过来窗口短了,20Hz附近的谱线糊成一团,连轴频都数不清。CWT则完全没有这个烦恼,低频区自动提高频率精度,高频区自动提高时间精度,一张图全搞定。

当然,CWT不是银弹。它算出来的时频图是冗余的,相邻尺度之间高度相关,直接拿去做定量绝对值的比较,得小心谨慎。下一节我会专门聊这些坑。


5. 常见问题与排查技巧实录

5.1 边界效应:时频图两端的“假信号”

CWT最容易被忽略的问题,是信号边界处的小波系数完全没有可信度。原因很容易理解:小波在尺度较大时宽度很宽,在信号开头处做内积时,小波会超出信号范围,超出部分没有数据可取,算法只能用补零或某种延拓方式去凑。凑出来的结果反映的不是真实信号,而是边界假象。

这种效应在时频图上表现为左右两侧各有一块“锥形”区域,尺度越大(频率越低)锥形越宽。我在一份实测数据里就栽过跟头:一个低频成分在信号末尾本来已经消失了,但时频图末端还亮着一大片,一开始我还以为发现了新特征,后来一查才发现是边界效应。识别和规避这个问题的办法不算复杂:

  • 信号采集时尽量留出足够长的“前导”和“尾巴”,不要从信号开始就急着分析;
  • 分析时把时频图两侧各截掉一部分,宽度参考最大尺度的小波有效支撑长度;
  • 如果主要关心高频瞬态,边界影响通常不大,因为高频对应的小波尺度小,宽度窄,边界效应范围有限;
  • 高频成分在信号开头或结尾出现时,结果只作定性参考,别用边界区域的幅值去做定量判断。

5.2 小波系数不是“功率”,别直接拿它和FFT幅值比

另一个常见的误区,是把CWT的小波系数振幅直接当成和FFT幅值类似的东西去比较。CWT是冗余变换,系数的大小受小波归一化方式、尺度密度、小波类型影响极大,不同尺度之间、不同小波之间的数值没有直接可比性。

如果你需要从时频图里提取定量指标(比如某个频带的总能量、某个故障特征频率的幅值趋势),建议先做一次“标定”:用一段已知幅值、已知频率的纯正弦信号,跑到CWT里,找到对应尺度位置的最大系数,反推一个缩放因子。后续分析用这个因子去换算,结果才靠谱。我实验室每次换小波参数,都会先拿正弦标准信号重新标定一遍,这条路走过一次之后,能少掉很多数据解释上的麻烦。

5.3 参数调整速查

我把CWT的参数调试经验整理成一个速查表,方便遇到问题直接对照:

现象可能原因处理方向
时频图整片发糊,看不到清晰谱线尺度间隔太粗增加尺度数量,或改用对数尺度分布
高频细节看不清Morlet中心频率过高、带宽偏小降低中心频率,或增大带宽参数
低频谱线被切断、不连续尺度范围不够大按 f_min 反推增大最大尺度
时频图上下颠倒extent参数顺序错确认freqs首尾顺序,低频在前则frequency轴从高到低
图两侧出现竖向亮带边界效应截掉两侧锥形区域再分析
脉冲定位到了,但频率对不上尺度-频率换算用了默认fc用实际使用的Morlet中心频率重算频率轴
复小波画出的图振幅忽高忽低未取模或未取绝对值画图时对系数取 np.abs()

这些参数里,最核心的还是“尺度范围”和“小波中心频率”这两个。尺度范围决定你看到的频率范围,中心频率决定尺度与频率的映射关系。先算清楚这两项,再谈细调带宽和尺度数量,基本就是CWT调参的标准路径。

5.4 尺度分布:均匀取还是对数取

pywt的cwt函数默认接受一个尺度数组,很多人习惯用np.arange(1, N)均匀取,这在尺度跨度不大时够用。但如果你要分析的频率范围很宽(比如10Hz到1000Hz,跨两个数量级),均匀尺度分布会让低频段相邻尺度对应的频率差很小(冗余度过高),高频段相邻尺度对应的频率差很大(可能漏掉特征)。

更优的做法是采用对数分布,让每个尺度对应频率间隔在相对意义上均匀:

scales = np.logspace(np.log10(2), np.log10(256), 128)

这样低频段尺度密集取值,高频段自然变疏,配合CWT自身的多分辨率特性,时频图在超大频率跨度下看起来更均衡。我处理地震波数据时,频率跨度经常超过三个数量级,对数分布基本是必须的。

5.5 关于“傅里叶作画”的一个小旁注

最近“傅里叶作画”在网上挺火,很多人拿傅里叶变换做图像处理、做艺术效果。这从一个侧面说明:频域思维远不只是“算频谱”那么简单,它和时频分析、图像滤波、压缩编码是同一套哲学的不同应用。CWT在图像里也有对应物,比如二维小波变换常用于图像去噪和边缘检测。如果你被“傅里叶作画”勾起了对频域变换的兴趣,那CWT绝对值得纳入你的武器库——它比单纯傅里叶变换多了一个“局部”维度,而“局部”恰恰是信息最丰富的地方。


最后再分享一个我自己的操作习惯。每次拿到一段新信号,我不是直接跑CWT,而是先看原始波形和FFT频谱,确定目标频段的大致范围,然后反推尺度区间,再跑CWT。跑完第一版时频图,先检查两侧边界和整体背景噪声,确认没有大面积假亮带后,才会去抠具体的故障特征。CWT给了我们一张很“聪明”的放大镜,但放大镜本身不会告诉你该看哪里——知道自己想看什么,永远比工具本身更重要。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 2:32:42

百货零售数字化转型方案拆解:从战略到落地

简介:德勤大型百货零售集团数字化转型解决方案以八十五页演示文稿形式呈现,面向零售企业中高层、数字化转型项目团队及咨询从业者。方案深入分析了零售业从传统模式向数字化、智能化转型的关键时期,覆盖云计算、大数据、物联网等新兴技术带来…

作者头像 李华
网站建设 2026/9/21 2:31:34

torch2trt源码深度解析:从PyTorch到TensorRT的企业级落地指南

先说明一句:这篇文章会以企业技术尽调的口吻来写,所有分析都基于源码实证,不掺水分。torch2trt 这个项目在 PyTorch 转 TensorRT 的生态里名气很大,但真正打开源码逐行读过的团队其实不多。很多同学只是 pip install 之后跑通了 d…

作者头像 李华
网站建设 2026/9/21 2:31:25

Harvey LAB SWEEP_MATRIX完整配置指南:快速掌握模型扫描矩阵

Harvey LAB SWEEP_MATRIX完整配置指南:快速掌握模型扫描矩阵 【免费下载链接】harvey-labs A benchmark built to evaluate and improve agent capabilities for supporting legal work. 项目地址: https://gitcode.com/GitHub_Trending/ha/harvey-labs Harv…

作者头像 李华
网站建设 2026/9/21 2:28:22

用户画像7大维度实战:从数据清洗到标签落地

用户画像这几年已经被说烂了,但真正能把画像做扎实、能直接支撑业务决策的大数据分析师,其实并不多。尤其是旅游网站这类垂直领域,用户的决策链路长、场景碎片化,画像要是只停留在“性别年龄城市”的粗粒度标签,那基本…

作者头像 李华