概念的区分
IIR(Infinite Impulse Response,无限脉冲响应)和FIR(Finite Impulse Response,有限脉冲响应)滤波器是两种常见的数字信号处理滤波器类型,它们在结构、性能和用途上有显著区别:
结构差异:
IIR滤波器具有反馈结构,即滤波器的输出不仅取决于当前和过去输入信号的值,还包括过去的输出信号。这意味着IIR滤波器的冲激响应永远不会完全衰减到零,理论上无穷期延续。
FIR滤波器则不包含反馈环路,其输出只依赖于当前和过去的输入信号。FIR滤波器的冲激响应在有限时间内会衰减至零。
稳定性:
FIR滤波器总是稳定的,因为没有反馈回路,不存在发散的可能性。
IIR滤波器的稳定性取决于其极点位置,如果所有的极点都在单位圆内,则滤波器是稳定的;否则,可能会导致不稳定。
相位响应:
FIR滤波器具有线性相位响应,即所有频率的信号通过滤波器后,其相位延迟与频率成线性关系,这对于保持信号的相位同步和时间对齐很重要。
IIR滤波器一般具有非线性相位响应,除非特别设计,否则相位延迟会随着频率变化,这在某些应用中可能会带来问题。
滤波特性:
IIR滤波器因其反馈结构可以实现较高的滤波效率,用较少的阶数就能达到所需的滤波特性,比如陡峭的滚降特性或接近理想滤波器的响应曲线。
FIR滤波器虽然阶数较高时才能达到类似的效果,但因其线性相位和无发散风险,更适合需要精确相位和稳态无误差的场合。
设计灵活性:
FIR滤波器的设计通常更为直观和灵活,易于实现严格的带宽、阻带衰减和线性相位响应。
IIR滤波器可以基于经典滤波器设计方法(如巴特沃兹、切比雪夫等)实现特定频率响应,但其非线性相位特性增加了设计难度。
在工程实践中,选择IIR还是FIR滤波器通常取决于具体的应用需求。如果对相位线性度要求较高,或担心稳定性问题,通常会选择FIR滤波器。而在对计算资源有限,需要以较低阶数实现较强滤波效果,且对相位非线性有一定的容忍度时,IIR滤波器则是一个合适的选择。在现代DSP应用中,由于计算能力的增强,FIR滤波器因其稳定性、线性相位和易于设计的特点而越来越受到青睐。
C语言实现
实现IIR(无限脉冲响应)和FIR(有限脉冲响应)带通滤波器通常涉及到计算每个新样本点时的滤波操作。以下是对这两种滤波器的基本实现框架的概述以及示例C代码片段:
IIR带通滤波器
IIR滤波器通常通过递归的方式实现,即输出不仅取决于当前的输入样本,还取决于过去若干个输入样本以及过去若干个输出样本。巴特沃斯、切比雪夫、椭圆滤波器等经典滤波器结构都可以被用来设计IIR带通滤波器。以下是一个二阶Butterworth IIR带通滤波器的例子:
#include <math.h>
// 假设我们已经有了滤波器的系数
#define B0 0.5 * (1.0 + sqrt(2)) // 振幅系数B0
#define B1 0.0 // 振幅系数B1
#define B2 (-1.0 * B0) // 振幅系数B2
#define A1 (-1.0 * sqrt(2) / 2.0) // 阻尼系数A1
#define A2 (1.0 - sqrt(2) / 2.0) // 阻尼系数A2
// 初始化历史状态变量
float x_n_1 = 0.0; // 上一次输入样本
float x_n_2 = 0.0; // 上上次输入样本
float y_n_1 = 0.0; // 上一次输出样本
float y_n_2 = 0.0; // 上上次输出样本
// IIR带通滤波器核心函数
float iir_bandpass_filter(float input)
{
// 计算当前输出
float output = B0 * input + B1 * x_n_1 + B2 * x_n_2 - A1 * y_n_1 - A2 * y_n_2;
// 更新历史状态
x_n_2 = x_n_1;
x_n_1 = input;
y_n_2 = y_n_1;
y_n_1 = output;
return output;
}
// 使用滤波器
int main()
{
float input_signal[signal_length];
float output_signal[signal_length];
// ... 填充input_signal...
for (int i = 0; i < signal_length; ++i)
{
output_signal[i] = iir_bandpass_filter(input_signal[i]);
}
// ... 处理output_signal...
return 0;
}
注意,实际使用时你需要根据具体的设计参数(如截止频率、品质因数等)计算合适的滤波器系数B0、B1、B2、A1和A2。
FIR带通滤波器
FIR滤波器没有反馈部分,仅依赖于当前和过去若干个输入样本。带通FIR滤波器通常通过卷积实现,系数来自于窗函数法或其他设计方法。
#include <stdio.h>
// 滤波器系数数组h[]
#define FILTER_ORDER 51 // FIR滤波器阶数
float h[FILTER_ORDER];
// 初始化滤波器系数
// ...省略了根据设计要求填充滤波器系数的过程...
// FIR带通滤波器核心函数
void fir_bandpass_filter(float input, float* state, float* output)
{
static const int STATE_SIZE = FILTER_ORDER - 1; // FIR滤波器的状态大小
static float input_state[STATE_SIZE]; // 存储过去输入样本的状态
// 移除最早的一个输入样本,添加最新的输入样本
memmove(input_state + 1, input_state, sizeof(float) * (STATE_SIZE - 1));
input_state[0] = input;
// FIR滤波输出计算
*output = 0.0;
for (int i = 0; i < FILTER_ORDER; ++i)
{
*output += h[i] * input_state[i];
}
}
// 使用滤波器
int main()
{
float input_signal[signal_length];
float output_signal[signal_length];
float filter_state[FILTER_ORDER - 1] = {0}; // 初始化状态为零
// ... 填充input_signal...
for (int i = 0; i < signal_length; ++i)
{
fir_bandpass_filter(input_signal[i], filter_state, &output_signal[i]);
}
// ... 处理output_signal...
return 0;
}
同样,这里的FIR滤波器系数`h[]`需要根据目标频率响应曲线设计得到。在实际项目中,可以使用像MATLAB、Python的SciPy库或者其他DSP工具设计滤波器并获取这些系数。
请确保根据实际应用调整滤波器阶数、类型和系数,同时注意边界条件的处理,特别是对于实时流数据的第一段样本,可能需要预先填充足够的“零”值来初始化滤波器状态。