巴特沃斯滤波器和陷波滤波器的Python实现(基于Scipy)

巴特沃斯滤波器和陷波滤波器在信号处理中很常见,很多论文中也会有所涉及,前者可以滤除高频段、低频段信号或者通过一定频率的信号,后者可用于滤除特定频率的信号,本人并非通信专业,在信号的理论知识方面比较匮乏,因此本文主要围绕代码实现展开。

本人在查找资料的过程中,发现很多博客对函数的介绍都是一笔带过,很少展开说明,因此本文会比较详细说明各个函数的使用方法还有最终结果的实现效果,就当做是做个记录。

数据生成

为了后续演示,我生成了一个三个三角函数叠加的图像作为后面的滤波输入信号

plt.rcParams['font.sans-serif'] = ['SimHei']  # 显示中文
plt.rcParams['axes.unicode_minus'] = False
fs = 300.0  # 采样频率
T = 5.0  # 周期5s
n = int(T * fs)  # 总样本数
t = np.linspace(0, T, n, endpoint=False)
y_1 = np.sin(2.4 * 2 * np.pi * t)  
y_2 = np.sin(9 * 2 * np.pi * t) 
y_3 = np.sin(12 * 2 * np.pi * t) 
data = y_1 + y_2 + y_3  # 生成一个三个函数叠加的图像
plt.plot(t, data)
plt.title('原始图像')
plt.show()

三角函数sin和cos的频率计算公式为w/(2*pi),所以三个函数的频率分别是2.4、9、12。

原始图像的图像如下

巴特沃斯滤波器(Butterworth Filter)

首先是巴特沃斯滤波器,实现的库是signal.butter,库中可以实现的功能有高通、低通、带通和带阻,其中低通比较常用,一般用于提取信号信息,也是该库的默认滤波方式,高通用于锐化信号,带通通过指定频率内的频率,带阻阻止一段频率内的信号通过,有点像是陷波滤波方式。

实现butterworth的函数如下:

b, a = butter(N=order, Wn=normal_f, btype='lowpass', analog=False)

其中:

N表示阶数;

Wn表示的是标准化频率,计算公式为2*截止频率/采样频率;

btype的参数可以是{‘lowpass’, ‘highpass’, ‘bandpass’, ‘bandstop’},分别是低通、高通、带通、带阻,默认是低通,如果是带通的范围是一个二元列表,因为是一个范围,分别是上限和下限频率;analog为False表示返回数字信号,否则是模拟信号(这个我也不太懂)。

下面是代码,实现的是低通滤波:

def butter_filter(data, fs, cutoff, order=5):
    normal_f = 2*np.array(cutoff)/fs
    b, a = butter(N=order, Wn=normal_f, btype='lowpass', analog=False)
    # 根据阶数n和归一化截止频率Wn计算ButterWorth滤波器分子分母系数(b为分子系数的矢量形式,a为分母系数的矢量形式)
    return np.array(filtfilt(b, a, data)),b,a  

# 滤波参数并绘制频率响应图
cutoff = 3.667  # 设定截止频率,频率为截止频率时信号是输入信号的0.707倍
order = 5 # 设定滤波器阶数,阶数越高那么越能够有效地抑制不需要的频率成分
y,b,a = butter_lowpass_filter(data, fs, cutoff, order)  # 小数据集
w, h = freqz(b, a, worN=800)  # 计算滤波器的频率响应(Response),也就是对不同频段的放大倍数,0.707倍的是截止频率
plt.subplot(2, 1, 1)
plt.plot(0.5 * fs * w / np.pi, np.abs(h), 'b')
plt.plot(cutoff, 0.5 * np.sqrt(2), 'ko')
plt.axvline(cutoff, color='k')
plt.xlim(0, 10)
plt.title("频率响应图")
plt.xlabel('频率')
plt.grid()  # 画线
plt.subplot(2, 1, 2)
plt.plot(t, data, 'b-', label='滤波前')
plt.plot(t, y, 'g-', linewidth=2, label='滤波后')
plt.xlabel('时间')
plt.grid()
plt.legend()
plt.subplots_adjust(hspace=0.35)
plt.show()

运行图像,观察第一张图片即频率响应图,可以看到在截止频率3.6处信号的放大倍数是0.7左右,在频率为8之后就几乎为0了;看第二张图也就是滤波频率信号展示,可以看到信号变为了接近一条三角函数,这个就是频率为2.4的那一条曲线,因为高频段的信号(f=9&f=12)已经被滤掉了。

 同时画出频率为2.4的和滤波后的图像,发现几乎完全重合。

plt.plot(t,y,'b-', label='滤波后图像')
plt.plot(t,y_1,'y',label='f=2.4图像')
plt.legend()
plt.xlabel('频率')
plt.title('函数对比')
plt.show()

本部分完整代码

import numpy as np
from matplotlib import pyplot as plt
from scipy import signal
from scipy.signal import lfilter, butter, filtfilt, freqz


def butter_filter(data, fs, cutoff, order=5):
    normal_f = 2*np.array(cutoff)/fs
    b, a = butter(N=order, Wn=normal_f, btype='lowpass', analog=False)
    # 根据阶数n和归一化截止频率Wn计算ButterWorth滤波器分子分母系数(b为分子系数的矢量形式,a为分母系数的矢量形式)
    return np.array(filtfilt(b, a, data)),b,a  


if __name__ == '__main__':
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 显示中文
    plt.rcParams['axes.unicode_minus'] = False
    fs = 300.0  # 采样频率
    T = 5.0  # 周期5s
    n = int(T * fs)  # 总样本数
    t = np.linspace(0, T, n, endpoint=False)
    y_1 = np.sin(2.4 * 2 * np.pi * t) 
    y_2 = np.sin(9 * 2 * np.pi * t)  
    y_3 = np.sin(12 * 2 * np.pi * t)  
    data = y_1 + y_2 + y_3  # 生成一个三个函数叠加的图像
    plt.plot(t, data)
    plt.title('原始图像')
    plt.show()

    # 滤波参数并绘制频率响应图
    cutoff = 3.667  # 设定截止频率,频率为截止频率时信号是输入信号的0.707倍
    order = 5 # 设定滤波器阶数,阶数越高那么越能够有效地抑制不需要的频率成分
    y,b,a = butter_filter(data, fs, cutoff, order)  # 小数据集
    w, h = freqz(b, a, worN=800)  # 计算滤波器的频率响应(Response),也就是对不同频段的放大倍数,0.707倍的是截止频率
    plt.subplot(2, 1, 1)
    plt.plot(0.5 * fs * w / np.pi, np.abs(h), 'b')
    plt.plot(cutoff, 0.5 * np.sqrt(2), 'ko')
    plt.axvline(cutoff, color='k')
    plt.xlim(0, 10)
    plt.title("频率响应图")
    plt.xlabel('频率')
    plt.grid()  # 画线
    plt.subplot(2, 1, 2)
    plt.plot(t, data, 'b-', label='滤波前')
    plt.plot(t, y, 'g-', linewidth=2, label='滤波后')
    plt.xlabel('时间')
    plt.grid()
    plt.legend()
    plt.subplots_adjust(hspace=0.35)
    plt.show()
    
    # 绘制对比图像 
    plt.plot(t,y,'b-', label='滤波后图像')
    plt.plot(t,y_1,'y',label='f=2.4图像')
    plt.legend()
    plt.xlabel('频率')
    plt.title('函数对比')
    plt.show()

 陷波滤波器(Norch Filter)

陷波滤波器主要用于滤除特定频率的信号,比如50Hz的信号是交流电信号,很多时候就会被滤掉。

要实现该滤波器,也是要计算两个参数a和b,函数如下:

b,a = signal.iirnotch(w0, Q)

其中:

w0表示标准化频率,计算方式和上面的巴特沃斯一致;

Q表示质量因数,越大似乎是滤波范围越窄。

下面是代码的实现,原始的图像还是跟上面的巴特沃斯一致,我直接写在一起了。

def notch_filter(data,fs,Q,f_remove):
    w0 = 2*f_remove / fs
    b, a = signal.iirnotch(w0, Q)
    return np.array(filtfilt(b, a, data)),b,a

# 滤波参数并绘制频率响应图
f_remove = 9  # 设定截止频率,频率为截止频率时信号是输入信号的0.707倍
Q = 300 # Q表示质量因数,越大滤波的范围越窄(目标频率周围的)
y,b,a = notch_filter(data, fs, Q,f_remove)  # 小数据集
w, h = freqz(b, a, worN=800)  # 计算滤波器的频率响应(Response),也就是对不同频段的放大倍数,0.707倍的是截止频率
plt.subplot(2, 1, 1)
plt.plot(0.5 * fs * w / np.pi, np.abs(h), 'b')
plt.plot(f_remove, 0.5 * np.sqrt(2), 'ko')
plt.axvline(f_remove, color='k')
plt.xlim(0, 10)
plt.title("频率响应图")
plt.xlabel('频率')
plt.grid()  # 画线
plt.subplot(2, 1, 2)
plt.plot(t, data, 'b-', label='滤波前')
plt.plot(t, y, 'g-', linewidth=2, label='滤波后')
plt.xlabel('时间')
plt.grid()
plt.legend()
plt.subplots_adjust(hspace=0.35)
plt.show()

plt.plot(t,y,'b-', label='滤波后图像')
plt.plot(t,y_1+y_3,'y',label='f=2.4图像')
plt.legend()
plt.xlabel('频率')
plt.title('函数对比')
plt.show()

 我设定的滤波频率为f=9,可以看到频率响应图中的9处已经为0,看第二个图,滤波后的图像应该还有两个三角函数。 

画出两个三角函数叠加和滤波后的图的对比,发现几乎完全重合,说明频率为9的图像被滤掉了。

本部分完整代码

import numpy as np
from matplotlib import pyplot as plt
from scipy import signal
from scipy.signal import lfilter, butter, filtfilt, freqz


def notch_filter(data,fs,Q,f_remove):
    w0 = 2*f_remove / fs
    b, a = signal.iirnotch(w0, Q)
    return np.array(filtfilt(b, a, data)),b,a

if __name__ == '__main__':
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 显示中文
    plt.rcParams['axes.unicode_minus'] = False
    fs = 300.0  # 采样频率
    T = 5.0  # 周期5s
    n = int(T * fs)  # 总样本数
    t = np.linspace(0, T, n, endpoint=False)
    y_1 = np.sin(2.4 * 2 * np.pi * t)
    y_2 = np.sin(9 * 2 * np.pi * t)
    y_3 = np.sin(12 * 2 * np.pi * t)
    data = y_1 + y_2 + y_3  # 生成一个三个函数叠加的图像
    plt.plot(t, data)
    plt.title('原始图像')
    plt.show()

    # 滤波参数并绘制频率响应图
    f_remove = 9  # 设定截止频率,频率为截止频率时信号是输入信号的0.707倍
    Q = 30 # Q表示质量因数,越大滤波的范围越窄(目标频率周围的)
    y,b,a = notch_filter(data, fs, Q,f_remove)  # 小数据集
    w, h = freqz(b, a, worN=800)  # 计算滤波器的频率响应(Response),也就是对不同频段的放大倍数,0.707倍的是截止频率
    plt.subplot(2, 1, 1)
    plt.plot(0.5 * fs * w / np.pi, np.abs(h), 'b')
    plt.plot(f_remove, 0.5 * np.sqrt(2), 'ko')
    plt.axvline(f_remove, color='k')
    plt.xlim(0, 10)
    plt.title("频率响应图")
    plt.xlabel('频率')
    plt.grid()  # 画线
    plt.subplot(2, 1, 2)
    plt.plot(t, data, 'b-', label='滤波前')
    plt.plot(t, y, 'g-', linewidth=2, label='滤波后')
    plt.xlabel('时间')
    plt.grid()
    plt.legend()
    plt.subplots_adjust(hspace=0.35)
    plt.show()

    plt.plot(t,y,'b-', label='滤波后图像')
    plt.plot(t,y_1+y_3,'y',label='f=2.4+f=12图像')
    plt.legend()
    plt.xlabel('频率')
    plt.title('函数对比')
    plt.show()

就先到这里了,有机会再加一些其他的

猜你喜欢

转载自blog.csdn.net/weixin_60360239/article/details/131827400