PID kontrol, mühendislikte en çok kullanılan ve en çok yanlış anlaşılan algoritmalardan biri. Adı karmaşık görünse de yaptığı iş son derece sezgisel: bir şeyi olması gereken yerde tutmak.
Klima odayı 22 derecede tutarken, drone havada asılı kalırken, çizgi izleyen robot bandın üstünde giderken hep aynı üç terim çalışıyor. Bu yazıda PID’i formülle değil sezgiyle anlatıyorum, sonra Arduino’da çalışan bir uygulamaya dönüştürüyoruz.
Temel fikir: hata
Her kontrol sisteminin başlangıcı tek bir sayı: hata.
hata = hedef_deger - olculen_deger
Robot çizginin 2 cm sağındaysa hata 2’dir. Motorun hızı 100 rpm olması gerekirken 80 rpm ise hata 20’dir. PID’in tek görevi bu sayıyı sıfıra indirmek.
Peki hatayı bilmek yeterli mi? Değil. Çünkü “ne kadar düzeltme yapmalıyım?” sorusunun cevabı sadece hatanın büyüklüğüne bağlı değil.
P — Oransal terim
En sezgisel bileşen: hata büyükse çok düzelt, küçükse az düzelt.
P = Kp * hata
Direksiyon çevirmeye benzer. Yoldan çok saptıysanız direksiyonu çok çevirirsiniz, az saptıysanız hafifçe.
Aşağıdaki simülatörde üç katsayıyı da canlı değiştirebilirsiniz. Başlangıç için Ki ve Kd’yi sıfıra çekip yalnızca Kp ile oynayın: küçük değerlerde sistem hedefe yavaş sürünür, büyüttükçe hızlanır, belli bir noktadan sonra hedefin etrafında salınmaya başlar. Bu üç davranışı bir kez gözünüzle görmek, sayfalarca anlatımdan daha çok şey öğretir.
P’nin iki problemi
1. Kalıcı hata. Hata küçüldükçe düzeltme de küçülür. Sürtünmeyi yenemeyecek kadar küçük bir düzeltme kalır ve sistem hedefe hiç ulaşamaz — hep birazcık altında kalır.
2. Salınım. Kp’yi büyütürseniz kalıcı hata azalır ama sistem hedefi aşar, geri döner, tekrar aşar. Ağır bir kapıyı hızla itip durduramamaya benzer.
I — Integral terim
Integral, hatanın zaman içindeki toplamına bakar. “Uzun süredir hedefin altındayım” bilgisini taşır.
integral = integral + hata;
I = Ki * integral;
Küçük ama ısrarlı bir hata, integrali sürekli büyütür; büyüyen integral çıkışı zorlar ve sonunda sürtünme yenilir. P’nin çözemediği kalıcı hatayı çözen budur.
Integral şişmesi (windup)
I terimi tehlikelidir. Sistem hedefe ulaşamıyorsa (motor zaten tam güçte, ya da bir engel var) integral büyümeye devam eder. Engel kalktığında birikmiş dev integral sistemi hedefin çok ötesine fırlatır.
İki standart çözüm var:
// 1) Integrali sınırla
integral = constrain(integral, -MAKS_INTEGRAL, MAKS_INTEGRAL);
// 2) Çıkış doyuma ulaştıysa integrali biriktirme
if (cikis < MAKS_CIKIS && cikis > MIN_CIKIS) {
integral += hata;
}
Robotik projelerinde I terimi çoğu zaman gereksizdir, hatta zararlıdır. Çizgi izleyen robotta Ki=0 genellikle en iyi sonucu verir. I’yı sadece sabit bir bozucu kuvvet (yerçekimi, sürekli sürtünme, sabit rüzgâr) varsa ekleyin.
D — Türev terim
Türev, hatanın ne kadar hızlı değiştiğine bakar. Geleceği tahmin eder.
turev = hata - onceki_hata;
D = Kd * turev;
Hata hızla küçülüyorsa, sistem hedefe hızla yaklaşıyor demektir — D terimi frene basar ve taşmayı engeller. Bu yüzden D’ye “sönümleme” de denir.
Türev gürültüsü
D teriminin zayıf noktası gürültüdür. Ölçümde küçük bir sıçrama, türevde büyük bir sıçrama yaratır. Gürültülü bir analog sensörle yüksek Kd kullanmak, motorların titremesine yol açar.
İki çözüm:
// 1) Türeve alçak geçiren filtre uygula
turev_filtreli = 0.7 * turev_filtreli + 0.3 * (hata - onceki_hata);
// 2) Hata yerine ölçümün türevini al (türev vuruşunu önler)
turev = -(olcum - onceki_olcum);
İkinci yöntem, hedef değeri aniden değiştiğinde çıkışın sıçramasını (derivative kick) da önler.
Üçünü birleştirmek
float Kp = 2.0, Ki = 0.0, Kd = 12.0;
float integral = 0;
float oncekiHata = 0;
unsigned long sonZaman = 0;
const unsigned long PERIYOT = 10; // ms — SABİT tutun
void loop() {
if (millis() - sonZaman < PERIYOT) return;
sonZaman = millis();
float olcum = sensorOku();
float hata = HEDEF - olcum;
float turev = hata - oncekiHata;
oncekiHata = hata;
float cikis = Kp * hata + Ki * integral + Kd * turev;
cikis = constrain(cikis, -255, 255);
// Anti-windup: sadece doyumda değilken biriktir
if (cikis > -255 && cikis < 255) {
integral += hata;
integral = constrain(integral, -1000, 1000);
}
cikisUygula(cikis);
}
Buradaki en kritik satır PERIYOT kontrolü. PID matematiği sabit örnekleme aralığı varsayar. Döngü bazen 5 ms bazen 50 ms sürerse, I ve D terimleri anlamsızlaşır.
Katsayı ayarı: sistematik yöntem
Rastgele sayı denemek yerine şu sırayı izleyin. Her adımda tek bir katsayı değiştirin ve sonucu not edin.
| Adım | Yapılacak | Beklenen davranış |
|---|---|---|
| 1 | Ki=0, Kd=0. Kp’yi küçükten başlat. | Sistem yavaş yavaş hedefe yaklaşır, tam ulaşamaz. |
| 2 | Kp’yi iki katına çıkararak artır. | Tepki hızlanır, sonunda salınım başlar. |
| 3 | Salınım noktasından %40–60 geri çek. | Hızlı ama kararlı tepki. |
| 4 | Kd ekle, Kp’nin 5 katından başla. | Salınım sönmeye başlar, taşma azalır. |
| 5 | Kd’yi artır; titreme başlarsa geri çek. | Yumuşak ve hızlı oturma. |
| 6 | Kalıcı hata varsa çok küçük Ki ekle. | Hata yavaşça sıfıra iner. |
Belirtilerden teşhis
| Gördüğünüz | Muhtemel sebep | Deneyin |
|---|---|---|
| Çok yavaş tepki | Kp düşük | Kp’yi artırın |
| Hedef etrafında salınım | Kp yüksek, Kd düşük | Kd artırın veya Kp düşürün |
| Yüksek frekanslı titreme | Kd çok yüksek / gürültü | Kd düşürün, ölçümü filtreleyin |
| Hedefe hiç ulaşamıyor | Kalıcı hata, I yok | Küçük Ki ekleyin |
| Büyük taşma sonra düzelme | Integral şişmesi | Integrali sınırlayın |
| Düzensiz, öngörülemez | Döngü periyodu değişken | Sabit periyot kurun |
Katsayıları canlı değiştirebilmek, ayar süresini saatlerden dakikalara indirir. Üç potansiyometre bağlayın ya da ESP32 kullanıyorsanız Bluetooth üzerinden katsayı gönderin. Her seferinde kod yükleyerek ayar yapmaya çalışmak en büyük zaman kaybıdır.
Üç gerçek uygulama
1. Çizgi izleyen robot
Hedef: çizgi robotun merkezinde. Ölçüm: sensör dizisinden gelen konum. Çıkış: sol ve sağ motor hız farkı.
int hata = konumHesapla() - MERKEZ;
int duzeltme = Kp * hata + Kd * (hata - oncekiHata);
motorSur(TABAN + duzeltme, TABAN - duzeltme);
Tipik değerler: Kp = 0.05–0.3, Ki = 0, Kd = 0.5–3. Ayrıntılar için çizgi izleyen robot rehberine bakın.
2. Motor hız kontrolü
Hedef: sabit rpm. Ölçüm: encoder’dan hesaplanan devir. Çıkış: PWM değeri.
Burada I terimi gereklidir, çünkü sürtünme ve yük sabit bir bozucu kuvvettir. Encoder kullanımını motor seçimi rehberinde anlattım.
3. Duvar takibi
Hedef: duvardan sabit mesafe. Ölçüm: yan taraftaki mesafe sensörü. Çıkış: dönüş açısı.
Bu uygulamada sensör seçimi kritik: geniş konili ultrasonik sensör köşelerde yanılır, dar ışınlı ToF daha kararlıdır. Karşılaştırma için mesafe sensörleri rehberine göz atın.
PID’i yeniden kullanılabilir hale getirmek
Aynı PID kodunu her projede kopyalayıp yapıştırmak yerine küçük bir sınıf yazın. Bir kez yazarsınız, her yerde kullanırsınız — ve bir hata düzelttiğinizde her projede düzelmiş olur.
class PID {
public:
PID(float kp, float ki, float kd, float cikisSiniri = 255)
: _kp(kp), _ki(ki), _kd(kd), _sinir(cikisSiniri) {}
void ayarla(float kp, float ki, float kd) { _kp = kp; _ki = ki; _kd = kd; }
void sifirla() { _integral = 0; _oncekiOlcum = 0; _ilk = true; }
float hesapla(float hedef, float olcum, float dt) {
float hata = hedef - olcum;
// Türevi ölçümden al: hedef değişince çıkış sıçramaz
float turev = _ilk ? 0 : -(olcum - _oncekiOlcum) / dt;
_oncekiOlcum = olcum;
_ilk = false;
float cikis = _kp * hata + _ki * _integral + _kd * turev;
// Anti-windup: sadece doyumda değilken biriktir
if (cikis < _sinir && cikis > -_sinir) {
_integral += hata * dt;
}
return constrain(cikis, -_sinir, _sinir);
}
private:
float _kp, _ki, _kd, _sinir;
float _integral = 0, _oncekiOlcum = 0;
bool _ilk = true;
};
Kullanımı:
PID cizgiPid(0.08, 0.0, 1.2, 255);
void loop() {
static unsigned long son = 0;
unsigned long simdi = millis();
if (simdi - son < 10) return;
float dt = (simdi - son) / 1000.0;
son = simdi;
float duzeltme = cizgiPid.hesapla(MERKEZ, konumHesapla(), dt);
motorSur(TABAN + duzeltme, TABAN - duzeltme);
}
Buradaki iki ayrıntı önemli. Birincisi, dt’yi gerçek geçen süreden hesaplamak; böylece döngü periyodu biraz kaysa bile I ve D terimleri doğru kalır. İkincisi, sifirla() fonksiyonu: robot durup yeniden başladığında birikmiş integrali temizlemezseniz ilk hareket bir sıçramayla başlar.
İleri besleme: PID’in eksik yarısı
PID tepkiseldir — bir hata oluşmadan hiçbir şey yapmaz. Oysa bazı bozucu etkileri önceden biliyorsunuzdur. İleri besleme (feedforward), bu bilinen kısmı doğrudan çıkışa ekler.
Basit örnek: motor hız kontrolü. Belirli bir hız için gereken PWM değerini kabaca bilirsiniz. Bunu PID’in düzeltmesine eklerseniz, PID sadece kalan farkla uğraşır:
// Sıfırdan başlamak yerine doğru değere yakın başla
float ileriBesleme = hedefHiz * PWM_PER_RPM;
float duzeltme = hizPid.hesapla(hedefHiz, olculenHiz, dt);
float cikis = ileriBesleme + duzeltme;
PWM_PER_RPM katsayısını bulmak için birkaç PWM değerinde motorun kaç rpm döndüğünü ölçüp doğru orantı kurmanız yeterli.
Sonuç: sistem hedefe çok daha hızlı ulaşır ve PID katsayıları daha küçük, dolayısıyla daha kararlı olabilir. Robot kolu gibi yerçekimine karşı çalışan sistemlerde ileri besleme neredeyse zorunludur.
Ne zaman PID kullanmamalı
PID her problemin çözümü değil:
- Aç/kapa yeterliyse (ısıtıcı, fan) PID gereksiz karmaşıklık getirir. Histerezisli basit bir termostat mantığı daha uygundur.
- Sistem çok gecikmeliyse (büyük termal kütle) PID zorlanır; ileri besleme (feedforward) eklemek gerekir.
- Sistem doğrusal değilse tek bir katsayı seti her çalışma noktasında iyi sonuç vermez. Kazanç planlaması (gain scheduling) gerekebilir.
Özet
- P şimdiki hataya bakar → ana itici güç.
- I geçmiş hataya bakar → kalıcı hatayı kapatır, dikkatli kullanın.
- D geleceği tahmin eder → salınımı söndürür, gürültüye duyarlı.
Bir PID döngüsünü doğru kurmayı öğrendiğinizde, robotikten sıcaklık kontrolüne, drone dengelemeden CNC eksen kontrolüne kadar çok geniş bir alanda aynı aracı kullanabiliyorsunuz. Bu yüzden zaman ayırmaya değer.
Sıkça Sorulan Sorular
PID kontrol nedir, ne işe yarar?
PID, ölçülen bir değeri istenen hedefte tutmaya yarayan bir kontrol algoritmasıdır. Hedefle gerçek değer arasındaki farkı (hata) üç açıdan değerlendirir: P hatanın büyüklüğüne, I hatanın zaman içindeki birikimine, D hatanın değişim hızına bakar. Üçünün toplamı düzeltme sinyalini oluşturur.
PID katsayıları nasıl bulunur?
En pratik yöntem elle ayardır: Ki ve Kd sıfırken Kp’yi sistem sürekli salınıma girene kadar artırın, sonra %40–60 geri çekin. Ardından Kd’yi salınım sönene kadar yükseltin. Ki’yi en son ve çok küçük adımlarla, sadece kalıcı hata varsa ekleyin.
Sadece P kontrol yeterli mi?
Birçok robot uygulamasında evet. Çizgi izleyen robotlar düşük hızda saf P ile gayet iyi çalışır. D bileşeni hız arttıkça, I bileşeni ise yerçekimi veya sürtünme gibi sabit bir bozucu kuvvet varsa gerekli hale gelir.
Integral şişmesi (windup) nedir?
Sistem hedefe ulaşamazken integral teriminin sürekli büyümesidir. Hata sonunda kapansa bile birikmiş integral çıkışı zorlamaya devam eder ve sistem hedefi büyük bir taşmayla aşar. Çözüm, integral terimini constrain() ile sınırlamak veya çıkış doyuma ulaştığında integrali dondurmaktır.
PID döngüsü ne sıklıkla çalışmalı?
Döngü periyodu sabit olmalı ve sistemin tepki süresinden en az 10 kat hızlı olmalı. Çizgi izleyen robotlar için 100–1000 Hz, sıcaklık kontrolü için 1–10 Hz uygundur. Değişken periyot, D teriminde büyük gürültü üretir.
Yorumlar
0Henüz yorum yok. İlk yorumu sen yaz!