目录
在信号滤波 (中) 这篇中对于二阶陷波滤波器的传递函数到C++代码的转化过程还有人有疑问,本篇一步步推导整个过程。
信号滤波 (中)
二阶陷波滤波器计算实例
在信号滤波 (中)这篇里已经通过Z域构建了传递函数。
H
(
z
)
=
b
0
+
b
1
z
−
1
+
b
2
z
−
2
1
+
a
1
z
−
1
+
a
2
z
−
2
H(z) = \frac{b_0 + b_1 z^{-1} + b_2 z^{-2}}{1 + a_1 z^{-1} + a_2 z^{-2}}
H(z)=1+a1z−1+a2z−2b0+b1z−1+b2z−2
式中的未知数是怎么求解出来的呢?接下来一步步推导。
一、 传递函数的参数推导
1. 首先 b 0 , b 1 , b 2 b_0, b_1, b_2 b0,b1,b2是怎么推导出来的?
b 0 , b 1 , b 2 b_0, b_1, b_2 b0,b1,b2的设置是基于前人的经验值,我们把这几个参数带入传递函数分子计算。
- b 0 = 1 b_0 = 1 b0=1
- b 1 = − 2 cos ( ω 0 ) b_1 = -2 \cos(\omega_0) b1=−2cos(ω0)
- b 2 = 1 b_2 = 1 b2=1
所谓零点就是
H
(
z
)
H(z)
H(z)的分子等于零,带入参数后
1
−
2
c
o
s
(
ω
0
)
z
−
1
+
z
−
2
=
0
{1 - 2cos( \omega_0) z^{-1} + z^{-2}} = 0
1−2cos(ω0)z−1+z−2=0.
求
z
z
z的解,大学知识就可以:
1.1. 两边同乘以
z
2
z^2
z2:
z
2
−
2
cos
(
ω
0
)
z
+
1
=
0
z^2 - 2 \cos(\omega_0) z + 1 = 0
z2−2cos(ω0)z+1=0
1.2. 使用求根公式:
z
=
−
b
±
b
2
−
4
a
c
2
a
z = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
z=2a−b±b2−4ac
1.3. 带入求根公式计算:
z
=
−
(
−
2
cos
(
ω
0
)
)
±
(
−
2
cos
(
ω
0
)
)
2
−
4
⋅
1
⋅
1
2
⋅
1
z = \frac{-(-2 \cos(\omega_0)) \pm \sqrt{(-2 \cos(\omega_0))^2 - 4 \cdot 1 \cdot 1}}{2 \cdot 1}
z=2⋅1−(−2cos(ω0))±(−2cos(ω0))2−4⋅1⋅1
z
=
2
cos
(
ω
0
)
±
4
cos
2
(
ω
0
)
−
4
2
z = \frac{2 \cos(\omega_0) \pm \sqrt{4 \cos^2(\omega_0) - 4}}{2}
z=22cos(ω0)±4cos2(ω0)−4
z
=
2
cos
(
ω
0
)
±
4
(
cos
2
(
ω
0
)
−
1
)
2
z = \frac{2 \cos(\omega_0) \pm \sqrt{4 (\cos^2(\omega_0) - 1)}}{2}
z=22cos(ω0)±4(cos2(ω0)−1)
z
=
2
cos
(
ω
0
)
±
4
(
−
sin
2
(
ω
0
)
)
2
z = \frac{2 \cos(\omega_0) \pm \sqrt{4 (-\sin^2(\omega_0))}}{2}
z=22cos(ω0)±4(−sin2(ω0))
z
=
2
cos
(
ω
0
)
±
2
j
sin
(
ω
0
)
2
z = \frac{2 \cos(\omega_0) \pm 2j \sin(\omega_0)}{2}
z=22cos(ω0)±2jsin(ω0)
1.4.求得两个解:
z
=
cos
(
ω
0
)
±
j
sin
(
ω
0
)
z = \cos(\omega_0) \pm j \sin(\omega_0)
z=cos(ω0)±jsin(ω0)
欧拉公式转换如下:
z
1
=
e
j
ω
0
,
z
2
=
e
−
j
ω
0
z_1 = e^{j \omega_0}, \quad z_2 = e^{-j \omega_0}
z1=ejω0,z2=e−jω0
1.5.结论:
可以看到,带入经验值求得的共轭复根,位于复平面的单位圆上,更具体的说是位于
ω
0
\omega_0
ω0(陷波频率)的单位圆上。
零点的作用就是使目标频率完全衰减。
同样 a 0 , a 1 , a 2 a_0, a_1, a_2 a0,a1,a2也是是基于前人的经验值来设置的,保持极点在收敛域内又接近零点。
2. 带入实际值求解
假定初始参数如下:
- 陷波频率 f 0 = 50 Hz f_0 = 50 \, \text{Hz} f0=50Hz
- 采样频率 f s = 1000 Hz f_s = 1000 \, \text{Hz} fs=1000Hz
- 品质因子 Q = 30 Q = 30 Q=30
2.1. 计算
ω
0
\omega_0
ω0:
ω
0
=
2
π
50
1000
=
0.31416
角频率/样本
\omega_0 = 2 \pi \frac{50}{1000} = 0.31416 \, \text{角频率/样本}
ω0=2π100050=0.31416角频率/样本
2.2.计算
α
\alpha
α:
α
=
sin
(
0.31416
)
2
×
30
=
0.005235
\alpha = \frac{\sin(0.31416)}{2 \times 30} = 0.005235
α=2×30sin(0.31416)=0.005235
2.3. 计算参数:
b
0
=
1
,
b
1
=
−
2
cos
(
0.31416
)
=
−
1.9021
,
b
2
=
1
b_0 = 1, \quad b_1 = -2 \cos(0.31416) = -1.9021, \quad b_2 = 1
b0=1,b1=−2cos(0.31416)=−1.9021,b2=1
a
0
=
1
+
0.005235
=
1.005235
,
a
1
=
−
2
cos
(
0.31416
)
=
−
1.9021
,
a
2
=
1
−
0.005235
=
0.994765
a_0 = 1 + 0.005235 = 1.005235, \quad a_1 = -2 \cos(0.31416) = -1.9021, \quad a_2 = 1 - 0.005235 = 0.994765
a0=1+0.005235=1.005235,a1=−2cos(0.31416)=−1.9021,a2=1−0.005235=0.994765
2.4. 归一化:
b
0
=
1
1.005235
=
0.99477
,
b
1
=
−
1.9021
1.005235
=
−
1.8934
,
b
2
=
1
1.005235
=
0.99477
b_0 = \frac{1}{1.005235} = 0.99477, \quad b_1 = \frac{-1.9021}{1.005235} = -1.8934, \quad b_2 = \frac{1}{1.005235} = 0.99477
b0=1.0052351=0.99477,b1=1.005235−1.9021=−1.8934,b2=1.0052351=0.99477
a
1
=
−
1.9021
1.005235
=
−
1.8934
,
a
2
=
0.994765
1.005235
=
0.98953
a_1 = \frac{-1.9021}{1.005235} = -1.8934, \quad a_2 = \frac{0.994765}{1.005235} = 0.98953
a1=1.005235−1.9021=−1.8934,a2=1.0052350.994765=0.98953
2.5. 求得传递函数的最终公式:
H
(
z
)
=
0.99477
−
1.8934
z
−
1
+
0.99477
z
−
2
1
−
1.8934
z
−
1
+
0.98953
z
−
2
H(z) = \frac{0.99477 - 1.8934 z^{-1} + 0.99477 z^{-2}}{1 - 1.8934 z^{-1} + 0.98953 z^{-2}}
H(z)=1−1.8934z−1+0.98953z−20.99477−1.8934z−1+0.99477z−2
3. 验证上述的传递函数
验证方法就是1.1的计算过程,首先将分子分母才开单独求根:
H
(
z
)
=
A
(
z
)
B
(
z
)
=
0.99477
z
2
−
1.8934
z
+
0.99477
z
2
−
1.8934
z
+
0.98953
H(z) = \frac{A(z)}{B(z)} = \frac{0.99477z^2 - 1.8934z + 0.99477}{z^2 - 1.8934z + 0.98953}
H(z)=B(z)A(z)=z2−1.8934z+0.989530.99477z2−1.8934z+0.99477
0.99477
z
2
−
1.8934
z
+
0.99477
=
0
0.99477z^2 - 1.8934z + 0.99477 = 0
0.99477z2−1.8934z+0.99477=0带入求根公式
z
=
−
(
−
1.8934
)
±
(
−
1.8934
)
2
−
4
(
0.99477
)
(
0.99477
)
2
(
0.99477
)
z = \frac{-(-1.8934) \pm \sqrt{(-1.8934)^2 - 4(0.99477)(0.99477)}}{2(0.99477)}
z=2(0.99477)−(−1.8934)±(−1.8934)2−4(0.99477)(0.99477)
z
=
1.8934
±
−
0.3752
1.98954
z = \frac{1.8934 \pm \sqrt{-0.3752}}{1.98954}
z=1.989541.8934±−0.3752
分子部分的根(也就是零点)为:
z
=
0.9516
±
0.3083
i
z = 0.9516 \pm 0.3083i
z=0.9516±0.3083i
同理分母
z
2
−
1.8934
z
+
0.98953
=
0
z^2 - 1.8934z + 0.98953 = 0
z2−1.8934z+0.98953=0带入求根公式
z
=
−
(
−
1.8934
)
±
(
−
1.8934
)
2
−
4
(
1
)
(
0.98953
)
2
(
1
)
z = \frac{-(-1.8934) \pm \sqrt{(-1.8934)^2 - 4(1)(0.98953)}}{2(1)}
z=2(1)−(−1.8934)±(−1.8934)2−4(1)(0.98953)
z
=
1.8934
±
−
0.37922
2
z = \frac{1.8934 \pm \sqrt{-0.37922}}{2}
z=21.8934±−0.37922
分母部分的根(也就是极点)为:
z
=
0.9467
±
0.3070
i
z = 0.9467 \pm 0.3070i
z=0.9467±0.3070i
画出零点和极点在Z域上的图像,可以看到零点在单位圆上,极点在单位圆内侧(收敛域内,在外侧就发散了),极点和零点挨得很近。
二、将传递函数转化成差分方程
2.1 传递函数写成输入输出形式
将传递函数可以看成Z域上输入X(z)和输出Y(z)的比值
H
(
z
)
=
Y
(
z
)
X
(
z
)
H(z) = \frac{Y(z)}{X(z)}
H(z)=X(z)Y(z)。
将输出Y(z)放到等式左边得
Y
(
z
)
=
H
(
z
)
⋅
X
(
z
)
Y(z) = H(z) \cdot X(z)
Y(z)=H(z)⋅X(z),展开后如下:
Y
(
z
)
=
(
b
0
+
b
1
z
−
1
+
b
2
z
−
2
1
+
a
1
z
−
1
+
a
2
z
−
2
)
⋅
X
(
z
)
Y(z) = \left( \frac{b_0 + b_1 z^{-1} + b_2 z^{-2}}{1 + a_1 z^{-1} + a_2 z^{-2}} \right) \cdot X(z)
Y(z)=(1+a1z−1+a2z−2b0+b1z−1+b2z−2)⋅X(z)
把分母去掉得到:
(
1
+
a
1
z
−
1
+
a
2
z
−
2
)
⋅
Y
(
z
)
=
(
b
0
+
b
1
z
−
1
+
b
2
z
−
2
)
⋅
X
(
z
)
(1 + a_1 z^{-1} + a_2 z^{-2}) \cdot Y(z) = (b_0 + b_1 z^{-1} + b_2 z^{-2}) \cdot X(z)
(1+a1z−1+a2z−2)⋅Y(z)=(b0+b1z−1+b2z−2)⋅X(z)
2.2 Z域转化为时域
z
−
n
z^{-n}
z−n 代表在n时刻的采样,
z
−
1
z^{-1}
z−1代表在n-1时刻的采样,
z
−
2
z^{-2}
z−2代表在n-1时刻的采样,以此类推。
因此转化为时域后的差分方程如下:
y
[
n
]
+
a
1
y
[
n
−
1
]
+
a
2
y
[
n
−
2
]
=
b
0
x
[
n
]
+
b
1
x
[
n
−
1
]
+
b
2
x
[
n
−
2
]
y[n] + a_1 y[n-1] + a_2 y[n-2] = b_0 x[n] + b_1 x[n-1] + b_2 x[n-2]
y[n]+a1y[n−1]+a2y[n−2]=b0x[n]+b1x[n−1]+b2x[n−2]
整理后如下:
y
[
n
]
=
b
0
x
[
n
]
+
b
1
x
[
n
−
1
]
+
b
2
x
[
n
−
2
]
−
a
1
y
[
n
−
1
]
−
a
2
y
[
n
−
2
]
y[n] = b_0 x[n] + b_1 x[n-1] + b_2 x[n-2] - a_1 y[n-1] - a_2 y[n-2]
y[n]=b0x[n]+b1x[n−1]+b2x[n−2]−a1y[n−1]−a2y[n−2]
三、将差分方程转化成C++代码
3.1 改写输入输出变量
在C++中,用x_n表示输入x[n],y_n代表输出y[n],x0输入来自于i时刻电压采样voltageData[i]。y_n是经过差分方程计算后的输出。
同理,前一个时刻的输入x[n-1]用x_n_1表示,前两个个时刻的输入x_n_2用x[n-2]表示,前一个时刻的输出y[n-1]用y_n_1表示,前两个时刻的输出y[n-2]用y_n_2表示。
转化后如下:
double y_n = b0 * x_n + b1 * x_n_1 + b2 * x_n_2 - a1 * y_n_1 - a2 * y_n_2;
滤波后的变量存储在filteredData[i]中。
3.2 用循环改写差分方程的递推过程
差分方程其实就是一个递推过程,依据当前输入、前一时刻输入和输出、前两时刻输入和输出,推出当前输出。
当循环完成一次后执行下面的代码把当前的输入输出变成前一时刻的输入输出:
x_n_2 = x_n_1;
x_n_1 = x_n;
y_n_2 = y_n_1;
y_n_1 = y_n;
其中
x_n_1
和x_n_2
: 前时刻的采样 x[n-1] 和 x[n-2]。y_n_1
和y_n_2
: 前时刻采样的输出 y [ n − 1 ] y[n-1] y[n−1] 和 y [ n − 2 ] y[n-2] y[n−2]。
3.3 转化成C++后的代码
下面用QT C++写applyNotchFilter滤波函数:
QVector<double> applyNotchFilter(const QVector<double> &voltageData, double sampleRate, double notchFreq, double qualityFactor)
{
QVector<double> filteredData(voltageData.size());
double w0 = 2.0 * M_PI * notchFreq / sampleRate;
double alpha = sin(w0) / (2.0 * qualityFactor);
double b0 = 1.0;
double b1 = -2.0 * cos(w0);
double b2 = 1.0;
double a0 = 1.0 + alpha;
double a1 = -2.0 * cos(w0);
double a2 = 1.0 - alpha;
b0 /= a0;
b1 /= a0;
b2 /= a0;
a1 /= a0;
a2 /= a0;
double x_n_1 = 0.0, x_n_2 = 0.0;
double y_n_1 = 0.0, y_n_2 = 0.0;
for (int i = 0; i < voltageData.size(); ++i) {
double x_n = voltageData[i];
double y_n = b0 * x_n + b1 * x_n_1 + b2 * x_n_2 - a1 * y_n_1 - a2 * y_n_2;
filteredData[i] = y_n;
x_n_2 = x_n_1;
x_n_1 = x_n;
y_n_2 = y_n_1;
y_n_1 = y_n;
}
return filteredData;
}
完整代码的应用见信号滤波 (上) 3.3部分。