STM32 İLE PID KONTROL ALTINDA DC MOTOR SÜRMEK

Bu yayın ile oldukça kapsamlı bir konuya el atıyoruz. Küçük bir DC motoru, sabit bir hızda çalıştırmak için bir kapalı çevrim süreç kontrol sistemi oluşturacağız.

Bunu yapabilmek için, mikro denetleyici sistemleri geliştirirken edindiğimiz neredeyse tüm birikimimize ihtiyacımız olacak. Öyle ki :

  • Motoru istediğimiz hızda sürebilmek için bir TIMER ile PWM kontrol sinyali üreteceğiz
  • Motoru sürmek için bir L298 Motor sürücüyü kontrol edeceğiz.
  • Motorun devir hızını bir Hall Effect sensör ile algılayıp bir TIMER’in “Input Capture” fonksiyonundan yararlanarak ölçeceğiz.
  • İstediğimiz çalışma hızını bir potansiyometre ile ayarlamak üzere bir ADC kullanacağız.
  • Geliştirdiğimiz kodun kontrolu ve hata ayıklaması için ARM Cortex M çekirdeğinin SWV/ITM araçlarını kullanacağız.

İşimiz çok yani, hadi başlayalım.

DONANIM VE KULLANILAN MALZEMELER

MİKRO DENETLEYİCİ

STM32 ailesinin alt seviye üyelerinden bir MCU kullanıyoruz. STM32F103C8 tabanlı bir Mapple Leaf modülü. Harici kristali olmayan bu modülü 36MHz de çalıştıracağız. Bağlantı kolaylığı açısından bu modülü Mapple adaptör kartıma takılı olarak kullanıyorum.

MOTOR VE MİL ÜZERİNDE DÖNEN MIKNATIS

Hobi uygulamaları için satılan redüktörlü minik bir motor kullanıyoruz. Bu motorun miline plastik bir dişli, bu dişlinin bir kenarına da 2 mm çapında 1 mm kalınlığında bir neodyum mıknatısı gömerek yapıştırdım. Bu mıknatıs milin dönüşü esnasında her turda bir defa hall effect sensörün altından geçerek onu uyaracak.

Bu fotoda motor miline takılı plastik dişli ve bunun tepesinde gömülü bulunan mıknatısın manyetik alanını algılayan hall effect sensör görülüyor. Küçük bir PCB üzerine monteli olarak satın aldığım bu sensör modülünü bu şekilde monte edebilmek için biraz uğraşmam gerekti.

HALL EFFECT SENSÖR

Keyes markalı bu sensör minik PCB si ile bir yerlere iliştirirken kolaylık sağlıyor. Bunlar çok ucuz şeyler, Aliexpress de 10 tanesi 2.5 dolar civarında.

Bunu motor milinin yakınına monte edebilmek için iki deliğin olduğu kenara yakın olarak 4 mm lik bir delik daha açmam gerekti. PCB yi, motor mili o delikten geçecek şekilde yerleştirip epoksi yapıştırıcıyla sabitledim.

MOTOR SÜRÜCÜ

Motor ne kadar küçük olursa olsun, mikro denetleyici ile doğrudan süremeyeceğime göre bir de motor sürücü gerekiyor. Şu popüler L298 sürücülerden birisi işimi görecek. L298 in iki H köprüsünden birisini kullanıyorum.

Bu modülün H köprülerinden birisine ait iki giriş konnektörü bizim STM32F103 ün PA2 ve PA3 pinlerine bağlı. STM32 nin bu pinleri GPIO output olarak yapılandırılmış durumda. L298 bu pinlerden, motora istenen yönde akım uygulatan sinyalleri alıyor. Bu H köprüye ait Enable pinine PA0 dan gelen PWM sinyalini uyguluyoruz. İki adet çıkış klemensine de motorumuzu bağlıyoruz.

LCD EKRAN

I2C Arayüzlü 20×4 popüler LCD ekranlardan birini kullanıyorum.



POTANSİYOMETRE

Motorun dönüş hızını ayarlamak için 10 kOhm luk çok turlu bir potansiyometre kullanıyorum. Bunun değeri kritik değil, 0.5K dan 20K ya kadar herhangi bir potansiyometre olabilir ama mutlaka lineer tipte olmalı, daha hassas ayarlama için çok turlu olmasında yarar var. Küçük değerlerde seçilirse besleme yolundan gereksiz yere fazla akım çekeriz.

Bunun iki dış ucundan birisini GND, diğerini de 3V3 Vcc ye bağlıyoruz. Orta ucunu da ADC1 girişi olarak kullandığımız PA1 e bağlıyoruz.

BESLEME KAYNAKLARI

Mikrodenetleyici modülü 5 Vdc veren bir USB kaynaktan besliyorum, modülün kendi 3V3 gerilim regülatörü var. Potansiyometreye ve hall sensöre 3V3 gerilimlerini mikro denetleyici modülden veriyorum.

Motor beslemesi için ayrı bir 12 VDC besleme kaynağı kullanıyorum. Bu besleme gerilimi L298 motor sürücü modüle bağlı.






BAĞLANTILAR

Mikrodenetleyici

PA0 (PWM çıkışı) –> L298 Enable klemensine
PA1 (ADC girişi) –> Potansiyometrenin gezinen ucuna
PA2 (GPIO Output) –> L298 kontrol girişi 1
PA3 (GPIO Output) –> L298 kontrol girişi 2
PA6 (TIM3_CH1) –> Hall effect sensor sinyal çıkışı (S)
PA13 SWDIO –> ST_link
PA14 SWCLK –> ST_Link
PB3 SWO –> ST Link (SWV Debug çıktıları için)
PB6 I2C1 CLK –> LCD I2C CLK
PB7 I2C1 DIO –> LCD I2C DI

Motor –> L298 in çıkış klemenslerine
12 VDC Motor beslemesi –> L298 motor besleme klemenslerine
LCD Besleme –> Mapple adaptör kartı I2C konnektörü
LCD GND –> Mapple adaptör I2C konnektörü

STM32 CUBE IDE İLE YAPILANDIRMA İŞLEMLERİ

Yeni proje oluşturmak için geçilen ilk birkaç adımı atlıyorum. CubeMX yapılandırıcısı açıldıktan sonraki adımlar aşağıdaki gibi.

SİSTEM SAAT AYARLARI

CubeMX üzerinde sistem saat ayarlarını aşağıdaki gibi yaparak dahili osilatör ile -Mapple da harici kristal yok- 36 MHz e ayarlıyoruz. ADC1,2 ayarının 6 MHz olmasına dikkat edelim, bu bizim uygulamamız açısından çok kritik değil ama geçersiz bir değer olmasın. Potansiyometre değişim hızı ne kadar yüksek olabilir ki?

KODLAMA – PWM ÜRETİMİ

Motoru süreceğimiz PWM sinyalini üretmek için TIM2 zamanlayıcısını kullanacağız. PWM i Channel 1 de üreterek bu kanalın bağlı olduğu PA0 portuna veriyoruz.

Sistem saatini 36 MHz e ayarlamış durumdayız. PWM periyodunun 1 milisaniye olmasını istiyorum. TIM2 nin sayıcı frekansını 1µs yaparsam uygun olur. Bu nedenle Prescaler ayarını sistem saatini 36 ya bölecek şekilde “35” olarak yapıyorum. Bu ayarı, istediğimiz bölme faktörünün 1 eksiği olarak yapıyoruz, çünkü cubeMX yapılandırıcısı buna 1 ekleyerek değerlendiriyor. (0 verildiğinde bölme işleminin sonsuz vermemesi için.)

PWM periyodunu belirleyecek olan “Counter Period” değerini de “1000” olarak ayarlıyorum, böylece 1000µs, yani 1 milisaniye periyodlar elde edeceğim.

PWM darbe genişliğini belirleyecek olan parametre yukarıdaki ekran görüntüsünde “Pulse” olarak verilmiş olan. Burada bir değer vermemize gerek yok, çünkü bu değeri yazılım içinde
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pwm); komutu ile ayarlıyoruz.

Programımız içinde Timeri aşağıdaki komut ile başlatmamız gerekiyor :
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);

Değişik PWM değerleri ile PA0 dan aldığımız iki osiloskop görüntüsünü vereyim:

İlki düşük (%40 gibi, PWM 400 civarında seçilmiş) :









İkincisi de yüksek PWM, değerli, %90 seçilmiş PWM 900 olarak ayarlanmış.







TIM3 – INPUT CAPTURE, HALL SENSOR ALGILANMASI

Hall effect sensörden gelen motor devir sinyallarini TIM3 zamanlayıcısının PA5 portuna bağlı Channel 1 girişinden algılayacağız. Bunun için TIM3 ayarlarını yapalım:

TIM3 ZAMANLAYICISININ PRESCCALER PARAMETRESİNİN HESAPLANMASI

Bu zamanlayıcının sayıcısı ile motorun her turunun ne kadar sürdüğünü ölçeceğiz. Bu sayıcı 0 dan başlayıp 65535 e kadar sayabiliyor. Motorun bir turu bu süre içinde tamamlanmazsa, ölçüm yapamayız. Çünkü satıcı değeri 65535 den sonra sıfıra dönerek yeniden saymaya başlayacaktır.

Öte yandan sistem saatimiz 36 MHz, bunun bir periyodu, 0.028 µs ediyor. Bunu sayıcıya doğrudan verirsek 65535 sayımını 1.834 µs de aşacaktır. Bu durumda 545 devir/sn den daha yavaş dönüş hızlarını ölçemeyiz demektir.

O halde bir prescaler faktörü ile sayıcının periyodunu uzatmamız gerekecek. Bu prescaler değerini hesaplayalım:

Kullandığım motor 5 devir/sn nin altında pek stabil çalışmıyor. Bu nedenle minimum devir hızını 5 devir/sn olarak belirledim. Bu da 200 milisaniyelik bir devir süresine karşı düşüyor. Eğer bu süreye karşı 7000 sayıcı periyodunu denk getirecek bir prescaler belirlersem uygun olacak. Neden 7000 diye sormayın, bunu 10000 ya da 2000 diye de seçebilirdik, o an aklıma bu gelmiş işte.

Bu durumda sayıcı periyodu 200 ms/7000 = 28.5 µs oluyor. 36 MHz ilk MCU saatini 1028 e bölersek bu periyodu elde ederiz. Ben bana daha sempatik gelen 1024 değerini tercih ettim. Buradan hareketle hesapladığım devir/dk değerini LCD ekranda gösterdiğim için sonuçta doğru bilgilendirilmiş oluyoruz.

TIM3 INPUT CAPTURE GİRİŞ FİLTRE AYARI

Hall sensörden gelen darbelerin kenarlarında kararsız bölgeler olabilir. Bu nedenle TIM3 girişindeki filtreyi devreye sokmakta yarar var. En uzun filtreleme periyodu olan 15 saat periyodunu seçiyorum.

TIM3 ile kesmeleri kullanıyoruz. O nedenle NVIC ayarları altında Global Interrupt ları etkinleştirelim.

ADC AYARLARI

Motorun hız ayarını potansiyometre ile yapacağız. Bunun için potansiyometrenin orta ucundaki gerilimi STM32 nin ADC1 analog sayısal çeviricisi ile okuyarak, okuduğumuz değeri motordan istediğimiz devir periyoduna karşı düşecek şekilde değerlendireceğiz.

Hesap şöyle : Motorun en yüksek devrine karşı düşen periyod 2000 sayaç atımı. Buradan başlayarak 5000 e kadar giden bir ayar sahamız olsun istiyorum. Böylece motor devrini 420d/dk – 1050 devir/dk aralığında ayarlayabileceğiz.

Potansiyometreden okunan gerilime karşılık gelen ADC okuma değerini periyoda çevirmek için kullanacağımız formül şöyle:

setPoint = (adc * periodSpan) / 4095 + minPeriyod;

Burada periodSpan değeri 5000-2000=3000, minimum periyod da 2000.

Şimdi ADC ayarlarına gelelim. ADC1 in INPUT 1 girişi PA1 portunda, potansiyometremizin orta ucu da buraya bağlı.

Yukarıda görüldüğü gibi ADC1 i etkinleştirmek için IN1 girişini seçiyoruz. Parametre ayarları da burada göründüğü gibi. Yalnız alt pencerenin biraz aşağısını görmek üzere yukarı kaydırmamız gerekiyor.

Burada “Rank” altındaki “Sampling Time” ı 1.5 saat çevrimi olarak seçtim. Potansiyometreden gelen gerilim oldukça stabil, daha uzun bir örnekleme süresine ihtiyaç yok.

Bu ayarlarla, ADC1 girişine potansiyometreden gelen gerilim 0 ila 3V3 arasında değişecek. ADC1 de buna karşılık 0 ila 4095 arasında değişen ölçüm sonuçları verecek. Okuduğumuz bu değerlerden “0” minimum motor periyoduna, “4095” maksimum periyoda karşı gelecek şekilde periyod hesaplayıp “setPoint” olarak PID kontroluna vereceğiz.

I2C AYARLARI

Kullanıcıya bilgi vermek üzere I2C arayüzlü LCD kullanıyoruz. Bunun için I2C1 i etkinleştirmek gerekiyor:

Varsayılan ayarlarda bir değişiklik yapmaya gerek yok, sadece etkinleştirip kapatıyoruz.

CUBE MX YAPILANDIRMALARI TAMAM – ŞİMDİ DEVAMI

PID KONTROL BLOK DİYAGRAMI

Önce şu meşhur PID kontrol blok diyagramını buraya koyalım.

Buraya bakınca korkunç bir matematik gerekecekmiş gibi geliyor ama öyle değil. Bu diyagram şunu diyor:

Motorun (Proses oluyor bu) devir hızını ölç. Bu hızı hall effect sensörden her turda bir gelen sinyalleri algılayan TIM3 ile motor devir süresi olarak ölçüyoruz.

Bu hız bizim istediğimizden farklı mı? Fark ne kadar ? Bu fark Error yani hata olarak adlandırılıyor. İstediğimiz hızı STM32 ye potansiyometremiz ile ADC üzerinden bildiriyoruz.

Şimdi ölçtüğümüz hata değerine bağlı olarak motor akımını PWM değerini arttırıp azaltarak ayarlayacağız. BU ayarlamayı yaparken 3 faktöre bakmamızı söylüyor yukarıdaki diyagram.

Birincisi : Hata ne kadar büyükse o kadar büyük bir düzeltme yap. Yani PWM e hata ile orantılı bir ekleme ya da çıkartma yapacağız. Çok büyük ekleme çıkartmalar yaparsak bu sefer de hedefi aşabiliriz, küçük ekleme çıkartmalar da hedefe ulaşmamızı geciktirir. O halde Kp dediğimiz bir çarpanla düzeltme miktarını ayarlayalım. Bu PID nin Proportional, yani oransal düzeltme faktörü oluyor. Şu anki durumumuzu değerlendiriyoruz.

İkincisi : Hata ne süredir devam ediyor ? Buna göre de bir düzeltme gerekebilir. Hata küçük olduğu için çok küçük düzeltemeler yapıyor, hatta hiç düzeltmeye gerek görmüyor olabiliriz. Örneğin motor sürtünmeleri ya da üzerindeki yükten dolayı sürekli olarak hedef hızın biraz altında çalışıyor olabilir. Hatta duruyor, ama seçtiğimiz düşük çalışma hızından dolayı uyguladığımız küçük PWM değerleri -hata küçük ya- başlangıç sürtünmelerini ve ataletini aşmaya yetmiyor olabilir. O halde, başlangıçtan itibaren biriken hataların toplamına da bakmak lazım. Hata küçük ama uzun süredir devam etmekte ise düzeltme faktörünü büyültelim. İşte bu da PID nin Integral faktörü oluyor. zaman içinde biriken hatayı da bir katsayı ile düzeltme faktörüne eklemek gerekiyor. Bu faktör ile geçmişteki durumu değerlendirmeye katıyoruz.

Üçüncüsü : uygulamakta olduğumuz düzeltmelere motorumuz ne kadar hızlı tepki gösteriyor. Örneğin ; motorun üzerindeki yük hızlanmasını yavaşlatıyor olabilir, o zaman hedef hıza çok uzun sürede ulaşacaktır. Bu durumda motorun hızının artış hızına – yani ivmeye bakarak da bir düzeltme faktörü eklemek gerecektir. Bu faktör de PID nin “D” si, yani türev bileşenini oluşturuyor. Bu faktör ile de geleceğe bakıyoruz.

Bir başka deyişle, düzeltme faktörümüzün içinde şu an (P: Proportional – oransal), geçmiş zaman (I : integral) ve gelecek zaman (D : derivatives – türevsel) üç bileşen var.

Ben bu çalışmayı yaparken adım adım ilerleyeceğim. Önce sadece P-Proportional/oransal faktör ile çalıştıracağım. Ardından buna I-Integral faktörü ekleyeceğim, en sonunda da D-Derivative türevsel faktörü ekleyeceğim. Bu şekilde üç bileşenin sisteme katkısını ayrı ayrı gözlemlemiş olacağız.

PROPORTIONAL – ORANSAL KONTROL İLE ÇALIŞMA

Önce sadece orantısal kontrol ile çalışan bir sistem kurarak çalıştıralım.

Yukarıdaki paragraflarda anlatılan adımları izleyerek projenin genel kurulumunu yapıp bu noktaya kadar gelelim.

LCD yi sürmek için LCD_I2C kütüphanesinin projeye eklenmesi gerekiyor. Ya da hangi ekran kullanılacaksa ona ait sürücülerin eklenmesi gerekir. Bunun için cube IDE – Link vererek projeye kaynak kodu eklemek başlıklı yayınım yol gösterici olacaktır.

Projenin cubeIDE Project Explorer penceresindeki görünümü şöyle olacak. sağ penceredeki main loop çevrimi içindeki kodları şimdilik yok sayın. Bunları ileriki paragraflarda anlatacağım :

SWV üzerinden hata ayıklamak ve değişkenleri izlemek için STM32 cubeIDE – SWV ile hata ayıklamak başlıklı yayınımda anlattığım ayarlar yapılmalı. SWV yapılandırma ayarlarında izlenecek değişkenler olarak pwm ve ICValue (Input Capture Value) i seçelim. Bu şekilde her okunan motor devir periyoduna karşılık hesaplanan yeni PWM değerini SWV ITM Data konsolu üzerinden canlı olarak izleyebileceğiz. Gerçi bu bilgileri LCD üzerinde de gösteriyoruz ama, LCD nin bulunmadığı bir durum için alternatifimiz hazır olsun.

ANALOG SAYISAL ÇEVİRİCİYİ (ADC) OKUMAK

Motor dönüş hızını bir potansiyometre ile seçiyoruz. Bunun için potansiyometrenin orta ucundaki gerilimi okumamız gerekiyor. Bu okumayı da ADC1 ile yapıyoruz. Bunun ayarlarını projenin kurulum aşamasında iken yapmıştık. Şimdi, ADC den okuma yapan fonksiyonu programımıza ekleyelim. Bu programı Cube IDE nin bize ayırdığı User Code 0 ya da User Code 4 bölümlerinden birisine koyabiliriz. Ben ilkini tercih ettim:

Bundan sonra ana çevrim içindeki aşağıdaki iki satırda ADC yi okuyup, okuduğumuz değere karşı düşen motor periyodunu “setPoint” olarak hesaplıyoruz:

adc = ADC_Read();
setPoint = (adc * periodSpan) / 4095 + minPeriyod;


Potansiyometrenin orta ucundaki gerilim 0 ila 3V3 Vcc arasında değişiyor. Bu gerilim ADC1 in Input 1 (Port A1) girişinden okunuyor. ADC1, okuduğu gerilimi 0 ila 4095 arasında (12 bit) kodlanmış bir sayı olarak veriyor. Bu değeri, ikinci satırda minimum motor periyodu ile maksimum motor periyodu arasında bir setPoint parametresine dönüştürüyoruz. Minimum periyod motorun en yüksek, maksimum periyod da en düşük devir hızı oluyor.

TIM3 – ZAMANLAYICI KESME FONKSİYONLARI (IRQ callBack)

Motorun her dönüşünde hall effect sensörümüz bir darbe üretiyor. Ardışıl iki darbe arasındaki süreyi ölçerek motor dönüş periyodunu buluyoruz.

Hall sensör sinyallerini TIM3 zamanlayıcısının “Input Capture” fonksiyonunu kullanarak yakalıyoruz. TIM3 bu modda iken algıladığı her darbe ile bir “Input Capture” kesmesi üretip o anki sayaç değerini kaydediyor. Bu kesme oluştuğunda yapılacak işlemler için programımıza bir “input capture call back” fonksiyonu eklememiz gerekiyor. Bu fonksiyonu main.c içindeki user code 4 bölümüne koydum:

HAL_TIM_IC_CaptureCallback() fonksiyonu, kaydedilmiş olan sayaç değerini ICValue değişkenine koyuyor, bir sonraki periyodu ölçmek üzere sayacı başa alıyor.

Bir overflow kontrolu TIM3 Period period elapsed kesmesinin diktiği bayrağa bakarak, hatalı kaydedilmiş olan sayaç değerilerinin dikkate alınmamasını sağlıyor. Parazitik tetiklemeleri ayıklayan bir kontrol da var. Maalesef jumper tellerle yapılan montajlarda parazitler sorun yaratabiliyor. Montajın dağınıklığı bir yana, motor tellerine verdiğimiz PWM akım darbelerinden endüklenen parazitler var.

Yukarıdaki ekran görünümünde bir ikinci call back fonksiyonu daha var. O da şunun için: Eğer motor duruyor ya da çok yavaş hareket ediyor ise TIM3 sayacı 65535 i aşıp tekrar sıfırdan saymaya devam edecektir. Bu durumda okuduğumuz sayaç değerleri bize motorun devir süresini doğru olarak vermeyecektir. Hele motor durmakta ise, input capture kesmesi hiç oluşmayacak, dolayısı ile ICValue değeri güncellenmeyecektir bile.

Bu durumlarda sayacın 65535 değerini aştığından haberdar olup, ona göre bir şeyler yapmamız gerekiyor. O nedenle TIM3 ün overflow kesmesini de etkinleştiriyoruz. Overflow olduğunda bu IRQ, tim3_overflow_flag bayrağını dikerek diğer fonksiyonlara haber veriyor.

CALLBACK FONKSİYONLARININ PROTOTİPLERİNE ULAŞMAK

Gerek TIM kesintileri, gerekse başka kesintiler olsun, HAL fonksiyonları ile çalışmaya kalkıştığımızda böyle CallBack fonksiyonları hazırlamamız gerekiyor. Bu fonksiyonların HAL kütüphaneleri içinde prototiplerini bulup, buraya kopyalayıp içlerine kendi kodlarımızı yazmak zorundayız. O halde, ilgilendiğimiz kesmeye ilişkin CallBack fonksiyonunu nereden bileceğiz?

Bunu “STM32 CUBE IDE INTERRUPT CALL BACK FONKSİYONLARI” başlıklı yayınımda anlatmıştım.

ANA ÇEVRİME GİRMEDEN ÖNCEKİ İŞLEMLER

Programın ana çevrimi içine girilmeden önce TIM3 ü başlatmak gerekiyor. Bu satırları main() fonksiyonun başına, User Code 2 bölümüne koyuyoruz.

Burada LCD yi başlatıp ilk mesajı yazan komutları da görüyoruz.

Motora başlangıç seviyesinde bir PWM sinyali verip çalıştıralım. PWM oranı ayarını, bunu üretmekte kullandığımız TIM2 zamanlayıcısının Pulse değerini değiştirerek yapıyoruz. Bunun için de __HAL_TIM_SET_COMPARE() fonksiyonunu kullanıyoruz. Ondan sonra TIM2 yi HAL_TIM_PWM_Start() fonksiyonunu çağırarak PWM üretimini başlatıyoruz.

PWM başlangıç değeri, main.c global alanında %10 duty cycle a karşı düşecek şekilde “100” olarak tanımlanmış durumda.

L298 köprüsünü bir yönde açmak üzere motor_1 (PA2) ve motor_2 (PA3) pinlerinden birisini “1” diğerini de “0” yaparak yol da veriyoruz. Bu noktadan itibaren motor bir yönde %10 PWM ile dönmeye başlayacaktır.

Bu ilk aşamada motoru tek yönde çeviriyoruz, yön kontrolu yok. Yön kontrolu daha sonra.

DEĞİŞKEN BİLDİRİMLERİ

Burada, main.c dosyasının başında, User Code PV bölümü içinde tanımlanan global değişken bildirimlerini vereyim. Bunlar sadece Proportional Kontrol için olanlar. “I” ve “D” kontrollarını ekledikçe onlara ait değişkenler de bu bölüme eklenecek.

PID DÜZELTME FAKTÖRÜNÜN HESAPLANMASI – ORANSAL FAKTÖR

Ölçülen hataya bağlı olarak PWM süresinde düzeltme yapacağız. Bu aşamada sadece oransal/proportional düzeltme faktörünü dikkate alıyoruz. Bu faktörü hesaplayan fonksiyonumuz User Code 4 bölümüne koyduğumuz computePID():

Parametre olarak verdiğimiz input, TIM3 Input Capture ile yakalayıp ölçmüş olduğumuz motor devir periyodu, ICValue.

Bu fonksiyona ilerideki bölümlerde Integral ve türevsel/derivative faktör hesaplamalarını da katacağız.

ORANSAL (PROPORTIONAL) KONTROL TEST PROGRAMI

Buraya kadar PID kontrolun sadece “Proportional/oransal” faktörünü dikkate alan bir kod geliştirdik. Ancak, diğer faktörlerin hesaplanmasında ihtiyaç duyulacak tüm ortak fonksiyonları da hazırlamış olduk. Integral ve Derivatives-Türevsel kısımları çok daha kısa olacak.

Şimdi bu haliyle programımızı bir test edelim:

Bu çevrim saniyede bir defa hata değerlendirerek PWM düzeltmesi yapacak şekilde hazırlanmış durumda.

TIM3 input capture kesmeleri ile çağrılan call back fonksiyonu arka planda motorun her turunda bir defa devir periyodunu ölçmekte. Ama biz saniyede bir defa ölçülen değere bakarak düzeltme yapıyoruz. Bu düzeltme işlemi, kesme fonksiyonunun içine konarak her motor devrinde bir defa olacak şekilde de yapılabilir – hatta öyle yapılmalı. Ama bu sunum açısından şimdilik böyle çalışalım.

Bu kod içinde dikkat çekebilecek bir “anormal değişimleri ayıklama” algoritması var. Yukarılarda arada bir parazitik kesmeler gelebileceğini belirtmiştim. Bu ayıklama işlemi için son 5 ölçüm değerini ICbuffer[] dizisi içinde saklayıp ortalamasını alıyorum. Yeni gelen ölçüm sonucu ortalamaya göre %50 den fazla ya da düşük ise bunu değerlendirmeye almıyorum. Motor hızında bu kadar kısa sürede bu kadar değişmesi bir anormal ölçüm sonucuna işaret ediyor olabilir.

Kodun geri kalanı kendi kendini açıklar gibi görünüyor. Şimdi programı çalıştırıp sonucu görelim. Motorun istediğimiz dönüş hızının değişimini ve hedefe ne kadar sürede ulaşıldığını görmek için SWV ITM Data konsolundan yararlanacağım. Örneğimizdeki motoru 710 Devir(dakika hızla çalıştırmak için gereken dönüş periyodu 3000 sayaç adımı. Bunu setPoint olarak LCD ekrana bakarak potansiyometre ile ayarlıyoruz. Programı SWV ITM konsolu kayıtta iken koşturduğumuzda aşağıdaki gibi bir çıktı elde ediyoruz.

Ben bu çıktıları kopyalayıp bir Excel (aslında Mac OS kullandığım için Numbers) tabloya aktararak grafiğe dökmeyi tercih ettim.

Bu çalışmanın bir parçası da, uygulamamız için en uygun Kp değerinin belirlenmesi. Bunun için programı Kp katsayısının farklı değerleri ile çalıştırarak aldığım çıktıları aynı grafik üzerinde görselleştirdim. Sonuç aşağıdaki gibi:

Bu sonuçlara göre Kp katsayısı 0.01 olması halinde overshot olmuyor ama istenen hıza ulaşmak uzun sürüyor. Kp nin 0.1 olması durumunda da biraz fazla overShoot oluyor. Bu dört Kp katsayısı arasında 0.02 en optimal değer olarak görünüyor. hem over shoot yok, hem de 3 saniye içinde istenen devir hızı yakalanıyor.

Burada bir not : Kp, Ki ve Kd faktörleri aslında birer çarpan. Yukarılarda 0.01 çarpanından söz ettiğimde kodun içinde bunu 100 olarak ayarlamış olduğum görülebilir. Çünkü kayan nokta aritmetiğinden ve float tipi değişkenlerden kaçınmak için, bu faktörleri "çarpan" değil, "bölen" olarak kullanmayı tercih ettim. Örneğin 0.05 katsayısı istediğimde bunu 20 olarak tanımlıyorum.

Birkaç farklı setPoint ayarı ile alınan ekran ve Hall sensörden gelen sinyalin osiloskop görüntülerini de vereyim:

Böylece PID nin sadece “P” si ile nasıl kapalı çevrim çalışılabildiğini görmüş olduk. Bu kadarı yeterli imiş gibi görünse de, küçük fakat uzun süren hataların giderilmesi için “I” faktörüne de ihtiyaç oluyor. Şimdi sıra onda.

INTEGRAL FAKTÖRÜNÜN SİSTEME KATILMASI

Integral faktörü hesaplarken, ölçülen hatanın süregeldiği dönem içindeki birikimini hesaplıyoruz. Böylece küçüklüğü nedeniyle oransal faktörü etkilemeyen hataların uzun sürelerdeki birikimli değerlerini de hesaba katmış olacağız.

Bu çalışmada entegrasyon periyodunu ana programdaki örnekleme süresi ile sınırladım. Daha sonra daha da rafine bir yaklaşıma gidebilirim.

Bu faktörün düzeltme katsayısına katılması için computePID() fonksiyonumuza aşağıdaki gibi eklemeler yapıyoruz:

Burada ıntegral faktörün devreye sokulmasını biraz geciktirerek, hatanın IcStart adlı parametre ile belirlenen bir seviyenin (%20) altına düşrüimesinden sonra yaptığımız görülüyor. Yani hata büyük iken sadece “P” faktörü devrede, aksi halde iki faktör birlikte iken büyük over shoot lara yol açıyor.

Burada kullanılan yeni değişkenleri de main.c global alanında aşağıdaki gibi bildiriyoruz:

Integral faktör için değişiklik ve ilaveler bu kadar. Şimdi çalıştırıp sonucu görelim. Bu test çalıştırmasını ilk aşamada en iyi sonucu veren Kp = 0.02 değeri ve Ki = 0.01 değerleri için yapalım.

SWV ile aldığımız sonuçları sadece “P” oransal faktör ve buna “I” ıntegral faktörünün de katıldığı iki durumu yan yana aynı tabloda vereyim, böylece Integral faktörün sonuca etkisini daha kolay görebiliriz. Tablodaki ilk ikişer “5000” değeri gerçek değil, aslında 10.000 in üzerinde değerler. Grafik eksenlerinin uygun boyutlarda olması için gerçek değerleri silip 5000 yaptım.

Burada sadece oransal kontrolun olduğu durumda 2.8 saniyede stabil duruma gelinmesine karşın, buna Integral kontrolu da eklediğimizde 1.6 saniyede stabil hıza ulaşıldığı görülüyor.

Sadece oransal kontrol var iken 3000 olan setPoint’e karşılık motor periyodunun 3070 civarında kaldığını, bu “70” ilk farkı bir türlü gideremediğini de görüyoruz.

Halbuki Integral faktör de devreye sokulunca motor periyodu setPoint’e ulaşıp bunun civarında küçük salınımlar yapıyor.

Bir de bu iki karakteristiği grafik üzerinde karşılaştıralım:

Burada da Integral faktörü devreye almadığımda, hedef civarındaki küçük hataların bir türlü düzeltilemediğini görebiliyoruz. Mavi çizgi : sadece oransal faktör devrede, Yeşil çizgi : Oransal ve Integral faktörler birlikte devrede.

Evet, böylece Integral faktörü de devreye sokarak PID ye varış hedefimizde “PI” aşamasını tamamlamış olduk.

Bütün bu sonuçlar kod içindeki Kp, Ki parametrelerinin seçimine, Integral alma periyoduna çok bağlı. Burada optimizasyon ve ince ayarlama için yapılacak çok şey var. Bunlar benim ilk denediğim katsayılar.

Şimdi motorun boşta çalışması halini ve milini el ile tutup yavaşlatmaya çalıştığımdaki regülasyonu gözlemleyelim:

Burada motor yüksüz çalışarak 703 Devir/Dk hızla dönüyor. Bunun için %34.5 PWM ile sürülüyor.
















Burada da motor milini el ile tutarak yavaşlatmaya çalışıyorum, sistem de hızı 700 de sabit tutmak için ayarları değiştiriyor, PWM %91.4 e kadar yükselmiş, bu boyuttaki bir motor için fena bir sonuç değil. Artık gerisi motor gücüne de bağlı.











“PI” kontrolumuz da çalıştı. Geriye “D” türevsel kontrol kısmı kaldı. Ama o kısmını ileriye, bir başka yayına bırakıyorum. Şimdi aklıma gelen daha heyacanlı projeler var.

Bu yayının sonu – Selçuk Özbayraktar Ağustos 2020

Leave a Reply

Your email address will not be published. Required fields are marked *