Fly Wheel sensor - Best approach?

Hello everyone,

Currently, for my project I need to measure the Time period of the signal that I am getting from the fly wheel sensor. For Example, My flywheel has 113 tooth on it. So, I get 113 pulses per revolution. I want to keep on measuring time periods continuously of upcoming pulses from the sensor and use that time period to perform some calculations and log them out. As far as I have seen from the web there are interrupts, timer interrupts, and pulseIn(). I tried to use pulse in to measure the time period. But, I was not happy with results.

Note:- The Flywheel can run at 1200 RPM to 1600 RPM. The frequency of the teeth will be 2000 Hz to 3500 Hz. And, is there any way I can measure the time periods continuously without missing any. Okay, I am just being too greedy over there, just a small hope. I will be posting the pulseIn() code soon, I don’t have access to my arduino workstation.

Thanks in advance. Arduino

There are many ways to time signals reliably.

Explain why you were not happy with using pulseIn().

What sensor are you using to detect the teeth and how is it wired?

I want to keep on measuring time periods continuously of upcoming pulses from the sensor and use that time period to perform some calculations and log them out.

What are you trying to do that you need 113 intervals per revolution? What else does your program need to be doing as well as determining interval timing?

I can't image a flywheel turning at 1200-1600 rpm that can change fast enough that you need to compare one tooth interval to the next.

I would start with a basic external interrupt to count pulses. 3500Hz us not to fast for a 16MHz arduino. You can count some number of pulses and determine a start and stop time for that interval.

okay,

@jremington Basically, it's a magnetic pick up sensor which detects the tooth. The signal is sine wave Vp-p. Since, bipolar signal is not usable to do anything. I shifted the signal up using op - Amp. That's it no more extra circuits. The reason why I am not happy with pulseIn because of it's accuracy. My signal is off my + or - 50Hz with pulseIn technique. Since, My application is time sensitive. I dont want much deviation from the true value.

@ CattleDog I am doing two things, This is gonna get too technical

  1. I am calculating RPM from the time period using (1000000 / timePeriod)* (60/113)
  2. I am calculating the ThirdOrderharmonics present in that signal.

Note:- The calculation contains 6 lines of code in order to get the above two things to get done

I will post some images of the arduino output(serial plotter) on problem I am encountering as soon as I have access to my workstation. I will be also posting the code.

pulseIn() is not very accurate.

A better approach might be to count (for example) 10-20 pulses, and time the interval using the micros() function.

How are you detecting the pulse? If using analogIn(), that is also a problem.

Perhaps you should be using a comparator rather than an op amp (or the op amp in comparator mode), to convert the sensor sine wave output into a square or rectangular pulse for a digital input.

jremington: pulseIn() is not very accurate.

A better approach might be to count (for example) 10-20 pulses, and time the interval using the micros() function.

How are you detecting the pulse? If using analogIn(), that is also a problem.

Perhaps you should be using a comparator rather than an op amp (or the op amp in comparator mode), to convert the sensor sine wave output into a square or rectangular pulse for a digital input.

I am using the digital pin not the anlalog. I am using the pulseIn(high) + pulseIN(LOW) which gives time in microseconds and from that I can get the time period. I was wondering why cant I measure period of each pulse instead of 10 ~ 20. I am curious what you mean by that.

Digital pins don’t work very well with slowly varying, noisy signals, which introduces substantial error into your timing. That is why I recommended using a comparator rather than an amplifier to process the sensor output.

Given edge detection timing errors, it is much more accurate to time a given number of pulses, than to time one pulse.

If you divide the time for 10 pulses by 10, you also divide the uncertainty in identifying the pulse edge by 10.

I will state again: the pulseIn() function is not very accurate.

jremington: Given edge detection timing errors, it is [u]much[/u] more accurate to time a given number of pulses, than to time one pulse.

If you divide the time for 10 pulses by 10, you also divide the uncertainty in identifying the pulse edge by 10.

I will state again: the pulseIn() function is not very accurate.

It makes sense, I will change the op - amp as comparator mode as you suggested.

I might have confused you. But, for my project having the time period of each pulse is necessary. For example, for one revolution of flywheel there are 113 pulses. I would like to get the time period of each pulses and use it for my calculations. (Such as calculating firing pulse time in one revolution in engine).

Can you give me suggestion on --> to use polling technique or interrupt service routine or timer interrupts which can do the task I wanted.

Note:- I am not worried about uncertainty at this stage. This is just proof of concept. Later I can optimize it for everything.

Thanks for your patience

Have a great Day :D

Note:- I am not worried about uncertainty at this stage.

You certainly should be.

Edge timing uncertainty is the problem and until you solve it, you will not be able to time the passage of each tooth with any reliability.

jremington: You certainly should be.

Edge timing uncertainty [u]is[/u] the problem and until you solve it, you will not be able to time the passage of each tooth with any reliability.

You are right. Uncertainty is killing it. Today, I checked the number of pulses it has registered. It registered only 31 pulses out of 113, which is bad. How to reduce this uncertainty in detecting the edge. I have changed the signal sine wave to square wave upon your suggestion.

Post a schematic diagram of the circuit (no Fritzing, please), a link to the sensor data sheet, and if possible, show a scope trace of the signal being fed to the Arduino.

As jremington has stressed, getting a clean signal is key to your application.

Do you have a zero index point? I'm sure you can stuff 113 pulse interval values per rotation into an array, but what do you want to do with the values?

Here's some code to count the period for 10 pulses. I have used tone(3,3000) on the interrupt pin to provide a test signal which is close to your 1600 rpm.

volatile byte  count = 0;
byte numCount = 10; //number of pulse intervals to measure
byte countsPerRevolution = 113;

volatile unsigned long startTime;
volatile unsigned long endTime;
unsigned long copy_startTime;
unsigned long copy_endTime;

volatile boolean finishCount = false;
float period;

unsigned int rpm = 0;

void setup()
{
  Serial.begin(115200);
  Serial.println("start...");

  tone(3, 3000); //test signal 3000 pulses/second  = 1593 rpm
  //333 microseconds between pulses

  attachInterrupt(digitalPinToInterrupt(3), isrCount, FALLING);//interrupt on pin3
}

void loop()
{
  if (finishCount == true)
  {
    finishCount = false;//reset flag
    // disable interrupts, make protected copy of time values
    noInterrupts();
    copy_startTime = startTime;
    copy_endTime = endTime;
    count = 0;
    interrupts();

    period = (copy_endTime - copy_startTime) / 1000.0; //convert micros to millis
    //debug prints
    Serial.print(period); //total time for numCount
    Serial.print('\t');
    Serial.println(period / numCount); //time between individual pulses

    rpm = 60.0 * 1000.0 / (((float)countsPerRevolution / (float)numCount) * period); //time for one revolution in ms
    Serial.print("RPM = ");
    Serial.println(rpm);
  }
}


void isrCount()
{
  if (count == 0)//first entry to isr
  {
    startTime = micros();
  }

  if (count == numCount)
  {
    endTime = micros();
    finishCount = true;
  }
  count++; //increment after test for numCount
}

jremington:
Post a schematic diagram of the circuit (no Fritzing, please), a link to the sensor data sheet, and if possible, show a scope trace of the signal being fed to the Arduino.

I am attaching the circuit, and the signal that I am feeding to arduino from the oscilloscope. Regarding, the sensor data sheet I didn’t find any it gives vp-p + or - 10 V. It’s just a standard pick up magnetic sensor, nothing fancy.

Note:- I am feeding vp-p 0 to 2v to arduino.

cattledog:
As jremington has stressed, getting a clean signal is key to your application.

Do you have a zero index point? → what do you mean by this. The problem with my signal is, it has frequency modulation. So, the time period of pulses always changes. That is why I am stressing too much on calculating time period of each pulse. Check the signal that is being fed so you can get an idea of what I am saying. I am doing all these calculations as you can see in my code.

 rpm = (freq * 60) / 113;        // to calculate the rpm of the flywheel 

 rpm_exponentialavg = rpm_exponentialavg * 0.99115044 + rpm * 0.00884956; // exponential smoothing average of the rpm being calculated

 Inst_speed = rpm_exponentialavg - rpm; // to calculate the instantaneous speed of the flywheel for every tooth
 
 sinval = sin(6*pii* count/113); // to calculate the sine of angle between the teeth in radians.
 
 cosval = cos(6*pii* count/113); // to calculate the cosine of angle between the teeth in radians.
 
 count++;  // increment the counter for every tooth passing.
 
 sincomp = sinval * Inst_speed; // to calculate the sine component to perform fourier transformation
 
 coscomp = cosval * Inst_speed; // to calculate the cosine component to perform  FT.
 
 // to calculate the exponential smoothing averages of sine and cosine, we get

  sincoeffavg = sincoeffavg * 0.017 + sincomp * 0.983; 
 
 coscoeffavg = coscoeffavg * 0.017 + coscomp * 0.983; 
 
 thirdOrder = 2*sqrt(sincoeffavg*sincoeffavg + coscoeffavg*coscoeffavg)*(2*pii/60)*rpm_exponentialavg*6*pii/60;

circuit.PNG

Note:- I am feeding vp-p 0 to 2v to arduino.

This approach generally won't work for a digital input, 5V Arduino. The minimum input voltage is 0.6Vcc (in the case of 2.4 to 5.5V operation) for a guaranteed "HIGH" detection.

It also won't work with the circuit you posted, because the op amp or comparator won't switch until the input signal exceeds Vcc/2. Use a different resistive divider and lower the threshold voltage to about 1 V (e.g. make the top resistor 3.9K and the bottom resistor 1K).

I’m not sure I see the time variation in the scope image attached. What differences in time periods from tooth to tooth do you see? The resolution you need will determine whether you can use micros() to capture your data, or if you need the hardware timers.

I’m not sure how you plan to access the data and calculations you perform. Are you trying to calculate something for every tooth in real time? What do you do with the results of the calculations? Or, are you capturing the raw data and analyse it later? How many values to you need to capture?

I think that you can capture the period for each tooth. Processing that data is a different issue. If it’s any use, here’s an example of how to place 500 interval readings into an array for later analysis.

const int numValues = 500;

volatile int interval[numValues] = {0};
volatile boolean finishCount = false;
volatile unsigned long time_last;
volatile int index = 0;
volatile unsigned long startTime = 0;
volatile byte count = 0;

void setup()
{
  Serial.begin(115200);
  Serial.println("start...");
  attachInterrupt(digitalPinToInterrupt(3), pulsePeriod, RISING);//interrupt on pin3
  //test signal 333 microseconds between pulses
  tone(3, 3000); //test signal 3000 pulses/second 3000/113 8*60 = 1593 rpm
}
void loop()
{
  if (finishCount == true)
  {
    for (int i = 0; i < numValues; i++)
    {
      Serial.print("interval ");
      Serial.print(i);
      Serial.print(" = ");
      Serial.println(interval[i]);
    }
    while (1);
  }
}

void pulsePeriod()
{
  if (count == 0)//special case for first rising edge
  {
    startTime = micros();
    time_last = startTime;
    count = 1;
  }
  else if (count == 1)
  {
    interval[index] = (micros() - time_last);
    time_last = micros();
    index++;
  }
  if (index == numValues)
  {
    finishCount = true;
    detachInterrupt(digitalPinToInterrupt(3));
  }
}

cattledog: I'm not sure how you plan to access the data and calculations you perform. Are you trying to calculate something for every tooth in real time? What do you do with the results of the calculations? Or, are you capturing the raw data and analyse it later? How many values to you need to capture?

I think that you can capture the period for each tooth. Processing that data is a different issue. If it's any use, here's an example of how to place 500 interval readings into an array for later analysis.

The period variations between each tooth can be 15 microseconds to 60 microseconds. But, the code you provided works best than mine. Okay, I need to perfrom this in real time to control my gear shifting in car. So, the arduino should be capable of doing it in real time. I don't have any idea on how to do it. Suggest me what are the best practices to do in real time. I just want to capture the raw data by performing calculations.

jremington: It also won't work with the circuit you posted, because the op amp or comparator won't switch until the input signal exceeds Vcc/2. Use a different resistive divider and lower the threshold voltage to about 1 V (e.g. make the top resistor 3.9K and the bottom resistor 1K).

I changed the signal like you suggested now it's better. When I did calculations with cattledog provided code for 500 data points the results were good.

I can't really see a way to do this in real time with individual tooth-tooth periods. At 3000Hz, I was missing counts when trying to pull data and do some simple calculations and prints.

You might want to explore using the period determined from several teeth like shown in an earlier reply and see if you can get meaningful results with real time calculations. I was not missing any pulses when I pulled intervals calculated from 6 teeth and did some analysis and printing, but the time of your calculations will be different.

You might want to consider using a faster processor than 16MHz.

cattledog: You might want to explore using the period determined from several teeth like shown in an earlier reply and see if you can get meaningful results with real time calculations. I was not missing any pulses when I pulled intervals calculated from 6 teeth and did some analysis and printing, but the time of your calculations will be different.

You might want to consider using a faster processor than 16MHz.

I am gonna try that by changing intervals, I'll let you know if I get better results.

I have a crazy idea. Since, real time is not possible I am thinking to delay the tooth interrupts for some time so that it can finish off calculations for one revolutiion or two depending upon the results.

Guys, I have a quick question, is the x - axis time in milli seconds or for number of data points.

If you are plotting the stored array data from the code I posted, x axis is data points(pulse #,tooth#), and the y axis is interval from the last pulse.