Best way to measure period between pulses? Counters and FPGA

We have a digital optical sensor, and we want to measure the time between between pulses. Once we get the time, we would then calculate the rotational speed of a wheel.

I thought the Counter class did this for us using the FPGA?

Using the GearTooth class (which extends Counter), for some reason we’re getting very poor resolution from getPeriod().

GearTooth geartooth = new GearTooth(1);
rpm = ??? / geartooth.getPeriod();

Our RPM steps up and down in increments of 375 RPM!

You might ask how many pulses per revolution we have, but should that even matter? I was under the impression that the resolution should be dependent on the precision of the timing device?

Ironically, we did the math, and the resolution of 375 RPM is what we would get if we polled the Counter every 20ms, and calculated the period by dividing 20ms by the number of pulses received.

What am I missing here? Do I need to explicitly set up the Counter to use the FPGA / Timers / etc? Or is this all a ruse, and the Counter doesn’t measure time between pulses at all, but polls every 20ms instead?

Has anyone successfully calculated a wheel speed by measuring time between pulses using the FPGA via the Counter/GearTooth class?

I need to clarify this post…

I am having problems with how the period is being calculated.

There is a BIG difference between these two approaches:

  1. Polling the counter every 20ms and counting how many pulses occurred. Period = 20ms / pulses

  2. ACTUALLY measuring the time between two pulses using the FPGA. Period = the measured time

Approach 2 results in a much higher resolution in our application, and the documentation in WPILib is unclear when 1 or 2 is used with Counter/GearTooth objects.

From looking at source more, I’m lead to believe approach 2 ONLY happens if you’ve specifically setup a 4x Encoder object and you can’t do approach 2 at all with a Counter/GearTooth object.

Has anyone else seen otherwise?

Well, to answer my own question…

It seems it is possible to directly measure time between transitions with a Counter object by using SemiPeriodMode.

Thanks to Joe Hershberger’s post on the FIRST Forums here:
http://forums.usfirst.org/showpost.php?p=27558&postcount=12

I am currently trying to do the same thing, using a light sensor to create a custom tachometer. As it has been explained to me (correct me if I am wrong) the crio FPGA has an integrater function built in, so even if pulses moving faster than 20ms can be measured. I calculated that on our shooter even with a 1 tick tachometer it will be sending a pulse every 1-2ms, which may be too fast for the AndyMark optical sensors, but I haven’t checked too closely. However, it does seem like a good idea for certain high speed feedback.

That’s not quite an accurate interpretation of my post. You can measure the frequency in any of the counter / encoder modes. The point I was trying to make was that the semi-period mode is pretty much useful only for timing.

From reading your application description, you probably want to use Up/Down counter mode, and only specify the “up source”. When you read the timer output, it will be reporting the speed based on “method 2” that you described above.

Cheers
-Joe

So for our shooter last year we used a counter with a light sensor. How we got the correct value is we took the period, then took the reciprocal. We then divided that number by how many pulses you get per rotation, and then multiply that by 60. That gives the RPM.

Oooh, we were doing this today! Thank you for potentially solving any questions that we might have come up with!

Also, teams who have done this before, how accurate is finding the RPM using an optical sensor compared to using an encoder?

The typical quadrature encoders used on FRC robotis are optical sensors. The accuracy is going to be the same, assuming the sensor detects all the marks it’s looking for. The precision will be a function of the number of “ticks” per revolution and the consistency of the spacing of the light and dark marks. In general, more marks seen going by in a given time gives better precision.

This is odd, because all weekend we were have major resolution problems.

We had a Counter set up exactly as you describe above. The periods we were getting had a very pronounced stepwise response. As a result, our RPM readings changed in increments of about 375 RPM.

We did the math, and these increments corresponded with 1 count / 20ms, which led us to believe the reads were still being done via method 1.

Very strange.

Is it possible that the other modes (other than semi-period) use the FPGA to count pulses over a 20ms period instead of measuring the time between pulses directly?

So in theory, is it possible to use the WPI Encoder Libraries with an optical sensor and a piece of reflective tape?


public double getPeriod() {
        double period;
        if (m_counter.readTimerOutput_Stalled()) {
            return Double.POSITIVE_INFINITY;
        } else {
            period = (double) m_counter.readTimerOutput_Period() / (double) m_counter.readTimerOutput_Count();
        }
        return period / 1.0e6;
    }

Something isn’t jiving here. In the above code, why would you take the period and divide by the count? If the FPGA is measuring the period directly, should we just be taking the period directly? Will count ever be anything other than 1?

The FPGA encoder support requires a pair of sensors in order to give a quadrature signal. In one direction of motion, A goes high, then B goes high, then A goes low, then B goes low. In the other direction, B goes high, then A goes high, then B goes low, then A goes low. If you can arrange your sensors and marks so that’s what you get, the WPI library encoder functions should work.

But the counter functions should work just fine with a single sensor.

The FPGA doesn’t return floating point values, so the period isn’t in units you can use directly. Presumably, the Period is actually the period in clock cycles, and the count is the number of clock cycles per second. Dividing those two gives you a period in seconds.

I dont know about in Java or C++, but in labview the library already converts it into period per pulse in seconds. So if you do 1/x on that, you get pulses per period. Then you have pulses per second, and that is easy to convert to rotations per minute with just unit conversions. We used geartooth mode for this purpose and it worked perfectly.

As described in another post, hidden in the LabVIEW programming forum (even though we were doing this in Java), Team 1519 had success last year using the Encoder object at 1X sampling to count pulses from a USDigital E7P encoder with a 250CPR optical wheel spinning at around 4000RPM. However, we performed our RPM computation in a different manner than using the built-in functions. We found our approach to give excellent accuracy. This same strategy should work just fine for a Counter object.

First off, we set up a separate task in Java to periodically compute the RPM of our shooter wheels. (This task also will control the motor for the wheels, too.) Below is a code excerpt showing how to set this up:


    private Encoder wheelEncoder = new Encoder(RobotMap.SHOOTER_WHEEL_ENCODER_A, RobotMap.SHOOTER_WHEEL_ENCODER_B, false, CounterBase.EncodingType.k1X);
    private static final long WHEEL_SPEED_PERIOD = 20; // milliseconds

    timer = new java.util.Timer();
    timer.scheduleAtFixedRate(new ShooterWheelTask(), 10000, WHEEL_SPEED_PERIOD);

Below is the definition for the ShooterWheelTask, of which the “run” method will be executed every 20 milliseconds. This code computed the wheel RPM by dividing the number of counts observed since the last invocation by the elapsed time since the last invocation, with appropriate unit conversions. Time was measured using the FPGATimestamp, which has a resolution of [strike]approximately 6.5usec[/strike] 1usec. (Edit: updated resolution per jhersh posting, below.) We then compared the measured RPM against the desired RPM, and used a “Bang - Bang” controller to set the wheel speed. This simplistic approach worked incredibly well. We plan to use it again this year.

We can’t take the credit for the “Bang - Bang” controller application – lengthy CD discussions on this last year led to Ether’s white paper on the subject.

Below is a snippet of our “ShooterWheelTask” code. Various declarations of variables are not in this code snippet, but I think you’ll be able to figure out what they are from the context.

PS: See you at GSR, Mr. Lim!


    private class ShooterWheelTask extends java.util.TimerTask {
        public void run() {
            double power;
            
            double now = Timer.getFPGATimestamp();
            int count = wheelEncoder.get();

            wheelEncoder.reset();
            // NOTE:  60 seconds per minute;   250 counts per rotation
            actualWheelSpeed = (60.0 / 250.0) * count / (now - prevWheelTime);
            prevWheelTime = now;
            
            if (actualWheelSpeed >= wheelSpeed) {
                power = 0;
            } else {
                power = 1;
            }
            
            if (automatic) {
                setWheelJag(power);
            }
            else {
                setWheelJag(0);
            }
        }
    }

This is “method 1” above. This is what he claims not to want.

This is inaccurate. The FPGATimestamp itself has a resolution of 1 us. The digital I/O that it timestamps (in the case of digital interrupts or encoder pulse measurements, etc.) is 6.525 us.

Cheers,
-Joe

The variables are not terribly verbose… the “count” is the number of samples in the sliding window average. Count will always be 1 if you specify not to average (NumberOfSamplesToAverage = 1).

The FPGA does not like to divide without using up a bunch of gates, where and the RT CPU with an FPU has no problem doing this. This is an optimization where all the addition and synchronization is done in hardware where it needs to be done, but the divide operation to get the actual average period is pushed to the driver.

If you want “Method 1”, you have to do it the way Ken Streeter described. If you want “Method 2”, you must use the FPGA.

Cheers,
-Joe

Thanks for the correction, Joe! I’ll edit my post above to fix it!

There is a finite resolution on the timing of the pulses. That period is 6.525 us per edge. In your system, does that correspond to 375 RPM? You may want to turn on averaging to improve the resolution (but increase the latency).

Sounds like a coincidence, maybe? Where are you getting 20ms as the refresh period? Just because that’s the Driver Station’s packet rate?

No. The FPGA only implements method 2.

Close. The “period” is in microseconds. The “count” is the number of samples accumulated by the averaging engine. The code also divides by 1.0e6 before returning to give the required “seconds” unit.