Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/matlab/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Matlab 如何找到周期性声音信号的频率?_Matlab_Audio_Signal Processing_Fft_Frequency - Fatal编程技术网

Matlab 如何找到周期性声音信号的频率?

Matlab 如何找到周期性声音信号的频率?,matlab,audio,signal-processing,fft,frequency,Matlab,Audio,Signal Processing,Fft,Frequency,我正在研究行走模式的声音信号,它有明显的规律: 然后我想我可以使用FFT函数得到行走的频率(从图像中约1.7Hz): x = walk_5; % Walking sound with a size of 711680x2 double Fs = 48000; % sound frquency L=length(x); t=(1:L)/Fs; %time base plot(t,x); figure; NFFT=2^nextpow2(

我正在研究行走模式的声音信号,它有明显的规律:

然后我想我可以使用FFT函数得到行走的频率(从图像中约1.7Hz):

    x = walk_5; % Walking sound with a size of 711680x2 double
    Fs = 48000; % sound frquency
    L=length(x); 

    t=(1:L)/Fs; %time base
    plot(t,x);
    figure;

    NFFT=2^nextpow2(L);      
    X=fft(x,NFFT);       
    Px=X.*conj(X)/(NFFT*L); %Power of each freq components       
    fVals=Fs*(0:NFFT/2-1)/NFFT;      
    plot(fVals,Px(1:NFFT/2),'b','LineSmoothing','on','LineWidth',1);         
    title('One Sided Power Spectral Density');       
    xlabel('Frequency (Hz)')         
    ylabel('PSD');
但它并没有给我我所期望的:

FFT结果:

缩放图像有很多噪音:

在1.7Hz附近没有任何信息

下面是日志域中使用

    semilogy(fVals,Px(1:NFFT));
但它是相当对称的:

我找不到我的代码有任何错误。您是否有任何解决方案可以轻松地从行走模式中提取1.7Hz

这里是mat中音频文件的链接

多谢各位


Kai

我建议您忘记DFT方法,因为由于许多原因,您的信号不适合这种类型的分析。即使通过查看您感兴趣的频率范围内的频谱,也没有简单的方法来估计峰值:

当然,您可以尝试PSD/STFT和其他时髦的方法,但这是一种过分的做法。对于这项任务,我可以想出两种相当简单的方法


第一种是基于自相关函数的

  • 计算ACF
  • 定义它们之间的最小距离。因为您知道预期频率约为1.7Hz,所以它对应于0.58s。让我们把最小距离设为0.5s
  • 计算发现的峰值之间的平均距离
  • 这给了我大约1.72赫兹的频率


    第二种方法是基于对您的信号已经有一些周期性峰值的观察。因此,我们可以使用
    findpeaks
    函数简单地搜索它们

  • 以与前面相同的方式定义最小峰值距离
  • 定义最小峰值高度。例如,最大峰值的10%
  • 求平均差
  • 这给了我1.7赫兹的平均频率

    简单快速的方法。显然有一些地方可以改进,例如:

    • 精炼阈值
    • 找到正负峰
    • 注意一些缺失的峰值,即由于低振幅
    无论如何,这应该让你开始,而不是停留在蹩脚的FFT和懒惰的半对数X


    代码段:

    load walk_sound
    
    fs = 48000;
    dt = 1/fs;
    
    x = walk_5(:,1);
    x = x - mean(x);
    N = length(x);
    t = 0:dt:(N-1)*dt;
    
    % FFT based
    win = hamming(N);
    X = abs(fft(x.*win));
    X = 2*X(1:N/2+1)/sum(win);
    X = 20*log10(X/max(abs(X)));
    f = 0:fs/N:fs/2;
    
    subplot(2,1,1)
    plot(t, x)
    grid on
    xlabel('t [s]')
    ylabel('A')
    title('Time domain signal')
    
    subplot(2,1,2)
    plot(f, X)
    grid on
    xlabel('f [Hz]')
    ylabel('A [dB]')
    title('Signal Spectrum')
    
    % Autocorrelation
    [ac, lag] = xcorr(x);
    min_dist = ceil(0.5*fs);
    [pks, loc] = findpeaks(ac, 'MinPeakDistance', min_dist);
    
    % Average distance/frequency
    avg_dt = mean(gradient(loc))*dt;
    avg_f = 1/avg_dt;
    
    figure
    plot(lag*dt, ac);
    hold on
    grid on
    plot(lag(loc)*dt, pks, 'xr')
    title(sprintf('ACF - Average frequency: %.2f Hz', avg_f))
    
    
    % Simple peak finding in time domain
    [pkst, loct] = findpeaks(x, 'MinPeakDistance', min_dist, ...
                                'MinPeakHeight', 0.1*max(x));
    
    avg_dt2 = mean(gradient(loct))*dt;
    avg_f2 = 1/avg_dt2;
    
    figure
    plot(t, x)
    grid on
    hold on
    plot(loct*dt, pkst, 'xr')
    xlabel('t [s]')
    ylabel('A')
    title(sprintf('Peak search in time domain - Average frequency: %.2f Hz', avg_f2))
    

    我建议您忘记DFT方法,因为由于许多原因,您的信号不适合这种类型的分析。即使通过查看您感兴趣的频率范围内的频谱,也没有简单的方法来估计峰值:

    当然,您可以尝试PSD/STFT和其他时髦的方法,但这是一种过分的做法。对于这项任务,我可以想出两种相当简单的方法


    第一种是基于自相关函数的

  • 计算ACF
  • 定义它们之间的最小距离。因为您知道预期频率约为1.7Hz,所以它对应于0.58s。让我们把最小距离设为0.5s
  • 计算发现的峰值之间的平均距离
  • 这给了我大约1.72赫兹的频率


    第二种方法是基于对您的信号已经有一些周期性峰值的观察。因此,我们可以使用
    findpeaks
    函数简单地搜索它们

  • 以与前面相同的方式定义最小峰值距离
  • 定义最小峰值高度。例如,最大峰值的10%
  • 求平均差
  • 这给了我1.7赫兹的平均频率

    简单快速的方法。显然有一些地方可以改进,例如:

    • 精炼阈值
    • 找到正负峰
    • 注意一些缺失的峰值,即由于低振幅
    无论如何,这应该让你开始,而不是停留在蹩脚的FFT和懒惰的半对数X


    代码段:

    load walk_sound
    
    fs = 48000;
    dt = 1/fs;
    
    x = walk_5(:,1);
    x = x - mean(x);
    N = length(x);
    t = 0:dt:(N-1)*dt;
    
    % FFT based
    win = hamming(N);
    X = abs(fft(x.*win));
    X = 2*X(1:N/2+1)/sum(win);
    X = 20*log10(X/max(abs(X)));
    f = 0:fs/N:fs/2;
    
    subplot(2,1,1)
    plot(t, x)
    grid on
    xlabel('t [s]')
    ylabel('A')
    title('Time domain signal')
    
    subplot(2,1,2)
    plot(f, X)
    grid on
    xlabel('f [Hz]')
    ylabel('A [dB]')
    title('Signal Spectrum')
    
    % Autocorrelation
    [ac, lag] = xcorr(x);
    min_dist = ceil(0.5*fs);
    [pks, loc] = findpeaks(ac, 'MinPeakDistance', min_dist);
    
    % Average distance/frequency
    avg_dt = mean(gradient(loc))*dt;
    avg_f = 1/avg_dt;
    
    figure
    plot(lag*dt, ac);
    hold on
    grid on
    plot(lag(loc)*dt, pks, 'xr')
    title(sprintf('ACF - Average frequency: %.2f Hz', avg_f))
    
    
    % Simple peak finding in time domain
    [pkst, loct] = findpeaks(x, 'MinPeakDistance', min_dist, ...
                                'MinPeakHeight', 0.1*max(x));
    
    avg_dt2 = mean(gradient(loct))*dt;
    avg_f2 = 1/avg_dt2;
    
    figure
    plot(t, x)
    grid on
    hold on
    plot(loct*dt, pkst, 'xr')
    xlabel('t [s]')
    ylabel('A')
    title(sprintf('Peak search in time domain - Average frequency: %.2f Hz', avg_f2))
    

    这里有一个很好的解决方案:

    在进行FFT之前,先获取原始数据的绝对值。数据中有大量的高频噪声,这些噪声淹没了信号中存在的任何低频周期。高频噪声的振幅每1.7秒就变大一次,振幅的增加对眼睛来说是可见的,而且是周期性的,但是当你用低频正弦波乘以信号,然后将所有信号相加时,你仍然会得到接近于零的结果。取绝对值会改变这一点,使这些振幅调制在低频下具有周期性

    尝试以下代码,将常规数据的FFT与abs(数据)的FFT进行比较。请注意,我对您的代码做了一些修改,例如将我假设的两个立体声通道组合成一个单声道

    x = (walk_5(:,1)+walk_5(:,2))/2; % Convert from sterio to mono
    Fs = 48000; % sampling frquency
    L=length(x); % length of sample
    fVals=(0:L-1)*(Fs/L); % frequency range for FFT
    
    walk5abs=abs(x); % Take the absolute value of the raw data
    
    Xold=abs(fft(x)); % FFT of the data (abs in Matlab takes complex magnitude)
    Xnew=abs(fft(walk5abs-mean(walk5abs))); % FFT of the absolute value of the data, with average value subtracted
    
    figure;
    plot(fVals,Xold/max(Xold),'r',fVals,Xnew/max(Xnew),'b')
    axis([0 10 0 1])
    legend('old method','new method')
    
    [~,maxInd]=max(Xnew); % Index of maximum value of FFT
    walkingFrequency=fVals(maxInd) % print max value
    
    绘制新方法和旧方法的FFT,从0到10 Hz得出:


    正如你所见,它在1.686赫兹左右检测到一个峰值,对于这个数据,这是FFT频谱中的最高峰值

    这里有一个很好的解决方案:

    在进行FFT之前,先获取原始数据的绝对值。数据中有大量的高频噪声,这些噪声淹没了信号中存在的任何低频周期。高频噪声的振幅每1.7秒就变大一次,振幅的增加对眼睛来说是可见的,而且是周期性的,但是当你用低频正弦波乘以信号,然后将所有信号相加时,你仍然会得到接近于零的结果。取绝对值会改变这一点,使这些振幅调制在低频下具有周期性

    尝试以下代码,将常规数据的FFT与abs(数据)的FFT进行比较。请注意,我对您的代码做了一些修改,例如将我假设的两个立体声通道组合成一个单声道

    x = (walk_5(:,1)+walk_5(:,2))/2; % Convert from sterio to mono
    Fs = 48000; % sampling frquency
    L=length(x); % length of sample
    fVals=(0:L-1)*(Fs/L); % frequency range for FFT
    
    walk5abs=abs(x); % Take the absolute value of the raw data
    
    Xold=abs(fft(x)); % FFT of the data (abs in Matlab takes complex magnitude)
    Xnew=abs(fft(walk5abs-mean(walk5abs))); % FFT of the absolute value of the data, with average value subtracted
    
    figure;
    plot(fVals,Xold/max(Xold),'r',fVals,Xnew/max(Xnew),'b')
    axis([0 10 0 1])
    legend('old method','new method')
    
    [~,maxInd]=max(Xnew); % Index of maximum value of FFT
    walkingFrequency=fVals(maxInd) % print max value
    
    绘制新方法和旧方法的FFT,从0到10 Hz得出: