You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现Butterworth带通滤波器后输出与输入信号一致的排查求助

解决Butterworth带通滤波未生效的问题

直接说你代码里的核心问题和对应修复方法:

1. DataFrame浅拷贝导致原始数据被篡改

你写的smooth_df = original_df是浅拷贝,修改smooth_df的列时,original_df会被同步修改,最后对比的其实是同一个数据,自然看不出差异。

修复:

smooth_df = original_df.copy()  # 改成深拷贝,彻底分离原始数据和滤波后数据

2. 滤波器参数重复归一化导致全通滤波

scipy的butter函数在传入fs参数时,Wn应该直接填实际频率值(Hz),不需要手动除以nyquist归一化。你现在手动归一化后又传入fs,相当于把截止频率缩小了一倍,导致滤波器的通带几乎覆盖整个信号频率,变成了全通滤波,输出自然和输入一致。

修复:
删掉手动归一化的代码,直接传原始截止频率:

cutoff_high = 4.0
cutoff_low = 0.01

# 去掉归一化步骤,直接传实际频率给Wn
b, a = butter(N=3, Wn=[cutoff_low, cutoff_high],
              btype='bandpass', fs=con.sample_rate_ET)

3. 频率响应图的错误

你用freqs(b,a)得到的是角频率(rad/s),而且画的axvline(100)和你的截止频率完全不相关,根本没法验证滤波器是否正确。

修复:用freqz替代freqs,指定fs参数让x轴显示实际频率(Hz),并画正确的截止线:

# 放到循环内部,每个列都生成对应的响应图
w, h = freqz(b, a, fs=con.sample_rate_ET)
plt.semilogx(w, 20 * np.log10(abs(h)))
plt.title(f'Butterworth带通滤波器频率响应({column}列)')
plt.xlabel('频率 [Hz]')
plt.ylabel('幅度 [dB]')
plt.margins(0, 0.1)
plt.grid(which='both', axis='both')
plt.axvline(cutoff_low, color='green', linestyle='--', label=f'低截止 {cutoff_low}Hz')
plt.axvline(cutoff_high, color='red', linestyle='--', label=f'高截止 {cutoff_high}Hz')
plt.legend()
plt.show()

其他细节优化

  • 把打印语句print("Data will be now be smoothed with a low-pass BW filter.")改成带通的描述,避免混淆。
  • 确认con.sample_rate_ET确实是当前数据集的采样率(比如120Hz),如果这个值不对,滤波器参数也会失效。

修复后的关键代码片段

def smooth(columns_to_smooth):
    original_df = mask(mask_cols=['diameter', 'diameter_3d'])
    smooth_df = original_df.copy()  # 深拷贝分离数据

    logging.basicConfig(filename='filtering_errors.log', level=logging.ERROR)

    print("将使用Butterworth带通滤波器平滑数据。")

    cutoff_high = 4.0
    cutoff_low = 0.01

    for column in columns_to_smooth:
        try:
            if smooth_df[column].isnull().any():
                raise ValueError(f"列 {column} 中检测到NaN值,无法滤波")

            # 直接传入实际频率和采样率
            b, a = butter(N=3, Wn=[cutoff_low, cutoff_high],
                          btype='bandpass', fs=con.sample_rate_ET)
            smooth_values = filtfilt(b, a, smooth_df[column])

            if np.isnan(smooth_values).any():
                raise ValueError(f"列 {column} 滤波后出现NaN值")

            smooth_df[column] = smooth_values

            # 绘制当前列的频率响应图
            w, h = freqz(b, a, fs=con.sample_rate_ET)
            plt.semilogx(w, 20 * np.log10(abs(h)))
            plt.title(f'Butterworth带通滤波器频率响应({column}列)')
            plt.xlabel('频率 [Hz]')
            plt.ylabel('幅度 [dB]')
            plt.margins(0, 0.1)
            plt.grid(which='both', axis='both')
            plt.axvline(cutoff_low, color='green', linestyle='--', label=f'低截止 {cutoff_low}Hz')
            plt.axvline(cutoff_high, color='red', linestyle='--', label=f'高截止 {cutoff_high}Hz')
            plt.legend()
            plt.show()

        except ValueError as e:
            print(f"错误: {e}")

    smooth_df.to_csv(con.SMOOTHED_PUPIL, index=False)

    # 对比绘图
    plt.plot(original_df['pupil_timestamp'], original_df['diameter'], label='原始数据', alpha=0.7)
    plt.plot(smooth_df['pupil_timestamp'], smooth_df['diameter'], label='滤波后数据', alpha=0.7)
    plt.xlabel('时间戳')
    plt.ylabel('瞳孔直径')
    plt.title('原始数据与滤波后数据对比')
    plt.legend()
    plt.show()

    # 统计信息输出
    original_mean = original_df['diameter'].mean()
    smoothed_mean = smooth_df['diameter'].mean()
    original_std = original_df['diameter'].std()
    smoothed_std = smooth_df['diameter'].std()

    print(f"原始均值: {original_mean:.4f}, 滤波后均值: {smoothed_mean:.4f}")
    print(f"原始标准差: {original_std:.4f}, 滤波后标准差: {smoothed_std:.4f}")

验证步骤

  1. 先看频率响应图,确认0.01Hz以下和4Hz以上的频率幅度被衰减(低于0dB)。
  2. 对比数据图,应该能看到滤波后的数据更平滑,高频噪声被去除。
  3. 检查统计信息,滤波后的数据标准差会明显变小。

内容的提问来源于stack exchange,提问作者Fiza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:44:52