PID庫與PID基本優化(一)

本系列旨在以我本身寫的PID lib爲例,講一下PID的幾點基本優化,PID的基本原理網上有不少資料,所以本系列將不會涉及PID的基本實現原理,在這裏特別推薦Matlab tech talk的PID教程:https://ww2.mathworks.cn/videos/series/understanding-pid-control.html。html

因爲筆者大一在讀,尚未學習自動控制原理等課程,所以本系列將不會從自控原理角度展開,相反的,本系列將試圖從「直覺」展開,經過直觀的描述讓你們從直覺上感覺並理解PID的一些包括微分先行、積分分離等基礎的優化。git

因爲筆者水平有限,文中不免存在一些不足和錯誤之處,誠請各位批評指正。github

(一)中主要講解代碼結構與代碼使用,算法有關內容於(二)開始講解算法

1 代碼結構

該PID lib所有代碼詳見:https://github.com/CharlesW1970/PID_Libraryide

1.1 PID結構體與有關枚舉

該lib經過pid結構體保存於pid運算有關的參數數據,經過枚舉表示其餘有關量:函數

//PID結構體
typedef struct _PID_TypeDef
{
    float Target;
    float LastNoneZeroTarget;
    float Kp;
    float Ki;
    float Kd;

    float Measure;
    float Last_Measure;
    float Err;
    float Last_Err;

    float Pout;
    float Iout; //Iout = ITerm_0 + ITerm_1 +....+ ITerm_n
    float Dout;
    float ITerm; //ITerm = Err * Ki

    float Output;
    float Last_Output;

    float MaxOut;
    float IntegralLimit;
    float DeadBand;
    float ScalarA; //變積分公式參數
    float ScalarB; //ITerm = Err*((A-abs(err)+B)/A)  when B<|err|<A+B

    uint8_t Improve; //用於使能優化

    PID_ErrorHandler_t ERRORHandler;

    void (*PID_param_init)(
        struct _PID_TypeDef *pid,
        uint16_t maxOut,
        uint16_t integralLimit,
        float deadband,
        float Kp,
        float ki,
        float kd,
        float A,
        float B,
        uint8_t improve);

    void (*PID_reset)(
        struct _PID_TypeDef *pid,
        float Kp,
        float ki,
        float kd);
} PID_TypeDef;

//PID優化功能枚舉
typedef enum pid_Improvement_e
{
    NONE = 0X00,                        //無
    Integral_Limit = 0x01,              //積分限幅
    Derivative_On_Measurement = 0x02,   //微分先行
    Trapezoid_Intergral = 0x04,         //梯形積分
    Proportional_On_Measurement = 0x08, //該系列不涉及
    OutputFilter = 0x10,                //輸出濾波
    ChangingIntegralRate = 0x20,        //變積分
    ErrorHandle = 0x80,                 //異常處理
} PID_Improvement_e;

//異常狀況枚舉,這裏只寫了電機堵轉保護一種
typedef enum errorType_e
{
    PID_ERROR_NONE = 0x00U,
    Motor_Blocked = 0x01U
} ErrorType_e;

//異常狀況結構體
typedef struct
{
    uint64_t ERRORCount;
    ErrorType_e ERRORType;
} PID_ErrorHandler_t;

1.2 PID初始化

在使用以前須要先調用PID_Init函數進行參數初始化和函數鏈接學習

void PID_Init(
    PID_TypeDef *pid,
    uint16_t max_out,
    uint16_t intergral_limit,
    float deadband,

    float kp,
    float Ki,
    float Kd,

    float A,
    float B,

    uint8_t improve)
{
    pid->PID_param_init = f_PID_param_init;
    pid->PID_reset = f_PID_reset; //鏈接Kp Ki Kd參數重設函數
    pid->PID_param_init(pid, max_out, intergral_limit, deadband,
                        kp, Ki, Kd, A, B, improve); //鏈接並調用參數初始化函數
}

static void f_PID_param_init(
    PID_TypeDef *pid,
    uint16_t max_out,
    uint16_t intergral_limit,
    float deadband,

    float kp,
    float Ki,
    float Kd,

    float Changing_Integral_A,
    float Changing_Integral_B,

    uint8_t improve)
{
    //參數初始化
    pid->DeadBand = deadband;
    pid->IntegralLimit = intergral_limit;
    pid->MaxOut = max_out;
    pid->Target = 0;

    pid->Kp = kp;
    pid->Ki = Ki;
    pid->Kd = Kd;
    pid->ITerm = 0;

    pid->ScalarA = Changing_Integral_A;
    pid->ScalarB = Changing_Integral_B;

    pid->Improve = improve;

    //異常處理初始化
    pid->ERRORHandler.ERRORCount = 0;
    pid->ERRORHandler.ERRORType = PID_ERROR_NONE;

    pid->Output = 0;
}

1.3 PID 計算

PID_Calculate函數與網上大多數代碼大致結構相同,只是添加了不一樣的優化函數,具體優化在各函數(如:f_PID_ErrorHandle、f_Trapezoid_Intergral)中實現,PID_Calculate函數具體代碼以下:優化

float PID_Calculate(PID_TypeDef *pid, float measure, float target)
{
    if (pid->Improve & ErrorHandle) 
    {
        //異常處理
        f_PID_ErrorHandle(pid);
        if (pid->ERRORHandler.ERRORType != PID_ERROR_NONE)
        {
            //電機堵轉保護
            pid->Output = 0;
            return 0; 
        }
    }

    //偏差更新
    pid->Measure = measure;
    pid->Target = target;
    pid->Err = pid->Target - pid->Measure;

    //死區內進行計算
    if (ABS(pid->Err) > pid->DeadBand)
    {
        //計算比例、微分輸出與該週期積分項結果
        pid->Pout = pid->Kp * pid->Err;
        pid->ITerm = pid->Ki * pid->Err;
        pid->Dout = pid->Kd * (pid->Err - pid->Last_Err);

        //判斷是否使能梯形積分
        if (pid->Improve & Trapezoid_Intergral)
            f_Trapezoid_Intergral(pid);
        //判斷是否使能變積分
        if (pid->Improve & ChangingIntegralRate)
            f_Changing_Integral_Rate(pid);
        //判斷是否使能積分限幅
        if (pid->Improve & Integral_Limit)
            f_Integral_Limit(pid);
        //判斷是否使能微分先行
        if (pid->Improve & Derivative_On_Measurement)
            f_Derivative_On_Measurement(pid);

        //計算積分輸出
        pid->Iout += pid->ITerm;

        //計算pid總輸出
        pid->Output = pid->Pout + pid->Iout + pid->Dout;

        //判斷是否使能輸出濾波
        if (pid->Improve & OutputFilter)
            f_OutputFilter(pid);

        //輸出限幅
        f_Output_limit(pid);
    }
    //數據保存供下一週期調用
    pid->Last_Measure = pid->Measure;
    pid->Last_Output = pid->Output;
    pid->Last_Err = pid->Err;

    return pid->Output;
}

2 如何使用

這裏給出以發佈在GitHub上的示例,具體不在詳細講解ui

//pid函數鏈接
PID_Init(&PID_Example, 9600, 5000, 3, 1, 5, 0.3, 0.3, 100, 100, 
   		 ErrorHandle | Integral_Limit | OutputFilter);

//修改kp ki kd
PID_Example.PID_reset(&PID_Example, 3, 1, 0);

//計算
PID_Calculate(&PID_Example, measure, target);

該篇對該lib結構和使用就講到這裏,下一篇將會開始算法講解。code

相關文章
相關標籤/搜索