基於C++的PID控制器

PID控制器是一種廣泛用於各種工業控制場合的控制器,它結構簡單,可以根據工程經驗整定參數Kp,Ki,Kd. 雖然現在控制專家提出了很多智能的控制算法,比如神經網絡,模糊控制等,但是PID仍然被廣泛使用。
常見的PID控制器有位置PID控制器,增量PID控制器。兩個PID控制器各有自己的優點,需要根據具體的場合來使用。
pid控制器的結構框圖爲:
這裏寫圖片描述
(1)位置PID
連續形式公式如下:
連續形式的位置型PID公式
也可以寫成如下形式:
一般形式PID公式
爲了方便軟件編程實現,一般轉換成離散形式,即用連加代替積分,有差分代替微分,
離散變換
離散型PID公式:
離散形式的PID公式
使用C++實現一個位置PID控制器,首先需要創建一個PID_position的類,其定義如下:

//位置式PID
class PID_position
{
private:
    float kp;//比例係數
    float ki;//積分系數
    float kd;//微分系數
    float target;//目標值
    float actual;//實際值
    float e;//誤差
    float e_pre;//上一次誤差
    float integral;//積分項
public:
    PID_position();
    ~PID_position(){};
    PID_position(float p,float i,float d);
    float pid_control(float tar,float act);//執行PID控制
    void pid_show();//顯示PID控制器的內部參數
};

接下來,對類中聲明的方法進行定義;

//位置PID
PID_position::PID_position():kp(0),ki(0),kd(0),target(0),actual(0),integral(0)
{
    e=target-actual;
    e_pre=e;
}
PID_position::PID_position(float p,float i,float d):kp(p),ki(i),kd(d),target(0),actual(0),integral(0)
{
   e=target-actual;
   e_pre=e;
}
float PID_position::pid_control(float tar,float act)
{
    float u;
    target=tar;
    actual=act;
    e=target-actual;
    integral+=e;
    u=kp*e+ki*integral+kd*(e-e_pre);
    e_pre=e;
    return u;
}
void PID_position::pid_show()
{
    using std::cout;
    using std::endl;
    cout<<"The infomation of this position PID controller is as following:"<<endl;
    cout<<"       Kp="<<kp<<endl;
    cout<<"       Ki="<<ki<<endl;
    cout<<"       Kd="<<kd<<endl;
    cout<<" integral="<<integral<<endl;
    cout<<"   target="<<target<<endl;
    cout<<"   actual="<<actual<<endl;
    cout<<"        e="<<e<<endl;
    cout<<"    e_pre="<<e_pre<<endl;
}

以上代碼就可以實現一個位置PID控制器,只需要實例化PID_position類對象,調用相關的方法就可以實現P位置ID控制。
位置PID結構簡單,但是由於有積分項,容易產生積分飽和的現象,而且它每次輸出的都是全量,此全量均和過去的輸出有關,易產生累計誤差。需要對其進行改進,由此產生的改進型PID控制器——增量型PID控制器。其區別在於,控制器輸出的不是全量,而只是增量,每次輸出均與過去的所有狀態無關,而且它沒有積分項,運算量小,容易實現手動到自動的無衝擊切換。
增量型PID控制器的公式如下:
增量PID
可以表示爲更一般的形式:
PID ABC
由此可以編寫增量式PID的C++代碼:
類聲明爲:

//增量式PID
class PID_incremental
{
private:
    float kp;
    float ki;
    float kd;
    float target;
    float actual;
    float e;
    float e_pre_1;
    float e_pre_2;
    float A;
    float B;
    float C;
public:
    PID_incremental();
    PID_incremental(float p,float i,float d);
    float pid_control(float tar,float act);
    void pid_show();
};

方法定義:

//增量PID
PID_incremental::PID_incremental():kp(0),ki(0),kd(0),e_pre_1(0),e_pre_2(0),target(0),actual(0)
{
   A=kp+ki+kd;
   B=-2*kd-kp;
   C=kd;
   e=target-actual;
}
PID_incremental::PID_incremental(float p,float i,float d):kp(p),ki(i),kd(d),e_pre_1(0),e_pre_2(0),target(0),actual(0)
{
   A=kp+ki+kd;
   B=-2*kd-kp;
   C=kd;
   e=target-actual;
}
float PID_incremental::pid_control(float tar,float act)
{
   float u_increment;
   target=tar;
   actual=act;
   e=target-actual;
   u_increment=A*e+B*e_pre_1+C*e_pre_2;
   e_pre_2=e_pre_1;
   e_pre_1=e;
   return u_increment;
}

void PID_incremental::pid_show()
{
    using std::cout;
    using std::endl;
    cout<<"The infomation of this incremental PID controller is as following:"<<endl;
    cout<<"     Kp="<<kp<<endl;
    cout<<"     Ki="<<ki<<endl;
    cout<<"     Kd="<<kd<<endl;
    cout<<" target="<<target<<endl;
    cout<<" actual="<<actual<<endl;
    cout<<"      e="<<e<<endl;
    cout<<"e_pre_1="<<e_pre_1<<endl;
    cout<<"e_pre_2="<<e_pre_2<<endl;
}

下面可以對兩個控制器進行簡單的測試,這裏需要對PID的參數進行整定,筆者也是通過多次試驗,最終獲得了比較合理的kp,ki,kd。
測試代碼如下:

#include<iostream>
#include"pid_controller.h"
using namespace std;
int main()
{
    //測試增量PID
    PID_incremental pid1(0.35,0.65,0.005);
    float target=1000.0;
    float actual=0;
    float pid_increment=0.0;
    int N=50;
    pid1.pid_show();
    cout<<"target="<<target<<endl;
    for(;N>0;N--)
    {
        pid_increment=pid1.pid_control(target,actual);
        actual+=pid_increment;
        cout<<"N="<<50-N<<"   actual="<<actual<<endl;
    }
    pid1.pid_show();

    //測試位置PID
    PID_position pid2(0.59,0.35,0.002);
    pid2.pid_show();
    cout<<"target="<<target<<endl;
    N=100;
    for(;N>0;N--)
    {
        actual=pid2.pid_control(target,actual);
        cout<<"N="<<100-N<<"   actual="<<actual<<endl;
    }
    pid2.pid_show();

    system("pause");
    return 0;
}

測試結果顯示增量PID,28次到達目標值1000,位置PID,61次達到1000.
結果1
結果2

發佈了59 篇原創文章 · 獲贊 220 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章