在原有的博客基础上,我们补充了内插CIC滤波器的实现与验证。新增的"3.4 内插CIC滤波器实现与验证"一节包含结构说明、Python类实现以及测试示例。最终博客全文如下(包含新内容)。
CIC滤波器设计与实现:从数学原理到Python实战
在数字信号处理领域,特别是在软件无线电和数字下变频应用中,我们经常需要处理采样率的巨大变化。当降采样倍数高达几十甚至几百倍时,传统的FIR滤波器会变得异常臃肿,因为它的抽头数量与降采样倍数成正比。级联积分梳状滤波器 应运而生,它以一种极其经济的方式解决了这个问题------不需要任何乘法器,只需加法和延迟。
1. CIC滤波器的数学原理
1.1 从滑动平均说起
要理解CIC滤波器,最直观的切入点是滑动平均滤波器。一个长度为 DDD 的滑动平均滤波器的差分方程为:
y[n]=1D∑k=0D−1x[n−k] y[n] = \frac{1}{D} \sum_{k=0}^{D-1} x[n-k] y[n]=D1k=0∑D−1x[n−k]
将其变换到Z域,传递函数为:
HMA(z)=1D1−z−D1−z−1 H_{MA}(z) = \frac{1}{D} \frac{1-z^{-D}}{1-z^{-1}} HMA(z)=D11−z−11−z−D
这个形式隐藏着一个秘密:它由一个积分器 1/(1−z−1)1/(1-z^{-1})1/(1−z−1) 和一个梳状滤波器 1−z−D1-z^{-D}1−z−D 级联而成。
1.2 CIC滤波器的传递函数
CIC滤波器正是利用了这一分解。单级CIC滤波器的传递函数为:
H(z)=1−z−RMRM(1−z−1)=1RM⋅积分器⋅梳状滤波器 H(z) = \frac{1 - z^{-RM}}{RM(1 - z^{-1})} = \frac{1}{RM} \cdot \text{积分器} \cdot \text{梳状滤波器} H(z)=RM(1−z−1)1−z−RM=RM1⋅积分器⋅梳状滤波器
其中,RRR 为抽取或内插因子,MMM 为差分延迟(通常取1或2),RMRMRM 决定了滤波器的长度。
将 z=ejωz = e^{j\omega}z=ejω 代入,得到频率响应:
∣H(ejω)∣=∣sin(ωRM/2)RMsin(ω/2)∣ |H(e^{j\omega})| = \left|\frac{\sin(\omega RM/2)}{RM \sin(\omega/2)}\right| ∣H(ejω)∣= RMsin(ω/2)sin(ωRM/2)
当 ω\omegaω 较小时,sin(ω/2)≈ω/2\sin(\omega/2) \approx \omega/2sin(ω/2)≈ω/2,上式近似为:
∣H(ejω)∣≈∣sin(ωRM/2)ωRM/2∣=∣sinc(ωRM/2)∣ |H(e^{j\omega})| \approx \left|\frac{\sin(\omega RM/2)}{\omega RM/2}\right| = |\text{sinc}(\omega RM/2)| ∣H(ejω)∣≈ ωRM/2sin(ωRM/2) =∣sinc(ωRM/2)∣
这正是sinc函数的形态------CIC滤波器本质上是一个sinc滤波器。
1.3 多级级联的考量
单级CIC滤波器的旁瓣电平仅比主瓣低约13.46dB,阻带衰减远不能满足实际需求。解决方案是采用多级级联 。若级联 NNN 级,则阻带衰减变为 N×13.46dBN \times 13.46\text{dB}N×13.46dB。例如,5级CIC滤波器可提供约67dB的阻带衰减,相当可观。
但代价是通带衰减也随着级数增加而恶化。级联 NNN 级后的传递函数为:
HN(z)=(1−z−RMRM(1−z−1))N H_N(z) = \left(\frac{1 - z^{-RM}}{RM(1 - z^{-1})}\right)^N HN(z)=(RM(1−z−1)1−z−RM)N
2. CIC滤波器的实现结构
2.1 直接型结构与硬件友好性
如果按照传递函数直接实现,CIC滤波器在结构上非常清晰:积分器在前,抽取/内插在中,梳状在后(对于抽取CIC)。图1展示了N级抽取CIC滤波器的整体实现架构。
x[n] (高采样率 f_s)
│
▼
┌─────────────┐
│ 积分器 1 │ y1[n] = y1[n-1] + x[n]
└─────────────┘
│
▼
┌─────────────┐
│ 积分器 2 │ y2[n] = y2[n-1] + y1[n]
└─────────────┘
│
▼
⋮
│
▼
┌─────────────┐
│ 积分器 N │ yN[n] = yN[n-1] + y(N-1)[n]
└─────────────┘
│
▼
[ ↓R ] <── 抽取器 (每R个点保留一个)
│
▼
┌─────────────┐
│ 梳状 1 │ c1[m] = u[m] - u[m-M] (u为抽取后的序列)
└─────────────┘
│
▼
┌─────────────┐
│ 梳状 2 │ c2[m] = c1[m] - c1[m-M]
└─────────────┘
│
▼
⋮
│
▼
┌─────────────┐
│ 梳状 N │ cN[m] = c(N-1)[m] - c(N-1)[m-M]
└─────────────┘
│
▼
y[m] (低采样率 f_s/R)
图1:N级抽取CIC滤波器结构框图
所有积分器工作在原始高采样率 fsf_sfs 下,每个积分器是一个累加器:y[n]=y[n−1]+x[n]y[n] = y[n-1] + x[n]y[n]=y[n−1]+x[n]。抽取器 ↓R\downarrow R↓R 将采样率降低为 fs/Rf_s/Rfs/R。梳状部分工作在降低后的采样率 fs/Rf_s/Rfs/R 下,每个梳状单元是一个差分器:y[m]=x[m]−x[m−RM]y[m] = x[m] - x[m-RM]y[m]=x[m]−x[m−RM](其中 MMM 通常取1或2)。
这种结构完全由加法和延迟单元构成,没有任何乘法运算,在FPGA或ASIC实现中极为高效。图2给出了积分器和梳状滤波器的基本单元内部结构。
积分器单元:
x[n] ──→⊕──→ y[n]
↑ │
│ │ z⁻¹
└───┘
图2a:CIC积分器结构
传递函数:HI(z)=11−z−1H_I(z) = \frac{1}{1-z^{-1}}HI(z)=1−z−11
梳状滤波器单元:
x[m] ──→⊕──→ y[m]
↑ │
│ │ z^{-RM}
└───┘
图2a:CIC梳状滤波器结构
传递函数:HC(z)=1−z−RMH_C(z) = 1 - z^{-RM}HC(z)=1−z−RM
2.2 Hogenauer结构与位宽增长
经典的Hogenauer结构正是图1所示的布局,它将积分器全部置于抽取之前,梳状全部置于抽取之后。这种安排使得大部分运算(积分)在高速时钟下进行,而梳状运算在低速时钟下进行,功耗更优。
一个关键问题是位宽增长 。由于积分器是累加器,数据位宽会不断增长。在N级CIC滤波器中,为避免溢出,输出位宽 LLL 需满足:
L=输入位宽+⌈Nlog2(RM)⌉ L = \text{输入位宽} + \lceil N \log_2(RM) \rceil L=输入位宽+⌈Nlog2(RM)⌉
例如,输入位宽16位,R=64R=64R=64,M=1M=1M=1,N=5N=5N=5,则输出位宽需达到 16+⌈5log2(64)⌉=16+30=4616 + \lceil 5 \log_2(64) \rceil = 16 + 30 = 4616+⌈5log2(64)⌉=16+30=46 位。
2.3 补偿滤波器的必要性
从sinc频率响应可以看出,CIC滤波器的通带并不平坦,随着频率增加有明显衰减。在许多应用中,需要在CIC后级联一个补偿滤波器(通常是FIR),其频率响应为CIC响应的倒数:
Hcomp(f)≈∣πMfsin(πMf)∣N H_{comp}(f) \approx \left|\frac{\pi M f}{\sin(\pi M f)}\right|^N Hcomp(f)≈ sin(πMf)πMf N
以此使通带恢复平坦。
3. Python实现与验证
理论讲再多,不如代码跑一跑。下面我们通过Python实现CIC抽取和内插滤波器,并验证其功能。
3.1 面向对象的CIC抽取滤波器实现
我们首先设计一个CIC抽取滤波器类,包含积分和梳状两个主要过程。
python
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import freqz
class CICDecimator:
"""
级联积分梳状抽取滤波器 (CIC Decimator)
参数:
num_stages (int): 级联级数 N
decimation_factor (int): 抽取因子 R
differential_delay (int): 差分延迟 M (通常为1或2)
"""
def __init__(self, num_stages, decimation_factor, differential_delay=1):
self.N = num_stages
self.R = decimation_factor
self.M = differential_delay
# 积分器状态 (每个积分级保存上一个输出)
self.integrator_state = [0] * self.N
# 梳状部分延迟线: 每个梳状级需要保存 M 个历史样本
self.comb_delay_line = np.zeros((self.N, self.M))
def reset(self):
"""重置滤波器状态"""
self.integrator_state = [0] * self.N
self.comb_delay_line = np.zeros((self.N, self.M))
def process(self, x):
"""
处理输入信号 (抽取模式: 积分在前, 抽取在中, 梳状在后)
参数:
x: 输入信号 (numpy数组)
返回:
y: 抽取后的输出信号
"""
# 积分器 (高采样率)
integrator_out = np.zeros(len(x))
for i, sample in enumerate(x):
self.integrator_state[0] += sample
current = self.integrator_state[0]
for stage in range(1, self.N):
self.integrator_state[stage] += current
current = self.integrator_state[stage]
integrator_out[i] = current
# 抽取
decimated = integrator_out[::self.R]
# 梳状滤波 (低采样率)
y = np.zeros(len(decimated))
for i, sample in enumerate(decimated):
current = sample
for stage in range(self.N-1, -1, -1):
diff = current - self.comb_delay_line[stage][0]
self.comb_delay_line[stage] = np.roll(self.comb_delay_line[stage], -1)
self.comb_delay_line[stage][-1] = current
current = diff
y[i] = current
return y
3.2 抽取滤波器验证
我们生成一个包含多频分量的测试信号,通过CIC抽取滤波器后观察其频谱。
python
# 参数设置
Fs = 1e6 # 采样率 1MHz
R = 64 # 抽取因子
M = 1 # 差分延迟
N = 5 # 级联级数
# 生成测试信号: 5kHz + 20kHz + 100kHz 的混合
t = np.arange(0, 0.01, 1/Fs)
x = (np.sin(2*np.pi*5000*t) +
0.5*np.sin(2*np.pi*20000*t) +
0.3*np.sin(2*np.pi*100000*t))
# 创建CIC抽取滤波器
cic_dec = CICDecimator(num_stages=N, decimation_factor=R, differential_delay=M)
# 处理信号
y = cic_dec.process(x)
# 绘制时域波形对比
plt.figure(figsize=(12, 8))
plt.subplot(3, 1, 1)
plt.plot(t[:500], x[:500])
plt.title('原始信号 (时域片段)')
plt.xlabel('时间 (s)')
plt.ylabel('幅度')
plt.subplot(3, 1, 2)
t_dec = t[::R][:len(y)]
plt.plot(t_dec[:min(500, len(y))], y[:min(500, len(y))])
plt.title(f'CIC抽取后信号 (R={R}, N={N})')
plt.xlabel('时间 (s)')
plt.ylabel('幅度')
# 频谱分析
plt.subplot(3, 1, 3)
freq = np.fft.fftfreq(len(x), 1/Fs)[:len(x)//2]
X_fft = np.abs(np.fft.fft(x))[:len(x)//2]
X_fft = X_fft / np.max(X_fft)
y_fs_new = Fs / R
freq_y = np.fft.fftfreq(len(y), 1/y_fs_new)[:len(y)//2]
Y_fft = np.abs(np.fft.fft(y))[:len(y)//2]
Y_fft = Y_fft / np.max(Y_fft)
plt.plot(freq/1000, 20*np.log10(X_fft+1e-10), label='原始信号频谱', alpha=0.7)
plt.plot(freq_y/1000, 20*np.log10(Y_fft+1e-10), label='CIC输出频谱', linewidth=2)
plt.title('频谱对比')
plt.xlabel('频率 (kHz)')
plt.ylabel('归一化幅度 (dB)')
plt.legend()
plt.grid(True)
plt.ylim(-100, 5)
plt.xlim(0, 100)
plt.tight_layout()
plt.show()
滤波器效果如下图所示。

3.3 理论响应验证
绘制CIC滤波器的理论频率响应,与实际滤波效果对照。
python
w, h = freqz(np.ones(R*M), 1, worN=2048, fs=Fs)
h_N = h ** N
plt.figure(figsize=(10, 6))
plt.plot(w/1000, 20*np.log10(np.abs(h_N) + 1e-10), label=f'理论响应 (N={N}, R={R})')
plt.axvline(x=Fs/(2*R)/1000, color='r', linestyle='--', label=f'抽取后奈奎斯特频率 ({Fs/(2*R)/1000:.1f} kHz)')
plt.title('CIC抽取滤波器频率响应 (抽取前)')
plt.xlabel('频率 (kHz)')
plt.ylabel('幅度 (dB)')
plt.grid(True)
plt.legend()
plt.ylim(-150, 5)
plt.xlim(0, Fs/2/1000)
plt.show()

从图中可以清晰看到sinc函数的形状,以及在抽取后奈奎斯特频率处的混叠抑制能力。
3.4 内插CIC滤波器实现与验证
对于采样率升高的内插应用,CIC滤波器的结构恰好对称:梳状滤波器在内插之前(低采样率侧),内插器在中间,积分器在内插之后(高采样率侧)。图3展示了N级内插CIC滤波器的结构。
x[m] (低采样率 f_s)
│
▼
┌─────────────┐
│ 梳状 1 │ c1[m] = x[m] - x[m-M]
└─────────────┘
│
▼
┌─────────────┐
│ 梳状 2 │ c2[m] = c1[m] - c1[m-M]
└─────────────┘
│
▼
⋮
│
▼
┌─────────────┐
│ 梳状 N │ cN[m] = c(N-1)[m] - c(N-1)[m-M]
└─────────────┘
│
▼
[ ↑R ] <── 内插器 (每两个样本间插入R-1个零)
│
▼
┌─────────────┐
│ 积分器 1 │ y1[n] = y1[n-1] + u[n]
└─────────────┘
│
▼
┌─────────────┐
│ 积分器 2 │ y2[n] = y2[n-1] + y1[n]
└─────────────┘
│
▼
⋮
│
▼
┌─────────────┐
│ 积分器 N │ yN[n] = yN[n-1] + y(N-1)[n]
└─────────────┘
│
▼
y[n] (高采样率 f_s * R)
图3:N级内插CIC滤波器结构框图
内插CIC的工作原理是:首先在低采样率下对输入信号进行梳状滤波(差分运算),然后通过内插器(插入零值样本)将采样率提高R倍,最后在高采样率下通过积分器链(累加器)得到最终输出。这种结构同样只有加法和延迟,非常适合硬件实现。
下面我们实现内插CIC滤波器类,并进行验证。
python
class CICInterpolator:
"""
级联积分梳状内插滤波器 (CIC Interpolator)
参数:
num_stages (int): 级联级数 N
interpolation_factor (int): 内插因子 R
differential_delay (int): 差分延迟 M (通常为1或2)
"""
def __init__(self, num_stages, interpolation_factor, differential_delay=1):
self.N = num_stages
self.R = interpolation_factor
self.M = differential_delay
# 梳状部分状态 (在低采样率下)
self.comb_state = [0] * self.N # 当前输出
self.comb_delay_line = np.zeros((self.N, self.M)) # 历史样本
# 积分器状态 (在高采样率下)
self.integrator_state = [0] * self.N
def reset(self):
"""重置滤波器状态"""
self.comb_state = [0] * self.N
self.comb_delay_line = np.zeros((self.N, self.M))
self.integrator_state = [0] * self.N
def process(self, x):
"""
处理输入信号 (内插模式: 梳状在前, 内插在中, 积分在后)
参数:
x: 输入信号 (低采样率数组)
返回:
y: 内插后的高采样率信号
"""
# 1. 梳状滤波 (低采样率)
comb_out = np.zeros(len(x))
for i, sample in enumerate(x):
current = sample
for stage in range(self.N):
diff = current - self.comb_delay_line[stage][0]
self.comb_delay_line[stage] = np.roll(self.comb_delay_line[stage], -1)
self.comb_delay_line[stage][-1] = current
current = diff
self.comb_state[stage] = current
comb_out[i] = current
# 2. 内插 (零值插入)
# 在每两个梳状输出之间插入 R-1 个零
upsampled = np.zeros(len(comb_out) * self.R)
upsampled[::self.R] = comb_out # 零填充
# 3. 积分滤波 (高采样率)
y = np.zeros(len(upsampled))
for i, sample in enumerate(upsampled):
self.integrator_state[0] += sample
current = self.integrator_state[0]
for stage in range(1, self.N):
self.integrator_state[stage] += current
current = self.integrator_state[stage]
y[i] = current
# 注意: 此处未进行增益归一化,实际应用需缩放
return y
为了验证内插CIC的性能,我们生成一个低频正弦信号,通过内插CIC将采样率提高,并观察其频谱中镜像频率的抑制情况。
python
# 内插参数
Fs_low = 10000 # 低采样率 10 kHz
R = 8 # 内插因子
M = 1 # 差分延迟
N = 5 # 级联级数
F_signal = 1000 # 信号频率 1 kHz
# 生成低采样率信号 (1 kHz正弦波)
t_low = np.arange(0, 0.01, 1/Fs_low) # 10 ms
x_low = np.sin(2*np.pi*F_signal * t_low)
# 创建内插CIC滤波器
cic_int = CICInterpolator(num_stages=N, interpolation_factor=R, differential_delay=M)
# 处理信号
y_high = cic_int.process(x_low)
# 高采样率对应的时间轴
Fs_high = Fs_low * R
t_high = np.arange(0, len(y_high)) / Fs_high
# 绘制时域波形
plt.figure(figsize=(12, 10))
plt.subplot(3, 1, 1)
plt.stem(t_low[:20], x_low[:20], basefmt=" ", linefmt='r-', markerfmt='ro', label='低采样率输入')
plt.title('内插前信号 (低采样率)')
plt.xlabel('时间 (s)')
plt.ylabel('幅度')
plt.legend()
plt.subplot(3, 1, 2)
plt.plot(t_high[:20*R], y_high[:20*R], 'b-o', markersize=3, label='内插后输出')
plt.title(f'内插后信号 (R={R}, N={N})')
plt.xlabel('时间 (s)')
plt.ylabel('幅度')
plt.legend()
# 频谱分析
plt.subplot(3, 1, 3)
# 低采样率信号频谱
freq_low = np.fft.fftfreq(len(x_low), 1/Fs_low)[:len(x_low)//2]
X_low = np.abs(np.fft.fft(x_low))[:len(x_low)//2]
X_low = X_low / np.max(X_low)
# 高采样率信号频谱
freq_high = np.fft.fftfreq(len(y_high), 1/Fs_high)[:len(y_high)//2]
Y_high = np.abs(np.fft.fft(y_high))[:len(y_high)//2]
Y_high = Y_high / np.max(Y_high)
plt.plot(freq_low/1000, 20*np.log10(X_low+1e-10), label='低采样率信号频谱', alpha=0.7)
plt.plot(freq_high/1000, 20*np.log10(Y_high+1e-10), label='内插后信号频谱', linewidth=2)
plt.axvline(x=Fs_low/2/1000, color='r', linestyle='--', label='原始奈奎斯特频率')
plt.title('频谱对比 (镜像抑制)')
plt.xlabel('频率 (kHz)')
plt.ylabel('归一化幅度 (dB)')
plt.legend()
plt.grid(True)
plt.ylim(-100, 5)
plt.xlim(0, Fs_high/2/1000)
plt.tight_layout()
plt.show()

运行上述代码,可以看到内插后的信号时域上变得更平滑(因为插入了更多采样点),频谱中除了原始信号频率外,还在原始采样率的整数倍附近出现了镜像频率,但由于CIC滤波器的sinc响应,这些镜像被有效抑制。验证了内插CIC的镜像抑制能力。
4. 设计要点与工程考虑
4.1 参数选择指南
- 级数N:通常取3~5。N=1时阻带衰减不足;N>5时通带衰减过大且硬件位宽急剧增长。
- 差分延迟M:通常取1或2。M增大虽能改善混叠/镜像抑制,但会进一步压缩通带。
- 抽取/内插因子R:CIC擅长处理大幅率变换(如R>10)。对于小幅变换,FIR可能更经济。
4.2 溢出处理与增益控制
CIC滤波器具有固定的增益 G=(RM)NG = (RM)^NG=(RM)N。在定点实现中,必须为中间结果预留足够的位宽,或在输出级进行缩放。常用的方法是输出右移操作:
yfinal=yCIC2S y_{\text{final}} = \frac{y_{\text{CIC}}}{2^S} yfinal=2SyCIC
其中 S=⌈log2(G)⌉S = \lceil \log_2(G) \rceilS=⌈log2(G)⌉,也可根据信噪比要求适当选择截位位数。
4.3 补偿滤波器设计
若需平坦的通带响应,可在CIC后级联补偿滤波器。补偿滤波器通常为低阶FIR,其系数可通过如下方法生成:
python
from scipy.signal import firwin2
def design_cic_compensator(R, M, N, cutoff, numtaps=100):
"""
设计CIC补偿滤波器
"""
# 补偿响应 = 1 / H_cic(f) 在通带内
freq_points = np.linspace(0, cutoff, 50)
cic_resp = np.sinc(freq_points * M * R) ** N # 近似
comp_resp = 1 / (cic_resp + 1e-10)
# 构造完整的频率响应数组 (包括阻带)
freqs = np.concatenate((freq_points / cutoff * cutoff, [cutoff, 0.5]))
gains = np.concatenate((comp_resp, [comp_resp[-1], 0]))
# 归一化
gains = gains / gains[0]
# 设计FIR
taps = firwin2(numtaps, freqs, gains, fs=1.0)
return taps
结语
CIC滤波器以其无乘法器的简洁结构,在多速率信号处理领域占据着不可替代的地位。本文从数学原理出发,通过Python实现了抽取和内插两种模式的CIC滤波器,并辅以清晰的实现框图和验证示例。在实际工程中,无论是FPGA实现还是ASIC设计,CIC滤波器都是处理大幅采样率转换的首选方案。
希望本文能帮助您理解CIC滤波器的核心思想,并在您的项目中灵活运用。欢迎动手运行代码,调整参数,感受sinc滤波器的魅力。
参考文献 :
1\] Hogenauer, E. B. "An Economical Class of Digital Filters for Decimation and Interpolation." IEEE Transactions on Acoustics, Speech and Signal Processing, 1981. \[2\] Harris, F. J. "Multirate Signal Processing for Communication Systems." Prentice Hall, 2004. *** ** * ** *** **研究学习不易,点赞易。 工作生活不易,收藏易,点收藏不迷茫 :)** *** ** * ** ***