Savitzky-Golay滤波算法教程
1. 引言与背景溯源
在信号处理中,平滑滤波 是抑制噪声、提取有效信息的核心步骤。传统的平滑方法(如移动平均、指数平滑)通过对局部窗口内的样本取平均实现噪声抑制,但存在致命缺陷:会显著平滑信号的高阶特征(如峰值、斜率、曲率),导致信号失真(例如光谱分析中的特征峰被"抹平"、心电图的QRS波形态畸变)。
1964年,美国科学家Abraham Savitzky和Marcel J. E. Golay在《Analytical Chemistry》发表论文《Smoothing and Differentiation of Data by Simplified Least Squares Procedures》,提出了一种基于局部多项式拟合的平滑方法 ------即Savitzky-Golay(SG)滤波。该方法的核心优势是:在平滑噪声的同时,能保持信号的高阶矩(如均值、方差、斜率、曲率)不变,因此广泛应用于光谱分析、色谱分析、生物信号处理(如心电图、脑电图)、机械振动监测等需要保留特征细节的领域。
2. 核心思想与基本概念
SG滤波的核心思想可概括为:用局部窗口内的低阶多项式拟合原始信号,以拟合多项式在窗口中心处的值作为滤波结果。这种方法本质是"加权平均",但权重由多项式拟合的最小二乘解确定,而非简单的均匀权重(如移动平均)。
2.1 关键概念定义
为了后续推导清晰,先严格定义核心概念:
- 信号形式 :假设原始信号为等间距采样 的离散序列 {yn}n=0N−1\{y_n\}_{n=0}^{N-1}{yn}n=0N−1,采样间隔为 Δx\Delta xΔx(通常取1以简化计算,此时自变量 xn=nx_n = nxn=n)。若采样间隔不为1,需对自变量进行归一化处理(令 t=(x−xi)/Δxt = (x - x_i)/\Delta xt=(x−xi)/Δx,将窗口内的自变量映射为以中心为0的整数序列)。
- 窗口大小 :取奇数 W=2M+1W = 2M + 1W=2M+1(MMM 为窗口半宽),表示每个滤波结果由中心样本及左右各 MMM 个样本共同决定(如 M=2M=2M=2 对应窗口大小 W=5W=5W=5,覆盖中心样本+左右各2个样本)。
- 多项式阶数 :假设窗口内的信号可由 kkk 阶多项式拟合,形式为:
p(t)=a0+a1t+a2t2+⋯+aktk p(t) = a_0 + a_1 t + a_2 t^2 + \dots + a_k t^k p(t)=a0+a1t+a2t2+⋯+aktk
其中 ttt 是中心化后的自变量 (令窗口中心的 t=0t=0t=0),即对窗口内的第 jjj 个样本(相对中心的位置为 j=−M,−M+1,...,0,...,M−1,Mj=-M,-M+1,\dots,0,\dots,M-1,Mj=−M,−M+1,...,0,...,M−1,M),有 tj=jt_j = jtj=j(等间距采样且 Δx=1\Delta x=1Δx=1 时)。
2.2 核心优势的直观理解
传统移动平均的权重是均匀的(如5点平均的权重为 [1/5,1/5,1/5,1/5,1/5][1/5,1/5,1/5,1/5,1/5][1/5,1/5,1/5,1/5,1/5]),而SG滤波的权重由多项式拟合的最小二乘解确定(如5点2阶SG的权重为 [−3/35,12/35,17/35,12/35,−3/35][-3/35,12/35,17/35,12/35,-3/35][−3/35,12/35,17/35,12/35,−3/35])。这种权重的差异带来两个关键优势:
- 保留峰值位置:均匀权重会导致峰值向窗口中心偏移(如尖锐峰被"拉平"),而多项式拟合能准确捕捉峰的顶点(拟合多项式在中心处的极值与原始峰位置一致);
- 保留高阶特征 :对线性信号(yn=a+bny_n = a + bnyn=a+bn),SG滤波后斜率 bbb 不变;对二次信号(yn=a+bn+cn2y_n = a + bn + cn^2yn=a+bn+cn2),曲率 2c2c2c 不变。
3. 算法原理与公式推导(进阶版)
SG滤波的推导过程分为模型建立→最小二乘求解→滤波系数推导→中心值计算四个步骤,以下展开详细推导:
3.1 多项式拟合的矩阵模型
对任意中心样本 yiy_iyi(对应自变量 xix_ixi),取其窗口内的 W=2M+1W=2M+1W=2M+1 个样本 {yi−M,yi−M+1,...,yi,...,yi+M}\{y_{i-M}, y_{i-M+1}, \dots, y_i, \dots, y_{i+M}\}{yi−M,yi−M+1,...,yi,...,yi+M},对应的中心化自变量为 {t−M,t−M+1,...,t0,...,tM}\{t_{-M}, t_{-M+1}, \dots, t_0, \dots, t_M\}{t−M,t−M+1,...,t0,...,tM}(其中 tj=jt_j = jtj=j,t0=0t_0=0t0=0 为中心)。
假设窗口内的信号可由 kkk 阶多项式 p(t)p(t)p(t) 拟合,则每个样本 yi+jy_{i+j}yi+j 满足:
yi+j=p(tj)+ϵj=a0+a1tj+a2tj2+⋯+aktjk+ϵj(j=−M,...,M) y_{i+j} = p(t_j) + \epsilon_j = a_0 + a_1 t_j + a_2 t_j^2 + \dots + a_k t_j^k + \epsilon_j \quad (j=-M,\dots,M) yi+j=p(tj)+ϵj=a0+a1tj+a2tj2+⋯+aktjk+ϵj(j=−M,...,M)
其中 ϵj\epsilon_jϵj 是零均值的随机噪声(假设为高斯白噪声)。
将上述关系写成矩阵形式 :
Y=XA+ϵ(1) \boldsymbol{Y} = \boldsymbol{X}\boldsymbol{A} + \boldsymbol{\epsilon} \tag{1} Y=XA+ϵ(1)
其中:
- Y=[yi−M,yi−M+1,...,yi+M]T\boldsymbol{Y} = [y_{i-M}, y_{i-M+1}, \dots, y_{i+M}]^TY=[yi−M,yi−M+1,...,yi+M]T:窗口内的观测值向量(W×1W \times 1W×1);
- X\boldsymbol{X}X:设计矩阵(W×(k+1)W \times (k+1)W×(k+1)),每行对应一个样本的多项式项:
X=[1t−Mt−M2...t−Mk1t−M+1t−M+12...t−M+1k⋮⋮⋮⋱⋮1tMtM2...tMk] \boldsymbol{X} = \begin{bmatrix} 1 & t_{-M} & t_{-M}^2 & \dots & t_{-M}^k \\ 1 & t_{-M+1} & t_{-M+1}^2 & \dots & t_{-M+1}^k \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & t_M & t_M^2 & \dots & t_M^k \end{bmatrix} X= 11⋮1t−Mt−M+1⋮tMt−M2t−M+12⋮tM2......⋱...t−Mkt−M+1k⋮tMk - A=[a0,a1,...,ak]T\boldsymbol{A} = [a_0, a_1, \dots, a_k]^TA=[a0,a1,...,ak]T:多项式系数向量((k+1)×1(k+1) \times 1(k+1)×1);
- ϵ=[ϵ−M,ϵ−M+1,...,ϵM]T\boldsymbol{\epsilon} = [\epsilon_{-M}, \epsilon_{-M+1}, \dots, \epsilon_M]^Tϵ=[ϵ−M,ϵ−M+1,...,ϵM]T:噪声向量(W×1W \times 1W×1)。
3.2 最小二乘求解系数向量
SG滤波的目标是最小化噪声的残差平方和 (即拟合误差的L2范数):
minA∥Y−XA∥∗22=min∗A(Y−XA)T(Y−XA) \min_{\boldsymbol{A}} \|\boldsymbol{Y} - \boldsymbol{X}\boldsymbol{A}\|*2^2 = \min*{\boldsymbol{A}} (\boldsymbol{Y} - \boldsymbol{X}\boldsymbol{A})^T (\boldsymbol{Y} - \boldsymbol{X}\boldsymbol{A}) Amin∥Y−XA∥∗22=min∗A(Y−XA)T(Y−XA)
为求解极值,对 A\boldsymbol{A}A 求偏导并令导数为零。根据矩阵求导法则:
∂∂A(Y−XA)T(Y−XA)=−2XT(Y−XA) \frac{\partial}{\partial \boldsymbol{A}} (\boldsymbol{Y} - \boldsymbol{X}\boldsymbol{A})^T (\boldsymbol{Y} - \boldsymbol{X}\boldsymbol{A}) = -2\boldsymbol{X}^T (\boldsymbol{Y} - \boldsymbol{X}\boldsymbol{A}) ∂A∂(Y−XA)T(Y−XA)=−2XT(Y−XA)
令导数为零,得到正规方程 (Normal Equation):
XTXA=XTY(2) \boldsymbol{X}^T \boldsymbol{X} \boldsymbol{A} = \boldsymbol{X}^T \boldsymbol{Y} \tag{2} XTXA=XTY(2)
若 XTX\boldsymbol{X}^T \boldsymbol{X}XTX 可逆(要求 W>k+1W > k+1W>k+1,即窗口大小大于多项式阶数+1,否则矩阵秩不足),则系数向量的解为:
A=(XTX)−1XTY(3) \boldsymbol{A} = (\boldsymbol{X}^T \boldsymbol{X})^{-1} \boldsymbol{X}^T \boldsymbol{Y} \tag{3} A=(XTX)−1XTY(3)
3.3 滤波系数的推导(核心步骤)
SG滤波的结果是窗口中心样本的拟合值 ,即 p(t0)=p(0)p(t_0) = p(0)p(t0)=p(0)(因 t0=0t_0=0t0=0 为中心)。将 t=0t=0t=0 代入多项式 p(t)p(t)p(t),得:
p(0)=a0+a1⋅0+a2⋅02+⋯+ak⋅0k=a0 p(0) = a_0 + a_1 \cdot 0 + a_2 \cdot 0^2 + \dots + a_k \cdot 0^k = a_0 p(0)=a0+a1⋅0+a2⋅02+⋯+ak⋅0k=a0
因此,滤波结果等价于多项式的常数项 a0a_0a0 。结合式(3),a0a_0a0 是系数向量 A\boldsymbol{A}A 的第一个元素,可表示为:
a0=e1TA=e1T(XTX)−1XTY(4) a_0 = \boldsymbol{e}_1^T \boldsymbol{A} = \boldsymbol{e}_1^T (\boldsymbol{X}^T \boldsymbol{X})^{-1} \boldsymbol{X}^T \boldsymbol{Y} \tag{4} a0=e1TA=e1T(XTX)−1XTY(4)
其中 e1=[1,0,0,...,0]T\boldsymbol{e}_1 = [1, 0, 0, \dots, 0]^Te1=[1,0,0,...,0]T 是 (k+1)×1(k+1) \times 1(k+1)×1 的单位向量(第一个元素为1,其余为0)。
令滤波系数向量 c=[c−M,c−M+1,...,c0,...,cM]T\boldsymbol{c} = [c_{-M}, c_{-M+1}, \dots, c_0, \dots, c_M]^Tc=[c−M,c−M+1,...,c0,...,cM]T(W×1W \times 1W×1),则式(4)可改写为加权和形式 :
y^∗i=a0=cTY=∑∗j=−MMcjyi+j(5) \hat{y}*i = a_0 = \boldsymbol{c}^T \boldsymbol{Y} = \sum*{j=-M}^M c_j y_{i+j} \tag{5} y^∗i=a0=cTY=∑∗j=−MMcjyi+j(5)
其中滤波系数 c\boldsymbol{c}c 的表达式为:
c=X(XTX)−1e1(6) \boldsymbol{c} = \boldsymbol{X} (\boldsymbol{X}^T \boldsymbol{X})^{-1} \boldsymbol{e}_1 \tag{6} c=X(XTX)−1e1(6)
推导说明 :式(6)由式(4)变形而来------因为 cT=e1T(XTX)−1XT\boldsymbol{c}^T = \boldsymbol{e}_1^T (\boldsymbol{X}^T \boldsymbol{X})^{-1} \boldsymbol{X}^TcT=e1T(XTX)−1XT,两边转置后得到 c=X[(XTX)−1]Te1\boldsymbol{c} = \boldsymbol{X} [(\boldsymbol{X}^T \boldsymbol{X})^{-1}]^T \boldsymbol{e}_1c=X[(XTX)−1]Te1;又因为 XTX\boldsymbol{X}^T \boldsymbol{X}XTX 是对称矩阵((XTX)T=XTX(\boldsymbol{X}^T \boldsymbol{X})^T = \boldsymbol{X}^T \boldsymbol{X}(XTX)T=XTX),其逆矩阵也对称,故 [(XTX)−1]T=(XTX)−1[(\boldsymbol{X}^T \boldsymbol{X})^{-1}]^T = (\boldsymbol{X}^T \boldsymbol{X})^{-1}[(XTX)−1]T=(XTX)−1,最终得到式(6)。
3.4 等间距采样的简化计算(以5点2阶SG为例)
当信号等间距采样时,中心化后的 tjt_jtj 是对称整数序列(t−j=−tjt_{-j} = -t_jt−j=−tj),此时 XTX\boldsymbol{X}^T \boldsymbol{X}XTX 是对称矩阵 ,且其非对角线元素中奇数阶项为零 (因 tjt_jtj 对称,∑j=−MMtjm+n=0\sum_{j=-M}^M t_j^{m+n} = 0∑j=−MMtjm+n=0 当 m+nm+nm+n 为奇数时)。以下以5点2阶SG滤波为例,详细展示系数计算过程:
步骤1:确定参数与 tjt_jtj 序列
取窗口大小 W=5W=5W=5(M=2M=2M=2,半宽为2),多项式阶数 k=2k=2k=2(二次多项式),则 tj=−2,−1,0,1,2t_j = -2, -1, 0, 1, 2tj=−2,−1,0,1,2(对称整数序列)。
步骤2:构造设计矩阵 X\boldsymbol{X}X
设计矩阵 X\boldsymbol{X}X 的每行对应 tjt_jtj 的多项式项(0阶、1阶、2阶):
X=[1−2(−2)21−1(−1)2100211121222]=[1−241−11100111124] \boldsymbol{X} = \begin{bmatrix} 1 & -2 & (-2)^2 \\ 1 & -1 & (-1)^2 \\ 1 & 0 & 0^2 \\ 1 & 1 & 1^2 \\ 1 & 2 & 2^2 \end{bmatrix} = \begin{bmatrix} 1 & -2 & 4 \\ 1 & -1 & 1 \\ 1 & 0 & 0 \\ 1 & 1 & 1 \\ 1 & 2 & 4 \end{bmatrix} X= 11111−2−1012(−2)2(−1)2021222 = 11111−2−101241014
步骤3:计算 XTX\boldsymbol{X}^T \boldsymbol{X}XTX
根据矩阵乘法,XTX\boldsymbol{X}^T \boldsymbol{X}XTX 的元素为 ∑j=−MMtjm+n\sum_{j=-M}^M t_j^{m+n}∑j=−MMtjm+n(m,n=0,1,2m,n=0,1,2m,n=0,1,2):
XTX=[∑tj0∑tj1∑tj2∑tj1∑tj2∑tj3∑tj2∑tj3∑tj4] \boldsymbol{X}^T \boldsymbol{X} = \begin{bmatrix} \sum t_j^0 & \sum t_j^1 & \sum t_j^2 \\ \sum t_j^1 & \sum t_j^2 & \sum t_j^3 \\ \sum t_j^2 & \sum t_j^3 & \sum t_j^4 \end{bmatrix} XTX= ∑tj0∑tj1∑tj2∑tj1∑tj2∑tj3∑tj2∑tj3∑tj4
代入 tj=−2,−1,0,1,2t_j = -2,-1,0,1,2tj=−2,−1,0,1,2 计算各项和:
- ∑tj0=5\sum t_j^0 = 5∑tj0=5(每个 tj0=1t_j^0=1tj0=1,共5项);
- ∑tj1=(−2)+(−1)+0+1+2=0\sum t_j^1 = (-2) + (-1) + 0 + 1 + 2 = 0∑tj1=(−2)+(−1)+0+1+2=0(奇数阶和为零);
- ∑tj2=4+1+0+1+4=10\sum t_j^2 = 4 + 1 + 0 + 1 + 4 = 10∑tj2=4+1+0+1+4=10;
- ∑tj3=(−8)+(−1)+0+1+8=0\sum t_j^3 = (-8) + (-1) + 0 + 1 + 8 = 0∑tj3=(−8)+(−1)+0+1+8=0(奇数阶和为零);
- ∑tj4=16+1+0+1+16=34\sum t_j^4 = 16 + 1 + 0 + 1 + 16 = 34∑tj4=16+1+0+1+16=34。
因此:
XTX=[5010010010034] \boldsymbol{X}^T \boldsymbol{X} = \begin{bmatrix} 5 & 0 & 10 \\ 0 & 10 & 0 \\ 10 & 0 & 34 \end{bmatrix} XTX= 5010010010034
步骤4:求 XTX\boldsymbol{X}^T \boldsymbol{X}XTX 的逆矩阵
对于对称矩阵,可通过伴随矩阵法 求逆。首先计算行列式:
det(XTX)=5×(10×34−0×0)−0+10×(0×0−10×10)=5×340−10×100=1700−1000=700 \det(\boldsymbol{X}^T \boldsymbol{X}) = 5 \times (10 \times 34 - 0 \times 0) - 0 + 10 \times (0 \times 0 - 10 \times 10) = 5 \times 340 - 10 \times 100 = 1700 - 1000 = 700 det(XTX)=5×(10×34−0×0)−0+10×(0×0−10×10)=5×340−10×100=1700−1000=700
然后计算伴随矩阵(对称矩阵的伴随矩阵也是对称的):
adj(XTX)=[10×34−0×00−0×100×0−10×100−0×105×34−10×1010×0−5×00×0−10×1010×0−5×05×10−0×0]=[3400−1000700−100050] \text{adj}(\boldsymbol{X}^T \boldsymbol{X}) = \begin{bmatrix} 10 \times 34 - 0 \times 0 & 0 - 0 \times 10 & 0 \times 0 - 10 \times 10 \\ 0 - 0 \times 10 & 5 \times 34 - 10 \times 10 & 10 \times 0 - 5 \times 0 \\ 0 \times 0 - 10 \times 10 & 10 \times 0 - 5 \times 0 & 5 \times 10 - 0 \times 0 \end{bmatrix} = \begin{bmatrix} 340 & 0 & -100 \\ 0 & 70 & 0 \\ -100 & 0 & 50 \end{bmatrix} adj(XTX)= 10×34−0×00−0×100×0−10×100−0×105×34−10×1010×0−5×00×0−10×1010×0−5×05×10−0×0 = 3400−1000700−100050
因此逆矩阵为:
(XTX)−1=1det(XTX)adj(XTX)=1700[3400−1000700−100050]=[17/350−1/701/100−1/701/14] (\boldsymbol{X}^T \boldsymbol{X})^{-1} = \frac{1}{\det(\boldsymbol{X}^T \boldsymbol{X})} \text{adj}(\boldsymbol{X}^T \boldsymbol{X}) = \frac{1}{700} \begin{bmatrix} 340 & 0 & -100 \\ 0 & 70 & 0 \\ -100 & 0 & 50 \end{bmatrix} = \begin{bmatrix} 17/35 & 0 & -1/7 \\ 0 & 1/10 & 0 \\ -1/7 & 0 & 1/14 \end{bmatrix} (XTX)−1=det(XTX)1adj(XTX)=7001 3400−1000700−100050 = 17/350−1/701/100−1/701/14
步骤5:计算滤波系数 c\boldsymbol{c}c
根据式(6),c=X(XTX)−1e1\boldsymbol{c} = \boldsymbol{X} (\boldsymbol{X}^T \boldsymbol{X})^{-1} \boldsymbol{e}_1c=X(XTX)−1e1。代入 e1=[1,0,0]T\boldsymbol{e}_1 = [1,0,0]^Te1=[1,0,0]T:
(XTX)−1e1=[17/350−1/7] (\boldsymbol{X}^T \boldsymbol{X})^{-1} \boldsymbol{e}_1 = \begin{bmatrix} 17/35 \\ 0 \\ -1/7 \end{bmatrix} (XTX)−1e1= 17/350−1/7
再与 X\boldsymbol{X}X 相乘:
c=[1−241−11100111124][17/350−1/7]=[17/35−0−4/717/35−0−1/717/35−0−017/35−0−1/717/35−0−4/7]=[−3/3512/3517/3512/35−3/35] \boldsymbol{c} = \begin{bmatrix} 1 & -2 & 4 \\ 1 & -1 & 1 \\ 1 & 0 & 0 \\ 1 & 1 & 1 \\ 1 & 2 & 4 \end{bmatrix} \begin{bmatrix} 17/35 \\ 0 \\ -1/7 \end{bmatrix} = \begin{bmatrix} 17/35 - 0 - 4/7 \\ 17/35 - 0 - 1/7 \\ 17/35 - 0 - 0 \\ 17/35 - 0 - 1/7 \\ 17/35 - 0 - 4/7 \end{bmatrix} = \begin{bmatrix} -3/35 \\ 12/35 \\ 17/35 \\ 12/35 \\ -3/35 \end{bmatrix} c= 11111−2−101241014 17/350−1/7 = 17/35−0−4/717/35−0−1/717/35−0−017/35−0−1/717/35−0−4/7 = −3/3512/3517/3512/35−3/35
最终,5点2阶SG滤波的结果为:
y^∗i=−335y∗i−2+1235yi−1+1735yi+1235yi+1−335yi+2 \hat{y}*i = -\frac{3}{35}y*{i-2} + \frac{12}{35}y_{i-1} + \frac{17}{35}y_i + \frac{12}{35}y_{i+1} - \frac{3}{35}y_{i+2} y^∗i=−353y∗i−2+3512yi−1+3517yi+3512yi+1−353yi+2
4. 完整模型求解步骤(精细化)
SG滤波的实际应用需遵循参数选择→系数预计算→逐点滤波→边界处理→结果输出的流程,以下展开每个步骤的细节:
4.1 参数选择(关键决策)
SG滤波的效果高度依赖窗口大小 WWW 和多项式阶数 kkk,需满足以下约束:
- 窗口大小 WWW :必须为奇数(保证窗口有中心样本),且 W>k+1W > k+1W>k+1(确保 XTX\boldsymbol{X}^T \boldsymbol{X}XTX 可逆)。通常根据噪声强度选择:噪声越大,窗口越大(如噪声方差为 σ2\sigma^2σ2,窗口大小可按 W∝1/σW \propto 1/\sigmaW∝1/σ 调整)。
- 多项式阶数 kkk :通常取 k=2k=2k=2(适用于大多数平滑场景)或 k=3k=3k=3(适用于含曲率变化的信号)。需注意:阶数过高易过拟合(拟合噪声),阶数过低易欠拟合(无法捕捉信号趋势)。
参数优化方法 :可通过交叉验证 选择最优参数------将信号分为训练集和验证集,尝试不同的 (W,k)(W,k)(W,k) 组合,选择验证集上均方误差(MSE)最小的组合。
4.2 预计算滤波系数
对选定的 WWW 和 kkk,预计算滤波系数 c\boldsymbol{c}c(仅需计算一次,可复用):
- 生成中心化的 tjt_jtj 序列:tj=−M,−M+1,...,0,...,Mt_j = -M, -M+1, \dots, 0, \dots, Mtj=−M,−M+1,...,0,...,M(其中 M=(W−1)/2M=(W-1)/2M=(W−1)/2);
- 构造设计矩阵 X\boldsymbol{X}X(W×(k+1)W \times (k+1)W×(k+1));
- 计算 XTX\boldsymbol{X}^T \boldsymbol{X}XTX 并求逆(利用对称性简化计算);
- 按式(6)计算滤波系数 c\boldsymbol{c}c。
4.3 逐点滤波计算
对原始信号的每个样本 yiy_iyi(i=0,1,...,N−1i=0,1,\dots,N-1i=0,1,...,N−1),执行以下操作:
- 窗口样本提取 :对中心样本 iii,提取窗口内的样本 {yi−M,yi−M+1,...,yi+M}\{y_{i-M}, y_{i-M+1}, \dots, y_{i+M}\}{yi−M,yi−M+1,...,yi+M};
- 加权求和 :按式(5)计算滤波结果 y^∗i=∑∗j=−MMcjyi+j\hat{y}*i = \sum*{j=-M}^M c_j y_{i+j}y^∗i=∑∗j=−MMcjyi+j;
- 边界处理 :对于信号两端的 MMM 个样本(如 i<Mi < Mi<M 或 i>N−1−Mi > N-1-Mi>N−1−M),窗口无法完全覆盖,需采用以下方法处理:
- 镜像扩展 (最常用):将信号两端的样本镜像到外侧(如 i=0i=0i=0 时,窗口左侧的样本取 y1,y2y_1, y_2y1,y2 代替 y−1,y−2y_{-1}, y_{-2}y−1,y−2;i=N−1i=N-1i=N−1 时,窗口右侧的样本取 yN−2,yN−3y_{N-2}, y_{N-3}yN−2,yN−3 代替 yN,yN+1y_N, y_{N+1}yN,yN+1);
- 零填充:将外侧样本视为0(易引入偏差,仅适用于信号两端无重要信息的场景);
- 截断处理 :直接使用现有样本(如 i=0i=0i=0 时,窗口为 {y0,y1,y2}\{y_0, y_1, y_2\}{y0,y1,y2},此时需重新计算滤波系数,复杂度较高)。
4.4 结果输出
将所有滤波后的样本 {y^0,y^∗1,...,y^∗N−1}\{\hat{y}_0, \hat{y}*1, \dots, \hat{y}*{N-1}\}{y^0,y^∗1,...,y^∗N−1} 组合成最终的滤波信号。
5. 适用边界与局限性
SG滤波并非"万能滤波",其适用场景与局限性需明确:
5.1 适用场景
- 等间距采样信号 :设计矩阵的构造基于等间距的 tjt_jtj,不等间距信号需重新计算 tjt_jtj(复杂度上升,且滤波效果下降);
- 需保留高阶特征的信号:如光谱峰、心电图的QRS波、机械振动的共振峰等,SG滤波能在平滑噪声的同时保留特征的位置、斜率与曲率;
- 高斯白噪声主导的信号:SG滤波对高斯白噪声的抑制效果优于移动平均,且不会引入相位偏移(因滤波系数对称)。
5.2 局限性
- 非平稳信号:若信号存在突变(如阶跃信号、脉冲信号),局部多项式拟合会导致突变处的"拖尾"失真(例如阶跃信号的上升沿被平滑成斜坡);
- 强脉冲噪声:SG滤波对脉冲噪声(如椒盐噪声)的抑制效果差,因脉冲噪声会严重影响多项式拟合的结果(需先通过中值滤波预处理);
- 参数敏感性:窗口大小或阶数选择不当会导致过拟合(阶数过高)或欠拟合(窗口过大),需通过交叉验证优化;
- 计算复杂度:对大窗口或高阶多项式,矩阵求逆的计算量较大(但可通过预计算系数缓解)。
6. 总结
Savitzky-Golay滤波是一种基于局部多项式拟合的自适应平滑方法 ,其核心是通过最小二乘法求解多项式系数,并用中心处的拟合值作为滤波结果。与传统移动平均相比,SG滤波的优势在于保留信号的高阶特征,因此成为需要"平滑+保特征"场景的首选方法。
关键结论:
- 滤波结果是窗口内样本的加权和,权重由多项式拟合的最小二乘解确定;
- 等间距采样时,滤波系数仅与窗口大小和多项式阶数有关,与信号本身无关;
- 边界处理是实际应用中的关键步骤,镜像扩展是最优选择;
- 参数选择需平衡噪声抑制与特征保留,交叉验证是有效的优化方法。
通过合理选择参数,SG滤波能在噪声抑制与特征保留之间取得最佳平衡,是信号处理领域的经典算法之一。
案例介绍
生成模拟光谱数据:包含2个高斯特征峰、随机高斯白噪声,用于演示Savitzky-Golay滤波的噪声抑制与特征保留效果。
完整代码
python
import numpy as np
import matplotlib.pyplot as plt
def generate_simulation_data(n_points=1000, noise_std=0.05):
"""
生成带噪声的模拟光谱数据
参数:
n_points: 采样点数
noise_std: 高斯白噪声标准差
返回:
x: 自变量(波长)
y_raw: 原始无噪声信号
y_noisy: 带噪声的信号
"""
x = np.linspace(0, 10, n_points)
# 两个高斯峰
peak1 = np.exp(-(x - 3) ** 2 / 0.1)
peak2 = np.exp(-(x - 7) ** 2 / 0.2)
y_raw = peak1 + peak2
# 加入高斯白噪声
y_noisy = y_raw + np.random.normal(0, noise_std, n_points)
return x, y_raw, y_noisy
def compute_sg_coefficients(window_size, poly_order):
"""
计算Savitzky-Golay滤波系数
参数:
window_size: 窗口大小(必须为奇数)
poly_order: 多项式阶数
返回:
coeffs: 滤波系数数组
"""
# 确保窗口大小为奇数且满足约束条件
if window_size % 2 == 0:
raise ValueError("Window size must be odd")
if window_size <= poly_order:
raise ValueError("Window size must be larger than polynomial order")
# 计算窗口半宽
half_window = (window_size - 1) // 2
# 生成中心化的自变量序列 t = -M, ..., 0, ..., M
t = np.arange(-half_window, half_window + 1)
# 构造设计矩阵 X (W × (k+1)),每行是 [t^0, t^1, ..., t^k]
X = np.zeros((window_size, poly_order + 1))
for i in range(window_size):
for j in range(poly_order + 1):
X[i, j] = t[i] ** j
# 计算正规方程的系数矩阵 X^T X 并求逆
XTX = np.dot(X.T, X)
XTX_inv = np.linalg.inv(XTX)
# 单位向量 e1 (第一个元素为1,其余为0)
e1 = np.zeros(poly_order + 1)
e1[0] = 1
# 计算滤波系数 c = X (X^T X)^{-1} e1
coeffs = np.dot(np.dot(X, XTX_inv), e1)
return coeffs
def sg_filter(y, window_size, poly_order):
"""
对信号应用Savitzky-Golay滤波
参数:
y: 原始信号数组
window_size: 窗口大小(必须为奇数)
poly_order: 多项式阶数
返回:
y_smooth: 滤波后的信号
"""
coeffs = compute_sg_coefficients(window_size, poly_order)
half_window = (window_size - 1) // 2
n = len(y)
y_smooth = np.zeros(n)
# 处理中间样本
for i in range(half_window, n - half_window):
# 提取窗口内的样本
window = y[i - half_window: i + half_window + 1]
# 加权求和计算滤波值
y_smooth[i] = np.dot(window, coeffs)
# 处理左边界(镜像扩展)
for i in range(half_window):
# 镜像序列:y[0], y[1], ..., y[half_window], y[half_window-1], ..., y[half_window - (half_window - i) + 1]
mirror_left = y[half_window - i: half_window + 1]
# 填充右半部分
if i < half_window:
mirror_right = y[half_window: half_window + (half_window - i)]
else:
mirror_right = []
# 组合镜像窗口
window = np.concatenate([mirror_left, mirror_right])
# 加权求和计算滤波值
y_smooth[i] = np.dot(window, coeffs[:len(window)])
# 处理右边界(镜像扩展)
for i in range(n - half_window, n):
# 镜像序列:y[n - half_window - 1], ..., y[n-1], y[n-2], ..., y[n - (i - (n - half_window)) - 2]
mirror_right = y[n - half_window - 1: i][::-1]
# 填充左半部分
if i < n - 1:
mirror_left = y[i: i + (n - i - 1)][::-1]
else:
mirror_left = []
# 组合镜像窗口
window = np.concatenate([mirror_left, mirror_right])
# 加权求和计算滤波值
y_smooth[i] = np.dot(window, coeffs[-len(window):])
return y_smooth
# 主程序
if __name__ == "__main__":
# 生成模拟数据
x, y_raw, y_noisy = generate_simulation_data()
# 设置SG滤波参数
window_size = 21
poly_order = 3
# 应用SG滤波
y_smoothed = sg_filter(y_noisy, window_size, poly_order)
# 绘制结果
plt.figure(figsize=(10, 6))
plt.plot(x, y_raw, 'k--', label='Original Signal', linewidth=2)
plt.plot(x, y_noisy, 'r', label='Noisy Signal', alpha=0.5)
plt.plot(x, y_smoothed, 'b', label='SG Smoothed Signal', linewidth=2)
plt.xlabel('Wavelength')
plt.ylabel('Intensity')
plt.title('Savitzky-Golay Filtering Demonstration')
plt.legend()
plt.grid(True)
plt.show()
代码深度解析:Savitzky-Golay(SG)滤波模拟光谱去噪
一、整体框架与背景
代码功能:生成带高斯峰+高斯白噪声的模拟光谱数据,实现Savitzky-Golay滤波 (多项式滑动窗口滤波)的噪声抑制,并可视化对比效果。核心原理:在滑动窗口内用多项式拟合信号,取拟合多项式在窗口中心的值作为滤波输出,同时保留信号的特征(如峰位、峰形)和高阶导数信息(光谱建模的关键需求)。
二、逐模块解析
1. 导入依赖库
python
import numpy as np
import matplotlib.pyplot as plt
numpy:提供高效的数值计算(数组运算、矩阵求逆等),是SG滤波的数学核心依赖。matplotlib.pyplot:用于可视化原始信号、噪声信号、滤波结果的对比。
2. 模拟光谱数据生成 generate_simulation_data
python
def generate_simulation_data(n_points=1000, noise_std=0.05):
x = np.linspace(0, 10, n_points)
# 两个高斯峰
peak1 = np.exp(-(x - 3) ** 2 / 0.1)
peak2 = np.exp(-(x - 7) ** 2 / 0.2)
y_raw = peak1 + peak2
# 加入高斯白噪声
y_noisy = y_raw + np.random.normal(0, noise_std, n_points)
return x, y_raw, y_noisy
- 参数 :
n_points(采样点数,默认1000)、noise_std(高斯白噪声标准差,默认0.05,模拟光谱测量噪声)。 - 自变量x :用
np.linspace生成[0,10]范围内的均匀采样波长(光谱常见的自变量类型)。 - 高斯峰构造 :单个高斯峰的数学形式为 f(x)=exp(−(x−μ)2γ)f(x) = \exp\left(-\frac{(x-\mu)^2}{\gamma}\right)f(x)=exp(−γ(x−μ)2),其中:
peak1:μ=3(峰位),γ=0.1(带宽,γ越小峰越窄);peak2:μ=7(峰位),γ=0.2(带宽,比peak1宽);
两个峰叠加得到无噪声原始光谱y_raw。
- 噪声叠加 :用
np.random.normal生成均值为0、标准差为noise_std的高斯白噪声,叠加到原始光谱上得到带噪声光谱y_noisy。
3. SG滤波系数计算 compute_sg_coefficients(数学核心!)
python
def compute_sg_coefficients(window_size, poly_order):
# 约束检查
if window_size % 2 == 0:
raise ValueError("Window size must be odd")
if window_size <= poly_order:
raise ValueError("Window size must be larger than polynomial order")
half_window = (window_size - 1) // 2
t = np.arange(-half_window, half_window + 1) # 中心化位置坐标
X = np.zeros((window_size, poly_order + 1)) # 设计矩阵
for i in range(window_size):
for j in range(poly_order + 1):
X[i, j] = t[i] ** j
XTX = np.dot(X.T, X)
XTX_inv = np.linalg.inv(XTX)
e1 = np.zeros(poly_order + 1)
e1[0] = 1
coeffs = np.dot(np.dot(X, XTX_inv), e1)
return coeffs
3.1 约束检查的数学意义
window_size % 2 == 0:窗口大小必须为奇数,保证窗口存在「中心采样点」(SG滤波的核心是取窗口中心的拟合值)。window_size <= poly_order:窗口大小必须大于多项式阶数,若等于则多项式拟合会过拟合窗口内的噪声,且正规方程矩阵不可逆。
3.2 设计矩阵与最小二乘法推导
SG滤波的本质是:在滑动窗口内用k阶多项式拟合信号,取拟合多项式在窗口中心的值作为滤波输出。
- 中心化坐标t :设窗口半宽为
M=half_window,则窗口内的位置坐标为 t∈[−M,−M+1,...,0,...,M−1,M]t \in [-M, -M+1, ..., 0, ..., M-1, M]t∈[−M,−M+1,...,0,...,M−1,M](以窗口中心为0点,简化计算)。 - k阶多项式模型 :设拟合多项式为 P(t)=a0+a1t+a2t2+...+aktkP(t) = a_0 + a_1 t + a_2 t^2 + ... + a_k t^kP(t)=a0+a1t+a2t2+...+aktk,其中 k=polyorderk=poly_orderk=polyorder,a=[a0,a1,...,ak]T\mathbf{a} = [a_0, a_1, ..., a_k]^Ta=[a0,a1,...,ak]T 为多项式系数。
- 设计矩阵X :形状为
(window_size, poly_order+1),每行对应窗口内的一个采样点,列对应 t0,t1,...,tkt^0, t^1, ..., t^kt0,t1,...,tk,即:
X=[t10t11...t1kt20t21...t2k............tW0tW1...tWk](W=window_size)X = \begin{bmatrix} t_1^0 & t_1^1 & ... & t_1^k \\ t_2^0 & t_2^1 & ... & t_2^k \\ ... & ... & ... & ... \\ t_W^0 & t_W^1 & ... & t_W^k \end{bmatrix} \quad (W=window\_size)X= t10t20...tW0t11t21...tW1............t1kt2k...tWk (W=window_size) - 最小二乘解 :最小化窗口内信号 yw\mathbf{y}_wyw 与拟合值 XaX\mathbf{a}Xa 的残差平方和,得到正规方程:
XTXa=XTyw ⟹ a=(XTX)−1XTywX^T X \mathbf{a} = X^T \mathbf{y}_w \implies \mathbf{a} = (X^T X)^{-1} X^T \mathbf{y}_wXTXa=XTyw⟹a=(XTX)−1XTyw - 滤波系数coeffs的计算 :
- 拟合多项式在窗口中心t=0处的值 为 P(0)=a0P(0) = a_0P(0)=a0(因为 t=0t=0t=0 时高阶项全为0);
- 用单位向量 e1=[1,0,...,0]T\mathbf{e}_1 = [1, 0, ..., 0]^Te1=[1,0,...,0]T 提取 a0a_0a0,即 a0=e1Taa_0 = \mathbf{e}_1^T \mathbf{a}a0=e1Ta;
- 代入最小二乘解,得到:
P(0)=e1T(XTX)−1XTywP(0) = \mathbf{e}_1^T (X^T X)^{-1} X^T \mathbf{y}_wP(0)=e1T(XTX)−1XTyw - 令 coeffs=X(XTX)−1e1\text{coeffs} = X (X^T X)^{-1} \mathbf{e}_1coeffs=X(XTX)−1e1,则 P(0)=ywT⋅coeffsP(0) = \mathbf{y}_w^T \cdot \text{coeffs}P(0)=ywT⋅coeffs(内积运算),这就是代码中滤波系数的物理意义。
4. SG滤波应用 sg_filter
python
def sg_filter(y, window_size, poly_order):
coeffs = compute_sg_coefficients(window_size, poly_order)
half_window = (window_size - 1) // 2
n = len(y)
y_smooth = np.zeros(n)
# 中间样本处理(无边界问题)
for i in range(half_window, n - half_window):
window = y[i - half_window: i + half_window + 1]
y_smooth[i] = np.dot(window, coeffs)
# 左边界处理(镜像扩展避免失真)
for i in range(half_window):
mirror_left = y[half_window - i: half_window + 1]
mirror_right = y[half_window: half_window + (half_window - i)] if i < half_window else []
window = np.concatenate([mirror_left, mirror_right])
y_smooth[i] = np.dot(window, coeffs[:len(window)])
# 右边界处理(镜像扩展避免失真)
for i in range(n - half_window, n):
mirror_right = y[n - half_window - 1: i][::-1]
mirror_left = y[i: i + (n - i - 1)][::-1] if i < n - 1 else []
window = np.concatenate([mirror_left, mirror_right])
y_smooth[i] = np.dot(window, coeffs[-len(window):])
return y_smooth
4.1 中间样本处理
- 当窗口中心
i满足half_window ≤ i < n - half_window时,窗口完全落在信号内部,直接提取窗口内的y值与滤波系数coeffs做内积,得到滤波结果。
4.2 边界处理(关键优化!)
当窗口中心i接近信号两端时,窗口会超出信号范围,若直接截断会导致边界失真。代码采用镜像扩展策略:
- 左边界 :将窗口超出左边界的部分,用中心右侧的对称信号镜像填充(如
i=0时,用y[0], y[1], ..., y[M]的镜像替代左半部分)。 - 右边界 :将窗口超出右边界的部分,用中心左侧的对称信号镜像填充(如
i=n-1时,用y[n-1], y[n-2], ..., y[n-M-1]的镜像替代右半部分)。 - 扩展后的窗口与对应长度的滤波系数(左边界用前半系数,右边界用后半系数)做内积,保证边界滤波的连续性。
5. 主程序与可视化
python
if __name__ == "__main__":
# 生成模拟数据
x, y_raw, y_noisy = generate_simulation_data()
# SG滤波参数设置(常用组合:窗口21,3次多项式)
window_size = 21
poly_order = 3
# 应用滤波
y_smoothed = sg_filter(y_noisy, window_size, poly_order)
# 可视化对比
plt.figure(figsize=(10, 6))
plt.plot(x, y_raw, 'k--', label='Original Signal', linewidth=2)
plt.plot(x, y_noisy, 'r', label='Noisy Signal', alpha=0.5)
plt.plot(x, y_smoothed, 'b', label='SG Smoothed Signal', linewidth=2)
plt.xlabel('Wavelength')
plt.ylabel('Intensity')
plt.title('Savitzky-Golay Filtering Demonstration')
plt.legend()
plt.grid(True)
plt.show()
- 参数设置 :
window_size=21(窗口半宽10,覆盖21个采样点)、poly_order=3(3次多项式,平衡噪声抑制与特征保留)。 - 可视化 :
- 黑色虚线:无噪声原始光谱(真实特征);
- 红色半透明:带噪声光谱(模拟测量数据);
- 蓝色实线:SG滤波后的光谱(噪声抑制效果);
- 清晰对比SG滤波在保留高斯峰形、峰位的同时,有效抑制噪声的效果。
三、建模亮点与优化方向
- 模拟数据合理性:高斯峰+高斯白噪声符合光谱测量的真实场景,适合滤波算法的验证。
- SG滤波优势 :相比滑动平均滤波,SG滤波能保留信号的高阶导数信息(如峰的斜率、曲率),是光谱特征提取、导数光谱计算的首选方法。
- 边界处理优化:镜像扩展避免了边界失真,比零填充、截断更适合光谱等连续信号。
- 参数调优空间 :
- 窗口大小:增大窗口可增强去噪效果,但易平滑峰形;减小窗口则保留细节但去噪不足。
- 多项式阶数:阶数越高越易拟合噪声,阶数越低越易丢失特征(通常选择2-4阶)。
以上解析结合了数学原理与代码逻辑,覆盖了SG滤波的核心思想、实现细节与建模应用,可直接用于论文撰写或队友讲解。