Good day,
I am currently implementing a single loop PID controller using angle setpoints as inputs. I was trying out a different approach for the D part of the PID controller.
What bought this about is that when I was able to reach a 200Hz (0.00419ms) loop rate, when adding a D gain, the quadcopter seems to dampen the movements in a non continous manner. This was not the case when my algorithm was running at around 10Hz. At an angle set point of 0 degrees, I would try to push it to one side by 5 degrees then the quad would try to stay rock solid by resisting the movements but lets go after while enabling me to get it of by 2 degrees (the dampening effect weakens over time) then tries to dampen the motion again.
This is my implementation of the traditional PID:
Derivative on Error:
//Calculate Orientation Error (current - target)
float pitchError = pitchAngleCF - pitchTarget;
pitchErrorSum += (pitchError*deltaTime2);
float pitchErrorDiff = pitchError - pitchPrevError;
pitchPrevError = pitchError;
float rollError = rollAngleCF - rollTarget;
rollErrorSum += (rollError*deltaTime2);
float rollErrorDiff = rollError - rollPrevError;
rollPrevError = rollError;
float yawError = yawAngleCF - yawTarget;
yawErrorSum += (yawError*deltaTime2);
float yawErrorDiff = yawError - yawPrevError;
yawPrevError = yawError;
//PID controller list
float pitchPID = pitchKp*pitchError + pitchKi*pitchErrorSum + pitchKd*pitchErrorDiff/deltaTime2;
float rollPID = rollKp*rollError + rollKi*rollErrorSum + rollKd*rollErrorDiff/deltaTime2;
float yawPID = yawKp*yawError + yawKi*yawErrorSum + yawKd*yawErrorDiff/deltaTime2;
//Motor Control - Mixing
//Motor Front Left (1)
float motorPwm1 = -pitchPID + rollPID - yawPID + baseThrottle + baseCompensation;
What I tried to do now is to implement a derivative on measurement method from this article to remove derivative output spikes. However the Derivative part seems to increase the corrective force than dampen it.
Derivative on Measurement:
//Calculate Orientation Error (current - target)
float pitchError = pitchAngleCF - pitchTarget;
pitchErrorSum += (pitchError*deltaTime2);
float pitchErrorDiff = pitchAngleCF - pitchPrevAngleCF; // <----
pitchPrevAngleCF = pitchAngleCF;
float rollError = rollAngleCF - rollTarget;
rollErrorSum += (rollError*deltaTime2);
float rollErrorDiff = rollAngleCF - rollPrevAngleCF; // <----
rollPrevAngleCF = rollAngleCF;
float yawError = yawAngleCF - yawTarget;
yawErrorSum += (yawError*deltaTime2);
float yawErrorDiff = yawAngleCF - yawPrevAngleCF; // <----
yawPrevAngleCF = yawAngleCF;
//PID controller list // <---- The D terms are now negative
float pitchPID = pitchKp*pitchError + pitchKi*pitchErrorSum - pitchKd*pitchErrorDiff/deltaTime2;
float rollPID = rollKp*rollError + rollKi*rollErrorSum - rollKd*rollErrorDiff/deltaTime2;
float yawPID = yawKp*yawError + yawKi*yawErrorSum - yawKd*yawErrorDiff/deltaTime2;
//Motor Control - Mixing
//Motor Front Left (1)
float motorPwm1 = -pitchPID + rollPID - yawPID + baseThrottle + baseCompensation;
My question now is:
Is there something wrong with my implementation of the second method?
Source: http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-derivative-kick/
The way I've obtained the change in time or DT is by taking the timestamp from the start of the loop then taking the next time stamp at the end of the loop. Their difference is obtained to obtain the DT. getTickCount() is an OpenCV function.
/* Initialize I2c */
/* Open Files for data logging */
while(1){
deltaTimeInit=(float)getTickCount();
/* Get IMU data */
/* Filter using Complementary Filter */
/* Compute Errors for PID */
/* Update PWM's */
//Terminate Program after 40 seconds
if((((float)getTickCount()-startTime)/(((float)getTickFrequency())))>20){
float stopTime=((float)getTickCount()-startTime)/((float)getTickFrequency());
gpioPWM(24,0); //1
gpioPWM(17,0); //2
gpioPWM(22,0); //3
gpioPWM(18,0); //4
gpioTerminate();
int i=0;
for (i=0 ; i < arrPitchCF.size(); i++){
file8 << arrPitchCF.at(i) << endl;
}
for (i=0 ; i < arrYawCF.size(); i++){
file9 << arrYawCF.at(i) << endl;
}
for (i=0 ; i < arrRollCF.size(); i++){
file10 << arrRollCF.at(i) << endl;
}
for (i=0 ; i < arrPitchAccel.size(); i++){
file2 << arrPitchAccel.at(i) << endl;
}
for (i=0 ; i < arrYawAccel.size(); i++){
file3 << arrYawAccel.at(i) << endl;
}
for (i=0 ; i < arrRollAccel.size(); i++){
file4 << arrRollAccel.at(i) << endl;
}
for (i=0 ; i < arrPitchGyro.size(); i++){
file5 << arrPitchGyro.at(i) << endl;
}
for (i=0 ; i < arrYawGyro.size(); i++){
file6 << arrYawGyro.at(i) << endl;
}
for (i=0 ; i < arrRollGyro.size(); i++){
file7 << arrRollGyro.at(i) << endl;
}
for (i=0 ; i < arrPWM1.size(); i++){
file11 << arrPWM1.at(i) << endl;
}
for (i=0 ; i < arrPWM2.size(); i++){
file12 << arrPWM2.at(i) << endl;
}
for (i=0 ; i < arrPWM3.size(); i++){
file13 << arrPWM3.at(i) << endl;
}
for (i=0 ; i < arrPWM4.size(); i++){
file14 << arrPWM4.at(i) << endl;
}
for (i=0 ; i < arrPerr.size(); i++){
file15 << arrPerr.at(i) << endl;
}
for (i=0 ; i < arrDerr.size(); i++){
file16 << arrDerr.at(i) << endl;
}
file2.close();
file3.close();
file4.close();
file5.close();
file6.close();
file7.close();
file8.close();
file9.close();
file10.close();
file11.close();
file12.close();
file13.close();
file14.close();
file15.close();
file16.close();
cout << " Time Elapsed = " << stopTime << endl;
break;
}
while((((float)getTickCount()-deltaTimeInit)/(((float)getTickFrequency())))<=0.00419){ //0.00209715|0.00419
cout << " DT end = " << deltaTime2 << endl;
deltaTime2=((float)getTickCount()-deltaTimeInit)/(((float)getTickFrequency()));
}
cout << " DT end = " << deltaTime2 << endl;
}
Here's my data: