So our team recently made a PID, but it’s really janky and has a bit of stuff we added in. The 3 main issues right now with it are
-
When I set the total error = 0, it make the wheels stop abruptly and only happens when I am using the integral term.
-
When I increase the derivative term, for long distances, the error is less, but for short distances, the error is more. The D term basically scales the error if it increases or decreases. For example, if I want the robot to go forward 12 inches, and then 36 inches, at a kD value of like 0.5, at 12 inch distance, the robot always has steady state error, while at 36 inch distance, it has no steady state error.
-
When there is only a proportional term, the robot doesnt oscillate; it just stops the moment the code detects the error is 0. Even if I have a high or low kP value, the wheels just instantly stop once it reaches the desired position.
Here’s our code:
double t = 0.00;
const double wheelRad = 5.7/2;
const double wheelCir = wheelRad * 2 * 3.14159;
bool enableDrivePID = true;
bool resetSensors = false;
double inchesTarget = 0;
double rotationTarget = 0;
//lateral
double kP = 0.06; //0.085, 0.4, 0.07, 0.085
double kI = 0.000; //0.0, 0.001, 0, 0 less than 0.0010
double kD = 0.0; //0.0, 0.35, 0.3, 0.45
double error;
double prevError = 0.0;
double totalError = 0.0;
double derivative;
//rotation -> t to represent variables
double tKP = 0.15;
double tKI = 0.0;
double tKD = 0.0;
double tError = 0;
double tPrevError = 0.0;
double tTotalError = 0.0;
double tDerivative = 0;
bool directionLeft = true;
//motor power
double leftPower = 0;
double rightPower = 0;
timer PIDTimeout = timer();
int drivePID()
{
while(enableDrivePID)
{
if(resetSensors)
{
resetSensors = false;
leftFront.resetPosition();
leftBack.resetPosition();
leftTop.resetPosition();
rightFront.resetPosition();
rightBack.resetPosition();
rightTop.resetPosition();
turnSensor.resetHeading();
turnSensor.setHeading(1, degrees);
if(directionLeft) {
turnSensor.setHeading(359, degrees);
}
}
/* general */
// collect sensor values
double leftDegrees = (leftFront.position(degrees) + leftBack.position(degrees) + leftTop.position(degrees)) / 3;
double rightDegrees = (rightFront.position(degrees) + rightBack.position(degrees) + rightTop.position(degrees)) / 3;
double rotationDegrees = turnSensor.heading(degrees);
/* lateral */
// calculations for degrees needed to move for lateral
double lateralDeg = inchesTarget * 360 / wheelCir;
// lateral position
double currentPos = (leftDegrees + rightDegrees) / 2;
if(lateralDeg != 0)
{
// proportional
error = lateralDeg - currentPos;
// integral + anti-windup
totalError += error;
if(fabs(error/360*wheelCir) < 0.25 || fabs(error/360*wheelCir) > 100) {
totalError = 0;
}
// derivative
derivative = error - prevError;
}
else
{
error = 0;
totalError = 0;
derivative = 0;
}
// power
double lateralPower = (error * kP) + (totalError * kI) + (derivative * kD);
/* turning */
/* general */
if(rotationTarget != 0 && !directionLeft)
{
// proportional
tError = rotationTarget + 1 - rotationDegrees;
// integral
tTotalError += tError;
// derivative
tDerivative = tError - tPrevError;
double rotationPower = (tError * tKP) + (tTotalError * tKI) + (tDerivative * tKD);
leftPower = lateralPower + rotationPower;
rightPower = lateralPower - rotationPower;
}
else if(rotationTarget != 0 && directionLeft)
{
// proportional
tError = -rotationDegrees + (rotationTarget);
// integral
tTotalError += tError;
// derivative
tDerivative = tError - tPrevError;
double rotationPower = (tError * tKP) + (tTotalError * tKI) + (tDerivative * tKD);
leftPower = lateralPower + rotationPower;
rightPower = lateralPower - rotationPower;
}
else
{
tError = 0;
tPrevError = 0;
tTotalError = 0;
tDerivative = 0;
leftPower = lateralPower;
rightPower = lateralPower;
}
double friction = 0.94;
// set motor power
leftFront.spin(forward, leftPower*friction, volt);
leftBack.spin(forward, leftPower*friction, volt);
leftTop.spin(forward, leftPower*friction, volt);
rightFront.spin(forward, rightPower, volt);
rightBack.spin(forward, rightPower, volt);
rightTop.spin(forward, rightPower, volt);
prevError = error;
tPrevError = tError;
wait(10, msec);
}
return 1;
}
void runPID(double lateral, double turning, double ti, bool direction)
{
resetSensors = true;
directionLeft = direction;
inchesTarget = lateral;
if(directionLeft)
{
turning = 360 - turning;
}
rotationTarget = turning;
wait(ti, seconds);
inchesTarget = 0;
rotationTarget = 0;
turnSensor.resetHeading();
}
I’m testing the code in the main function so that I don’t have to keep downloading the code, so I can just change each of the constants. Right now, I’m only testing lateral movement, but maybe the turning movement affects it somehow which is causing the issues? Or maybe I need a threshold somewhere where it limits the top motor speed? (if the error is really large, the robot will start off really fast, and because of issue 3, it also stops really fast)
Lastly, some additional info is we have a timeout so that the PID will stop correcting after a certain amount of time. but we’re trying to make it so the PID will stop once it reaches the desired position and stays there for a certain amount of time(so if a robot bumps us, the PID should recorrect half the time). For the rotation part, since we used heading, we had a bunch of issues with turning left specifically, so we worked out a weird way that fixed it, which is why there’s some weird stuff there. We also have some friction on the right side of our drive, which is why we are multiplying the leftPower by a number less than 1.
Thanks.
ps. ik this is a lot lol