阶段三:IIR滤波器

🔄 第14课:双线性变换

模拟到数字的映射——频率预畸变与双线性变换

1. 双线性变换的基本原理

双线性变换(Bilinear Transform)是将模拟滤波器转换为数字滤波器的标准方法。它通过s→z的映射,将s平面的虚轴映射为z平面的单位圆,保持频率响应形状:

s = (2/T)·(z-1)/(z+1),其中T为采样周期

双线性变换的核心优势:(1)保持稳定性——s左半平面映射到z单位圆内;(2)无频率混叠——整个jΩ轴映射到单位圆;(3)代数变换——直接替换s即可得到H(z)。

但也有一个重要缺点:频率扭曲(frequency warping)。模拟频率Ω与数字频率ω的关系是非线性的:

Ω = (2/T)·tan(ω/2),即频率轴被"扭曲"了

这意味着需要预畸变(prewarping)来补偿:设计时先将关键频率按反方向扭曲,再进行双线性变换。

2. 频率预畸变

预畸变是双线性变换的关键步骤,确保数字滤波器在指定频率处的响应与模拟原型一致:

Ω_prewarp = (2/T)·tan(ω_c·T/2),然后用预畸变后的Ω设计模拟原型

对于每个关键频率(通带边沿、阻带边沿),都需要进行预畸变。这样双线性变换后,数字滤波器在这些频率点的响应与原始设计目标一致。

📋 预畸变步骤

  1. 确定数字指标:ωp, ωs, δp, δs
  2. 预畸变:Ωp=(2/T)tan(ωpT/2), Ωs=(2/T)tan(ωsT/2)
  3. 用预畸变频率设计模拟原型H(s)
  4. 双线性变换:s→(2/T)(z-1)/(z+1)
  5. 验证数字H(z)的频率响应

3. 双线性变换的数学推导

从s到z的映射推导:

s = (2/T)·(z-1)/(z+1) → z = (1+sT/2)/(1-sT/2)

当s=jΩ时:z = (1+jΩT/2)/(1-jΩT/2),|z|=1(在单位圆上)

当Ω=0时z=1,当Ω=∞时z=-1,当Ω=2/T时z=j。整个jΩ轴[0,∞)映射到单位圆的[0,π)。

这种一一映射保证了无混叠,但也引入了非线性频率扭曲。低频处扭曲小(Ω≈ω/T),高频处扭曲严重。

4. Verilog实现:双线性变换IIR

//=============================================
// dsp_bilinear.v
// 双线性变换IIR滤波器
// 预畸变 + 系数映射
//=============================================
module dsp_bilinear #(
    parameter DATA_WIDTH = 16,
    parameter COEFF_WIDTH = 16,
    parameter OUT_WIDTH = 32
)(
    input  wire                          clk,
    input  wire                          rst_n,
    input  wire                          data_valid,
    input  wire signed [DATA_WIDTH-1:0]  data_in,
    input  wire signed [COEFF_WIDTH-1:0] b0, b1, b2,   // 分子系数
    input  wire signed [COEFF_WIDTH-1:0] a1, a2,        // 分母系数
    output reg  signed [OUT_WIDTH-1:0]   data_out,
    output reg                           out_valid
);
    // 二阶IIR(双二阶)
    reg signed [DATA_WIDTH-1:0]  x1, x2;  // 输入延迟
    reg signed [OUT_WIDTH-1:0]   y1, y2;   // 输出延迟

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            x1 <= 0; x2 <= 0; y1 <= 0; y2 <= 0;
            data_out <= 0; out_valid <= 1'b0;
        end else if (data_valid) begin
            // y[n] = b0*x[n] + b1*x[n-1] + b2*x[n-2]
            //       - a1*y[n-1] - a2*y[n-2]
            data_out <= b0*data_in + b1*x1 + b2*x2
                        - a1*y1 - a2*y2;
            x2 <= x1; x1 <= data_in;
            y2 <= y1; y1 <= data_out;
            out_valid <= 1'b1;
        end else out_valid <= 1'b0;
    end
endmodule

//=============================================
// dsp_prewarp.v
// 频率预畸变计算模块
// Ω = (2/T)*tan(ω*T/2)
//=============================================
module dsp_prewarp #(
    parameter PHASE_WIDTH = 16,
    parameter DATA_WIDTH = 16,
    parameter SAMPLE_RATE = 48000
)(
    input  wire [PHASE_WIDTH-1:0]        omega_d,    // 数字角频率
    output reg  signed [DATA_WIDTH-1:0]   omega_a     // 预畸变模拟角频率
);
    // 预畸变:Ω = 2*fs*tan(ω/(2*fs))
    // 使用查表法近似tan函数
    reg signed [DATA_WIDTH-1:0] tan_lut [0:255];
    initial begin
        integer i;
        for (i = 0; i < 256; i = i + 1)
            tan_lut[i] = $rtoi((2.0**(DATA_WIDTH-1)-1) *
                $tan(3.14159265*i/512));
    end
    always @(*) begin
        omega_a <= tan_lut[omega_d[PHASE_WIDTH-1:PHASE_WIDTH-8]];
    end
endmodule

//=============================================
// tb_bilinear.v
// 双线性变换IIR测试台
//=============================================
`timescale 1ns/1ps
module tb_bilinear;
    parameter DW=16, CW=16, OW=32;
    reg clk, rst_n, dv;
    reg signed [DW-1:0] x_in;
    reg signed [CW-1:0] b0, b1, b2, a1, a2;
    wire signed [OW-1:0] y_out;
    wire ov;

    dsp_bilinear #(.DATA_WIDTH(DW),.COEFF_WIDTH(CW),.OUT_WIDTH(OW))
        uut(.clk(clk),.rst_n(rst_n),.data_valid(dv),.data_in(x_in),
            .b0(b0),.b1(b1),.b2(b2),.a1(a1),.a2(a2),
            .data_out(y_out),.out_valid(ov));

    initial clk = 0; always #5 clk = ~clk;
    initial begin
        $dumpfile("bilinear.vcd"); $dumpvars(0, tb_bilinear);
        // 设置二阶低通系数(示例值)
        b0 = 16'sd2076; b1 = 16'sd4153; b2 = 16'sd2076;
        a1 = -16'sd5956; a2 = 16'sd3754;
        rst_n = 0; dv = 0; x_in = 0; #50; rst_n = 1;
        // 脉冲响应测试
        dv = 1; x_in = 32767;
        @(posedge clk); x_in = 0;
        repeat(100) @(posedge clk);
        dv = 0;
        $display("双线性变换IIR测试完成");
        $finish;
    end
endmodule

5. 脉冲响应不变法对比

📋 双线性变换 vs 脉冲响应不变法

特性双线性变换脉冲响应不变法
频率混叠有(高频折叠到低频)
频率扭曲有(需预畸变)
稳定性保持保持
适用滤波器低通/带通/高通/带阻仅低通/带通
频率响应保真形状保持但扭曲高频失真

脉冲响应不变法的优势是频率不扭曲,缺点是有混叠。对于低通和带通滤波器且阻带衰减足够时,两种方法都可使用。对于高通和带阻,必须用双线性变换。

6. 双二阶(Biquad)滤波器

双线性变换得到的IIR通常分解为二阶节(双二阶)级联。一个双二阶的系统函数为:

H(z) = (b0 + b1·z⁻¹ + b2·z⁻²) / (1 + a1·z⁻¹ + a2·z⁻²)

双二阶是IIR的基本构建模块,所有高阶IIR都可分解为双二阶级联或并联。其系数计算步骤:(1)设计模拟原型;(2)预畸变频率;(3)双线性变换得到数字系数;(4)量化为定点。

7. 系数量化对IIR的影响

⚠️ IIR系数量化的严重性

8. 练习

📝 练习1:预畸变计算

数字低通:ωp=0.2π,ωs=0.3π,T=1/48000。计算预畸变后的Ωp和Ωs。

📝 练习2:双线性变换

一阶模拟H(s)=1/(s+1),用双线性变换求H(z)(T=1)。

📝 练习3:系数计算

用双线性变换设计二阶Butterworth低通:fc=1kHz,fs=8kHz。写出数字系数b0,b1,b2,a1,a2。

📝 练习4:量化影响

上述系数分别量化为8位和16位,分析极点位置偏移。

9. 成就

🏆 成就解锁:双线性变换大师

✅ 理解了双线性变换的数学原理

✅ 掌握了频率预畸变的方法

✅ 实现了双二阶IIR滤波器

✅ 理解了双线性变换vs脉冲响应不变法

✅ 掌握了IIR系数量化的影响与对策

11. 补充:双线性变换的进阶主题

双线性变换在进阶应用中还有以下重要主题值得深入研究:

📚 进阶研究方向

12. 与前后课程的关联

本课内容在整个DSP课程体系中处于承上启下的位置:

🔗 课程关联图

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单

13. 设计经验总结

在实际工程中,双线性变换的设计经验总结如下:(1)始终从系统级需求出发,不要过早陷入实现细节;(2)先用浮点仿真验证算法正确性,再转为定点;(3)系数量化后必须重新验证频率响应;(4)硬件实现前用C/MATLAB模型作为参考;(5)综合后检查时序和资源是否符合预期;(6)板级验证时用已知的测试信号对比参考输出。每一步验证都是必要的,跳过任何一步都可能导致最终的调试时间成倍增加。

📌 关键设计检查清单