Go Down

Topic: Smoothing RPM data (Read 999 times) previous topic - next topic

nitrolx

Hi all,
I'm working on a project which logs the RPM of a shaft; shaft has magnets on it and I'm using a reed switch which pulls an input to ground when a magnet is near. The code is using an interrupt and measuring the period to work out RPM. This works fine. However I'd like to include some 'smoothing' or noise reduction in the data.

My ISR for the rpm calc is as follows;

Code: [Select]
void DSIsr()
{
  unsigned long DSnow = micros();
  unsigned long DSinterval = DSnow - DSlastPulseTime;
  if (DSinterval > 100)
  {
    DSrpm = 60000000.0/(DSinterval * DSpulsesPerRev);
    DSlastPulseTime = DSnow;
  } 
}


I then log the result of DSrpm (to SD card eventually but currently just to the Serial output) at an adjustable refresh rate using;

Code: [Select]
unsigned long refreshcurrentMillis = millis();
  if(refreshcurrentMillis - refreshpreviousMillis > refresh) {
    refreshpreviousMillis = refreshcurrentMillis;
    lcd.setCursor(0, 1);
    lcd.print(DSrpm);
    lcd.print("  ");
    lcd.print("RPM ");   
    // Serial.println(DSrpm);
  }


'refresh' is 50ms at the moment.

I would like a way to 'average' the rpm results, say over 5 or 10 samples, to smooth the data that I'm getting.

I tried a similar method I've used to smooth analog inputs;

Code: [Select]
    rawvalue = 0;
    for (int i=0; i< count; i++) rawvalue += analogRead(A1);
    rawvalue = rawvalue / count;


but that doesn't seem to work using an ISR-derived value, as each time the ISR is triggered, DSrpm is re-calculated.

Is there anyway I can use a similar idea to 'average' a few samples of DSrpm and smooth the data??

Cheers,
Ryan.
   

marco_c

#1
Jul 21, 2012, 12:47 pm Last Edit: Jul 21, 2012, 12:52 pm by marco_c Reason: 1
Adding in a fraction, say 30%, of the difference between the current value and the new data creates a much smoother change profile and eliminates spikes and fluctuation.
Arduino libraries http://arduinocode.codeplex.com
Parola hardware & library http://parola.codeplex.com

PaulS

Your ISR should simply count pulses (one per time called). You should only calculate RPM when you need to display it. The RPM value is a function of the number of pulses per unit of time. The number of pulses is known, as is the unit of time (the refresh time).

JavaMan

like marco_c said, using a fraction will smooth the data.  I use the code below in several sketches, a method I found in the book "30 Arduino Projects for the Evil Genius" by Simon Monk.  Alpha is a number between zero and one.  Larger alpha for more smoothing.

Code: [Select]

float value, rawvalue, oldvalue=0, alpha=.7;

void loop() {
  rawvalue = analogRead(1);
  value = alpha*oldvalue + (1-alpha)*rawvalue;
  oldvalue = value;
}

dc42

I would do 2 things:

1. Use a Hall sensor instead of a reed switch to detect the magnets. Contact bounce in the reed switch is likely to cause jitter. There may be less need for smoothing if you eliminate this jitter. Hall sensors cost very little these days.

2. One way of smoothing the output is to measure the time for several rotations instead of just one. You could have the ISR record the times of e.g. the last 8 pulses instead of just the last one, like this (warning: untested code!):

Code: [Select]

const int nReadings = 8;

volatile unsigned long times[nReadings ];
volatile unsigned char lastReading = 0;

void DSIsr()
{
  unsigned long DSnow = micros();
  if (DSnow - times[lastReading] > 100)
  {
    lastReading = (lastReading + 1) % nReadings ;
    times[lastReading] = DSnow;
  } 
}

...

unsigned long refreshcurrentMillis = millis();
  if(refreshcurrentMillis - refreshpreviousMillis > refresh) {
    refreshpreviousMillis = refreshcurrentMillis;
    noInterrupts();
    unsigned int lr = lastReading;
    unsigned long interval = times[lr] - times[(lr + 1) % nReadings];
    interrupts();
    unsigned long DSrpm = ((nReadings - 1) * 60000000UL)/interval;
    lcd.setCursor(0, 1);
    lcd.print(DSrpm);
    lcd.print("  ");
    lcd.print("RPM ");   
  }


I've done the calculation in unsigned long instead of float for better speed, but you can use float if you want to display fractional RPM.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

nitrolx


I would do 2 things:

1. Use a Hall sensor instead of a reed switch to detect the magnets. Contact bounce in the reed switch is likely to cause jitter. There may be less need for smoothing if you eliminate this jitter. Hall sensors cost very little these days.

2. One way of smoothing the output is to measure the time for several rotations instead of just one. You could have the ISR record the times of e.g. the last 8 pulses instead of just the last one, like this (warning: untested code!):

Code: [Select]

const int nReadings = 8;

volatile unsigned long times[nReadings ];
volatile unsigned char lastReading = 0;

void DSIsr()
{
  unsigned long DSnow = micros();
  if (DSnow - times[lastReading] > 100)
  {
    lastReading = (lastReading + 1) % nReadings ;
    times[lastReading] = DSnow;
  } 
}

...

unsigned long refreshcurrentMillis = millis();
  if(refreshcurrentMillis - refreshpreviousMillis > refresh) {
    refreshpreviousMillis = refreshcurrentMillis;
    noInterrupts();
    unsigned int lr = lastReading;
    unsigned long interval = times[lr] - times[(lr + 1) % nReadings];
    interrupts();
    unsigned long DSrpm = ((nReadings - 1) * 60000000UL)/interval;
    lcd.setCursor(0, 1);
    lcd.print(DSrpm);
    lcd.print("  ");
    lcd.print("RPM ");   
  }


I've done the calculation in unsigned long instead of float for better speed, but you can use float if you want to display fractional RPM.


Thanks for that!
Fractional RPM isn't needed, not sure why I used float in my code actually, I think it's code I used from another project that displayed a fractional RPM.

The whole length of the event I'm logging will only be 9 seconds (hopefully a bit less) and the shaft will accelerate from 0 RPM to about 7000 in that 9 seconds. I'm looking for the most accurate RPM calculation I can get to analyse the acceleration rate of the shaft over the 9 seconds. The shaft will have 4 magnets at 90 degree intervals, so there will be 4 'pulses' per revolution.

Will your method give a less accurate or less 'responsive' result then the original calculations, or will the difference be negligible?

PeterH



I'm looking for the most accurate RPM calculation I can get to analyse the acceleration rate of the shaft over the 9 seconds.



I think you need to be clearer about what you're trying to achieve. Smoothing will reduce the accuracy, not increase it. Exactly what are you trying to get out of this thing?
I only provide help via the forum - please do not contact me for private consultancy.

Go Up