Başlangıç > Donanım/Yazılım > PID implementasyonu (kodlaması)

PID implementasyonu (kodlaması)

PID (Proportional, Integral, Derivative) oransal-integral-türevsel denetleyici PID kontrol döngüsü yöntemi, yaygın endüstriyel kontrol sistemlerinde kullanılan genel bir kontrol döngüsü geribildirim mekanizmasıdir.

Ayrık zamanda PID kontrolü, kararlılık söz konusu olduğunda, geniş kapsamlı incelenmesi ve analiz edilmesi gerekir. Bu yazıda transfer fonksiyonu bilinen bir devre için kapalı çevrim bir kontrol döngüsü tasarlamayacak ve kararlılık analizi yapmayacağız. Bunun yerine şansını denemek isteyenler için basit PID kontrol algoritmasının nasıl kodlanması gerektiğini ve katsayılarının nasıl ayarlaması gerektiğini anlatacağız. Ayrıca bir PID döngüsünde dikkat edilmesi gerekenlere de göz atacağız.

Örnekleme frekansı PID döngüsünün en önemli temellerindendir. Belirli zaman aralıklarında alınan örneklerle döngü beslenir. Burada dikkat edilmesi gereken en önemli şey; iki örnekleme zamanı arasında kontrol mekanizması için gereken örnekler alınmalı, PID hesabı bitirilmeli ve gerekli çıkış işlemi çoktan yapılmış olmalıdır. Herhangi bir şekilde yapılan işlemlerin bir sonraki örnekleme zamanının içine sarkması döngünün kararsız çalışmasına yol açacaktır. Mikroişlemci veya mikrokontrolör ile PID döngüsü gerçekleştiriyorsanız; bir timer ile örnekleme zamanının ayarlanması, timer kesme rutininin içerisinde PID ile ilgili tüm işlemlerin bitirilip çıkılması tavsiye edilir.

PID döngüsünde dikkat edilmesi gereken bir diğer kriter de doğrusal bölgede çalışmaktır. Yani örneklediğiniz işaretinde, çıkışı sürmek için kullandığınız değerlerinde sonucunda doğrusal bir sonuç elde edilmelidir. Giriş ve çıkışın y = ax + b şeklinde ifade edilebilir olması döngünün kararlılığı bakımından önemlidir. Doğrusal olmayan sistemin neden olmadığını şöyle açıklayabiliriz. Örneğin bir eğrinin tepe notasında kalmaya çalışan bir top için PID uyguladığımızı düşünelim. Top tepe noktasındayken aşağı düşmeye başladığında, olduğu noktaya geri yükselmek için sağa mı gitmeli sola mı gitmeli şeklinde iki farklı çözüm ortaya çıkacaktır, y = x^2 gibi. y nin 1 olması için x = 1 de olabilir -1 de.  Burada bahsi geçen PID kodlaması bu işi çözemeyecek kadar ilkeldir. Bu yüzden, topu bir noktaya kadar dengede tutabilirken, bu noktanın ilerisinde dengeyi sağlamak için atılan adım aslında gerekenin tam tersi olduğundan, döngü topun dengede kalmaması için gereken her türlü adımı atacaktır.

Bir diğer önemli noktada PID döngüsü girdi ve çıktılarının birbirleri türünden olmaları gerekliliğidir. Yani armut girdisiyle elma kontrol edilmemelidir. Bu yüzden girişler öncelikle normalize edilir (-1, +1 aralığı gibi). Bu durumda PID döngüsü hata oranları cinsinden çalışır. PID çıkışı ise işlem sonucunda hata cinsinden kontrol edeceği birimin cinsine dönüştürülür. Örneğin girişler 0 ila 255 arasında değişiyorsa, bu aralık -1, +1 aralığına çekilir. PID nin tüm işlemleri bu değerler için yapılır. Çıkışta elde edilen sonuç, döngünün kontrol ettiği birimin cinsine yani aralığına dönüştürülür. Mesela PID çıkışının sürdüğü birimin alacağı değerler 200 ila 1000 arasında değişiyorsa, -1 +1 aralığı bu aralığa çevrilir.

PID kodlaması için gerçek sayılar kullanılması önerilir. Donanımsal olarak gerçek sayılar ile program yazma imkanı varsa derleyici için ek olarak bir ayar gerektirip gerektirmediğinden emin olun. Donanımsal olarak gerçek sayıların desteklenmediği durumda gerçek sayılarla çalışmak hesaplama süresini artıracaktır. Bu durumda iki örnekleme zamanı arasında tüm hesaplamanın bittiğinden ve çıkış birimini güncellediğinizden emin olun. Ayrıca gerçek sayıların desteklenmediği durumlarda tam sayılarda kullanılabilir fakat sistem kararsızlığı ihtimali yüksektir.

PID_en.svg

Yukarıda gösterilen klasik PID döngüsünün bir şemasıdır. Bu döngüyü gerçekleyen kod aşağıda verilmiştir.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//PID parametreleri
#define MIN_ACTUATOR_VALUE 200 //PID sonucunu gercekleyecek birimin alacagi min deger
#define MAX_ACTUATOR_VALUE 1000 //PID sonucunu gercekleyecek birimin alacagi max deger
#define MIN_SETPOINT_VALUE 0 //ayar noktasinin alacagi min deger
#define MAX_SETPOINT_VALUE 255 //ayar noktasinin alacagi max deger
#define MIN_FEEDBACK_VALUE 0 //geribeslemenin alacagi min deger
#define MAX_FEEDBACK_VALUE 255 //geribeslemenin alacagi max deger
#define PID_SCALE_LOWER_LIMIT -3.0 //PID sonucunun alacagi min deger. PID limitlerine gore bu deger degisebilir
#define PID_SCALE_UPPER_LIMIT +3.0 //PID sonucunun alacagi max deger. PID limitlerine gore bu deger degisebilir
#define SET_POINT 85 //ayar noktasi

void vPID(uint16 wInput); //her timer kesmesinde bu fonksiyonu cagirin. wInput degiskeni geribeslemeden aldiginiz degerdir.
static float fNormalizeFeedback(uint16 wValue);
static void vSetActuator(uint16 wToActuator); //PID sonucunu gerceklemek icin bu fonksiyonu cagirin

//Asagidaki fonksiyonlarda degisiklik yapmayin
static float fNormalizeSetPoint(uint16 wValue);
static float fNormalize(uint16 wValue, float fPIDLowerLimit, float fPIDUpperLimit, uint16 wInputMin, uint16 wInputMax);
static uint16 wPlantProcess(float fInput, float fPIDLowerLimit, float fPIDUpperLimit, uint16 wInputMin, uint16 wInputMax);

void vPID(uint16 wInput)
{
float fError;
static float fLastError = 0;
uint16 wToActuator;

float fSetPoint = fNormalizeSetPoint(SET_POINT);
float fInput = fNormalizeFeedback(wInput);
float fPIDOutput;

float fProportional;
float fDerivative ;
static float fIntegral;
//PID kazanc ve limitleri. Debug modunda belirlediginiz kazanc ve limit degerlerini,
//programin son halinde sabit degiskeler olarak degistirebilirsiniz.
static float fGainProportional = 1; //P kazanc
static float fLimitProportional = 1; //P limit
static float fGainIntegral = 0; //I kazanc
static float fLimitIntegral = 1; //I limit
static float fGainDifferential = 0; //D kazanc
static float fLimitDerivative = 1; //D limit

//Error
fError = fSetPoint - fInput;

//Proportional
fProportional = fError * fGainProportional;
if (fProportional > fLimitProportional) fProportional = fLimitProportional;
else if (fProportional < -fLimitProportional) fProportional = -fLimitProportional;
//Integral
fIntegral += fError * fGainIntegral;
if (fIntegral > fLimitIntegral) fIntegral = fLimitIntegral;
else if (fIntegral < -fLimitIntegral) fIntegral = -fLimitIntegral;
//Derivative
fDerivative = (fError - fLastError) * fGainDifferential; fLastError = fError;
if (fDerivative > fLimitDerivative ) fDerivative = fLimitDerivative ;
else if (fDerivative < -fLimitDerivative ) fDerivative = -fLimitDerivative ;

fPIDOutput = fProportional + fIntegral + fDerivative ;

if (fPIDOutput > PID_SCALE_UPPER_LIMIT) fPIDOutput = PID_SCALE_UPPER_LIMIT;
else if (fPIDOutput < PID_SCALE_LOWER_LIMIT) fPIDOutput = PID_SCALE_LOWER_LIMIT;

wToActuator = wPlantProcess(fPIDOutput, PID_SCALE_LOWER_LIMIT, PID_SCALE_UPPER_LIMIT, MIN_ACTUATOR_VALUE, MAX_ACTUATOR_VALUE);

vSetActuator(wToActuator);

//Calisma aninda PID degerlerini gozlemlemek icin asagidaki satiri acabilirsiniz
//printf("Set:%0.3f fError:%0.3f Act:%d P:%0.3f I:%0.3f D:%0.3f \n\r", fSetPoint, fError, wToActuator, fProportional, fIntegral, fDerivative);

}

//Bu fonksiyonu PID sonucunu gerceklemek icin kullanin
static void vSetActuator(uint16 wToActuator)
{
//bu fonksiyonu PID sonucu ile etkilemek istediginiz
//register i guncelleyin. mesela bir PWM modulunun darbe bosluk oranini
//ayarlayan bir register gibi
}

//Bu satirin altinda kalanlari degistirmeyin
static float fNormalize(uint16 wValue, float fPIDLowerLimit, float fPIDUpperLimit, uint16 wInputMin, uint16 wInputMax)
{
//function normalizes the input to PID limits
//y = ax + b
float a = (fPIDUpperLimit - fPIDLowerLimit)/(wInputMax - wInputMin);
float b = (fPIDUpperLimit + fPIDLowerLimit - a*(wInputMax + wInputMin))/2.0;

return a*wValue + b;
}

static float fNormalizeSetPoint(uint16 wValue)
{
return fNormalize(wValue, PID_SCALE_LOWER_LIMIT, PID_SCALE_UPPER_LIMIT, MIN_SETPOINT_VALUE, MAX_SETPOINT_VALUE);
}

static float fNormalizeFeedback(uint16 wValue)
{
return fNormalize(wValue, PID_SCALE_LOWER_LIMIT, PID_SCALE_UPPER_LIMIT, MIN_FEEDBACK_VALUE, MAX_FEEDBACK_VALUE);
}

static uint16 wPlantProcess(float fInput, float fPIDLowerLimit, float fPIDUpperLimit, uint16 wInputMin, uint16 wInputMax)
{
//function returns the real output
//x = (y - b)/a
float a = (fPIDUpperLimit - fPIDLowerLimit)/(wInputMax - wInputMin);
float b = (fPIDUpperLimit + fPIDLowerLimit - a*(wInputMax + wInputMin))/2.0;

return (uint16)((fInput - b)/a);
}

PID katsayılarının ayarlanması istenilen kriterlere bağlı olup deneme yanılma ile oldukça zor bulunur. Ziegler Nichols metodu katsayıların kabaca ayarlanmasına yardımcı olacak bir metoddur. Metod, sistemi osilasyona götüren P değeri üzerinden katsayıları bulmaya dayanır. Fakat sistemi osilasyona götüren değeri bulduktan sonra sistemin osilasyon frekansını bulacak imkânınız yoksa pratik olarak bu değerin yarısını P kazancı olarak alıp kalan integral ve türev katsayılarını deneysel olarak bulmak mümkündür. Bu durumda printf gibi bir fonksiyonla anlık olarak döngü değişkenlerinin değerleri izlenebilir. Debug modunda çalışmak mümkünse anlık olarak katsayıları değiştirip kontrol döngüsünün katsayılarının daha hızlı bulunması da sağlanabilir. Daha öncede bahsettiğimiz gibi döngüyü ayarlamak için kullandığınız fonksiyonlarında zaman harcadığını unutmayın ve bir sonraki örnekleme zamanından önce herşeyin bittiğine emin olun.

Konu ile ilgili görüş, öneri ve sorularınız için yorum alanını kullanabilirsiniz.

Reklamlar
Kategoriler:Donanım/Yazılım
  1. marksman
    19 Mayıs 2017, 05:17

    tesekkurler r2d2droids. nasil bir sistemi kontrol ediyorsunuz merak ettim

  2. marksman
    19 Mayıs 2017, 14:28

    elma ile armut olayini acarsak, kontrol edilen degisken cinsi ile bizim verdigimiz cikis cinsi genelde hep farkli. ornegin bir oyuncak arabanin hizini kontrol etmek istiyoruz. kontrol ettigimiz degisken hiz(m/dk) ama motora verdigimiz cikis ise voltaj(0-12V) veriyoruz. buradaki eslestirme mantigi nedir. mesela istenen hiz 50m/dk olsun. mevcut hiz ise 40m/dk olsun. buradaki pid hesaplamasini yaparken hata=10 mu alacagiz yoksa hata=%20 mi alacagiz. yanlizca p kontrol yaptigimizi varsayalim, motora v=10.Kp volt mu verecegiz yoksa v=12.%20.Kp volt mu verecegiz vb…

  3. 20 Mayıs 2017, 05:29

    Tam olarak anlatmak istediğimde buydu. Herşeyin aynı birim üzerinden çalışması gerekiyor. Yani motor devir ayarı 40 ile 50 arası değişiyorsa bu aralık -1, +1 aralığına çekilmeli, motor kontrol geri beslemeside yine aynı şekilde düzenlenmeli. PID sonucu ise seçilen bu aralıktan olması gereken 0-12V aralığına son işlem olarak çevrilmelidir. Buradan elde edilen değer doğrudan motoru sürmek için kullanılır.
    Konuyla ilgili gerçek bir uygulamanız varsa ve ayrıntılı anlatabilirseniz bununla ilgili örnek bir uygulama içeren yeni bir yazı yazabilirim.

    • marksman
      17 Ağustos 2017, 21:04

      vaktiniz okursa…
      0-12v dc motora takili bir pervanenin ruzgari ile bir kanatın açısını(orn. 0-60 derece) kontrol ediyorum.

      yanilmiyorsam sizin anlatiminiza gore çıktı olarak tanımladiginiz, kontrol edilen degisken olan ‘açı’ ile, girdi olarak tanimladiginiz motora verdigimiz ‘voltaj’ degiskenlerini nasil normalize ediyoruz.
      (0)-(12) : (-1)-(+1)
      (0)-(60) : (-1)-(+1)
      seklinde oranlarsak;
      ornegin;
      Kp=5, set degeri=45, olculen deger=30
      varsayalim. bu durumda sadece p kontrol yaparsak motora kac volt verecegiz. ayrica ki ve kd de killanilirsa ne olur.

      enteresan olan da hic bir pid anlatimlarinda bu tarz uygulama ornekleri goremiyoruz her sey teorik toz pembe anlatiliyor. onerebileceginiz turkce kaynak varsa…

  1. No trackbacks yet.

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Connecting to %s

%d blogcu bunu beğendi: