简介这份《信号与系统》实验报告来自广东工业大学信息工程学院面向电子信息类专业学生及需要完成同类课程实验的学习者帮助系统梳理MATLAB环境下的信号分析与系统仿真流程。报告围绕连续与离散时间信号展开涵盖基本信号生成与波形绘制、卷积运算求解LTI系统响应、Z变换与离散卷积、采样量化影响分析以及傅里叶级数、傅里叶变换、拉普拉斯变换和系统频率特性等频域内容并配有实验目的、内容与结果图便于对照理解理论计算与仿真验证的对应关系。资源包为1个docx文档压缩后约1.68MB结构完整、章节清晰可直接作为实验预习、报告撰写与期末复习的参考。目前已有1255人学习下载适合希望提升MATLAB数值计算与信号处理实操能力的学生使用。1. 信号与系统实验报告从波形到频谱一套能跑通的完整链路很多同学做“信号与系统”实验时最头疼的不是理论而是实验报告里那些波形图、频谱图到底怎么来的。卷积、傅里叶变换、采样定理课本上公式推得头头是道一到动手就发现信号怎么生成频谱怎么画采样率设多少才不混叠报告里的图怎么复现这篇笔记就围绕“信号与系统实验报告”这个具体场景把从信号生成、卷积运算、频谱分析到采样重建的完整链路拆开讲。适合正在做实验报告、想用代码把每个结论验证一遍的本科生也适合想快速搭一套信号处理验证环境的工程师。核心工具用 Python 的 NumPy 和 Matplotlib不依赖任何付费软件所有代码可直接复制运行。2. 实验环境搭建与信号生成把公式变成可运行的数组2.1 为什么选 Python 而不是 MATLAB信号与系统实验常见的工具是 MATLAB但 MATLAB 需要授权且脚本在同学之间传递时容易因为版本差异翻车。Python 的 NumPy 提供与 MATLAB 几乎同构的数组运算Matplotlib 的绘图能力足够覆盖实验报告需求而且完全免费、跨平台。我一般会建议用 Python 3.10 以上版本配合以下三个库pip install numpy matplotlib scipyNumPy 负责数组和数学运算Matplotlib 负责绘图SciPy 负责信号处理中的卷积、滤波、频谱分析等高级操作。安装完成后用一行代码验证环境import numpy as np import matplotlib.pyplot as plt from scipy import signal print(np.__version__, plt.__version__, signal.__version__)如果三个版本号都能正常打印环境就没问题。这里注意不要用pip install signal那个包和信号处理无关正确的包名是scipy。2.2 连续信号的离散化表示采样率怎么定信号与系统实验的第一步是把连续时间信号 (x(t)) 变成计算机能处理的离散序列 (x[n])。这里的关键参数是采样率 (f_s)。根据奈奎斯特采样定理采样率必须大于信号最高频率的两倍否则会发生混叠。实验报告里经常出现的正弦信号、方波、三角波它们的频谱成分不同采样率要求也不同。以最常见的正弦信号 (x(t) \sin(2\pi f_0 t)) 为例如果 (f_0 5) Hz那么最高频率就是 5 Hz采样率至少取 10 Hz。但实际做实验报告时我一般会把采样率取到信号频率的 20 倍以上这样画出来的波形足够光滑不会出现明显的折线感。import numpy as np import matplotlib.pyplot as plt # 参数设置 f0 5 # 信号频率 5 Hz fs 200 # 采样率 200 Hz是信号频率的 40 倍 T 1.0 # 观察时长 1 秒 t np.arange(0, T, 1/fs) # 时间轴 # 生成正弦信号 x np.sin(2 * np.pi * f0 * t) # 绘图 plt.figure(figsize(10, 4)) plt.plot(t, x, linewidth1.5) plt.xlabel(时间 t (s)) plt.ylabel(幅度) plt.title(f正弦信号 f0{f0}Hz, fs{fs}Hz) plt.grid(True, alpha0.3) plt.tight_layout() plt.show()这段代码的逻辑很直接np.arange(0, T, 1/fs)生成从 0 到 T、步长为 (1/f_s) 的时间序列np.sin(2*np.pi*f0*t)计算每个时刻的幅度值。参数说明f0是信号频率fs是采样率T是观察时长。如果fs设得太低比如只取 10 Hz画出来的正弦波会变成锯齿状这就是混叠的直观表现。实验报告里如果要求对比不同采样率的效果可以把fs分别设为 10、50、200画三张子图放在一起。2.3 常见实验信号的生成模板除了正弦信号实验报告里还经常要求生成方波、三角波、单位冲激和单位阶跃。这些信号在 NumPy 和 SciPy 里都有现成函数但要注意离散表示和连续定义的差异。import numpy as np from scipy import signal fs 1000 t np.arange(0, 0.1, 1/fs) # 方波频率 50 Hz占空比 50% square_wave signal.square(2 * np.pi * 50 * t, duty0.5) # 三角波频率 50 Hz triangle_wave signal.sawtooth(2 * np.pi * 50 * t, width0.5) # 单位阶跃t 0 时为 1 step np.where(t 0, 1.0, 0.0) # 单位冲激近似表示只在第一个点置 1 impulse np.zeros_like(t) impulse[0] 1.0方波和三角波用scipy.signal生成duty参数控制占空比width参数控制三角波的对称性。单位阶跃用np.where实现单位冲激在离散域里通常用“第一个点置 1、其余为 0”来近似。这里有个坑连续域的单位冲激是狄拉克函数宽度趋于零、幅度趋于无穷离散域无法真正表示所以实验报告里一般用“单位脉冲序列”代替只在 n0 处取 1。3. 卷积运算与系统响应从公式到手算验证3.1 卷积的离散实现np.convolve 与 scipy.signal.convolve卷积是信号与系统实验报告的核心内容之一。连续卷积公式是 (y(t) x(t) * h(t) \int x(\tau)h(t-\tau)d\tau)离散形式是 (y[n] \sum x[k]h[n-k])。在 Python 里np.convolve可以直接计算两个序列的卷积和。import numpy as np import matplotlib.pyplot as plt # 定义输入信号 x[n] 和系统冲激响应 h[n] x np.array([1, 2, 3, 4, 5]) h np.array([1, 1, 1]) # 计算卷积 y np.convolve(x, h, modefull) print(输入 x:, x) print(冲激响应 h:, h) print(卷积结果 y:, y) print(y 的长度:, len(y))这段代码的输出是y [1, 3, 6, 9, 12, 9, 5]长度是len(x) len(h) - 1 7。modefull返回完整卷积结果modesame返回与输入等长的中心部分modevalid只返回完全重叠的部分。实验报告里如果要求验证卷积的交换律、结合律可以交换x和h再算一次结果应该完全一致。3.2 卷积结果的手算验证三个必查点代码跑出来的结果不能直接抄进报告至少要做三处验证。第一检查长度len(y) len(x) len(h) - 1这是离散卷积的基本性质。第二检查首尾值y[0] x[0]*h[0]y[-1] x[-1]*h[-1]如果这两个值不对说明卷积方向或边界处理有问题。第三用多项式乘法交叉验证把x和h看作多项式系数np.convolve的结果应该和np.poly1d乘法一致。# 用多项式乘法验证卷积 poly_x np.poly1d(x) poly_h np.poly1d(h) poly_y poly_x * poly_h print(多项式乘法系数:, poly_y.coeffs) print(与 np.convolve 结果一致:, np.allclose(y, poly_y.coeffs))如果np.allclose返回True说明卷积计算正确。这个验证方法在实验报告里可以作为“结果校验”部分比单纯贴一张图更有说服力。3.3 连续卷积的数值近似采样率与积分步长连续卷积在计算机里只能用数值积分近似。常见做法是把连续信号离散化然后用np.convolve计算最后乘以采样间隔 (dt)。这里的关键参数是采样间隔dt越小近似越准但计算量越大。import numpy as np import matplotlib.pyplot as plt # 定义连续信号 fs 1000 dt 1 / fs t np.arange(-1, 1, dt) # 输入信号矩形脉冲宽度 0.5 秒 x np.where((t -0.25) (t 0.25), 1.0, 0.0) # 系统冲激响应指数衰减 h np.exp(-2 * t) * (t 0) # 数值卷积 y np.convolve(x, h, modefull) * dt t_y np.arange(len(y)) * dt - 1 # 调整时间轴 # 绘图 plt.figure(figsize(10, 6)) plt.subplot(3, 1, 1) plt.plot(t, x) plt.title(输入信号 x(t)) plt.grid(True, alpha0.3) plt.subplot(3, 1, 2) plt.plot(t, h) plt.title(冲激响应 h(t)) plt.grid(True, alpha0.3) plt.subplot(3, 1, 3) plt.plot(t_y, y) plt.title(卷积结果 y(t)) plt.grid(True, alpha0.3) plt.tight_layout() plt.show()这里modefull返回的序列长度是len(x) len(h) - 1乘以dt后得到近似的连续卷积值。时间轴t_y需要重新构造起点是t[0] t[0]步长仍是dt。实验报告里如果要求对比理论值和数值结果可以把理论卷积公式也画在同一张图上用不同线型区分。4. 傅里叶变换与频谱分析从时域到频域的完整链路4.1 FFT 的调用方式与频率轴构造傅里叶变换是信号与系统实验报告的另一核心。Python 里用np.fft.fft计算离散傅里叶变换但直接画出来的频谱图频率轴是错的必须手动构造。import numpy as np import matplotlib.pyplot as plt # 生成信号两个正弦分量叠加 fs 1000 T 1.0 t np.arange(0, T, 1/fs) x np.sin(2 * np.pi * 50 * t) 0.5 * np.sin(2 * np.pi * 120 * t) # 计算 FFT N len(x) X np.fft.fft(x) freq np.fft.fftfreq(N, 1/fs) # 只取正频率部分 mask freq 0 freq_pos freq[mask] X_pos np.abs(X[mask]) / N * 2 # 幅度归一化 # 绘图 plt.figure(figsize(10, 4)) plt.plot(freq_pos, X_pos) plt.xlabel(频率 (Hz)) plt.ylabel(幅度) plt.title(信号频谱) plt.xlim(0, 200) plt.grid(True, alpha0.3) plt.tight_layout() plt.show()这段代码的关键在np.fft.fftfreq(N, 1/fs)它生成与 FFT 结果对应的频率轴。N是采样点数1/fs是采样间隔。np.abs(X)取幅度除以N再乘以 2 是为了归一化到实际幅度因为 FFT 结果是对称的正负频率各占一半能量。实验报告里如果要求标注峰值频率可以用np.argmax找到幅度最大值对应的频率。4.2 频谱泄漏与窗函数矩形窗为什么让峰值变胖直接对有限长信号做 FFT相当于加了矩形窗。矩形窗的频谱是 sinc 函数会导致频谱泄漏原本单一频率的峰值会向两侧扩散。实验报告里如果要求分析泄漏现象可以对比加窗前后的频谱。import numpy as np import matplotlib.pyplot as plt fs 1000 T 0.5 t np.arange(0, T, 1/fs) f0 50.5 # 故意取非整数频率加剧泄漏 x np.sin(2 * np.pi * f0 * t) # 不加窗 X1 np.abs(np.fft.fft(x)) / len(x) * 2 freq np.fft.fftfreq(len(x), 1/fs) # 加汉宁窗 window np.hanning(len(x)) X2 np.abs(np.fft.fft(x * window)) / len(x) * 2 # 绘图对比 plt.figure(figsize(10, 4)) plt.plot(freq[:len(freq)//2], X1[:len(freq)//2], label矩形窗) plt.plot(freq[:len(freq)//2], X2[:len(freq)//2], label汉宁窗) plt.xlabel(频率 (Hz)) plt.ylabel(幅度) plt.title(频谱泄漏对比) plt.xlim(40, 60) plt.legend() plt.grid(True, alpha0.3) plt.tight_layout() plt.show()汉宁窗的旁瓣衰减比矩形窗快得多峰值更集中但主瓣宽度会增加。实验报告里如果要求分析窗函数的影响可以对比矩形窗、汉宁窗、汉明窗、布莱克曼窗的效果。我一般会建议至少对比两种说明“主瓣宽度”和“旁瓣衰减”之间的权衡。4.3 采样定理验证从混叠到重建采样定理是信号与系统实验报告里最容易出彩的部分。用代码演示“采样率低于奈奎斯特频率时发生混叠”比单纯画公式更有说服力。import numpy as np import matplotlib.pyplot as plt # 原始信号频率 10 Hz f0 10 t_cont np.linspace(0, 1, 10000) x_cont np.sin(2 * np.pi * f0 * t_cont) # 欠采样采样率 12 Hz低于 2*f020 Hz fs_low 12 t_low np.arange(0, 1, 1/fs_low) x_low np.sin(2 * np.pi * f0 * t_low) # 过采样采样率 100 Hz fs_high 100 t_high np.arange(0, 1, 1/fs_high) x_high np.sin(2 * np.pi * f0 * t_high) # 绘图 plt.figure(figsize(10, 6)) plt.subplot(2, 1, 1) plt.plot(t_cont, x_cont, k--, alpha0.3, label原始信号) plt.stem(t_low, x_low, linefmtr-, markerfmtro, basefmt , label欠采样 fs12Hz) plt.legend() plt.title(欠采样导致混叠) plt.grid(True, alpha0.3) plt.subplot(2, 1, 2) plt.plot(t_cont, x_cont, k--, alpha0.3, label原始信号) plt.stem(t_high, x_high, linefmtb-, markerfmtbo, basefmt , label过采样 fs100Hz) plt.legend() plt.title(过采样保留信号形状) plt.grid(True, alpha0.3) plt.tight_layout() plt.show()欠采样时采样点连起来的形状频率变低这就是混叠。实验报告里可以进一步计算混叠后的频率f_alias |f0 - fs_low| |10 - 12| 2 Hz说明 10 Hz 的信号在 12 Hz 采样率下被误认为是 2 Hz。这个结论用代码验证一遍比背公式印象深得多。5. 实验报告常见翻车点排查波形不对、频谱不对、结果对不上5.1 波形图看起来像折线不够光滑现象画出来的正弦波有明显的棱角不像课本上的光滑曲线。原因采样率太低或者绘图时用了plt.plot但数据点太少。解决把采样率提高到信号频率的 20 倍以上或者用plt.plot时增加数据点密度。如果数据点已经很多但图还是折线检查plt.plot的linewidth参数太细的线在屏幕上会显得不连续。5.2 频谱峰值位置偏移或出现多个峰现象FFT 频谱图上峰值不在预期的频率处或者一个单频信号出现多个峰。原因频率轴构造错误或者信号时长不是信号周期的整数倍导致泄漏。解决用np.fft.fftfreq(N, 1/fs)构造频率轴确保N和fs对应。如果信号时长不是周期整数倍加窗函数抑制泄漏或者调整信号时长使其包含整数个周期。5.3 卷积结果长度不对或首尾值异常现象np.convolve返回的序列长度和预期不符或者首尾值与手算不一致。原因mode参数选错或者输入序列的顺序搞反。解决modefull返回完整卷积长度是len(x)len(h)-1modesame返回与x等长的中心部分。检查x和h的定义顺序卷积满足交换律但np.convolve(x, h)和np.convolve(h, x)的结果在数值上应该一致。5.4 采样定理验证时混叠频率算错现象欠采样后重建信号的频率和理论混叠频率对不上。原因混叠频率的计算公式用错或者采样率设置不合理。解决混叠频率 (f_{alias} |f_0 - k \cdot f_s|)其中 (k) 是使得结果落在 ([0, f_s/2]) 范围内的整数。以 (f_010) Hz、(f_s12) Hz 为例(k1) 时 (f_{alias}2) Hz与代码演示一致。实验报告里要把这个计算过程写清楚。5.5 绘图时中文显示为方框现象plt.title(信号频谱)中的中文变成方框。原因Matplotlib 默认字体不支持中文。解决在绘图前设置中文字体plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] FalseSimHei是黑体Windows 系统一般自带。如果是在 Linux 或 macOS 上可以换成Noto Sans CJK SC或Arial Unicode MS。axes.unicode_minus设为False是为了正常显示负号。6. 从实验报告到实际信号处理一个完整的音频分析实例实验报告里的信号大多是人工生成的但真实信号处理面对的是音频、振动、生物电等复杂信号。这一章用一个音频片段做完整分析把前面所有知识点串起来。音频文件可以用scipy.io.wavfile读取也可以用soundfile库。这里用scipy自带的读取函数避免额外依赖。import numpy as np import matplotlib.pyplot as plt from scipy.io import wavfile from scipy import signal # 读取音频文件替换为实际文件路径 fs, audio wavfile.read(sample.wav) # 如果是立体声取单声道 if len(audio.shape) 1: audio audio.mean(axis1) # 归一化到 [-1, 1] audio audio / np.max(np.abs(audio)) # 截取前 0.5 秒 duration 0.5 n_samples int(fs * duration) segment audio[:n_samples] t np.arange(n_samples) / fs # 时域波形 plt.figure(figsize(12, 8)) plt.subplot(3, 1, 1) plt.plot(t, segment) plt.title(音频时域波形前 0.5 秒) plt.xlabel(时间 (s)) plt.ylabel(幅度) plt.grid(True, alpha0.3) # 频谱 N len(segment) X np.abs(np.fft.fft(segment)) / N * 2 freq np.fft.fftfreq(N, 1/fs) mask freq 0 plt.subplot(3, 1, 2) plt.plot(freq[mask], X[mask]) plt.title(音频频谱) plt.xlabel(频率 (Hz)) plt.ylabel(幅度) plt.xlim(0, fs/2) plt.grid(True, alpha0.3) # 语谱图 f, t_spec, Sxx signal.spectrogram(segment, fs, nperseg256, noverlap128) plt.subplot(3, 1, 3) plt.pcolormesh(t_spec, f, 10 * np.log10(Sxx 1e-10), shadinggouraud) plt.title(语谱图) plt.xlabel(时间 (s)) plt.ylabel(频率 (Hz)) plt.colorbar(label功率 (dB)) plt.tight_layout() plt.show()这段代码做了三件事时域波形展示幅度随时间的变化频谱展示频率成分语谱图展示频率成分随时间的变化。signal.spectrogram的参数nperseg是每段长度noverlap是重叠点数这两个参数决定了时频分辨率。nperseg越大频率分辨率越高但时间分辨率越低noverlap越大时频图越平滑但计算量越大。实际做音频分析时我一般会先看语谱图因为人耳对频率随时间的变化最敏感。如果语谱图上出现明显的水平条纹说明有持续的单频干扰如果出现垂直条纹说明有瞬态冲击。实验报告里如果要求分析实际信号可以录一段自己的声音用这套代码跑一遍把时域、频域、时频域三张图放在一起结论会非常直观。最后说一个我踩过的坑音频文件的采样率fs必须从文件头读取不能自己假设。曾经有一次我默认fs44100结果文件实际是48000频谱图上所有峰值都偏移了约 9%排查了半天才发现是采样率搞错。从那以后我养成了一个习惯任何信号处理代码第一行先打印fs和信号长度确认无误再往下写。希望帮到你。本文还有配套的精品资源点击获取