(O)關於程序BUG說明,看最后面的紅色字體,視頻和源代碼中都沒有說明
(一)PID控制算法(P:比例 I:積分 D:微分)
(二)首先先說明原理,使用的是數字PID算法,模擬PID算法在計算機這樣的系統中是不能夠直接使用的,數字PID算法又分為位置式PID控制算法和增量式PID控制算法,那么下面從原理上說明這兩種算法
(三)原理分析如圖
(四)從上面圖中我們可以得到定義
定義變量
用戶設定值: SV
當前值(實際值): PV
偏差: E = SV - PV
(五)如果我們在一段時間內就從傳感器讀取一個值,那么我們就可以得到一個實際值的數據序列,,那么我們也會得到一個偏差值的序列
讀取時間: t(1) t(2) ------ t(k-1) t(k) 讀取到的值: X(1) X(2) ------ X(k-1) X(k)
偏差值: E(1) E(2) ------ E(k-1) E(k)
那么我們從偏差值中可以知道: E(X) > 0 說明未達標
E(X) = 0 說明正好達標
E(X) < 0 說明超標
(六)比例控制(P),作用:對偏差起到及時反映的作用,一旦產生偏差,控制器立即做出反映.............
定義:
比例系數:Kp (根據系統進行調節)
比例輸出:POUT = Kp * E(k)
POUT = Kp * E(k) + OUT0
OUT0說明:OUT0是防止E(K) = 0 時候比例控制不作用,所以添加個OUT0進去,OUT0可以根據系統定義大小
Kp說明:如果我們得到一個偏差之后,將偏差進行放大或者縮小來讓控制器進行控制
(七)積分控制(I),作用:消除靜差............
從上面我們得到偏差序列:
偏差值: E(1) E(2) ------ E(k-1) E(k)
定義,歷史偏差值之和:S(k) = E(1) + E(2) + .... + E(k-1) + E(K)
定義,積分輸出: IOUT = Kp * S(k) + OUT0
(八)微分控制(D),作用:反映偏差信號的變化趨勢.............
從上面我們得到偏差序列: 偏差值: E(1) E(2) ------ E(k-1) E(k)
定義,偏差之差:D(k) = E(k) - E(k-1)
定義,微分輸出:DOUT = Kp * D(k) + OUT0
(九)那么我們從上面就能得出PID的控制算法
PIDOUT = POUT + IOUT + DOUT = (Kp * E(k) + OUT0) + (Kp * S(k) + OUT0) + (Kp * D(k) +OUT0)
= Kp * (E(k) + S(k) + D(k)) + OUT0
OUT0防止PIDOUT = 0 時候算法還有輸出,防止失去控控制
比例(P):考慮當前
積分(I):考慮歷史
微分(D):考慮未來
(十)位置式PID,上面只是簡單的說明了一下原理,那么實際的數字PID控制算法中,額,那個變換公式打不出來,自行百度,這里就直接打出結果
Ti:積分常數
TD:微分常數
T:計算周期
上面兩個是變化后的積分和微分的那個,那么我們把上面的兩個替換到第九點中,我們就得到位置上PIDOUT的公式,兩個式子是一樣的
(十一)增量式PID,本次基礎上加上多少偏差:△OUT
/**********************分割線************************/
(十二)上面的只是PID的原理說明,那么數字PID的公式是
(十三)那么我們將上面的公式通過通過C語言寫出來
1. 位置式PID
#ifndef _pid_ #define _pid_ #include "stm32f10x_conf.h"
#define MODEL_P 1
#define MODEL_PI 2
#define MODEL_PID 3 typedef struct { u8 choose_model; //使用哪個模式調節
float Sv; //用戶設定值
float Pv; //當前值,實際值
float Kp; //比例系數
float T; //PID計算周期--采樣周期
u16 Tdata; //判斷PID周期到沒到
float Ti; //積分時間常數
float Td; //微分系數
float Ek; //本次偏差
float Ek_1; //上次偏差
float SEk; //歷史偏差之和
float Iout; //積分輸出
float Pout; //比例輸出
float Dout; //微分輸出
float OUT0; //一個維持的輸出,防止失控
float OUT; //計數最終得到的值
u16 pwmcycle;//pwm周期
}PID; extern PID pid; //存放PID算法所需要的數據
void PID_Calc(void); //pid計算
void PID_Init(void); //PID初始化
#endif
#include "pid.h" #include "PWM_Config.h" #include "USART_Config.h" //USART設置
PID pid; //存放PID算法所需要的數據
void PID_Init() { pid.choose_model = MODEL_PID; pid.T=330; //采樣周期,定時器使用1ms,則最小執行PID的周期為330ms
pid.Sv=280; //用戶設定值
pid.Kp=0.5; //比例系數
pid.Ti=180; //積分時間
pid.Td=1; //微分時間
pid.OUT0=0; //一個維持的輸出
pid.pwmcycle = 330; //PWM的周期
} void PID_Calc() //pid計算
{ float DelEk; //本次和上次偏差,最近兩次偏差之差
float ti,ki; float td; float kd; float out; if(pid.Tdata < (pid.T)) //最小計算周期未到
{ return ; } pid.Tdata = 0; pid.Ek=pid.Sv-pid.Pv; //得到當前的偏差值
pid.Pout=pid.Kp*pid.Ek; //比例輸出
pid.SEk+=pid.Ek; //歷史偏差總和
DelEk=pid.Ek-pid.Ek_1; //最近兩次偏差之差
ti=pid.T/pid.Ti; ki=ti*pid.Kp; pid.Iout=ki*pid.SEk*pid.Kp; //積分輸出
/*注意:上面程序中多了個pid.Kp,原程序中有,請自動刪除,正確的應該是pid.Iout=ki*pid.SEK */
td=pid.Td/pid.T; kd=pid.Kp*td; pid.Dout=kd*DelEk; //微分輸出
switch(pid.choose_model) { case MODEL_P: out= pid.Pout; printf("使用P運算\r\n") ; break; case MODEL_PI: out= pid.Pout+ pid.Iout; printf("使用PI運算\r\n") ; break; case MODEL_PID: out= pid.Pout+ pid.Iout+ pid.Dout; printf("使用PID運算\r\n") ; break; } printf("PID算得的OUT:\t%d\r\n",(int)out) ; //////////////////////////////////////////////////////////
/*判斷算出的數是否符合控制要求*/
if(out>pid.pwmcycle) //不能比PWM周期大,最大就是全高嗎
{ pid.OUT=pid.pwmcycle; } else if(out<0) //值不能為負數
{ pid.OUT=pid.OUT0; } else { pid.OUT=out; } printf("實際輸出使用的pid.OUT:\t%d\r\n",(int)pid.OUT) ; pid.Ek_1=pid.Ek; //更新偏差
Turn_Angle((int)pid.OUT); //輸出PWM
}
2.增量式PID
#ifndef _pid_ #define _pid_ #include "stm32f10x_conf.h"
#define MODEL_P 1
#define MODEL_PI 2
#define MODEL_PID 3 typedef struct { u8 choose_model; //使用哪個模式調節
float curr; //當前值
float set; //設定值
float En; //當前時刻
float En_1; //前一時刻
float En_2; //前二時刻
float Kp; //比例系數
float T; //采樣周期---控制周期,每隔T控制器輸出一次PID運算結果
u16 Tdata; //判斷PID周期到沒到
float Ti; //積分時間常數
float Td; //微分時間常數
float Dout; //增量PID計算本次應該輸出的增量值--本次計算的結果
float OUT0; //一個維持的輸出,防止失控
short currpwm; //當前的pwm寬度
u16 pwmcycle; //pwm周期
}PID; extern u8 STATUS; extern PID pid; void PIDParament_Init(void); /*增量式PID初始化*/
void pid_calc(void); /*pid計算 並輸出*/
#endif
#include "pid.h" #include "PWM_Config.h" #include "USART_Config.h" //USART設置
PID pid; void PIDParament_Init() // { pid.choose_model = MODEL_PID; pid.T=330; //采樣周期,定時器使用1ms,則最小執行PID的周期為330ms
pid.set =280; //用戶設定值
pid.Kp=0.5; //比例系數
pid.Ti=40; //微分系數常數
pid.Td=10; //積分時間常數
pid.OUT0=0; //一個維持的輸出
pid.pwmcycle = 330; //PWM的周期
} void pid_calc() //pid??
{ float dk1;float dk2; float t1,t2,t3; if(pid.Tdata < (pid.T)) //最小計算周期未到
{ return ; } pid.Tdata = 0; pid.En=pid.set-pid.curr; //本次誤差
dk1=pid.En-pid.En_1; //本次偏差與上次偏差之差
dk2=pid.En-2*pid.En_1+pid.En_2; t1=pid.Kp*dk1; //比例
t2=(pid.Kp*pid.T)/pid.Ti; //積分
t2=t2*pid.En; t3=(pid.Kp*pid.Td)/pid.T; //微分
t3=t3*dk2; switch(pid.choose_model) { case MODEL_P: pid.Dout= t1; printf("使用P運算\r\n") ; break; case MODEL_PI: pid.Dout= t1+t2; printf("使用PI運算\r\n") ; break; case MODEL_PID: pid.Dout= t1+t2+t3; printf("使用PID運算\r\n") ; break; } pid.currpwm+=pid.Dout; //本次應該輸出的PWM
printf("PID算得的OUT:\t%d\r\n",(int)pid.currpwm) ; /*判斷算出的數是否符合控制要求*/
if(pid.currpwm>pid.pwmcycle) //算出的值取值,肯定是在0-pid.pwmcycle之間,不然的話PWM怎么輸出
{ pid.currpwm=pid.pwmcycle; } if(pid.currpwm<0) { pid.currpwm=0; } printf("實際輸出使用的OUT:\t%d\r\n",(int)pid.currpwm) ; pid.En_2=pid.En_1; pid.En_1=pid.En; Turn_Angle(pid.currpwm); //輸出PWM
}
(十五)上面我們貼出來位置式PID算法和增量式PID算法的核心部分了,但是上面的理論上可以直接移植,添加一些還沒有定義的變量就好了,下面是具體的演示工程
024——PID算法整定
(一)首先在使用PID算法之前先進行基礎的設置
1. PWM正脈沖控制輸出開
2. 傳感器曲線隨着PWM占空比越大而越大
3. 傳感器在環境下情況下最低數據 ,和最高數據。 設定值不能超過這兩個范圍
(三)總結試湊法
1. 比例系數有小到大,然后找出超調小的Kp
2. 積分時間常數Ti由大變小,適當調整Kp
3. 微分時間常數Td由小變大,適當調整Ti和Kp
(四)實驗總結
1. KP設定,最初使用1,假如控制之后實際值比設定值小不夠,那就增大。反之就減少。
2. TI設定,數據先很大。看效果
1). 如果TI加進去之后數據很久才變化到目標值就逐漸減小。如果TI減少到執行幾次都是比設定值大的時候那就逐漸增大
2). 如果剛加進去變化很快,並且超調很高,就增大來調節
3. 積分就看情況調節
注意:視頻中有一些地方口誤,記得識別,如毫秒可能會讀成微妙,額。。。。沒辦法
注意:關於位置式PID算法中程序積分公式那里多了個pid.Kp,記得刪除,具體的位置看上面的位置式PID
程序中有注解,資料下載下來的程序沒有刪除,記得刪除!!!!!!
視頻教程:https://v.qq.com/x/page/o0826j2am1x.html
資料下載:https://download.csdn.net/download/xiaoguoge11/10911361