PID kontrol veri tipi:
typedef struct { unsigned int pv; // olculen deger (feedback) signed int ev; // hata signed int ev_pre; // bir onceki hata signed int ev_sum; // hata integrali signed long int P; // hesaplanan PID komponentleri signed long int I; signed long int D; signed long int out; // hesaplanan cikis unsigned int controlcounter; unsigned overloaded: 1; // loadlevel belirlenen esigi asti ise bu bayrak 1 olur. unsigned restart : 1; // pid ara degerleri baslatilir, akümülatörler sifirlanir unsigned nocontrol: 1; // MDC = speedref, no pid control.. kullanici sifirlamali unsigned controlphase: 2; // siradaki speedcontrol cevriminde ne is yapilacak? } PIDParType;
pv: Process Value
motor encoder’ından örneklenen dönüş hızı değerinin PID çıkış veri tipine göre ölçeklenmiş halidir.
ev: Error Value
Referans değeri ile geçerli değerin farkı. pv çıkış gücü değeri cinsinden ölçekli olduğu için sadece çıkarma işlemi yeterlidir: ev = speedref – pv.
ev_pre:
Previous Error Value, Bir önceki hesaplama çevriminde saklanan hata değeri (türev hesabında kullanılır).
ev_sum:
Sum of Error Values, Hata değerlerinin toplamı (integral hesabında kullanılır)
P,I,D:
Hesaplanan P,I,D bileşenleri
out:
Hesaplanan çıkış. Basitçe, P,I ve D bileşenlerinin toplamından oluşur ancak bu değer fiziksel çıkışın ötesinde olabilir.
controlcounter:
Her SpeedControl( ) çevriminde +1 ilerleyen serbest sayaç. Motor kontrol zamanlamaları sırasında kullanılabilir, PID çevrimiyle eşzamanlı zamanlama sağlar.
overloaded:
Aşırı yüklenme bayrağı. Sürücü thread’i içinde motor akımı izleme ile ya da PID çıkışı üzerinden tetikleniyor olabilir. Kontrol onu sadece set eder. Haricen sıfırlanmalıdır.
restart:
Kontrol biti: Bu bit 1 yapıldığında tüm PID ara değerleri sıfırlanır. İşlem yapılınca bit sıfırlanır.
nocontrol:
Kontrol biti: Bu bit 1 yapıldığında PID kontrol devre dışı olur. speedref = MDC
controlphase
PID scheduling control flag. SpeedControl( ) fonksiyonunun her çağrılmasında hangi PID işlem parçalarının yapılacağını belirler.
Kontrol Çevrimi
// motor hiz kontrolü yapmak için 10ms'de bir çagrilmali: void SpeedControl(void) { LongData w; PID.controlcounter++; if (PID.nocontrol) { MDC = speedref; position.i[0] = POS1CNTL; // position update position.i[1] = POS1HLD; return; } // controlphase scheduling'e gore pv guncellemeleri: //////////////////////////////////////////////////////////////////////////////////// switch (PID.controlphase) { case 0: position.i[0] = POS1CNTL; // position update position.i[1] = POS1HLD; // hiz bilgisini guncelle (40ms'de bir) rpm = VEL1CNT; w.ul = abs(rpm); w.ul *= 5000; w.ul /= pm; PID.pv = w.ul; PID.ev = speedref - PID.pv; PID.controlphase = 1; break; case 1: // integral icin, errorvalue_sum'i güncelle: PID.ev_sum += PID.ev; // ev_sum limitleme (anti windup) if (PID.ev_sum > 3000) PID.ev_sum = 3000; else if (PID.ev_sum < -4000) PID.ev_sum = -4000; // I komponentini hesapla: PID.I = (signed long*)(KiN * PID.ev_sum); PID.controlphase = 2; break; case 2: // hiz bilgisini guncelle (40ms'de bir) rpm = VEL1CNT; w.ul = abs(rpm); w.ul *= 5000; w.ul /= pm; PID.pv = w.ul; // hata degerini guncelle: PID.ev_pre = PID.ev; // simdiki deger onceki deger olarak kaydedilir PID.ev = speedref - PID.pv; PID.controlphase = 3; break; case 3: // D komponentini hesapla: w.i[0] = PID.ev - PID.ev_pre; if (w.i[0] > 2000) w.i[0] = 2000; else if (w.i[0] < -2000) w.i[0] = -2000; PID.D = (signed long*)(KdN * w.i[0]); PID.controlphase = 0; break; } ////////////////////////////////////////////////////////////////////////////////////////// if (PID.restart) { PID.ev = speedref; PID.ev_pre = 0; PID.ev_sum = 0; PID.I = speedref / 3; PID.D = speedref / 3; PID.controlphase = 0; PID.restart = 0; } //// P bileseninin hesabi: P = Kp * ev /////////////////////////////////////////////////////////////////////////////// PID.P = (signed long*) (KpN * PID.ev); /////////////////////////////////////////////////////////////////////////////// //// cikisin birlestirilmesi: ////////////////////////////////////////////////////////////////////////////// PID.out = (signed long*)(PID.P + PID.I + PID.D); PID.out = PID.out / 10; ////////////////////////////////////////////////////////////////////////////// //// output update: (with limiting) ////////////////////////////////////////////////////////////////////////////// if (PID.out < -1000) { MDC = 2000; MOTOR_BRK_L; return; } if (PID.out < 0) { MDC = 500; MOTOR_BRK_L; return; } if (PID.out > 5000) { MDC = 4900; SetDir(); return; } MDC = PID.out; SetDir(); ///////////////////////////////////////////////////////////////////////////////// }