☰
数字信号处理MATLAB代码解析:滤波器设计与功率谱估计实战
2026/10/15 12:20:40 网站建设 项目流程

简介:《数字信号处理理论、算法与实现》(胡广书著)的配套Matlab代码及参考文献包,是一份面向数字信号处理学习者与研究者的实用资源,旨在帮助读者将教材中的理论推导转化为可运行的仿真程序。压缩包共127个文件,以114个.m脚本为主体,按章节覆盖了离散时间信号与系统、Z变换、信号的傅里叶变换、IIR/FIR滤波器设计、功率谱估计,以及时频分析中的LOT、STFT等算法实现;同时包含7个wav信号样本用于实验验证,3个pdf参考文献便于延伸阅读,还有mat数据文件、bmp示意图和doc使用说明,整体约4.5MB,轻巧但体系完整。已有1663人学习下载,适合配合原书进行课程学习、考研复习或毕业设计前的算法预研。资源中的代码大多以书中例题为背景,命名清晰并与目录对应,配合信号样本可快速复现书中图表,能大幅缩短从理论到代码实践的摸索时间。

1. 数字信号处理:从理论推导到 Matlab 落地的完整闭环

很多人学数字信号处理,卡在“公式看得懂、MATLAB 跑不出来”。胡广书《数字信号处理——理论、算法与实现》这本经典教材的配套 Matlab 代码与参考文献资源,恰好补齐了从“理论”到“实现”的这段距离。整份资源把 FIR/IIR 滤波器设计、FFT 谱分析、随机信号处理、功率谱估计等章节的核心算法全部转成了可运行的 .m 文件,每一段代码都能对上教材里的公式编号和图表,拿到手可以直接在 MATLAB 里跑通并逐行对照。适合两类人:一类是正在啃这本书、被课后题和仿真图折磨的学生,另一类是做语音、振动、生物电信号分析的工程师,想快速拿到一组经过验证的算法基线,省掉自己从零写滤波器的时间。资源里附带的参考文献清单还能帮你顺藤摸瓜,找到每个算法背后的原始论文。

2. 技术底座:MATLAB 版本、工具箱依赖与目录结构

2.1 资源本体是什么形态

这不是一个打包好的工具箱,也不是 App Designer 做的图形界面程序,而是一组按章节组织的脚本和函数文件。最常见的组织方式是这样:每个章节一个文件夹,比如ch3_z变换、ch5_FIR滤波器、ch7_功率谱估计,文件夹内部以ex_数字.m或fig_图号.m命名。这种命名习惯直接对应书上的“图 X.X”和“例 X.X”,方便你在看书时随手打开同名脚本验证。

初版资源年代较早,部分脚本是基于早期 MATLAB 版本编写的,但核心 API(如fir1、fft、filter、pwelch)二十年来接口基本稳定。如果你用的是 R2016b 之后的版本,直接双击运行大概率能过;如果报错,九成是绘图函数或路径问题,而非算法本身有缺陷。

2.2 运行环境需要准备什么

基础环境只需要 MATLAB 本体,不强制要求 Signal Processing Toolbox——因为很多代码是拿conv、fft等基础函数手写的滤波器实现,这是这份资源比较“硬核”的地方。不过装了 Signal Processing Toolbox 后,部分脚本会走fir1这类高阶封装,跑起来效率更高,结果也更接近教材。

操作系统方面,Windows、macOS、Linux 下的 MATLAB 都能直接运行,没有平台相关代码。内存占用非常小,几百兆的机器也能跑完经典章节。需要留意的是,早期的脚本偶尔会用到wavread这类被移除的函数,新版 MATLAB 里需要改成audioread,这个后面在避坑章节会专门说。

2.3 核心目录结构速览

以常见的资源解压布局为例:

DSP_HuGuangShu/ ├── CH2_离散时间信号与系统/ │ ├── ex_2_1_单位脉冲响应.m │ ├── ex_2_2_线性卷积实现.m │ └── fig_2_5_频谱图.m ├── CH3_傅里叶变换与DFT/ │ ├── ex_3_4_DFT计算圆周卷积.m │ └── fig_3_7_DFT频谱泄漏.m ├── CH5_FIR数字滤波器设计/ │ ├── ex_5_2_窗函数法设计低通.m │ └── ex_5_5_切比雪夫逼近.m ├── CH6_IIR数字滤波器设计/ │ ├── ex_6_1_脉冲响应不变法.m │ └── ex_6_3_双线性变换法.m ├── CH7_功率谱估计/ │ ├── ex_7_1_周期图法.m │ └── ex_7_4_AR模型Burg算法.m └── references/ └── 参考文献清单.pdf

这套结构本身就回答了“怎么找代码”的问题:按章索引,按图号定位。你想验证某个结论,直接进对应目录,文件名里的图号就是最可靠的导航。

3. 核心代码拆解:从时域卷积到频域谱估计的完整链路

3.1 离散卷积与圆周卷积的实现对比

教材第 2 章的核心之一是线性卷积与圆周卷积的关系。配套代码里用一组对比实验把这个关系讲得很透:

% ex_2_2_线性卷积与圆周卷积对比.m x = [1 2 3 4]; % 输入序列 h = [1 1 1]; % 系统单位脉冲响应 N = length(x) + length(h) - 1; % 线性卷积结果长度 y_linear = conv(x, h); % 直接线性卷积 % 补零到 N 点后做圆周卷积 x_pad = [x zeros(1, N - length(x))]; h_pad = [h zeros(1, N - length(h))]; Xk = fft(x_pad); Hk = fft(h_pad); y_circular = ifft(Xk .* Hk); % 频域乘积再逆变换 % 比较两者误差 error = max(abs(y_linear - y_circular)); fprintf('线性卷积与圆周卷积最大误差: %e\n', error);

这段代码的核心逻辑是:先把两个序列都补零到同样长度,再分别做 FFT,频域点乘后做 IFFT,得到的圆周卷积结果应当与conv的线性卷积完全相同。error那行如果输出1e-14量级的数字,说明机器精度范围内两者一致,这也印证了教材里“圆周卷积当长度大于等于 N1+N2-1 时退化为线性卷积”的结论。

值得注意的参数是N = length(x) + length(h) - 1,这是圆周卷积与线性卷积等价的临界条件。如果你把 N 改小,比如只取 5 点,就会看到两个结果完全对不上,这就是频谱混叠的直观体现。实际工程里做长序列卷积时,这个临界长度直接决定了分段卷积的块大小。

3.2 FIR 滤波器窗函数法设计的可调参数

第 5 章的窗函数法设计低通 FIR 滤波器,配套代码给出了一个非常清晰的模板:

% ex_5_2_窗函数法低通滤波器设计.m fs = 1000; % 采样率 1000 Hz fc = 100; % 截止频率 100 Hz N = 33; % 滤波器阶数(时域窗长) % 理想低通单位脉冲响应 n = -(N-1)/2 : (N-1)/2; h_ideal = 2 * fc / fs * sinc(2 * fc * n / fs); % 加 Hamming 窗截断 w = hamming(N)'; h_fir = h_ideal .* w; % 归一化频率响应 [H, W] = freqz(h_fir, 1, 1024, fs); plot(W, 20*log10(abs(H))); grid on; xlabel('频率 (Hz)'); ylabel('幅度 (dB)'); title('Hamming 窗 FIR 低通滤波器幅频响应');

这段代码的核心思路是先算理想低通滤波器的无限长脉冲响应(用sinc函数表达),再乘一个有限长的 Hamming 窗做截断。N决定了过渡带宽度,N越大过渡带越窄;fc决定通带边界。运行后你会看到阻带衰减大约在 -53 dB 左右,这是 Hamming 窗的理论极限,改用矩形窗则只有 -21 dB,改 Blackman 窗能到 -74 dB。

实际调参时,我一般会先用fdatool或filterDesigner反推一个同规格滤波器,跟这段代码的结果做幅频对比,能快速验证手写实现有没有问题。这里有个容易被忽略的细节:sinc在 MATLAB 里是归一化的sin(pi*x)/(pi*x),所以2*fc/fs*sinc(2*fc*n/fs)里已经隐含了截止频率的归一化换算,不要再多乘一个pi。

3.3 IIR 双线性变换法的零极点映射

第 6 章的双线性变换法设计 Butterworth 滤波器,代码里把模拟原型到数字域的映射展示得很直白:

% ex_6_3_双线性变换法设计 Butterworth 低通.m fs = 2000; % 采样率 fp = 200; % 通带截止频率 fs_stop = 400; % 阻带起始频率 Rp = 3; % 通带波纹 3 dB Rs = 40; % 阻带衰减 40 dB % 转换为归一化频率 wp = 2 * pi * fp / fs; ws = 2 * pi * fs_stop / fs; % 预畸变处理 wp_pre = 2 * tan(wp / 2); ws_pre = 2 * tan(ws / 2); % 设计模拟低通原型 [n, wn] = buttord(wp_pre, ws_pre, Rp, Rs, 's'); [z, p, k] = butter(n, wn, 's'); % 双线性变换映射到数字域 [zd, pd, kd] = bilinear(z, p, k, fs); [sos, g] = zp2sos(zd, pd, kd); % 转换为二阶节级联 freqz(sos, 1024, fs);

这段代码的关键在于wp_pre = 2 * tan(wp / 2)这行预畸变。双线性变换会把模拟频率轴压缩到数字频率轴,如果不做预畸变,设计出来的截止频率会偏到错误位置。buttord根据通带/阻带指标自动确定滤波器阶数,bilinear完成从 s 域到 z 域的映射。

运行后你会看到数字滤波器的实际 -3 dB 点精确落在 200 Hz,而不是比 200 Hz 低一点点——这就是预畸变的价值。实际工程中,如果通带很宽(比如从 20 Hz 到 5 kHz),预畸变的效果更明显,不做的话高端频率会严重偏离设计值。

3.4 周期图法与 AR 模型的功率谱对比

第 7 章的功率谱估计是很多人的痛点。配套代码里对比了经典周期图法和现代 AR 模型法的差异:

% ex_7_1_周期图法与Burg法谱估计对比.m fs = 1000; t = 0:1/fs:1; x = sin(2*pi*50*t) + 0.5*sin(2*pi*120*t) + 0.3*randn(size(t)); N = length(x); % 周期图法 P_periodogram = abs(fft(x)).^2 / N; f = (0:N-1) * fs / N; % Burg 法 AR 谱估计 order = 14; [a, e] = arburg(x, order); [H, f_ar] = freqz(sqrt(e), a, 1024, fs); P_ar = abs(H).^2; subplot(2,1,1); plot(f(1:N/2), 10*log10(P_periodogram(1:N/2))); title('周期图法功率谱'); subplot(2,1,2); plot(f_ar, 10*log10(P_ar)); title('Burg 法 AR 功率谱 (order=14)');

对比重点很明确:周期图法的谱线在 50 Hz 和 120 Hz 处有较宽的“裙边”,而且受噪声影响明显,底噪抬升;Burg 法在同样数据长度下能给出尖锐得多的谱峰,噪声基底平坦,两个频率分量可以被清晰地分辨。order=14是 AR 模型的阶数——阶数太低则分辨率不够,两个频率会糊成一个;阶数太高则会出现虚假谱峰。实际使用时我一般从N/20开始试,观察谱峰数量稳定后再收敛。

这段代码也侧面说明了一个重要事实:经典 FFT 谱估计的分辨率受数据长度限制,而现代谱估计方法能突破这个限制,在短数据场景下给出更好的结果。

4. 在 MATLAB 里跑通这套代码:环境配置与执行步骤

4.1 首次运行前的路径配置

拿到资源后,第一步不是双击.m文件,而是先把整个目录加进 MATLAB 路径,不然跨目录调用函数时会报Undefined function错误。操作方式有两种:

% 方式一:命令窗口执行,一次性生效 addpath(genpath('D:\DSP_HuGuangShu')); savepath; % 保存路径,下次启动自动加载 % 方式二:通过 MATLAB 菜单路径设置界面手动添加 % 主页 -> 设置路径 -> 添加并包含子文件夹

genpath会把所有子文件夹递归加入路径,这一步很重要,因为CH5_FIR里的代码可能调用了CH3目录下的工具函数,不递归加路径就找不到。savepath的作用是把当前路径配置写进用户路径文件,避免每次重启 MATLAB 都要重新 addpath。

4.2 按章节逐个运行的建议顺序

不建议跳着运行。这套代码是按教材章节设计的,前面的脚本产生的变量可能被后面的脚本复用(虽然绝大多数脚本是独立的),更重要的是,顺着书的结构跑能让你同时建立“理论公式 ↔ 代码实现 ↔ 图形结果”的三向对应关系。

推荐顺序是:

% 建议从第 2 章开始逐个运行 run('CH2_离散时间信号与系统/ex_2_1_单位脉冲响应.m'); run('CH2_离散时间信号与系统/ex_2_2_线性卷积实现.m'); run('CH3_傅里叶变换与DFT/ex_3_4_DFT计算圆周卷积.m'); % 以此类推,按章节递增顺序

每个脚本运行后,注意看命令窗口打印的数字输出和弹出的图形窗口。很多配套脚本里都嵌了fprintf输出关键数值(如误差、增益、阶数),这些数值就是教材里表格的复现,值得记录下来跟书上的表对照。

4.3 常见运行报错的快速定位法

脚本运行报错时,先看错误信息出在哪个文件哪一行,再按这个优先级排查:第一步检查路径,第二步检查函数是否存在,第三步检查参数维度是否匹配。这里给出一个通用的排查段落:

% 在命令窗口里先验证关键函数是否可调用 which fir1 % 若提示 not found,说明工具箱未安装或路径未配置 which audioread % 文件读取函数是否在当前版本可用

如果which输出not found,不要急着怀疑资源有问题,大概率是环境缺东西。早期 MATLAB 脚本里常见的wavread,在 2016a 之后的版本已经被audioread取代,遇到Undefined function 'wavread'就直接全局替换成audioread即可。另外,有些脚本开头写了clear all,如果你的工作区里有正在用的变量,会被一并清掉,跑之前最好确认当前工作区没有需要保留的数据。

5. 避坑与排查:我在这套代码上踩过的五个典型坑

5.1sinc函数让设计结果全错

现象:窗函数法设计的 FIR 滤波器,通带纹波极大,幅频响应长得完全不像预期。原因:把sinc当成普通sin(x)/x直接用了,忽略了 MATLAB 内置sinc是归一化版本。解决:检查代码里的sinc调用,确认参数是否带了pi因子;如果自己写sin(x)./x,要先处理x=0处的除零问题,补x==0时值为 1 的特殊分支。

5.2 旧版wavread导致的文件读取失败

现象:运行语音相关脚本时报Undefined function 'wavread'。原因:MATLAB R2016a 之后的版本移除了wavread,改用audioread。解决:全局搜索替换wavread为audioread,替换后注意返回值从[y, Fs, bits]变为[y, Fs],原来读bits的地方要删掉或改为audiowrite时的BitsPerSample配置。

5.3 Cross-directory 函数调用失败

现象:在CH5目录下直接运行脚本,报找不到CH3目录下的某个工具函数。原因:没有把整个资源根目录加进 MATLAB 路径,脚本内部用了相对路径调用函数。解决:对资源根目录执行addpath(genpath('根目录')),再执行savepath保存。如果不想污染全局路径,可以用run命令配合cd切换,但强烈建议直接加路径,省事且稳定。

5.4 频谱横坐标不对导致图与书对不上

现象:画出来的频谱图横坐标是“样本点”而不是“Hz”,跟书上的图对不上。原因:直接用plot(abs(fft(x)))而没有构建频率轴。解决:按f = (0:N-1) * fs / N构建频率轴再绘图;用freqz时记得传采样率参数作为第四个参数,否则默认横坐标是归一化频率(0 到 1 或 0 到 pi),需要用freqz(b, a, N, fs)转成 Hz。

5.5 FFT 结果镜像没有取半边,画出对称双峰

现象:功率谱图左右对称,看起来是两个峰。原因:FFT 结果对 N/2 对称,直接画全谱就会得到镜像。解决:画单边谱,即只取1:N/2+1部分,幅度乘 2(直流和 Nyquist 点除外)。配套代码里部分脚本已经做了这步,但你自己改脚本时很容易忽略,这是一个高频翻车点。

6. 二次开发:把这套代码改造成你自己的信号处理工具链

6.1 把脚本封装成函数库

原资源以脚本为主,直接拿来跑没问题,但想在自己的项目里复用就要花点功夫。最常见的改造思路是把脚本核心逻辑提取成函数,输入输出接口化:

function [h_fir, f_response] = design_lowpass_fir(fs, fc, N, win_type) % 设计低通 FIR 滤波器并返回频率响应 % 输入: fs - 采样率, fc - 截止频率, N - 阶数, win_type - 窗口类型字符串 % 输出: h_fir - 滤波器系数, f_response - 频率响应曲线 n = -(N-1)/2 : (N-1)/2; h_ideal = 2 * fc / fs * sinc(2 * fc * n / fs); switch lower(win_type) case 'hamming' w = hamming(N)'; case 'hanning' w = hanning(N)'; case 'blackman' w = blackman(N)'; otherwise w = rectwin(N)'; end h_fir = h_ideal .* w; [H, f_response] = freqz(h_fir, 1, 1024, fs); end

这样封装之后,你可以把这个函数放进自己的工具包,在批处理脚本里循环调用,批量设计不同规格的滤波器。相比直接改原脚本,函数化改造带来的最大收益是:参数显式化,不再需要去脚本头部逐行看变量;可测试性增强,每个函数都可以单独写测试用例验证。

6.2 同样的设计理念迁移到实际工程数据

这套代码教的方法论,迁移到实际场景时只需要改数据入口。比如你在处理某传感器采集的振动信号,采样率可能是 5120 Hz,关心的频带可能在 0.5~2 kHz,这时候拿ex_5_2改两个参数(fs改成 5120,fc改成你需要的截止频率)就能直接得到对应滤波器。我一般会把原代码保留一份不动,另存为custom_xxx.m再做修改,这样随时能回头对照原版实现。

改造后的完整流程通常是这样:读取原始数据(audioread或load)→ 按需设计滤波器 →filter做滤波 →pwelch或burg做谱分析 → 绘图保存。这套流程跑顺了,你对这份资源的理解就不再是“跟着书做实验”,而是真正把它当成自己的信号处理工具箱。从那以后我每次拿到任何教材配套代码,都会先跑通原始版本、再做一次函数化封装、最后替换成自己的数据验证一遍,三步走下来才算真正吸收了这套代码。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询