资讯动态

信号处理核心概念解析:自相关、互相关、卷积与相干性

发布时间:2026/10/5 12:32:43 来源:尧图企业网站定制
做信号处理这些年被问得最多的一个问题不是“FFT怎么实现”而是“两个信号到底像不像”。你要是查资料会发现答案一会儿是相关性一会儿是相干性中间还夹着一个怎么都绕不开的卷积。自相关、互相关、卷积、相关性、相干性五个词长得像亲戚实际的关系却经常让人一脸懵。这篇东西我想用一个工程师的习惯把这几个概念摆在一起讲清楚它们分别是干什么的公式怎么来的实际操作中怎么用以及我踩过哪些坑。不管你是刚接触信号处理的学生还是在用Python做数据分析、跑深度学习时遇到卷积和相关的名词这篇文章都适用。我会尽量避开教科书式的推导用“生活类比 代码示例 踩坑记录”的组合把这块硬骨头啃下来。1. 从“像不像”到“有多像”相关性到底在算什么1.1 皮尔逊相关系数用一条直线解释所有相似先看最简单的场景。两个数组 x 和 y长度都是 N你想知道它们之间有没有线性关系。最常用的就是皮尔逊相关系数 rr Σ(x_i - x̄)(y_i - ȳ) / sqrt(Σ(x_i - x̄)² · Σ(y_i - ȳ)²)这个公式干的事情就是先分别把两个序列减去各自的均值让它们都变成“零均值”序列然后算两个零均值序列的“内积”再除以各自能量的乘积开根号。最终结果被限制在 [-1, 1] 之间。r1 表示完全正线性相关r-1 表示完全负线性相关r0 表示没有线性相关。这句“没有线性相关”很关键因为两个变量完全可能是非线性关系比如正弦和它的平方r 可能算出来很小但它们显然有关。实际操作中很多人直接拿原始数据算 r忘了先去均值。虽然公式里已经减了均值但如果你在写代码时用 np.corrcoef 就会自动去均值可一旦你手写公式或者用互相关函数去模拟相关系数就很容易漏掉这一步。我见过不少人用 np.correlate(x, y, modevalid) 算出来一个巨大的数值然后直接当相关系数解读这就是没做零均值和归一化导致的结果。1.2 相关性和“相干性”别急着混为一谈第一次接触“相干性”这个词我以为是相关性的高级叫法。后来做振动台数据分析时才明白相关性和相干性虽然中文只差两个字管的东西完全不同。相关性是在时域或样本空间里两个序列整体的线性关联强度。它给你的是一个“总账”这两个信号在整个时间范围内整体上是不是一起变化。相干性则是在频域里两个信号在每个频率分量上的线性关联强度。它给你的是“明细账”在 20Hz 这个频率上这两个信号的线性关系有多强在 40Hz 上可能完全没关系。如果你把两个信号想象成两个乐队相关性回答的是“这两个乐队整场演出的节奏是不是同步”相干性回答的是“吉他手在哪个频段上跟鼓手配合得最默契”。一个是总体一个是分布。这个区分在工程应用里非常重要尤其是在故障诊断、脑电信号分析、声学测量这些场合只看总账会漏掉太多信息。2. 自相关让信号和过去的自己对话2.1 自相关函数怎么计算自相关函数描述的是一个信号与它自身在不同延迟时刻的相似程度。对于离散序列 x[n]自相关的定义是Rxx[m] Σ_n x[n] · x[n - m]这里的 m 就是延迟量通常叫 lag。如果 m0那就是信号和自身完全不延迟地相乘求和结果就是信号的能量。如果 m 不等于 0就是在问“我把信号往左挪了 m 个点它和自己原来的形态还有多像”。在 Python 里通常用 np.correlate 一步到位import numpy as np import matplotlib.pyplot as plt fs 1000 t np.arange(0, 1, 1/fs) # 10Hz正弦 噪声 x np.sin(2 * np.pi * 10 * t) 0.5 * np.random.randn(len(t)) # 自相关前先去均值 x x - np.mean(x) rxx np.correlate(x, x, modefull) lags np.arange(-(len(x) - 1), len(x)) plt.figure(figsize(10, 4)) plt.plot(lags, rxx) plt.xlabel(lag (samples)) plt.ylabel(Rxx) plt.title(Autocorrelation) plt.grid(True) plt.show()注意modefull 返回的长度是 2N-1对应的 lag 范围是 -(N-1) 到 N-1。这样做的好处是能同时看到正延迟和负延迟。如果你只需要单边信息可以取 lags 0 的部分。2.2 用自相关看周期顺手就检测出隐藏的规律自相关最经典的用途是检测周期性。假设信号是一个纯正弦那么自相关函数会是一个同周期的余弦函数在 lag 等于信号周期的整数倍时出现峰值。如果信号里混着噪声噪声的自相关在非零延迟处会迅速衰减到 0而周期成分的自相关却会一直保持峰值。这就是为什么自相关能从强噪声里把微弱周期信号“扒”出来。拿上面那个例子10Hz 正弦采样率 1000Hz一个周期是 100 个采样点。算完自相关后你会在 lag100、200、300……处看到明显的峰值。哪怕信噪比很低这些峰值依然清晰。实际做振动诊断和转速测量时这个方法非常常用不需要做复杂的频谱分析看一眼自相关就能知道大致周期。要注意的是如果信号本身不是平稳信号比如带有明显趋势或缓慢漂移直接做自相关会被趋势项干扰。我的习惯是先做去趋势再用自相关分析周期。2.3 自相关和功率谱维纳-辛钦定理的实用意义自相关和功率谱密度是一对傅里叶变换对这就是维纳-辛钦定理。中文说得很绕但实际含义非常实用一个信号的自相关函数做傅里叶变换就得到它的功率谱密度反过来功率谱密度做逆傅里叶变换也能得到自相关函数。这意味着如果你想用 FFT 快速计算自相关不需要真的去循环卷积。可以这样做n len(x) Xf np.fft.rfft(x, n2*n) # 补零到2N避免循环卷积 psd np.abs(Xf) ** 2 rxx_fft np.fft.irfft(psd, n2*n)补零是必须的因为 FFT 本身会带来循环卷积效应不补零的话自相关的后半部分会叠加尾部的值。这个技巧在长信号分析里能省下大量时间而且 FFT 方法比直接时域卷积快得多。3. 互相关两个信号的时差定位器3.1 互相关的计算与归一化互相关和自相关几乎一样区别是它比较的是两个不同信号。定义是Rxy[m] Σ_n x[n] · y[n - m]它回答的问题是当我让 y 信号相对于 x 延迟 m 个点后两个信号的相似度有多高。互相关最常见的应用就是时延估计也就是找出两个传感器接收到同一个信号的时间差。裸算出来的 Rxy 是未归一化的它的大小同时受到两个信号能量影响。如果想要一个类似于相关系数那样的值需要做归一化变成ρxy[m] Rxy[m] / sqrt(Rxx[0] · Ryy[0])这样得到的结果在 [-1, 1] 之间。不过实际做时延估计时我们通常不关心这个归一化值只关心 Rxy 峰值位置对应的 lag。3.2 实操案例用互相关测两个麦克风的时延假设有两个麦克风放在不同位置录制同一个声源。声源到两个麦克风的距离不同所以两个信号之间存在一个时间延迟 τ。用互相关就能估计这个 τ。我写一个最小例子import numpy as np fs 16000 t np.arange(0, 0.1, 1/fs) # 一个400Hz的短音 signal np.sin(2 * np.pi * 400 * t) * np.exp(-50 * t) # 模拟第二个麦克风比第一个晚接收到100个采样点 delay 100 y np.concatenate([np.zeros(delay), signal])[:len(t)] # 去均值 x signal - np.mean(signal) y y - np.mean(y) # 互相关 corr np.correlate(x, y, modefull) lags np.arange(-(len(x) - 1), len(x)) # 峰值对应的lag est_delay lags[np.argmax(corr)] print(f真实延迟: {delay} 采样点估计延迟: {est_delay} 采样点)这里 modefull 很关键因为 y 比 x 延迟了 100 个点互相关的峰值会出现在正 lag 方向。如果 modesame输出长度虽然和输入一样但 lag 坐标不是从 -(N-1) 开始很容易搞错索引。我一般在做互相关时都默认用 modefull再用 lags 数组去对应。延迟时间就是 est_delay / fs。如果两个麦克风之间的距离已知还能进一步算出声源的方向这就是声阵列定位的基础原理。3.3 互相关峰值不是越高越好注意边界和噪声很多人第一次做互相关看到峰值很高就欢呼“找到时延了”。实际上互相关峰值的高度受信号能量影响很大两个不同幅度的信号之间的互相关峰值可能非常高但线性关系却没那么强。如果只做时延估计峰值高度其实不重要重要的是峰值位置。另外一个容易忽略的问题是窄带信号。如果信号是单一频率的正弦波互相关会呈现出周期性振荡峰值不止一个而是每隔一个周期出现一个峰。这时你直接取全局最大很可能取到某个假的周期峰。解决办法是先对信号做包络解调或者用广义互相关GCC-PHAT对频谱进行白化让尖峰更尖锐。边界效应也很讨厌。截断信号的边缘会造成不真实的低相关性尤其在 lag 很大的时候重叠的有效数据变少估计方差变大。我的习惯是只在 lag 绝对值不超过信号长度的三分之一时取峰值这样可以避开大部分边界误差。4. 卷积与相关一对被名字误导的亲戚4.1 卷积的本质翻转、滑动、相乘、求和卷积在信号处理里的定义是y(t) ∫ x(τ) h(t - τ) dτ换成离散形式就是y[n] Σ_k x[k] · h[n - k]它的操作可以拆成四步翻转、滑动、相乘、求和。h 必须先沿纵轴翻转成 h[-k]然后跟 x 对齐从负无穷滑到正无穷每一步做逐点相乘再累加。为什么要翻转因为卷积描述的是系统对输入的因果响应。h 可以理解为系统的脉冲响应翻转保证了“过去的输入影响现在的输出而不是未来的输入影响现在的输出”。这和你做相关时的操作有本质区别。生活化类比卷积就像工厂流水线每个产品经过多个工位每个工位的加工效果叠加在一起而“相关”更像两个人做手势猜谜不翻转任何一方只看当前动作和另一个动作像不像。4.2 相关与卷积的数学换算一句话记牢互相关和卷积的关系可以用一句话记牢互相关等于把其中一个信号时间反转后再与另一个信号做卷积。公式是这样的Rxy[m] Σ_n x[n] · y[n - m]令 k n - m则 n k m代入可得Rxy[m] Σ_k x[k m] · y[k] Σ_k x[-(-k - m)]? 写起来绕。更直接的是用符号表示Rxy[m] (x[-n] * y[n])[m]也就是说把 x 的时域序列绕原点反转变成 x[-n]再跟 y 做卷积得到的结果就是互相关。如果你只有卷积函数没有相关函数就用这个等式来算相关。反过来如果你只有相关函数也可以用相关实现卷积把 y 反转后做互相关再反转结果。我在代码里这样做过多次尤其在嵌入式平台上没有现成相关函数时直接用 FFT 卷积来实现互相关速度很快。4.3 顺带聊聊CNN里的“卷积”其实是相关既然提到卷积就不得不说说卷积神经网络CNN里的“卷积层”。很多做深度学习的人第一次接触信号处理里的卷积会疑惑深度学习框架里的卷积怎么不翻转卷积核事实是CNN 里的卷积层严格说应该叫“互相关层”。它做的事情就是把卷积核和输入图像的区域做点积这个操作没有翻转卷积核。所以从数学定义看CNN 卷积核和图像块计算的是互相关而不是经典卷积。这个区别在工程上影响不大因为网络可以通过学习自动适应是否翻转但对理解公式的人来说这是个值得知道的坑。如果你用信号处理的眼光去看 CNN很多操作就变得亲切了转置卷积、膨胀卷积、深度可分离卷积本质都是在“相关/卷积”这个框架上做变形。5. 相干性频域里的相关系数5.1 从自功率谱到相干函数公式与直觉相干性Coherence的定义是幅值平方相干函数Cxy(f) |Sxy(f)|² / (Sxx(f) · Syy(f))其中 Sxx(f) 是 x 的自功率谱密度Syy(f) 是 y 的自功率谱密度Sxy(f) 是 x 和 y 的互功率谱密度。结果是一个关于频率 f 的函数取值范围在 0 到 1 之间。直觉上可以把它理解为“在频率 f 处y 的功率有多少比例是由 x 的线性变换贡献的”。如果两个信号在某个频率上完全同步变化只是幅度和相位有差异相干性就接近 1如果它们在该频率上的分量是独立的相干性接近 0。一个典型的例子x 是电机振动信号y 是电机附近的噪声信号。如果电机以 50Hz 旋转那么 x 和 y 在 50Hz 处都会出现较强的频率成分但除了 50Hzy 可能还有大量气流噪声。这时候看相干谱50Hz 处会出现一个尖峰说明两者在这个频率上有共同的振源。这就是相干分析在故障诊断里的核心用途。5.2 相干性估计的实操要点加窗、分段、平均相干性估计最容易犯的错误是只用一段数据算功率谱然后套公式结果会发现所有频率的相干性都等于 1。这不是真实结果而是数学恒等式在作怪——单段数据的互功率谱和自功率谱之间存在固有的代数关系直接除必然得到 1。要得到有意义的相干估计必须做分段平均也就是 Welch 方法。把信号分成若干小段每段加窗常用汉宁窗分别计算功率谱然后对多段的功率谱取平均最后再用平均后的 Sxx、Syy、Sxy 求相干。分段的数目越多相干估计的方差越低但频率分辨率会下降。我常用的参数是from scipy.signal import coherence import numpy as np fs 1000 t np.arange(0, 10, 1/fs) x np.sin(2 * np.pi * 20 * t) 0.3 * np.random.randn(len(t)) y np.sin(2 * np.pi * 20 * t 0.5) 0.5 * np.random.randn(len(t)) f, Cxy coherence(x, y, fsfs, nperseg1024, noverlap512, windowhann)nperseg 是每段长度noverlap 是重叠采样数。我一般会保证至少有 8 到 16 段做平均这样相干谱才比较稳定。如果分段太少相干值会普遍偏高看上去像噪声也有很强的相干。5.3 相干谱读法一个实际例子还是用上面那个代码x 和 y 都包含 20Hz 的正弦分量但各自混入不同噪声。算出来的相干谱在 20Hz 处会有一个明显的峰值接近 1其他频率则比较低。这就是想要的结果两个信号在 20Hz 存在强线性关系其他频率没有关系。读相干谱时还要注意一个坑峰值宽度。峰值越尖锐说明两个信号共同成分的频率越稳定如果峰值宽而起伏可能是信号频率漂移也可能是分段长度太短导致频率分辨率不足。如果相干谱整体都很高比如达到 0.8 以上那要先检查是不是两个信号本身是同一个源的不同幅度版本或者是不是数据分段数太少。如果整体都很低但你知道它们应该有某个频率的联系可以适当增大 nperseg 提高频率分辨率再看对应频率附近有没有尖峰。6. 常见问题与排查技巧那些年我踩过的坑6.1 相关值异常高先查数据是否去均值这是一个极其常见的坑。两个完全无关的序列如果都带有缓慢上升趋势直接用皮尔逊相关算出来的系数可能接近 1。原因很简单趋势项在协方差计算中占据了主导地位掩盖了随机部分的真实关系。处理方法是先对数据去均值再做一次线性去趋势通常用 np.polyfit 拟合一次项后减掉拟合线。更严格的做法是如果你关心的是高频成分的相关性可以再做一个高通滤波。在我做振动台数据分析时这个习惯救了我很多次。6.2 互相关峰值找错小心周期信号和边界效应周期信号互相关会出现多个等间隔的峰值这时全局最大不一定代表真实时延。比如两个麦克风接收同一个正弦声源由于信号是周期的互相关峰在每个周期处都会出现而真实时延对应的峰不一定就是最高的那个。我的解决方案是结合信号包络再做互相关而不是直接用原始信号。先取信号的包络用希尔伯特变换求解析信号的幅值再对包络做互相关包络是缓慢变化的不会产生周期性的假峰。另外在时延估计之前先检查信号的频谱如果频谱是窄带的就要对 GCC-PHAT 这类加权方法多一点了解。6.3 相干谱全是1平均次数不够看到相干谱全为 1第一个反应不是数据有问题而是估计方法有问题。分段数太少甚至单段必然导致近似 1 的相干值。解决方法是减小 nperseg 以增加段数或者增大 noverlap 增加重叠率。不过nperseg 也不能无限减小因为分段越短频率分辨率越差。如果信号频率相近比如 49Hz 和 51Hznperseg 太短就分不开这两个峰。所以在使用 Welch 相关估计前先想清楚你的频率分辨率需求。经验值是让每段时长至少包含 10 个目标频率周期。6.4 自相关、互相关、卷积、相干一张速查表名称域核心定义实信号典型用途常见坑自相关时域Rxx[m] Σ x[n]x[n-m]检测周期、估计频谱未去均值/趋势项干扰互相关时域Rxy[m] Σ x[n]y[n-m]时延估计、信号匹配周期信号多峰、边界效应卷积时域y[n] Σ x[k]h[n-k]线性系统响应、滤波容易和相关混淆相干性频域Cxy(f)Sxy(f)²/(SxxSyy)皮尔逊相关样本空间r cov(x,y)/(σxσy)线性关联总览虚假相关、非线性关系我在实际处理数据时通常会先问自己三个问题这个问题关心的是整体关系还是频率分布关心的是时延还是相似度数据是不是平稳的这三个问题基本能决定应该用哪个工具而不是哪个公式好看用哪个。我记得自己最早做振动台数据分析时就是把自相关和互相关当成同一个东西结果花了两天时间才找到问题出在没去均值。后来养成一个习惯凡是算相关第一步永远先画时域图看均值、趋势和边缘是否异常第二步才套公式。这个习惯帮我躲过了无数个“看似正确”的坑。如果你也想把这几个概念印在脑子里可以把速查表存下来下次遇到相关、相干、卷积中的任何一个先问自己一句现在这个场景我需要的是总账、明细账还是翻转滑动。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑