Hello
I am new to Arduino, but I'm a very experienced programmer.
A couple of years ago, I made my own PLC controller for my induction wind turbine. I am now interested in making a microcontroller for my wind turbine and so I have a few questions:
I would like to interface with a 500 pulse per revolution rotary encoder (24v, so I will use an optoisolator). In my current PLC controller, I had to purchase a special "high speed" card to accomplish this (very expensive!). Considering that I need to measure up to 2200 RPM @ 500 PPR, this means that I need to count up to almost 20,000 pulses per second. I'm assuming that this will be hard to do with the Arduino.
I am willing to change encoders to a lower PPR count - but was wondering what anyone though the maximum PPR (pulse per revolution) encoder value should be?
Also, I'm not sure of main loop resolution on an average program (how many ms/us between iterations of the loop - my Arduino is still on its way so I can't test for myself yet). I am wondering if it is fast enough that I can use a standart input pin, or if I should use an interrupt. Any advice on this would be appreciated as well!
I would look into using either a prescaler or an external counter.
You are looking at 20KHz which isn't really that fast for the Arduino but why waste a microcontroller to just count, it should be busy handling other (higher-level) tasks.
How accurate and how often do you need to monitor the speed?
I would like to monitor the speed at about 20hz - count how many pulses every 50ms and calculate RPM from that.
Maybe I would be better with a low PPR count(5PPR for example) and actually measure microseconds between pulses to calculate RPM? Does this sound more feasible?
Is this a quadrature encoder or a single-line encoder? If single-line you may want to use the input capture functionality of the microcontroller. I don't think there's a high-level Arduino function for this yet but you can always work with the chip registers at the lower level. This will give you single-cycle resolution in period (i.e., 62.5ns if you're running at 16 MHz).
Using interrupts will give you good accuracy but there will be jitter and latency depending upon whatever else is going on in the system. Interrupts are pretty much the only solution if you have a quadrature encoder unless you want to ignore one of the quadrature lines and go back to the single-line encoder case.
And I don't think there's a need for optoisolation if you can connect grounds together; just use a voltage divider to knock down the 24V down to 5V.
Yes, I'm using a quad encoder - but only using one "pulse train" (I don't need direction information) so essentially its a single-line encoder for how I'm using it.
I'm starting to lean towards using a low PPR value and counting elapsed time between pulses - this should be a lot less demanding.
I'm not sure what you mean by tieing the grounds together to eliminate the optoisolation, could you explain? Thanks!
(I dont' have a whole lot of experience with electronic circuits, but am trying to learn!)
Again, I'm probably going to lean towards a low pulse device, which gives me the option of instead going with a simple hall effect pickup that senses magnets embeded in the turbine blades etc.
I am a little bit astonished about your question. There is no need for any "more feasibility". It is absolutely feasible as I explained, and there is no need for interrupts....
You do not measure the width of pulses for such frequencies!! You count the number of pulses for a definite (gate) time.
This is a most simple application, needing 20 lines of code, or less if you use existing and well working libraries....
Jon, is there some other reason to replace the encoder?
New encoder >$100
counter IC <$1
Using a 74HC4020 will let you select from resolutions of 500, 250, 125, 62.5, 31.25, all the way down to 0.030 PPR.
The encoder systems I'm most familiar with (extremely high-accuracy position encoders) use a counter and latch system. You feed the quadrature pulses into a quadrature detector which changes it to either COUNT_UP or COUNT_DOWN pulses that feed a series of counter ICs that count continously. Every so often (let take your 50 mSec) a clock pulse comes along and latches the encoder counter which also generates an interrupt. The controller can than takes its time to service the interrupt (as long as it <50 mSec) without losing a single count (because the counter is still counting). Speed can be determined by subtracting the previous from current count and dividing by the sample clock (50 mSec).
Do you really need to monitor continously and a such a high rate? It would seem to me that a wind turbine would not vary it's speed substantially over a period of 100-200 mS.
Frankly, I'm intrigued by the library that deSilva posted.
Yes, the programming will be very easy, I have over 20 years of programming experience. My question was more hardware related.
Let me explain a bit -
Using a low count PPR encoder will only provide a low resolution measurement by using a pulse counting method. For example, if I decide to poll every 50 MS (20hz) for the number of pulses on a 5 PPR encoder rotating at 1800 rpm, would only receive about 6-7 pulses per 50ms polling peroid. This essentially means each pulse would cover a 150ms gap, and thats at the high end of the RPM I need to measure.
However, counting time between pulses allows me to calculate the RPM just using the delta time between any 2 pulses, regardless of the speed at which they are coming in. This will allow my program to make much quicker decisions, as connecting a 1800RPM generator to the grid before it overspeeds needs to happen very quickly! (I know this from experience using a PLC to do this currently). I chose 500PPR in the past, as this allowed fairly high resolution for me to respond in a low amount of time.
My thinking was that now that I would like to try a microprocessor instead of a PLC, I should use a lower pulse count and calculate based on delta time instead of counting pulses. (High PPR encoders seem to suffer from noise a lot more in out application - also the PLC I use didn't have good funtions for measuring elapsed time, which didn't allow me the option to count delta time between pulses)
Most of the time, you are correct in that the wind turbine will not change speed quickly. However, one of the main objectives of the controller is the connect the wind turbine to the power grid (through an SCR) as soon as possible after it crosses 1800RPM - the more beyond 1800RPM the alternator is when you connect the line, the higher the THD is as the alternator struggles to get in phase with the line (yes, this has more to do with difference in phase angle than rpm, but trust me, it is very hard on things when you have a large phase angle difference AND a lot of RPM to "slow down" to synchronous speed (i use simple PWM to do this currently, to slowly coax it into phase). Therefore, it is a huge advantage to start the PWM the instant the alternator hits 1800RPM. Consider that our turbine blades have a 7:1 lift ratio, the alternator speed can go from 1799 to over 1830 in the matter of a second with a normal gust! THerefore, the quicker I can detect a change in the RPM the better!
15.2.1 Registers
(...)
The Timer/Counter can be clocked internally, via the prescaler, or by an external clock source on
the T1 pin. The Clock Select logic block controls which clock source and edge the Timer/Counter
uses to increment (or decrement) its value. The Timer/Counter is inactive when no clock source
is selected. The output from the Clock Select logic is referred to as the timer clock (clkT1).
(...)
And with regard to the sampling times it says
16.3 External Clock Source
(...)
Enabling and disabling of the clock input must be done when T1/T0 has been stable for at least
one system clock cycle, otherwise it is a risk that a false Timer/Counter clock pulse is generated.
Each half period of the external clock applied must be longer than one system clock cycle to
ensure correct sampling. The external clock must be guaranteed to have less than half the system
clock frequency (fExtClk < fclk_I/O/2) given a 50/50% duty cycle. Since the edge detector uses
sampling, the maximum frequency of an external clock it can detect is half the sampling frequency
(Nyquist sampling theorem). However, due to variation of the system clock frequency
and duty cycle caused by Oscillator source (crystal, resonator, and capacitors) tolerances, it is
recommended that maximum frequency of an external clock source is less than fclk_I/O/2.5.
(...)
The Arduino uses fclk I/O = 16MHz, so your frequency of 80kHz is way below the maximum rating.
As always in such discussions the real constrains and rationals only show up one after the other.
So you have a maximum reaction time when the signals cross some 1800 RPM line = 15 kHz = pulse distance 66us
The Arduino, without tricks, can distinguish between 64 and 68us which is around +/-3% if that suffices you have a response time = gate time of 1 pulse.
Assume a somewhat more sensible response time of around 1ms. You can precisely determine the combined length of 15 pulses. They will be 1000 us @ 1800 RPM with an error of 4us = +/- 0.2% Does that suffice?
Note the fine difference! This algorithm is not a simple brute force pulse counter but takes into account the exact length of the pulse - it is "phase sensitive" so to speak. A counter would only distinguish between 14 or 15 pulses which gives an error of +/- 3%
The drawback of this algorithm - in its simplest implementation - is, that it is most sensitive around 1800 RPM only, and will not work below 120 RPM at all..
Although my board hasn't shown up yet (Arduino Deumilanove), I whipped up a quick and dirty example of what I want to do -
(just a quick example, yes I know that I'm not accounting for wraparound of the micros() function, etc.)
#define PPR 5 //encoder PPR
int RPM=0; //Calculated RPM
int rpmPin=5;
int rpmStateLast = LOW;
void setup() {
pinMode(rpmPin,INPUT);
}
void loop() {
if (digitalRead(rpmPin)==HIGH) {
if (rpmStateLast==LOW) {
rpmStateLast=HIGH;
CalcRPM();
}
} else {
rpmStateLast=LOW;
}
if(RPM>=1800) {
//do stuff
}
}
void CalcRPM() {
static unsigned long oldTime;
unsigned long currentTime;
unsigned long deltaTime;
//calculate the RPM and store it
currentTime=micros();
deltaTime=currentTime-oldTime;
RPM=(1000000/PPR)/deltaTime;
//record the old time
oldTime=currentTime;
}
The good side effect of this style is that the resolution increases as the RPM increases - this is exactly where it is most important!
(about 66us@1800 RPM would be an amazing response time, compared to polling at 20HZ and having a 50ms response time! This is the reason why I want to calculate based on delta time of pulses, rather than counting pulses)
Can anyone see a reason not to do it this way, and go back to traditional pulse counting at a fixed interval?? (Since I have not yet actually used the Arduino platform, I was just trying to make sure I wasn't going to hit a hardware speed limitation)
yes, but if you read the code, you will see that I'm edge triggering in software.
This is the reason why I was asking, as I was not sure how long iterations between loops takes...
The loops don't take that long, but high-level functions like digitalRead() do (faster would be to use PINx register), as does the integer division by the unsigned long deltaTime. I don't think it'll fit within 50us if you plan to do it repeatedly, but if you only plan to sample the RPM once every 50ms then it should be OK.
Better would be to, in the loop() function, wait for the go-ahead that 50ms has gone by and it's time to sample, then sit in a tight loop (don't return from loop()) until you have your measurement, using PINx access instead of digitalRead().
Ok, i might as well wait for my board and do some tests at this point. I may have some design decisions to make:
Currently, the RPM sensing is done right on the alternator, which means I have to track up to 2000 RPM. I may have to start measuring at the rotor (before the gearbox which is a known ratio) and calculate the alternator RPM from the delta time between pulses at the rotor speed (which is about 90 RPM) - this would make my software solution above probably more feasible!