信号分析与处理实验:Matlab波形到频域分析的完整实操记录
简介:这份资源是南京邮电大学「信号分析与处理实验」课程的完整实验报告,面向正在修读数字信号处理、信号与系统相关课程的高校学生,以及需要借助 MATLAB 完成信号处理实验与课程设计的自学者。报告围绕信号的产生和运算、连续时间信号的频域分析、信号的时域采样和重建、离散傅里叶变换与应用等核心实验展开,并延伸至连续时间系统的频域与复频域分析、数字滤波器设计及信号处理综合应用,配有可运行的 MATLAB 程序示例与图形结果,便于对照理解时域与频域特性。资源包共 1 个 docx 文件,约 1.02MB,内容涵盖实验目的、实验内容、程序代码与结果分析,结构完整、条理清晰。目前已有 952 人学习下载,适合作为实验预习、报告撰写参考与 DSP 入门练习的实用材料。
1. 信号分析与处理实验:从Matlab波形到频域分析的完整实操记录
很多同学学完《信号与系统》理论课,面对傅里叶级数、采样定理、DFT这些概念还是一头雾水,公式推得再熟,一到实际信号上就不知道从哪下手。这份实验报告记录的就是把抽象公式变成Matlab波形的过程——从最基础的阶跃序列、复指数序列生成,到周期矩形脉冲的频谱分析,再到时域采样与信号重建,最后落到数字滤波器设计。四个实验层层递进,每个都配有可直接运行的Matlab代码和结果分析。如果你正在学数字信号处理(DSP)这门课,或者需要一份能照着跑通的实验参考,这份材料能帮你把理论和代码之间的那层窗户纸捅破。
2. 实验环境与项目结构:先搞清楚手里有什么
2.1 运行环境与依赖
这套实验全部基于Matlab完成,不依赖任何额外工具箱。我实测在Matlab R2018b及以上版本都能直接跑,低版本理论上也没问题,因为用到的都是基础函数:stem、plot、subplot、conv、fft、sinc、filter这些。不需要Simulink,不需要DSP System Toolbox,纯脚本就能搞定。
如果你用的是Octave,大部分代码也能跑,但freqz和butter这类滤波器设计函数可能需要安装signal包。我一般建议直接用Matlab,省得折腾兼容性问题。
2.2 实验模块划分
整份报告按实验序号组织,每个实验对应一个独立文件夹,里面包含脚本文件和结果图。目录结构大致如下:
信号分析与处理实验/ ├── 实验一_信号的产生和运算/ │ ├── P1_1.m % 单位阶跃序列 │ ├── P1_2.m % 复指数序列 │ ├── P1_3.m % 实指数序列 │ ├── P1_4.m % 正弦序列 │ ├── P1_5.m % 序列加乘运算 │ ├── P1_6.m % 奇偶分量分解 │ └── P1_7.m % 线性卷积 ├── 实验二_连续时间信号频域分析/ │ ├── P2_1.m % 周期矩形脉冲傅里叶级数 │ ├── P2_2.m % 谐波叠加 │ └── P2_4.m % 吉布斯现象过冲计算 ├── 实验三_时域采样与重建/ │ ├── P3_1.m % 采样与频谱分析 │ └── P3_2.m % 信号重建 └── 实验四_离散傅里叶变换与应用/ ├── P4_1.m % DFT实现 └── P4_2.m % 滤波器设计每个脚本都是独立可运行的,变量命名清晰,没有跨文件依赖。这种组织方式对新手很友好——你不需要理解整个工程,打开哪个文件就跑哪个。
2.3 实验报告的核心产出
每个实验最终要交付三样东西:一是可运行的Matlab代码,二是波形图或频谱图,三是对结果的分析说明。报告里对每个问题的分析都写得比较具体,比如采样定理那部分,不仅画出了混叠前后的频谱对比,还解释了为什么Ts=2时会发生混叠而Ts=pi/2时不会。这种“代码+图+解释”的结构,拿来当实验模板或者自学材料都合适。
3. 核心实验拆解:从信号生成到频域分析的代码实现
3.1 离散序列的生成与基本运算
实验一的核心是让你熟悉Matlab里怎么表示离散时间信号。离散序列和连续信号最大的区别在于,你得同时定义时间轴向量和幅度向量。比如要画单位阶跃序列u[n],代码是这样的:
% 产生单位阶跃序列 u[n] 和 u[n+7] clear; close all; n = -10:20; % 时间轴范围 x = [zeros(1,10) 1 ones(1,20)]; % u[n]: n<0时为0, n>=0时为1 subplot(1,2,1); stem(n, x); xlabel('n'); ylabel('u[n]'); title('单位阶跃序列 u[n]'); M = 7; % 移位量 xd = [zeros(1,10+M) 1 ones(1,20-M)]; % u[n+7]: 左移7位 subplot(1,2,2); stem(n, xd); xlabel('n'); ylabel('u[n+7]'); title('移位后的阶跃序列 u[n+7]');这里的关键点在于zeros和ones的拼接方式。n=-10:20一共31个点,zeros(1,10)对应n=-10到n=-1,1对应n=0,ones(1,20)对应n=1到n=20。移位操作u[n+7]本质上是把序列左移7位,所以前面补7个零,后面少7个一。这个逻辑搞清楚了,后面所有序列的移位操作都是一个套路。
复指数序列稍微绕一点,因为涉及复数运算:
% 复指数序列 x[n] = 1.5 * exp((0.2 + j3)n), 0 <= n <= 15 clear; close all; c = 0.2 + 3*1i; % 复数指数系数 K = 1.5; % 幅度系数 n = 0:15; x = K * exp(c * n); % 生成复指数序列 subplot(1,2,1); stem(n, real(x)); % 取实部 xlabel('n'); ylabel('幅度'); title('实部'); subplot(1,2,2); stem(n, imag(x)); % 取虚部 xlabel('n'); ylabel('幅度'); title('虚部');real(x)和imag(x)分别提取实部和虚部。复指数序列在DSP里非常重要,因为它是DFT的基函数。这里c = 0.2 + 3i,实部0.2控制包络的衰减或增长,虚部3控制振荡频率。你可以试着改一下c的值,观察波形怎么变——比如把虚部改成0.5,振荡会明显变慢。
3.2 序列的卷积与奇偶分解
卷积是信号处理里最核心的运算之一。实验一里用conv函数计算了两个有限长序列的线性卷积:
% 计算 x[n] 和 h[n] 的线性卷积 clear; close all; nx = 0:14; x = (-1).^nx; % x[n] = (-1)^n, 0<=n<=14 nh = 0:7; w = 0.25 * pi; h = cos(w * nh); % h[n] = cos(0.25*pi*n), 0<=n<=7 y = conv(x, h); % 线性卷积 n = 0:21; % 卷积结果长度 = 15 + 8 - 1 = 22 subplot(3,1,1); stem(nx, x); title('x[n]'); subplot(3,1,2); stem(nh, h); title('h[n]'); subplot(3,1,3); stem(n, y); title('y[n] = x[n] * h[n]');conv函数的输出长度是length(x) + length(h) - 1,这里是15+8-1=22个点。注意时间轴n要重新定义,不能直接用nx或nh。这个细节很容易翻车——我见过不少同学画卷积结果时时间轴对不上,波形整体偏移了。
奇偶分解的代码也值得看一下:
% 序列的奇偶分量分解 clear; close all; n = -2:2; x = [1 2 4 -5 -2]; y = fliplr(x); % 时间反转 xe = 0.5 * (x + y); % 偶部 xo = 0.5 * (x - y); % 奇部 subplot(3,1,1); stem(n, x); title('x[n]'); subplot(3,1,2); stem(n, xe); title('偶部 x_e[n]'); subplot(3,1,3); stem(n, xo); title('奇部 x_o[n]');fliplr是左右翻转,对应时间反转x[-n]。偶部是(x[n]+x[-n])/2,奇部是(x[n]-x[-n])/2。这个分解在分析系统对称性时很有用,比如实偶信号的傅里叶变换也是实偶函数。
3.3 周期矩形脉冲的傅里叶级数与吉布斯现象
实验二的重头戏是周期矩形脉冲的频谱分析。指数形式的傅里叶级数系数公式是:
$$c_k = \frac{A\tau}{T} \cdot \text{sinc}\left(\frac{k\Omega_0\tau}{2\pi}\right)$$
代码实现时用sinc函数直接算:
% 周期矩形脉冲的傅里叶级数系数 A = 1; tau = 0.1; T = 0.5; Omega0 = 2*pi/T; k = -20:20; ck = A*tau/T * sinc(k*Omega0*tau/(2*pi)); subplot(2,1,1); stem(k, abs(ck)); title('双边幅度谱'); subplot(2,1,2); stem(k, angle(ck)); title('相位谱');这里k取-20到20,覆盖了主要谐波分量。sinc函数在Matlab里定义为sin(pi*x)/(pi*x),和信号处理里的归一化sinc一致。如果你把T从0.5改成1或2,会发现谱线间隔变密、幅度降低——这正是报告里问题2.2要你观察的现象。
吉布斯现象的计算用了一个循环来累加谐波:
function p2_4(N) t = -2:1e-4:2; A = 1; tau = 1; T = 2; Omega0 = 2*pi/T; c0 = A*tau/T; f_N = c0 * ones(1, length(t)); for k = 1:N f_N = f_N + 2*A*tau/T * sinc(k*Omega0*tau/(2*pi)) * cos(k*Omega0*t); end overshoot = max(f_N); fprintf('The maximum overshoot is %.4f\n', overshoot); end当N增大时,过冲峰值趋近于一个常数,大约在跳变幅度的9%左右。这个函数直接返回过冲最大值,方便你验证不同N下的收敛情况。
3.4 采样定理验证与信号重建
实验三用升余弦脉冲信号来验证采样定理。核心逻辑是:先定义连续信号的频谱,然后以不同采样间隔Ts采样,观察频谱是否混叠。
% 采样与频谱分析 Ts = pi/2; % 采样间隔 t = -10:Ts:10; % 升余弦脉冲信号生成 x = ...; % 具体表达式见报告 % 计算并绘制频谱 X = fftshift(fft(x)); f = (-length(x)/2:length(x)/2-1) / (length(x)*Ts); plot(f, abs(X));当Ts = pi/2时,采样频率满足奈奎斯特条件,频谱没有混叠;当Ts = 2时,采样频率低于两倍截止频率,频谱出现混叠。报告里对这两个情况的对比图和分析都很清楚。
信号重建部分用低通滤波器从采样信号中恢复原信号:
% 信号重建 Wc = 2; % 低通滤波器截止频率 % 设计理想低通滤波器 h = Wc/pi * sinc(Wc/pi * (n - delay)); % 卷积实现重建 xr = conv(xs, h);重建误差的大小直接反映了采样定理是否被满足。Ts=2时误差明显增大,因为混叠导致高频分量无法恢复。
4. 避坑与常见问题:跑代码时容易翻车的几个地方
4.1 时间轴对不上导致波形偏移
现象:画卷积结果或移位序列时,波形整体左移或右移,和理论位置对不上。
原因:stem或plot的横轴向量和信号向量长度不一致,或者移位操作后没有重新定义时间轴。
解决:每次生成新序列后,先确认length(n) == length(x)。卷积后用n = 0:length(y)-1重新定义时间轴。移位操作时,时间轴不变,只改幅度向量的补零位置。
4.2 复数序列取实部虚部时忘记用real/imag
现象:stem画复指数序列时报错或画出奇怪的结果。
原因:Matlab的stem不能直接画复数,必须显式取实部或虚部。
解决:用real(x)和imag(x)分别提取。如果想画幅度和相位,用abs(x)和angle(x)。
4.3 采样频率设置不当导致频谱混叠
现象:频谱图上出现明显的频率折叠,高频分量跑到低频区域。
原因:采样间隔Ts大于奈奎斯特间隔,不满足采样定理。
解决:先计算信号的最高频率fm,然后确保Ts < 1/(2*fm)。报告里升余弦脉冲的截止频率是Wm=2,所以奈奎斯特间隔是pi/2。如果你不确定信号的带宽,先把Ts设小一点,逐步增大观察频谱变化。
4.4 fft后的频率轴计算错误
现象:频谱图的横轴频率值不对,峰值位置和理论值偏差很大。
原因:fft输出的频率分辨率是fs/N,其中fs=1/Ts,N是采样点数。频率轴应该从-fs/2到fs/2。
解决:用f = (-N/2:N/2-1) * (fs/N)计算频率轴,配合fftshift使用。如果只画正频率部分,取前N/2+1个点,频率轴从0到fs/2。
4.5 滤波器设计时阶数选择不当
现象:重建信号要么过渡带太宽,要么通带纹波太大。
原因:滤波器阶数太低导致过渡带不够陡峭,阶数太高又可能引入数值不稳定。
解决:先用buttord或cheb1ord估算所需阶数,再用butter或cheby1设计。对于理想低通重建,直接用sinc函数生成冲激响应,但要注意截断长度——截断太短会导致吉布斯现象,太长会增加计算量。
5. 二次开发与扩展:把这套代码改成自己的工具
5.1 封装成可复用的函数
原始代码都是脚本形式,变量直接暴露在工作区。如果你想反复使用某个功能,比如生成任意参数的复指数序列,可以把它封装成函数:
function [n, x] = gen_complex_exp(K, sigma, omega, n_start, n_end) % 生成复指数序列 x[n] = K * exp((sigma + j*omega)n) % 输入: % K - 幅度系数 % sigma - 实部衰减/增长因子 % omega - 虚部角频率 % n_start - 起始索引 % n_end - 结束索引 % 输出: % n - 时间轴向量 % x - 复指数序列 n = n_start:n_end; x = K * exp((sigma + 1i*omega) * n); end调用时只需要一行:[n, x] = gen_complex_exp(1.5, 0.2, 3, 0, 15);。这样你就不用每次改参数都去翻脚本了。
5.2 用App Designer做个交互界面
如果你想让不懂Matlab的人也能玩这些实验,可以用App Designer搭个简单界面。拖几个滑块控制幅度、频率、采样间隔,实时刷新波形图。核心逻辑还是调用上面那些函数,只是把输入从命令行改成GUI控件。
我一般会保留一个“导出数据”按钮,把当前生成的信号和频谱存成.mat文件,方便后续分析。这个功能在写论文或做项目时特别实用——你不需要重新跑一遍代码,直接加载数据就行。
5.3 扩展到实际信号处理
这套实验用的是理想信号,实际工程中你面对的是带噪声的音频、传感器数据或者通信信号。把实验里的采样、频谱分析、滤波这套流程套上去,基本框架不用大改,只需要注意几点:
- 实际信号的频率范围未知,先做频谱预览再定采样率
- 噪声会让频谱变得毛糙,需要加窗函数(如
hamming、blackman)抑制泄漏 - 滤波器设计要考虑实时性,IIR滤波器阶数低但相位非线性,FIR滤波器线性相位但阶数高
5.4 验证方法
改完代码后怎么确认没跑偏?我的习惯是拿一个已知答案的简单信号做回归测试。比如生成一个x[n] = cos(0.1*pi*n)的序列,理论上它的DFT应该在k=0.1*N处有一个峰值。跑一遍代码,看峰值位置对不对。如果对得上,说明整个流程没问题;如果对不上,就从采样、加窗、FFT逐段排查。
从那以后我每次改完信号处理代码,都强制走一遍这个“已知信号验证”流程。看起来多花了几分钟,但省下了后面调试玄学问题的大把时间。希望这份实验记录能帮你少走点弯路,顺利把信号分析与处理的实验跑通。
本文还有配套的精品资源,点击获取