我正在尝试在Java中实现低通滤波器。我的要求非常简单,我必须消除特定频率以外的信号(单维)。看起来Butterworth过滤器可以满足我的需要。
现在重要的是CPU时间应该尽可能低。过滤器必须处理近百万个样本,我们的用户不喜欢等待太久。是否有任何现成的Butterworth滤波器实现,它具有最佳的滤波算法。
答案 0 :(得分:40)
我有一个页面描述了一个非常简单,非常低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编写的函数,但该公式与语言无关。
答案 1 :(得分:6)
我从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<Float> 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<signal.size(); i++) {
unfilteredSignal[i] = signal.get(i);
}
double cutoffFraction = cutoffFreq/sampleRate; // convert cut-off frequency to fraction of sample rate
System.out.println("Filtering: cutoffFraction: " + cutoffFraction);
//ButterworthFilter(0.4,6,ButterworthFilter.Type highPass);
double[] coeffA = new double[22]; //a coeffs
double[] coeffB = new double[22]; //b coeffs
double[] tA = new double[22];
double[] tB = new double[22];
coeffA[2] = 1;
coeffB[2] = 1;
// calling subroutine
for (int i=1; i<filterOrder/2; i++) {
double[] filterParameters = MakeFilterParameters(cutoffFraction, filterType, ripplePercent, filterOrder, i);
for (int j=0; j<coeffA.length; j++){
tA[j] = coeffA[j];
tB[j] = coeffB[j];
}
for (int j=2; j<coeffA.length; j++){
coeffA[j] = filterParameters[0]*tA[j]+filterParameters[1]*tA[j-1]+filterParameters[2]*tA[j-2];
coeffB[j] = tB[j]-filterParameters[3]*tB[j-1]-filterParameters[4]*tB[j-2];
}
}
coeffB[2] = 0;
for (int i=0; i<20; i++){
coeffA[i] = coeffA[i+2];
coeffB[i] = -coeffB[i+2];
}
// adjusting coeffA and coeffB for high/low pass filter
double sA = 0;
double sB = 0;
for (int i=0; i<20; i++){
if (filterType==0) sA = sA+coeffA[i];
if (filterType==0) sB = sB+coeffB[i];
if (filterType==1) sA = sA+coeffA[i]*Math.pow(-1,i);
if (filterType==1) sB = sB+coeffA[i]*Math.pow(-1,i);
}
// applying gain
double gain = sA/(1-sB);
for (int i=0; i<20; i++){
coeffA[i] = coeffA[i]/gain;
}
for (int i=0; i<22; i++){
recursionCoefficients[i][0] = coeffA[i];
recursionCoefficients[i][1] = coeffB[i];
}
double[] filteredSignal = new double[signal.size()];
double filterSampleA = 0;
double filterSampleB = 0;
// loop for applying recursive filter
for (int i= (int) Math.round(filterOrder); i<signal.size(); i++){
for(int j=0; j<filterOrder+1; j++) {
filterSampleA = filterSampleA+coeffA[j]*unfilteredSignal[i-j];
}
for(int j=1; j<filterOrder+1; j++) {
filterSampleB = filterSampleB+coeffB[j]*filteredSignal[i-j];
}
filteredSignal[i] = filterSampleA+filterSampleB;
filterSampleA = 0;
filterSampleB = 0;
}
return filteredSignal;
}
/* pi=3.14...
cutoffFreq=fraction of samplerate, default 0.4 FC
filterType: 0=LowPass 1=HighPass LH
rippleP=ripple procent 0-29 PR
iterateOver=1 to poles/2 P%
*/
// subroutine called from "filterSignal" method
double[] MakeFilterParameters(double cutoffFraction, int filterType, double rippleP, double numberOfPoles, int iteration) {
double rp = -Math.cos(Math.PI/(numberOfPoles*2)+(iteration-1)*(Math.PI/numberOfPoles));
double ip = Math.sin(Math.PI/(numberOfPoles*2)+(iteration-1)*Math.PI/numberOfPoles);
System.out.println("MakeFilterParameters: ripplP:");
System.out.println("cutoffFraction filterType rippleP numberOfPoles iteration");
System.out.println(cutoffFraction + " " + filterType + " " + rippleP + " " + numberOfPoles + " " + iteration);
if (rippleP != 0){
double es = Math.sqrt(Math.pow(100/(100-rippleP),2)-1);
// double vx1 = 1/numberOfPoles;
// double vx2 = 1/Math.pow(es,2)+1;
// double vx3 = (1/es)+Math.sqrt(vx2);
// System.out.println("VX's: ");
// System.out.println(vx1 + " " + vx2 + " " + vx3);
// double vx = vx1*Math.log(vx3);
double vx = (1/numberOfPoles)*Math.log((1/es)+Math.sqrt((1/Math.pow(es,2))+1));
double kx = (1/numberOfPoles)*Math.log((1/es)+Math.sqrt((1/Math.pow(es,2))-1));
kx = (Math.exp(kx)+Math.exp(-kx))/2;
rp = rp*((Math.exp(vx)-Math.exp(-vx))/2)/kx;
ip = ip*((Math.exp(vx)+Math.exp(-vx))/2)/kx;
System.out.println("MakeFilterParameters (rippleP!=0):");
System.out.println("es vx kx rp ip");
System.out.println(es + " " + vx*100 + " " + kx + " " + rp + " " + ip);
}
double t = 2*Math.tan(0.5);
double w = 2*Math.PI*cutoffFraction;
double m = Math.pow(rp, 2)+Math.pow(ip,2);
double d = 4-4*rp*t+m*Math.pow(t,2);
double x0 = Math.pow(t,2)/d;
double x1 = 2*Math.pow(t,2)/d;
double x2 = Math.pow(t,2)/d;
double y1 = (8-2*m*Math.pow(t,2))/d;
double y2 = (-4-4*rp*t-m*Math.pow(t,2))/d;
double k = 0;
if (filterType==1) {
k = -Math.cos(w/2+0.5)/Math.cos(w/2-0.5);
}
if (filterType==0) {
k = -Math.sin(0.5-w/2)/Math.sin(w/2+0.5);
}
d = 1+y1*k-y2*Math.pow(k,2);
double[] filterParameters = new double[5];
filterParameters[0] = (x0-x1*k+x2*Math.pow(k,2))/d; //a0
filterParameters[1] = (-2*x0*k+x1+x1*Math.pow(k,2)-2*x2*k)/d; //a1
filterParameters[2] = (x0*Math.pow(k,2)-x1*k+x2)/d; //a2
filterParameters[3] = (2*k+y1+y1*Math.pow(k,2)-2*y2*k)/d; //b1
filterParameters[4] = (-(Math.pow(k,2))-y1*k+y2)/d; //b2
if (filterType==1) {
filterParameters[1] = -filterParameters[1];
filterParameters[3] = -filterParameters[3];
}
// for (double number: filterParameters){
// System.out.println("MakeFilterParameters: " + number);
// }
return filterParameters;
}
}
答案 2 :(得分:5)
这是一个低通滤波器,在apache数学库中使用傅立叶变换。
newlist="a,b,c"
list2=newlist.split(',')
finalList=list2[:]
答案 3 :(得分:4)
我最近设计了一个简单的butterworth功能(http://baumdevblog.blogspot.com/2010/11/butterworth-lowpass-filter-coefficients.html)。它们很容易用Java编写代码,如果你问我我应该足够快(你只需要更改filter(double * samples,int count)来过滤(double [] sample,int count),我猜。) / p>
JNI的问题在于它需要平台独立性,可能会使热点编译器和代码中的JNI方法调用混淆,这可能会使速度变慢。所以我建议尝试Java并查看它是否足够快。
在某些情况下,首先使用快速傅立叶变换并在频域中应用滤波可能是有益的,但我怀疑这比一个简单的低通滤波器的每个样本的约6次乘法和几次加法要快。
答案 4 :(得分:4)
过滤器设计是一种权衡的艺术,要做得好,你需要考虑一些细节。
“没有太多”考虑必须通过的最大频率是多少,“没有多少”的最大值是多少?
“很多”必须减弱的最低频率是多少?“很多”的最小值是多少?
在滤波器应该通过的频率范围内,可以接受多少纹波(即衰减的变化)?
您有多种选择,这将花费您各种计算量。 像matlab或scilab这样的程序可以帮助您比较权衡。您需要熟悉诸如将频率表示为采样率的小数部分,以及线性和对数(dB)衰减测量之间的交换等概念。
例如,“完美”低通滤波器在频域中是矩形的。在时域中表示为脉冲响应,这将是一个sinc函数(sin x / x),尾部达到正无穷大。显然你无法计算,所以如果你将sinc函数逼近可以计算的有限持续时间,那么问题会变成什么,这会使你的过滤器降低多少?
或者,如果你想要一个计算成本非常低廉的有限脉冲响应滤波器,你可以使用“盒子车”或矩形滤波器,其中所有系数都是1.(如果你把它作为实现它可以做得更便宜一个CIC过滤器利用二进制溢出来做“循环”累加器,因为无论如何你将在以后使用导数。但是一个矩形的滤波器在频率上看起来像一个sinc函数 - 它在通带中有一个sin x / x滚降(通常会提升到一些功率,因为你通常会有一个多级版本),还有一些“反弹”在阻带。在某些情况下它仍然有用,无论是单独使用还是后续使用其他类型的过滤器。
答案 5 :(得分:1)
答案 6 :(得分:1)
我知道这是一个古老的问题,但是我想添加似乎以前没有提到的内容。
您应该意识到的第一件事是:
当您需要在应用程序中使用滤波器时,必须选择某种类型的滤波器,为该类型的滤波器选择一种特定的设计方法,然后应用该方法来找到满足您的约束条件的滤波器系数,并,最后,将这些系数复制到您的过滤器实现中。
使用适当的软件(例如Matlab或Octave或Scilab)来选择滤波器的类型并应用设计方法是您一次要做的事情。这可能需要进行一些实验和观察,以了解所获得的滤波器特性,例如频率响应(幅度和相位)和/或脉冲响应,以查看它们是否符合您的规格。确定解决方案后,您将拥有一组常数。这些数字或这些数字的某种线性组合,只需要将它们作为常量表复制到程序(Java或其他方式)即可。
然后,在您的程序中,您仅需要一个函数,该函数应用一些过滤器实现,该函数使用这些系数对输入流样本(可能还有以前的输出样本)进行线性组合,以在每个时刻生成新的输出样本。
我假设您可能对具有有限脉冲响应(FIR)或无限脉冲响应(IIR)的线性时不变滤波器感兴趣。这两个子类之间的设计方法不同。巴特沃思,切比雪夫椭圆滤波器只是设计IIR滤波器的不同技术(或优化方法)的结果。这些可以小阶数实现非常好的频率响应,这意味着您只需很少的系数,因此在实现中每个样本的乘法/加法次数很少。 但是,例如,如果您真的想要线性相位响应,则需要FIR滤波器。 这些具有不同的设计技术,并且对于相似的频率特性通常要求更高的阶数。有使用快速傅立叶变换的高效实现,但是我怀疑您是否需要这种东西。
可能的不同滤波器实现方式的主要区别在于数值稳定性。除非您使用非常低的精度算术和/或非常奇特的系数,否则您可能不会注意到差异。我相信Matlab / Octave过滤器函数使用“ Direct-form II”实现,这非常简单。我敢肯定,您会在DSP书籍或网络上找到说明。
JoãoManuel Rodrigues