[英]Conflicting results when computing an FFT using ARM DSP libraries and in MatLab
[英]Numerical error building up when computing derivative of function repeatedly with FFT
我編寫了一個C程序,它使用FFTW來計算函數的導數(重復)。 我正在測試簡單的sin(x)函數。 每個步驟計算前一步驟的答案的導數。 我觀察到錯誤構建,並且在20步之后,數字是純垃圾。 附件是樣本輸出。 答案(在特定點)應該是0,+ 1或-1,但它不是。
---- out ---- data '(0) = 1.000000 -0.000000
---- out ---- data '(1) = 0.000000 -0.000000
---- out ---- data '(2) = -1.000000 0.000000
---- out ---- data '(3) = -0.000000 0.000000
---- out ---- data '(4) = 1.000000 -0.000000
---- out ---- data '(5) = 0.000000 -0.000000
---- out ---- data '(6) = -1.000000 0.000000
---- out ---- data '(7) = -0.000000 0.000000
---- out ---- data '(8) = 1.000000 -0.000000
---- out ---- data '(9) = 0.000000 -0.000000
---- out ---- data '(10) = -1.000000 0.000000
---- out ---- data '(11) = -0.000000 0.000000
---- out ---- data '(12) = 1.000000 -0.000002
---- out ---- data '(13) = 0.000007 -0.000000
---- out ---- data '(14) = -1.000000 0.000028
---- out ---- data '(15) = -0.000113 0.000000
---- out ---- data '(16) = 0.999997 -0.000444
---- out ---- data '(17) = 0.001798 -0.000000
---- out ---- data '(18) = -0.999969 0.007110
---- out ---- data '(19) = -0.028621 0.000004
我無法弄清楚為什么錯誤會不斷增長。 任何建議都深表感謝。 我將實函數包裝成復雜的數據類型,並將虛部設置為零。 這是代碼:
# include <stdlib.h>
# include <stdio.h>
# include <time.h>
# include <math.h>
# include <complex.h>
# include <fftw3.h>
int main ( int argc, char *argv[] ){
int i;
fftw_complex *in;
fftw_complex *in2;
fftw_complex *out;
double pi = 3.14159265359;
int nx = 8, k, t, tf = 20;
double xi = 0, xf = 2*pi;
double dx = (xf - xi)/((double)nx); // Step size
complex double *kx;
fftw_plan plan_backward;
fftw_plan plan_forward;
in = fftw_malloc ( sizeof ( fftw_complex ) * nx );
out = fftw_malloc ( sizeof ( fftw_complex ) * nx );
in2 = fftw_malloc ( sizeof ( fftw_complex ) * nx );
kx = malloc ( sizeof ( complex ) * nx );
// only need it once, hence outside the loop
for (k = 0; k < nx; k++){
if (k < nx/2){
kx[k] = I*2*pi*k/xf;
} else if (k > nx/2){
kx[k] = I*2*pi*(k-nx)/xf;
} else if (k == nx/2){
kx[k] = 0.0;
}
}
// create plan outside the loop
plan_forward = fftw_plan_dft_1d ( nx, in, out, FFTW_FORWARD, FFTW_ESTIMATE );
plan_backward = fftw_plan_dft_1d ( nx, out, in2, FFTW_BACKWARD, FFTW_ESTIMATE );
// initialize data
for ( i = 0; i < nx; i++ )
{
in[i] = sin(i*dx) + I*0.0; // note the complex notation.
}
//-------------------- start time loop ---------------------------------------//
for (t = 0; t < tf; t++){
// print input data
//for ( i = 0; i < nx; i++ ) { printf("initial data '(%f) = %f %f \n", i*dx, creal(in[i]), cimag(in[i]) ); }
fftw_execute ( plan_forward );
for ( i = 0; i < nx; i++ )
{
out[i] = out[i]*kx[i]; // for first order derivative
}
fftw_execute ( plan_backward );
// normalize
for ( i = 0; i < nx; i++ )
{
in2[i] = in2[i]/nx;
}
printf("---- out ---- data '(%d) = %f %f \n", t, creal(in2[0]), cimag(in2[0]) );
// overwrite input array with this new output and loop over
for ( i = 0; i < nx; i++ )
{
in[i] = in2[i];
}
// done with the curent loop.
}
//--------------------- end of loop ----------------------------------------//
fftw_destroy_plan ( plan_forward );
fftw_destroy_plan ( plan_backward );
fftw_free ( in );
fftw_free ( in2 );
fftw_free ( out );
return 0;
}
使用gcc source.c -lfftw3 -lm編譯
更新:這是M_PI循環25次的輸出。 相同的錯誤累積。
---- out ---- data '(0) = 1.000000 0.000000
---- out ---- data '(1) = -0.000000 -0.000000
---- out ---- data '(2) = -1.000000 -0.000000
---- out ---- data '(3) = 0.000000 0.000000
---- out ---- data '(4) = 1.000000 0.000000
---- out ---- data '(5) = -0.000000 -0.000000
---- out ---- data '(6) = -1.000000 -0.000000
---- out ---- data '(7) = 0.000000 0.000000
---- out ---- data '(8) = 1.000000 0.000000
---- out ---- data '(9) = -0.000000 -0.000000
---- out ---- data '(10) = -1.000000 -0.000000
---- out ---- data '(11) = 0.000000 0.000000
---- out ---- data '(12) = 1.000000 0.000000
---- out ---- data '(13) = -0.000000 -0.000000
---- out ---- data '(14) = -1.000000 -0.000000
---- out ---- data '(15) = 0.000000 0.000000
---- out ---- data '(16) = 1.000000 0.000001
---- out ---- data '(17) = -0.000002 -0.000000
---- out ---- data '(18) = -0.999999 -0.000008
---- out ---- data '(19) = 0.000033 0.000004
---- out ---- data '(20) = 0.999984 0.000132
---- out ---- data '(21) = -0.000527 -0.000069
---- out ---- data '(22) = -0.999735 -0.002104
---- out ---- data '(23) = 0.008427 0.001099
---- out ---- data '(24) = 0.995697 0.033667
實際上,提高pi的價值並不能解決你的問題,即使它提高了20階導數的准確性。 問題是通過重復導數濾波器來增加小錯誤。 為了限制此問題,可以建議引入低通濾波器以及使用四倍精度。 引入條件數的概念有助於理解錯誤的膨脹方式並相應地設置過濾器。 盡管如此,計算20階導數仍將是一場噩夢,因為計算20階導數是不合理的 :即使對於最干凈的實驗輸入,這根本不可能......
1.總是存在小錯誤。
導數濾波器,也稱為斜坡濾波器,比高頻濾波器更高的頻率。 通過重復使用斜坡濾波器,高頻上的最輕微錯誤會大大增加。
讓我們通過打印頻率來查看小的初始錯誤
printf("---- out ---- data '(%d) %d = %20g %20g \n", t,i, creal(out[i]), cimag(out[i]) );
當使用pi=3.14159265359
,您會得到:
---- out ---- data '(0) 0 = -2.06712e-13 0
---- out ---- data '(0) 1 = 6.20699e-13 -4
---- out ---- data '(0) 2 = -2.06823e-13 2.92322e-13
---- out ---- data '(0) 3 = -2.07053e-13 1.03695e-13
---- out ---- data '(0) 4 = -2.06934e-13 0
---- out ---- data '(0) 5 = -2.07053e-13 -1.03695e-13
---- out ---- data '(0) 6 = -2.06823e-13 -2.92322e-13
---- out ---- data '(0) 7 = 6.20699e-13 4
由於pi的缺失數字引起的不連續性,所有頻率都存在小的非空值,並且這些值通過取導數而膨脹。
由於使用了pi=M_PI
,這些初始錯誤較小,但仍然是非空的:
---- out ---- data '(0) 0 = 1.14424e-17 0
---- out ---- data '(0) 1 = -4.36483e-16 -4
---- out ---- data '(0) 2 = 1.22465e-16 -1.11022e-16
---- out ---- data '(0) 3 = 1.91554e-16 -4.44089e-16
---- out ---- data '(0) 4 = 2.33487e-16 0
---- out ---- data '(0) 5 = 1.91554e-16 4.44089e-16
---- out ---- data '(0) 6 = 1.22465e-16 1.11022e-16
---- out ---- data '(0) 7 = -4.36483e-16 4
這些小錯誤就像以前的錯誤一樣膨脹,問題並沒有完全解決。 讓我們嘗試在第一個循環期間將這些頻率歸零:
if(t==0){
for (k = 0; k < nx; k++){
if (k==1 || nx-k==1){
out[k] = I*4.0;
}else{
out[k] =0.0;
}
}
}
這次在第一個循環t=0
期間唯一的非零頻率是正確的。 讓我們看看第二個循環:
---- out ---- data '(1) 0 = 0 0
---- out ---- data '(1) 1 = -4 0
---- out ---- data '(1) 2 = 0 0
---- out ---- data '(1) 3 = -4.44089e-16 0
---- out ---- data '(1) 4 = 0 0
---- out ---- data '(1) 5 = 4.44089e-16 0
---- out ---- data '(1) 6 = 0 0
---- out ---- data '(1) 7 = 4 0
由於在DFT后向/前向變換和縮放期間的有限精度計算,出現小錯誤並且被誇大。 再次。
2.為了限制錯誤的增長,可以引入過濾。
大多數實驗輸入都是由大的高頻噪聲引起的,可以通過應用低通濾波器(如巴特沃斯濾波器)來降低這些噪聲。 有關詳細信息和替代方法,請參閱https://www.hindawi.com/journals/ijbi/2011/693795/ 。 該濾波器的特征在於切割頻率kc和指數,並且斜坡濾波器的頻率響應被修改如下:
//parameters of Butterworth Filter:
double kc=3;
double n=16;
// only need it once, hence outside the loop
for (k = 0; k < nx; k++){
if (k < nx/2){
// add low pass filter:
kx[k] = I*2*pi*k/xf;
kx[k]*=1./(1.+pow(k/kc,n));
} else if (k > nx/2){
kx[k] = I*2*pi*(k-nx)/xf;
kx[k]*=1./(1.+pow((nx-k)/kc,n));
} else if (k == nx/2){
kx[k] = 0.0;
}
}
使用這些參數,20階導數的誤差從5.27e-7減少到1.22e-12。
通過不回到衍生物之間的真實空間,可以實現另一種改進。 這樣,避免了浮點計算期間的許多舍入誤差。 在這種特殊情況下,將輸入頻率歸零可確保誤差保持為零,但這有點人為......從實際的角度來看,如果在真實空間中提供輸入信號,則幾乎需要使用濾波器來計算導數。 。
3.由於微分濾波器的條件數,誤差正在增加
該導數是線性應用,其特征在於條件數 。 假設輸入受到所有頻率上的錯誤eps
困擾。 如果第一頻率被因子alpha
放大,則頻率k
被放大因子k*alpha
。 因此,每次應用導數時,信噪比除以比率kc(最大頻率),稱為條件數。 如果濾波器重復20次,則信噪比除以kc ^ 20。
雙精度數約為eps=10e-14
精度:這是您可以獲得的最佳信噪比! 大多數實驗投入都會比這更糟糕。 例如,通常使用16位= 65536灰度級對灰度圖像進行采樣。 結果,灰度圖像的精度最多為eps=1/65536
。 類似地,典型的音頻比特深度是24,對應於eps = 6e-8。 對於接近分析的輸入,可以建議四倍精度,精度約為esp = 1e-34 ...讓我們找到kc ^ 20 * eps <1的頻率:
eps=10e-14 kc=5
eps=1/65536 kc=1
eps=1/2^24 kc=2
esp=1e-34 kc=44
因此,如果輸入是雙精度,最多只有20個導數的4個頻率將是顯着的......所有高於4的頻率必須通過強低通濾波器進行濾波。 因此,可以建議使用四倍精度:請參閱fftw的文檔,以便為gcc的四元精度類型__float128
鏈接四元組編譯fftw 。 如果輸入是圖像,那么計算20階導數就超出了范圍:沒有一個頻率會變得很重要!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.