Python实现所有算法-音频过滤器.下(巴特沃斯)

原创 云深之无迹 2022-07-16 17:33


上节简单的写了一下音频滤波器的定义和作用。而这篇文章将主要集中精力在巴特沃斯过滤器上,在末尾将会给出:使用 Butterworth 设计的二阶 IIR 滤波器。


另外,因为微信这个垃圾的公式排版,我也使用了:


来进行一个排版


$H(z)=\frac{b_{0}+b_{1}z^{-1}+b_{2}z^{-2}+...+b_{k}z^{-k}}{a_{0}+a_{1}z^{-1}+a_{2}z^{-2}+...+a_{k}z^{-k}}$

在构建N阶IIR滤波器的时候,使用的传递函数是这样的。


这个


然后会重写成这样:


y[n]={\frac{1}{a_{0}}}\left(\left(b_{0}x[n]+b_{1}x[n-1]+b_{2}x[n-2]+...+b_{k}x[n-k]\right)-\left(a_{1}y[n-1]+a_{2}y[n-2]+...+a_{k}y[n-k]\right)\right)


因为这个重写的公式太长了,截图就是这样的


接下来就是大家不爱看的东西了,我尽量选通俗易懂的话。先解释频繁出现的IIR滤波器是什么?

IIR(Infinite Impulse Response)——无限脉冲响应滤波器,是数字信号处理的基本组成部分。


1、IIR滤波器是什么

IIR滤波器是用于数字信号处理(DSP)应用的两种主要数字滤波器之一(另一种是FIR)。“IIR”的意思是“无限脉冲响应”。


2、 IIR为什么脉冲响应是“无限的”?

脉冲响应是“无限”的,因为滤波器中有反馈;如果你输入一个脉冲(一个“1”样本后面跟着多个“0”样本),理论上就会输出无限个非零值。


3、 IIR过滤器的替代方案是什么?

DSP滤波器也可以是“有限脉冲响应”(FIR)。FIR滤波器不使用反馈,所以对于N个系数的FIR滤波器,输入N个脉冲响应的样本后输出总是为零。


 4、IIR滤波器(与FIR滤波器相比)的优点是什么?

与类似的FIR滤波器相比,IIR滤波器可以用更少的内存和计算来实现给定的滤波特性。


5、IIR滤波器(与FIR滤波器相比)的缺点是什么?


(1)它们更容易受到有限长度算法问题的影响,比如计算产生的噪声和极限环。(这是反馈造成的直接结果:当输出没有被完美地计算出来并得到反馈时,不完美可能会加剧。)


(2)它们使用定点算法更难(更慢)实现.


(3)对于多速率(抽取和插值)应用,它们没有FIR滤波器的计算优势。


再说一个,什么叫线性时不变系统?


一、线性

通信系统中的线性不再是数学中坐标轴上的直线,也不是所有的直线都符合线性特征,通信系统中的线性要满足一个条件。


为什么线性系统怎么重要。如果已知一个系统是线性系统,一旦有了任何特定输入的输出,就可以在不进行实际测试的情况下计算出所有其他输出。这就是线性系统的优点。也就是系统是可控的

二、时不变系统


一句话概括时不变系统:如果你的系统是时不变系统,如果将相同的输入输入到系统中,无论何时将输入输入到系统中,都将获得相同的输出。


那么我们还假设你的系统是时不变系统,你有一个确定的输入,你只需在知道某个时间下这个输入的所有特征。如果你明天还输入相同的输入,那么你的不用重新掌握它的特征。


相对的如果你的系统是时变系统:


投入了一定的投入,花了大量的时间和精力调查了投入的所有特征。但是无法重用结果,因为当重用它时,时间不同,结果也会不同。就像。。。你有一台电脑。每次运行完全相同的程序时,都会得到不同的结果。


唯一可以重用结果的情况是进一步努力找出规则,以显示输出是如何随时间变化的。不幸的是,你不能保证你能找到规则。即使你很幸运找到了规则,你的系统模型也会非常复杂。


三、总结


线性系统,你知道一个确定的结果,你就知道其他所有的结果,

时不变系统,你的特定输入,其输出的结果不会随着时间而变化。



这个IIR二阶的传递函数是从wiki里面拿到的



因为这个IIR是给下一级使用的,这里设计成一个类



类的初始化




接下来是使用一个现成的包来验证一下结果



返回的是分子和分母的多项式



计算a,b


设置系数用于 IIR 滤波器。这些都应该是 size order + 1,a_0 可以省略,它将使用 1.0 作为默认值。



这就是这样,下面的都是对参数的约束



a,b就是系数



计算f(n),对着公式编程序



这个就是两项计算的公式



按照这个写的


关于滤波器还有若干的问题需要说明:一阶滤波器,思路就是把一个连续的滤波器形式,通过离散化的方式,转换成差分方程。从wiki或者文章里面拿到的滤波器公式,通常是用传递函数表达的,这是S域下的表达形式,是连续的,这种我们称之为模拟滤波器。模拟滤波器传递函数,目的是用来设计滤波电路,针对的是连续时间的模拟信号,组成元器件是电阻,电容,电感。而数字滤波器实现方法是把滤波器所要完成的运算编成程序并让计算机执行,也就是采用在代码的形式。它面对的是离散时间的数字信号,是把输入序列通过一定的运算变换成输出序列。有没有办法能把连续的模拟滤波器变成离散的数字滤波器?


其中最常使用的一种叫做双线性变换:




把这个公式带入传递函数就可以得到一个z域的差分方程了。


以上变换这段参考:



后面截至频率什么的没有写


但是知道接下来应该看的是:自动控制原理。



我们在BW滤波器里面将要实现这些算法


所有滤波器传递函数均源自模拟原型,并已使用双线性变换 (BLT) 进行数字化。BLT 频率扭曲已被考虑到重要的频率重定位(这是使用 BLT 时必需的正常“预扭曲”)和带宽重新调整(因为使用 BLT 从模拟映射到数字时带宽被压缩)。这个就是上面文章里面说的:




出现这个问题,需要使用数学工具矫正


模拟截止频率与数字截止频率的关系



证明自己看吧,我也看不懂了,越看越觉得自己不该念这个书。。。这是TM的什么人间疾苦。



不过看了下。。。有欧拉公式就行



先定义双线性的函数,在上面写了



经过一次变换



最后才写成这个


省去一段推导,给出结果:



低通滤波器的结果



参数是,频率,采样率,Q值



第一个的滤波器的算法设计


在最后给出一个在双线性变换下使用补偿频率扭曲的补偿算法推导:



首先是归一化的操作



使用这个三角函数



还有这个



对公式进行重构,构个头就是重新带进去



三个



还有因子也重新写


对分子和分母中的所有项都是通用的,可以因式分解,因此在上面的替换中被忽略,所以有:



又重写了


此外,所有项,分子和分母,都可以乘以一个共同的:



好大个系数



最后就是简化出来的双二阶系数公式



啧,万物之源是数学,还不滚去学习。



在最终的演示中



这个错误



说是3.8才有


pip install typing_extensions
from typing_extensions import Protocol



可以写成这样



话说有朝一日我还能拍出这种照片



是不是有点古城的味道了嗷!


考虑到这个算法的复杂性,这里将代码写在了一起,可以直接调用:

from __future__ import annotations# from audio_filters.iir_filter import IIRFilterimport numpy as npimport matplotlib.pyplot as pltfrom typing_extensions import Protocolfrom math import pifrom math import cos, sin, sqrt, tau

class IIRFilter: def __init__(self, order: int) -> None: self.order = order # a_{0} ... a_{k} self.a_coeffs = [1.0] + [0.0] * order # b_{0} ... b_{k} self.b_coeffs = [1.0] + [0.0] * order
# x[n-1] ... x[n-k] self.input_history = [0.0] * self.order # y[n-1] ... y[n-k] self.output_history = [0.0] * self.order
def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None: if len(a_coeffs) < self.order: a_coeffs = [1.0] + a_coeffs if len(a_coeffs) != self.order + 1: raise ValueError( f"预期 a_coeffs to 有 {self.order + 1} elements for {self.order}" f"-order filter, got {len(a_coeffs)}" ) if len(b_coeffs) != self.order + 1: raise ValueError( f"Expected b_coeffs to have {self.order + 1} elements for {self.order}" f"-order filter, got {len(a_coeffs)}" ) self.a_coeffs = a_coeffs self.b_coeffs = b_coeffs
def process(self, sample: float) -> float: result = 0.0 # 从索引 1 开始,最后执行索引 0 for i in range(1, self.order + 1): result += (self.b_coeffs[i] * self.input_history[i-1]-self.a_coeffs[i]*self.output_history[i-1] ) result = (result + self.b_coeffs[0] * sample) / self.a_coeffs[0] self.input_history[1:] = self.input_history[:-1] self.output_history[1:] = self.output_history[:-1] self.input_history[0] = sample self.output_history[0] = result return result

def make_lowpass( frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2)) -> IIRFilter: w0 = tau * frequency / samplerate _sin = sin(w0) _cos = cos(w0) alpha = _sin / (2 * q_factor)
b0 = (1 - _cos) / 2 b1 = 1 - _cos
a0 = 1 + alpha a1 = -2 * _cos a2 = 1 - alpha
filt = IIRFilter(2) filt.set_coefficients([a0, a1, a2], [b0, b1, b0]) return filt

def make_highpass( frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2)) -> IIRFilter: w0 = tau * frequency / samplerate _sin = sin(w0) _cos = cos(w0) alpha = _sin / (2 * q_factor)
b0 = (1 + _cos) / 2 b1 = -1 - _cos
a0 = 1 + alpha a1 = -2 * _cos a2 = 1 - alpha
filt = IIRFilter(2) filt.set_coefficients([a0, a1, a2], [b0, b1, b0]) return filt

def make_bandpass( frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2)) -> IIRFilter: """ 创建带通滤波器
>>> filter = make_bandpass(1000, 48000) >>> filter.a_coeffs + filter.b_coeffs # doctest: +NORMALIZE_WHITESPACE [1.0922959556412573, -1.9828897227476208, 0.9077040443587427, 0.06526309611002579, 0, -0.06526309611002579] """ w0 = tau * frequency / samplerate _sin = sin(w0) _cos = cos(w0) alpha = _sin / (2 * q_factor)
b0 = _sin / 2 b1 = 0 b2 = -b0
a0 = 1 + alpha a1 = -2 * _cos a2 = 1 - alpha
filt = IIRFilter(2) filt.set_coefficients([a0, a1, a2], [b0, b1, b2]) return filt

def make_allpass( frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2)) -> IIRFilter: w0 = tau * frequency / samplerate _sin = sin(w0) _cos = cos(w0) alpha = _sin / (2 * q_factor)
b0 = 1 - alpha b1 = -2 * _cos b2 = 1 + alpha
filt = IIRFilter(2) filt.set_coefficients([b2, b1, b0], [b0, b1, b2]) return filt

def make_peak( frequency: int, samplerate: int, gain_db: float, q_factor: float = 1 / sqrt(2)) -> IIRFilter: w0 = tau * frequency / samplerate _sin = sin(w0) _cos = cos(w0) alpha = _sin / (2 * q_factor) big_a = 10 ** (gain_db / 40)
b0 = 1 + alpha * big_a b1 = -2 * _cos b2 = 1 - alpha * big_a a0 = 1 + alpha / big_a a1 = -2 * _cos a2 = 1 - alpha / big_a
filt = IIRFilter(2) filt.set_coefficients([a0, a1, a2], [b0, b1, b2]) return filt

def make_lowshelf( frequency: int, samplerate: int, gain_db: float, q_factor: float = 1 / sqrt(2)) -> IIRFilter: w0 = tau * frequency / samplerate _sin = sin(w0) _cos = cos(w0) alpha = _sin / (2 * q_factor) big_a = 10 ** (gain_db / 40) pmc = (big_a + 1) - (big_a - 1) * _cos ppmc = (big_a + 1) + (big_a - 1) * _cos mpc = (big_a - 1) - (big_a + 1) * _cos pmpc = (big_a - 1) + (big_a + 1) * _cos aa2 = 2 * sqrt(big_a) * alpha
b0 = big_a * (pmc + aa2) b1 = 2 * big_a * mpc b2 = big_a * (pmc - aa2) a0 = ppmc + aa2 a1 = -2 * pmpc a2 = ppmc - aa2
filt = IIRFilter(2) filt.set_coefficients([a0, a1, a2], [b0, b1, b2]) return filt

def make_highshelf( frequency: int, samplerate: int, gain_db: float, q_factor: float = 1 / sqrt(2)) -> IIRFilter: w0 = tau * frequency / samplerate _sin = sin(w0) _cos = cos(w0) alpha = _sin / (2 * q_factor) big_a = 10 ** (gain_db / 40) pmc = (big_a + 1) - (big_a - 1) * _cos ppmc = (big_a + 1) + (big_a - 1) * _cos mpc = (big_a - 1) - (big_a + 1) * _cos pmpc = (big_a - 1) + (big_a + 1) * _cos aa2 = 2 * sqrt(big_a) * alpha
b0 = big_a * (ppmc + aa2) b1 = -2 * big_a * pmpc b2 = big_a * (ppmc - aa2) a0 = pmc + aa2 a1 = 2 * mpc a2 = pmc - aa2
filt = IIRFilter(2) filt.set_coefficients([a0, a1, a2], [b0, b1, b2]) return filt

class FilterType(Protocol): def process(self, sample: float) -> float: return 0.0

def get_bounds( fft_results: np.ndarray, samplerate: int) -> tuple[int | float, int | float]: lowest = min([-20, np.min(fft_results[1: samplerate // 2 - 1])]) highest = max([20, np.max(fft_results[1: samplerate // 2 - 1])]) return lowest, highest

def show_frequency_response(filter: FilterType, samplerate: int) -> None:
size = 512 inputs = [1] + [0] * (size - 1) outputs = [filter.process(item) for item in inputs]
filler = [0] * (samplerate - size) # zero-padding outputs += filler fft_out = np.abs(np.fft.fft(outputs)) fft_db = 20 * np.log10(fft_out)
# Frequencies on log scale from 24 to nyquist frequency plt.xlim(24, samplerate / 2 - 1) plt.xlabel("Frequency (Hz)") plt.xscale("log")
# Display within reasonable bounds bounds = get_bounds(fft_db, samplerate) plt.ylim(max([-80, bounds[0]]), min([80, bounds[1]])) plt.ylabel("Gain (dB)")
plt.plot(fft_db) plt.show()

def show_phase_response(filter: FilterType, samplerate: int) -> None: size = 512 inputs = [1] + [0] * (size - 1) outputs = [filter.process(item) for item in inputs]
filler = [0] * (samplerate - size) outputs += filler fft_out = np.angle(np.fft.fft(outputs)) plt.xlim(24, samplerate / 2 - 1) plt.xlabel("Frequency (Hz)") plt.xscale("log")
plt.ylim(-2 * pi, 2 * pi) plt.ylabel("Phase shift (Radians)") plt.plot(np.unwrap(fft_out, -2 * pi)) plt.show()

filt = IIRFilter(4)show_phase_response(filt, 48000)


应该是可以直接出现这样的图


https://stackoverflow.com/questions/54274630/can-not-import-protocol-from-typing
https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html
https://blog.csdn.net/abc123mma/article/details/120251384
https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html
https://blog.csdn.net/SHC2012377/article/details/120910496
https://en.wikipedia.org/wiki/Digital_biquad_filter
https://zhuanlan.zhihu.com/p/357619650
https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.butter.html

评论
  • 食物浪费已成为全球亟待解决的严峻挑战,并对环境和经济造成了重大影响。最新统计数据显示,全球高达三分之一的粮食在生产过程中损失或被无谓浪费,这不仅导致了资源消耗,还加剧了温室气体排放,并带来了巨大经济损失。全球领先的光学解决方案供应商艾迈斯欧司朗(SIX:AMS)近日宣布,艾迈斯欧司朗基于AS7341多光谱传感器开发的创新应用来解决食物浪费这一全球性难题。其多光谱传感解决方案为农业与食品行业带来深远变革,该技术通过精确判定最佳收获时机,提升质量控制水平,并在整个供应链中有效减少浪费。 在2024
    艾迈斯欧司朗 2025-01-14 18:45 71浏览
  • PNT、GNSS、GPS均是卫星定位和导航相关领域中的常见缩写词,他们经常会被用到,且在很多情况下会被等同使用或替换使用。我们会把定位导航功能测试叫做PNT性能测试,也会叫做GNSS性能测试。我们会把定位导航终端叫做GNSS模块,也会叫做GPS模块。但是实际上他们之间是有一些重要的区别。伴随着技术发展与越发深入,我们有必要对这三个词汇做以清晰的区分。一、什么是GPS?GPS是Global Positioning System(全球定位系统)的缩写,它是美国建立的全球卫星定位导航系统,是GNSS概
    德思特测试测量 2025-01-13 15:42 508浏览
  • 01. 什么是过程能力分析?过程能力研究利用生产过程中初始一批产品的数据,预测制造过程是否能够稳定地生产符合规格的产品。可以把它想象成一种预测。通过历史数据的分析,推断未来是否可以依赖该工艺持续生产高质量产品。客户可能会要求将过程能力研究作为生产件批准程序 (PPAP) 的一部分。这是为了确保制造过程能够持续稳定地生产合格的产品。02. 基本概念在定义制造过程时,目标是确保生产的零件符合上下规格限 (USL 和 LSL)。过程能力衡量制造过程能多大程度上稳定地生产符合规格的产品。核心概念很简单:
    优思学院 2025-01-12 15:43 537浏览
  • ARMv8-A是ARM公司为满足新需求而重新设计的一个架构,是近20年来ARM架构变动最大的一次。以下是对ARMv8-A的详细介绍: 1. 背景介绍    ARM公司最初并未涉足PC市场,其产品主要针对功耗敏感的移动设备。     随着技术的发展和市场需求的变化,ARM开始扩展到企业设备、服务器等领域,这要求其架构能够支持更大的内存和更复杂的计算任务。 2. 架构特点    ARMv8-A引入了Execution State(执行状
    丙丁先生 2025-01-12 10:30 478浏览
  • 流量传感器是实现对燃气、废气、生活用水、污水、冷却液、石油等各种流体流量精准计量的关键手段。但随着工业自动化、数字化、智能化与低碳化进程的不断加速,采用传统机械式检测方式的流量传感器已不能满足当代流体计量行业对于测量精度、测量范围、使用寿命与维护成本等方面的精细需求。流量传感器的应用场景(部分)超声波流量传感器,是一种利用超声波技术测量流体流量的新型传感器,其主要通过发射超声波信号并接收反射回来的信号,根据超声波在流体中传播的时间、幅度或相位变化等参数,间接计算流体的流量,具有非侵入式测量、高精
    华普微HOPERF 2025-01-13 14:18 500浏览
  •   在信号处理过程中,由于信号的时域截断会导致频谱扩展泄露现象。那么导致频谱泄露发生的根本原因是什么?又该采取什么样的改善方法。本文以ADC性能指标的测试场景为例,探讨了对ADC的输出结果进行非周期截断所带来的影响及问题总结。 两个点   为了更好的分析或处理信号,实际应用时需要从频域而非时域的角度观察原信号。但物理意义上只能直接获取信号的时域信息,为了得到信号的频域信息需要利用傅里叶变换这个工具计算出原信号的频谱函数。但对于计算机来说实现这种计算需要面对两个问题: 1.
    TIAN301 2025-01-14 14:15 118浏览
  • 根据Global Info Research(环洋市场咨询)项目团队最新调研,预计2030年全球无人机电池和电源产值达到2834百万美元,2024-2030年期间年复合增长率CAGR为10.1%。 无人机电池是为无人机提供动力并使其飞行的关键。无人机使用的电池类型因无人机的大小和型号而异。一些常见的无人机电池类型包括锂聚合物(LiPo)电池、锂离子电池和镍氢(NiMH)电池。锂聚合物电池是最常用的无人机电池类型,因为其能量密度高、设计轻巧。这些电池以输出功率大、飞行时间长而著称。不过,它们需要
    GIRtina 2025-01-13 10:49 201浏览
  • 随着数字化的不断推进,LED显示屏行业对4K、8K等超高清画质的需求日益提升。与此同时,Mini及Micro LED技术的日益成熟,推动了间距小于1.2 Pitch的Mini、Micro LED显示屏的快速发展。这类显示屏不仅画质卓越,而且尺寸适中,通常在110至1000英寸之间,非常适合应用于电影院、监控中心、大型会议、以及电影拍摄等多种室内场景。鉴于室内LED显示屏与用户距离较近,因此对于噪音控制、体积小型化、冗余备份能力及电气安全性的要求尤为严格。为满足这一市场需求,开关电源技术推出了专为
    晶台光耦 2025-01-13 10:42 516浏览
  • 随着通信技术的迅速发展,现代通信设备需要更高效、可靠且紧凑的解决方案来应对日益复杂的系统。中国自主研发和制造的国产接口芯片,正逐渐成为通信设备(从5G基站到工业通信模块)中的重要基石。这些芯片凭借卓越性能、成本效益及灵活性,满足了现代通信基础设施的多样化需求。 1. 接口芯片在通信设备中的关键作用接口芯片作为数据交互的桥梁,是通信设备中不可或缺的核心组件。它们在设备内的各种子系统之间实现无缝数据传输,支持高速数据交换、协议转换和信号调节等功能。无论是5G基站中的数据处理,还是物联网网关
    克里雅半导体科技 2025-01-10 16:20 451浏览
  • 新年伊始,又到了对去年做总结,对今年做展望的时刻 不知道你在2024年初立的Flag都实现了吗? 2025年对自己又有什么新的期待呢? 2024年注定是不平凡的一年, 一年里我测评了50余块开发板, 写出了很多科普文章, 从一个小小的工作室成长为科工公司。 展望2025年, 中国香河英茂科工, 会继续深耕于,具身机器人、飞行器、物联网等方面的研发, 我觉得,要向未来学习未来, 未来是什么? 是掌握在孩子们生活中的发现,和精历, 把最好的技术带给孩子,
    丙丁先生 2025-01-11 11:35 466浏览
  • 数字隔离芯片是现代电气工程师在进行电路设计时所必须考虑的一种电子元件,主要用于保护低压控制电路中敏感电子设备的稳定运行与操作人员的人身安全。其不仅能隔离两个或多个高低压回路之间的电气联系,还能防止漏电流、共模噪声与浪涌等干扰信号的传播,有效增强电路间信号传输的抗干扰能力,同时提升电子系统的电磁兼容性与通信稳定性。容耦隔离芯片的典型应用原理图值得一提的是,在电子电路中引入隔离措施会带来传输延迟、功耗增加、成本增加与尺寸增加等问题,而数字隔离芯片的目标就是尽可能消除这些不利影响,同时满足安全法规的要
    华普微HOPERF 2025-01-15 09:48 96浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦