为什么MATLAB和C版本会产生不同的结果?
MATLAB:
gender
C:
[B_coeffs, A_coeffs ] = butter(4, 100/(16000/2), 'high');
state = zeros( 4, 1 );
input = zeros( 64,1 );
for i=1:64
input(i)=i;
end
[filtered_output, state] = filter( B_coeffs, A_coeffs, input, state );
,其中
int main(...)
{
for(int test=0; test<64;test++)
Xin[test]=test+1;
...
high_pass_filter_init(...)
high_pass_filter_do(...)
}
// Do the filtering
void high_pass_filter_do( t_high_pass_filter* hpf, float *Xin, float *Yout )
{
double Xi, Yi;
double z0 = hpf->state[0],
z1 = hpf->state[1],
z2 = hpf->state[2],
z3 = hpf->state[3];
unsigned int N = 64;
unsigned int i = 0;
for(i=0;i<N;i++)
{
Xi = Xin[i];
Yi = hpf->B_coeffs[0] * Xi + z0;
z0 = hpf->B_coeffs[1] * Xi + z1 - hpf->A_coeffs[1] * Yi;
z1 = hpf->B_coeffs[2] * Xi + z2 - hpf->A_coeffs[2] * Yi;
z2 = hpf->B_coeffs[3] * Xi + z3 - hpf->A_coeffs[3] * Yi;
z3 = hpf->B_coeffs[4] * Xi - hpf->A_coeffs[4] * Yi;
Yout[i] = (float) Yi;
}
hpf->state[0] = z0;
hpf->state[1] = z1;
hpf->state[2] = z2;
hpf->state[3] = z3;
return;
}
**输出为:**
typedef struct
{
float A_coeffs[5];
float B_coeffs[5];
float state[4];
} t_high_pass_filter;
void high_pass_filter_init( t_high_pass_filter* hpf)
{
hpf->A_coeffs[0] = 1.0000;
hpf->A_coeffs[1] = -3.8974;
hpf->A_coeffs[2] = 5.6974;
hpf->A_coeffs[3] = -3.7025;
hpf->A_coeffs[4] = 0.9025;
hpf->B_coeffs[0] = 0.9500;
hpf->B_coeffs[1] = -3.7999;
hpf->B_coeffs[2] = 5.6999;
hpf->B_coeffs[3] = -3.7999;
hpf->B_coeffs[4] = 0.9500;
hpf->state[0] = 0.0;
hpf->state[1] = 0.0;
hpf->state[2] = 0.0;
hpf->state[3] = 0.0;
}
前几个值相同(或类似),但随后它们发散。此外,在第一次迭代后,过滤器状态完全不同。
我做错了什么?
答案 0 :(得分:3)
在第二次修改后,您的问题变得清晰:chaotic behavior。
第一次,您似乎刚刚将MATLAB命令窗口中的系数复制到C函数中。但是,MATLAB的format
似乎已设置为short
,因为C函数中的系数舍入到4位小数。这个舍入(以及第一次在C函数中使用float
)是你的问题。
这次是我这次做的事情:
结论:您看到对初始值的敏感度非常高。
TL; DR :此代码:
clc
[B_coeffs, A_coeffs] = butter(4, 100/(16000/2), 'high');
state = zeros(4, 1);
input = 1:64;
% MATLAB version
[filtered_output0, state0] = filter(B_coeffs, A_coeffs, input, state);
mex filter_test.c
% MEX, using built-in constants (of which only the first few digits are equal)
[filtered_output1, state1] = filter_test([],[], input, state, 0);
% MEX, using the exact same constants
[filtered_output2, state2] = filter_test(B_coeffs, A_coeffs, input, state, 1);
% Compare!
[filtered_output0.' filtered_output1.' filtered_output2.']
[state0 state1 state2]
filter_test.c
包含的位置:
#include <stdio.h>
#include "mex.h"
#define N ( 64u)
#define C ( 5u)
#define S (C-1u)
/* helper struct for HPF */
typedef struct
{
double A_coeffs[C];
double B_coeffs[C];
double state[S];
} t_high_pass_filter;
/* "Default" values (note that these are ROUNDED to 4 digits only)
void
high_pass_filter_init(t_high_pass_filter* hpf)
{
hpf->A_coeffs[0] = 1.0000;
hpf->A_coeffs[1] = -3.8974;
hpf->A_coeffs[2] = 5.6974;
hpf->A_coeffs[3] = -3.7025;
hpf->A_coeffs[4] = 0.9025;
hpf->B_coeffs[0] = 0.9500;
hpf->B_coeffs[1] = -3.7999;
hpf->B_coeffs[2] = 5.6999;
hpf->B_coeffs[3] = -3.7999;
hpf->B_coeffs[4] = 0.9500;
hpf->state[0] = 0.0;
hpf->state[1] = 0.0;
hpf->state[2] = 0.0;
hpf->state[3] = 0.0;
}
/* the actual filter */
void
high_pass_filter_do(t_high_pass_filter* hpf,
const double *Xin,
double *Yout)
{
double Xi, Yi;
double z0 = hpf->state[0],
z1 = hpf->state[1],
z2 = hpf->state[2],
z3 = hpf->state[3];
unsigned int i = 0u;
for(i=0; i<N; ++i)
{
Xi = Xin[i];
Yi = hpf->B_coeffs[0] * Xi + z0;
z0 = hpf->B_coeffs[1] * Xi + z1 - hpf->A_coeffs[1] * Yi;
z1 = hpf->B_coeffs[2] * Xi + z2 - hpf->A_coeffs[2] * Yi;
z2 = hpf->B_coeffs[3] * Xi + z3 - hpf->A_coeffs[3] * Yi;
z3 = hpf->B_coeffs[4] * Xi - hpf->A_coeffs[4] * Yi;
Yout[i] = Yi;
}
hpf->state[0] = z0;
hpf->state[1] = z1;
hpf->state[2] = z2;
hpf->state[3] = z3;
}
/* Wrapper between MATLAB MEX and filter function */
void
filter(const double *B_coeffs,
const double *A_coeffs,
const double *input,
const double *state,
double *filtered_output,
double *state_output)
{
unsigned int i = 0u;
t_high_pass_filter hpf;
/* Use built-in defaults when coefficients
* have not been passed explicitly */
if (B_coeffs == NULL)
{
high_pass_filter_init(&hpf);
}
/* Otherwise, use the coefficients on the arguments */
else
{
for (i=0u; i<C; ++i) {
hpf.B_coeffs[i] = B_coeffs[i];
hpf.A_coeffs[i] = A_coeffs[i];
}
for (i=0u; i<S; ++i)
hpf.state[i] = state[i];
}
/* Apply filter function */
high_pass_filter_do(&hpf,
input,
filtered_output);
/* Assign output state explicitly */
for (i=0u; i<S; ++i)
state_output[i] = hpf.state[i];
}
/* MATLAB/MEX Gateway function */
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
{
unsigned int i = 0u;
double *B_coeffs = mxGetPr(prhs[0]),
*A_coeffs = mxGetPr(prhs[1]),
*input = mxGetPr(prhs[2]),
*state = mxGetPr(prhs[3]);
int flag = *mxGetPr(prhs[4]);
double *filtered_output,
*state_output;
/* filtered_output, state */
plhs[0] = mxCreateDoubleMatrix(1, N, mxREAL);
plhs[1] = mxCreateDoubleMatrix(S, 1, mxREAL);
filtered_output = mxGetPr(plhs[0]);
state_output = mxGetPr(plhs[1]);
if (flag == 0)
{
filter(NULL,
NULL,
input,
state,
filtered_output,
state_output);
}
else
{
filter(B_coeffs,
A_coeffs,
input,
state,
filtered_output,
state_output);
}
}
给出了这个输出:
% MATLAB C with rounding C without rounding
%===============================================================================
filtered values:
9.499817826393004e-001 9.500000000000000e-001 9.499817826393004e-001
1.802482117980145e+000 1.802630000000000e+000 1.802482117980145e+000
2.562533610562189e+000 2.563140162000000e+000 2.562533610562189e+000
... (58 more values) ... ...
-5.477082906502112e+000 2.637900416547026e+002 -5.477082906502112e+000
-5.550054712403821e+000 2.808186934967214e+002 -5.550054712403821e+000
-5.610782439991141e+000 2.985749768301545e+002 -5.610782439991141e+000
states after filter run:
-6.740826417997180e+001 2.553210086702404e+002 -6.740826417997180e+001
2.006572641218511e+002 -7.151404729138806e+002 2.006572641218511e+002
-1.991114894348111e+002 6.686913808328562e+002 -1.991114894348111e+002
6.586237103693912e+001 -2.086639165892145e+002 6.586237103693912e+001
下次,请确保在复制粘贴之前使用format long e
:)
答案 1 :(得分:2)
当我运行你的代码时,我明白了:
MATLAB C
================================
0.9500 0.950000
1.8026 1.802630
2.5631 2.563139
...
(58 more values)
...
263.7900 263.687500
280.8187 280.705475
298.5750 298.450012
但是,当我将C
代码更改为使用double
代替float
时,我明白这一点:
MATLAB C
================================
0.9500 0.950000
1.8026 1.802630
2.5631 2.563140
...
(58 more values)
...
263.7900 263.790042
280.8187 280.818693
298.5750 298.574977
所以,@ Richard是正确的:
double
代替float
。