第三章 数字信号处理基础(下):FIR滤波器设计、CIC滤波器、多速率信号处理
好,咱们接着往下聊。上一章我们把数字信号处理的基础概念过了一遍,这一章我打算聚焦在三个在雷达系统里天天打交道的模块:FIR滤波器、CIC滤波器,还有多速率信号处理。说实话,这三个东西在FPGA里实现的时候,坑不少,但用好了,效果也是立竿见影的。
3.1 FIR滤波器设计——从理论到FPGA实现
FIR滤波器,全称是有限长单位冲激响应滤波器。为什么雷达里爱用FIR?说白了,就两个原因:线性相位和绝对稳定。线性相位意味着信号经过滤波器后,不同频率分量的延迟是一样的,不会把脉冲波形给“扯歪”了。这在雷达脉冲压缩里是致命的——波形一歪,测距精度就没了。
我个人习惯,在设计FIR滤波器时,第一步不是写代码,而是先在MATLAB里把系数算好。用fir1或者firpm函数,把通带、阻带、阶数定下来。这里有个经验:滤波器阶数越高,过渡带越陡,但资源消耗也越大。在FPGA里,乘法器是稀缺资源,所以别一味追求高指标。
核心要点: FIR滤波器的FPGA实现结构主要有三种:直接型、转置型、对称型。雷达里最常用的是对称型,因为可以利用系数对称性,把乘法器数量砍掉一半。
举个例子,一个32阶的FIR滤波器,如果系数是对称的,你只需要16个乘法器。我在项目中遇到过,一个脉冲压缩模块里用了4个128阶的FIR,如果不用对称结构,光乘法器就能吃掉大半个芯片。
3.1.1 量化效应——别让精度坑了你
嗯,这里要注意。MATLAB算出来的系数是双精度浮点,但FPGA里只能用定点数。量化成16位还是12位?这直接影响滤波器的性能。
我曾经在一个项目里,为了省资源,把系数从16位砍到12位。结果仿真时没发现问题,一上板子,脉冲压缩的旁瓣抬高了3个dB。后来查了半天,就是量化误差闹的。所以我的建议是:系数量化至少保留14位以上,如果资源允许,16位更稳妥。
// Verilog示例:对称型FIR滤波器核心代码
// 假设系数已经量化并存储在ROM中
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
sum <= 0;
end else begin
// 对称加法:x[n] + x[N-1-n]
sym_add <= data_in + data_delay[N-1];
// 乘加运算
sum <= sum + sym_add * coeff[0];
// ... 依次类推,流水线处理
end
end
小技巧: 在FPGA里实现FIR时,记得用流水线结构。把乘法和加法拆成多级,可以跑更高的时钟频率。我一般会插入2-3级寄存器,这样时钟频率能轻松上200MHz。
3.2 CIC滤波器——抽取与插值的利器
CIC滤波器,全称是级联积分梳状滤波器。这玩意儿在雷达里太常见了,尤其是做降采样的时候。为什么用它?因为它不需要乘法器,只用加法器和寄存器,资源消耗极低。
你想想看,如果ADC采样率是200MHz,但你的基带处理只需要50MHz,中间差了4倍。直接用FIR做抽取?那滤波器阶数会高得吓人。用CIC先粗抽一把,再用FIR做精细滤波,这是标准做法。
3.2.1 CIC的结构与设计
CIC由两部分组成:积分器和梳状器。积分器就是累加器,梳状器就是延迟相减。级联的级数N决定了阻带衰减特性。N越大,阻带衰减越陡,但通带衰减也越大。
我记得有一次,一个同事设计了一个5级CIC,抽取因子是16。仿真时通带衰减只有0.2dB,觉得没问题。结果级联了后面的FIR后,整体通带衰减到了1.5dB。这就是没算清楚CIC的通带“滚降”。
避坑指南: CIC滤波器的通带衰减是固定的,与级数N和抽取因子R有关。公式是:衰减 = 20 * N * log10( (R * sin(π * f_c / f_s)) / (π * f_c / f_s) )。设计时一定要预留补偿空间,或者在后面加一个CIC补偿滤波器。
在FPGA里实现CIC,关键是要注意位宽增长。积分器是累加器,位宽会一直增长。如果不做截位,位宽会变得非常大。我一般会在每级之间做截位,或者用ceil(log2(R * N))来估算最大位宽。
// Verilog示例:单级CIC积分器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
integrator <= 0;
end else begin
integrator <= integrator + data_in;
end
end
// 梳状器(在抽取之后)
always @(posedge clk_dec or negedge rst_n) begin
if (!rst_n) begin
comb_delay <= 0;
comb_out <= 0;
end else begin
comb_delay <= integrator_out;
comb_out <= integrator_out - comb_delay;
end
end
3.3 多速率信号处理——降采样与升采样
多速率信号处理,说白了就是改变信号的采样率。在雷达里,这个需求太普遍了。ADC采样率很高,但后续处理不需要那么高,那就降采样。DAC需要高采样率,但基带数据率低,那就升采样。
这里有个核心概念:抽取和插值。抽取就是扔掉一些样本,插值就是在样本之间插入零值。但直接抽取会引入混叠,直接插值会引入镜像。所以必须配合滤波器使用。
3.3.1 抽取与抗混叠滤波
抽取之前,一定要加一个抗混叠滤波器。这个滤波器的截止频率要小于抽取后采样率的一半。比如,采样率100MHz,抽取因子2,抽取后采样率50MHz,那抗混叠滤波器的截止频率要小于25MHz。
我见过有人图省事,直接抽取不滤波。结果频谱里全是混叠的杂散,目标都找不到了。所以,抽取必滤波,这是铁律。
3.3.2 插值与镜像抑制
插值之后,一定要加一个镜像抑制滤波器。插值会在原始频谱周围产生镜像,必须滤掉。这个滤波器的截止频率也是原始信号带宽的一半。
在实际工程中,我经常用多级实现。比如,要做8倍降采样,我不会直接用一个FIR做8倍抽取,而是用CIC做4倍抽取,再用FIR做2倍抽取。这样总资源更少,而且滤波器设计更灵活。
经验之谈: 多速率系统的设计原则是“先滤波,后抽取;先插值,后滤波”。这个顺序不能乱。另外,多级实现时,每一级的滤波器指标可以放宽,因为每一级处理的采样率不同,过渡带要求也不同。
3.3.3 多相滤波器结构
当抽取或插值因子很大时,直接实现滤波器效率很低。这时候就要用多相结构。多相结构把滤波器系数分成多组,每组只处理对应相位的样本。这样,计算量可以降低到原来的1/R。
我在一个项目里,需要做32倍降采样。如果用直接型FIR,每个输出点需要做128次乘加。用了多相结构后,每个输出点只需要做4次乘加。效率提升了32倍。你说香不香?
// 多相抽取滤波器示意(R=4)
// 系数分成4组:h0, h1, h2, h3
// 每个时钟周期,只处理一组系数
always @(posedge clk) begin
case (phase_counter)
0: sum <= sum + data_in * h0[addr];
1: sum <= sum + data_in * h1[addr];
2: sum <= sum + data_in * h2[addr];
3: sum <= sum + data_in * h3[addr];
endcase
end
设计建议: 多相滤波器的系数分组,可以用MATLAB的reshape函数轻松实现。把系数向量重排成R行,每行就是一组多相系数。然后FPGA里用循环寻址,就能高效实现。
3.4 本章小结
这一章的内容,说白了就是三个核心模块:FIR、CIC、多速率处理。FIR是精细滤波的主力,CIC是粗抽粗插的利器,多速率处理是连接不同采样率的桥梁。在雷达信号处理链里,它们经常组合使用。
我个人建议,初学者可以先从FIR的对称结构入手,再学CIC的位宽控制,最后掌握多相结构。每一步都动手写写Verilog代码,跑跑仿真。纸上得来终觉浅,绝知此事要躬行。
下一章,我们会聊到FFT和脉冲压缩。那才是雷达信号处理的重头戏。咱们到时候见。