别再傻傻分不清了!用Python+Matplotlib手把手带你画出AM和FM的波形图
Python+Matplotlib实战:AM与FM波形可视化全解析
在通信工程的学习中,调幅(AM)和调频(FM)是两个最基础也最容易混淆的概念。很多初学者面对教材中抽象的数学公式和理论描述时,常常感到一头雾水。本文将带你用Python和Matplotlib这两个强大的工具,通过代码实现和可视化对比,彻底搞懂这两种调制方式的本质区别。
1. 基础准备:搭建Python信号处理环境
在开始绘制AM和FM波形之前,我们需要配置好Python环境并导入必要的库。推荐使用Anaconda作为Python发行版,它已经集成了我们所需的大多数科学计算包。
首先安装核心依赖库:
pip install numpy matplotlib scipy
然后创建一个新的Python脚本文件,导入以下模块:
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
为了确保我们的模拟尽可能接近真实情况,我们需要定义一些基本参数:
# 时间参数
duration = 0.02 # 信号持续时间(秒)
sample_rate = 44100 # 采样率(Hz)
t = np.linspace(0, duration, int(duration * sample_rate), endpoint=False)
# 载波信号参数
carrier_freq = 1000 # 1kHz载波频率
# 调制信号参数
mod_freq = 100 # 100Hz调制频率
mod_index = 0.5 # 调制指数
2. 调幅(AM)波形生成与可视化
调幅是通过改变载波信号的幅度来传递信息的技术。让我们从生成基本的AM信号开始。
2.1 AM信号数学原理
AM信号的数学表达式为:
s(t) = [1 + m(t)] * c(t)
其中:
- m(t)是调制信号
- c(t)是载波信号
- 1是为了保证调制后的信号不会出现负值
2.2 Python实现AM调制
# 生成载波信号
carrier = np.sin(2 * np.pi * carrier_freq * t)
# 生成调制信号(正弦波)
modulating = np.sin(2 * np.pi * mod_freq * t)
# 生成AM信号
am_wave = (1 + mod_index * modulating) * carrier
2.3 可视化AM波形
plt.figure(figsize=(12, 8))
# 绘制调制信号
plt.subplot(3, 1, 1)
plt.plot(t, modulating)
plt.title('调制信号(100Hz)')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
# 绘制载波信号
plt.subplot(3, 1, 2)
plt.plot(t, carrier)
plt.title('载波信号(1kHz)')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
# 绘制AM信号
plt.subplot(3, 1, 3)
plt.plot(t, am_wave)
plt.title('调幅(AM)信号')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
plt.tight_layout()
plt.show()
运行这段代码,你将看到三个子图分别展示了调制信号、载波信号和最终的AM信号。注意观察AM信号的"包络"形状如何跟随调制信号变化。
2.4 AM信号频谱分析
为了更全面地理解AM信号,我们还需要分析它的频谱特性:
def plot_spectrum(signal, sample_rate, title):
n = len(signal)
freq = np.fft.rfftfreq(n, d=1/sample_rate)
spectrum = np.abs(np.fft.rfft(signal)/n)
plt.figure(figsize=(10, 4))
plt.plot(freq, spectrum)
plt.title(title)
plt.xlabel('频率(Hz)')
plt.ylabel('幅度')
plt.grid()
plt.show()
plot_spectrum(am_wave, sample_rate, 'AM信号频谱')
在频谱图中,你会看到三个明显的峰:中心频率(载波频率)和两个边带频率(载波频率±调制频率)。
3. 调频(FM)波形生成与可视化
调频是通过改变载波信号的频率来传递信息的技术。与AM不同,FM信号的幅度保持不变。
3.1 FM信号数学原理
FM信号的数学表达式为:
s(t) = sin(2πf_c t + 2πk_f ∫m(τ)dτ)
其中:
- f_c是载波频率
- k_f是频率偏差常数
- m(t)是调制信号
3.2 Python实现FM调制
# 计算频率调制后的相位
phase = 2 * np.pi * carrier_freq * t + 2 * np.pi * mod_index * np.cumsum(modulating) / sample_rate
# 生成FM信号
fm_wave = np.sin(phase)
3.3 可视化FM波形
plt.figure(figsize=(12, 8))
# 绘制调制信号
plt.subplot(3, 1, 1)
plt.plot(t, modulating)
plt.title('调制信号(100Hz)')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
# 绘制载波信号
plt.subplot(3, 1, 2)
plt.plot(t, carrier)
plt.title('载波信号(1kHz)')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
# 绘制FM信号
plt.subplot(3, 1, 3)
plt.plot(t, fm_wave)
plt.title('调频(FM)信号')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
plt.tight_layout()
plt.show()
观察FM信号,你会发现它的幅度保持不变,但波形的"疏密"程度随着调制信号变化而变化。
3.4 FM信号频谱分析
plot_spectrum(fm_wave, sample_rate, 'FM信号频谱')
FM信号的频谱比AM复杂得多,你会看到多个边带频率,这是FM信号的一个重要特征。
4. AM与FM波形对比分析
现在我们已经分别生成了AM和FM信号,让我们将它们放在一起比较,更直观地理解它们的区别。
4.1 时域波形对比
plt.figure(figsize=(12, 6))
plt.subplot(2, 1, 1)
plt.plot(t, am_wave)
plt.title('AM信号')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
plt.subplot(2, 1, 2)
plt.plot(t, fm_wave)
plt.title('FM信号')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
plt.tight_layout()
plt.show()
关键区别点:
- AM信号 :幅度变化明显,频率固定
- FM信号 :幅度固定,频率变化明显(通过波形疏密体现)
4.2 频域特性对比
plt.figure(figsize=(12, 6))
# AM频谱
plt.subplot(2, 1, 1)
freq = np.fft.rfftfreq(len(am_wave), d=1/sample_rate)
spectrum = np.abs(np.fft.rfft(am_wave)/len(am_wave))
plt.plot(freq, spectrum)
plt.title('AM信号频谱')
plt.xlabel('频率(Hz)')
plt.ylabel('幅度')
# FM频谱
plt.subplot(2, 1, 2)
freq = np.fft.rfftfreq(len(fm_wave), d=1/sample_rate)
spectrum = np.abs(np.fft.rfft(fm_wave)/len(fm_wave))
plt.plot(freq, spectrum)
plt.title('FM信号频谱')
plt.xlabel('频率(Hz)')
plt.ylabel('幅度')
plt.tight_layout()
plt.show()
频谱对比要点:
- AM频谱 :简单的载波频率和两个边带
- FM频谱 :复杂的多边带结构,带宽更宽
4.3 抗干扰能力对比
为了展示AM和FM在抗干扰能力上的差异,我们可以给信号添加一些噪声:
# 添加高斯白噪声
noise_level = 0.3
am_noisy = am_wave + noise_level * np.random.normal(size=len(am_wave))
fm_noisy = fm_wave + noise_level * np.random.normal(size=len(fm_wave))
# 绘制带噪声的信号
plt.figure(figsize=(12, 6))
plt.subplot(2, 1, 1)
plt.plot(t, am_noisy)
plt.title('带噪声的AM信号')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
plt.subplot(2, 1, 2)
plt.plot(t, fm_noisy)
plt.title('带噪声的FM信号')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
plt.tight_layout()
plt.show()
从图中可以明显看出,FM信号在噪声环境下更能保持其信息完整性,而AM信号的质量下降更为明显。
5. 进阶应用:实际场景中的调制技术
理解了基本原理后,我们可以探索一些更接近实际应用的调制技术。
5.1 语音信号调制
让我们尝试用实际的语音信号而不是简单的正弦波作为调制信号。首先,我们需要录制或加载一个语音文件:
from scipy.io import wavfile
# 读取语音文件(假设有一个test.wav文件)
sample_rate_voice, voice_data = wavfile.read('test.wav')
voice_data = voice_data / np.max(np.abs(voice_data)) # 归一化
# 截取一小段语音
voice_segment = voice_data[:int(0.02 * sample_rate_voice)]
# 重新采样以匹配我们的时间轴
from scipy import interpolate
f = interpolate.interp1d(np.linspace(0, duration, len(voice_segment)), voice_segment, kind='linear')
voice_resampled = f(t)
现在��们可以用这个语音信号作为调制信号:
# AM调制语音
am_voice = (1 + 0.5 * voice_resampled) * carrier
# FM调制语音
phase_voice = 2 * np.pi * carrier_freq * t + 2 * np.pi * 5 * np.cumsum(voice_resampled) / sample_rate
fm_voice = np.sin(phase_voice)
# 绘制结果
plt.figure(figsize=(12, 6))
plt.subplot(2, 1, 1)
plt.plot(t, am_voice)
plt.title('语音AM调制')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
plt.subplot(2, 1, 2)
plt.plot(t, fm_voice)
plt.title('语音FM调制')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
plt.tight_layout()
plt.show()
5.2 数字信号调制
我们还可以尝试用数字信号(如方波)作为调制信号:
# 生成数字调制信号
digital_signal = signal.square(2 * np.pi * mod_freq * t)
# AM数字调制
am_digital = (1 + 0.5 * digital_signal) * carrier
# FM数字调制
phase_digital = 2 * np.pi * carrier_freq * t + 2 * np.pi * 5 * np.cumsum(digital_signal) / sample_rate
fm_digital = np.sin(phase_digital)
# 绘制结果
plt.figure(figsize=(12, 6))
plt.subplot(2, 1, 1)
plt.plot(t, am_digital)
plt.title('数字信号AM调制')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
plt.subplot(2, 1, 2)
plt.plot(t, fm_digital)
plt.title('数字信号FM调制')
plt.xlabel('时间(s)')
plt.ylabel('幅度')
plt.tight_layout()
plt.show()
6. 性能优化与实用技巧
在实际应用中,我们还需要考虑一些性能优化和实用技巧。
6.1 计算效率优化
对于长时间的信号模拟,直接计算可能会很慢。我们可以使用一些优化技巧:
# 使用预计算相位提高FM计算效率
def efficient_fm(mod_signal, carrier_freq, mod_index, sample_rate):
phase = 2 * np.pi * carrier_freq * np.arange(len(mod_signal)) / sample_rate
phase += 2 * np.pi * mod_index * np.cumsum(mod_signal) / sample_rate
return np.sin(phase)
6.2 交互式可视化
使用Matplotlib的交互式功能可以更好地探索信号特性:
from matplotlib.widgets import Slider
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
plt.subplots_adjust(bottom=0.25)
# 初始信号
l1, = ax1.plot(t, am_wave)
l2, = ax2.plot(t, fm_wave)
ax1.set_title('AM信号')
ax2.set_title('FM信号')
# 添加滑动条
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03])
freq_slider = Slider(
ax=axfreq,
label='调制频率(Hz)',
valmin=10,
valmax=500,
valinit=mod_freq,
)
def update(val):
current_freq = freq_slider.val
new_mod = np.sin(2 * np.pi * current_freq * t)
# 更新AM信号
new_am = (1 + mod_index * new_mod) * carrier
l1.set_ydata(new_am)
# 更新FM信号
new_phase = 2 * np.pi * carrier_freq * t + 2 * np.pi * mod_index * np.cumsum(new_mod) / sample_rate
new_fm = np.sin(new_phase)
l2.set_ydata(new_fm)
fig.canvas.draw_idle()
freq_slider.on_changed(update)
plt.show()
这个交互式界面允许你实时调整调制频率,观察AM和FM信号如何变化。
更多推荐


所有评论(0)