Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SetSampleTime(0) doesn't change the internal SampleTime class member #110

Open
paynterf opened this issue Apr 7, 2021 · 15 comments
Open

Comments

@paynterf
Copy link

paynterf commented Apr 7, 2021

Passing in a value of '0' for SetSampleTime() doesn't actually do anything, as the 'if' statement is coded incorrectly.

void PID::SetSampleTime(int NewSampleTime)
{
   if (NewSampleTime > 0)
   {
      double ratio  = (double)NewSampleTime
                      / (double)SampleTime;
      ki *= ratio;
      kd /= ratio;
      SampleTime = (unsigned long)NewSampleTime;
   }
}

should be

void PID::SetSampleTime(int NewSampleTime)
{
   if (NewSampleTime >= 0)
   {
      double ratio  = (double)NewSampleTime
                      / (double)SampleTime;
      ki *= ratio;
      kd /= ratio;
      SampleTime = (unsigned long)NewSampleTime;
   }
}

Frank

@br3ttb
Copy link
Owner

br3ttb commented Apr 7, 2021 via email

@paynterf
Copy link
Author

paynterf commented Apr 8, 2021

Hmm, you are correct - a value of zero should make Kd go to infinity (unless it is zero to start with, and then it is just 'undefined' -- oops!

I was trying to use the timing supplied by an external timer interrupt, and it actually seems to work; no idea now.

Looking a little deeper, it seems like there is no way to use an external timing source - is this correct?

TIA,

Frank

@br3ttb
Copy link
Owner

br3ttb commented Apr 8, 2021 via email

@paynterf
Copy link
Author

paynterf commented Apr 8, 2021

I generally do know that the function will only be called when eval is necessary. I was hoping that setting the sample time to zero would be accomplish that without having to modify PID.cpp but as you pointed out, that would result in Ki = 0 and Kd = infinity

What about doing it this way? This way SetSampleTime(0) turns off time checking entirely, without modifying Ki or Kd in the process. If PID.cpp were implemented this way, users wouldn't have to modify the library file in order to use interrupt timing.

Thoughts?

Frank

/* SetTunings(...)*************************************************************
 * This function allows the controller's dynamic performance to be adjusted. 
 * it's called automatically from the constructor, but tunings can also
 * be adjusted on the fly during normal operation
 ******************************************************************************/ 
void PID::SetTunings(double Kp, double Ki, double Kd)
{
   if (Kp<0 || Ki<0 || Kd<0) return;
 
   dispKp = Kp; dispKi = Ki; dispKd = Kd;
   
   double SampleTimeInSec = ((double)SampleTime)/1000;

   if(SampleTimeInSec > 0)
   { 
     kp = Kp;
     ki = Ki * SampleTimeInSec;
     kd = Kd / SampleTimeInSec;
   }
  if(controllerDirection ==REVERSE)
   {
      kp = (0 - kp);
      ki = (0 - ki);
      kd = (0 - kd);
   }
}
  
  
/* SetSampleTime(...) *********************************************************
 * sets the period, in Milliseconds, at which the calculation is performed	
 ******************************************************************************/
void PID::SetSampleTime(int NewSampleTime)
{
   if (NewSampleTime > 0)
   {
      double ratio  = (double)NewSampleTime
                      / (double)SampleTime;
      ki *= ratio;
      kd /= ratio;
      //SampleTime = (unsigned long)NewSampleTime;
   }

    SampleTime = (unsigned long)NewSampleTime;
}

@br3ttb
Copy link
Owner

br3ttb commented Apr 8, 2021 via email

@paynterf
Copy link
Author

paynterf commented Apr 8, 2021

I understand there are very good reasons why Ki & Kd should be modified when/if the sample time is changed. However, since you already skip over this code in the case of SampleTime == 0, it seems to me that the changes I suggested won't alter functionality at all, but will allow knowledgeable users to control update timing externally by setting SampleTime to zero.

Isn't what I'm suggesting functionally equivalent to manually editing my copy of PID.cpp to removing the

if(timeChange>=SampleTime)

line from PID::Compute()?

If I manually modify PID::Compute(), the modifications will be overwritten the next time the library is updated, but if you make the changes I suggested, the functionality before and after are unchanged except in the special case of SetSampleTime(0), which you disallow now anyways.

Frank

@br3ttb
Copy link
Owner

br3ttb commented Apr 8, 2021 via email

@paynterf
Copy link
Author

paynterf commented Apr 8, 2021

i also skip over the SampleTime assignment when you call it with (0). So
no, that isn't the same as removing the if statement in compute. calling
it with 0 is the same as not calling it all.

Uh, that's why I moved the SampleTime assignment line out of the 'if()' statement in SetSampleTime() - so it would be assigned even if the calling value was == 0. My thinking was that way we get the best of both worlds; a SampleTime value of zero skips any Ki, Kd adjustments in SetSampleTime() and in SetTunings(), while also causing Compute() to update every time it is called.

As you also pointed out, I can always fork the library, but I really don't want to do that; that's what happened to the Arduino I2C after Arduino refused to fix the I2C hangup bug for over a decade; dozens of alternate libraries got spawned, all with their own quirks and idiosyncracies and the whole thing was a huge mess. Fortunately Arduino finally came to their senses and fixed it last summer. The PID library is used in many different applications and I would rather not confuse programmers with 'yet another PID library'.

To turn this argument around, can you tell me what the downside would be to implementing these changes? It seems to me there would be zero functionality change for any positive non-zero argument for SetSampleTime(). However, a user who deliberately set the SampleTime to zero would get exactly what they were after - external control over Compute() timing without having to modify PID.cpp; as long as the external timing is reasonably regular, the output from Compute() would be identical to the output using an internal time interval with the same value. Obviously the final values for Ki, & Kd would be different for an external time interval different from the default 100mSec SampleTime, but that is a necessary part of the tuning procedure anyway.

Frank

@br3ttb
Copy link
Owner

br3ttb commented Apr 8, 2021 via email

@paynterf
Copy link
Author

paynterf commented Apr 8, 2021

Oh! Sorry if I missed the option of a separate constructor - that would be great!

Frank

@paynterf
Copy link
Author

paynterf commented Apr 9, 2021

How will the new constructor work? I'm a bit hazy on how it would change PID behavior to be interrupt controlled. Anything you can share would be appreciated ;-)

Frank

@br3ttb
Copy link
Owner

br3ttb commented Apr 10, 2021 via email

@paynterf
Copy link
Author

paynterf commented Apr 10, 2021 via email

@paynterf
Copy link
Author

paynterf commented Apr 10, 2021 via email

@br3ttb
Copy link
Owner

br3ttb commented Apr 12, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants