1. 实验背景与目的
在数字信号处理中,连续时间信号在进入数字系统之前,必须先经过抽样,转换为离散时间序列。教材"连续时间信号的抽样"中指出:若抽样频率选择不当,则抽样后的频谱会发生周期延拓并产生重叠,从而出现频谱混叠。因此,在实际抽样之前,往往需要配置抗混叠滤波器,以抑制高频分量,减小混叠失真。
本实验正是围绕这一主线展开。通过构造一个由 10 kHz 和 70 kHz 两个正弦信号叠加而成的混频信号,先以较高采样率进行仿真,再做 10:1 抽取,观察抽样后频谱的变化;随后分别加入一阶和二阶 RC 低通滤波器,对比不同滤波条件下高频分量的衰减效果,从而验证教材中关于抽样定理、混叠现象以及实际抽样中抗混叠滤波器作用的相关结论。
2. 理论基础
2.1 连续时间信号的抽样

2.2 抽样的频域效应与频谱周期延拓

2.3 抽样定理与混叠

2.4 70 kHz 为什么会混叠到 30 kHz

2.5 实际抽样中的抗混叠滤波器

3.实验信号与参数设置

RC 系统


4.python库函数
import numpy as np,数值计算库。
import matplotlib.pyplot as plt,画图库。
np.arange()
x1 = np.arange(0, T_SIM, T_SMP)
#从 0 开始,每次加 2,到 10 之前停止
a = np.arange(0, 10, 2)
print(a)
输出:
[0 2 4 6 8]
np.sin()
y1 = np.sin(2 * np.pi * F_SIG * x1)
# 1 Hz 正弦波在几个典型时刻的取值
x = np.array([0, 0.25, 0.5, 0.75, 1.0])
y = np.sin(2*np.pi*x)
print(y)
输出:[0, 1, 0, -1, 0],
x2 = x1[::10],y2 = y1[::10]
a = np.array([0,1,2,3,4,5,6,7,8,9])
print(a[::2])
输出:
[0 2 4 6 8]
plt.subplots(2,2)
fig, ax = plt.subplots(2, 2)
#创建一个 2 行 2 列的子图窗口
ax[0][0] ax[0][1]
ax[1][0] ax[1][1]
你的图就是:
左上:原始时域
左下:原始频域
右上:抽取后时域
右下:抽取后频域
np.fft.fft()
np.fft.fft(y1)
#把时域信号拆成"有哪些频率成分"
np.abs()
np.fft.fft(y1),返回的是复数,不是普通实数。有实部、虚部,所以画频谱时通常取模长:
np.abs(np.fft.fft(y1))
频率轴
freq_x1 = [F_SMP / len(x1) * i for i in range(len(x1))]
#len(x1) 就是数组长度。
#range(1000)表示:0,1,2,3,...,999


5. 三版代码的设计思路
课堂代码一:只有 10 kHz,验证"抽取后不混叠"
import numpy as np # 数值计算库
from matplotlib import pyplot as plt # 画图库
# ========== 参数设置 ==========
F_SMP = 1e6 # 采样频率 1MHz(每秒采100万个点) Sampling Frequency(采样频率)
T_SIM = 1e-3 # 仿真时长 1ms
F_SIG = 1e4 # 信号频率 10kHz
T_SMP = 1/F_SMP # 采样间隔 = 1/采样频率 = 1μs
# ========== 生成原始信号 ==========
x1 = np.arange(0, T_SIM, T_SMP) # 时间轴:0到1ms,步长1μs,共1000个点
y1 = np.sin(2*np.pi*F_SIG*x1) # 生成10kHz正弦波
# ========== 10:1抽取(降采样) ==========
x2 = x1[::10] # 每隔10个点取1个(采样率从1MHz降到100kHz)
y2 = y1[::10] # 对应的信号值
# ========== 画4个子图 ==========
fig, ax = plt.subplots(2, 2) # 创建2×2的子图
# 左上:原始信号时域波形
ax[0][0].plot(x1*1000, y1) # x轴乘1000把秒转成毫秒
# 右上:抽取后信号时域波形
ax[0][1].plot(x2*1000, y2)
# 左下:原始信号频谱(FFT)
ax[1][0].plot(
[F_SMP/len(x1)*i for i in range(len(x1))], # 频率轴
np.abs(np.fft.fft(y1)) # FFT取幅值
)
# 右下:抽取后信号频谱
ax[1][1].plot(
[F_SMP/10/len(x2)*i for i in range(len(x2))],
np.abs(np.fft.fft(y2))
)
#设置 Matplotlib 子图的标题和坐标轴标签
ax[0][0].set_title("Undecimated signal, time")
ax[0][1].set_title("Decimated signal at 10:1, time")
ax[1][0].set_title("Undecimated signal, freq")
ax[1][1].set_title("Decimated signal at 10:1, freq")
ax[0][0].set_xlabel("Time (ms)"); ax[0][0].set_ylabel("Amplitude")
ax[0][1].set_xlabel("Time (ms)"); ax[0][1].set_ylabel("Amplitude")
ax[1][0].set_xlabel("Freq (Hz)"); ax[1][0].set_ylabel("Amplitude")
ax[1][1].set_xlabel("Freq (Hz)"); ax[1][1].set_ylabel("Amplitude")
''Undecimated,"未抽取的"或"未降采样的",指原始、未经抽点的信号。
Decimated,"抽取后的"或"降采样后的",指经过降采样处理的信号。
at 10:1 指抽取比例为 10:1,即每 10 个原始采样点保留 1 个。
time 这里指 Time Domain(时域),显示信号幅度随时间的变化。
freq 是 Frequency 的缩写,指 Frequency Domain(频域),显示信号的频谱成分。''
plt.tight_layout() # 自动调整子图间距
plt.show() # 显示图形窗口
关键代码:
np.arange(0, T_SIM, T_SMP)
/*生成时间轴:
从 0 到 1 ms
步长 1 us
得到 1000 个采样点。
*/
np.sin(2*np.pi*F_SIG*x1)
/*生成 10 kHz 正弦波:*/
x1[::10]
/*Python 切片语法,意思是:
从头到尾,每隔 10 个取 1 个
这就是 10:1 抽取。
*/
抽取后有效采样率变成:100 kHz
奈奎斯特频率:50 kHz,而信号只有 10 kHz,所以满足:10 kHz<50 kHz
因此不会混叠。
课堂代码二:加 70 kHz,观察混叠
第二版核心就是把信号改成了:
y1 = np.sin(2*np.pi*F_SIG1*x1) + np.sin(2*np.pi*F_SIG2*x1)
抽取后新采样率:fs′=100 kHz
奈奎斯特频率:fN=50 kHz
由于:70 kHz>50 kHz,所以发生混叠。


课堂代码三:加入一阶 RC 低通后再抽取



课堂代码四:加入二阶 RC 低通后再抽取



问题:


6.实验结果的整体比较
三版实验可归纳为以下结论:

7.代码:
#!/usr/bin/env python3
import numpy as np
from matplotlib import pyplot as plt
F_SMP = 1e6 # 原始采样率 1MHz
T_SIM = 1e-3 # 仿真时间 1ms
F_SIG1 = 1e4 # 信号1:10kHz
F_SIG2 = 70e3 # 信号2:70kHz (新增!)
T_SMP = 1 / F_SMP
FC= 70e3
x1 = np.arange(0, T_SIM, T_SMP)
# 把 10kHz 和 70kHz 的正弦波叠加在一起
y1 = np.sin(2 * np.pi * F_SIG1 * x1) + np.sin(2 * np.pi * F_SIG2 * x1)
"""
k = 1 - np.exp(-2 * np.pi * FC / F_SMP)
# 第一级
y1_filt = np.zeros_like(y1)
y1_filt[0] = y1[0]
# 第二级
y2_filt = np.zeros_like(y1)
y2_filt[0] = y1_filt[0]
for n in range(1, len(y1)):
y1_filt[n] = y1_filt[n-1] + (y1[n] - y1_filt[n-1]) * k
y2_filt[n] = y2_filt[n-1] + (y1_filt[n] - y2_filt[n-1]) * k
"""
# 抽取
x2 = x1[::10]
#y2 = y2_filt[::10]
y2 = y1[::10]
fig, ax = plt.subplots(2, 2)
# 时域画图
ax[0][0].plot(x1 * 1000, y1)
ax[0][1].plot(x2 * 1000, y2, marker='.', linestyle='-') # 加了点(marker)更清楚看到离散点
# 频域画图 (FFT)
freq_x1 = [F_SMP / len(x1) * i for i in range(len(x1))]
ax[1][0].plot(freq_x1, np.abs(np.fft.fft(y1))/len(y1))
freq_x2 = [F_SMP / 10 / len(x2) * i for i in range(len(x2))]
ax[1][1].plot(freq_x2, np.abs(np.fft.fft(y2))/len(y2))
# 设置标题和坐标轴
ax[0][0].set_title("Undecimated signal, time")
ax[0][1].set_title("Decimated signal at 10:1, time")
ax[1][0].set_title("Undecimated signal, freq")
ax[1][1].set_title("Decimated signal at 10:1, freq")
ax[0][0].set_xlabel("Time (ms)"); ax[0][0].set_ylabel("Amplitude")
ax[0][1].set_xlabel("Time (ms)"); ax[0][1].set_ylabel("Amplitude")
ax[1][0].set_xlabel("Freq (Hz)"); ax[1][0].set_ylabel("Amplitude")
ax[1][1].set_xlabel("Freq (Hz)"); ax[1][1].set_ylabel("Amplitude")
# 限制频域显示的范围,方便观察低频部分(选做,但我推荐加上这行)
#ax[1][0].set_xlim(0, 150000)
#ax[1][1].set_xlim(0, 100000)
plt.tight_layout()
plt.show()