在Python中应用时变过滤器

2023-11-26

我正在尝试使用 Python 将具有时变截止频率的带通滤波器应用于信号。我当前使用的例程将信号划分为等长的时间段,然后对于每个段,我应用具有特定时间参数的滤波器,然后将信号合并在一起。这些参数基于预先存在的估计。

我似乎遇到的问题是,应用过滤器后,每个时间段的边缘都会出现“波纹”。这会导致信号不连续,从而干扰我的滤波后数据分析。

我希望有人能告诉我是否有任何现有的例程可以在Python中实现具有时变参数的过滤器?或者,如果我能就如何解决这个问题提出建议,我将不胜感激。

EDIT-- 下面添加了我想要做的示例。

假设我有一个信号 x(t)。我想使用参数为 (100,200) Hz 的带通滤波器对前半部分进行滤波。后半部分我想用参数 (140, 240) Hz 进行过滤。我迭代 x(t),将过滤器应用于每一半,然后重新组合结果。一些示例代码可能如下所示:

outputArray = np.empty(len(x))
segmentSize = len(x) / 2
filtParams  = [(100, 200), (140, 240)]

for i in range(2):
    tempData     = x[i*segmentSize:(i+1)*segmentSize]
    tempFiltered = bandPassFilter(tempData, parameters=filtParams[i])
    outputArray[i*segmentSize:(i+1)*segmentSize] = tempFiltered

(为了节省空间,我们假设我有一个执行带通滤波的函数)。

正如您所看到的,数据段并不重叠,而是简单地“粘贴”回新数组中。

EDIT 2-- 我的问题的一些示例代码@H.D.

首先,感谢您迄今为止的重要投入。 audiolazy 包看起来是一个很棒的工具。

我认为如果我更详细地描述我的目标会更有用。正如我已经发布的别处,我正在尝试提取瞬时频率(IF) 信号,使用希尔伯特变换。我的数据包含明显的噪声,但我对 IF 信号所在的带宽有很好的估计。然而,我遇到的一个问题是 IF 通常是不稳定的。因此,使用“静态”滤波器方法时,我经常需要使用宽带通区域,以确保捕获所有频率。

以下代码演示了增加滤波器带宽对 IF 信号的影响。它包括一个信号生成函数、使用 scipy.signal 包实现带通滤波器以及提取所得滤波信号的 IF 的方法。

from audiolazy import *
import scipy.signal as sig
import numpy as np
from pylab import *

def sineGenerator( ts, f, rate, noiseLevel=None ):
    """generate a sine tone with time, frequency, sample rate and noise specified"""

    fs = np.ones(len(ts)) * f    
    y  = np.sin(2*np.pi*fs*ts)

    if noiseLevel: y = y + np.random.randn(len(y))/float(noiseLevel)
    return y

def bandPassFilter( y, passFreqs, rate, order ):
    """STATIC bandpass filter using scipy.signal Butterworth filter"""

    nyquist = rate / 2.0
    Wn      = np.array([passFreqs[0]/nyquist, passFreqs[1]/nyquist])    
    z, p, k = sig.butter(order, Wn, btype='bandpass', output='zpk')
    b, a    = sig.zpk2tf(z, p, k)

    return sig.lfilter(b, a, y)

if __name__ == '__main__':
     rate = 1e4
     ts   = np.arange(0, 10, 1/rate)

     # CHANGING THE FILTER AFFECTS THE LEVEL OF NOISE
     ys    = sineGenerator(ts, 600.0, 1e4, noiseLevel=1.0) # a 600Hz signal with noise
     filts = [[500, 700], [550, 650], [580, 620]]

    for f in filts:
        tempFilt = bandPassFilter( ys, f, rate, order=2 )
        tempFreq = instantaneousFrequency( tempFilt, rate )

        plot( ts[1:], tempFreq, alpha=.7, label=str(f).strip('[]') )

    ylim( 500, 750 )
    xlabel( 'time' )
    ylabel( 'instantaneous frequency (Hz)' )

    legend(frameon=False)
    title('changing filter passband and instantaneous frequency')
    savefig('changingPassBand.png')

changing passband

信号中存在单一频率分量(600Hz)。图例显示了每种情况下使用的过滤器参数。使用较窄的“静态”过滤器可提供“更干净”的输出。但我的滤波器的窄度受到频率的限制。例如,考虑以下具有两个频率分量的信号(一个频率为 600Hz,另一个频率为 650Hz)。

varying frequency

在这个例子中,我被迫使用更宽的带通滤波器,这导致额外的噪声渗透到 IF 数据中。

我的想法是,通过使用时变滤波器,我可以在特定的时间增量“优化”信号的滤波器。因此,对于上述信号,我可能希望在前 5 秒内过滤 580-620Hz 左右的信号,然后在接下来的 5 秒内过滤 630-670Hz 的信号。本质上,我想最大限度地减少最终 IF 信号中的噪声。

根据您发送的示例代码,我编写了一个函数,该函数使用 audiolazy 在信号上实现静态巴特沃斯滤波器。

def audioLazyFilter( y, rate, Wp, Ws ):
    """implement a Butterworth filter using audiolazy"""

    s, Hz = sHz(rate)
    Wp    = Wp * Hz # Bandpass range in rad/sample
    Ws    = Ws * Hz # Bandstop range in rad/sample

    order, new_wp_divpi = sig.buttord(Wp/np.pi, Ws/np.pi, gpass=dB10(.6), gstop=dB10(.1))
    ssfilt = sig.butter(order, new_wp_divpi, btype='bandpass')
    filt_butter = ZFilter(ssfilt[0].tolist(), ssfilt[1].tolist())

    return list(filt_butter(y))

使用此滤波器结合希尔伯特变换例程获得的 IF 数据与使用 scipy.signal 获得的数据进行了很好的比较:

AL_filtered = audioLazyFilter( ys, rate, np.array([580, 620]), np.array([570, 630]) )
SP_filtered = bandPassFilter( ys, [580, 620], rate, order=2 )
plot(ts[1:], instantaneousFrequency( SP_filtered, 1e4 ), alpha=.75, label='scipy.signal Butterworth filter')
plot(ts[1:], instantaneousFrequency( AL_filtered, 1e4 ), 'r', alpha=.75, label='audiolazy Butterworth filter')

compare audiolazy with scipy.signal

我现在的问题是,我可以将audiolazy Butterworth 例程与您在原始帖子中描述的时变属性结合起来吗?


音频懒惰与随时间变化的过滤器一起工作

from audiolazy import sHz, white_noise, line, resonator, AudioIO

rate = 44100
s, Hz = sHz(rate)

sig = white_noise() # Endless white noise Stream

dur = 8 * s # Some few seconds of audio
freq = line(dur, 200, 800) # A lazy iterable range
bw = line(dur, 100, 240)

filt = resonator(freq * Hz, bw * Hz) # A simple bandpass filter

with AudioIO(True) as player:
  player.play(filt(sig), rate=rate)

您还可以使用它来绘图(或一般分析),方法是使用list(filt(sig)) or filt(sig).take(inf)。还有许多其他资源也可能有用,例如直接在 Z 变换滤波器方程中应用时变系数。

编辑:有关 AudioLazy 组件的更多信息

以下示例是使用 IPython 完成的。

谐振器是一个StrategyDict例如,它将许多实现绑定在一个地方。

In [1]: from audiolazy import *

In [2]: resonator
Out[2]: 
{('freq_poles_exp',): <function audiolazy.lazy_filters.freq_poles_exp>,
 ('freq_z_exp',): <function audiolazy.lazy_filters.freq_z_exp>,
 ('poles_exp',): <function audiolazy.lazy_filters.poles_exp>,
 ('z_exp',): <function audiolazy.lazy_filters.z_exp>}

In [3]: resonator.default
Out[3]: <function audiolazy.lazy_filters.poles_exp>

So resonator内部调用resonator.poles_exp函数,从中您可以获得一些帮助

In [4]: resonator.poles_exp?
Type:       function
String Form:<function poles_exp at 0x2a55b18>
File:       /usr/lib/python2.7/site-packages/audiolazy/lazy_filters.py
Definition: resonator.poles_exp(freq, bandwidth)
Docstring:
Resonator filter with 2-poles (conjugated pair) and no zeros (constant
numerator), with exponential approximation for bandwidth calculation.

Parameters
----------
freq :
  Resonant frequency in rad/sample (max gain).
bandwidth :
  Bandwidth frequency range in rad/sample following the equation:

    ``R = exp(-bandwidth / 2)``

  where R is the pole amplitude (radius).

Returns
-------
A ZFilter object.
Gain is normalized to have peak with 0 dB (1.0 amplitude).

因此,详细的过滤器分配将是

filt = resonator.poles_exp(freq=freq * Hz, bandwidth=bw * Hz)

哪里的Hz只是一个将单位从 Hz 更改为 rad/sample 的数字,如大多数 AudioLazy 组件中所使用的那样。

让我们这样做freq = pi/4 and bw = pi/8 (pi已经在audiolazy命名空间):

In [5]: filt = resonator(freq=pi/4, bandwidth=pi/8)

In [6]: filt
Out[6]: 
              0.233921
------------------------------------
1 - 1.14005 * z^-1 + 0.675232 * z^-2


In [7]: type(filt)
Out[7]: audiolazy.lazy_filters.ZFilter

您可以尝试使用此过滤器而不是第一个示例中给出的过滤器。

另一种方法是使用z包中的对象。 首先让我们找到该全极谐振器的常数:

In [8]: freq, bw = pi/4, pi/8

In [9]: R = e ** (-bw / 2)

In [10]: c = cos(freq) * 2 * R / (1 + R ** 2) # AudioLazy included the cosine

In [11]: gain = (1 - R ** 2) * sqrt(1 - c ** 2)

分母可以直接使用z在等式中:

In [12]: denominator = 1 - 2 * R * c * z ** -1 + R ** 2 * z ** -2

In [13]: gain / denominator
Out[14]: 
              0.233921
------------------------------------
1 - 1.14005 * z^-1 + 0.675232 * z^-2

In [15]: type(_) # The "_" is the last returned value in IPython
Out[15]: audiolazy.lazy_filters.ZFilter

编辑2:关于时变系数

滤波器系数也可以是 Stream 实例(可以从任何可迭代对象进行转换)。

In [16]: coeff = Stream([1, -1, 1, -1, 1, -1, 1, -1, 1, -1]) # Cast from a list

In [17]: (1 - coeff * z ** -2)(impulse()).take(inf)
Out[17]: [1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

同样,给定一个列表输入而不是impulse() Stream:

In [18]: coeff = Stream((1, -1, 1, -1, 1, -1, 1, -1, 1, -1)) # Cast from a tuple 

In [19]: (1 - coeff * z ** -2)([1, 0, 0, 0, 0, 0, 0]).take(inf)
Out[19]: [1.0, 0.0, -1, 0, 0, 0, 0]

NumPy 一维数组也是可迭代的:

In [20]: from numpy import array

In [21]: array_data = array([1, -1, 1, -1, 1, -1, 1, -1, 1, -1])

In [22]: coeff = Stream(array_data) # Cast from an array

In [23]: (1 - coeff * z ** -2)([0, 1, 0, 0, 0, 0, 0]).take(inf)
Out[23]: [0.0, 1.0, 0, 1, 0, 0, 0]

最后一个示例显示了随时间变化的行为。

编辑3:分块重复序列行为

线函数的行为类似于numpy.linspace,它获取范围“长度”而不是“步长”。

In [24]: import numpy

In [25]: numpy.linspace(10, 20, 5) # Start, stop (included), length
Out[25]: array([ 10. ,  12.5,  15. ,  17.5,  20. ])

In [26]: numpy.linspace(10, 20, 5, endpoint=False) # Makes stop not included
Out[26]: array([ 10.,  12.,  14.,  16.,  18.])

In [27]: line(5, 10, 20).take(inf) # Length, start, stop (range-like)
Out[27]: [10.0, 12.0, 14.0, 16.0, 18.0]

In [28]: line(5, 10, 20, finish=True).take(inf) # Include the "stop"
Out[28]: [10.0, 12.5, 15.0, 17.5, 20.0]

这样,滤波器方程具有不同的每个样本行为(1 个样本“块”)。无论如何,您可以使用转发器来处理更大的块:

In [29]: five_items = _ # List from the last Out[] value

In [30]: @tostream
   ....: def repeater(sig, n):
   ....:     for el in sig:
   ....:         for _ in xrange(n):
   ....:             yield el
   ....:             

In [31]: repeater(five_items, 2).take(inf)
Out[31]: [10.0, 10.0, 12.5, 12.5, 15.0, 15.0, 17.5, 17.5, 20.0, 20.0]

并在第一个示例的行中使用它,这样freq and bw变成:

chunk_size = 100
freq = repeater(line(dur / chunk_size, 200, 800), chunk_size)
bw = repeater(line(dur / chunk_size, 100, 240), chunk_size)

编辑 4:使用时变增益/包络从 LTI 滤波器模拟时变滤波器/系数

另一种方法是对信号的两个不同滤波版本使用不同的“权重”,并对信号进行一些“交叉淡入淡出”数学运算,例如:

signal = thub(sig, 2) # T-Hub is a T (tee) auto-copy
filt1(signal) * line(dur, 0, 1) + filt2(signal) * line(dur, 1, 0)

这将应用来自同一信号的不同滤波版本的线性包络(从 0 到 1 和从 1 到 0)。如果thub看起来很混乱,尝试一下sig1, sig2 = tee(sig, 2)申请filt(sig1) and filt(sig2)相反,这些应该做同样的事情。

编辑 5:时变巴特沃斯滤波器

我花了最后几个小时试图让巴特沃斯个性化作为你的例子,强加order = 2并直接给出半功率带宽(~3dB)。我做了四个例子,代码是在这个要点中,并且我更新了 AudioLazy 以包含gauss_noise高斯分布噪声流。请注意,要点中的代码没有任何优化,它只是在这种特殊情况下工作,并且由于“每个样本”系数查找行为,chirp 示例使其变得非常慢。瞬时频率可以从 rad/sample 中的[过滤]数据中获得:

diff(unwrap(phase(hilbert(filtered_data))))

where diff = 1 - z ** -1或另一种在离散时间内求导数的方法,hilbert是函数scipy.signal它为我们提供了分析信号(离散希尔伯特变换是其结果的虚部),另外两个是 AudioLazy 的辅助函数。

这就是当巴特沃斯突然改变其系数同时保持其记忆而没有噪声时所发生的情况:

variable_butterworth_abrupt_pure_sinusoid.png

在这个转变过程中,出现了明显的振荡行为。您可以使用移动中值来“平滑”较低频率侧的频率,同时保持突然的过渡,但这不适用于较高频率。好吧,这就是我们对完美正弦曲线的期望,但是有了噪声(大量噪声,高斯函数的标准差等于正弦曲线幅度),它就变成了:

variable_butterworth_abrupt_noisy.png

然后我尝试用鸣叫声做同样的事情,正是这样:

variable_butterworth_pure_sinusoid.png

当在最高频率下使用较低带宽进行滤波时,这显示出奇怪的行为。加上附加噪声:

variable_butterworth_noisy.png

gist 中的代码也AudioIO().play这最后的嘈杂的鸣叫声。

编辑 6:时变谐振器滤波器

我已经添加到相同的要点使用谐振器代替巴特沃斯的示例。它们采用纯 Python 编写,未经过优化,但执行速度比调用更快butter对于啁啾期间的每个样本,并且更容易实现,因为所有resonator策略接受 Stream 实例作为有效输入。以下是两个谐振器级联(即二阶滤波器)的图:

reson_2_abrupt_pure_sinusoid.png reson_2_abrupt_noisy.png reson_2_pure_sinusoid.png reson_2_noisy.png

对于三个谐振器的级联(即三阶滤波器)也是如此:

reson_3_abrupt_pure_sinusoid.png reson_3_abrupt_noisy.png reson_3_pure_sinusoid.png reson_3_noisy.png

这些谐振器在中心频率处的增益等于 1 (0 dB),并且即使根本没有任何滤波,过渡中“突变纯正弦曲线”图的振荡模式也会发生。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在Python中应用时变过滤器 的相关文章

随机推荐

  • 如何在C++中读取格式化数据?

    我已将数据格式化如下 Words 5 AnotherWord 4 SomeWord 6 它在一个文本文件中 我使用 ifstream 来读取它 但如何分离数字和单词 该单词仅由字母组成 单词和数字之间会有一定的空格或制表符 不确定有多少 假
  • 为什么 CDATA 在脚本标签下被注释掉?

    我正在读这个question我有一个相关的问题 这家伙here说 它用在脚本标签中以避免解析 already CDATA Question 1 如果脚本是already CDATA 为什么它 在脚本标签下 仍然呈现为 CDATA Quest
  • Sql Server 中的“IN”子句限制

    有谁知道 IN 子句的表达式列表 用于测试匹配 中可以拥有的值的数量限制是多少 是的 有限制 但是微软仅指定其位于 数千 在 IN 子句中的括号内显式包含大量值 数千个用逗号分隔的值 可能会消耗资源并返回错误 8623 或 8632 要解决
  • 我在哪里可以获得线程安全的 CollectionView?

    在后台线程上更新业务对象集合时 我收到以下错误消息 这种类型的 CollectionView 不支持从与 Dispatcher 线程不同的线程更改其 SourceCollection 好吧 这是有道理的 但它也引出了一个问题 什么版本的 C
  • Java 中原始整数类型的行为不一致

    有人可以向我解释一下 就像我五岁一样 为什么我在 Java 中表示整数的四种基本类型中的两种会得到不同的行为 AFAIK 所有四个都是有符号的 并且它们都使用最高有效位作为符号位 那么为什么 byte 和 Short 表现正常 而 int
  • 显示进度条,显示表单提交的进度

    这个问题还没有完全解答 欢迎大家踊跃留言 我正在尝试显示一个简单的progress bar提交大表格时 该表单包含十几个字段 以及一些文件上传字段 用户可以在其中选择图片 然后 当他点击Create按钮 提交带有数据和图片的表单 并在数据库
  • 使用 Fluent nHibernate 和 Ninject 实现多租户。每个租户一个数据库

    我正在构建一个多租户 Web 应用程序 出于安全考虑 我们需要为每个租户拥有一个数据库实例 所以我有一个用于身份验证的 MainDB 和许多用于应用程序数据的 ClientDB 我正在使用 Asp net MVC 与 Ninject 和 F
  • 使用 PDFBox 添加页码

    如何向使用 PDFBox 生成的文档中的页面添加页码 谁能告诉我如何在合并不同的 PDF 后向文档添加页码 我正在使用 Java 中的 PDFBox 库 这是我的代码 它运行良好 但我需要添加页码 PDFMergerUtility ut n
  • iOS StoreKit - 何时调用 - (void)restoreCompletedTransactions?

    我的应用程序中有很多一次性购买的 IAP 用户可以购买它们 我的问题是 我正在与 Flurry 集成来跟踪实际购买情况 而不是仅仅恢复购买情况 但我的SKPaymentTransaction s transactionState总是回来作为
  • AttributeError:“列表”对象没有属性“点击” - Selenium Webdriver

    我正在尝试使用 python 在 Selenium webdriver 中使用 click 命令 但我收到以下错误 有人能帮我吗 Traceback most recent call last File C Users vikram wor
  • 在android中创建自定义工具栏

    我正在尝试在 android 中创建一个自定义扩展工具栏 并在工具栏中编辑文本 我想要实现的布局看起来像这样 我编写的实现代码是这样的
  • 无法获取当前线程的事务同步会话

    我在从 xml 转换为 Java Config 的 Spring4 Hibernate4 项目中遇到以下异常 org hibernate HibernateException Could not obtain transaction syn
  • threading.local() 是在 Google AppEngine 中存储单个请求的变量的安全方法吗?

    我有一个 google appengine 应用程序 我只想为该请求设置一个全局变量 我可以这样做吗 在request vars py中 request vars py global vars threading local 在另一个 py
  • 在 Fortran 中打开二进制文件:状态、表单、访问

    我使用 Fortran 已有多年 但文件 I O 对我来说仍然很模糊 我的理解status form access recl是有限的 因为我在研究生院需要某些用例 我知道 Fortran 二进制文件在文件顶部有描述文件大小的额外信息 但这对
  • 无法使用response.sendRedirect进行重定向

    我在 google 上搜索了好几个小时 了解如何在 jsp 或 servlet 中进行重定向 然而 当我尝试应用它时 它不起作用 我在jsp页面内的代码 我从调试中知道 regexp 有效 并且如果有任何时候 articleId 不是数字
  • Android 布局参数仅更改宽度和高度

    我知道如何使用设置视图的宽度和高度LayoutParams通过执行以下操作 android view ViewGroup LayoutParams params button getLayoutParams params width hei
  • 可以运行的最大 Swing 工作线程数是多少

    可以运行的 Swing Worker 线程数是否有上限 或者是内存支持的上限 这也可以在某处配置吗 A SwingWorker本身不是线程 而是将在线程中执行的任务 通常 您会使用ExecutorService执行实例SwingWorker
  • C# 将一个列表拆分为多个列表

    我有一个发送到队列的字符串列表 我需要拆分列表 以便最终得到一个列表列表 其中每个列表包含最大 用户定义 数量的字符串 例如 如果我有一个包含以下 A B C D E F G H I 的列表 并且列表的最大大小为 4 我希望最终得到一个列表
  • 如何在Windows上安装tesserocr?

    我下载了 tesseract OCR 的可执行文件并安装了它 另一方面 我还从以下位置下载了 leptonica 的 zip 文件 http www leptonica com download html 它包括两个目录 即lib and
  • 在Python中应用时变过滤器

    我正在尝试使用 Python 将具有时变截止频率的带通滤波器应用于信号 我当前使用的例程将信号划分为等长的时间段 然后对于每个段 我应用具有特定时间参数的滤波器 然后将信号合并在一起 这些参数基于预先存在的估计 我似乎遇到的问题是 应用过滤