Calculating RPM from encoder by timing pulses

Hi guys,

I wonder if somebody can give me some pointers here as to whether the Arduino Diecimila is what I'm after for this project or not.

What I'm trying to do is build a dyno to calculate the horsepower output of motorcycles. So the rear wheel sits on a heavy metal drum and is accelerated to full speed, some simple math allows you to calculate the horsepower based on the acceleration of the drum knowing its interia.

So the mechanical side of things is built and I've got an encoder connected to the drum to give me TTL pulses either once per revolution or fifty times. The encoder is a Hengstler RI38.

To calculate power I need to find the acceleration of the drum, so I need to find the time between one set of pulses from the encoder and see how much faster/slower the time between the next set is. It's pretty simple really, but the problem is speed and accuracy. As I'm comparing such small times they need to be quite accurate and because the time between pulse is so small a PC is useless for reading the data.

Just some figures -
The encoder will spin from between 200RPM and 2500RPM, so at 50 pulses per revolution and max RPM you're talking about 2083 pulses per second. So the time between pulses will be between 6000 microseconds and 500 microseconds as the speed of the drum increases. As I need to tell the difference between one time and the next it needs to be quite accurate. Also a run will last between 5-15 seconds depending on the power of the bike, so I'll be looking at maybe 5-6000 values.

So what are my options with the Diecimila? If I hook the TTL output from the encoder to one of the interrupt pins is there a function/timer/counter that will allow me tell exactly how ong ago (in microseconds) the last interrupt happened? Do I have enough memory to do this or can I be pushing the values to the PC as they happen without worry of destroying the accuracy of the timers?

Any pointers would be great. Not looking for the whole solution or anything, just some ideas I can go and research.

Thanks!

Well, depending on the size of the program you might need more memory?

Couldn't you calculate acceleration fairly accurately by counting the number of pulses that occured in, say, each 100ms interval? Or measure the time delta at every 100th pulse? I hope one of these alteratives work, because I doubt you're going to find an easy way to accurately measure time deltas as small as 500us (although some clever programming might accomplish this). You certainly will have trouble storing 5000 samples.

Interesting project!

Mikal

You could use pulseIn command. This will return the width of a pulse in microseconds: http://arduino.cc/en/Reference/PulseIn

Another way is to use the timer1 input capture mode. This is more elegant because the measurements are done in the background so you can be calculating acceleration at the same time the next pulse is being measured. But the code is much more complicated. See this thread for some examples of timer1 input capture : http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1201890734

I would guess that the changes in pulse width duration due to acceleration is relativly slow compared to the calculation time of the arduino so pulseIn would be a good place to start.

Good advice from mem.
However, I think your main problem will be what to do with the samples. At that rate it is probably too fast to push them out to the serial port. You need 10 bits per byte and assuming you have 4 bytes per sample you get:-
10 * 4 * 2083 = 83320 bits per second or baud speed on the serial port. While it will go higher there will not be any time left to make the measurements. So you need 8.332K bytes of memory for one second of storage. This is not a lot in the desktop world but is large in the embedded domain. The Arduino has just about 1K of RAM memory in total for everything. You need to store a byte every 0.12mS which is fast for flash memory. I haven't seen anyone interface RAM to the Arduino, mainly you don't have the I/O lines to do it.
Basically I think you will struggle with this set up.

robotkid249 - It will be as small as possible. All I want the microcontroller to do is grab the data and send it to the PC, it will be number crunched and dealt with from there.

mikalhart - You could count the pulses in 100ms, but at low RPMs you would get very few, giving a very rough graph (the graph will be something like this http://img375.imageshack.us/img375/2575/skora125lbs9br.jpg). I don't think it would be hugely accurate either and when I'll be subtracting one count from the next that accuracy would become a big problem.

Grumpy_Mike/mem - That sounds good. My first thought is that if I can do some calculation on the microcontroller (which was not easyily done when I was planning on doing this with just ICs) I could just store the differences between one pulse width and the next. I'd have to check my spreadsheets, but I'm sure I wouldn't need anything like 4 bytes to store these values because the change in RPM from one pulse to the next will be quite small. I'd say 1-2 bytes max. Also I don't necessarily need to use 50 pulses per RPM, this just happens to be what the encoder does on one of its outputs. I could divide this down with a counter (I have heaps of 74HC ripple counters from when I was trying to do this with ICs), then go for say 12 pulses per revolution, I think this would give enough samples for a smooth graph.

So the question is is this possible with the Arduino at all or should I look at something more powerful do you think?

Thanks for the replies!

I just read the PulseIn function spec, that does look kinda perfect alright! I'll get some figures tonight and get back to you.

I don't think the issue Mike raised will be a problem for your application. If your encoder is reasonably accurate then you can sample the pulse rate at intervals that suit the accuracy you need. You don't need every pulse. I would guess that a two byte value of pulse width sent at the baud rates the Arduino can handle would give plenty of accuracy for your application.

How much resolution do you need? i.e. how many samples per second are you looking for to calculate power to the accuracy you want to display?

I can afford to divide down the number of pulses but I can't afford to miss any. If any are missed the change in acceleration I calculate will be for an incorrect time period (software thinks it's the acceleration over 1/2 a revolution when in fact it's over 3/4, if you know what I mean).

I really don't know exactly how many pulses I need, the more the better. Anything less then a total of a few hundred would give a very rough graph that would take a lot of smoothing to make it useable, then all accuracy would be lost.

I reckon I'll order a Diecimila and see what I can do, if nothing else they look really interesting anyway. The fact that they are USB is a god send, I wasn't looking forward to interfacing my home made board to a laptop!

Any more ideas keep them coming!

What about using one of the external storage solutions that has been discussed on this forum, like a small SD card? Even at max speed over 15 seconds that would only require

15s x 2000 samples/sec * 4 bytes/sample = 120K bytes

You'd probably have to do some pretty clever buffering to get this scheme to work, but on the surface it seems possible to me, depending on the latency of the write.

Mikal

As you say, you need to ensure that you are taking samples at regular intervals, but because you are measuring the pulse period to calculate velocity, you can drop pulses between the measuring intervals without distorting your data, and the number of pulses you can drop will be proportional to the rotation rate.

Say you measure the period of every pulse at your slowest rotation rate, 200rpm. You will have a measurement every 6 milliseconds. Assuming that's precise enough for your application, then I would think you could continue to sample the pulse widths every 6 milliseconds as the velocity increases. Because the pulses will become shorter you will be ignoring more and more pulses but still providing a measure of the velocity at 6 millisecond intervals. Using this method you only need to deal with 166 samples per second at the expense of some error due to the maximum change that could occur in velocity in between 6 millisecond samples. I am guessing that for the accelerations you are measuring this will be too small to matter.

Well the Diecimila arrived yesterday. I'm very impressed at how easy it is to get a basic program up and running!

I've started with the pulseIn function first. I connected it up to an old 555 time and was a bit concerned by the 0-2% inaccuracy between pulses, but apparently this is normal for a 555. I've got it hooked up to my encoder (50ppr wire) and it looks to be quit accurate, certainly no major swing between values sampled.

I've programmed the following code onto the Diecimila and have a Processing app that just reads from the serial port to a CSV file.

unsigned long duration;

void setup()
{
  Serial.begin(115200);
  pinMode(2, INPUT);
}

void loop()
{
  duration =  pulseIn(2, LOW);

  Serial.println((unsigned int)duration);
}

The problem is I have no real way of knowing whether I'm missing pulses or not. While I'm doing the Serial.println another pulse edge could have come and gone. My understanding of serial isn't great, but I think at 115200 baud I should be able to send a UINT every 140 microseconds, so with the shortest duration proving to be about 500-600 microseconds I think I should be getting every pulse but I may not be. I have a few ripple counters here so I could divide down the pulses from the encoder a few times (50ppr is way more then I need in reality!).

I know what you're saying mem about sampling at a specific interval but suppose at a 6ms interval I call pulseIn but the pulse doesn't start for say 1500 microseconds (I will not know what this figure is obviously), then lasts 1800 microseconds. At the next 6ms interval I call pusleIn but now get a pulse in 100 microseconds (again I won't know this) and it lasts 1790 microseconds. So I now assume from the 1800 and 1790 that the drum has accelerated from 666.6RPM to 670.3RPM in 6ms (or 6000 microseconds). Assuming you use something like delay(6) after calling pulseIn, then the amount of time between the pulses is more like 6100 microseconds one time, but could be 7000 microseconds next time depending on how slow the drum is spinning. That seems like a huge margin or error. Or are you thinking to use millis()?

Maybe I'm confusing myself here, but I feel the only way I can guarantee accuracies is to get every pulse?

Having said all that, I feel if I divided the pulses from the encoder down by 4 (12.5 per revolution) the Diecimila would be more then fast enough to send them to the serial port, but I'd like some way to confirm I'm not missing any samples. If I had a decent signal generator I could probably just keep cranking up the frequency and compare the number of readings I get in my CSV file? Any way to do this with one of the PWM outputs on the Diecimila in the background so generating the PWM doesn't affect my main program?

Thanks!

As you say, what is important is measuring at regular intervals. But for the relatively low (i.e. less than 2g) accelerations you will be measuring, you don't need to capture every pulse.
Using millis is a good idea.
Here is the kind of thing I had in mind:

int samplePeriod = 6; // sample every 6 milliseconds
unsigned int duration; // this can be an int because the longest pulse is 6000 us
unsigned long start;

void setup()
{
Serial.begin(115200);
pinMode(2, INPUT);
}

void loop()
{
start = millis();
duration = pulseIn(2, LOW);
Serial.println(duration);
while( millis() - start < samplePeriod)
;
// we get here within a microsecond or so after 6 milliseconds from the start
// assuming the minimum pulse width (plus the time to send the serial data) is always less than 6 milliseconds
}

Its not quite as simple as that because you probably need to handle the case where the system is at rest (or any time the pulse width is close to or greater than 5 milliseconds)

But if you can rig something up that can rotate the your sensor at know rotation rates it should be easy to verify this approach.

I think I understand what you're getting at now mem, I'll give it a try tomorrow and see what happens.

I added a ripple counter to divide down the encoder signal by 4, so I'm now getting 12.5 pulses per revolution. Still concerned about what Grumpy_Mike said above about baud rates I changed to code to the following

unsigned long duration;
volatile unsigned int pulseCount = 0;

void setup()
{
  Serial.begin(115200);
  pinMode(2, INPUT);
  attachInterrupt(1, encoderPulseInterrupt, RISING);
}

void loop()
{
  duration =  pulseIn(2, LOW);

  Serial.print(cnt++);
  Serial.print(',');
  Serial.print(pulseCount);
  Serial.print(',');
  Serial.print(millis());
  Serial.print(',');
  Serial.println(duration);
}

void encoderPulseInterrupt()
{
  pulseCount++;  
}

The idea being that using interrupts I could not possibly miss a pulse, even if the serial port is too slow to get it to the PC. The output of this code is something like

168,167,6049,13963
169,168,6077,13992
170,169,6107,14917
171,170,6854,670223
172,171,7176,165747
173,172,7587,221984

The first two numbers being the important ones; the first is the count of pulses in the main loop(), the second is the count of pulses from the interrupt handler. If they stay in sync then I think the serial.print() commands aren't taking too long, if they start to get out of sync (value two gets ahead of value one) the serial comms is taking too long. I ran the encoder from 100RPM up, sometime after 12000RPM the values started to get out of whack. So it looks like I can stream values directly to the PC without worry at 12.5ppr or even 25.

More fun tomorrow :). Thanks for the help so far guys!

The idea being that using interrupts I could not possibly miss a pulse

I don't think thats something you need worry about. As I see it, you should be taking a snapshot of the speed at regular intervals and not worrying about dropping pulses. Indeed I would think you want to drop pulses as the speed increases. Remember that the maximum acceleration you will encounter is limited, so the rate of change of rotation speed over a fraction of revolution should be negligible. So there is no disadvantage ignoring more and more of the pulses as the speed increases as long as your measurement interval is constant.

Perhaps do this calculation, at the highest speed you will meaure, how much will the period change between successive pulses at the highest acceleration you will encounter?

I've never actually tried to figure out the acceleration, it wouldn't be easy to do. I'll get the dyno down to the house during the week and take some measurements, then get back to you.

In the meantime I'll program up what you're suggesting as well. I should be able to try the different options and see how the graphs look.

I look forward to hearing about the results

Should I expect to get some spurios data through the serial port at 115200 baud? For example the following is form the middle of a run of about 4000 samples

Cnt1 Cnt2 Millis pulseIn RPM
6231 10084 35119 904 5309
6232 10085 35123 922 5206
6233 10086 35127 929 5167
623
6336 10189 35566 1043 4602
6337 10190 35570 879 5460
6338 10191 35574 1041 4611

Note the one in the middle that is missing half its data. Sometimes I see lines with missing EOL characters or lines half missing like this one. It happens both in my Processing app and in the Arduino Serial Monitor. They could be filtered pretty easily I'm sure, just wondering what they might be telling me?

Thanks!

Here's one from further up the page on the same run

5416 9269 32019 189 25402
5417 9270 32022 201 23885
5418 9271 32026 187 256735544 9397 32468 293 16384
5545 9398 32473 303 15843
5546 9399 32476 298 16107
5547 9400 32480 300 16000

Odd huh?

It could be a serial buffer is sometimes overflowing. Try reducing the number of chars sent per sample or increase the time between samples.