Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting Peaks in a FFT Plot

I was wondering how is it possible to detect new peaks within an FFT plot in Python. let's say i have this simple Plot:enter image description here And i want to automatically measure the 'Similarity' or the Peaks location within a noisy Signal, i have tried to use the cosine Similarity but my real Signal is way too noisy, and with even if i add a new peak to the signal, i keep getting a Cosine of 0.9 since it's only one peak. This is an example of my real signal, and i also have the problem that my signal can be shiffted within the measures, so i can't get a stable frequency array they can be within a window of +/- 100 Hz : enter image description here This the code that used for the first Plot :

import numpy as np
from pylab import *
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y1 = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)+ 0.7*np.sin(30.0 * 2.0*np.pi*x)+ 0.5*np.sin(10.0 * 2.0*np.pi*x)
y2 = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)+ 0.2*np.sin(60.0 * 2.0*np.pi*x)+ 0.4*np.sin(40.0 * 2.0*np.pi*x)
yf1 = scipy.fftpack.fft(y1)
yf2 = scipy.fftpack.fft(y2)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)

fig, ax = plt.subplots()
plot(xf, 2.0/N * np.abs(yf1[:N/2]))
plot(xf, 2.0/N * np.abs(yf2[:N/2]))
xlabel('Freq (Hz)',fontsize=16,weight='bold')
ylabel('|Y(freq)|',fontsize=16,weight='bold')
ax = gca()
fontsize = 14
for tick in ax.xaxis.get_major_ticks():
    tick.label1.set_fontsize(fontsize)
    tick.label1.set_fontweight('bold')
for tick in ax.yaxis.get_major_ticks():
    tick.label1.set_fontsize(fontsize)
    tick.label1.set_fontweight('bold')
grid(True)
show()
def cosine_similarity(v1,v2):
    "compute cosine similarity of v1 to v2: (v1 dot v2)/{||v1||*||v2||)"
    sumxx, sumxy, sumyy = 0, 0, 0
    for i in range(len(v1)):
        x = v1[i]; y = v2[i]
        sumxx += x*x
        sumyy += y*y
        sumxy += x*y
    return sumxy/math.sqrt(sumxx*sumyy)

print 'Cosine Similarity', cosine_similarity(2.0/N * np.abs(yf1[:N/2]),2.0/N * np.abs(yf2[:N/2]))

I have also though of setting a threshold, but sometime the peaks within the real signal can be smaller than the pre-defined Threshold. Any ideas ?

like image 578
Mehdi Avatar asked Jun 09 '16 07:06

Mehdi


People also ask

How do you get to peaks in FFT?

Look for points where the value for the difference function goes from positive to negative. Those are your peak points. To find the most prominent peaks, compute the second order difference function at the points obtained from the first order difference and select the ones which are of highest magnitude.

How do you find peaks in FFT Matlab?

If you give your frequency vector as the second argument to findpeaks, the second output 'locs', will be in terms of frequency. See the documentation example Find Peaks with Minimum Separation (link) for details. [pks,frqs] = findpeaks(abs(X),freq);

What does peaks in FFT mean?

Accepted Answer The peaks are the frequencies at which the vibration amplitude is maximal. Here, they appear to be at about 900 Hz, and about 1200-1300 Hz.


1 Answers

There are many ways to find peaks, and even to interpolate their sub-sample location. Once you have the peaks, just check if you find a new one.

You can use the peakutils package to find the peaks. You can set there the threshold and minimum distance between peaks.

import numpy as np
from pylab import *
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y1 = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)+ 0.7*np.sin(30.0 * 2.0*np.pi*x)+ 0.5*np.sin(10.0 * 2.0*np.pi*x)
y2 = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)+ 0.2*np.sin(60.0 * 2.0*np.pi*x)+ 0.4*np.sin(40.0 * 2.0*np.pi*x)
yf1 = scipy.fftpack.fft(y1)
yf2 = scipy.fftpack.fft(y2)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)

v1 = 2.0/N * np.abs(yf1[:N/2])
v2 = 2.0/N * np.abs(yf2[:N/2])

# Find peaks
import peakutils
peaks_ind1 = peakutils.indexes(v1, thres=0.2, min_dist=5)
peaks_ind2 = peakutils.indexes(v2, thres=0.2, min_dist=5)

dist_th_for_new_peaks = 3
new_peaks = []
for p in peaks_ind2:
    found_new_peak = np.all(np.abs(p - peaks_ind1) > dist_th_for_new_peaks)
    if found_new_peak:
        new_peaks.append(p)
        print("New Peak!! - %d" % p)

fig, ax = plt.subplots()
plot(xf, v1, color='blue')
plot(xf, v2, color='green')
for p in peaks_ind1:
    ax.scatter(xf[p], v1[p], s=40, marker='s', color='blue', label='v1')
for p in peaks_ind2:
    ax.scatter(xf[p], v2[p], s=40, marker='s', color='green', label='v2')    
for p in new_peaks:
    ax.scatter(xf[p], v2[p], s=40, marker='s', color='red', label='new peaks')        

xlabel('Freq (Hz)',fontsize=16,weight='bold')
ylabel('|Y(freq)|',fontsize=16,weight='bold')

ax = gca()
fontsize = 14
for tick in ax.xaxis.get_major_ticks():
    tick.label1.set_fontsize(fontsize)
    tick.label1.set_fontweight('bold')
for tick in ax.yaxis.get_major_ticks():
    tick.label1.set_fontsize(fontsize)
    tick.label1.set_fontweight('bold')
ax.set_xlim([0,400])
ax.set_ylim([0,0.8])
grid(True)
show()

The red squares are the new peaks that were found in the green signal: enter image description here

like image 196
Elad Joseph Avatar answered Sep 19 '22 04:09

Elad Joseph