RPM Sensing

We’re trying to build a flywheel and measure the rpm with a shaft encoder but every time the encoder reaches a value of ±32760 the value negates and runs back. Is there any way to stop this from happening or a better sensor for measuring the rotations of a wheel?

Interesting. I never had this problem last year. Where in your gearbox is the encoder?

You could gear down to your encoder so that you get more revolutions before it roles over. Maybe use a Long Int variable, if you aren’t already. Always check the previous and current values before calculating, and if it has rolled over, drop that data point and reset to 0.

We never had this problem last year, when we were running our flywheel for 20+ minutes at a time while testing. Even if the value had reset (which it might have), we never stored our values for more than two loops (current & last), so it’d simply have a negative velocity for a fraction of a second before returning to normal.

It will always be counting up, so yes, if you wrote your code well, then you would just get a very small percentage of garbage values. Should be there and gone fast enough not to hurt anything.

That number looks familiar. I think are declaring an


int

for your variable. You may want to use a


long

to assign your encoder results.


long myTicks = SensorValue[myEncoder];

Source: https://en.wikipedia.org/wiki/C_data_types

This has nothing to do with the sensor. You should use a variable type that will hold a larger variable.

What you are seeing is the number incrementing into the sign bit. RobotC “int” type is a 2 byte signed number, which means you have 15 bits for the value and one bit available for the sign. The highest value that will fit into 15 bits is: 32767. If you add one to 32767 in a 2 byte (16 bit) number that is being interpreted as “signed” the result is -1. As you continue to increment the number, it will go more and more negative.

32767 in binary: 0111 1111 1111 1111
-1 in twos complement binary: 1000 000 0000 0000

Changing to unsigned int will double the positive range of values available. Here are the ranges:

int: -32,768 to 32,767
unsigned int 0 to 65,535
long 4 bytes -2,147,483,648 to 2,147,483,647
unsigned long 4 bytes 0 to 4,294,967,295

Choose the smallest variable type that gets you what you want.

Perhaps I misunderstand you, but the number will actually be less negative as the encoder continues to turn. When the number wraps around to the smallest value (a large negative value), the next values will make the number larger (less negative) until you reach the positive range again.

Additionally, RobotC has a bug where SensorValue] will always return an int regardless of the variable type you store the result in. Instead, you should add your quads in the motor tab under motors and sensors config and then access it with nMotorEncoder] to get the full range of values (a long).

Yes, of course. I was distracted while writing that, and didn’t pay attention to my own answer.

It counts up from
-32768 in two’s complement binary: 1000 0000 0000 0000
to
-1 in two’s complement binary: 1111 1111 1111 1111

That’s not a bug; that’s just how it has to work. But most importantly, what it returns doesn’t matter as much as you might think.

First off, just tell the compiler you want to interpret the number as unsigned. Then, if you really want to accumulate the value into something larger, you can:

   unsigned int encoderVal;
    unsigned int previousVal=0;
    unsigned long bigEncoderVal=0;
    int notDoneLooping=1;

    while (notDoneLooping)
    {   
        // cast the value from SensorVal as an unsigned int to avoid warnings.
        encoderVal = (unsigned int)SensorVal[myEncoder];
        // add the change into our accumulator
        bigEncoder += encoderVal - previousVal;
        // hang on to the curren value to use next round
        previousVal = encoderVal;
    }

Note that this only staves off your problem for a little while, but it illustrates how to handle both the “signed int” and the overflow problem. A more complete solution would be to notice when the encoder value was close to a rollover, and to reset it to zero right after you read it.

    unsigned int encoderVal;
    unsigned int previousVal=0;
    unsigned long bigEncoderVal=0;

    // Set the highDipstickLevel to something close to the max value an unsigned int can hold,
    // but low enough that the encoder won't overflow in a single pass
    // assuming the encoder count can't get from 60,000 to 65536 in a single loop pass
    unsigned int highDipstickLevel = 60000; 
   int notDoneLooping=1;
    while (notDoneLooping)
    {   
        if (previousVal > highDipstickLevel)
        {
            // cast the value from SensorVal as an unsigned int to avoid warnings.
            encoderVal = (unsigned int)SensorVal[myEncoder];
            SensorVal[myEncoder]=0;
        }
        else
        {
            // cast the value from SensorVal as an unsigned int to avoid warnings.
            encoderVal = (unsigned int)SensorVal[myEncoder];
        }  
        // add the change into our accumulator
        bigEncoder += encoderVal - previousVal;
        // hang on to the curren value to use next round
        previousVal = encoderVal;
        // here's where we should put some test to get us out of the loop
        if (bigEncoder > triggerVal)
        {
            notDoneLooping=0;
        }
     }

The reason for all the fun with highDipstickLevel instead of just resetting every time you read the value is a common one in soft realtime systems like this. Namely, there is no way to avoid losing some encoder changes between when the value is read and when you set it to zero. This is typically described as “atomic test and set.” You’d like to be able to read the value and set it to zero in the same clock cycle so that the shaft the encoder is attached to won’t move in between. We don’t have that option in the Cortex, so one of the typical things to do is to limit frequency with which you lose data.

I typed this directly into the forum post, I’ve never run it, and it could easily contain mistakes. Probably does. But it should get the idea across.

Not sure what you are all seeing, SensorValue will return an “int” which is 32 bits with RobotC set to default settings. The sensor debug window only displays as a short (which I guess is a bug, long ago RobotC used 16 bit ints and this is left over from that) and you will see the value jump from 32767 to -32768, however, the value that you read in the code does not do that. Try this program, you will see values keep incrementing.

#pragma config(Sensor, dgtl2,  enc,            sensorQuadEncoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

task main()
{
  while(1) {
    writeDebugStreamLine("%d", SensorValue[enc]);
    wait1Msec(1000);
  }
}

OP, which version of RobotC do you have?

Just as FYI, this is where you could force an int to be 16 bit (ie. short) in RobotC V4, we did this for compatibility with programs written using RobotC V3.
int_setting.png

Well, I wasn’t seeing anything; just made the assumption the report was correct. Haven’t done OSE code in a while; should have tried it.

Yeah, now that you mention it… I knew this. Too tired to remember.

Thanks, as always, for the enlightenment.