function plot_spectrum(signal, fs, title_str)
L = length(signal); % 信号长度
Y = fft(signal); % 计算FFT
P2 = abs(Y/L); % 计算双侧频谱
P1 = P2(1:L/2+1); % 计算单侧频谱
P1(2:end-1) = 2*P1(2:end-1); % 保留单侧频谱
f = fs*(0:(L/2))/L; % 频率轴
plot(f, P1); % 绘制频谱
title(title_str);
xlabel('频率(Hz)');
ylabel('幅度');
end
封装了一个求信号频谱的函数。
P2为什么要Y/L呢,ai是这样说的,傅里叶变换得到的频域数据跟信号长度有关。L是信号的长度,除以L可以得到归一化的振幅值,这使得计算得到的频谱与实际幅度相关。(此处FFT算法需要复习一下,不是很理解这句话)。
abs函数就是求幅值不多说了。
傅里叶变换后的频谱是双边的,对称的,因此只需要保留一半频谱,所以要保留P2一半的频谱1:L/2+1;
P1(2:end-1) = 2*P1(2:end-1)
- 幅值调整:由于我们只保留了单侧频谱,但原始信号的频谱是对称的,所以必须将单侧频谱的幅值乘以 2,确保能量保持一致性。
P1(2:end-1)
:这里- 的
2:end-1
表示除了直流分量(频率为0的分量)外的其他频率成分。直流分量不需要乘以 2,因为它是唯一不对称的点。
频率轴那行代码,fs是采样频率,(0:L/2)是频率索引,代表0~L/2的频率点,然后再乘一个间隔公式δ=fs/L得到横坐标频率的索引。为什么是fs/L?因为时域有L个点,傅里叶后频域也有L个点,间隔公式是最小频率分辨率计算公式。举个例子:
假设你有一个信号,由两个正弦波组成,一个是 50 Hz,一个是 52 Hz。如果你采样 1000 个点,采样频率为 1000 Hz,那么 fs/L = 1 Hz
,这意味着你在频域中能区分的最小频率间隔就是 1 Hz。
如果这两个信号的频率差更小,比如 50 Hz 和 50.5 Hz,由于频域的分辨率是 1 Hz,你将无法清楚地区分这两个信号。为了更精细地区分这两个频率,你需要增加采样点数 L
。假设增加到 2000 个点,那么 Δf = fs / L = 1000 / 2000 = 0.5 Hz
,这样你就能分辨 50 Hz 和 50.5 Hz 之间的差异。