DSP实验报告实操指南:采样率、FFT频谱分析与滤波器设计 简介这份DSP实验报告指导书与最终实验报告压缩包面向数字信号处理课程的本科生与自学者围绕循环操作、双操作数乘法、并行运算、小数运算、长字运算、浮点运算、卷积和相关运算等核心实验展开每个实验都包含原理说明、硬件平台实现步骤和结果分析帮助读者从理论过渡到工程实践。包内共21个文件包含10份docx实验报告、1份doc指导书以及CCS平台配套的C源码、out、pjt、cmd、map等工程文件便于对照工程配置与运行结果整体大小约8.12MB。已有599人学习下载。通过报告与工程文件结合不仅能掌握DSP基本运算的硬件实现方法还能熟悉有限精度下的舍入误差处理、长字扩展和并行优化等关键技巧是实验复习与期末备考的实用资料。1. 拿到 DSP 实验报告指导书这份压缩包的人基本分成两种拿到 DSP 实验报告指导书这份压缩包的人基本分成两种。一种手头刚发指导书对着任务清单不知道报告每一节该写什么另一种实验已经做完了但报告交上去总被批「只有图没有分析」。这份指导书和最终实验报告是配套的从任务书长成完整报告的全过程都在里面能直接看到每个实验任务对应报告中的哪些图表、哪些分析。它适合正在做 DSP 课程实验、需要按验收点补齐分析、又想核对仿真参数的同学。我不打算替你抄报告而是把这份包拆成五个实验对应的验收链——每个任务该出什么图、用什么参数、卡哪些坑——你看完就能自己复现。2. 拆包之后先看骨架指导书是任务清单报告是验收单别读反了2.1 指导书与最终报告之间的映射关系指导书的作用不是「让你照着抄一份实验报告」而是把每个实验要验证的原理、要观测的现象、要提交的图表定义清楚。最终报告则是你对指导书每一条任务的逐项回答。很多同学把顺序搞反了拿到报告先抄结论结果导师一问参数来源就答不上来。先按指导书常见的内容把五个实验和报告产出的对应关系列出来这份包里的完整实验报告大体也是按这个结构组织的实验任务报告里的关键产出最容易失分的检查点采样定理与信号生成不同采样率下的时域波形与频谱对比采样率是否满足奈奎斯特条件混叠频率有没有算出来卷积与差分方程卷积结果图形、系统单位响应对比卷积长度 LM-1 是否标对边界效应是否说明FFT 频谱分析单边幅值谱、加窗前/后对比图横轴是否换算成 Hz幅度是否做了归一化IIR 带通滤波器设计幅频/相频响应曲线、零极点图归一化频率是否正确阻带衰减指标是否达标FIR 低通滤波器设计不同窗函数对比、阶数与过渡带关系窗函数带来的主瓣/旁瓣变化是否讲清典型的一处对应关系指导书写「观察采样率不满足奈奎斯特条件时的混叠现象」报告里就应该出现两张图一张是 fs8000 Hz 下 2 kHz 信号正常还原另一张是 fs3000 Hz 下同一信号折返出 1 kHz 假谱。分析段落要把假谱频率用公式 fs - f 算出来而不是只写一句「可以看出混叠」。2.2 报告的通用骨架目的、原理、步骤、结果与分析五段式课程实验报告大多沿用五段式结构这份包里的最终实验报告也是这个套路。五段各有分工但比重完全不同。实验目的写一两句即可讲清楚「验证什么」。像「观察采样率对频谱的影响」就够不用把教材第一页整段搬进来。实验原理是给后面图表做铺垫的只写本次用到的公式和物理含义。比如 FFT 实验就写 X(k)Σx(n)e^(-j2πkn/N) 和频率分辨率 Δffs/N不要把所有 DFT 性质都罗列一遍。实验步骤是「别人能照着复现」的完整参数链。采样率、点数、信号频率、滤波器类型、阶数、窗函数都要写清楚。报告里常见的问题是把步骤写成「用 fft 算了一下」没有任何数值导师没法核验。结果部分是图表展示每张图必须带编号图下注明 fs、N、窗函数等关键参数。分析部分是整份报告的权重所在也是最容易被打回的地方。分析不是复述图里肉眼可见的曲线而是要回答三个问题峰值出现在哪个频率、理论值是多少、偏差来自哪里。2.3 阅读顺序先任务后报告先复现后抄录我拿到这份包之后不会先翻最终报告而是先看指导书按三步走第一步把指导书里的每个实验任务转成「验收点清单」。比如任务写「设计一个带通滤波器」验收点就是通带范围、阻带衰减、使用什么设计方法这三个点缺一个报告就不完整。第二步拿验收点逐项对照完整实验报告里的图和数据表看报告是怎么回答这些点的。这一步能快速看出这套报告的质量也能判断哪些段落可以直接复用哪些参数需要自己重算。第三步用 Python 或 Matlab 把关键实验重跑一遍。常见做法是用 Python 的 numpy/scipy 复现信号生成、FFT 和滤波器设计跑通后再对照报告的截图核对幅值、频率和衰减值。这一步最花时间但能把报告里含糊的参数全部暴露出来。如此处理之后那份「DSP 完整的所用实验报告」对你就不再是一堆截图而是一份可以随时查证的数据来源。我的习惯是每复现一个实验就在报告对应位置补一行参数备注后续写自己的报告时直接引用效率高很多。3. 把经典实验跑通采样率、FFT 频谱分析与频谱泄漏的三个关键参数3.1 信号生成与采样率选择先满足奈奎斯特再谈美观DSP 实验里最容易翻车的不是算法本身而是参数选得不对。指导书里常见的实验板采样率是 8000 Hz这个值对应语音信号的常规采样率也足够容纳 4 kHz 以内的信号。我一般会用 f11000 Hz、f23000 Hz 两个正弦分量合成输入信号这样既能看到两条清晰的谱线又不会因为频率太靠近 fs/2 让边界效应干扰观察。参数取值说明fs8000 Hz采样率决定可分析的最高频率 4000 Hzf1 / f21000 / 3000 Hz两个正弦分量避开折叠区N1024采样点数频率分辨率约 7.8 Hz数据时长0.128 s由 N/fs 得到对应 128 个采样周期先用代码把信号生成和基础 FFT 完整跑一遍import numpy as np import matplotlib.pyplot as plt fs 8000 # 采样率Hz指导书实验三常用值 N 1024 # 采样点数点数越多频率分辨率越细 t np.arange(N) / fs # 时间序列单位秒 f1, f2 1000, 3000 # 两个正弦分量的频率 x np.sin(2 * np.pi * f1 * t) 0.5 * np.sin(2 * np.pi * f2 * t) X np.fft.rfft(x) # 实数序列取单边 FFT freq np.fft.rfftfreq(N, d1 / fs) # 把 bin 索引换算成真实频率 mag np.abs(X) / N * 2 # 单边幅度谱归一化 plt.plot(freq, mag, lw1.2) plt.xlim(0, fs / 2) plt.xlabel(Frequency (Hz)) plt.ylabel(Magnitude) plt.grid(alpha0.3) plt.savefig(fft_experiment.png, dpi150)这段代码里x 是实信号所以用 rfft 只取正频率部分输出点数是 N/21 而不是 N 个复数。幅度归一化的逻辑是FFT 结果的绝对值要先除以 N 才是双边谱幅度再乘 2 把负频率侧的能量合并回单边。频率轴必须用 rfftfreq 生成否则横轴是 0 到 511 的 bin 序号看起来谱峰位置对得上实际频率全错。参数层面最需要理解的是 Δffs/N≈7.8 Hz。这意味着 1000 Hz 分量恰好落在第 128 个频点上谱线干净无泄漏。如果信号频率改成 1001 Hz它落在两个频点之间能量就会扩散到相邻的几个频点上这就是 3.3 要讲的泄漏现象。想减小 Δf 的方法是增大 N而不是降低 fsN 翻倍到 2048分辨率能到 3.9 Hz但计算量和显示密度也会跟着涨。3.2 FFT 幅值谱横轴必须换成 Hz不然对不上很多报告翻车都翻在同一个位置频谱图横轴用的是 0~N-1 的点序号而不是频率。图线形状完全正确但横轴标注没法读。更隐蔽的是有些工具默认横轴是归一化频率0 到 1 对应 0 到 fs/2如果直接抄进报告评审根本没法判断 1000 Hz 峰值的位置对不对。上面代码里 rfftfreq(N, d1/fs) 生成的就是 0 到 fs/2 的频率轴间隔正好是 Δf7.8 Hz。这里需要注意的是 d 的单位必须和 t 保持一致t 用秒d1/fs 就是秒得到的 freq 单位才是 Hz。很多同学把 d 写成 N/fs结果整个横轴差了 N 倍谱峰跑到几千赫兹外去了属于典型的单位换算错误。提示如果用 Matlab 的 fft 函数画频谱同样不要用默认的 1:N 做横轴而应该用 (0:N-1)/N*fs 换算成实际频率。3.3 频谱泄漏与加窗报告中必须出现的第三张图FFT 是对有限长度信号做周期延拓截断带来的边界跳变会在频谱里产生额外分量这就是频谱泄漏。指导书一般会要求你观察「不加窗 vs 加窗」的区别。矩形窗相当于直接截断主瓣窄但旁瓣高汉宁窗牺牲了一点主瓣宽度把旁瓣压低 30 dB 以上适合观察小幅度信号。win np.hanning(N) # 汉宁窗主瓣略宽旁瓣衰减更快 xw x * win # 时域加窗 Xw np.fft.rfft(xw) mag_w np.abs(Xw) / (N * win.mean()) # 加窗后幅度补偿 plt.figure(figsize(7, 4)) plt.plot(freq, np.abs(X) / N * 2, labelrectangular, lw1.2) plt.plot(freq, mag_w, labelhanning, lw1.2) plt.xlim(0, 5000) plt.xlabel(Frequency (Hz)) plt.ylabel(Magnitude) plt.legend() plt.savefig(leakage_compare.png, dpi150)加窗后信号能量变小直接除以 N 会导致幅度偏低所以代码里除以 N * win.mean() 做补偿。汉宁窗的均值大约是 0.5补偿后峰值能恢复到接近原值。三个窗的取舍可以参考下表窗函数主瓣宽度最大旁瓣适用场景矩形窗窄-13 dB频率间隔较大、重视幅度保真时汉宁窗约为矩形 2 倍-31 dB一般频谱分析的首选凯泽窗可调可低至 -58 dB需要分辨小幅度邻近分量时写报告时这张对比图最好和未加窗的谱线画在同一张图里横轴相同、纵轴相同直接展示旁瓣被压低的效果。很多查重靠改参数躲不过去但加窗对比图这种「同数据双标注」的图评审一眼就能看出是不是你自己跑出来的。4. 从指标到实现IIR/FIR 滤波器中的通带、阻带与阶数取舍4.1 指标先行一张表把验收点说清楚滤波器设计的报告里最常被批的不是代码而是指标不完整。指导书让你「设计一个带通滤波器」你必须先在报告里把指标写全后面所有图表才有评判标准。以这份包里的 IIR 带通实验为例我整理出的典型指标如下参数数值说明采样率 fs8000 Hz与分析实验保持一致通带范围1200 ~ 2400 Hz需要通过的目标频带通带纹波≤ 1 dB通带内容许的增益波动阻带衰减≥ 40 dB通带外需要压制的强度设计方法IIR 椭圆 / FIR 窗函数按指导书要求选择选型逻辑通常是如果报告要求突出「阶数低、计算省」选 IIR 椭圆或切比雪夫如果要求突出「线性相位」选 FIR。IIR 的优点是同样指标下阶数只有 FIR 的十分之一左右缺点是相位非线性群延迟不平坦。FIR 的优点是能严格线性相位缺点是为了窄过渡带要付出很多阶数。4.2 IIR 带通设计椭圆滤波器与双线性变换IIR 设计绕不开双线性变换。模拟滤波器频率轴是从 0 到无穷数字滤波器频率轴只有 0 到 fs/2双线性变换用正切函数把两端压缩进有限区间代价是频率被非线性扭曲。在 scipy 的 iirfilter 里只要传入 fs 参数并把 Wn 写成 Hz这个预畸变会由库内部处理不需要手工换算。import numpy as np import matplotlib.pyplot as plt from scipy import signal as sig fs 8000 b, a sig.iirfilter( N4, # 阶数椭圆 4 阶即可拿到 40 dB 阻带衰减 Wn[1200, 2400], # 通带边缘频率单位 Hz rp1, # 通带纹波 1 dB rs40, # 阻带衰减 40 dB btypebandpass, ftypeellip, # 椭圆滤波器过渡带最窄 fsfs, ) w, h sig.freqz(b, a, worN2048, fsfs) resp 20 * np.log10(np.abs(h) 1e-12) plt.plot(w, resp) plt.axvspan(1200, 2400, colorgreen, alpha0.15) # 通带范围 plt.axhline(-40, colorred, ls--, lw0.8) # 阻带衰减指标线 plt.xlabel(Frequency (Hz)) plt.ylabel(Gain (dB)) plt.savefig(iir_bandpass.png, dpi150)跑完代码图上应该看到绿色区间内增益接近 0 dB区间外快速下降并在某处穿过 -40 dB 红线。报告里的分析需要落到具体频率从图中读取通带边缘处增益、阻带起始频率、实际最小衰减和 4.1 指标表逐项对比。验证方法是用 lfilter 分别滤 1 kHz 和 2 kHz 正弦比较输出幅度2 kHz 分量应基本保留1 kHz 分量明显削弱。4.3 FIR 低通设计窗函数与阶数经验公式FIR 设计里窗函数决定过渡带和旁瓣。以 1800 Hz 低通为例常见做法是先用凯泽窗经验公式估算阶数N ≈ (A - 7.95) / (2.285 × Δω)其中 A 是目标阻带衰减 dB 值Δω 是归一化过渡带宽度。这个公式给的是估数实际跑完要回头检查阻带是否达标不达标就加大 N。from scipy import signal as sig fs 8000 cut 1800 # 低通截止频率单位 Hz N_fir 51 # 阶数由过渡带宽估算后取整 b_fir sig.firwin(N_fir, cut, windowhann, fsfs) w_fir, h_fir sig.freqz(b_fir, worN2048, fsfs) plt.plot(w_fir, 20 * np.log10(np.abs(h_fir) 1e-12)) plt.xlabel(Frequency (Hz)) plt.ylabel(Gain (dB)) plt.savefig(fir_lowpass.png, dpi150)汉宁窗的规律是过渡带宽度约等于几个频点间隔N 越大过渡带越窄但旁瓣高度不会因为 N 增大而明显下降。想压旁瓣要换窗想压过渡带要加阶数这两件事经常被写混。报告放图时建议同时画出 0.5 kHz 到 3 kHz 的局部放大视图让评审看清过渡带斜率和 -40 dB 截止点。IIR 和 FIR 放一起对比时常见写法是列一张小表写明 IIR 椭圆 4 阶、FIR 汉宁窗 51 阶同一阻带指标下阶数差约 12 倍但 FIR 相位严格线性。这个对比是报告里的加分项既说明你理解两种设计路线的差异也解释了为什么 FIR 要付出更多系数。5. 避坑与常见问题频谱、归一化频率与截图分析里的五个返工雷区这五条是我自己踩过的坑也是替别人审报告时最常见的返工点按频次排序。每条都是「现象 → 原因 → 解决」三段式直接对照你自己的报告检查。5.1 频谱横轴没有换算成 Hz谱峰位置全错现象FFT 图画出来两条谱线间距看起来没问题但横轴标注是 0 到 1024 的点序号导师问「1000 Hz 的峰在哪」答不上来。原因用了 np.fft.fft 之后直接把索引当横轴没做频率换算。频谱的横轴本质是频点序号必须乘以 fs/N 才能变成 Hz。解决统一用 np.fft.rfftfreq(N, d1/fs) 生成频率轴。Matlab 里则用 (0:N-1)/N*fs。这个换算没有任何玄学就是单位换算但报告里出错的频率极高。5.2 归一化频率被当成 Hz 使用滤波器设计完全失效现象iirfilter 或 firwin 设计出的滤波器通带位置和预期完全对不上滤完信号整个静音或者原样通过。原因数字滤波器内部普遍用归一化频率0 到 1 对应 0 到 fs/2。有人在 Wn 里直接写 1200库默认它是 0.12 之类的小数实际截止频率差了几千赫兹。解决设计函数里明确传入 fs8000Wn 直接用 Hz 写。如果用的库不支持 fs 参数就手动把 Hz 换算成归一化值Wn_norm Wn / (fs/2)。写报告时设计代码块里一定要标注这一行否则复现的人必踩。5.3 加窗后幅值掉了一半被怀疑信号衰减现象加汉宁窗后的频谱峰值从 1.0 掉到 0.5 左右报告里写着「加窗导致信号衰减约 6 dB」。原因加窗改变了信号总能量幅度归一化时没有把窗函数的均值算进去。汉宁窗均值约 0.5直接 abs/N 自然只剩一半。解决幅度归一化用 mag_w np.abs(Xw) / (N * win.mean())。这行代码和 3.3 里一样写报告时把这个补偿公式放进原理段能省下很多和评审解释的精力。5.4 报告截图 dpi 太低图的细节根本看不清现象滤波器幅频响应图里-40 dB 的红线和曲线交点糊成一团零极点图的零点位置完全看不清评审直接打回重画。原因Jupyter 里默认 inline 显示的分辨率不高直接截图贴进 Word 又被压缩一轮细节就没了。解决所有图用 plt.savefig(xxx.png, dpi150) 保存Word 里插入时选择「嵌入型」不要拉伸。dpi 设 150 是字体和体积平衡点300 更清晰但文件会变大。报告提交前把图放大到 200% 检查一遍看不清就先处理图而不是在 Word 里硬拉。5.5 只贴图不写分析报告被批没有结论现象报告里图倒是不少但每张图下面只有图号没有文字或者写着「由上图可知滤波效果良好」。原因把「结果」和「分析」混为一谈。图只是结果分析必须回答指标是否达标、偏差来源在哪。解决每张图配 3 到 4 行分析格式固定为「峰值/衰减出现在哪个频率 → 理论预期是多少 → 偏差原因是什么」。带通滤波器就写「通带 1200-2400 Hz 内增益波动小于 1 dB阻带在 900 Hz 处衰减约 44 dB满足 40 dB 指标偏差来自椭圆滤波器的通带纹波」。这种分析有具体的数字和原因不会被打回。6. 最后一招把这份报告包还原成可复现的工程包很多导师判断一份实验报告是不是认真做的不是看图表数量而是看关键数值能不能重算出来。所以我把这套报告对应的脚本整理成一份可复现的工程包核心技巧是把所有参数集中在头部每个实验输出独立命名的图文件名带上采样率和点数。这样改写参数重跑时不会覆盖旧图交报告时按文件名核对数据也方便。# 统一参数区换一组参数所有图自动重画 params dict(fs8000, N1024, f11000, f23000) def plot_spectrum(p): import numpy as np import matplotlib.pyplot as plt t np.arange(p[N]) / p[fs] x np.sin(2 * np.pi * p[f1] * t) 0.5 * np.sin(2 * np.pi * p[f2] * t) X np.fft.rfft(x) f np.fft.rfftfreq(p[N], d1 / p[fs]) plt.plot(f, np.abs(X) / p[N] * 2) plt.savefig(ffigures/fft_{p[fs]}_{p[N]}.png, dpi150) plot_spectrum(params)文件名里的 fs 和 N 是关键参数属于「看见文件名就知道图是怎么跑出来的」的命名习惯。滤波器实验也可以同样处理把通带、阻带写进文件名比如 iir_bp_1200_2400_rs40.png。最后说一个我自己的习惯拿到任何实验包先花半小时把指导书里的验收点列成清单再用脚本逐项跑通最后才动笔写报告。这个习惯让我少返了三四次工也把「实验报告指导书」从一份抄写素材变成真正能复现的实验记录。希望帮到你。本文还有配套的精品资源点击获取