[Smart car] Detailed explanation of fuzzy PID control principle and code implementation

Fuzzy PID control


This paper mainly consists of three parts: the principle of fuzzy PID controller, the implementation and testing of fuzzy PID controller in C++.

1. Fuzzy PID principle

The fuzzy PID control process is shown in the figure below. The error e between the target value Xtarget and the output value Xout and the change rate de/dt of e are used as the input of the fuzzy controller. The fuzzy controller first performs fuzzy processing on the input, and then performs fuzzy reasoning , and finally defuzzify the result of fuzzy reasoning to output the three parameters kp, ki, kd of the PID controller , so as to achieve the effect of adaptive tuning of the PID controller parameters.
insert image description here

According to the above description, the fuzzy controller is mainly composed of three parts: defuzzification, fuzzy reasoning and defuzzification . The three parts will be explained in detail below.

1.1 Fuzzification

To realize fuzzification, it is first necessary to initialize the fuzzification, which includes the determination of the domain of discourse and the determination of the membership function.

1.1.1 Subject area

The domain of discourse can be said to be an artificially determined range. Since the input e, de/dt, and the output ranges of kp, ki, kd are different, it is better to map the input to the domain of discourse for unified processing. After determining the scope of the domain of discourse, it is necessary to carry out fuzzy classification on the domain of discourse, which is to divide the domain of discourse. Assuming that the domain of discourse is [-3, 3], divide the domain of discourse into 5 equal parts, namely [-3, -2], [-2, -1], [-1, 0], [0, 1], [1, 2], [2, 3]. Then divide each endpoint into grades, in order: -3—>NB (negative large), -2—>NM (negative middle), -1——>NS (negative small), 0—>ZO( zero), 1---->PS(positive small), 2---->PM(positive middle), 3---->PB(positive big). Assuming that the range of the input e is [-10, 10], the value of e at this moment is 8, and the value after mapping is 2.4, and 2.4 is in the interval [2, 3], then the point is between the middle and the positive. The schematic diagram is as follows:

1.1.2 Determination of membership function.

Common membership functions include triangular membership functions, trapezoidal membership functions, and parabolic membership functions. Taking the simplest triangular membership function as an example, the shape is shown in the figure below:

insert image description here

As can be seen from the figure above, the value range of the membership function is [0, 1]. If the value of the input e after mapping is 2.4, then the values ​​corresponding to the red line and the green line in the figure below are 0.6 and 0.4 respectively. These two are the degree of membership, indicating that the probability that the input belongs to PM is 0.6, and the probability of belonging to PB is 0.4. So the degree of membership is also a probability.

insert image description here

From the explanation of the domain of discourse and the membership function above, the process of fuzzification can be summarized: interval mapping, and the membership degree is calculated according to the membership function. The flow chart is as follows:

insert image description here

1.2 Fuzzy reasoning

Fuzzy reasoning, that is, look up the table according to the membership degree of e and de/dt to get the size of the output, that is, NB, NS, etc. So the core work of fuzzy reasoning is to build reasoning tables. The inference table commonly used by fuzzy PID is shown in the figure below:

insert image description here

insert image description here
insert image description here

The following uses an example to illustrate how to use the rule table.

Suppose the input e at this moment is 8, de/dt is -12, and the range of e is [-10,10], and the range of de/dt is [-20,20]. Then through fuzzification, the membership degrees of e are 0.6 (PM) and 0.4 (PB), and the membership degrees of de/dt are 0.8 (NM) and 0.2 (NS). Then, the membership degrees of e and de/dt are two Combining the two, and performing a table lookup, the relationship in the following table is obtained:

insert image description here

Next, calculate the degree of membership of each output Kp, Ki, Kd.

Take Kp as an example:

insert image description here

Similarly, Ki and Kd also calculate the degree of membership.

Finally, the membership degree of each output is integrated, such as Kp. According to the above calculation, the membership degree of Kp is 0.8 (ZO), 0.12 (NS), and 0.08 (NM).

1.3 Deblurring

Defuzzification is to calculate the value of the output in the domain of discourse according to the membership degree of each output obtained by fuzzy inference, and then obtain the output according to the interval mapping relationship.

1.3.1 Calculate the value of the output in the domain of discourse

The above example is used to illustrate the calculation process. It can be seen from the above that the membership degree of Kp is 0.8 (ZO), 0.12 (NS), and 0.08 (NM). In the explanation of the universe, the value of ZO has been set as 0, the value of NS is set as -1, and the value of NM The value is set to -2. Then the expectation of Kp is:

insert image description here

Taking expectation as the value of Kp in the domain of discourse, after determining the range of Kp, the output value of Kp can be obtained according to the interval mapping formula.

The above is the flow of the fuzzy controller.

It is worth noting that the output Kp, Ki, Kd are increments. During initialization, the range (interval) of input and output should be determined for interval mapping.

2.1 C++ implementation of fuzzy PID controller

This version of the membership function is a fixed triangle membership function , and the domain of discourse is fixed at [-3, 3] .

FuzzyPID.h

#ifndef FuzzyPID_H
#define FuzzyPID_H
class FuzzyPID
{
    
    
public:
    FuzzyPID();
    ~FuzzyPID();
    void Get_grad_membership(float erro, float erro_c);
    float Quantization(float maximum, float minimum, float x);
    float Inverse_quantization(float maximum, float minimum, float qvalues);
    void GetSumGrad();
    void GetOUT();
    float FuzzyPIDcontroller(float e_max, float e_min, float ec_max, float ec_min, float kp_max, float kp_min, float erro, float erro_c, float ki_max, float ki_min,float kd_max, float kd_min,float erro_pre, float errp_ppre);
    const int  num_area = 8; //划分区域个数
    //float e_max;  //误差做大值
    //float e_min;  //误差最小值
    //float ec_max;  //误差变化最大值
    //float ec_min;  //误差变化最小值
    //float kp_max, kp_min;
    float e_membership_values[7] = {
    
    -3,-2,-1,0,1,2,3}; //输入e的隶属值
    float ec_membership_values[7] = {
    
     -3,-2,-1,0,1,2,3 };//输入de/dt的隶属值
    float kp_menbership_values[7] = {
    
     -3,-2,-1,0,1,2,3 };//输出增量kp的隶属值
    float ki_menbership_values[7] = {
    
     -3,-2,-1,0,1,2,3 }; //输出增量ki的隶属值
    float kd_menbership_values[7] = {
    
     -3,-2,-1,0,1,2,3 };  //输出增量kd的隶属值
    float fuzzyoutput_menbership_values[7] = {
    
     -3,-2,-1,0,1,2,3 };

    //int menbership_values[7] = {-3,-};
    float kp;                       //PID参数kp
    float ki;                       //PID参数ki
    float kd;                       //PID参数kd
    float qdetail_kp;               //增量kp对应论域中的值
    float qdetail_ki;               //增量ki对应论域中的值
    float qdetail_kd;               //增量kd对应论域中的值
    float qfuzzy_output;  
    float detail_kp;                //输出增量kp
    float detail_ki;                //输出增量ki
    float detail_kd;                //输出增量kd
    float fuzzy_output;
    float qerro;                    //输入e对应论域中的值
    float qerro_c;                  //输入de/dt对应论域中的值
    float errosum;                  
    float e_gradmembership[2];      //输入e的隶属度
    float ec_gradmembership[2];     //输入de/dt的隶属度
    int e_grad_index[2];            //输入e隶属度在规则表的索引
    int ec_grad_index[2];           //输入de/dt隶属度在规则表的索引
    float gradSums[7] = {
    
    0,0,0,0,0,0,0};
    float KpgradSums[7] = {
    
     0,0,0,0,0,0,0 };   //输出增量kp总的隶属度
    float KigradSums[7] = {
    
     0,0,0,0,0,0,0 };   //输出增量ki总的隶属度
    float KdgradSums[7] = {
    
     0,0,0,0,0,0,0 };   //输出增量kd总的隶属度
    int NB = -3, NM = -2, NS = -1, ZO = 0, PS = 1, PM = 2, PB = 3; //论域隶属值

    int  Kp_rule_list[7][7] = {
    
     {
    
    PB,PB,PM,PM,PS,ZO,ZO},     //kp规则表
                                {
    
    PB,PB,PM,PS,PS,ZO,NS},
                                {
    
    PM,PM,PM,PS,ZO,NS,NS},
                                {
    
    PM,PM,PS,ZO,NS,NM,NM},
                                {
    
    PS,PS,ZO,NS,NS,NM,NM},
                                {
    
    PS,ZO,NS,NM,NM,NM,NB},
                                {
    
    ZO,ZO,NM,NM,NM,NB,NB} };

    int  Ki_rule_list[7][7] = {
    
     {
    
    NB,NB,NM,NM,NS,ZO,ZO},     //ki规则表
                                {
    
    NB,NB,NM,NS,NS,ZO,ZO},
                                {
    
    NB,NM,NS,NS,ZO,PS,PS},
                                {
    
    NM,NM,NS,ZO,PS,PM,PM},
                                {
    
    NM,NS,ZO,PS,PS,PM,PB},
                                {
    
    ZO,ZO,PS,PS,PM,PB,PB},
                                {
    
    ZO,ZO,PS,PM,PM,PB,PB} };

    int  Kd_rule_list[7][7] = {
    
     {
    
    PS,NS,NB,NB,NB,NM,PS},    //kd规则表
                                {
    
    PS,NS,NB,NM,NM,NS,ZO},
                                {
    
    ZO,NS,NM,NM,NS,NS,ZO},
                                {
    
    ZO,NS,NS,NS,NS,NS,ZO},
                                {
    
    ZO,ZO,ZO,ZO,ZO,ZO,ZO},
                                {
    
    PB,NS,PS,PS,PS,PS,PB},
                                {
    
    PB,PM,PM,PM,PS,PS,PB} };

    int  Fuzzy_rule_list[7][7] = {
    
     {
    
    PB,PB,PB,PB,PM,ZO,ZO},  
                                   {
    
    PB,PB,PB,PM,PM,ZO,ZO},
                                   {
    
    PB,PM,PM,PS,ZO,NS,NM},
                                   {
    
    PM,PM,PS,ZO,NS,NM,NM},
                                   {
    
    PS,PS,ZO,NM,NM,NM,NB},
                                   {
    
    ZO,ZO,ZO,NM,NB,NB,NB},
                                   {
    
    ZO,NS,NB,NB,NB,NB,NB}};


//private:

};
#endif

FuzzyPID.cpp

#include "FuzzyPID.h"
FuzzyPID::FuzzyPID()  //构造函数
{
    
    
    kp = 0;
    ki = 0;
    kd = 0;
    fuzzy_output = 0;
    qdetail_kp = 0;
    qdetail_ki = 0;
    qdetail_kd = 0;
    qfuzzy_output = 0;
    errosum = 0;
}

FuzzyPID::~FuzzyPID()//析构函数
{
    
    
}

//输入e与de/dt隶属度计算函数///
void FuzzyPID::Get_grad_membership(float erro,float erro_c)  
{
    
    
    if (erro > e_membership_values[0] && erro < e_membership_values[6])
    {
    
    
        for (int i = 0; i < num_area - 2; i++)
        {
    
    
            if (erro >= e_membership_values[i] && erro <= e_membership_values[i + 1])
            {
    
    
                e_gradmembership[0] = -(erro - e_membership_values[i + 1]) / (e_membership_values[i + 1] - e_membership_values[i]);
                e_gradmembership[1] = 1+(erro - e_membership_values[i + 1]) / (e_membership_values[i + 1] - e_membership_values[i]);
                e_grad_index[0] = i;
                e_grad_index[1] = i + 1;
                break;
            }
        }
    }
    else
    {
    
    
        if (erro <= e_membership_values[0])
        {
    
    
            e_gradmembership[0] = 1;
            e_gradmembership[1] = 0;
            e_grad_index[0] = 0;
            e_grad_index[1] = -1;
        }
        else if (erro >= e_membership_values[6])
        {
    
    
            e_gradmembership[0] = 1;
            e_gradmembership[1] = 0;
            e_grad_index[0] = 6;
            e_grad_index[1] = -1;
        }
    }

    if (erro_c > ec_membership_values[0] && erro_c < ec_membership_values[6])
    {
    
    
        for (int i = 0; i < num_area - 2; i++)
        {
    
    
            if (erro_c >= ec_membership_values[i] && erro_c <= ec_membership_values[i + 1])
            {
    
    
                ec_gradmembership[0] = -(erro_c - ec_membership_values[i + 1]) / (ec_membership_values[i + 1] - ec_membership_values[i]);
                ec_gradmembership[1] = 1 + (erro_c - ec_membership_values[i + 1]) / (ec_membership_values[i + 1] - ec_membership_values[i]);
                ec_grad_index[0] = i;
                ec_grad_index[1] = i + 1;
                break;
            }
        }
    }
    else
    {
    
    
        if (erro_c <= ec_membership_values[0])
        {
    
    
            ec_gradmembership[0] = 1;
            ec_gradmembership[1] = 0;
            ec_grad_index[0] = 0;
            ec_grad_index[1] = -1;
        }
        else if (erro_c >= ec_membership_values[6])
        {
    
    
            ec_gradmembership[0] = 1;
            ec_gradmembership[1] = 0;
            ec_grad_index[0] = 6;
            ec_grad_index[1] = -1;
        }
    }

}

/获取输出增量kp,ki,kd的总隶属度/
void FuzzyPID::GetSumGrad()
{
    
    
    for (int i = 0; i <= num_area - 1; i++)
    {
    
    
        KpgradSums[i] = 0;
        KigradSums[i] = 0;
    KdgradSums[i] = 0;

    }
  for (int i=0;i<2;i++)
  {
    
    
      if (e_grad_index[i] == -1)
      {
    
    
       continue;
      }
      for (int j = 0; j < 2; j++)
      {
    
    
          if (ec_grad_index[j] != -1)
          {
    
    
              int indexKp = Kp_rule_list[e_grad_index[i]][ec_grad_index[j]] + 3;
              int indexKi = Ki_rule_list[e_grad_index[i]][ec_grad_index[j]] + 3;
              int indexKd = Kd_rule_list[e_grad_index[i]][ec_grad_index[j]] + 3;
              //gradSums[index] = gradSums[index] + (e_gradmembership[i] * ec_gradmembership[j])* Kp_rule_list[e_grad_index[i]][ec_grad_index[j]];
              KpgradSums[indexKp]= KpgradSums[indexKp] + (e_gradmembership[i] * ec_gradmembership[j]);
              KigradSums[indexKi] = KigradSums[indexKi] + (e_gradmembership[i] * ec_gradmembership[j]);
              KdgradSums[indexKd] = KdgradSums[indexKd] + (e_gradmembership[i] * ec_gradmembership[j]);
          }
          else
          {
    
    
            continue;
          }

      }
  }

}

计算输出增量kp,kd,ki对应论域值//
void FuzzyPID::GetOUT()
{
    
    
    for (int i = 0; i < num_area - 1; i++)
    {
    
    
        qdetail_kp += kp_menbership_values[i] * KpgradSums[i];
        qdetail_ki += ki_menbership_values[i] * KigradSums[i];
        qdetail_kd+= kd_menbership_values[i] * KdgradSums[i];
    }
}

//模糊PID控制实现函数/
float FuzzyPID::FuzzyPIDcontroller(float e_max, float e_min, float ec_max, float ec_min, float kp_max, float kp_min, float erro, float erro_c,float ki_max,float ki_min,float kd_max,float kd_min,float erro_pre,float errp_ppre)
{
    
    
    errosum += erro;
    //Arear_dipart(e_max, e_min, ec_max, ec_min, kp_max, kp_min,ki_max,ki_min,kd_max,kd_min);
    qerro = Quantization(e_max, e_min, erro);
    qerro_c = Quantization(ec_max, ec_min, erro_c);
    Get_grad_membership(qerro, qerro_c);
    GetSumGrad();
    GetOUT();
    detail_kp = Inverse_quantization(kp_max, kp_min, qdetail_kp);
    detail_ki = Inverse_quantization(ki_max, ki_min, qdetail_ki);
    detail_kd = Inverse_quantization(kd_max, kd_min, qdetail_kd);
    qdetail_kd = 0;
    qdetail_ki = 0;
    qdetail_kp = 0;
    /*if (qdetail_kp >= kp_max)
        qdetail_kp = kp_max;
    else if (qdetail_kp <= kp_min)
        qdetail_kp = kp_min;
    if (qdetail_ki >= ki_max)
        qdetail_ki = ki_max;
    else if (qdetail_ki <= ki_min)
        qdetail_ki = ki_min;
    if (qdetail_kd >= kd_max)
        qdetail_kd = kd_max;
    else if (qdetail_kd <= kd_min)
        qdetail_kd = kd_min;*/
    kp = kp + detail_kp;
    ki = ki + detail_ki;
    kd = kd + detail_kd;
    if (kp < 0)
        kp = 0;
    if (ki < 0)
        ki = 0;
    if (kd < 0)
        kd = 0;
    detail_kp = 0;
  detail_ki=0;
  detail_kd=0;
  float output = kp*(erro - erro_pre) + ki * erro + kd * (erro - 2 * erro_pre + errp_ppre);
    return output;
}

///区间映射函数///
float FuzzyPID::Quantization(float maximum,float minimum,float x)
{
    
    
    float qvalues= 6.0 *(x-minimum)/(maximum - minimum)-3;
    //float qvalues=6.0*()
    return qvalues;
   
    //qvalues[1] = 3.0 * ecerro / (maximum - minimum);
}

//反区间映射函数
float FuzzyPID::Inverse_quantization(float maximum, float minimum, float qvalues)
{
    
    
    float x = (maximum - minimum) *(qvalues + 3)/6 + minimum;
    return x;
}

Test the fuzzy PID controller:

#include <iostream>
#include "FuzzyPID.h"

int main()
{
    
    
    FuzzyPID myfuzzypid;
    float Target = 600;
    float actual = 0;
    float e_max =1000;
    float e_min = -1000;
    float ec_max = 800;
    float ec_min = -800;
    float kp_max =100;
    float kp_min = -100;
    float ki_max = 0.1;
    float ki_min = -0.1;
    float kd_max = 0.01;
    float kd_min = -0.01;
    float erro;
    float erro_c;
    float erro_pre = 0;
    float erro_ppre = 0;
    erro =Target - actual;
    erro_c = erro - erro_pre;
    for (int i = 0; i < 100; i++)
    {
    
    
        float u;
        u = myfuzzypid.FuzzyPIDcontroller(e_max, e_min, ec_max, ec_min, kp_max, kp_min, erro, \
                                          erro_c,ki_max,ki_min,kd_max,kd_min,erro_pre,erro_ppre);
        actual +=u;
        erro_ppre = erro_pre;
        erro_pre = erro;
        erro = Target - actual;
        erro_c= erro - erro_pre;
        std::cout << "i:" << i << "\t" << "Target:" << Target << "\t" << "Actual:" << actual  << std::endl;
    }
}

Running result :

img

This article mainly reproduces the fuzzy control principle part and code routines, and publishes it to CSDN for the convenience of management and collection.

Original address: https://www.codenong.com/cs105632129/

Guess you like

Origin blog.csdn.net/weixin_45636061/article/details/124996230