Go Down

Topic: Adjusting PID with Pot (Read 2730 times) previous topic - next topic

MrBear

Hi. I am trying to find the correct PID prarmeters for my homebuilt servo motor. I have combined a Dc motor and a Pot to get a servomotor as you can see on the picture. the motor is mounted on a gear and the pot is mounted directly on the steering shaft under the motor. The servo is supposed to move a steering wheel to a constantly changing position as fast as possible.... it is giving me some trouble though and i cant figure out why. Hope to find someone smarter then myself here.
I have attached 3 pots (one for each parameter) and by adjusting them i can adjust the P,I,D values... but the weird thing is that when i adjust the P and D factores, the system seems to change acordingly, but if i play with the I parameter, it is like the value can only go up! so if i have turned it to a too high value, i have to turn it down and reload the program. Does anyone know why?

Code: [Select]

#include <PID_v1.h>


// Define input/output pins
const int steerPot = A3;
const int wheelPot = A1;
const int outputPin = 3;
const int directionPin = 2;

const double KpPin = A5;  //PID-reg*****************
const double KiPin = A4 ;
const double KdPin = A2;

// Define variables
int steerPos;    // position of steeringwheel
int wheelPos;    // position of wheels
double error;
double Setpoint, Input, Output; // PID variables

double Kp;  //PID-reg***************
double Ki;
double Kd;

// Define changeble values
byte outputMax = 255;
const byte errorMax = 2;
const int steerUL = (1023-175);
const int steerLL = 175;

// Major timer, lets user adjust how often the major loop (read pots and take action) should run
unsigned long previousMillis = 0UL;     
unsigned long inputinterval = 25UL; // Time between main-loop run

// Serial output to console
unsigned long serialMillis = 0UL; // store timer current value, and start offset
unsigned long serialFeedbackMillis = 200UL;  //Time between sending feedback to serial


//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, 1 ,0 ,0, DIRECT);


void setup() {

  TCCR2B = TCCR2B & 0b11111000 | 0x02;  // Timer 2: PWM 3 & 11 @ 3906,25 Hz
  Serial.begin(115200);

  pinMode(steerPot, INPUT);                 
  pinMode(wheelPot, INPUT);
  pinMode(Output, OUTPUT);
  pinMode(directionPin, OUTPUT);

  pinMode(Kp, INPUT);    //PID-reg******************
  pinMode(Ki, INPUT);
  pinMode(Kd, INPUT);

  //initialize the variables we're linked to
  Input = analogRead(steerPot);
  Setpoint = analogRead(wheelPot);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(0, outputMax);  //Limits the output
  myPID.SetSampleTime(50); // Determines how often the PID algorithm evaluates (default = 200)
}

void loop() {

  // current times since startup
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > inputinterval) {  //Checks if its time to run the master loop
    previousMillis += inputinterval;  // if its time, it ads one interuptinterval for the next interval

      Kp = analogRead(KpPin); // reads the 3 pots and updates the Kp,Ki,Kd values
    Ki = analogRead(KiPin);
    Kd = analogRead(KdPin);
    myPID.SetTunings(analogRead(KpPin)/100, analogRead(KiPin)/1000, analogRead(KdPin)/1000);

    error = getError();
    if(error >= errorMax ) {
      TurnR();
    }
    else if (error <= -errorMax)  {
      TurnL();
    }
    else {
      digitalWrite(3, LOW);
    }
  }

  if(currentMillis - serialMillis > serialFeedbackMillis) { //Checks if its time to run the master loop
    serialMillis += serialFeedbackMillis; // if its time to print, it ads one interuptinterval for the next interval

    Cprint();
  }
}
void TurnR() {
  if (analogRead(steerPot) < steerLL) {  // To stop the pot from overturning
    analogWrite(outputPin, 0);
  }   
  else {
    Setpoint = getSetpoint()*-1;
    Input = getInput()*-1;
    myPID.Compute();
    Output = abs(Output);
    digitalWrite(directionPin, HIGH);
    analogWrite(outputPin, Output);
  }
}
void TurnL() {
  if (analogRead(steerPot) > steerUL) {  // To stop the pot from overturning
    analogWrite(outputPin, 0);
  }   
  else {
    Setpoint = getSetpoint();   // If the pot is within the limits, the output is generated and sent to the motorboard
    Input = getInput();
    myPID.Compute();
    Output = abs(Output);
    digitalWrite(directionPin, LOW);
    analogWrite(outputPin, Output);
  }
}
double getError(){
  double wheelPos = analogRead(wheelPot);
  double steerPos = analogRead(steerPot);
  error = (steerPos-wheelPos);
  return error;
}
double getSetpoint(){
  int No1 = analogRead(wheelPot);
  delay(3);
  int No2 = analogRead(wheelPot);
  delay(3);
  int No3 = analogRead(wheelPot);
  Setpoint = (No1+No2+No3)/3;
  return Setpoint;
}
double getInput(){
   int No1 = analogRead(steerPot);
  delay(3);
  int No2 = analogRead(steerPot);
  delay(3);
  int No3 = analogRead(steerPot);
  Input = (No1+No2+No3)/3;
  return Input;
}
void Cprint() {
  Serial.write("error: ");
  Serial.print(error);
  Serial.write("  potRAT: ");
  Serial.print(analogRead(steerPot));
  Serial.write("  potHJUL: ");
  Serial.print(analogRead(wheelPot));
  Serial.write("  Output: ");
  Serial.println(Output);
  Serial.print(" P: ");
  Serial.print(Kp/100);
  Serial.print("  I: ");
  Serial.print(Ki/1000);
  Serial.print("  D: ");
  Serial.println(Kd/1000);

}



















johnwasser

You should not be using 'double' or 'float' for pin numbers:
Code: [Select]

const double KpPin = A5;  //PID-reg*****************
const double KiPin = A4 ;
const double KdPin = A2;

  pinMode(Output, OUTPUT);


You do not need to set pins to INPUT if you are going to use analogRead() with them.
Code: [Select]

  pinMode(steerPot, INPUT);                 
  pinMode(wheelPot, INPUT);


If you take an integer value between 0 and 1023 and divide it by an integer 1000 the only possible outcomes are 0 and 1.  If you divide by 100 you will get values from 0 to 10.  Neither case gives you a wide range of choices.
Code: [Select]

  myPID.SetTunings(analogRead(KpPin)/100, analogRead(KiPin)/1000, analogRead(KdPin)/1000);
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

MrBear

Hi.
I see what you mean using double, i have changed it to int.
Did not know that analogRead() lets me loose the INPUT
The reason for the 100 was that as i get over P=5, the system begins to oscillate and the same goes for teh I and D, the system gets weird when they get high (above 1) - is that normal?

Thanks for the feedback

johnwasser

You should probably do your math in floats for the tuning parameters:
Code: [Select]

    // reads the 3 pots and updates the Kp,Ki,Kd values
    Kp = analogRead(KpPin) / 100.0;  // Values from 0.0 to 10.23
    Ki = analogRead(KiPin) / 1000.0;  // Values from 0.0 to 1.023
    Kd = analogRead(KdPin) / 1000.0;  // Values from 0.0 to 1.023
    myPID.SetTunings(Kp, Ki, Kd);


Start with Ki and Kd at 0.
Tune Kp to get as fast a response as you can without oscillation.  There should be some overshoot.
Turn up Kd just enough to get rid of the overshoot.
Turn up Ki just enough to make the control responsive to small changes.
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

MrBear

That is kinda what i did. I followed wikipedia in the following way:
If the system must remain online, one tuning method is to first set K_i and K_d values to zero. Increase the K_p until the output of the loop oscillates, then the K_p should be set to approximately half of that value for a "quarter amplitude decay" type response. Then increase K_i until any offset is corrected in sufficient time for the process. However, too much K_i will cause instability. Finally, increase K_d, if required, until the loop is acceptably quick to reach its reference after a load disturbance.
I also tried the Ziegler-Nichols tuning method by using a DAQ to measure the oscillation period (plot of the 2 pots) but it yielded something like P= 2,7 I= 6,3 D=0,29. that is completely off and the result was that the system went crazy instantly.

My problem with the tuning is that my Ki factor cant be turned down! if i turn it (Ki) up and the system starts to get unstable, i cant stabilize it again by turning Ki  down -. even if i turn all 3 parameters down to 0, the system is still unstable. I dont understand that.

Thanks for the reply

//Bear

Go Up