Pid control coding help please

Hi guys i am new to robotc and i am wondering how to use and code PID control without using IME. I know what PID control is and what is used for, i just dont know how to code it and program. I tried it my self but i failed miserably. I need this for my chainbar so i can keep it at a specific place. PLEASE HELP ME

THANKS IN ADVANCE

if you don’t use a sensor, u can’t do PID.

If you don’t have any encoders on whatever you’re controlling (IME’s or regular encoders) there’s no possible way you can do PID.

I didn’t think the OP said no sensors, just no IME’s. For a chain bar, if it doesn’t rotate too far I would use potentiometers. If it rotates too far, scale that back using gears/sprockets to use potentiometers or use optical shaft encoders. But if you use optical shaft encoders there, you’re going to run out of ports quickly and probably can’t use them on your drivetrain, where they’re more useful.

Assuming you choose to use potentiometers, then “error” is the difference between the potentiometer’s value where you want it to hold still and its actual value. You can roughly just stick that into any of the PID code you find posted here or elsewhere.

Sgarg14,

It seems to me like you need some position control. You probably need some form of what is called closed-loop control but not necessarily PID. This will definitely need some sensor feedback as others pointed out. I would be inclined to use the potentiometer if you can. Attach it to a shaft for which you want to control the position either directly or with some gear reduction since the potentiometer has hard rotational limits.

Then write some code so can read the potentiometer values at the upper and lower limits of motion for your mechanism. After, use those values to enclose your motor write statements under conditions similar to below:

/check for lower position to prevent movement
if(potValue <= lowerPotValue)
/write zero to motor
motorWrite(0);
else
/ pass along your desired command to the motor (probably a scaled value from a controller input)
motorWrite(joystickPosition)
end

You will do a similar check for the upper limit using the potentiometer reading. This a version of what is called a bang-bang (ON-OFF) controller. Much simpler than PID to get started.

Hope this helps :slight_smile:

Best,

Jose

PID is a bit complex. You can find code from all over the forum and basically just copy and paste it, but I would recommend against that, as they are difficult and time-consuming to tune as well. If you feel up to it, go ahead, but I would recommend starting off with something simpler. As @JoseAvendano said, a bang-band controller is the easiest to code, but is also probably the least effective (and it’s not super healthy for your motors). If the intent of your PID is to hold your chainbar where the driver lets go, a proportional control is the best balance between simplicity and performance. The code is pretty simple:


motor[m] = (target - current) * kp;

It is the ‘P’ in PID, and functions under the same principle.


target

is often called setpoint as well, that is simply where you want the chainbar to be.


current

is the current value that the potentiometer reads, and


kp

is your tuning parameter. For a simple holding proportional control (and a potentiometer), this will probably be around 0.06, but you should increase that number as much as you can before it starts to oscillate. If you use an encoder for some reason, start with kp at 1. Below is some sample code for controlling the chainbar:


int chainbar_target = SensorValue[chainbar_pot];

while (true) {

    // other code

    if (vexRT[Btn6U] || vexRT[Btn6D]) {

        motor[m] = (vexRT[Btn6U] - vexRT[Btn6D]) * 127; // use buttons 6U and 6D to control the chainbar
        chainbar_target = SensorValue[chainbar_pot]; // update target to the current position of the chainbar

    }
    else {

        motor[m] = (chainbar_target - SensorValue[chainbar_pot]) * 0.06; // run proportional control

    }

    // other code

}

However, if your intention with a PID is to move the chainbar during autonomous (or with auto-stack or something), proportional will probably not be enough. By definition, proportional controls have something called ‘steady-state error.’ This is the difference between the current and target in a static system. As the proportional control approaches the target, it lowers the power supplied to the motor. At some point, this power will not be enough to move the motor, and the chainbar will stop moving, locked in position. This will happen before it reaches the target, and therefore a proportional control will always have some steady-state error, so it may not be accurate enough for autonomous. One way to somewhat reduce this issue is to add a constant component, so your proportional code would change from


motor[m] = (target - current) * kp;

to


motor[m] = (target - current) * kp + c;

where


c

is the constant. Again, you will have to tune this value, but a good start would be around 15-20. The constant should be as low as possible while still holding the chainbar up at its maximum horizontal extension. This will help, but not totally alleviate the issue (nothing really can).

If this isn’t good enough, you’ll have to either make a full PID (check this out), or replace the constant with an ‘angular’ component, which I’ll describe in another post, because I’m worried about the character limit in this one.

The thing is i cant gear it down cause i dont have enouph space so i cant use a potentiometer and i have a shaft encoder already on it and about running out of ports i only have one shaft encoder and potentiometer and then one for the chain bar

Do you need to gear it down? Unless you have the potentiometer directly on the motor, your range should be small enough for a potentiometer to work. Another thing you could try is to put the potentiometer on the end of the chainbar (I attached a bad MS paint illustration). I wouldn’t recommend an encoder, but I guess it would work if that’s all you can do. Running out of ports should not be an issue. Unless you have some sort of sensor on your chainbar, you cannot use any sort of control algorithm, PID or otherwise, because how can the robot know where to move the chainbar if it doesn’t know where it is?

Ok but the thing i dont get is how to code the chainbar to keep at the position i want it to be in. the reason why i wanna do that is because when the autonomous goes my code make the mobile goal go down and the the chainbar geos to the mid position and i want it to stay there until i tell it to come down. I wanna know how to code it to do that either using PID or something else. And i either will have potentiometer or a encoder on the actual chainbar.

this is where i will have the potentiometer or encoder

The low-tech option, which requires no sensors, is to figure out what the minimum amount of power is to hold the arm in position, and after you raise it to the desired place, just leave the motors on at that low power (probably 15-30, I’d guess). Since it’s autonomous, you know exactly what weight the arm will be holding and in exactly what location, so it’s not that hard to go the low-tech option, which happens to take very little time to implement.

the thing is i want to learn how to do PID and i already have an sensor and have done some coding with the sensor so i dont want to take it off

is it possible if someone could give the PID code and then i can just tune to my robot that would be really helpful

thanks

While I don’t condone giving out code for copy-pasting, I’ll use some of my code to show an example of how a PID controller can be implemented. Keep in mind I use PROS but hopefully you should be able to replicate something like this in RobotC:

static float errorIntegral[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

int PID(float current, float target, int mechanismIdentifier, float kP, float kI, float kD){  
  float errorCurrent = target - current;
  errorIntegral[mechanismIdentifier] += errorCurrent;
  
  float pTerm = kP*errorCurrent;
  float iTerm = kI*errorIntegral[mechanismIdentifier];
  float dTerm = kD*0;
  int motorSpeed = pTerm + iTerm + dTerm;
  return motorSpeed;
}

What I do for my code is I have a general-purpose PID that I can use for anything (eg drivetrain, chainbar, linear gear elevator). If this is your first time writing a PID, I would recommend that you only focus on the P term. If you’ve noticed, I don’t use the D term (kD* 0 means whatever my constant is for kD, it won’t affect motor speed) and I term.

The P term by itself is just:

int current = <insert what you would use to grab sensor reading, for robotC i think its sensorValue>;
int target = <what you want the target value to be, should be numerical value>;
int error =  target - current;
int kP = <constant that you need to pick and tune for your robot>;

int motorSpeed = kP * error;

return motorSpeed; //or set your motors to motorSpeed instead of returning motorSpeed

Since you’re coding this for autonomous, target should be a static number that shouldn’t change.

Once you have more experience with PID, then you can start experimenting with the I term and D term, but in my few months of experience starting with just P control should help get you started. But like what @ZachDaChampion says, you’ll have to deal with steady-state error, so adding a small constant to the P term should help.

1 Like

ok but for the p term where would you put the code is it going to be before void pre auton right or is it going to be somewhere else

and

For current value how would you code is it going to be something like this


while (true)
     {
      if (SensorValue[armencoder] < clicks)
      {
      motor[chain1] = 127;
     motor[chain2] = 127;
   }
      else if (SensorValue[armencoder] > clicks)
      {
     motor[chain1] = -127;
     motor[chain2] = -127;
   }

or something else

and

how do i tune the kp do i just test different values in autonomous and find the best one or something else

thanks for the help

Instead of setting your chainbar motors to 127, you would put your P term code there. For example your code might look something like this(assuming clicks is your target):


while (true)
     {
      if (SensorValue[armencoder] < clicks) //assuming clicks here is your target position
      {
      motor[chain1] = (clicks - SensorValue[armencoder]) * kP;
     motor[chain2] = (clicks - SensorValue[armencoder]) * kP;
   }
      else if (SensorValue[armencoder] > clicks)
      {
     motor[chain1] = (clicks - SensorValue[armencoder]) * kP; //if chainbar is moving in the opposite direction when this else if block is running, just add negatives in front of kP or the error
     motor[chain2] = (clicks - SensorValue[armencoder]) * kP;
   }

Your current value would just be sensorValue[armencoder].

As for tuning the kP, you just test different values in autonomous and use the best one. If it oscillates too much (chainbar is moving back and forth between the target position) then the kP value is too high, and if the chainbar isn’t moving towards the target position fast enough (or never comes close to target position) then the kP value is too low.

Not quite sure how much load your chainbar is carrying, but I would recommend testing kP at 0.5 or 0.6, then either increasing or decreasing it depending on how your chainbar moves in autonomous. Setting up the debug terminal to show your chainbar position (sensorValue) and chainbar speed should help a lot when trying to tune your kP.

edit: accidentally posted same response when trying to edit, but I reworded a portion of the last part

ok thanks for the code

can i make a PID for the drive with only one encoder and only using P using the same code

or

would i have to implement I and D in it to

It’s possible to make a PID for a drivetrain with only one encoder using only a P loop, but it would work a lot better if you had an encoder on each side of the drivetrain. If you’re only using one encoder for your drivetrain, it would be harder (but still doable) to turn in autonomous.

Implementing an I or D term isn’t necessary for a PID drivetrain, but adding an I term might help if your drivetrain is going too slow (which could be an effect of a low kP).

the thing is i want to add an another encoder but our bot barely fits the 18 by 18 rule so if we added an encoder it will be out side of 18 and we cant put it in the inside because our space between the two bars where the wheels go is really small

so if i did it with one encoder can i use the same code below


while (true)
     {
      if (SensorValue[armencoder] < clicks) //assuming clicks here is your target position
      {
      motor[chain1] = (clicks - SensorValue[armencoder]) * kP;
     motor[chain2] = (clicks - SensorValue[armencoder]) * kP;
   }
      else if (SensorValue[armencoder] > clicks)
      {
     motor[chain1] = (clicks - SensorValue[armencoder]) * kP; //if chainbar is moving in the opposite direction when this else if block is running, just add negatives in front of kP or the error
     motor[chain2] = (clicks - SensorValue[armencoder]) * kP;
   }

or

something else

thanks

You should be able to use the above code (assuming you replace clicks, motor and encoder names, and kP) for the drivetrain, but keep in mind if you need to turn you’ll need to add some negatives to the motor speed.

For turn i would have to make another statement and add a negative sighn to all of the kp right ?

If i wanted to add the I what would the code be for that?

Sorry for all the questions

Thanks

Not necessarily another statement, just add a negative to the motor speeds for one side of the drivetrain. The end result/output should have one side of your drivetrain going forward and the other side going backward, making your robot turn.

Adding an I term might be more tricky if you don’t have a PID function that returns values, but it might look something like this (keep in mind I never tried using the I term, but this is how you would write a PI controller)


current = sensorValue[sensor];
target = <insert target value here>;
error = target - current;
errorSum += error;
kP = <P constant>;
kI = <I constant>;

pTerm = error*kP;
iTerm = errorSum*kI;

motorSpeed = pTerm + iTerm;

Once you get into PI or PID controllers, I would highly recommend making a general function that takes parameters (target, current, kP, and kI and kD) and returning motorSpeeds and calling that function when you need to use a PID to set motor speeds.