PID Kontrollü Çizgi İzleyen Robot

Çizgi izleyen robotlar siyah zemin üzerinde beyaz çizgiyi veya beyaz zemin üzerindeki siyah çizgiyi uzaktan kumanda tertibatı olmadan otonom olarak takip ederek hareket eden robotlardır.

Çizgiyi algılayabilmek için ön tarafına, tasarımına göre 3, 5 veya 8 adet sensör yerleştirilir. Bu sensörler kızılötesi ışık yayarak karşıdan yansıyan ışığın algılanması mantığı ile çalışır. En çok kullanılan sensörler CNY70 veya Pololu firması QTR8 serisi sensörlerdir.

Aracı hareket ettirebilmek için iki adet elektrik motoru vardır. Bu motorları aracın tasarımına ve ulaşmak istediği hıza göre seçmek gerekir. Benim tercihim 16mm 12V 1500RPM redüktörlü DC motordur. Ayrıca mikrodenetleyicilerin çıkış akımları bu motorları sürmek için yeterli olmadığından Pololu firması tarafından üretilen motor sürücü kullanmaktayım.

Mantık olarak aracın hareket kuralı sensörden okunan veriye göre motorlara farklı güçler uygulanmasıdır. Motorlara güç aktarımı PWM sinyalleri ile gönderilir. Gönderilecek olan PWM sinyalinin boyutu ise PID kontrol algoritması ile hesaplanır.




Şimdi yaptığım robot üzerinden hareket edecek olursak robot üzerinde QTR8A kontrast sensörü (Çizgi algılayan), PIC18F4450 mikrodenetleyici, TB6612FGN motor sürücü, 12V 2000RPM redüktörlü DC motor, 12v 1350mAh Lipo Batarya bulunmaktadır. kontrol devresi ve robotun gövdesi oluşturulduktan sonra geriye kalan tamamen yazılım işidir. Yazılım için MikroC dili kullandım. Aynı yazılım istenirse farklı dillerde de yazılabilir.

MikroC Kodları

#define Buton           PORTC.RC4 //Belki lazım olur diye bırakılmış buton pini
#define StndBy          PORTC.RC6 //Motor sürücü kartının on/off pini
#define Led_On_Off      PORTA.RA4 //QTR 8A nın on/off pini



int proportional=0,last_proportional=0,integral=0,derivative=0,power_difference=0,pozisyon=0,Mpos=0;
char son_sol,son_sag,i,j;
const Tpos=0;

void set_motor(int sol,int sag)
{
     if(sol<0){PortD.RD0=1;PortD.RD1=0;}
     else {PortD.RD0=0;PortD.RD1=1;}
     if(sag<0){PortD.RD2=1;PortD.RD3=0;}
     else {PortD.RD2=0;PortD.RD3=1;}
     Pwm1_Set_Duty(sag);
     Pwm2_Set_Duty(sol);
}
void ADC_Oku() //ADC ile sensör kartı (Pololu QTR-8A) analog olarak okunur
{
    unsigned int an0,an1,an2,an3,an4,an5,an6,an7;
    const oran=100; //oran değeri ortamdaki ışığa göre sensör kartının kalibrasyonu için ayarlanır.
                    //deneme yanılma ile bulunur

    an7=Adc_Read(7);
    an6=Adc_Read(6);
    an5=Adc_Read(5);
    an4=Adc_Read(4);
    an3=Adc_Read(3);
    an2=Adc_Read(2);
    an1=Adc_Read(1);
    an0=Adc_Read(0);

    if(an7<oran) PortB.F0=1; else PortB.F0=0;
    if(an6<oran) PortB.F1=1; else PortB.F1=0;
    if(an5<oran) PortB.F2=1; else PortB.F2=0;
    if(an4<oran) PortB.F3=1; else PortB.F3=0;
    if(an3<oran) PortB.F4=1; else PortB.F4=0;
    if(an2<oran) PortB.F5=1; else PortB.F5=0;
    if(an1<oran) PortB.F6=1; else PortB.F6=0;
    if(an0<oran) PortB.F7=1; else PortB.F7=0;


    switch (PortB)
    {
          case 0b00000001:         {Mpos=7;break;} //siyah zemine beyaz çizgi
          case 0b00000011:         {Mpos=6;break;}
          case 0b00000010:         {Mpos=5;break;}
          case 0b00000110:         {Mpos=4;break;}
          case 0b00000100:         {Mpos=3;break;}
          case 0b00001100:         {Mpos=2;break;}
          case 0b00001000:         {Mpos=1;break;}
          case 0b00011000:         {Mpos=0;break;}
          case 0b00010000:         {Mpos=-1;break;}
          case 0b00110000:         {Mpos=-2;break;}
          case 0b00100000:         {Mpos=-3;break;}
          case 0b01100000:         {Mpos=-4;break;}
          case 0b01000000:         {Mpos=-5;break;}
          case 0b11000000:         {Mpos=-6;break;}
          case 0b10000000:         {Mpos=-7;break;}
          case 0b00000000:         {if(Mpos<-4){Mpos=-8;} else { if(Mpos>4) Mpos=8; }}
          
    }
    return Mpos;
}
void pid()
{  
     const enb=127,Kp=13,Kd=10,Ki=1.1; 
     pozisyon=adc_oku();
     proportional=Tpos-pozisyon;
     derivative=(proportional-last_proportional);
     integral=integral+proportional*Ki;
     if (integral>10)integral=10;
     if (integral<-10)integral=-10;
     last_proportional=proportional;
     
     power_difference = proportional*Kp + derivative*Kd + integral;
     if(power_difference> enb)power_difference= enb;
     if(power_difference<-enb)power_difference=-enb;
     son_sol=(char) enb+power_difference;
     son_sag=(char) enb-power_difference;

     set_motor(son_sol,son_sag);
}
void main() 

{
     TrisB  =0b00000000;        //B Portu komple çıkış
     ADCON0 =0b00000001;        // A/D modülü aktif edildi
     ADCON1 = 0x07;
     CMCON =7;                 // Karşılaştırıcılar pasif edildi
     TrisA  =0b11101111;
     TrisE  =0b00000111;
     TrisC  =0b10110001;
     TrisD  =0b00000000;
     StndBy=0;
     Pwm1_Init(100000);
     Pwm2_Init(100000);
     
     Led_On_Off=1; //Sensör kartını aç

     set_motor(0,0); //Motorları durdur

     Pwm1_Start();   //PWMleri başlat
     Pwm2_Start();
     
     PortB=0;       //sensör gösterge ledlerini sıfırla


     while(1) //ana döngü
     {

          pid();


     }

}

Yazılımda dikkat edilecek noktalar ise enb, Kp, Kd, Ki sabitleridir. Bu değerler robotun fiziksel yapısına göre deneme yanılma yolu ile bulunur.

enb: robotun maksimum hızı
Kp: farksal sabit
Kd: Oransal sabit
Ki: İntegral sabit