Pages: 1 ... 6 7 [8] 9   Go Down
Author Topic: Arduino PID Library  (Read 54100 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 1
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

very interesting
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 6
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey all,

I've finished up the code for a standalone sous vide controller with a small LCD screen and a 3-button interface.  I'm using the one-wire DS18B20 temperature sensor for input and a solid state relay receives the output.  

All the parts are here, and everything works, I just need to put it together now.  I figured I'd post the code for others to critique:

Code:
#include <TimerOne.h>
#include <PID_Beta6.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
#include <Button.h>

#define ONE_WIRE_BUS 9

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

int controlPin = 10;

int upButtonPin = 6;
int downButtonPin = 7;
int selButtonPin = 8;

Button upButton = Button(upButtonPin, PULLDOWN);
Button downButton = Button(downButtonPin, PULLDOWN);
Button selButton = Button(selButtonPin, PULLDOWN);

double params[4] = {140, 90,300,0};
char param_char[4] = {'S', 'P', 'I', 'D'};
double increment[4] = {1, 5, 5, 5};

double Input, Output;                            
double Setpoint = params[0];                          
double Bias = 200;

float temperature = 0;                            
int menuPos = 0;
int loopDelay = 0;

PID pid(&Input, &Output, &Setpoint, &Bias, params[1], params[2], params[3]);

void setup()
{
  /*Serial.begin(9600);*/
  pinMode(controlPin, OUTPUT);  
  
  sensors.begin();

  pid.SetOutputLimits(0,1023);  
  Output = 0;
  pid.SetMode(AUTO);

  Timer1.initialize();
  Timer1.pwm(controlPin, Output);
  
 
  lcd.begin(16, 2);
  lcd.clear();
  lcd.print("Arduino PID");
  lcd.setCursor(0,1);
  lcd.print("Controller");
  /*Serial.print("Arduino PID Controller\n");*/
  delay(5000);
  lcd.clear();
}

void loop()
{
  sensors.requestTemperatures();
  temperature = sensors.getTempFByIndex(0);

  Input = (double)temperature;
  if(Setpoint - Input > 3){
    Setpoint -= 5;
    pid.Compute();
    Setpoint += 5;
  }
  else{
    pid.Compute();
  }
  
  Timer1.setPwmDuty(controlPin, (int)Output);
  delay(loopDelay);
  
  lcd.setCursor(0,0);
  lcd.print("T:");
  lcd.print(temperature,1);
  lcd.setCursor(9,1);
  lcd.print("O:");
  lcd.print(Output,0);
  lcd.setCursor(0, 1);
  lcd.print("S:");
  lcd.print(Setpoint,0);

  /*Serial.print("T:");
  Serial.print(temperature);
  Serial.print("\t");
  Serial.print("O:");
  Serial.print(Output,0);
  Serial.print("\t");
  Serial.print("S:");
  Serial.print(Setpoint,0);
  Serial.print("\n");*/
  
  if(selButton.uniquePress()) {
    lcd.clear();
    for(menuPos = 0; menuPos < 4; ){
      lcd.setCursor(0,0);
      lcd.print(param_char[menuPos]);
      lcd.print(": ");
      lcd.print(params[menuPos],0);

      /*Serial.print(param_char[menuPos]);
      Serial.print(": ");
      Serial.print(params[menuPos],0);      
      Serial.print("\n");*/

      if (upButton.uniquePress()) {        
        params[menuPos] += increment[menuPos];
      }
      if (downButton.uniquePress()) {        
        params[menuPos] -= increment[menuPos];
      }
      if(selButton.uniquePress()) {
        menuPos++;
        lcd.clear();
      }
    }
    Setpoint = params[0];
    pid.SetTunings(params[1], params[2], params[3]);
    lcd.clear();
  }
}  

All the commented out serial stuff is from testing before my LCD came.  Let me know if you have any questions or advice on something I could do better.

PS - I just realized scanning my code here that the menu loop keeps the PID from calculating, so if it was left in that mode the output would never change.  I guess I'll work a button timer in there to jump back to the main loop if you don't do anything for 5 seconds or so.


Logged

San Diego
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi, I just got my Arduino last week and have been playing around with it. Im working on part of a robot that is a turntable. Basically a disc with a friction drive (1 DC motor) that needs to rotate to specific locations. I had previously coded it in basic with my own PID but i wanted it to work with my new arduino. I ran across your library and had some questions. How is it that the direction of the motor is changed? not sure if i missed something but i cant seem to find where to define a pin for direction of the motor. The code i have so far is below, the position of the turntable is read with a potentiometer. Any help is appreciated.  smiley-grin

Code:
/*
Created by Alan Sanchez 2010
*/

#include <PID_Beta6.h>
double Setpoint, Input, Output;
PID pid(&Input, &Output, &Setpoint,5,30,1);


//VARIABLES//

int motorpwm = 3;
int direc = 2;
int button = 12;
int pos;
int photo;

void setup() {
  
  Serial.begin(9600);
  pinMode(motorpwm, OUTPUT);
  //pinMode(direc, OUTPUT);
  pinMode(button, INPUT);    //declare button pin as input
  Input = analogRead(A0);
  Output = 0;
  pid.SetOutputLimits(0,500);
  pid.SetMode(AUTO);       //turn on PID
  Setpoint = 313;
  
}


void loop() {
  
  digitalWrite(direc, HIGH);
      if (digitalRead(button) == HIGH){       //if button is pressed go to end()
          Serial.println("button has been pressed");
            while(1) {
                //pos = analogRead(A0);      //read pot in analog pin A0
                //photo = analogRead(A1);    //read photosensor in analog pin A1
            
                Input = analogRead(A0);
                pid.Compute();
                analogWrite(motorpwm, Output/4);
            
            Serial.print("Pot Value = ");   //print pot value
            Serial.print(Input, DEC);
            Serial.print("     ");
            Serial.print("Output = "); //print photo value
            Serial.println(Output/4, DEC);
            }
    analogWrite(motorpwm, 0);
      }
}


Logged

Boston
Offline Offline
Full Member
***
Karma: 0
Posts: 101
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
How is it that the direction of the motor is changed?

I understand your question to mean that the motor seems to be going backwards, forcing you away from setpoint.  if this is the case, you need to change the sign of the P term.  so instead of:
Code:
PID pid(&Input, &Output, &Setpoint,5,30,1);

you want:
Code:
PID pid(&Input, &Output, &Setpoint,-5,30,1);
« Last Edit: November 21, 2010, 08:06:20 am by br3ttb » Logged

San Diego
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

no, what i meant to ask is how does the PID switch the direction of the motor to deal with overshoot. The way it is right now it just stops but because of inertia it wont stop at the correct spot, and if it goes past the set point there is no bringing it back.
Logged

San Diego
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

actually if it isnt supported, i guess what i could do is switch direction depending on error something like:


Code:

if (Error<0) {
Pterm = -5
else if (Error>0)
Pterm = 5
}


Logged

Boston
Offline Offline
Full Member
***
Karma: 0
Posts: 101
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
no, what i meant to ask is how does the PID switch the direction of the motor to deal with overshoot
ah.  in that case, take a look at Post 16 on this thread.  maybe that will help.

Brett
Logged

San Diego
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

that works! thank you

this is what the code looks like now
Code:
/*
Created by Alan Sanchez 2010
*/

#include <PID_Beta6.h>
double Setpoint, Input, Output;
PID pid(&Input, &Output, &Setpoint,3,30,5);


//VARIABLES//

int motorpwm = 5;
int direc = 4;
int button = 2;
int pos;
int photo;

void setup() {
  
  Serial.begin(9600);
  pinMode(motorpwm, OUTPUT);
  pinMode(direc, OUTPUT);
  pinMode(button, INPUT);    //declare button pin as input
  Input = analogRead(A0);
  Output = 0;
  pid.SetOutputLimits(-400,400);
  pid.SetMode(AUTO);       //turn on PID
  Setpoint = 313;
  
}


void loop() {
  
  //digitalWrite(direc, HIGH);
      if (digitalRead(button) == HIGH){       //if button is pressed go to end()
          Serial.println("button has been pressed");
            while(1) {
                //pos = analogRead(A0);      //read pot in analog pin A0
                //photo = analogRead(A1);    //read photosensor in analog pin A1
            
                Input = analogRead(A0);
                pid.Compute();
              
                if (Output < 0) {
                digitalWrite(direc, LOW);
                }
                else if (Output>0) {
                  digitalWrite(direc, HIGH);
                }
                else {
                  Output=0;
                }
                int drive = Output/4;
                analogWrite(motorpwm, drive);
            
           Serial.print("Pot Value = ");   //print pot value
           Serial.print(Input, DEC);
           Serial.print("     ");
           Serial.print("Output = "); //print photo value
           Serial.println(drive, DEC);
            }
      }
}



all thats left to do is fine tune the PID. Again thanks for the library, it saves soooo much time.
Logged

San Diego
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

actually, ive been noticing that the Output value doesnt change when i set ranges for output from -255 to 255 . it just goes to max or min no matter what the gains are. as soon as comment out the limits it starts working again. any idea what might be causing this?

code below:
Code:
/*
Created by Alan Sanchez 2010
*/

#include <PID_Beta6.h>
double Setpoint, Input, Output;
PID pid(&Input, &Output, &Setpoint,4,35,10);


//VARIABLES//

int motorpwm = 5;
int direc = 4;
int button = 2;
int pos;
int photo;

void setup() {
  
  Serial.begin(9600);
  pinMode(motorpwm, OUTPUT);
  pinMode(direc, OUTPUT);
  pinMode(button, INPUT);    //declare button pin as input
  Input = analogRead(A0);
  Output = 0;
  pid.SetInputLimits(0,1023);
  pid.SetOutputLimits(-255,255);
  pid.SetMode(AUTO);       //turn on PID
  Setpoint = 313;
  
}


void loop() {
  
  //digitalWrite(direc, HIGH);
      if (digitalRead(button) == HIGH){       //if button is pressed go to end()
          Serial.println("button has been pressed");
            while(1) {
                //pos = analogRead(A0);      //read pot in analog pin A0
                //photo = analogRead(A1);    //read photosensor in analog pin A1
            
                Input = analogRead(A0);
                pid.Compute();
              
                if (Output < 0) {
                digitalWrite(direc, LOW);
                }
                else if (Output>0) {
                  digitalWrite(direc, HIGH);
                }
                else {
                  Output=0;
                }
                int drive = abs(Output)/4;
                analogWrite(motorpwm, drive);
            
           //Serial.print("Pot Value = ");   //print pot value
           Serial.println(Input, DEC);
           //Serial.print("     ");
           //Serial.print("Drive = "); //print photo value
           Serial.println(Output, DEC);
           //Serial.print("     ");
           //Serial.print("Direc = "); //print photo value
           //Serial.println(direc, DEC);          
          }
      }
}


Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 3
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello Brett,

First of all GREAT WORK!!

Your library has saved me a lot of time.

But i´m having a small problem. For some reason using the Processing Front End, everything works great, but when i try to edit manually the values and click to send it to Arduino, it just comes back to the original values.

As i couldn´t figure out what is wrong, since the communication seems to be ok, and i can see Processing IS sending something to arduino, and the arduino portion seems to be alright.. i had the ideia to have the best of both worlds anyway. I addes 3 pots to manually contro e P.I and D values. I would watch the values and the result on the Processing Front End, and manually adjust the values via the potentiometers.

The only problem now is that for some reason, pid.SetTunings doesn´t seem to change the values correctly.

Remember the Example 2 ? Well i´m doing exactly the same thing exept for the setpoint ramp stuff. Mine´s fixed.

Here´s the relevant portion of my code. I´m using it to stabilize de Y axis of a UAV. It´s loaded into a test platform where i have a horizontal arm being held in place by a fixed vertical arm. It has a motor on each side, and they try to keep the arm stable and leveled. Any disturbances should be imediately compensated.

Code:
double Input, Output, Setpoint;
 PID pid(&Input, &Output, &Setpoint, 3,9,3);
 // PID pid(&Input, &Output, &Setpoint,potPin1,potPin2,potPin3);
double Outputinv;
unsigned long serialTime; //this will help us know when to talk with processing

const int buttonPin = 52;
int buttonState = 0;

void setup() {
  static int i;
  Serial.begin(57600);
   pinMode(13, OUTPUT);
   servoL.attach(2);
   servoR.attach(3);
  pinMode(buttonPin, INPUT);
 
  
   pid.SetInputLimits(-80,89);
   pid.SetOutputLimits(900,1200); //tell the PID to range the output from 1000 to 2000
   Output = 900; //start the output at min and let the PID adjust it from there
  
   pid.SetMode(AUTO); //turn on the PID
   pid.SetSampleTime(10);
Setpoint = 57;
Code:
void loop() {
  
   buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {  
    initmotors();  
    delay(2000);
  }
  
  getEstimatedInclination();

 pot1Value = analogRead(potPin1);
   pot1Value = pot1Value /100;
    pot2Value = analogRead(potPin2);
   pot2Value = pot2Value /100;
    pot3Value = analogRead(potPin3);
   pot3Value = pot3Value /100;

 pid.SetTunings(potPin1,potPin2,potPin3);

 Input = (RwEst[1]*100);
   pid.Compute();
   servoR.writeMicroseconds(Output);
   Outputinv = map(Output, 900, 1200, 1200, 900);
   servoL.writeMicroseconds(Outputinv);
  
     if(millis()>serialTime)
  {
    SerialReceive();
    SerialSend();
    serialTime+=500;
  }
  
  
}

Any help would be much appreciated.


Logged

San Diego
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hello everyone, I gave up using the library for my DC motor project. I was just wasting too much time and getting nowhere. However I wrote my own PID. Code is attached below for anyone to use if they run into the same problems. It will allow you to switch motor directions to do precise positioning with a DC motor, all you have to do is adjust the gains to suit your needs.

-Alan

Code:
/*
Created by Alan Sanchez 2010
*/

//VARIABLES//

  int motorpin = 5;
  int drive;
  int direc = 4;
 // int direc;
  int button = 2;
  int pos;
  int photo;
  int target = 313;
  
//PID Variables//

  int kp = 90;
  int ki = 2;
  int kd = 100;
  int error;
  int last;
  int P;
  int I;
  int D;
  int integral = 0;
  int inthresh = 25;
  int nMet;
  int nMetReq = 5;
  int Emax = 2;
  int Vmax = 2;
  int V;
  
void setup() {
  
  Serial.begin(9600);
  pinMode(motorpin, OUTPUT);
  pinMode(direc, OUTPUT);
  pinMode(button, INPUT);    //declare button pin as input
 
}


void loop() {
 // direc = HIGH;
  //digitalWrite(direc, HIGH);
      analogWrite(motorpin,0);
      int state = digitalRead(button);
      if (state == HIGH){       //if button is pressed begin PID
          Serial.println("button has been pressed");
            while(1) {
                pos = analogRead(A0);      //read pot in analog pin A0
                photo = analogRead(A1);    //read photosensor in analog pin A1        
                drive = PID();             //get drive val from PID
                analogWrite(motorpin, drive); //send PID drive command to motor
                Serial.print("nMet =    ");
                Serial.println(nMet,DEC);
                //Check nMet, if satisfied end program.
                if(abs(error) < Emax && abs(V) < Vmax) {
                  nMet = nMet+1;
                  if(nMet > nMetReq) {
                    analogWrite(motorpin, 0);
                    Serial.println("Target reached :D");
                    end();
                  }
                }
                    
           //Serial.print("Pot Value = ");   //print pot value
           //Serial.print(pos, DEC);
           //Serial.print("     ");
           Serial.print("Drive = "); //print drive value
           Serial.print(drive, DEC);
           Serial.print("     ");
           Serial.print("Direc = "); //print direc
           Serial.println(direc, DEC);          
            }
        
      }
}

//PID: Calculates PID drive value.
int PID()
{
    //Serial.println("inside PID");
    error = target-pos;
    //Serial.print("abs(error)=");
    //Serial.println(abs(error),DEC);
    if (abs(error) < inthresh) {
    integral = integral + error;
    }
    else {
      integral = 0;
    }
    //Serial.println(integral,DEC);
   P = error*kp;
   I = integral*ki;
   D = V*kd;
   drive = P+I+D;
     Serial.print("P+I+D=  ");
     Serial.println(drive, DEC);
     if (drive<0) {
       digitalWrite(direc, LOW);
     }
     else if (drive>0) {
       digitalWrite(direc, HIGH);
     }
     else {
       drive=0;
     }
     Serial.print("direc_inside=  ");
     Serial.println(direc, DEC);
  drive = abs(drive)/50;
   Serial.print("drive1=  ");
     Serial.println(drive, DEC);
  drive = min(drive, 170);
     Serial.print("drive2=  ");
     Serial.println(drive, DEC);
  drive = max(drive, 80);
     Serial.print("drive 3  ");
     Serial.println(drive, DEC);
  last = pos;
  return(drive);
}

//end program
int end() {
  Serial.println("Program will now end");
  while(1);
}
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 3
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Never mind, i got it to work. Still don´t know what was causing the error though. I just deleted that portion of the code and re-typed it, and when i tried to compile, it did with no errors this time. Go figure.

Anyways, i´m still trying to get the tunings right. That is one hard task to acomplish.

Here´s my first test with manual tuning:



I wonder if i ever am going to see that "axis" stable.

Either i am too "intelectually challenged" , or there´s a looot of smart guys out there, because so many people seem to have built this kind of UAV´s seemingly with no big problems at all..

Maybe there´s an easier implementation of PID for a UAV that works better, with a fast response time and an easier way of tuning it.

One more thing, during the tests i have noticed that after a few minutes of continuous running of the engines, they start to stress out and they characteristics change a bit (sensitivity to commands and stuff). Isn´t there a way of implementing some kind of "dynamic tuning" system?

Thanks !
Logged

0
Offline Offline
Newbie
*
Karma: 1
Posts: 11
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Hey all,

I've finished up the code for a standalone sous vide controller with a small LCD screen and a 3-button interface.  I'm using the one-wire DS18B20 temperature sensor for input and a solid state relay receives the output.  

I'm planning on doing the same thing in a few weeks here(with an lm335 though).  What did you do to waterproof the sensor?
Logged

'round the world...
Offline Offline
Faraday Member
**
Karma: 42
Posts: 3267
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Anyways, i´m still trying to get the tunings right. That is one hard task to acomplish.

There are some manuals around the web. Have you got feedback sent to the computer so you can see the response and measure overshoot, settling time, etc, etc??

I find this one to be acceptable. http://www.expertune.com/tutor.html
Wikipedia's article is a good reference for this also.

Also, if you go through ATMEL's application notes, they have application note 221 for AVR that is the implementation of a PID in C for AVR. You can get some info from that too.
Logged

Eu não sou o teu criado. Se respondo no fórum é para ajudar todos mediante a minha disponibilidade e disposição. Responder por mensagem pessoal iria contra o propósito do fórum e por isso evito-o.
Se realmente pretendes que eu te ajude por mensagem pessoal, então podemos chegar a um acordo e contrato onde me pagas pela ajuda que eu fornecer e poderás então definir os termos de confidencialidade do meu serviço. De forma contrária toda e qualquer ajuda que eu der tem de ser visível a todos os participantes do fórum (será boa ideia, veres o significado da palavra fórum).
Nota também que eu não me responsabilizo por parvoíces escritas neste espaço pelo que se vais seguir algo dito por mim, entende que o farás por tua conta e risco.

Dito isto, mensagens pessoais só se forem pessoais, ou seja, se já interagimos de alguma forma no passado ou se me pretendes convidar para uma churrascada com cerveja (paga por ti, obviamente).

0
Offline Offline
Newbie
*
Karma: 0
Posts: 1
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There are some interger versions available e.g. google for:
AVR221: Discrete PID controller on tinyAVR and megaAVR devices
Logged

Pages: 1 ... 6 7 [8] 9   Go Up
Jump to: