Electronics > Microcontrollers

Peak detection find algorithm

(1/2) > >>

rvalente:
Hello Mates,

I'm looking for a better way to detect peaks in a signal sample

The signal is a sinusoidal from VR sensor and tone ring wheel with 40 to 140 teeth, the output frequency is from 40 to 200Hz, this signal is then rectified (80 to 400Hz) and sampled in the uC ADC, 12bits, 0 to 5V.

The sample rate is adjusted so 1.2 revolutions of the tone ring can be captured. Usually the tone ring has 100 teeth and the algorithm finds 239 to 240 Peaks, which is correct

The current algorithm is pretty simple it basically looks for a number of consecutive signal samples which are bigger than the previous, if not the increments the "fall" counter, when the fall counter is a given number (8 by now) is the peak is saved to a array. As the sample rate is now 20 times the non rectified frequency the number 8 for the fall counter works just fine.

The thing is: this works but, I feel I need to make it better, I'd like to have this fall number self adjusted or to find a better and proven peak detection algorithm

Anyway, I'd like some suggestions from you guys


--- Code: ---///////// SEARCH FOR SIGNAL PEAKS /////////
for(point=0; point<samples; point++)
{
if(buffer[point] > buffer[point-1])
{
fall = 0;
pointBuffer = point;
}
else
{
fall++;
if(fall == FALLS) //This FALLS constant is set to 8, I'd like it to be adjustable
{
if (peaks < expectedPeaks && firstPeak)
{//Make sure the array wont be overwritten
peakArray[peaks] = buffer[pointBuffer]; //Salva posição do pico
timeArray[peaks] = pointBuffer; //Salva tempo do pico na posição
peaks++;
}
firstPeak = 1; //Set first peak has passed already, save later peaks
if (peaks >= expectedPeaks)
{//Check for excessive readings
eturn(EXCESSIVE_PEAKS);
}
}
}
}

--- End code ---

ledtester:
The algorithm you use might depend on the kind of data you expect to collect.

This one might be worth trying out:

https://stackoverflow.com/questions/22583391/peak-signal-detection-in-realtime-timeseries-data

SiliconWizard:
As I see it, the algorithm you're using is more or less equivalent to low-pass filtering the signal and then determining a local maximum from the numeric derivative's sign change.

Of course the approach to use all depends on your requirements. One issue with the one you use is that, while trying to avoid local peaks from noise, it's also less time-accurate.

There's a bunch of articles on peak detection out there. Probably more than you'll have time to read.
One way I know of smoothing data while not having the drawbacks of simple low-pass filtering (/moving averages/...) is to use Savitzky–Golay filters. https://en.wikipedia.org/wiki/Savitzky%E2%80%93Golay_filter
I've used that successfully.

There are more efficient approaches as well. Probably you should first determine what exactly your requirements are:
- what you consider a valid 'peak' in the signals you're dealing with;
- what kind of noise you're expecting;
- what you expect in terms of time accuracy for peak detection.

rvalente:
Hello mates,

you've given a buch of useful information, I'm already studying.

Would you have some code to share?

DavidAlfa:
Edit: I wrote this thinking you wanted to get rid of the spikes.
But it can be useful too. o exactky this to have a clean, spikes-less signal.
Apply new readings to the EMA filtering, then compare the reading with the updated EMA value.
Now you can apply any threshold/percentage you want to consider the reading a spike or not.

You can make a simple EMA fitering, you'll get some delay but much cleaner signal.
https://www.norwegiancreations.com/2016/08/double-exponential-moving-average-filter-speeding-up-the-ema/

The basic EMA code implementation is:

--- Code: ---
  #define k 0.5f                             // coefficient. 0=use 0% of new, 100% of old(won't update) . 1=100% of new, 0% of old(no filtering).
                                             // Play with the coefficient to find the best value.
static float stored_ema;                     // Stored average

void reset_ema(uint32_t new_read){
  stored_ema = new_read;                     // Some times you'll need this to avoid the initial delay, as the average needs to build up (ex. first ADC reading).
}

uint32_t ema(unt32_t new_read){ 
  stored_ema= (1.0-k)*stored_ema+ k*new_read;
  return stored_ema;
}
--- End code ---

Also, you can apply a pre-filter. As I see in your waveforms, there're quite a lot of spikes. If you don't want them, simply use a threshold a window.
You know the signal is supossed to change progressively, so any reading that exceeds ex. 10% of last reading could be ignored.
That and a small EMA filtering for the high frequency changes will clean the signal a lot.

Navigation

[0] Message Index

[#] Next page

There was an error while thanking
Thanking...
Go to full version