☰
傅里叶变换、频谱分析、调制解调:用Python主线串联信号与系统
2026/10/17 12:00:55 网站建设 项目流程

很多学过《信号与系统》的人都有一种共同的体验:上课好像能听懂,一到做题就发懵,期末复习越看越乱。傅里叶变换、频谱分析、调制解调,翻开教材是三个独立章节,各有各的公式,各有各的题型,但很少有人告诉你,它们其实是一条主线上的不同环节。

这条主线就是“变换域思维”。傅里叶变换给了你一个从频域观察信号的视角,频谱分析是你在这个视角下读信号的方式,而调制解调则是把这个视角用到真实通信系统中的经典案例。它们不是三个考点,而是一套完整的技术逻辑。

这篇文章会用“主线串联 + Python 验证”的方式,把信号与系统里最容易让人混乱的几块内容讲清楚。你不需要先精通数学推导,只要跟着文章把概念骨架搭起来,再用 Python 跑一遍仿真,很多公式瞬间就活了。如果你是正在准备期末考试、考研复习,或者工作后需要回炉信号处理基础,这篇文章值得收藏后慢慢看完。

1. 这篇文章真正要解决的问题

先说一个观察:很多同学学信号与系统,卡住不是因为不努力,而是因为努力的方向错了。

教材的编排通常是先讲连续时间信号与系统,再讲傅里叶变换,然后讲拉普拉斯变换、Z 变换、系统函数。每章都很严谨,但问题是,当你学到第三章的时候,可能已经忘了第一章的卷积和系统分析有什么关系。章节之间缺少一个“为什么先学这个、再学那个”的导航图,于是学习变成了一堆孤立公式的背诵。

还有一个现实问题:这门课的数学门槛不低。傅里叶变换是积分变换,卷积是积分运算,系统响应要解微分方程。如果数学基础一般,很容易陷入“每个符号都认识,连起来不知道在说什么”的状态。更麻烦的是,概念高度抽象,时域波形、频域谱线、系统频率响应这些东西,光靠纸笔很难建立直觉。

本文要解决的,就是这三件事:

  • 把傅里叶变换、频谱分析、调制解调串成一条完整主线,让你知道它们之间到底是什么关系。
  • 用 Python 仿真把抽象概念变成能看到的波形和频谱,建立直觉之后再回来看公式。
  • 从“速成”角度给出复习路径:先抓主线、再补细节、最后用实验验证,而不是一上来死磕推导。

这门课不是背出来的,是“看”出来和“跑”出来的。下面我们从头开始。

2. 从时域到频域:傅里叶变换到底做了什么

2.1 时域看信号:你熟悉的“波形视角”

我们最开始接触信号,都是从示波器开始的。横轴是时间,纵轴是电压或幅度,看到的是一条随时间变化的曲线,比如一个正弦波、一个脉冲、一段语音。这个视角叫时域视角,它直观地告诉你:在某个时刻,信号的值是多少。

时域视角的问题在于,它把所有频率成分混在一起。一个由 5Hz 和 40Hz 正弦波叠加而成的信号,在时域里就是一条看起来有点复杂的波形曲线,你很难一眼看出它“包含哪两种频率”。这就像一杯混合果汁,你只看到颜色,看不出配方。

2.2 频域看信号:频谱仪的“配方视角”

频域视角换了一个维度:横轴是频率,纵轴是幅度。它回答的问题是:这个信号由哪些频率成分组成,每个频率成分的强度是多少。同一个混合信号,在频域里会变成两条清晰的谱线,一条在 5Hz,一条在 40Hz,一目了然。

把信号从时域变到频域的数学工具,就是傅里叶变换。用人话说,傅里叶变换做的事情是“把信号拆解成一系列不同频率的正弦波之和”。任何一个满足条件的周期信号,都可以被分解为基波和一系列谐波;非周期信号则可以理解为无穷多个频率连续分布的正弦波的叠加。

2.3 傅里叶变换的公式与“人话解释”

连续时间傅里叶变换的公式长这样:

$$X(j\omega) = \int_{-\infty}^{+\infty} x(t) e^{-j\omega t} dt$$

如果你现在被这个公式吓到了,先别跑。它的含义其实很朴素:把信号 $x(t)$ 分别与不同频率的复指数信号 $e^{-j\omega t}$ 做“相似度比较”,看它的能量在哪个频率上更集中。

$X(j\omega)$ 就是信号在频率 $\omega$ 处的“含量”。把所有频率的“含量”画出来,就得到频谱。

对比维度时域频域
横轴时间频率
观察内容信号随时间如何变化信号包含哪些频率成分
常见工具示波器频谱分析仪
典型问题波形是否失真、延迟多久带宽多大、有哪些干扰频率

这个表是这门课最重要的思维切换工具。后面讲到的频谱分析、调制解调,本质上都是在“时域—频域”之间来回切换。

3. 频谱与频谱分析:读懂一张频谱图

3.1 频谱图怎么读

频谱图是傅里叶变换最直观的产物。横轴是频率,纵轴是幅度。一个单一频率的正弦波,在频谱图上只有一根谱线;一个方波,在频谱图上会出现基波和一系列衰减的奇次谐波;一段语音,在频谱图上是一块连续的宽带谱。

很多初学者第一次看到频谱图,最大的困惑是“这个图到底能干嘛”。最简单的理解方式:频谱图告诉你的不是信号“长什么样”,而是信号“由什么构成”。就像一道菜的卖相是时域,配料表是频域。

读频谱图时有几个关键点:

  • 谱线位置决定频率成分。
  • 谱线高度决定该频率成分的强弱。
  • 谱线宽度和形状往往反映了信号的稳定性。
  • 连续谱范围决定了信号占用的带宽。

3.2 频谱分析的应用场景

理解了频谱图,你就会发现它几乎无处不在。

在设备故障诊断中,轴承磨损、齿轮断齿会产生特征频率,通过频谱分析可以在设备还没有完全损坏之前发现异常。在音频处理中,均衡器就是对频谱的频段进行增益调整,降噪算法则是在频谱域把噪声频段衰减掉。在通信系统设计中,调制信号占多大带宽、滤波器该设计成什么截止频率,都需要先做频谱分析。

频谱分析是傅里叶变换最直接、最成熟的应用之一。它的核心逻辑可以概括为:先把时域信号变换到频域,在频域里观察、处理,再根据需要变换回时域。

3.3 采样率与奈奎斯特定理

在做数字频谱分析之前,必须提一个前置条件:采样。

计算机只能处理离散点,所以模拟信号要先经过采样变成离散序列,才能用 FFT 分析。而采样率的选择有一个底线,就是奈奎斯特定理:采样率必须大于信号最高频率的两倍,否则高频成分会“混叠”到低频区域,频谱图就是错的。

这篇文章后面的 Python 示例里,采样率设为 500Hz,信号最高频率只有 50Hz 左右,远大于两倍关系,所以结果可以放心观察。实际项目中,这个关系永远是排查频谱图是否可信的第一道检查线。

4. 调制与解调:为什么信息要“搬个家”

4.1 为什么不直接把信号发出去

如果有一段语音信号,频率大概在 300Hz 到 3400Hz,能不能直接通过天线发射出去?理论上可以,但工程上问题很大。

首先是天线尺寸问题。天线的有效长度通常需要和波长可比拟,而 1kHz 信号的波长是 300 公里,不可能造出那么大的天线。把信号搬到高频载波上之后,波长缩短到米级甚至厘米级,天线才变得可制造。

其次是多路复用问题。多个人同时打电话,如果都用基带信号直接发射,接收端根本无法区分谁是谁。把不同用户的信号调制到不同的载波频率上,大家挤在同一条信道上互不干扰,接收端只要调到对应频率就能解出自己的信号。

最后还有信道匹配和抗干扰因素。某些信道对特定频段衰减较小,把信号搬到合适频段能获得更好的传输质量。这就是“调制”存在的根本原因:让信息在频域上搬个家,适应信道和系统需求。

4.2 AM 调制的数学表达

幅度调制(AM)是最直观的一种调制方式。设调制信号为 $m(t)$,载波为 $c(t) = \cos(2\pi f_c t)$,AM 已调信号可以写成:

$$s(t) = [1 + m_a m(t)] \cdot \cos(2\pi f_c t)$$

其中 $m_a$ 是调幅指数,控制调制深度。当 $m_a$ 小于 1 时,包络形状和 $m(t)$ 保持一致,接收机可以用最简单的包络检波恢复信号;当 $m_a$ 大于 1 时,包络会过调,解调就会失真。

从频域视角看,AM 调制的作用是把基带信号的频谱从低频“搬”到载波频率 $f_c$ 附近,形成上边带和下边带两条谐波。这个“频谱搬移”的效果,正是傅里叶变换的频移性质在通信系统里的直接体现。

4.3 解调的两种常见方式

解调是调制的逆过程,目标是恢复原始信号。

包络检波是最直观的方法:先用二极管之类的非线性器件对已调信号取绝对值,相当于全波整流,再用低通滤波器滤掉高频载波分量,剩下的就是原信号的包络。它的优点是简单、不需要载波同步,缺点是只能用于幅度调制,且容易受噪声影响。

相干解调则是让接收端产生一个和发射端同频同相的本地载波,把已调信号乘以本地载波,再用低通滤波器取出低频分量。这种方法性能更好,但代价是必须有载波同步电路。

4.4 傅里叶变换在调制中的角色

调制和解调整个过程,用傅里叶变换来看会格外清晰。

时域里 $s(t) = m(t) \cdot \cos(2\pi f_c t)$,而频域里“时域相乘”对应“频域卷积”。载波 $\cos(2\pi f_c t)$ 的傅里叶变换是在 $\pm f_c$ 处的两个冲激,所以 $m(t)$ 的频谱被复制到 $\pm f_c$ 附近。这一下就理解了为什么调制是“频谱搬移”。

同理,解调时再次乘以本地载波,频谱又一次搬移,把原始低频分量搬回基带,再用低通滤波器清除高频部分。整个通信链路,本质上就是傅里叶变换性质的一次次应用。

5. 环境准备:用 Python 做信号实验

理论说完了,下面进入动手环节。我们要用 Python 把傅里叶变换、频谱分析、AM 调制解调全部跑一遍。

建议使用 Python 3 的现代稳定版本,配合 NumPy、SciPy、Matplotlib 三个库。NumPy 负责数值计算和 FFT,SciPy 负责滤波,Matplotlib 负责画图。

安装命令:

pip install numpy matplotlib scipy

如果网络环境使用镜像源更顺畅,可以换用清华或阿里云镜像,这是常规操作,不再展开。

安装完成后,打开 Python 交互环境,运行下面的导入语句验证环境:

import numpy as np import matplotlib.pyplot as plt from scipy.signal import butter, filtfilt print("numpy version:", np.__version__)

能正确输出版本号,环境就没有问题了。下面开始第一个实验。

6. 傅里叶变换与频谱分析的 Python 实现

6.1 生成信号并做 FFT

我们用 Python 生成一段仿真信号,包含 5Hz 和 40Hz 两个正弦波,然后用 FFT 看它的频谱。

import numpy as np import matplotlib.pyplot as plt fs = 500 # 采样率 500Hz t = np.arange(0, 1, 1/fs) # 时长 1 秒 # 构造一个由 5Hz 和 40Hz 组成的信号 x = np.sin(2 * np.pi * 5 * t) + 0.5 * np.sin(2 * np.pi * 40 * t) N = len(x) # 采样点数 X = np.fft.fft(x) # 离散傅里叶变换 freqs = np.fft.fftfreq(N, 1/fs) # 频率轴 # 只取正频率部分,并换成单边谱幅度 half = N // 2 mag = np.abs(X[:half]) * 2 / N freq_axis = freqs[:half] # 画图 plt.figure(figsize=(10, 4)) plt.subplot(1, 2, 1) plt.plot(t, x) plt.title("时域波形") plt.xlabel("时间/s") plt.ylabel("幅度") plt.subplot(1, 2, 2) plt.stem(freq_axis, mag) plt.title("幅度谱(FFT)") plt.xlabel("频率/Hz") plt.ylabel("幅度") plt.tight_layout() plt.show()

这段代码做了几件关键的事:

  • np.fft.fft(x)计算离散傅里叶变换,结果是复数数组。
  • np.fft.fftfreq(N, 1/fs)生成与 FFT 结果对应的频率坐标。
  • np.abs(X[:half]) * 2 / N把复数幅度转成单边谱幅度。因为 FFT 结果关于 N/2 对称,所以取前一半并乘以 2,再除以 N 做归一化。

运行后,时域里是一条混合波形,频域里你能清晰地看到两根谱线,分别位于 5Hz 和 40Hz,幅度分别是 1 和 0.5,正好对应构造信号时设置的参数。这就验证了 FFT 频谱分析的正确性。

6.2 频谱泄漏:一个特别容易踩的坑

如果你把信号频率改成 5.3Hz,重新做 FFT,会发现谱峰变得很宽,幅度也变小了。这个现象叫频谱泄漏。

原因是 FFT 默认把信号当作周期性延拓来处理,当信号频率不是频率分辨率 $\Delta f = fs / N$ 的整数倍时,截断边界不连续,能量就会泄漏到相邻多个频点上。

解决办法有两个:一是增加采样点数 N,提高频率分辨率,让频率更接近整数倍;二是给信号加窗函数,比如汉宁窗,牺牲一点主瓣宽度来压低泄漏。

f = 5.3 x2 = np.sin(2 * np.pi * f * t) # 加汉宁窗 win = np.hanning(N) x2_win = x2 * win # 对比加窗前后的频谱 X2 = np.fft.fft(x2) X2_win = np.fft.fft(x2_win) mag2 = np.abs(X2[:half]) * 2 / N mag2_win = np.abs(X2_win[:half]) * 2 / N fig, axes = plt.subplots(1, 2, figsize=(10, 3), sharey=True) axes[0].stem(freq_axis, mag2, basefmt=" ") axes[0].set_title("未加窗:频谱泄漏明显") axes[1].stem(freq_axis, mag2_win, basefmt=" ") axes[1].set_title("加汉宁窗:泄漏降低") plt.tight_layout() plt.show()

这个实验做完,你就同时掌握了两件事:怎么用 FFT 做频谱分析,以及面对非整数频率时如何用窗函数改善结果。这两点在真实项目和课程作业里都非常常见。

7. AM 调制与解调的 Python 完整示例

7.1 生成 AM 信号

现在把傅里叶变换的知识用起来,完整走一遍 AM 调制、观察频谱、包络检波解调的流程。

先设置参数并生成信号:

fs = 500 t = np.arange(0, 1, 1/fs) fm = 5 # 调制信号频率 5Hz fc = 50 # 载波频率 50Hz ma = 0.5 # 调幅指数 # 调制信号 m = np.cos(2 * np.pi * fm * t) # 载波 c = np.cos(2 * np.pi * fc * t) # AM 已调信号 s = (1 + ma * m) * c

这段代码对应前面讲的 AM 公式。调制信号是 5Hz 余弦波,载波是 50Hz,调幅指数 0.5,包络不会过调。

7.2 观察调制后的频谱

定义一个画频谱的小函数,方便复用:

def plot_spectrum(signal, fs, title): N = len(signal) X = np.fft.fft(signal) freqs = np.fft.fftfreq(N, 1/fs) half = N // 2 plt.plot(freqs[:half], np.abs(X[:half]) * 2 / N) plt.title(title) plt.xlabel("频率/Hz") plt.ylabel("幅度") plt.grid(True) plt.figure(figsize=(8, 3)) plot_spectrum(s, fs, "AM 已调信号频谱") plt.tight_layout() plt.show()

运行后,频谱图上最明显的是三组谱线:

  • 50Hz 处是载波分量。
  • 45Hz 和 55Hz 分别是下边带和上边带分量。

基带 5Hz 的信号原本在低频处,经过调制后被搬移到了载波附近。这就是频谱搬移的直观体现。你会清楚地看到,调制并没有改变信息本身,只是改变了它在频域里的位置。

7.3 包络检波恢复信号

下面用包络检波恢复原始信号。思路是先取绝对值,再做低通滤波:

from scipy.signal import butter, filtfilt # 1. 全波整流:取绝对值 env = np.abs(s) # 2. 低通滤波,滤掉高频载波 b, a = butter(4, 10 / (fs/2), btype='low') # 截止频率 10Hz low = filtfilt(b, a, env) # 3. 去掉直流偏置,归一化便于和原始信号对比 m_est = low - np.mean(low) m_est = m_est / np.max(np.abs(m_est)) # 画图对比 plt.figure(figsize=(12, 6)) plt.subplot(3, 1, 1) plt.plot(t, m) plt.title("原始调制信号 m(t)") plt.grid(True) plt.subplot(3, 1, 2) plt.plot(t, s) plt.title("AM 已调信号 s(t)") plt.grid(True) plt.subplot(3, 1, 3) plt.plot(t, m / np.max(np.abs(m)), label="原始信号") plt.plot(t, m_est, '--', label="包络检波恢复信号") plt.title("包络检波结果对比") plt.legend() plt.grid(True) plt.tight_layout() plt.show()

关键逻辑解释:

  • np.abs(s)等价于全波整流,输出一个带有基带包络和大量高频分量的信号。
  • butter设计一个 4 阶巴特沃斯低通滤波器,截止频率 10Hz。因为调制信号只有 5Hz,10Hz 的截止频率足够保留它,同时滤掉 50Hz 载波及整流产生的高次谐波。
  • filtfilt是零相位滤波,避免波形产生相位偏移,非常适合观察波形形状。
  • 低通滤波后信号里含有 AM 波形带来的直流分量,所以要先减均值;由于整流和滤波会改变增益,这里做了一次归一化,让恢复信号和原始信号在同一个尺度上比较。

运行后,第三条子图里两条曲线会几乎重合。这说明包络检波成功恢复了原始调制信号。你可以把调幅指数改成 0.8、1.0 甚至 1.5 再运行一次,观察包络失真什么时候出现。这个实验比做十道计算题更能帮助你理解过调制的危害。

7.4 换成相干解调会怎样

包络检波虽然简单,但它在噪声环境下性能有限。相干解调的思路是让已调信号乘以同频同相本地载波,再低通滤波。

coherent = s * c # 乘以本地载波 b, a = butter(4, 10 / (fs/2), btype='low') m_coherent = filtfilt(b, a, coherent) # 去掉直流偏置 m_coherent = m_coherent - np.mean(m_coherent) # 归一化后对比 m_coherent = m_coherent / np.max(np.abs(m_coherent)) plt.figure(figsize=(8, 3)) plt.plot(t, m / np.max(np.abs(m)), label="原始信号") plt.plot(t, m_coherent, '--', label="相干解调恢复信号") plt.legend() plt.grid(True) plt.show()

它的原理是:$s(t) \cdot \cos(2\pi f_c t)$ 展开后会包含低频项和高频项,低通滤波器只留下低频项,完成解调。代价是接收端需要载波同步,如果本地载波的频率或相位不一致,恢复信号会明显畸变。

8. 常见问题与排查思路

在信号仿真与频谱分析中,很多问题反复出现,这里整理成一张排查表,遇到问题直接对照处理。

问题现象可能原因排查方式解决方案
频谱图中峰值位置不对频率轴计算错误检查fftfreq参数是否写成了fs而不是1/fs使用np.fft.fftfreq(N, 1/fs)
单根正弦的谱峰扩散到多个频点信号频率不是频率分辨率整数倍,发生频谱泄漏计算 $\Delta f = fs / N$,检查信号频率与 $\Delta f$ 的比值增加采样点数 N,或对信号加窗函数
出现低频区域本不该有的频率成分采样率不足,发生混叠检查最高频率是否超过fs/2提高采样率,或先加抗混叠滤波器再采样
解调后波形失真,包络出现负值调幅指数大于 1,发生了过调制绘制(1 + ma * m)的包络,观察是否小于 0将调幅指数调整为 0.5 或 0.8
低通滤波后波形有高频毛刺低通截止频率设置过高打印滤波前后的频谱对比降低截止频率,确保载波频率被滤除
包络检波恢复的波形幅度对不上整流和滤波改变了信号增益对比恢复信号与原始信号的幅度范围先归一化,再按理论增益恢复到原始幅度
FFT 结果幅度和时域幅度不一致忘记做归一化或单边谱处理检查幅度是否除以 N,双边谱是否乘 2单边谱使用np.abs(X[:half]) * 2 / N

这张表不只适用于本文的示例,也适用于你自己处理真实信号时的各种问题。建议截图或收藏,遇到问题先查表。

9. 信号与系统的“速成”复习路径

如果现在距离考试只剩两三周,我强烈建议你不要再按教材顺序从第一章慢慢看了,而是按下面的路径复习。

9.1 抓住唯一的骨架主线

信号与系统的核心主线是“激励—系统—响应”,而串联时域和频域的最重要关系是卷积定理:

$$y(t) = x(t) * h(t) \quad \xrightarrow{\text{傅里叶变换}} \quad Y(j\omega) = X(j\omega) H(j\omega)$$

这句话的意思是:时域里的卷积运算,在频域里变成了简单的乘法。$H(j\omega)$ 是系统的频率响应,它表示系统对每个频率分量是放大还是衰减、是滞后还是超前。

很多同学做滤波题、求系统响应题都会绕远路,根源就是没有理解这个等价关系。拿到一个系统,先问自己在时域做卷积方便,还是在频域做乘法方便。这是信号与系统题目里最重要的“策略问题”。

9.2 先建立直觉,再补数学推导

速成最大的误区,是花大量时间抄写公式推导,结果遇到应用题还是不会。更有效的顺序是:

  • 先用 Python 或仿真工具观察现象,比如用 FFT 看频谱、用滤波看波形变化。
  • 知道现象之后再回来看推导,你会更容易接受每一步数学操作的含义。
  • 最后用纸笔独立做题,把公式变成自己的工具。

傅里叶变换的对称性、时移性质、频移性质、卷积定理,这几个性质建议反复用仿真验证。一旦你看过 AM 调制之后的频谱长什么样,频移性质就永远不会忘。

9.3 用 Python 做三组经典小实验

光看不练没有用,推荐三组能够在半小时内完成的小实验:

  1. 用 FFT 分析方波的频谱,观察基波和奇次谐波的衰减规律。
  2. 改变采样率对同一个信号做 FFT,观察混叠如何发生。
  3. 设计一个低通滤波器,观察不同截止频率下的时域响应和频域响应。

这三组实验覆盖了傅里叶变换、采样定理、系统频率响应这三大核心考点,完成后你对整门课的理解会比刷十套真题更扎实。面试时如果聊到信号处理基础,这些动手经验也是一个非常加分的亮点。

10. 总结

写到最后,总结一下这篇文章真正想传达的东西。信号与系统这门课,难点从来不在公式本身,而在“时域”和“频域”这两个视角之间的频繁切换。傅里叶变换就是那副切换视角的眼镜,频谱分析是戴着眼镜读取信息,调制解调则是利用频域特性完成真实通信任务。

速成不是不学原理,而是先把主线搭起来,再用实验验证,最后回头补细节。如果你把这篇文章里的 Python 示例全部运行一遍,已经比许多只背公式的同学领先一大截。

下一步建议很具体:先拿一个真实声音信号录制几秒,用 FFT 分析它的频谱,再尝试设计一个滤波器把其中某个频段滤掉。这一套流程跑通之后,你就不再是“学过信号与系统”,而是真的“会用它”了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询