Arduino-PID-Library
Arduino-PID-Library copied to clipboard
Not getting default SampleTime after multiple compilations
I'm using PID_v2 in an Arduino application, and I get the following output:
MPU6050 connection successful
Initializing DMP...
Enabling DMP...
DMP ready! Waiting for MPU6050 drift rate to settle...
MPU6050 Ready at 0.52 Sec
TurnRatePID started with Kp/Ki/Kd = 5.0,0.8,3.0, kp/ki/kd = = 5.00,0.080,30.00, SampleTime = 30
End of test - Stopping!
From this code:
//05/31/21 latest & greatest PID values
double TurnRate_Kp = 5.0;
double TurnRate_Ki = 0.8;
double TurnRate_Kd = 3;
double TurnRatePIDSetPointDPS, TurnRatePIDOutput;
double TurnRatePIDInput = 15.f;
PID TurnRatePID(&TurnRatePIDInput, &TurnRatePIDOutput, &TurnRatePIDSetPointDPS, TurnRate_Kp, TurnRate_Ki, TurnRate_Kd, DIRECT);
void setup()
{
Serial.begin(115200);
#pragma region MPU6050
mySerial.printf("\nChecking for MPU6050 IMU at I2C Addr 0x%x\n", MPU6050_I2C_ADDR);
mpu.initialize();
// verify connection
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
float StartSec = 0; //used to time MPU6050 init
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// make sure it worked (returns 0 if successful)
if (devStatus == 0)
{
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for MPU6050 drift rate to settle..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
bMPU6050Ready = true;
StartSec = millis() / 1000.f;
mySerial.printf("MPU6050 Ready at %2.2f Sec\n", StartSec);
}
else
{
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
bMPU6050Ready = false;
}
#pragma endregion MPU6050
mySerial.printf("TurnRatePID started with Kp/Ki/Kd = %2.1f,%2.1f,%,%2.1f, kp/ki/kd = = %2.2f,%2.3f,%,%2.2f, SampleTime = %d\n",
TurnRatePID.GetKp(), TurnRatePID.GetKi(), TurnRatePID.GetKd(), TurnRatePID.Getkp(), TurnRatePID.Getki(), TurnRatePID.Getkd(), TURN_RATE_UPDATE_INTERVAL_MSEC);
#pragma region TIMER_INTERRUPT
//09/18/20 can't use Timer1 cuz doing so conflicts with PWM on pins 10-12
cli();//stop interrupts
TCCR5A = 0;// set entire TCCR5A register to 0
TCCR5B = 0;// same for TCCR5B
TCNT5 = 0;//initialize counter value to 0
// set compare match register for 5hz increments
//10/11/20 chg timer interval to 10Hz vs 5
//OCR5A = 3124;// = (16*10^6) / (5*1024) - 1 (must be <65536)
OCR5A = 1562;// = (16*10^6) / (10*1024) - 1 (must be <65536)
TCCR5B |= (1 << WGM52);// turn on CTC mode
TCCR5B |= (1 << CS52) | (1 << CS50);// Set CS10 and CS12 bits for 1024 prescaler
TIMSK5 |= (1 << OCIE5A);// enable timer compare interrupt
sei();//allow interrupts
pinMode(TIMER_INT_OUTPUT_PIN, OUTPUT);
#pragma endregion TIMER_INTERRUPT
mySerial.printf("End of test - Stopping!\n");
while (true)
{
CheckForUserInput();
}
}
I expected the SampleTime to be 100 after the default PID constructor, but it isn't - it's still the same value I set later in the code. I have tried a full 'Clean' build, but get the same (unexpected) result.
What gives?
Frank
Made another run trying to figure this out. I instrumented PID_v2.cpp to report the actual value of the private SampleTime member as follows:
/* SetSampleTime(...) *********************************************************
* sets the period, in Milliseconds, at which the calculation is performed
******************************************************************************/
void PID::SetSampleTime(int NewSampleTime) {
if (NewSampleTime > 0) {
PIDSerial.printf("In SetSampleTime, SampleTime now %2.1f, Changing to %2.1f\n",
(double)NewSampleTime, (double)SampleTime);
double ratio = (double)NewSampleTime / (double)SampleTime;
ki *= ratio;
kd /= ratio;
SampleTime = (unsigned long)NewSampleTime;
}
}
using this Arduino code:
void setup()
{
Serial.begin(115200);
#pragma region MPU6050
mySerial.printf("\nChecking for MPU6050 IMU at I2C Addr 0x%x\n", MPU6050_I2C_ADDR);
mpu.initialize();
// verify connection
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
float StartSec = 0; //used to time MPU6050 init
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// make sure it worked (returns 0 if successful)
if (devStatus == 0)
{
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for MPU6050 drift rate to settle..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
bMPU6050Ready = true;
StartSec = millis() / 1000.f;
mySerial.printf("MPU6050 Ready at %2.2f Sec\n", StartSec);
}
else
{
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
bMPU6050Ready = false;
}
#pragma endregion MPU6050
TurnRatePID.SetSampleTime(100);
mySerial.printf("TurnRatePID started with Kp/Ki/Kd = %2.1f,%2.1f,%2.1f, kp/ki/kd = = %2.2f,%2.3f,%2.2f, SampleTime = %lu\n",
TurnRatePID.GetKp(), TurnRatePID.GetKi(), TurnRatePID.GetKd(), TurnRatePID.Getkp(), TurnRatePID.Getki(), TurnRatePID.Getkd(), TurnRatePID.GetSampleTime());
And I got this output:
I received: 63
ENTERING COMMAND MODE:
0 = 180 deg CCW Turn
1 = 180 deg CW Turn
A = Back to Auto Mode
S = Stop
F = Forward
R = Reverse
Faster
8
Left 4 5 6 Right
2
Slower
Got D
Got A
Got 31
CW 180 deg Turn
In SpinTurn(CW, 180.00, 45.00)
TurnRatePID started with Kp/Ki/Kd = 5.0,0.8,3.0, kp/ki/kd = = 5.00,0.080,30.00, SampleTime = 30
Init hdg = 142.35 deg, Turn = 180.00 deg, tgt = -37.65 deg, timeout = 8.00 sec
TurnRatePID started with Kp/Ki/Kd = 5.0,0.8,3.0, kp/ki/kd = = 5.00,0.080,30.00, SampleTime = 30
In SetSampleTime, SampleTime now 30.0, Changing to 100.0
TurnRatePID started with Kp/Ki/Kd = 5.0,0.8,3.0, kp/ki/kd = = 5.00,0.024,100.00, SampleTime = 30
Looking at the output, it seems clear from the value of private member variable kd (I added access functions), it is getting scaled initially as if the SampleTime is the correct (100 mSec) value, even though the reported value is 30, not 100. However, after SetSampleTime() executes, it appears that the internal kd value gets divided by 0.1 (i.e. SampleTimeInSec) again - and this is very unexpected behavior. In addition, I cannot for the life of me figure out how the internal private member can be one number, but the retrieved value is something else.
TIA,
Frank
Yeah, it's definitely weird. However I've noticed that in your initial post you're printing out TURN_RATE_UPDATE_INTERVAL_MSEC
as SampleTime
.
In the followup though you're using controller's method GetSampleTime()
which is not present in the committed version of the library, so it's hard to say exactly what's going on, sorry.