NI Home > Community > NI Discussion Forums

LabVIEW

Showing results for 
Search instead for 
Do you mean 
Reply
Member
Milqman
Posts: 114
0 Kudos

RPM as a Function of Crank Angle (optical encoder - counters)

I have a PCI 6289 (2 onboard counters) and a US Digital Optical encoder (3 channel A, B, Z).  I am trying to read RPM as a function of crank angle.  Right now what I am doing is outputing the position with counter 1 and doing a frequency measurement on the A signal with counter 2.  Is there a better way I can do this, or a way I can trick LabVIEW into doing what I want with higher precision.  Right now it does something like what I want, but seems to have quite a bit of variability that I am fairly certain does not exist.

Also, because of an unknown buffer issue, I am unable to set it to collect a certain number of points (you can see I am trying to collect everything a revolution at a time) so I need to run it continuously (which means the data I am sending to the host end of my application varies in size).  So I am using the "period" at the bottom to manually approximate what a full revolution is.  This seems like a poor way to do this.  Does anybody have any ideas on how to either fix my buffering issue, or trigger counter measurements?

Any ideas? (block diagram below)

~milq

Active Participant
Kevin_Price
Posts: 1,945
0 Kudos

Re: RPM as a Function of Crank Angle (optical encoder - counters)

Thanks for including the block diagram -- it's a big help.  Here are some thoughts in no particular order:
 
1.  The two tasks are not sync'ed in time.  Because of this, you won't be able to correlate specific position values to the correct corresponding freq (speed).  The two measurements will have an unknown and perhaps not repeatable offset in time.
 
Remedy: Configure both tasks for an "Arm Start" trigger, available as a DAQmx Trigger property node.  Search on the forums (especially the Counter/Timer board) for "arm start" for more details.
 
2. I see no specification for choosing between "finite sampling" and "continuous sampling".   Perhaps this is the cause of your "unknown buffer issue"?   Maybe you want continuous, but the task defaults to finite?
 
3. True confessions time.  As much as I've used NI's counter/timers, I've never used DAQmx's frequency measurement mode.  I always measure periods and do my own calcs, the way I got used to under trad. NI-DAQ.  I *think* that your config for Freq might not be right -- it doesn't seem right to use the same signal as the frequency to measure *and* as the sampling clock.  But I'm really not sure here.
 
4. I see no config to set the sampling rate for the encoder measurement.  I don't know what the default behavior might be.
 
Remedy:  I suspect you'll want to correlate position and speed, so why not use the same sample clock signal?
 
5. Your Read quantities are likewise not sync'ed to read = #'s of samples from each task.  You'll want to wire a common '# to Read' input to them.
 
-Kevin P.
Active Participant
Conseils
Posts: 585
0 Kudos

Re : RPM as a Function of Crank Angle (optical encoder - counters)

I am absolutely sure that Kevins advice is spot on, but you mention crank angle (from an engine ? ) may I therefore venture to suggest that the system is highly likely to suffer from cyclic irregularities as a result of the firing of the engine and also as a result of the speed control mechanisim.

If all you want is the speed then take a set number of pulses into the buffer ( I suggest multiples of two revolutions) and sum the pulse periods.

Just out of curisoty, how many pulses per rev does your encoder have?

Member
Milqman
Posts: 114
0 Kudos

Re: Re : RPM as a Function of Crank Angle (optical encoder - counters)

Ok, let's try to get all the questions answered.

I have 2 different encoders for 2 different systems (256 count and 1024 count).
It is an engine, but I need fine-speed (speed through a single cycle) not coarse speed (average speed per rev).  If I wanted to do that, I would put a period/freq measurement on the index signal.

The two are sync'd in time (I think).  In the EncoderPosition task, I have it use the A signal as the sample clock (it's inside the task definition), and you can see I route the A terminal over to the sample clock for the Speed Task.  One of my concerns here is how often (with what precision) I can use that one signal for all of these different purposes.

I will take a look at the arm trigger.

The finite/continuous choice is made in the task as well (I was worried that you guys would not be able to see what I was doing in there).  I had it first set to have the PulsesPerRev be the number of acquired point, you can still see those property nodes on the block diagram.  For some reason, this was giving me buffer errors (I think it is because it would spin through, and I would not be able to get the data out of the buffer fast enough, but I am not sure).

It was this error:
Error code: - 200279
Description: Attempted to read samples that are no longer available. The requested sample was previously available, but has since been overwritten.
Possible cause: The application did not retrieve the data from the buffer fast enough, so the data was overwritten.
Solutions: Increasing the buffer size or reading the data more frequently may correct the problem.

I just recently took the DAQ NI class, the period vs freq measurements really are just a division (no extra voodoo).  I assumed it would just be faster if it did it, instead of needing to perform the machine code of whatever version of division I implement.

Are my read quantities really not sync'd?  I thought that the SampQuant.SampPerChan property node did that for me (you can see I have both tasks lined up to do that).  If this is not the case, then I need to figure out how to do it.  And really, I would like to go to finite acquisition instead of continuous (the graph ends up looking dumpy on the other end, and you need to adjust the period to get it to look right at any given speed, which we really do not have the time to be doing).  Having finite acquisition would be a big step in the direction I want to go.

I ran this as is, and it works with the 1024 PPR encoder at 1200RPM (a little over 20kHz).  Eventually I will only need to use a 256 counter (which will give me more precise measurements with less resolution) but before I can use that rig, I need to have a solidly working proof of concept on this rig.

Any help would be great :smileyhappy:

Thanks,
~milq

I know how this is supposed to work, and what I want everything to do, but getting it to do that is a little tougher, and I have not quite built up the intuition to get everything working together within the set of rules defined by LabVIEW.
Active Participant
Conseils
Posts: 585
0 Kudos

Re : Re: Re : RPM as a Function of Crank Angle (optical encoder - counters)

Well depending on the computer hardware you might be having problems with the 20Khz throughput and so a 256 pulse will help but I suspect you could readily go lower (64), this will also have the effect of increasing the upper RPM that you can readily handle (you don't indicate what the RPM range is so watch out for low speeds below about 700RPM with 64 PPR). One other thought is to take care of the outer body motion of your sensor as this will have a huge effect on the measurement and also watch out for artificial resonances in the sensor / adaptation. Depending on the transducer you could end up with quite different measurements, light ones are not always optimal! You are going to have to spend quite a bit of time on the aforementioned.

Some encoders don't work well at these rates (20KHz is not actually the real frequency) and you need to be sure that the encoder you are working with can handle the required rates! Another problem on an engine can be heat soak into the sensor, as if they use infra red sensors inside the unit you can have issues.

I don't use quadrature encoders for this type of measurement so I don't fell well placed to comment on the configration of this part of your set up as I can't set any thing up to test and code. I do however, effectively use the Continous Buffered Period Measurement example. I have used all versions from LabVIEW 6.1 and on for this. If you take one channel then you could have two rotations giving a set of data every 10 seconds. This will be a walk in the park for any E series PCI board or M series board on a 1GHz CPU and Windows. The M series will easily handle two channels, the E series will struggle on the older hardware. The 6601 and 6602 are also good work horses. You could readily arm the counters to start from a set position if required, I apologise if this is not what you wanted to do and is distracting too much.

Don't forget that even one lost pulse will make the data pretty uselss if your processing on a block by block basis, you can try to compensate if you get time to bash some code together..

If you are getting bad data (high frequency noise / pulses) then you will get buffer overflow and this can also happen if there is lots of out of plane vibration being sent to typical sensors.
Active Participant
Kevin_Price
Posts: 1,945

Re: Re : RPM as a Function of Crank Angle (optical encoder - counters)


The two are sync'd in time (I think).  In the EncoderPosition task, I have it use the A signal as the sample clock (it's inside the task definition), and you can see I route the A terminal over to the sample clock for the Speed Task.  One of my concerns here is how often (with what precision) I can use that one signal for all of these different purposes...   I will take a look at the arm trigger.

What I see out of sync is the start times.  Your error cluster enforces dataflow sequencing such that your EncoderPosition task starts before you configure and start the CoarseSpeed task.  Any A edges that occur between the two start times will buffer measurements into the Pos task but not into the not-yet-started Speed task.  And there's really no way to know exactly how many samples of offset you'll get.  (I guess I'm kind of assuming that the encoder is already spinning when you start this vi so that A edges are coming in prior to running the vi).

The "Arm Start" trigger is the feature that will let you sync the two start times.  You'll need to Start both tasks before issuing the trigger edge.

The finite/continuous choice is made in the task as well (I was worried that you guys would not be able to see what I was doing in there).  I had it first set to have the PulsesPerRev be the number of acquired point, you can still see those property nodes on the block diagram.  For some reason, this was giving me buffer errors (I think it is because it would spin through, and I would not be able to get the data out of the buffer fast enough, but I am not sure).

I can't comment much here because I never use global tasks, so I don't know what might be configured there behind the scenes.  I normally call the regular DAQmx Timing vi rather than the Timing property node, and in *that* context the "# of samples" input would relate to a buffer size.  The # samples to read at a time is set in the call to DAQmx Read.  Your Read calls leave that input unwired, so the default is used.  I don't know whether it can use some global task default you had already set up, but whenever otherwise unspecified, the default behavior is "Read all available samples."

It was this error:
Error code: - 200279
Description: Attempted to read samples that are no longer available. The requested sample was previously available, but has since been overwritten.
Possible cause: The application did not retrieve the data from the buffer fast enough, so the data was overwritten.
Solutions: Increasing the buffer size or reading the data more frequently may correct the problem.

Sounds like perhaps you've set the buffer size exactly equal to the # points to read at a time.  There isn't time enough after filling the buffer to retrieve all the data before the next sample needs an available place to be stored.  Try making the buffer, say, 10x the size of the # to read.  That will let it, well, um, *buffer* your samples. 

I just recently took the DAQ NI class, the period vs freq measurements really are just a division (no extra voodoo).  I assumed it would just be faster if it did it, instead of needing to perform the machine code of whatever version of division I implement.

Sounds sensible to me.  The question remaining in my mind, is that the config seems different than I'd be doing for period measurement.  I'd have a call to DAQmx Timing set to "Implicit (Counter)" because the input signal whose period I'd be measuring *is* the sampling clock.  What you've got config'ed *looks like*

Are my read quantities really not sync'd?  I thought that the SampQuant.SampPerChan property node did that for me (you can see I have both tasks lined up to do that).  If this is not the case, then I need to figure out how to do it. 

I'll describe my reasoning b/c I don't know about global tasks enough to try to take them into account.  By my reasoning, the property "SampQuant.SampPerChan" will only affect the size of the buffer used for the data acq task, and not the # of values to read.  Then, a call to DAQmx Read with no wire specifying "# to Read" will by default read whatever # happen to be available.  There's nothing forcing the two tasks to have the same # available on every call so you're liable to get different #'s of samples back from the two tasks.  This aspect of being out-of-sync is *in addition to* having the start times be out-of-sync.

Solution: Use Arm Start Trigger to get start times in sync.  Set a buffer size that is much bigger than the # of samples to read at a time.  Be sure both tasks sample off the same signal.  Wire in the same # samples to read from both tasks in your loop. 

And really, I would like to go to finite acquisition instead of continuous (the graph ends up looking dumpy on the other end, and you need to adjust the period to get it to look right at any given speed, which we really do not have the time to be doing).  Having finite acquisition would be a big step in the direction I want to go.

I'd think you could switch over to finite sampling with minimal changes.  Did you encounter some problem when trying?

[...most of a busy day passes...]

<Homer Simpson> D'oh! </Homer Simpson>

You're measuring an engine!  Things can get much simpler now because your motion is unidirectional!  I locked into the notion that position encoder = quadrature bidirectional position.  Not so, not so.  I'll leave the stuff above for the historical record or whatever, but the stuff below should be the better answer.

All you need is the Freq (or period) measurement, using some sort of 1-per-rev reference signal as an Arm Start Trigger.  As you accumulate measurements, the array values are the speeds while the array indices imply cumulative distance traveled.  Voila!  Now you're down to 1 counter task and all that sync'ing nuisance goes away...

-Kevin P.

Member
Milqman
Posts: 114
0 Kudos

Re: Re : RPM as a Function of Crank Angle (optical encoder - counters)

Wow!  Thank you, that is the exact kind of stuff I was looking for.

I had not thought of the single counter option.  What you guys don't see is what my host app looks like (and rightfully so because it would only confuse the issue).  I need to pluck out different angles and convert them to something more meaningful (think piston/cam positions as a function of crankshaft angle).  Having that explicitly done by the encoder was convenient for that purpose.  Can you think of a fast way to assign an angle index to these numbers as they pour out?  Other than a loop that iterates for the size of the number of acquistions that uses the index of the loop to assign that index of the array's first component to be a certain number (360/PPR) times the index of the loop.

sort of like

for (x=0;x<size;x++)
{
AcqisitionArray[1][x] = x * (360/PPR)
}

Sounds like a lot of array manipulation.  I have a weekend to mull it over I guess (my weekend starts in a few minutes . . . WOOOO no friday!)

Thanks a bunch for your help, I am gonna sit on this and try to integrate the arm/buffer ideas you posted.  I owe you a drink!

~milq
Active Participant
Kevin_Price
Posts: 1,945

Re: Re : RPM as a Function of Crank Angle (optical encoder - counters)

[ Edited ]
You'll probably want the "position" to wrap around and restart at 0 after it gets to 360, right?  The "Quotient & Remainder" function is useful for that.

Here's an idea to get you started (see screenshot attachment). 


Note that "offset" in and out are meant to represent a cumulative total # of samples passed through this converter.  In your loop, offset out would go into a right-hand shift register while offset in would come from its corresponding left-hand shift register. 

-Kevin P.

Message Edited by Kevin Price on 10-27-2006 08:11 AM

Active Participant
Conseils
Posts: 585
0 Kudos

Re : Re: Re : RPM as a Function of Crank Angle (optical encoder - counters)

[ Edited ]

Your right it's a lot of array bashing, but at the rates I have indicated it's not very 'expensive' on the system and you will have heaps of free capacity for other things.

You might like to take some of the heartache out of the process and get the order analysis toolkit. There is some pretty neat stuff in it and there may be other tools that you don't yet know you need. On the negative side, it's yet another thing to have to spend that precious capital budget on (Mine is generally near zero, I think next year they may start cutting of limbs :smileywink:) ) and the toolkit is very expensive.

What's the final purpose of the data, it would be cool to know a bit more.

Order Anlaysis Toolkit Link

http://sine.ni.com/nips/cds/view/p/lang/en/nid/10706

Message Edité par Conseils le 10-27-2006 04:36 PM

Member
Milqman
Posts: 114
0 Kudos

Re: Re : Re: Re : RPM as a Function of Crank Angle (optical encoder - counters)

So I did another rev of the VI <shown here>



This time instead of using globals, I just made everything explicit.  An arm trigger would require me to set up a button to a digital out that I'd send to a PFI, that I would then call my arm trigger (which I could do, but it sounds silly).  Instead I set it up as shown.  Do you guys see a problem with this?  Each of the trigger/timing blocks half imply that they are computing the buffer on their own, but none of them explicitly state how much room I have in the buffer.  I suppose if I set up the ability to properly get one rotation of data, then I can set period arbitrarily large to try and slow the loop down so it does not fill the buffer so quickly (or would it fill the buffer faster because it is still recording and I am not reading it?)  Any things you guys would like to point out here?  You have been super helpful up to this point :smileyhappy:

~milq

P.S. the measurement time constant is gone now, and the error line is connected between start and read on the speed task, I just didn't feel like taking a new picture.