之前的幾篇文章(電機(jī)控制基礎(chǔ)篇),介紹的電機(jī)編碼器原理、定時器輸出PWM、定時器編碼器模式測速等。
本篇在前幾篇的基礎(chǔ)上,繼續(xù)來學(xué)習(xí)電機(jī)控制,通過PID算法,來進(jìn)行電機(jī)的速度控制,并進(jìn)行實(shí)驗(yàn)測試。
PID基礎(chǔ)
PID即:Proportional(比例)、Integral(積分)、Differential(微分)的縮寫。
PID是經(jīng)典的閉環(huán)控制算法,具有原理簡單,易于實(shí)現(xiàn),適用面廣,控制參數(shù)相互獨(dú)立,參數(shù)的選定比較簡單等優(yōu)點(diǎn)。
凡是需要將某一個物理量“保持穩(wěn)定”的場合(比如維持平衡,穩(wěn)定溫度、轉(zhuǎn)速等),PID都會派上大用場。
PID算法分類
PID算法可分為位置式PID與增量式PID兩大類。
在實(shí)際的編程應(yīng)用中,需要使用離散化的PID算法,以適用計(jì)算機(jī)的使用環(huán)境,下面以電機(jī)轉(zhuǎn)速控制為例,來看一下兩種PID算法的基本原理。
位置式PID
位置式PID是當(dāng)前系統(tǒng)的實(shí)際位置,與想要達(dá)到的預(yù)期位置的偏差,進(jìn)行PID控制
比例P:e(k) 此次誤差
積分I:∑e(i) 誤差的累加
微分D:e(k) - e(k-1) 此次誤差-上次誤差
因?yàn)橛姓`差積分 ∑e(i),一直累加,也就是當(dāng)前的輸出u(k)與過去的所有狀態(tài)都有關(guān)系。
位置式PID算法的偽代碼如下:
//位置式PID(偽代碼)
previous_err = 0;
integral = 0;
loop: //根據(jù)目標(biāo)值與測量值(如電機(jī)的設(shè)定速度與讀到的編碼器轉(zhuǎn)后后的速度),循環(huán)計(jì)算更新輸出值(如PWM)
error = setpoint - measured_value; /*誤差項(xiàng):目標(biāo)值-測量值*/
integral += error * dt; /*積分項(xiàng):誤差項(xiàng)的累計(jì)*/
derivative = (error - previous_error) / dt; /*微分項(xiàng):誤差的變化率*/
output = Kp*error + Ki*integral + Kd*derivative; /*三項(xiàng)分別乘以PID系數(shù)即為輸出*/
previous_err = err; //更新誤差
wait(dt); //等待固定的計(jì)算周期
goto loop;
增量式PID
比例P:e(k) - e(k-1) 此次誤差-上次誤差
積分I:e(k) 此次誤差d
微分D:e(k) - 2e(k-1)+e(k-2) 這次誤差-2×上次誤差+上上次誤差
注意增量式PID首先計(jì)算的是Δu(k),然后與上次的輸出相加,才是此次的輸出結(jié)果。增量式PID沒有誤差累加,控制增量Δu(k)的確定僅與最近3次的采樣值有關(guān)。
增量式PID算法的偽代碼如下:
//增量式PID(偽代碼)
previous02_error = 0; //上上次偏差
previous01_error = 0; //上次偏差
integral = 0; //積分和
pid_out = 0; //pid增量累加和
loop:
error = setpoint ? measured_value; /*誤差項(xiàng):目標(biāo)值-測量值*/
proportion = error - previous01_error; /*比例項(xiàng):誤差項(xiàng)-上次偏差*/
integral = error * dt; /*積分項(xiàng):誤差項(xiàng)的累計(jì)*/
derivative = (error ? 2*previous01_error + previous02_error) / dt;/*微分項(xiàng):上次誤差與上上次誤差的變化率*/
/*或?qū)懗桑篸erivative = ( (error ? previous01_error)/dt - (previous01_error - previous02_error)/dt )*/
pid_delta = Kp × error + Ki × integral + Kd × derivative; //計(jì)算得到PID增量
pid_out = pid_out + pid_delta; //計(jì)算最終的PID輸出
previous02_error = previous01_error; //更新上上次偏差
previous01_error = error; //更新上次偏差
wait(dt); //等待固定的計(jì)算周期
goto loop;
PID各項(xiàng)的作用
以這個彈簧為例(假設(shè)沒有重力,只有空氣阻力),先是在平衡位置上(目標(biāo)位置),拉它一下,然后松手,這時它會震蕩起來。
P 比例
P就是比例的意思。這里就類比彈簧的彈力(回復(fù)力):F=k*Δx
當(dāng)物塊距離平衡位置越遠(yuǎn)時,彈力越大,反之,離平衡位置越近,力越小。
當(dāng)物塊位于平衡位置上方時,彈性向下,當(dāng)物塊位于平衡位置下方時,彈性向上,即彈力總是使物塊朝平衡位置施力。
D 微分/求導(dǎo)/變化率
只有P控制,物塊一直在上下震蕩,整個系統(tǒng)不是特別穩(wěn)定。
這是因?yàn)榭諝庾枇μ。胂笠幌抡麄€把它放到水里,物塊應(yīng)該很快會靜止下來。這時因?yàn)?strong>阻力的作用。
D的作用就相當(dāng)于阻力:
它與變化速度(單位時間內(nèi)的變化量)有關(guān),變化的越大,它施加的阻力也就越大
它的方向與目標(biāo)值無關(guān),比如,當(dāng)物塊從下到上經(jīng)過平衡位置時,它的方向一直是朝下,
即先是阻止物塊靠近平衡位置,再是阻止物塊遠(yuǎn)離平衡位置(對比P的作用,始終阻止物塊遠(yuǎn)離平衡位置)
它的作用就是減小系統(tǒng)的超調(diào)量了(減少系統(tǒng)在平衡位置震蕩)
I 積分/誤差累積
有了P的動力和D的阻力,這個物塊就可以較快的穩(wěn)定下來了,那I的作用是什么呢?
想象一下,如果有其它外力的影響,在某一時刻,物塊將要到達(dá)平衡位置時,恰好P的動力與外力(與P的作用方向相反的恒定力)抵消,則之后物塊將停在此處附近(因?yàn)榇藭rD的力也趨近0,并很快變?yōu)?),一直到達(dá)不了平衡位置。
這時,I的誤差積分作用就很有必要了:
它計(jì)算的誤差的累計(jì),只要有誤差,它就一直增加,開始可能很小,但只要沒要到達(dá)平衡位置,該值就會越來越大
它的作用就是消除系統(tǒng)的靜態(tài)誤差了
PID參數(shù)整定
實(shí)際應(yīng)用,進(jìn)行PID參數(shù)調(diào)節(jié)時,一般使用試湊法,PID參數(shù)整定口訣如下:
參數(shù)整定找最佳,從小到大順序查,
先是比例后積分,最后再把微分加,
曲線振蕩很頻繁,比例度盤要放大,
曲線漂浮繞大灣,比例度盤往小扳,
曲線偏離回復(fù)慢,積分時間往下降,
曲線波動周期長,積分時間再加長,
曲線振蕩頻率快,先把微分降下來,
動差大來波動慢,微分時間應(yīng)加長,
理想曲線兩個波,前高后低4比1,
一看二調(diào)多分析,調(diào)節(jié)質(zhì)量不會低。
電機(jī)PID速度控制
上面介紹了PID的基礎(chǔ)知識,接下來就使用位置式PID來實(shí)現(xiàn)對直流電機(jī)轉(zhuǎn)速的控制。
程序
自定義PID結(jié)構(gòu)體
typedef struct
{
float target_val; //目標(biāo)值
float err; //偏差值
float err_last; //上一個偏差值
float Kp,Ki,Kd; //比例、積分、微分系數(shù)
float integral; //積分值
float output_val; //輸出值
}PID;
PID算法實(shí)現(xiàn)(位置式PID)
float PID_realize(float actual_val)
{
/*計(jì)算目標(biāo)值與實(shí)際值的誤差*/
pid.err = pid.target_val - actual_val;
/*積分項(xiàng)*/
pid.integral += pid.err;
/*PID算法實(shí)現(xiàn)*/
pid.output_val = pid.Kp * pid.err +
pid.Ki * pid.integral +
pid.Kd * (pid.err - pid.err_last);
/*誤差傳遞*/
pid.err_last = pid.err;
/*返回當(dāng)前實(shí)際值*/
return pid.output_val;
}
周期調(diào)用PID計(jì)算
//周期定時器的回調(diào)函數(shù)
void AutoReloadCallback()
{
int sum = 0;/*編碼器值(PID輸入)*/
int res_pwm = 0;/*PWM值(PID輸出)*/
/*讀取編碼器測量的速度值*/
sum = read_encoder();
/*進(jìn)行PID運(yùn)算,得到PWM輸出值*/
res_pwm = PID_realize(sum);
/*根據(jù)PWM值控制電機(jī)轉(zhuǎn)動*/
set_motor_rotate(res_pwm);
/*給上位機(jī)通道1發(fā)送實(shí)際值*/
set_computer_value(SEND_FACT_CMD, CURVES_CH1, &sum, 1);
}
上位機(jī)
這里使用野火多功能調(diào)試助手的"PID調(diào)試助手“來進(jìn)行實(shí)驗(yàn),用于顯示PID調(diào)節(jié)時的電機(jī)轉(zhuǎn)速曲線。
實(shí)驗(yàn)演示
目標(biāo)速度值設(shè)為50(這里的目標(biāo)值50使用的是編碼器10ms捕獲的脈沖數(shù)),通過體調(diào)節(jié)PID的參數(shù),來測試電機(jī)能否較快的達(dá)到目標(biāo)速度。
先調(diào)節(jié)P
P值先使用10看看效果,從速度曲線可以看出,達(dá)不到目標(biāo)速度,且與目標(biāo)速度相差較大。
P | I | D |
---|---|---|
10 | 0 | 0 |
P值加大到100,從速度曲線可以看出,還是達(dá)不到目標(biāo)速度。
P | I | D |
---|---|---|
100 | 0 | 0 |
只使用P,會存在靜差,始終達(dá)到不了目標(biāo)值,這時就要使用積分項(xiàng)來消除靜差了。
再調(diào)節(jié)I
P保持100,I使用0.2,從速度曲線可以看出,可以達(dá)到目標(biāo)速度,但跟隨的速度較慢。
P | I | D |
---|---|---|
100 | 0.2 | 0 |
P保持100,加大I,使用1.0,從速度曲線可以看出,可以達(dá)到目標(biāo)速度,跟隨的速度加快了。
P | I | D |
---|---|---|
100 | 1.0 | 0 |
P保持100,繼續(xù)加大I,使用3.0,從速度曲線可以看出,可以達(dá)到目標(biāo)速度,跟隨的速度進(jìn)一步加快了。
P | I | D |
---|---|---|
100 | 3.0 | 0 |
P保持100,再繼續(xù)加大I,使用6.0,從速度曲線可以看出,可以達(dá)到目標(biāo)速度,跟隨速度也很快,但有一點(diǎn)過沖。
P | I | D |
---|---|---|
100 | 6.0 | 0 |
對于過沖,可以再加入微分試試,微分D相當(dāng)于阻力的效果
最后調(diào)節(jié)D
P保持100,I保持6.0,D使用3.0,從速度曲線上,好像看不出明顯的變化。
P | I | D |
---|---|---|
100 | 6.0 | 3.0 |
P保持100,I保持6.0,D加大到6.0,從速度曲線上看,過沖幅度減小了點(diǎn)。
P | I | D |
---|---|---|
100 | 3.0 | 6.0 |
演示視頻
https://www.bilibili.com/video/BV18Q4y1R7e8?spm_id_from=333.999.0.0
總結(jié)
本篇簡單介紹了PID的基礎(chǔ)原理與參數(shù)整定,若想把PID參數(shù)調(diào)節(jié)好,還需要不斷的實(shí)踐與調(diào)試。
-
單片機(jī)
+關(guān)注
關(guān)注
6035文章
44554瀏覽量
634632 -
電機(jī)控制
+關(guān)注
關(guān)注
3534文章
1876瀏覽量
268740 -
PWM
+關(guān)注
關(guān)注
114文章
5181瀏覽量
213796 -
PID
+關(guān)注
關(guān)注
35文章
1472瀏覽量
85478 -
PID控制
+關(guān)注
關(guān)注
10文章
460瀏覽量
40091
發(fā)布評論請先 登錄
相關(guān)推薦
評論