我需要做这个MATLAB任务,到目前为止,我已经生成了一个由三个频率值的余弦函数组成的混合信号:86Hz,159Hz和392Hz。我使用了1Khz的采样频率并生成了一秒钟的数据。然后,我创建了该代码的离散傅立叶变换,它显示了(我认为FT的目的是)复合信号中最常出现的频率。代码和生成的图如下所示:
fs = 1000;
t = (0 : 1/fs : 1)';
f = [ 86, 159, 392 ];
x = sum( cos(2*pi * t * f), 2 );
y = fft(x);
subplot(2,1,1), plot(t,x);
subplot(2,1,2), stem(y,t);
(为了方便起见,我将省略轴和情节标题的代码)。以下是我迄今为止为报告生成的两个数字。
报告的下一部分问我:
“如果我们只考虑感兴趣的中频,找到LTI系统的频率响应,使用傅立叶变换滤除更高和更低的频率”
我需要知道是否有某种方法可以在MATLAB中自动完成。我不确定如何创建这个带通滤波器,然后手动找到它的频率响应,所以我想知道是否有某种方式MATLAB会自动执行它,因为我可能会过度思考它。
答案 0 :(得分:1)
中"频率,我假设您要滤除信号,以便只存在159 Hz的分量。这意味着输出结果应该只包含一个159 Hz的正弦曲线。首先,我想提一下傅立叶变换显示信号的频率分解。您可以将信号视为不同频率下许多正弦曲线的总和,并且它们可能会因不同的相移而异相。对于频率上的每个正弦曲线,存在幅度分量和相分量。幅度告诉你正弦波在这个频率上有多大,相位告诉你正弦波正在经历多少延迟。
现在,在计算FFT时,这会执行Cooley-Tukey算法,这是计算傅里叶变换的一种非常有效的方法。它以这样的方式计算:信号的前半部分显示来自0 <= f < fn
的频谱,信号的后半部分显示来自-fn <= f < 0
的频谱。 fn
就是所谓的Nyquist frequency,它是采样频率的一半。注意与上半部分相比,前半部分范围内的排他性。我们不在上半部分包含fn
,但我们在下半部分包含-fn
。
这仅适用于真实信号,这是三个正弦曲线相加的情况。为了使这更有意义,您可以使用fftshift
,以便重新组织频谱,使0 Hz / DC频率出现在中间,这样您就可以绘制频谱,使其成为&#39 ;在-fn <= f < fn
之间。
此外,信号的绘制方式是标准化,这意味着您可以看到频率实际上在-1 <= f <= 1
之间。要将其转换为实际频率,请使用以下关系:
freq = i * fs / N
i
是您想要的FFT上的bin编号或点,在您的情况下从0到500.请记住,信号的前半部分表示从0到{{的频率分布。信号中的1}}和0到500就是你需要的。只需用fn
代替i
即可找到负频率。 -i
是采样频率,fs
是FFT的大小,在您的情况下是信号的总长度,1001。要生成与正确点相对应的正确频率,您可以使用linspace
并在N
和N+1
之间生成-fn
点以确保间距正确,但因为我们不包括{ {1}}在正端,我们从范围的末尾删除它。
另外,要绘制信号的幅度和相位,请分别使用abs
和angle
。
因此,尝试以这种方式绘制它,并且也关注幅度和相位。只是绘制光谱是不确定的,因为通常有实部和虚部。
fn
这是我得到的:
如您所见,有三个尖峰对应于三个正弦分量。你想要中间的那个,那个是159赫兹。要创建带通滤波器,您需要滤除除+/- 159 Hz之外的所有组件。如果您想自动执行此操作,您可以找到最接近+/- 159 Hz的bin位置,然后展开围绕这两个点的邻域并确保它们不受影响而将其余部分归零组件。
因为你有精确的正弦曲线,所以在这方面使用带通滤波器是完全可以接受的。一般情况下,由于振铃和混叠效应,您不会这样做,因为以这种方式来自带通滤波器的截止的锐度会在时域中引入不需要的混叠效应。请参阅Wikipedia article on aliasing for more details。
因此,要找出我们需要过滤的位置,请尝试使用min
并找出上面生成的频率与159 Hz之间的绝对差异 - 特别是找到位置。一旦你找到+159 Hz和-159 Hz的这些,在这些点附近扩展一个邻域,确保它们不受影响,而其余的点在频谱中设置为0:
fn
%// Your code
fs = 1000;
t = (0 : 1/fs : 1)';
f = [ 86, 159, 392 ];
x = sum( cos(2*pi * t * f), 2 );
y = fft(x);
%// New code
%// Shift spectrum
ys = fftshift(y);
N = numel(x); %// Determine number of points
mag = abs(ys); %// Magnitude
pha = angle(ys); %// Phase
%// Generate frequencies
freq = linspace(-fs/2, fs/2, N+1);
freq(end) = [];
%// Draw stuff
figure;
subplot(2,1,1);
plot(freq, mag);
xlabel('Frequency (Hz)');
ylabel('Magnitude');
subplot(2,1,2);
plot(freq, pha);
xlabel('Frequency (Hz)');
ylabel('Phase (radians)');
现在包含滤除的信号,除去159 Hz之外的所有分量。如果您想显示此滤波信号的幅度和相位,我们可以这样做:
[~,min_pt_pos] = min(abs(freq - f(2))); %// Find location where +159 Hz is located
[~,min_pt_neg] = min(abs(freq + f(2))); %// Find location of where -159 Hz is
%// Neighbourhood size
ns = 100; %// Play with this parameter
%// Filtered signal
yfilt = zeros(1,numel(y));
%// Extract out the positive and negative frequencies centered at
%// 159 Hz
yfilt(min_pt_pos-ns/2 : min_pt_pos+ns/2) = ys(min_pt_pos-ns/2 : min_pt_pos+ns/2);
yfilt(min_pt_neg-ns/2 : min_pt_neg+ns/2) = ys(min_pt_neg-ns/2 : min_pt_neg+ns/2);
这是我们得到的:
正如我们所料,只有一个强频率可以分解这个信号,而这个频率分别为159 Hz。现在要重建此信号,您必须撤消我们所做的居中,并且您必须对此过滤结果使用ifftshift
,然后执行反向过滤ifft
。您也可能获得较小的残余虚部,因此在输出结果上使用real
是个好主意。
yfilt
如果我们绘制这个,我们得到:
mag2 = abs(yfilt);
pha2 = phase(yfilt);
figure;
subplot(2,1,1);
plot(freq, mag2);
xlabel('Frequency (Hz)');
ylabel('Magnitude');
subplot(2,1,2);
plot(freq, pha2);
xlabel('Frequency (Hz)');
ylabel('Phase (radians)');
正如您所看到的,有一个具有一个频率的正弦曲线,其频率为159 Hz。但不要介意振幅。这仅仅是由于您选择绘制信号的点数不精确,因此某些时间点可能与信号的真实峰值不完全一致。请记住,如果存在多个正弦波,则会出现所有正弦曲线的峰值相交的点,并且您将获得更高的峰值,而不是单个正弦曲线提供的峰值1。因为振幅在-1到1左右徘徊,你可以确定只有一个正弦曲线存在。如果您要选择更精细的颗粒步长,并因此在FFT中选择更多的点,则可以避免这种情况。
希望这足以让你入门。祝你好运!