EasyC

I am trying to help a student debug a program in EasyC. It’s a basic P controller (no ID).

A quad encoder is being used to measure the change in degrees of rotation, and a timer is being used to calculate the change in time. From this the angular velocity is calculated (in rotations per second). The measured speed is subtracted from the goal speed to find the error.

The speed is then set using the formula
Power = Power + Kp*Error;

Power is then limited to values between 0 and 127 with if statements.

I understand that there are many ways to improve this program, but this student is pretty new so I just wanted him to focus on the proportional term first. However, it isn’t working as expected. In an effort to debug, I am displaying the values of the variables on the graphic display. The values from the quad encoder, the time, the angular speed, and the error all look great and respond as expected when we manually turn the flywheel. However, when I try to display the power, it says “NaN,” which through research have discovered means “not a number”. These numbers are all floats.

Here’s the weird part.

In my efforts to help him debug, we found nothing to fix this. FInally, on a whim, I took the error value and multiplied it by 1000 and stored it in a variable called errorint (which is an integer). I then stored the value of errorint in a variable called errorfloat (which is a float). I then divided errorfloat by 1000 (the multiplying and dividing by 1000 was done to preserve 3 decimal places). Now when I set the power as

Power = Power + Kp*errorfloat;

It works great!

So what’s up with this??? What reasons could there be why my original float value stored in error was no good, but the new float value found by first converting it to an integer and then back to a float worked?

I have attached the easyc code, which has already been fixed.

#include "Main.h"

void OperatorControl ( unsigned long ulTime )
{
      int leftup = 1; 
      int leftdown = 1; 
      int rightdown = 1; 
      int rightup = 1; 
      int intakespeed1 = 127; 
      int intakespeed2 = 100; 
      int launchhigh = 0; 
      int launchoff = 0; 
      int launchlow = 0; 
      int launchmed = 0; 
      int errorint = 0; 
      float errorfloat = 0; 

      StartQuadEncoder ( 1 , 2 , 0 ) ;
      StartTimer ( 1 ) ;
      PresetTimer ( 1 , 0 ) ;
      while ( 1 )
      {
            Holonomic ( 1 , 2 , 1 , 4 , 9 , 2 , 8 , 1 , 1 , 1 , 1 , 0 ) ;
            leftdown = GetJoystickDigital ( 2 , 5 , 1 ) ;
            leftup = GetJoystickDigital ( 1 , 5 , 2 ) ;
            rightdown = GetJoystickDigital ( 2 , 6 , 1 ) ;
            rightup = GetJoystickDigital ( 1 , 6 , 2 ) ;
            launchmed = GetJoystickDigital ( 2 , 8 , 3 ) ;
            launchlow = GetJoystickDigital ( 2 , 8 , 1 ) ;
            launchoff = GetJoystickDigital ( 2 , 8 , 4 ) ;
            launchhigh = GetJoystickDigital ( 2 , 8 , 2 ) ;
            if ( rightdown==1 && leftdown==0 )
            {
                  SetMotor ( 10 , -intakespeed1 ) ;
            }
            else if ( leftdown ==1 && rightdown ==0 )
            {
                  SetMotor ( 10 , intakespeed1 ) ;
            }
            else
            {
                  SetMotor ( 10 , 0 ) ;
            }
            if ( rightup ==1 && leftup ==0 )
            {
                  SetMotor ( 7 , -intakespeed2 ) ;
            }
            else if ( leftup ==1 && rightup ==0 )
            {
                  SetMotor ( 7 , intakespeed2 ) ;
            }
            else
            {
                  SetMotor ( 7 , 0 ) ;
            }
            if ( launchhigh ==1 )
            {
                  goalspeed=1.6;
            }
            else if ( launchmed ==1 )
            {
                  goalspeed=1.35;
            }
            else if ( launchlow ==1 )
            {
                  goalspeed=1.2;
            }
            else if ( launchoff ==1 )
            {
                  goalspeed=0;
            }
            Quad = GetQuadEncoder ( 1 , 2 ) ;
            Time = GetTimer ( 1 ) ;
            PrintTextToGD ( 1 , 0 , 0 , "Quad Value: %f\n" , Quad ) ;
            RPS = ((Quad-LastQuad)/360)/((Time-LastTime)/1000);
            PrintTextToGD ( 0 , 0 , 0 , "RPS: %f\n" , RPS ) ;
            PrintTextToGD ( 5 , 0 , 0 , "goalspeed: %f\n" , goalspeed ) ;
            LastTime = Time;
            LastQuad = Quad;
            error = goalspeed - RPS ;
            errorint = error*1000;  
            errorfloat = errorint;
            errorfloat = errorfloat/1000;
            PrintTextToGD ( 13 , 0 , 0 , "errorint: %d\n" , errorint ) ;
            PrintTextToGD ( 14 , 0 , 0 , "errorfloat: %f\n" , errorfloat ) ;
            power=power + kp*errorfloat;
            PrintTextToGD ( 6 , 0 , 0 , "power: %f\n" , power ) ;
            PrintTextToGD ( 4 , 0 , 0 , "error: %f\n" , error ) ;
            PrintTextToGD ( 7 , 0 , 0 , "time: %f\n" , Time ) ;
            if ( power>127 )
            {
                  power=127;
            }
            else if ( power<0 )
            {
                  power=0;
            }
            SetMotor ( 3 , power ) ;
            SetMotor ( 6 , power ) ;
            Wait ( 100 ) ;
      }
}

Any variable that you don’t see defined there is defined as a global variable (because he was initially trying to do part of his code as a user function. We tried switching them to be local variables, and there was still the same problem.

I don’t answer EasyC questions anymore, however, with a name of “Wombat_of_Doom” I could’'t resist. The problem is the first calculation of RPS. The timer has been reset and by the time it is first read Time will still be read as 0 (almost no time has elapsed). For the first loop LastTime will also be zero, so this equation


RPS = ((Quad-LastQuad)/360)/((Time-LastTime)/1000);

Will have a divide by 0 and essentially cause RPS to be infinity (or NaN as well depending on what the compiler does). This has the knock on effect of making error NaN and therefore setting power to NaN. Once power becomes NaN it remains so as it is involved with each calculation, ie. you add something to NaN. Using this code


errorint = error*1000;  
errorfloat = errorint;
errorfloat = errorfloat/1000;

is forcing errorint to be some undefined number for the first loop and, although power will now be also invalid for that first loop, everything sorts itself out during the second loop as RPS is now valid.

either have a delay before the loops starts or check for divide by 0 and avoid doing it. for example.

           
if((Time-LastTime) != 0)
    RPS = ((Quad-LastQuad)/360)/((Time-LastTime)/1000);
else
    RPS = 0;

In ROBOTC we trap divide by 0 and throw an exception.

TL;DR
Divide by 0 is BAD.

Thank you so much! We’ll give this a try. I didn’t think of this because I figured time would eventually be ticking by and therefore not divide by zero, but it makes sense that if it does it just once (the first time through) it would essentially never let go of the error.

You da man.