Weird PID Problem

Hi guys

I have created a PID controller, but it doesn’t work the way it should. I know it calculates the velocity correctly because i see the value in the debugger. The flywheel motors keep switching between 0 and 127. Someone please help me. I have posted my code below.

#pragma config(I2C_Usage, I2C1, i2cSensors)
#pragma config(Sensor, I2C_1,  ,               sensorQuadEncoderOnI2CPort,    , AutoAssign)
#pragma config(Sensor, I2C_2,  ,               sensorQuadEncoderOnI2CPort,    , AutoAssign)
#pragma config(Motor,  port1,           rightshooter1, tmotorVex393HighSpeed_HBridge, openLoop)
#pragma config(Motor,  port2,           rightshooter2, tmotorVex393HighSpeed_MC29, openLoop, encoderPort, I2C_1)
#pragma config(Motor,  port3,           rightback,     tmotorVex393_MC29, openLoop, reversed)
#pragma config(Motor,  port4,           rightfront,    tmotorVex393_MC29, openLoop, reversed)
#pragma config(Motor,  port5,           intake1,       tmotorVex393_MC29, openLoop, reversed)
#pragma config(Motor,  port6,           intake2,       tmotorVex393_MC29, openLoop, reversed)
#pragma config(Motor,  port7,           leftfront,     tmotorVex393_MC29, openLoop)
#pragma config(Motor,  port8,           leftback,      tmotorVex393_MC29, openLoop)
#pragma config(Motor,  port9,           leftshooter2,  tmotorVex393HighSpeed_MC29, openLoop, reversed, encoderPort, I2C_2)
#pragma config(Motor,  port10,          leftshooter1,  tmotorVex393HighSpeed_HBridge, openLoop, reversed)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

#pragma platform(VEX)

//Competition Control and Duration Settings'
#pragma competitionControl(Competition)
#pragma autonomousDuration(20)
#pragma userControlDuration(120)

#include "Vex_Competition_Includes.c"   //Main competition background code...do not modify!



//global variables
float rpsL;
float rpsR;
float virtualspeed;
float shootingspeed;
float rotationsL;
float rotationsR;
float timeval;


//gloabal variables for PID

int shooterspeedL; //power sent to left motors
int shooterspeedR; //power sent to right motors
float shooterfactor;
float target; //stores desired speed
float ticksL; //ticks of left Motor Encoder
float ticksR; //ticks of right Motor Encoder
float time; //stores the elapsed time value
float velocityL; //velocity for left side
float velocityR; //velocity for right side
float errorL; //error for left side
float errorR; //error for right side
float errorTL; //total error for left side
float errorTR; //total error for right side
float proportionL; //value of proportional term for left side
float proportionR;//value of proportional term for right side
float integralL; //value of integral term for left side
float integralR;//value of integral term for right side
float derivitiveL; //value of derivative term for left side
float derivitiveR;//value of derivitive term for right side
float integralActiveZone = 400; //value in which the integral term is active
float lastErrorL; //stores previous error for left side
float lastErrorR; //stores previous error for right side
float Kp = .001;
float Ki = .000000001;
float Kd = 0;







task velocity_control() {

	while(true) {
		nMotorEncoder[leftshooter2] = 0;//clear the encoders
		nMotorEncoder[rightshooter2] = 0;//clear the encoders
		clearTimer(T1);
		time = time1[T1] / 1000
		ticksL = nMotorEncoder[leftshooter2];
		ticksR = nMotorEncoder[rightshooter2];
		rotationsL = ticksL / 392;
		rotationsR = ticksR / 392;
		velocityL = (rotationsL / time) * ((49 / 3);
		velocityR = (rotationsR / time) * ((49 / 3);

		errorL = target - velocityL;
		errorR = target - velocityR;
		if(errorL<integralActiveZone && errorL != 0) {
			errorTL += errorL;
		}
		else {
			errorTL = 0;
		}

		if(errorR<integralActiveZone && errorR != 0) {
			errorTR += errorR;
		}
		else {
			errorTR = 0;
		}
		if(errorTL > 50/Ki) {
			errorTL = 50/Ki;
		}
		if(errorTR > 50/Ki) {
			errorTR = 50/Ki;
		}
		if(errorL == 0) {
			derivitiveL = 0;
		}
		if(errorR == 0) {
			derivitiveR = 0;
		}
		proportionL = errorL * Kp;
		proportionR = errorR * Kp;
		integralL = errorTL * Ki;
		integralR = errorTR * Ki;
		derivitiveL = (errorL - lastErrorL) * Kd;
		derivitiveR = (errorR - lastErrorR) * Kd;

		lastErrorL = errorL;
		lastErrorR = errorR;

		shooterspeedL = proportionL + integralL + derivitiveL;
		shooterspeedR = proportionR + integralR + derivitiveR;

		motor[leftshooter1] = motor[leftshooter2] = shooterspeedL;
		motor[rightshooter1] = motor[rightshooter2] = shooterspeedR;
		}
}







/////////////////////////////////////////////////////////////////////////////////////////
//
//                          Pre-Autonomous Functions
//
// You may want to perform some actions before the competition starts. Do them in the
// following function.
//
/////////////////////////////////////////////////////////////////////////////////////////

void pre_auton()
{
	// Set bStopTasksBetweenModes to false if you want to keep user created tasks running between
	// Autonomous and Tele-Op modes. You will need to manage all user created tasks if set to false.
	bStopTasksBetweenModes = true;

	// 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.
//
/////////////////////////////////////////////////////////////////////////////////////////

task autonomous()
{
	//clearTimer(T1);
	//launch();
}

/////////////////////////////////////////////////////////////////////////////////////////
//
//                                 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.
//
/////////////////////////////////////////////////////////////////////////////////////////

task usercontrol() {

	while (true)
	{
startTask(velocity_control);
		slaveMotor(intake1, intake2);

		while(vexRT[Btn5U] == 1) {
			motor[leftfront] = motor[leftback] = vexRT[Ch2];
			motor[rightfront] = motor[rightback] = vexRT[Ch3];
		}

		motor[leftfront] = motor[leftback] = vexRT[Ch3];
		motor[rightfront] = motor[rightback] = vexRT[Ch2];

		if(vexRT[Btn5UXmtr2] == 1) {
			motor[intake1] = -80;
			motor[intake2] = -80;
		}

		else if(vexRT[Btn5DXmtr2] == 1) {
			motor[intake1] = 80;
			motor[intake2] = 80;
		}

		else {
			motor[intake1] = 0;
			motor[intake2] = 0;
		}



		slaveMotor(rightshooter1, rightshooter2);
		slaveMotor(leftshooter1, leftshooter2);


		if(vexRT[Btn8RXmtr2] ==1) {

		target = 500;


		}

		else if(vexRT[Btn8UXmtr2] ==1) {

		}

		else if(vexRT[Btn8LXmtr2] ==1) {

		}

		else if(vexRT[Btn8DXmtr2] == 1) {

		}


		else {
			target = 0;
		}
	}
}


		nMotorEncoder[leftshooter2] = 0;//clear the encoders
		nMotorEncoder[rightshooter2] = 0;//clear the encoders
		clearTimer(T1);
		time = time1[T1] / 1000
		ticksL = nMotorEncoder[leftshooter2];
		ticksR = nMotorEncoder[rightshooter2];
		rotationsL = ticksL / 392;
		rotationsR = ticksR / 392;
		velocityL = (rotationsL / time) * ((49 / 3);
		velocityR = (rotationsR / time) * ((49 / 3);

It seems like your velocity variables would be 0, or very close to 0. You’re setting the encoder values to 0, then immediately recording the velocity based on the encoder ticks (which were just set to 0). I think adding a wait in between somewhere would help. Not sure why your flywheel motors are fluctuating though, but this is what i’m thinking: Your error is always some high number (equal to your target velocity, because the program thinks the flywheel isn’t moving), so the integral builds up, which ramps up the flywheel motors. Then eventually the integral builds up above the integralActiveZone and resets to 0, which makes the flywheel motors stop.

Hope this helps!

It looks like he uses a timer in there. Just giving a cursory glance, I don’t really see anything wrong with the code. I’m surprised your target values are that high though; what kind of encoder/what do you have it geared at? Insane oscillation like this usually happens with p values that are too high, but seeing as your p value is 0.001, that’s probably not the issue here.

Hmm just read the part where you said it does calculate the velocity correctly.

I had a similar problem with a PID controller I programmed. Basically, I decided the issue was that, when the flywheel ramps up to the target speed, the error becomes 0. So without the integral and derivative, the PID controller would want to set the motor power to 0. In this sense it’s treating the motor power as acceleration (the motor power only needs to be above 0 when we need to get to a target speed). But when we want to say make a robot drive forwards, we set the motor power to 127, but we don’t set the motor power to 0 when it’s moving at the speed we want, we keep it at a certain level above 0. So in that sense I wasn’t sure whether motor power should practically be treated as acceleration or velocity. What I did was I changed


		motor[leftshooter1] = motor[leftshooter2] = shooterspeedL;
		motor[rightshooter1] = motor[rightshooter2] = shooterspeedR;

to


		motor[leftshooter1] = motor[leftshooter2] += shooterspeedL;
		motor[rightshooter1] = motor[rightshooter2] += shooterspeedR;

(added +'s)

and that seemed to remove the fluctuations. However, looking back, I’m not 100% sure my logic was right. I think motor power can be treated as acceleration, and the Integral part of the PID loop should take care of friction which pulls the speed down.

I’m surprised that there’s no divide by zero error with this code.

Time is never zero… right?


        clearTimer(T1);
	time = time1[T1] / 1000

Since the timer is cleared then immediately read from, time is always 0.

Also you never actually wait so your integral probably gets insanely high instantly. Once your error gets above the target value I think your wiping the integral back to 0. This seems to be the cause of your 127 to 0 change.

Ok, so I made some changes to the code. I am also now using the in built getMotorVelocity function. From what I have seen, that has made the velocity stay closer in the debugger. Also, if I take out the integral term completely, then the motors don’t even spin even though the proportion term in the debugger is around 113. I also changed the constants Kp and Ki to -1. I found that the positive values were causing it to spin backwards.

#pragma config(I2C_Usage, I2C1, i2cSensors)
#pragma config(Sensor, I2C_1,  ,               sensorQuadEncoderOnI2CPort,    , AutoAssign)
#pragma config(Sensor, I2C_2,  ,               sensorQuadEncoderOnI2CPort,    , AutoAssign)
#pragma config(Motor,  port1,           rightshooter1, tmotorVex393HighSpeed_HBridge, openLoop, reversed)
#pragma config(Motor,  port2,           rightshooter2, tmotorVex393HighSpeed_MC29, openLoop, encoderPort, I2C_1)
#pragma config(Motor,  port3,           rightback,     tmotorVex393_MC29, openLoop, reversed)
#pragma config(Motor,  port4,           rightfront,    tmotorVex393_MC29, openLoop, reversed)
#pragma config(Motor,  port5,           intake1,       tmotorVex393_MC29, openLoop, reversed)
#pragma config(Motor,  port6,           intake2,       tmotorVex393_MC29, openLoop, reversed)
#pragma config(Motor,  port7,           leftfront,     tmotorVex393_MC29, openLoop)
#pragma config(Motor,  port8,           leftback,      tmotorVex393_MC29, openLoop)
#pragma config(Motor,  port9,           leftshooter2,  tmotorVex393HighSpeed_MC29, openLoop, reversed, encoderPort, I2C_2)
#pragma config(Motor,  port10,          leftshooter1,  tmotorVex393HighSpeed_HBridge, openLoop)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

#pragma platform(VEX)

//Competition Control and Duration Settings'
#pragma competitionControl(Competition)
#pragma autonomousDuration(20)
#pragma userControlDuration(120)

#include "Vex_Competition_Includes.c"   //Main competition background code...do not modify!



//global variables
float rpsL;
float rpsR;
float virtualspeed;
float shootingspeed;
float rotationsL;
float rotationsR;
float timeval;


//gloabal variables for PID

int shooterspeedL = 0; //power sent to left motors
int shooterspeedR = 0; //power sent to right motors
float shooterfactor;
float target; //stores desired speed
float ticksL; //ticks of left Motor Encoder
float ticksR; //ticks of right Motor Encoder
float time; //stores the elapsed time value
float velocityL = 0; //velocity for left side
float velocityR = 0; //velocity for right side
float errorL = 0; //error for left side
float errorR = 0; //error for right side
float errorTL = 0;//total error for left side
float errorTR = 0; //total error for right side
float proportionL = 0; //value of proportional term for left side
float proportionR = 0;//value of proportional term for right side
float integralL = 0; //value of integral term for left side
float integralR = 0;//value of integral term for right side
float derivitiveL = 0; //value of derivative term for left side
float derivitiveR = 0;//value of derivitive term for right side
float integralActiveZone = 20; //value in which the integral term is active
float lastErrorL = 0; //stores previous error for left side
float lastErrorR = 0; //stores previous error for right side
float Kp = -1;
float Ki = -1;
float Kd = 0;







task velocity_control() {

	while(true) {
	//	bUseVexI2CEncoderVelocity;
		velocityL = getMotorVelocity(leftshooter2);
		velocityR = getMotorVelocity(rightshooter2);

		errorL = target - velocityL;
		errorR = target - velocityR;



		if(errorL<integralActiveZone && errorL != 0) {
			errorTL += errorL;
		}
		else {
			errorTL = 0;
		}

		if(errorR<integralActiveZone && errorR != 0) {
			errorTR += errorR;
		}
		else {
			errorTR = 0;
		}
		if(errorTL > 50/Ki) {
			errorTL = 50/Ki;
		}
		if(errorTR > 50/Ki) {
			errorTR = 50/Ki;
		}
		if(errorL == 0) {
			derivitiveL = 0;
		}
		if(errorR == 0) {
			derivitiveR = 0;
		}
		proportionL = errorL * Kp;
		proportionR = errorR * Kp;
		integralL = errorTL * Ki;
		integralR = errorTR * Ki;
		derivitiveL = (errorL - lastErrorL) * Kd;
		derivitiveR = (errorR - lastErrorR) * Kd;

		lastErrorL = errorL;
		lastErrorR = errorR;

		shooterspeedL = proportionL + integralL + derivitiveL;
		shooterspeedR = proportionR + integralR + derivitiveR;

		//shooterspeedL = proportionL + derivitiveL;
		//shooterspeedR = proportionR + derivitiveR;

		motor[leftshooter1] = motor[leftshooter2] = shooterspeedL;
		motor[rightshooter1] = motor[rightshooter2] = shooterspeedR;
		}
}







/////////////////////////////////////////////////////////////////////////////////////////
//
//                          Pre-Autonomous Functions
//
// You may want to perform some actions before the competition starts. Do them in the
// following function.
//
/////////////////////////////////////////////////////////////////////////////////////////

void pre_auton()
{
	// Set bStopTasksBetweenModes to false if you want to keep user created tasks running between
	// Autonomous and Tele-Op modes. You will need to manage all user created tasks if set to false.
	bStopTasksBetweenModes = true;

	// 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.
//
/////////////////////////////////////////////////////////////////////////////////////////

task autonomous()
{
	//clearTimer(T1);
	//launch();
}

/////////////////////////////////////////////////////////////////////////////////////////
//
//                                 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.
//
/////////////////////////////////////////////////////////////////////////////////////////

task usercontrol() {

	while (true)
	{
startTask(velocity_control);
		slaveMotor(intake1, intake2);

		while(vexRT[Btn5U] == 1) {
			motor[leftfront] = motor[leftback] = vexRT[Ch2];
			motor[rightfront] = motor[rightback] = vexRT[Ch3];
		}

		motor[leftfront] = motor[leftback] = vexRT[Ch3];
		motor[rightfront] = motor[rightback] = vexRT[Ch2];

		if(vexRT[Btn5UXmtr2] == 1) {
			motor[intake1] = -80;
			motor[intake2] = -80;
		}

		else if(vexRT[Btn5DXmtr2] == 1) {
			motor[intake1] = 80;
			motor[intake2] = 80;
		}

		else {
			motor[intake1] = 0;
			motor[intake2] = 0;
		}



		slaveMotor(rightshooter1, rightshooter2);
		slaveMotor(leftshooter1, leftshooter2);


		if(vexRT[Btn8RXmtr2] ==1) {
//if(velocityL < target) {
//	velocityL = getMotorVelocity(leftshooter2);
//		velocityR = getMotorVelocity(rightshooter2);
//		motor[rightshooter1] = motor[leftshooter1] = 127;
//			motor[leftshooter2] = motor[rightshooter2] = 127;
//		}
//		else{
//				motor[rightshooter1] = motor[leftshooter1] = 0;
//			motor[leftshooter2] = motor[rightshooter2] = 0;

		//target = 500;
			target = 120;
		}

		if(vexRT[Btn8UXmtr2] ==1) {

		}

		else if(vexRT[Btn8LXmtr2] ==1) {
				motor[rightshooter1] = motor[leftshooter1] = 50;
			motor[leftshooter2] = motor[rightshooter2] = 50;
velocityL = getMotorVelocity(leftshooter2);
		velocityR = getMotorVelocity(rightshooter2);
		}

		else if(vexRT[Btn8DXmtr2] == 1) {

		}


		else {
			target = 0;
			stopTask(velocity_control);
				motor[rightshooter1] = motor[leftshooter1] = 0;
			motor[leftshooter2] = motor[rightshooter2] = 0;
		}
	}
}