如何使用java实现低通滤波器

我试图在Java中实现低通滤波器。 我的要求很简单,我必须消除特定频率以外的信号(单维)。 看起来Butterworthfilter可以满足我的需要。

现在重要的是CPU时间应该尽可能低。 filter必须处理近百万个样本,我们的用户不喜欢等待太久。 是否有任何现成的Butterworth滤波器实现,它具有最佳的滤波算法。

我有一个页面描述了一个非常简单,非常低CPU的低通滤波器,它也能够与帧率无关。 我用它来平滑用户输入以及经常绘制帧速率。

http://phrogz.net/js/framerate-independent-low-pass-filter.html

简而言之,在您的更新循环中:

// If you have a fixed frame rate smoothedValue += (newValue - smoothedValue) / smoothing // If you have a varying frame rate smoothedValue += timeSinceLastUpdate * (newValue - smoothedValue) / smoothing 

smoothing值为1会导致不平滑,而值越高,结果越平滑。

该页面有几个用JavaScript编写的函数,但该公式与语言无关。

这是一个低通滤波器,在apache数学库中使用傅里叶变换。

  public double[] fourierLowPassFilter(double[] data, double lowPass, double frequency){ //data: input data, must be spaced equally in time. //lowPass: The cutoff frequency at which //frequency: The frequency of the input data. //The apache Fft (Fast Fourier Transform) accepts arrays that are powers of 2. int minPowerOf2 = 1; while(minPowerOf2 < data.length) minPowerOf2 = 2 * minPowerOf2; //pad with zeros double[] padded = new double[minPowerOf2]; for(int i = 0; i < data.length; i++) padded[i] = data[i]; FastFourierTransformer transformer = new FastFourierTransformer(DftNormalization.STANDARD); Complex[] fourierTransform = transformer.transform(padded, TransformType.FORWARD); //build the frequency domain array double[] frequencyDomain = new double[fourierTransform.length]; for(int i = 0; i < frequencyDomain.length; i++) frequencyDomain[i] = frequency * i / (double)fourierTransform.length; //build the classifier array, 2s are kept and 0s do not pass the filter double[] keepPoints = new double[frequencyDomain.length]; keepPoints[0] = 1; for(int i = 1; i < frequencyDomain.length; i++){ if(frequencyDomain[i] < lowPass) keepPoints[i] = 2; else keepPoints[i] = 0; } //filter the fft for(int i = 0; i < fourierTransform.length; i++) fourierTransform[i] = fourierTransform[i].multiply((double)keepPoints[i]); //invert back to time domain Complex[] reverseFourier = transformer.transform(fourierTransform, TransformType.INVERSE); //get the real part of the reverse double[] result = new double[data.length]; for(int i = 0; i< result.length; i++){ result[i] = reverseFourier[i].getReal(); } return result; } 

我最近设计了一个简单的butterworthfunction( http://baumdevblog.blogspot.com/2010/11/butterworth-lowpass-filter-coefficients.html )。 它们很容易用Java编写代码,如果你问我我应该足够快(你只需要更改filter(double * samples,int count)来过滤(double [] samples,int count),我猜)。

JNI的问题在于它需要平台独立性,可能会混淆热点编译器和代码中的JNI方法调用,这可能会使速度变慢。 所以我建议尝试Java并查看它是否足够快。

在某些情况下,首先使用快速傅立叶变换并在频域中应用滤波可能是有益的,但我怀疑这比简单的低通滤波器的每个样本的约6次乘法和几次加法更快。

filter设计是一种权衡的艺术,要做得好,您需要考虑一些细节。

什么是必须通过“没有太多”的最大频率,“没有多少”的最大值是多少?

什么是必须“大量”减弱的最小频率,“很多”的最小值是多少?

在滤波器应该通过的频率内,可以接受多少纹波(即衰减的变化)?

您有多种选择,这将花费您各种计算量。 像matlab或scilab这样的程序可以帮助你比较权衡 。 您需要熟悉诸如将频率表示为采样率的小数部分,以及线性和对数(dB)衰减测量之间的交换等概念。

例如,“完美”低通滤波器在频域中是矩形的。 在时域中表示为脉冲响应,这将是一个sinc函数(sin x / x),其尾部达到正无穷大。 显然你无法计算出来,所以如果你将sinc函数逼近有限的持续时间(你可以计算出来),那么问题会变成什么,这会使你的filter降低多少?

或者,如果你想要一个非常便宜的有限脉冲响应滤波器来计算,你可以使用“盒子车”或矩形滤波器,其中所有系数都是1.(如果你把它作为CIC滤波器实现,这可以更便宜利用二进制溢出来做“循环”累加器,因为无论如何你将在以后使用导数。 但是一个矩形的滤波器在频率上看起来像一个sinc函数 – 它在通带中有一个sin x / x滚降(通常会提升到一些功率,因为​​你通常会有一个多级版本),还有一些“反弹”在阻带。 在某些情况下它仍然有用,无论是单独使用还是后续使用其他类型的filter。

就像Mark Peters在评论中所说的那样:需要过滤很多的filter应该用C或C ++编写。 但是你仍然可以使用Java。 只需看看Java Native Interface(JNI) 。 由于C / C ++编译为本机机器代码,它运行速度比在Java虚拟机(JVM)中运行字节码要快得多,后者实际上是一个虚拟处理器,它将字节码转换为本地机器的本机代码(取决于在CPU指令集上,如x86,x64, ARM ,….)

我从http://www.dspguide.com/采用了这个,我对java很新,所以它不漂亮,但它有效

 /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package SoundCruncher; import java.util.ArrayList; /** * * @author 2sloth * filter routine from "The scientist and engineer's guide to DSP" Chapter 20 * filterOrder can be any even number between 2 & 20 * cutoffFreq must be smaller than half the samplerate * filterType: 0=lowPass 1=highPass * ripplePercent is amount of ripple in Chebyshev filter (0-29) (0=butterworth) */ public class Filtering { double[] filterSignal(ArrayList signal, double sampleRate ,double cutoffFreq, double filterOrder, int filterType, double ripplePercent) { double[][] recursionCoefficients = new double[22][2]; // Generate double array for ease of coding double[] unfilteredSignal = new double[signal.size()]; for (int i=0; i