别再死记硬背了!用Python+NumPy可视化常数1的傅里叶变换(附代码)
·
用Python动态演示:为什么常数1的傅里叶变换会生成2πδ(ω)
在信号处理领域,常数1的傅里叶变换结果2πδ(ω)这个结论常常让初学者感到困惑。与其死记硬背公式,不如让我们用Python代码构建一个动态可视化实验,亲眼见证这个神奇变换的诞生过程。本文将带你用NumPy和Matplotlib,从有限长矩形信号出发,逐步逼近常数信号,观察其频谱如何演化成冲激函数。
1. 理解问题本质:从矩形信号开始
要理解常数1的傅里叶变换,最直观的方法是观察矩形信号在时域宽度不断增大时,其频谱的变化趋势。我们从一个宽度为2T、高度为1的矩形脉冲开始:
import numpy as np
import matplotlib.pyplot as plt
def rect(t, T):
"""生成矩形信号"""
return np.where(np.abs(t) <= T, 1, 0)
t = np.linspace(-10, 10, 1000)
plt.plot(t, rect(t, 1), label='T=1')
plt.plot(t, rect(t, 2), label='T=2')
plt.plot(t, rect(t, 5), label='T=5')
plt.legend()
plt.title('不同宽度的矩形信号')
plt.show()
随着T增大,矩形信号越来越接近常数1。那么它的傅里叶变换会发生什么变化呢?
2. 计算矩形信号的频谱
矩形信号的傅里叶变换是著名的sinc函数:
def sinc_spectrum(omega, T):
"""计算矩形信号的频谱"""
return 2 * np.sin(omega * T) / omega
omega = np.linspace(-20, 20, 1000)
plt.plot(omega, sinc_spectrum(omega, 1), label='T=1')
plt.plot(omega, sinc_spectrum(omega, 2), label='T=2')
plt.plot(omega, sinc_spectrum(omega, 5), label='T=5')
plt.legend()
plt.title('不同宽度矩形信号的频谱')
plt.show()
观察这个频谱图,我们会发现三个关键现象:
- 主瓣高度 :随着T增大,主瓣高度线性增长(2T)
- 主瓣宽度 :随着T增大,主瓣宽度变窄(与1/T成正比)
- 能量守恒 :曲线下的总面积保持不变
3. 逼近极限:当T趋近于无穷大
让我们用动画展示T从1增长到50时的频谱变化:
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
T_values = np.linspace(1, 50, 100)
line, = ax.plot(omega, sinc_spectrum(omega, 1))
def update(T):
y = sinc_spectrum(omega, T)
line.set_ydata(y)
ax.set_title(f'矩形信号频谱 (T={T:.1f})')
ax.set_ylim(0, 100)
return line,
ani = FuncAnimation(fig, update, frames=T_values, blit=True)
plt.show()
从动画中可以清晰看到:
- 主瓣高度不断增长(2T)
- 主瓣宽度不断变窄
- 旁瓣相对主瓣的比例越来越小
这正是冲激函数δ(ω)的形成过程!
4. 解释2π因子的来源
为什么最终结果会有2π因子?关键在于傅里叶变换对的定义:
正变换:X(ω) = ∫x(t)e^(-jωt)dt
反变换:x(t) = 1/(2π)∫X(ω)e^(jωt)dω
为了保证能量守恒,当我们在时域有常数1时,频域需要满足:
1 = 1/(2π)∫X(ω)e^(jωt)dω
这只有在X(ω)=2πδ(ω)时才能成立。让我们用代码验证这一点:
def delta_approximation(omega, T):
"""冲激函数的数值近似"""
return T/np.pi * np.sinc(omega * T/np.pi)
plt.plot(omega, delta_approximation(omega, 10), label='T=10')
plt.plot(omega, delta_approximation(omega, 20), label='T=20')
plt.plot(omega, 2*np.pi*delta_approximation(omega, 20), '--', label='2πδ(ω)')
plt.legend()
plt.title('冲激函数近似与2π因子')
plt.show()
5. 完整实验代码与参数调整
以下是完整的实验代码,你可以调整参数观察不同效果:
# 完整实验设置
def full_experiment(T_max=50, steps=100):
t = np.linspace(-10, 10, 1000)
omega = np.linspace(-20, 20, 1000)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
for T in np.linspace(1, T_max, steps):
ax1.clear()
ax2.clear()
# 时域信号
ax1.plot(t, rect(t, T))
ax1.set_title(f'时域信号 (T={T:.1f})')
ax1.set_xlim(-10, 10)
# 频域频谱
spectrum = sinc_spectrum(omega, T)
ax2.plot(omega, spectrum)
ax2.set_title('频域频谱')
ax2.set_ylim(0, 2*T_max)
plt.pause(0.05)
plt.show()
# 运行实验(可以调整T_max和steps)
full_experiment(T_max=30, steps=50)
关键参数调整建议 :
| 参数 | 推荐值 | 效果说明 |
|---|---|---|
| T_max | 30-100 | 控制矩形信号的最大宽度 |
| omega范围 | -20到20 | 确保包含主瓣和多个旁瓣 |
| 采样点数 | 1000+ | 保证曲线光滑 |
6. 实际应用中的注意事项
在实际工程中处理类似问题时,有几个实用技巧:
-
数值稳定性 :
- 避免ω=0处的除零错误
def safe_sinc_spectrum(omega, T): with np.errstate(divide='ignore', invalid='ignore'): y = 2 * np.sin(omega * T) / omega y[omega == 0] = 2 * T # 利用极限值 return y -
可视化优化 :
- 对数坐标能更好展示旁瓣
plt.yscale('log') -
性能优化 :
- 对大T值,可以增加ω的范围
omega = np.linspace(-2*T, 2*T, 2000)
7. 扩展思考:傅里叶变换的对称性
这个实验也验证了傅里叶变换的一个重要性质——对称性。我们观察到:
- 时域的扩展导致频域的压缩
- 时域的极限(常数1)对应频域的冲激
- 2π因子保证了变换对的能量守恒
这解释了为什么δ(t) ↔ 1和1 ↔ 2πδ(ω)形成了完美的对称关系。
更多推荐


所有评论(0)