VEXCode PID Tutorial

I’m pretty sure “normal V5 text” is called “VEXcode V5 Pro”. You can’t figure out how to create it or how to call it? Make sure it’s an int function.

i found it, its

task PID = task(drivePID);

@anon4126930 quick question but is the desiredValue= and desiredTurnValue= measured in degrees?

They are in motor ticks, which is in degree if you have configured them with the right gear ratio in motor and sensor setup

@anon4126930 Hello! How would I be able to do this with a 6 motor drive?

Most definitely yes it’s possible!
You just tell more motors to spin for the result of the PID algorithm

How would I do this if our team is currently using drivetrain? We are switching to a 6 motor drive, so is it possible to do the 4 motor drivetrain, and also do the 2 extra motors in this PID?

Well, if I remember correctly, in this video you’re just measuring the rotations on a certain wheel
Adding more motors wouldn’t affect the rotation of that wheel

You can do a motor group for different sides of the drive, and tell it to run that under the section where it tells the motors to move, with the same result. If using the inertial sensor, it still wouldn’t change, so adding more motors should just be a result of changing the motors in the group/making a group

Just avoid the VEXCode drivetrain object. It really isn’t that good to be honest.

On another note for the future: In the future I’m planning on releasing code that is a better drivetrain OOP with integrated PID. But no guarantees, as it depends on how much I get done with the documentation and modularity.

could someone help me with my code, please?

I start the code and the robot just does nothing no matter what I do.
if you have any questions reply to me.

// ---- START VEXCODE CONFIGURED DEVICES ----
// Robot Configuration:
// [Name]               [Type]        [Port(s)]
// rightDrive           motor_group   12, 13          
// leftDrive            motor_group   18, 19          
// Inertial             inertial      15              
// ---- END VEXCODE CONFIGURED DEVICES ----

#include "vex.h"

using namespace vex;

// A global instance of competition
competition Competition;
// define your global instances of motors and other devices here

// define your global instances of motors and other devices here

/*---------------------------------------------------------------------------*/
/*                          Pre-Autonomous Functions                         */
/*                                                                           */
/*  You may want to perform some actions before the competition starts.      */
/*  Do them in the following function.  You must return from this function   */
/*  or the autonomous and usercontrol tasks will not be started.  This       */
/*  function is only called once after the V5 has been powered on and        */
/*  not every time that the robot is disabled.                               */
/*---------------------------------------------------------------------------*/

void pre_auton(void) {
  // Initializing Robot Configuration. DO NOT REMOVE!
  vexcodeInit();
  
}

//Settings
double kP = 0.00001;
double kD = 0.0;

double turnkP = 0.001;
double turnkD = 0.0;

int desiredValue = 0;
int desiredTurnValue = 0; //degrees

int error; //SensorValue - DesiredVaule : Position
int prevError = 0; //Position 20 msecs ago
int derivative; // error - prevError : Speed
int totalError = 0; //totalError = totalError + error

int turnError; //SensorValue - DesiredValue : Position
int turnPrevError = 0;//Position 20 msecs ago
int turnDerivative; // error - prevError : Speed
int turnTotalError = 0; //totalError = totalError + error

bool resetDriveSensors = false;

//variables (modifiable)
bool enableDrivePID = true;

int drivePID(){
   
  while(enableDrivePID){

    if (resetDriveSensors) {
      resetDriveSensors = true;
      leftDrive.setPosition(0, degrees);
      rightDrive.setPosition(0, degrees);
    //set = true to reset
    }
    
    //position of motors
    int leftDrivePosition = leftDrive.position(degrees);
    int rightDrivePosition = rightDrive.position(degrees);

    //////////////////////////////////////////////////////////////////////////////////////
    //Lateral Movement PID
    //////////////////////////////////////////////////////////////////////////////////////

    //average of motors
    int averagePosition = (leftDrivePosition + rightDrivePosition)/2;

    //Potential
    error = desiredValue - averagePosition;

    //Derivative
    derivative = error - prevError;

    //Integral
    //totalError += error;
    
    double lateralMotorPower = error * kP + derivative * kD;//how many volts the motors will spin for
    //////////////////////////////////////////////////////////////////////////////////////
    //Turning Movement PID
    //////////////////////////////////////////////////////////////////////////////////////    
    
    //Potential
    turnError = desiredTurnValue - Inertial.rotation(degrees);

    //Derivative
    turnDerivative = turnError - turnPrevError;

    //Integral
    //turnTotalError += turnError;
    
    double turnMotorPower = turnError * turnkP + turnDerivative * turnkD;
    //////////////////////////////////////////////////////////////////////////////////////
    
    leftDrive.spin(forward, lateralMotorPower + turnMotorPower, voltageUnits::volt);
    rightDrive.spin(forward, lateralMotorPower - turnMotorPower, voltageUnits::volt);    
    
    
    prevError = error;
    turnPrevError = turnError;
    vex::task::sleep(20);
  } 
return 1;
}


void autonomous(void) {

  vex::task MainFunction(drivePID);

  resetDriveSensors = true;
  desiredValue = 90;
  desiredTurnValue = 90;
  
  vex::task::sleep(100);
  

}

int main()  {


}Preformatted text

Would it be possible to surround your code with [code] and [/code] please :slight_smile: Otherwise it will be very hard for me to read your code

@Connor Done! Sorry, I knew that was a thing i just didn’t know how to do it :sweat_smile:

You do have a very small Kp (or atleast compared to mine), have you tried using a larger one? Maybe make it very large just to see if it works?

Your code doesn’t seem to have all of the competition template assets (like initialization alongside passing the autonomous function in the int main, etc). I would suggest using the competition template and put the code you currently have in the designated regions and try again. :slight_smile:

Ok, I tried the code and now it runs at least, but there are a couple of problems I cant figure out.

The problem is with something else. The code ignores desiredTurnValue and just drives straight forever unless I turn it. if I turn it to the left, it speeds up, if I turn it to the right, it slows down and then eventually goes in reverse. desiredValue just makes the robot sit still and when I turn it left, it goes backward, and when I turn it right it goes forwards, it also ignores the desired value (both go faster the more I turn it). the kp’s don’t work either. I think they might control different values, for example, turnkp would affect desiredValue, even though I’m not turning, although I haven’t had enough testing to confirm this.

thanks!

here is the code I used (the kp’s and desired values might be different but that shouldn’t matter)

/*----------------------------------------------------------------------------*/
/*                                                                            */
/*    Module:       main.cpp                                                  */
/*    Author:       VEX                                                       */
/*    Created:      Thu Sep 26 2019                                           */
/*    Description:  Competition Template                                      */
/*                                                                            */
/*----------------------------------------------------------------------------*/

// ---- START VEXCODE CONFIGURED DEVICES ----
// Robot Configuration:
// [Name]               [Type]        [Port(s)]
// rightDrive           motor_group   12, 13          
// leftDrive            motor_group   18, 19          
// Inertial             inertial      15              
// ---- END VEXCODE CONFIGURED DEVICES ----

#include "vex.h"

using namespace vex;

// A global instance of competition
competition Competition;

// define your global instances of motors and other devices here

/*---------------------------------------------------------------------------*/
/*                          Pre-Autonomous Functions                         */
/*                                                                           */
/*  You may want to perform some actions before the competition starts.      */
/*  Do them in the following function.  You must return from this function   */
/*  or the autonomous and usercontrol tasks will not be started.  This       */
/*  function is only called once after the V5 has been powered on and        */
/*  not every time that the robot is disabled.                               */
/*---------------------------------------------------------------------------*/

void pre_auton(void) {
  // Initializing Robot Configuration. DO NOT REMOVE!
  vexcodeInit();

  // All activities that occur before the competition starts
  // Example: clearing encoders, setting servo positions, ...
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                              Autonomous Task                              */
/*                                                                           */
/*  This task is used to control your robot during the autonomous phase of   */
/*  a VEX Competition.                                                       */
/*                                                                           */
/*  You must modify the code to add your own robot specific commands here.   */
/*---------------------------------------------------------------------------*/
//Settings
double kP = 0.005;
double kD = 0.0;

double turnkP = 0.07;
double turnkD = 0.0;

int desiredValue = 0;
int desiredTurnValue = 0; //degrees

int error; //SensorValue - DesiredVaule : Position
int prevError = 0; //Position 20 msecs ago
int derivative; // error - prevError : Speed
int totalError = 0; //totalError = totalError + error

int turnError; //SensorValue - DesiredValue : Position
int turnPrevError = 0;//Position 20 msecs ago
int turnDerivative; // error - prevError : Speed
int turnTotalError = 0; //totalError = totalError + error

bool resetDriveSensors = false;

//variables (modifiable)
bool enableDrivePID = true;

int drivePID(){
   
  while(enableDrivePID){

    if (resetDriveSensors) {
      resetDriveSensors = true;
      leftDrive.setPosition(0, degrees);
      rightDrive.setPosition(0, degrees);
    //set = true to reset
    }
    
    //position of motors
    int leftDrivePosition = leftDrive.position(degrees);
    int rightDrivePosition = rightDrive.position(degrees);

    //////////////////////////////////////////////////////////////////////////////////////
    //Lateral Movement PID
    //////////////////////////////////////////////////////////////////////////////////////

    //average of motors
    int averagePosition = (leftDrivePosition + rightDrivePosition)/2;

    //Potential
    error = desiredValue - averagePosition;

    //Derivative
    derivative = error - prevError;

    //Integral
    //totalError += error;
    
    double lateralMotorPower = error * kP + derivative * kD;//how many volts the motors will spin for
    //////////////////////////////////////////////////////////////////////////////////////
    //Turning Movement PID
    //////////////////////////////////////////////////////////////////////////////////////    
    
    //Potential
    turnError = desiredTurnValue - Inertial.rotation(degrees);

    //Derivative
    turnDerivative = turnError - turnPrevError;

    //Integral
    //turnTotalError += turnError;
    
    double turnMotorPower = turnError * turnkP + turnDerivative * turnkD;
    //////////////////////////////////////////////////////////////////////////////////////
    
    leftDrive.spin(forward, lateralMotorPower + turnMotorPower, voltageUnits::volt);
    rightDrive.spin(forward, lateralMotorPower - turnMotorPower, voltageUnits::volt);    
    
    
    prevError = error;
    turnPrevError = turnError;
    vex::task::sleep(20);
  } 
return 1;
}


void autonomous(void) {

  vex::task MainFunction(drivePID);

  resetDriveSensors = true;
  desiredValue = 1;
  

  vex::task::sleep(100);
  

}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                              User Control Task                            */
/*                                                                           */
/*  This task is used to control your robot during the user control phase of */
/*  a VEX Competition.                                                       */
/*                                                                           */
/*  You must modify the code to add your own robot specific commands here.   */
/*---------------------------------------------------------------------------*/

void usercontrol(void) {
  // User control code here, inside the loop
  while (1) {
    // This is the main execution loop for the user control program.
    // Each time through the loop your program should update motor + servo
    // values based on feedback from the joysticks.

    // ........................................................................
    // Insert user code here. This is where you use the joystick values to
    // update your motors, etc.
    // ........................................................................

    wait(20, msec); // Sleep the task for a short amount of time to
                    // prevent wasted resources.
  }
}

//
// Main will set up the competition functions and callbacks.
//
int main() {
  // Set up callbacks for autonomous and driver control periods.
  Competition.autonomous(autonomous);
  Competition.drivercontrol(usercontrol);

  // Run the pre-autonomous function.
  pre_auton();

  // Prevent main from exiting with an infinite loop.
  while (true) {
    wait(100, msec);
  }
}

Hi I’m new to PID but have some decent experience coding in Vex. I was just curious how you would code the drivetrain to have 4 motors not 2 and how you would change, for example the linear motion’s data to inches? Also could someone show me what a full autonomous run would look like in PID?

Thanks

You would want to declare 4 motors first (I’m not sure of the exact syntax off the top of my ahead but it should look something like this):

// Delcare 4 motors first, I chose the port numbers arbitrarily and you can choose whichever ports you want
motor fLeft (1, true);
motor bLeft (2, true);
motor fRight (3, false);
motor bRight (4, false);

Next, if you want you could declare a motor group using the 4 motors to simplify it to just the left side of your drive and the right side of your drive.

motor_group leftDrive (fLeft, bLeft);
motor_group rightDrive (fRight, bRight);

You can use the motor groups with the spin command

// Set motor groups to a specified velocity. 100 is chosen arbitrarily
leftDrive.spin(directionType::fwd, 100, velocityUnits::pct);
rightDrive.spin(directionType::fwd, 100, velocityUnits::pct);

I haven’t watched Connor’s video in a long time but if I remember correctly, he uses motor degrees for all of his math. If we want to convert degrees to distance, we can use the circumfrence of a circle. For example, let’s assume we’re using 4-inch wheels on our drive base. Let’s also assume we are using green cartridges in our motors, and we have geared our drivetrain 84 : 60. One full revolution of the motor would be equal to

360 * (84/60) = 504 degrees (1.4 revolutions)

We then use this degree value to find the distance traveled after one full rotation of a motor by using the formula for the circumfrence of a circle.

504/360 * pi * 4 = 17.593 inches / one motor revolution

We can finally take this number and divide by 360, if you want to find the amount of distance traveled from a 1 degree change in the motor.

17.593 / 360 = 0.048869 inches/degree

You can tell your code to travel a certain distance, and reverse this process to find the amount of degrees your motor would need to travel in your code.

Here are two of my autonomous routines I’ve used. One in Change Up, one in Tipping Point.

Change Up, didn't fully hit but if the ball went in it would have

https://youtu.be/ruehq4NR9nU

Tipping Point, full speed hadn't been set yet at that point, we got it faster by worlds

https://youtu.be/f2eQEhSwbhQ

Hope this helped

#include "vex.h"

using namespace vex;

// A global instance of competition
competition Competition;

// define your global instances of motors and other devices here

/*---------------------------------------------------------------------------*/
/*                          Pre-Autonomous Functions                         */
/*                                                                           */
/*  You may want to perform some actions before the competition starts.      */
/*  Do them in the following function.  You must return from this function   */
/*  or the autonomous and usercontrol tasks will not be started.  This       */
/*  function is only called once after the V5 has been powered on and        */
/*  not every time that the robot is disabled.                               */
/*---------------------------------------------------------------------------*/





// "when Controller1 ButtonDown released" hat block






void pre_auton(void) {
  // Initializing Robot Configuration. DO NOT REMOVE!
  vexcodeInit();

  // All activities that occur before the competition starts
  // Example: clearing encoders, setting servo positions, ...
}


int nullifiedIntegral = 0;



//Settings  ADJUST THESE VALUES FOR OCELATING
double kP=0.0;
double kI=0.0;
double kD=0.0;
double turnkP=0.0;
double turnkI=0.0;
double turnkD=0.0;

int maxIntegral = 300;
int integralBound = 20;
int turnMaxIntegral = 300;


int error;  //Sensor Value - Desired Value. Positional Value
int prevError=0; // Position 20 milli seconds ago
int derivative; // error - prevError = Speed
int totalError=0;  //totalError = totalError + error


int turnError;  //Sensor Value - Desired Value. Positional Value
int turnPrevError=0; // Position 20 milli seconds ago
int turnDerivative; // error - prevError = Speed
int turnTotalError=0;  //totalError = totalError + error

//Autonomous Settings
int desiredValue = 40;
int desiredTurnValue = 0;


double signnum_c(double x) {
  if (x > 0.0) return 1.0;
  if (x < 0.0) return -1.0;
  return x;
}

//Variables Modified for Use
bool enabledrivePID = true;
bool resetDriveSensors = false;

int drivePID(){

  while(enabledrivePID){
  

   if(resetDriveSensors) {
    resetDriveSensors = false;
    FrontLeftMotor.setPosition(0.0,degrees);
    FrontRightMotor.setPosition(0.0,degrees);
    BackLeftMotor.setPosition(0.0,degrees);
    BackRightMotor.setPosition(0.0,degrees);

   }

   //////////////////////////////////////
   /////Lateral Movement PID
   //////////////////////////////////////
   //Position of both motors
   int leftmotorposition = FrontLeftMotor.position(degrees);
   int rightmotorposition = FrontRightMotor.position(degrees);
   int brightmotorposition = BackRightMotor.position(degrees);
   int bleftmotorposition = BackLeftMotor.position(degrees);
   //Average of both motors
   int averagePosition = (leftmotorposition+rightmotorposition+brightmotorposition+bleftmotorposition)/4;

   //Potential
   error = averagePosition-desiredValue;

   //Derivative
   derivative = error-prevError;

   //Integral
   if(abs(error) < integralBound){
    totalError+=error; 
    }  else {
    totalError = 0; 
    }
    //totalError += error;

    totalError = abs(totalError) > maxIntegral ? signnum_c(totalError) * maxIntegral : totalError;
   
   double lateralMotorPower = error * kP + derivative * kD + totalError *kI; 
   /////////////////////////////////////////////////////////////
   


   //////////////////////////////////////
   /////Turning Movement PID
   //////////////////////////////////////
   //Average of both motors

   int turnDifference = (leftmotorposition+bleftmotorposition)-(rightmotorposition+brightmotorposition);

   //Potential
   turnError = turnDifference-desiredTurnValue;

   //Derivative
   turnDerivative = turnError-turnPrevError;

   //Integral
   if(abs(turnError) < integralBound){
    turnTotalError+=turnError; 
    }  else {
    turnTotalError = 0; 
    }
    //totalError += error;

   
   turnTotalError = abs(turnTotalError) > turnMaxIntegral ? signnum_c(turnTotalError) * turnMaxIntegral : turnTotalError;

   double turnMotorPower = turnError * turnkP + turnDerivative * turnkD + turnTotalError * turnkI;
   


   //////////////////////////////////////

   FrontLeftMotor.spin(forward,lateralMotorPower + turnMotorPower, voltageUnits::volt);
   FrontRightMotor.spin(forward,lateralMotorPower - turnMotorPower, voltageUnits::volt);
   BackRightMotor.spin(forward,lateralMotorPower - turnMotorPower, voltageUnits::volt);
   BackLeftMotor.spin(forward,lateralMotorPower - turnMotorPower, voltageUnits::volt);

   //code
   prevError=error;
   turnPrevError=turnError;
   vex::task::sleep(20);


  }

  return 1;
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                              Autonomous Task                              */
/*                                                                           */
/*  This task is used to control your robot during the autonomous phase of   */
/*  a VEX Competition.                                                       */
/*                                                                           */
/*  You must modify the code to add your own robot specific commands here.   */
/*---------------------------------------------------------------------------*/

void autonomous(void) {
  vex::task drive(drivePID);

  
  resetDriveSensors = true;
  desiredValue = 4;
  
  
  vex::task::sleep(1000);
  
  

}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*                              User Control Task                            */
/*                                                                           */
/*  This task is used to control your robot during the user control phase of */
/*  a VEX Competition.                                                       */
/*                                                                           */
/*  You must modify the code to add your own robot specific commands here.   */
/*---------------------------------------------------------------------------*/

void usercontrol(void) {
  // User control code here, inside the loop
  while (1) {
    enabledrivePID = false;

    wait(20, msec); // Sleep the task for a short amount of time to
                    // prevent wasted resources.
  }
}

//
// Main will set up the competition functions and callbacks.
//
int main() {
  

  // Set up callbacks for autonomous and driver control periods.
  Competition.autonomous(autonomous);
  Competition.drivercontrol(usercontrol);

  // Run the pre-autonomous function.
  pre_auton();

  // Prevent main from exiting with an infinite loop.
  while (true) {
    wait(100, msec);
  }
}

Can you tell me why this doesnt work?

Here’s an example of what teams can accomplish with just PID & smart sensor usage. Back in the day, teams were only just realizing the potential of PID, and team 1103 was able to use it to win the title of Programming Skills Champion at the 2011 VRC World Championships (btw I think his easyC code is still here on the VF somewhere).

Your front right motor should spin the same way as the front left, so lateralMotorPower+turnMotorPower.
What’s the behaviour you’re trying to fix?