İlerde yapacağım basınç regülasyonu için PID kullanmak istiyorum.
Deneme amaçlı C# ile bir proje oluşturdum fakat istediğim gibi sonuç alamıyorum.
Test yazılımına 2 adet track bar ekledim.
Birisi ile "Şuan ki" değeri değiştiriyorum
Diğeri ile "Set Point" değerini değiştiriyorum.
Listboxda belirlediğim şekilde bir sonuç alamadım.
Bir algoritma hatası mı yapıyorum ?
PID.cs
using System;
namespace PID_Control
{
public class PID
{
private double _Kp;
private double _Ki;
private double _Kd;
private double _dt;
private double _setPoint;
private double _pre_error;
private double _integral;
private double _max;
private double _min;
public PID(double Kp, double Ki, double Kd, double setValue, double dt, double max, double min)
{
_Kp = Kp;
_Ki = Ki;
_Kd = Kd;
_dt = dt;
_setPoint = setValue;
_pre_error = 0;
_integral = 0;
_max = max;
_min = min;
}
public void SetValue(double targetValue)
{
_setPoint = targetValue;
}
public double Update(double pv)
{
// Calculate error
double error = _setPoint - pv;
// Proportional term
double Pout = _Kp * error;
// Integral term
_integral += error * _dt;
double Iout = _Ki * _integral;
// Derivative term
double derivative = (error - _pre_error) / _dt;
double Dout = _Kd * derivative;
// Calculate total output
double output = Pout + Iout + Dout;
// Restrict to max/min
if (output > _max)
output = _max;
else if (output < _min)
output = _min;
// Save error to previous error
_pre_error = error;
return output;
}
}
}
Örnek
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PID_Control
{
public partial class Form1 : Form
{
const double kp = 3;
const double ki = 0.1;
const double kd = 1;
PID PID = new PID(kp, ki, kd, 35, 0.05, 100, 0);
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
double input = (double)trackBar1.Value;
double output = PID.Update(input);
listBox1.Items.Add(output);
int visibleItems = listBox1.ClientSize.Height / listBox1.ItemHeight;
listBox1.TopIndex = Math.Max(listBox1.Items.Count - visibleItems + 1, 0);
}
private void trackBar1_ValueChanged(object sender, EventArgs e)
{
label1.Text = "Current Value = " + trackBar1.Value.ToString();
}
private void trackBar2_ValueChanged(object sender, EventArgs e)
{
double setValue = (double)trackBar1.Value;
PID.SetValue(setValue);
label2.Text = "Set Value = " + trackBar2.Value.ToString();
}
}
}
Çıktılar genelde 0 yada 100 oluyor.
Ben nerede hata yapıyorum ?
Anladığım kadarıyla Kp, Kd ve Ki parametrelerinin sonuca etkisini gözlemleyebilmek için bilgisayarda simulasyon benzeri bir şey yapmak istiyorsun.
Projeye bir timer ekle. Bu timer'in tick süresi senin örnekleme periyodun olacak. Pid fonksiyonunu bu timer'in tick event'i içine yaz.
Şu haliyle programın çıktısının minimum veya maksimuma ulaşması normal, çünkü çıkış ne olursa olsun hata değişmiyor. Bunun sonucunda çıkış minimum ya da maksimum saturasyon noktasına ulaşıyor.
Kp, Kd ve Ki katsayılarını bilemediğiniz sürece dogru sonucu vermeyecektir. Auto tuning veya yukarıda hocamızın bahsettiği gibi simule ederek bulmamız gerekmektedir.
Piyasada bir cok ısı kontrol cihazı pid kontrol olsada tam verimli çalısan cihaz sayısı çok azdır.
Pid oldukça önemli bir konu takipteyim
Merhabalar
@baran123.
Katsayıları tune etmekte sıkıntı yok. Sıkıntı şu : Yazdığın kod sürekli Sistem için geçerli. Senin ayrık kod yazman lazım.
Öncelik olarak :
1- Sistemin örnekleme zamanı koy.
2- Bu örnekleme zamanına bağlı ayrık sistem PID formülünü çıkar (Tustin, Backward, Forward)
3- Bu formülü fark denklemi olarak yaz
Test için;
1- Oluşturulan sisteme bir set input değeri belirle
2- İşlem süresini belirle (örn: 10sn)
3- Matlab simulinkte discrete blok diyagram koy ve toplam süreyi aynı yap
4- Yazdığın C# kodu ile karşılaştır.
Alıntı YapTest yazılımına 2 adet track bar ekledim.
Birisi ile "Şuan ki" değeri değiştiriyorum
Diğeri ile "Set Point" değerini değiştiriyorum.
....
....
Çıktılar genelde 0 yada 100 oluyor.
Ben nerede hata yapıyorum ?
Bu cok normal. PID Controller cikisini kontrol duzenegine vermiyor ve geri beslemeyi sanki sistem, PID ciktisina tepki vermis gibi elle Track bar ile belirliyorsun.
Eger bu sekilde test edeceksen Delta T degerini 5 sn gibi yuksekce degerlere ayarla ki Track bari degistirmeye firsatin olsun.
Fakat PID yi bu sekilde test etmek sana fayda saglamaz.
Onerim, PID cikisina bir sistem modeli ekle ve onu test et. Boylece PID gercek sistem tepkisine maruz kalsin.
Mesela plant olarak a/(s+a) ya da a^2/(s^2+bs+a^2) gibi sistem modeli ekle.
Alıntı yapılan: Zoroaster - 30 Eylül 2018, 02:37:06Bu cok normal. PID Controller cikisini kontrol duzenegine vermiyor ve geri beslemeyi sanki sistem, PID ciktisina tepki vermis gibi elle Track bar ile belirliyorsun.
Eger bu sekilde test edeceksen Delta T degerini 5 sn gibi yuksekce degerlere ayarla ki Track bari degistirmeye firsatin olsun.
Fakat PID yi bu sekilde test etmek sana fayda saglamaz.
Onerim, PID cikisina bir sistem modeli ekle ve onu test et. Boylece PID gercek sistem tepkisine maruz kalsin.
Mesela plant olarak a/(s+a) ya da a^2/(s^2+bs+a^2) gibi sistem modeli ekle.
Aynen Benim dediğim gibi hocam. Burada sistemi test düzeneğinde bende aynısını yaptım.
Öncelik olarak Simullink ile Discrete control ve model oluşturdum.
Aynı sistemi pythonda yazdım. Kendi kontrol fark denklemimi yazdım.
Örnekleme zamanını belirleyip belirli bir süre çalıştırdım. Bu sayede kontrolümü test etmiş oldum.
Yazilan bir PID fonksiyonunu tetst etmek icin;
PID dongusunde her defasinda
Err=Err+0.1 yapilirsa PID nin 100'uncu dongude alacagi deger
islem hatasi yoksa PID[100]=10Kp+500KpDT+KdDT/10 olmasi gerekir.
Ben tımer olarak C# ın kendi içinde bulunan timerı kurup bunun interval değerini 50 ms yaptım.
Yine programda da dt değerini 0.05 olarak girdim.
Z hocamın dediği gibi çıkışı girişe aktarma olayını düzelttim.
1-input al
2-pid fonksiyonuna bu değeri yükle.
3-input = output yap
fakat yine saçma değerler görüyorum.
Katsayılar ise internetten buldum bir excel ile ayarladım.
Katsayılar kötü de olsa sistem uzun bir süre sonra yine set değerine oturmaz mı ?
Kontrol teorisi bilgim şuan için zayıf.
Yani şuan s domenin de bir fonksiyonu sistemin çıkışına ekleyip fonksiyondan dönen değeri mi input olarak almam gerekiyor ?
Alıntı yapılan: baran123 - 30 Eylül 2018, 18:30:05Ben tımer olarak C# ın kendi içinde bulunan timerı kurup bunun interval değerini 50 ms yaptım.
Yine programda da dt değerini 0.05 olarak girdim.
Z hocamın dediği gibi çıkışı girişe aktarma olayını düzelttim.
1-input al
2-pid fonksiyonuna bu değeri yükle.
3-input = output yap
fakat yine saçma değerler görüyorum.
Katsayılar ise internetten buldum bir excel ile ayarladım.
Katsayılar kötü de olsa sistem uzun bir süre sonra yine set değerine oturmaz mı ?
Kontrol teorisi bilgim şuan için zayıf.
Yani şuan s domenin de bir fonksiyonu sistemin çıkışına ekleyip fonksiyondan dönen değeri mi input olarak almam gerekiyor ?
Normalde kod paylaşmam ama konu gereksiz uzadı herkes aynı şeyleri paraphrase edip söylüyor.
Python dilinde yazdığım kod:
import time
class PID:
def __init__(self, P=0.2, I=0.0, D=0.0):
self.Kp = P
self.Ki = I
self.Kd = D
self.sample_time = 0.00
self.current_time = time.time()
self.last_time = self.current_time
self.clear()
def clear(self):
self.SetPoint = 0.0
self.PTerm = 0.0
self.ITerm = 0.0
self.DTerm = 0.0
self.last_error = 0.0
self.int_error = 0.0
self.windup_guard = 20.0
self.output = 0.0
def update(self, feedback_value):
error = self.SetPoint - feedback_value
self.current_time = time.time()
delta_time = self.current_time - self.last_time
delta_error = error - self.last_error
if (delta_time >= self.sample_time):
self.PTerm = self.Kp * error
self.ITerm += error * delta_time
if (self.ITerm < -self.windup_guard):
self.ITerm = -self.windup_guard
elif (self.ITerm > self.windup_guard):
self.ITerm = self.windup_guard
self.DTerm = 0.0
if delta_time > 0:
self.DTerm = delta_error / delta_time
self.last_time = self.current_time
self.last_error = error
self.output = self.PTerm + (self.Ki * self.ITerm) + (self.Kd * self.DTerm)
def setKp(self, proportional_gain):
self.Kp = proportional_gain
def setKi(self, integral_gain):
self.Ki = integral_gain
def setKd(self, derivative_gain):
self.Kd = derivative_gain
def setWindup(self, windup):
self.windup_guard = windup
def setSampleTime(self, sample_time):
self.sample_time = sample_time
Deneme yaparken sistem bile kurmana gerek yok.
set değerini ver kontrol çıkışına bak. feedback = 0 yap.
Hocam tesekkurler
Deneme yapip bilgi verecegim
böyle bir uygulama olmaz. senin sisteminin modeli ne? sıcaklık kontrolü için yapacağın PID farklı, motor için farklı, güç çevrimi için farklı çalışır. düz PID kodunu yazmak birşey ifade etmez, ki zaten C# ile yazdığın kodu gömülü yazılımda kullanamayacaksın.
amaç, PID mantığını anlamak ve değişkenler nasıl etki ediyor onu görmek ise MATLAB PID Tuner var. Aç örnek sistem yükle, katsayılarla oyna gör.
Alıntı yapılan: foseydon - 01 Ekim 2018, 12:07:59böyle bir uygulama olmaz. senin sisteminin modeli ne? sıcaklık kontrolü için yapacağın PID farklı, motor için farklı, güç çevrimi için farklı çalışır. düz PID kodunu yazmak birşey ifade etmez, ki zaten C# ile yazdığın kodu gömülü yazılımda kullanamayacaksın.
amaç, PID mantığını anlamak ve değişkenler nasıl etki ediyor onu görmek ise MATLAB PID Tuner var. Aç örnek sistem yükle, katsayılarla oyna gör.
Dediğin doğru. Örnekler yanlış. Basınç Regülasyonu, Motor Kontrolü ve Sıcaklık 2.Derece Sistemlerdir. Bu yüzden Cascade PID, Paralel yapı, Derivative Kick vs gerek olmaksızın aynı yapıyı kullanabilir. Tek farkı katsayıları tune etmek olacaktır.
Ama dediğim gibi örnekleme vs buradaki asıl önemli konu.
Konuda basınç regülasyonu diyor.
Alıntı yapılan: Firzen - 01 Ekim 2018, 15:33:26Dediğin doğru. Örnekler yanlış. Basınç Regülasyonu, Motor Kontrolü ve Sıcaklık 2.Derece Sistemlerdir. Bu yüzden Cascade PID, Paralel yapı, Derivative Kick vs gerek olmaksızın aynı yapıyı kullanabilir. Tek farkı katsayıları tune etmek olacaktır.
Ama dediğim gibi örnekleme vs buradaki asıl önemli konu.
Konuda basınç regülasyonu diyor.
hocam asıl mevzu şu, arkadaş PID konusunu fazla bilmiyor ama C# biliyor. C# ile birşeyler yapayım, anlamama yardımcı olur gibi bir yaklaşım var. bu gereksiz ve hatalı. salt PID controller kodu yazmak, PID mantığını anlamak için pek işe yaramaz. zaten kod yazmaya bile gerek yok, muhtemelen kullanılan işlemcinin PID kütüphanesi vardır. dspic için texas'ın işlemcileri için var bunlar, üstelik assembly ile yazılımış. sen otursan o kadar verimli yazamazsın zaten.
dediğim gibi mantığı anlamak istiyorsan, ya simülasyon yapacaksın. ya gerçek uygulama yapacaksın. burada sistem ortada yok ki. sadece hesap eden bir kod parçası var. sen kontrol sonucunda bir şey değiştirdiğinde(örneğin PWM duty cycle'ı) aldığın örnek nereye gidecek? bunu ne belirliyor bu kodda? bu olmadan sistem kararlı mı? tepkisi ne oluyor nasıl göreceksin? bu durumda MATLAB gibi bir programda simülasyon yapmak daha faydalı olur. dediğim gibi zaten kodu yazmaya bile gerek yok, yazman gerekirse de 1 saatlik iş C ile.
Sadece PC de simülasyon yapmak için deneyecektim fakat bu şekilde projeme yönelik verimli olmayacak gibi.
PC tarafında MATLAB ile denemeye karar verdim.
Diğer kısım için PCB yi tamamlayınca test edeceğim.
Cevap verenlere teşekkür ederim.
@foseydon PID öğrenmek içinse dediğiniz doğru ben
@baran123 ün en azından azda olsa otomatik kontrol biliyor diye paylaştım. İşin aslı yanlış yönlendirilmeye gitmesin diye yazdım.