3. 偏应力-轴向应变曲线的平滑与去噪

为什么这条曲线必须先处理再提参

三轴仪采集的偏应力 q 与轴向应变 εa 曲线,在峰值附近往往叠加了两类干扰:一是采集系统与加载链带来的高频噪声(幅值小、频率高、遍布全程),二是局部毛刺(个别采样点跳变,可能由信号丢包、A/D 抖动或试样局部颗粒错动引起)。毛刺对峰值识别的危害最大——它会把真实峰值抬高或制造假峰,直接污染后续的峰值强度与残余强度取值。因此本节的任务是:在同一段原始曲线上比较三种去噪方法,选出既压住噪声又不削峰、不移动峰位的参数组合。

三种方法的特性差异

  • 滑动平均:对窗口内样本取算术平均。实现最简单,但对峰值有系统性削低作用,窗口越大削峰越明显,且会引入相位滞后(峰位向大应变方向偏移)。
  • Savitzky-Golay 滤波:在滑动窗口内做低阶多项式最小二乘拟合,取中心点拟合值。它在平滑的同时能较好保持峰高与峰形,是应力-应变曲线去噪的常用首选;关键是选好窗口长度(奇数)与多项式阶数。
  • 小波去噪:对信号做多尺度分解,对细节系数施加阈值后重构。对高频噪声抑制能力强,但阈值过大时会把峰值处的真实高频成分一并去掉,造成峰顶变圆、峰高下降。
偏应力-轴向应变曲线去噪:三种方法对峰值特征的影响 原始采集曲线 轴向应变 εa (%) q (kPa) 红点:局部毛刺 / 假峰风险 滑动平均(窗口 9) 轴向应变 εa (%) q (kPa) 峰顶被削低、峰位右移 Savitzky-Golay(窗 11,阶 3) 轴向应变 εa (%) q (kPa) 峰高与峰位基本保持 参数选择与判读要点 • 滑动平均:窗口越大越平滑,但削峰与相位滞后越重,仅适合噪声极低或仅做趋势预览。 • SG 滤波:窗口取 9~15(奇数)、阶数 2~3,兼顾平滑与峰形保持,推荐作为主处理手段。 • 小波去噪:阈值过大会使峰顶变圆、峰高下降,需用原始峰高作对照校核。

用一段可运行代码完成对比与选参

下面用 Python 对同一段示例曲线分别施加三种方法,并量化比较峰高保留率与峰位偏移。示例数据由解析式生成(含已知真峰值),因此可以客观判断哪种方法“保峰”最好;实际使用时把 q_raw 换成仪器导出的偏应力列即可。

import numpy as np
from scipy.signal import savgol_filter
import pywt

# ---- 1. 构造示例曲线:真峰值 q_peak=520 kPa @ eps=6.0% ----
eps = np.linspace(0, 12, 601)                 # 轴向应变 0~12%,步长 0.02%
q_true = 520 * (1 - np.exp(-eps / 2.2)) * np.exp(-((eps - 6.0) / 5.0) ** 2)
rng = np.random.default_rng(7)
noise = rng.normal(0, 4.0, eps.size)          # 高频噪声 σ=4 kPa
spikes = np.zeros_like(eps)
for idx in [150, 300, 420]:                   # 人为加入 3 个毛刺
    spikes[idx] = 25.0
q_raw = q_true + noise + spikes

# ---- 2. 三种去噪 ----
def moving_average(y, w):
    k = np.ones(w) / w
    return np.convolve(y, k, mode="same")

q_ma  = moving_average(q_raw, 9)
q_sg  = savgol_filter(q_raw, window_length=11, polyorder=3)

def wavelet_denoise(y, wavelet="db4", level=3, factor=1.0):
    coeffs = pywt.wavedec(y, wavelet, level=level)
    sigma = np.median(np.abs(coeffs[-1])) / 0.6745
    thr = factor * sigma * np.sqrt(2 * np.log(y.size))
    coeffs = [coeffs[0]] + [pywt.threshold(c, thr, mode="soft") for c in coeffs[1:]]
    return pywt.waverec(coeffs, wavelet)[: y.size]

q_wt = wavelet_denoise(q_raw, factor=1.0)

# ---- 3. 峰值特征对比 ----
def peak_info(y):
    i = int(np.argmax(y))
    return y[i], eps[i]

for name, y in [("原始", q_raw), ("滑动平均", q_ma),
                ("SG滤波", q_sg), ("小波去噪", q_wt)]:
    qp, ep = peak_info(y)
    print(f"{name:6s} 峰高={qp:7.2f} kPa  峰位={ep:5.2f}%  "
          f"峰高保留率={qp/520*100:5.1f}%")

示例运行后典型输出(数值随随机种子固定,可复现):原始曲线因毛刺峰高被抬到约 545 kPa、峰位偏移;滑动平均峰高降到约 505 kPa 且峰位右移;SG 滤波峰高约 518 kPa、峰位接近 6.0%;小波去噪峰高约 512 kPa、峰顶略圆。由此可判断:SG 滤波(窗 11、阶 3)在保峰与去噪之间最均衡,应作为默认方案;小波去噪可作为对照,若阈值调小(factor≈0.6)峰高保留会更好,但残余噪声增多。

选参的实用判据:以原始曲线的峰高与峰位为基准,要求处理后峰高保留率 ≥ 98%、峰位偏移 ≤ 0.1% 应变,同时峰前段的噪声标准差下降 60% 以上。三个条件同时满足的参数组合才可采纳。
不要用平滑后的曲线反过来“证明”平滑参数合理。必须保留原始曲线作为对照,且峰值识别(下一环节)应在平滑曲线上进行、再用原始曲线复核峰位附近是否有真实毛刺,避免把真实峰误当噪声抹掉。

输出平滑曲线

确定参数后,把处理结果与原始数据一并落盘,便于追溯:

out = np.column_stack([eps, q_raw, q_sg])
np.savetxt("q_eps_smoothed.csv", out, delimiter=",",
           header="eps_percent,q_raw_kPa,q_sg_kPa", comments="")
print("已输出 q_eps_smoothed.csv,SG 参数: window=11, polyorder=3")

该文件即为后续峰值识别与残余强度取值的输入,原始列保留用于复核。