前言
老師布置了個(gè)任務(wù),用編程實(shí)現(xiàn)PID調(diào)節(jié),鑒于我們專業(yè)都學(xué)過C語言和VB,于是我就想拿Kotlin練練手.
上網(wǎng)搜索一番別人怎么用C實(shí)現(xiàn)的,找到個(gè)比較詳細(xì)的,但是展示了太多數(shù)據(jù),我這里整理一下基礎(chǔ)知識(shí).
Kotlin的實(shí)現(xiàn)在這里
簡要解析
參考資料,來自這里
通過誤差信號(hào)控制被控量,而控制器本身就是比例、積分、微分三個(gè)環(huán)節(jié)的加和。這里我們規(guī)定(在t時(shí)刻):
- 輸入量為rin(t);
- 輸出量為rout(t);
- 偏差量為err(t)=rin(t)-rout(t);
- 如果偏差為0,則比例環(huán)節(jié)不起作用,只有存在偏差時(shí),比例環(huán)節(jié)才起作用。
- 而微分信號(hào)則反應(yīng)了偏差信號(hào)的變化規(guī)律,或者說是變化趨勢(shì),根據(jù)偏差信號(hào)的變化趨勢(shì)來進(jìn)行超前調(diào)節(jié),從而增加了系統(tǒng)的快速性。。
位置型PID的C語言實(shí)現(xiàn)
第一步:定義PID變量結(jié)構(gòu)體,代碼如下:
struct _pid{
float SetSpeed; //定義設(shè)定值
float ActualSpeed; //定義實(shí)際值
float err; //定義偏差值
float err_last; //定義上一個(gè)偏差值
float Kp,Ki,Kd; //定義比例、積分、微分系數(shù)
float voltage; //定義電壓值(控制執(zhí)行器的變量)
float integral; //定義積分值
}pid;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
控制算法中所需要用到的參數(shù)在一個(gè)結(jié)構(gòu)體中統(tǒng)一定義,方便后面的使用。
第二部:初始化變量,代碼如下:
void PID_init(){
printf("PID_init begin \n");
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.015;
pid.Kd=0.2;
printf("PID_init end \n");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
統(tǒng)一初始化變量,尤其是Kp,Ki,Kd三個(gè)參數(shù),調(diào)試過程當(dāng)中,對(duì)于要求的控制效果,可以通過調(diào)節(jié)這三個(gè)量直接進(jìn)行調(diào)節(jié)。
第三步:編寫控制算法,代碼如下:
float PID_realize(float speed){
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
pid.integral+=pid.err;
pid.voltage=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
pid.err_last=pid.err;
pid.ActualSpeed=pid.voltage*1.0;
return pid.ActualSpeed;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
注意:這里用了最基本的算法實(shí)現(xiàn)形式,沒有考慮死區(qū)問題,沒有設(shè)定上下限,只是對(duì)公式的一種直接的實(shí)現(xiàn),后面的介紹當(dāng)中還會(huì)逐漸的對(duì)此改進(jìn)。
到此為止,PID的基本實(shí)現(xiàn)部分就初步完成了。下面是測(cè)試代碼:
int main(){
printf("System begin \n");
PID_init();
int count=0;
while(count<1000)
{
float speed=PID_realize(200.0);
printf("%f\n",speed);
count++;
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
增量型PID的C語言實(shí)現(xiàn)
這就是離散化PID的增量式表示方式,由公式可以看出,增量式的表達(dá)結(jié)果和最近三次的偏差有關(guān),這樣就大大提高了系統(tǒng)的穩(wěn)定性。需要注意的是最終的輸出結(jié)果應(yīng)該為
u(K)+增量調(diào)節(jié)值;
- 1
include<stdio.h>
struct _pid{
float SetSpeed; //定義設(shè)定值
float ActualSpeed; //定義實(shí)際值
float err; //定義偏差值
float err_next; //定義上一個(gè)偏差值
float err_last; //定義最上前的偏差值
float Kp,Ki,Kd; //定義比例、積分、微分系數(shù)
}pid;
void PID_init(){
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.err_next=0.0;
pid.Kp=0.2;
pid.Ki=0.015;
pid.Kd=0.2;
}
float PID_realize(float speed){
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
float incrementSpeed=pid.Kp*(pid.err-pid.err_next)+pid.Ki*pid.err+pid.Kd*(pid.err-2*pid.err_next+pid.err_last);
pid.ActualSpeed+=incrementSpeed;
pid.err_last=pid.err_next;
pid.err_next=pid.err;
return pid.ActualSpeed;
}
int main(){
PID_init();
int count=0;
while(count<1000)
{
float speed=PID_realize(200.0);
printf("%f\n",speed);
count++;
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
總結(jié):系統(tǒng)的穩(wěn)定性大大提高
積分分離的PID控制算法C語言實(shí)現(xiàn)
在普通PID控制中,引入積分環(huán)節(jié)的目的,主要是為了消除靜差,提高控制精度。但是在啟動(dòng)、結(jié)束或大幅度增減設(shè)定時(shí),短時(shí)間內(nèi)系統(tǒng)輸出有很大的偏差,會(huì)造成PID運(yùn)算的積分積累,導(dǎo)致控制量超過執(zhí)行機(jī)構(gòu)可能允許的最大動(dòng)作范圍對(duì)應(yīng)極限控制量,從而引起較大的超調(diào),甚至是震蕩,這是絕對(duì)不允許的。
為了克服這一問題,引入了積分分離的概念,其基本思路是 當(dāng)被控量與設(shè)定值偏差較大時(shí),取消積分作用; 當(dāng)被控量接近給定值時(shí),引入積分控制,以消除靜差,提高精度。其具體實(shí)現(xiàn)代碼如下:
pid.Kp=0.2;
pid.Ki=0.04;
pid.Kd=0.2; //初始化過程
if(abs(pid.err)>200)
{
index=0;
}else{
index=1;
pid.integral+=pid.err;
}
pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last); //算法具體實(shí)現(xiàn)過程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
總結(jié):系統(tǒng)的快速性得到了提高。
抗積分飽和的PID控制算法C語言實(shí)現(xiàn)
所謂的積分飽和現(xiàn)象是指如果系統(tǒng)存在一個(gè)方向的偏差,PID控制器的輸出由于積分作用的不斷累加而加大,從而導(dǎo)致執(zhí)行機(jī)構(gòu)達(dá)到極限位置,若控制器輸出U(k)繼續(xù)增大,執(zhí)行器開度不可能再增大,此時(shí)計(jì)算機(jī)輸出控制量超出了正常運(yùn)行范圍而進(jìn)入飽和區(qū)。一旦系統(tǒng)出現(xiàn)反向偏差,u(k)逐漸從飽和區(qū)退出。進(jìn)入飽和區(qū)越深則退出飽和區(qū)時(shí)間越長。在這段時(shí)間里,執(zhí)行機(jī)構(gòu)仍然停留在極限位置而不隨偏差反向而立即做出相應(yīng)的改變,這時(shí)系統(tǒng)就像失控一樣,造成控制性能惡化,這種現(xiàn)象稱為積分飽和現(xiàn)象或積分失控現(xiàn)象。
防止積分飽和的方法之一就是抗積分飽和法,該方法的思路是在計(jì)算u(k)時(shí),首先判斷上一時(shí)刻的控制量u(k-1)是否已經(jīng)超出了極限范圍: 如果u(k-1)>umax,則只累加負(fù)偏差; 如果u(k-1)<umin,則只累加正偏差。從而避免控制量長時(shí)間停留在飽和區(qū)。
struct _pid{
float SetSpeed; //定義設(shè)定值
float ActualSpeed; //定義實(shí)際值
float err; //定義偏差值
float err_last; //定義上一個(gè)偏差值
float Kp,Ki,Kd; //定義比例、積分、微分系數(shù)
float voltage; //定義電壓值(控制執(zhí)行器的變量)
float integral; //定義積分值
float umax;
float umin;
}pid;
void PID_init(){
printf("PID_init begin \n");
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.1; //注意,和上幾次相比,這里加大了積分環(huán)節(jié)的值
pid.Kd=0.2;
pid.umax=400;
pid.umin=-200;
printf("PID_init end \n");
}
float PID_realize(float speed){
int index;
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
if(pid.ActualSpeed>pid.umax) //灰色底色表示抗積分飽和的實(shí)現(xiàn)
{
if(abs(pid.err)>200) //藍(lán)色標(biāo)注為積分分離過程
{
index=0;
}else{
index=1;
if(pid.err<0)
{
pid.integral+=pid.err;
}
}
}else if(pid.ActualSpeed<pid.umin){
if(abs(pid.err)>200) //積分分離過程
{
index=0;
}else{
index=1;
if(pid.err>0)
{
pid.integral+=pid.err;
}
}
}else{
if(abs(pid.err)>200) //積分分離過程
{
index=0;
}else{
index=1;
pid.integral+=pid.err;
}
}
pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
pid.err_last=pid.err;
pid.ActualSpeed=pid.voltage*1.0;
return pid.ActualSpeed;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
總結(jié):系統(tǒng)的穩(wěn)定時(shí)間相對(duì)前幾次來講縮短了不少。
梯形積分的PID控制算法C語言實(shí)現(xiàn)
先看一下梯形算法的積分環(huán)節(jié)公式
作為PID控制律的積分項(xiàng),其作用是消除余差,為了盡量減小余差,應(yīng)提高積分項(xiàng)運(yùn)算精度,為此可以將矩形積分改為梯形積分,具體實(shí)現(xiàn)的語句為:
pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral/2+pid.Kd*(pid.err-pid.err_last); //梯形積分
- 1
總結(jié):精度進(jìn)一步提高。
變積分的PID控制算法C語言實(shí)現(xiàn)
變積分PID可以看成是積分分離的PID算法的更一般的形式。在普通的PID控制算法中,由于積分系數(shù)ki是常數(shù),所以在整個(gè)控制過程中,積分增量是不變的。但是,系統(tǒng)對(duì)于積分項(xiàng)的要求是,系統(tǒng)偏差大時(shí),積分作用應(yīng)該減弱甚至是全無,而在偏差小時(shí),則應(yīng)該加強(qiáng)。積分系數(shù)取大了會(huì)產(chǎn)生超調(diào),甚至積分飽和,取小了又不能短時(shí)間內(nèi)消除靜差。因此,根據(jù)系統(tǒng)的偏差大小改變積分速度是有必要的。
變積分PID的基本思想是設(shè)法改變積分項(xiàng)的累加速度,使其與偏差大小相對(duì)應(yīng):偏差越大,積分越慢; 偏差越小,積分越快。
這里給積分系數(shù)前加上一個(gè)比例值index:
當(dāng)abs(err)<180時(shí),index=1;
當(dāng)180<abs(err)<200時(shí),index=(200-abs(err))/20;
當(dāng)abs(err)>200時(shí),index=0;
- 1
- 2
- 3
- 4
- 5
最終的比例環(huán)節(jié)的比例系數(shù)值為ki*index;
具體PID實(shí)現(xiàn)代碼如下:
pid.Kp=0.4;
pid.Ki=0.2; //增加了積分系數(shù)
pid.Kd=0.2;
float PID_realize(float speed){
float index;
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
if(abs(pid.err)>200) //變積分過程
{
index=0.0;
}else if(abs(pid.err)<180){
index=1.0;
pid.integral+=pid.err;
}else{
index=(200-abs(pid.err))/20;
pid.integral+=pid.err;
}
pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
pid.err_last=pid.err;
pid.ActualSpeed=pid.voltage*1.0;
return pid.ActualSpeed;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
總結(jié):系統(tǒng)的穩(wěn)定速度非???/strong>
我在另一篇博客https://blog.csdn.net/qq_37258787/article/details/79603352使用Kotlin語言實(shí)現(xiàn)上述功能,并進(jìn)行了簡單的數(shù)據(jù)可視化展示.