詳解PID控制器原理
一、P - Proportional 比例
想象一下一個全速行進的機器人,假設(shè)傳感器上的值為1000。 現(xiàn)在,由于它的速度和慣性,它可能會超過一點, 當(dāng)編寫程序時,這可能是一個大麻煩,你想盡可能的準(zhǔn)確。這個問題如圖所示(x軸上的綠色標(biāo)記代表理想距離):
在理想世界中,您告訴機器人在哪里停止,它就停止在哪里
但是,我們不是理想世界,如果我們突然告訴它停止,我們會有超調(diào)的問題,結(jié)果可能是這樣的:
現(xiàn)在這個超調(diào)不會是一個問題,如果它的距離總是相同的。然而,有很多變量可以改變它超出的距離。 例如:
- 電池電壓。 如果電池電量不足,則電機不能快速運行,會有較少的慣性, 在這種情況下,機器人超調(diào)變小
- 如果機器人碰到一些東西,那么超調(diào)會變小
- 如果某些東西將機器人朝向想要行進的方向推動,則超調(diào)將會 變得更大
所以你可以看到,超調(diào)不好。 所以P控制器控制速度, 所以P控制器控制速度平穩(wěn)地,讓它在接近目標(biāo)時減速,以縮小超調(diào)。 這就是為什么它被稱為比例控制器 – 輸出速度與要更改的值成比例,我們稱之為誤差(error)。
它是怎么做的?
你有一個很好的變量,叫做誤差(error)。這將是根據(jù)這個值對應(yīng)的傳感器的讀書,它還在變化,就像前文提到的。例如,誤差可以是剩下要走的距離,剩下的要提升的高度,需要繼續(xù)加熱的溫度,等等。
為了計算誤差,我們只需減去傳感器給我們的讀數(shù),這個讀書我們希望傳感器在完成后立即告訴我們。
誤差 =(目標(biāo)值) - (傳感器讀數(shù))
error = (target value) – (sensor reading)
因此,通過給出一些示例值來說明你想要的距離和它實際走過的距離,你會看到當(dāng)它接近目標(biāo)時,誤差會越來越小。
下面是幾個示例值:
目標(biāo)值 | 當(dāng)前傳感器讀數(shù) | 誤差 |
---|---|---|
1000 | 200 | 800 |
1000 | 400 | 600 |
1000 | 600 | 400 |
1000 | 800 | 200 |
1000 | 1000 | 0 |
我們可以用這個來控制應(yīng)用程序的速度,如果你想用一個簡單的P控制器,沒有I和D項。為此,我們可以寫:
error = (target value) – (sensor reading); speed = error;
另外,誤差值可能不太像我們想要的那樣。 這個值可能太高了,所以它超標(biāo)了很多。 如果過調(diào),它會嘗試和糾正過調(diào)(誤差將變成負(fù)數(shù)),所以你可以在在調(diào)試器或窗口觀察值和發(fā)生的情況,你將看到誤差在振蕩,超調(diào)然后過度校正。
或者,誤差值太小,但通常不會遇到這個問題。
對于任一問題,您可以更改誤差,以保持其比例因子,但你可以將錯誤乘以(或除)另一個數(shù)字,一個常數(shù)。 你可以稱之為任何你喜歡的名字,但它通常被稱為“Kp” – 比例組件的常數(shù)。
Kp = 0.5; while (condition) { error = (target value) – (sensor reading); speed = Kp * error; }
二、I - Integral 積分
所以代碼的比例部分已經(jīng)得到了,所以剩下的誤差是相當(dāng)小的。比例太小,不能產(chǎn)生很大的差異。這就是積分。積分是之前的誤差的總和。所以當(dāng)你的誤差非常小,積分起到作用,但它實際上如何工作的呢?
積分想要得到它,使其行進足夠快以縮小誤差,但不要太快,因為那樣可能會有超調(diào)的風(fēng)險.它通過慢慢加速的方式去決定走多快,積分可以想這樣計算
積分=積分+誤差* 時間增量
integral = integral + error*dT
以上的設(shè)置是這樣的,所以“積分”的新值等于(等號左側(cè))
先前的“積分”值,加上(誤差*時間增量)。忽略時間增量部分,我稍后再來討論這個問題。
積分增加的方式可以如下表中所示(使用誤差為2 做例子):
Cycle # | Previous value for integral | Error | New value for integral |
---|---|---|---|
0 | 0 | 2 | 2 |
1 | 2 | 2 | 4 |
2 | 4 | 2 | 6 |
3 | 6 | 2 | 8 |
4 | 8 | 2 | 10 |
粗體的數(shù)字(New value for integral積分的新值)在不斷增加.
那么我們?nèi)绾螌⑺砑拥浆F(xiàn)有代碼中呢? 我們把它加起來。所以現(xiàn)在的速度是:
speed = (Kp * error) + integral
那么現(xiàn)在加了積分偽代碼就變成這樣:
Kp = 0.5; while (condition) { error = (target value) – (sensor reading); integral = integral + error; speed = Kp*error + integral; }
就像比例部分的代碼一樣,我們需要對積分加上一個常數(shù)項。我將使用0.2作為Ki的一個例子值,盡管和K一樣,它只是一個數(shù)字我隨便舉例的。
Kp = 0.5; Ki = 0.2; while (condition) { error = (target value) – (sensor reading); integral = integral + error; speed = Kp*error + Ki*integral; }
前文告訴過你忽略時間增量,但我現(xiàn)在就解釋一下
Integral = integral + error*dT
增量時間是必須的,因為循環(huán)不會總是花費同樣的時間完成每個周期,但是當(dāng)每個周期花費同樣的時間時, dT可以合并到Ki中,如果每個周期不花費同樣的時間,只需要將代碼用于每個周期,這樣你可以使用這個周期的時間作為你的增量時間
在這個教程里,我們將假設(shè)周期之間的時間總是相同的,因此dT將被并入Ki。
這里是關(guān)于積分的幾個問題:
問題1:
當(dāng)你的誤差幾近于0時,你的積分可能任然是一個可以保持速度足夠高保持錯誤的變化的值,方程只會自己達到0,如果它超過一個等于0 的誤差,那么負(fù)誤差就會減去現(xiàn)有的積分.所以,如果速度任然很高來保持誤差,我們將面臨一個問題,是吧?
對于這個題,有一個非常簡單的解決方案,那就是在誤差達到0時重新設(shè)置積分,如下所示
Kp = 0.5; Ki = 0.2; while (condition) { error = (target value) – (sensor reading); integral = integral + error; if (error is 0) { integral = 0; } speed = Kp*error + Ki*integral; }
問題2:
它被稱為integral wind-up.它可以從一個大誤差開始,一旦循環(huán)開始運行,積分將開始構(gòu)建。所以,在這個積分需要的時候
使用時,它的值已經(jīng)遠遠超過可用的值。有一些簡單的解決辦法, 我列舉三個解決方案:
解決方案#1 –限制積分所能達到的值。如果太高,為什么不給它加個限度?一個限制可以寫成如下:
if (integral is greater than or equal to the maximum value) { integral = maximum value; }
但是,如果積分太大,但它是負(fù)的形式(即,使速度相反)快速地,你需要重寫和上面一樣的,但是為負(fù)的版本的積分。
解決方案#2 - 限制積分允許建立的范圍。因此,如果錯誤對于積分來說太大了,我們可以禁用該范圍的積分。
if ( error is greater than useful for the integral ) { disable the integral (set the integral to 0); }
但同樣,就像在解決方案1中一樣,你需要重寫相同的但要是積分的負(fù)值?;蛘?,如果您的編程語言支持使用一個絕對值的工具,您可以使用它使代碼更短,也許更簡單。
如何在代碼中實現(xiàn)絕對值的工具:
if ( abs(error) is greater than useful for the integral) { disable the integral (set the integral to 0); }
解決方案#3–限制積分允許積累的時間。這樣做有點復(fù)雜,但仍然可行。
對于本教程,我們將使用解決方案#2,因為它是最簡短的.
適合計算積分的范圍將為+/- 40,但只是一個隨機數(shù)。 下面代碼的完整版(如果剩下的話會被稱為“PI控制“[比例積分控制])將是這樣的:
Kp = 0.5; Ki = 0.2; while (condition) { error = (target value) – (sensor reading); integral = integral + error; if (error = 0) { integral = 0; } if ( abs(error) > 40) { integral = 0; } speed = Kp*error + Ki*integral; }
三、D - Derivative 導(dǎo)數(shù)
PID代碼的最后一點——導(dǎo)數(shù)!導(dǎo)數(shù)的工作就是預(yù)測未來的誤差價值,然后進行相應(yīng)的速度行為。例如,如果它認(rèn)為它會過調(diào),會使它慢下來。
為了能夠預(yù)測下一個誤差,我們需要知道先前的誤差,然后找到這兩者的區(qū)別。
derivative = ( (current error) – (previous error) ) / dT
這個公式將發(fā)現(xiàn)當(dāng)前誤差和之前的誤差之間的變化,然后我們可以通過將其添加到當(dāng)前誤差中來預(yù)測下一個誤差。就像積分一樣,導(dǎo)數(shù)是由dT影響的,但是一個循環(huán)的周期話費的時間總是相同的,dT可以合并到Kd。
這里有一個表,展示了未來可能發(fā)生的誤差的例子,通過導(dǎo)數(shù)計算的:
Current error | Previous error | Next error (error + derivative) |
---|---|---|
50 | 55 | 45 |
20 | 30 | 10 |
2 | 3 | 1 |
5 | 15 | -5 |
在我們的代碼中,導(dǎo)數(shù)是用上面的方程計算出來的,然后加到速度上(在這里也乘以Kd來達到縮放的目的)。
我們需要創(chuàng)建一個新的整數(shù),來命名先前的誤差(或者任何一個你喜歡的方式,只要它表示之前的誤差值),我們讓它更新自己在我們我們計算完導(dǎo)數(shù)后。我們可以用簡單的方式讓它更新,設(shè)置它的值為誤差值。
一下是完整的控制PID的偽代碼:
Kp = 0.5; Ki = 0.2; Kd = 0.1; while (condition) { error = (target value) – (sensor reading); integral = integral + error; if (error = 0) { integral = 0; } if ( abs(error) > 40) { integral = 0; } derivative = error – previous_error; previous_error = error; speed = Kp*error + Ki*integral + Kd*derivative; }
四、調(diào)整常數(shù)項
這是費時又費力的工作。 有很多不同的方法來調(diào)整Kp,Ki和Kd,我會盡我所能地解釋一下他們。 調(diào)整PID常數(shù)可以通過計算機程序完成,通過數(shù)學(xué)計算或通過手動調(diào)整, 我強烈建議您隨時查看誤差,速度等等,所以你可以看到距離到達目標(biāo)還剩下多少需要改變。 使用調(diào)試器或類似的監(jiān)視工具來檢查結(jié)果。
首先,了解調(diào)節(jié)PID控制器的規(guī)則很重要。 當(dāng)每個常數(shù)增加時有什么改變?nèi)缦卤硭尽?常數(shù) 術(shù)語在左側(cè)的列中,并且它們具有的效果在頂行列出。
效果如下:
- Rise time - 從起點到目標(biāo)點所需的時間
- Overshoot - 改變的量太大了; 值比錯誤更大
- Settling time - 遇到變化時需要解決的時間
- Steady-state error - 均衡時的誤差
- Stability - 速度的“平滑度”
當(dāng)每個常數(shù)增加時會發(fā)生什么?
Constant: | Rise time: | Overshoot: | Settling | Steady-state Stability: | Stability: |
---|---|---|---|---|---|
Kp | decrease | increase | Small change | decrease | degrade |
Ki | decrease | increase | increase | decrease | degrade |
Kd | minor change | decrease | decrease | No effect | Improve (if small enough) |
手工調(diào)優(yōu):
手動調(diào)優(yōu)是完全由你自己完成的——沒有涉及到數(shù)學(xué),但有時也會有些低效。我個人使用手動調(diào)優(yōu)方法,因為我可以溫和地增加每一個常量,并且知道什么時候會變得太高,而像ziegler - nichols方法這樣的數(shù)學(xué)方法,你永遠不會知道事情會怎樣發(fā)展,直到你嘗試之后。畢竟,在理論上,實踐和理論是一樣的,但在實踐中,它們不是。我調(diào)整常量的方式如下:
1.將Kp、Ki和Kd設(shè)置為0。這將使他們暫時癱瘓。
2.增加Kp直到誤差相當(dāng)小,但是它仍然從開始到結(jié)束足夠快。
3.增加Kd,直到任何超過你可能擁有的覆蓋。但是小心Kd——太多會使它過度
4.增加Ki,直到任何仍然存在的錯誤被消除。從一個非常小的數(shù)字開始,不要驚訝,如果它小到0.0001甚至更小。
5.使用調(diào)整常量的規(guī)則(在上一頁的表格中),您可以稍微更改一些常量,以使其工作到最佳性能。
數(shù)學(xué)方式
@todo 試過再補充
工具方式
@todo試過再補充
五、補充
while循環(huán):
您可能已經(jīng)注意到,在我的所有偽代碼示例中,我將主代碼放在while循環(huán)中。 這是因為誤差變化時需要重新計算誤差,積分,微分和速度。 例如,如果您將速度計算一次,但不再次計算,則無法重新刷新并相應(yīng)地更改速度 - 它將以原始速度繼續(xù)運行!
循環(huán)對PID控制器至關(guān)重要 - 不要忘記添加一個!
那么,你如何讓它最終退出循環(huán),每次當(dāng)它完成了它的工作?
其中的一個常見的方法是,如果你知道它需要多長時間才能完成,你 可以將循環(huán)設(shè)置為指定的時間量(但顯然比它需要的稍微多一點),以確保它確實完成了循環(huán)。
另一種方法是檢測一旦誤差達到零,并且已經(jīng)完成。如果你選擇這種方式,一定要注意確保它已經(jīng)完全停止。舉個例子,如果你告訴它運行循環(huán)直到誤差達到0,如果有任何過度,沒什么能做的,因為它將會停止(過度,錯誤必須通過一個點(0)。所以,你可以得到循環(huán)多長時間內(nèi)誤差保持在0。如果它只是在一個非常短的時間內(nèi)處于0,那么很有可能它已經(jīng)被過度并且需要重新調(diào)整自己?;蛘?,如果它在一段較長時間內(nèi)保持在0的值,那么說它已完全停止是安全的。
重新設(shè)置積分和之前的錯誤:
我前面已經(jīng)講過了,有時你需要把積分重置為0,但是最后一次需要0的值。當(dāng)您開始循環(huán)時,代碼會自動假設(shè)這個積分是0,之前的誤差是它應(yīng)該的值。但是,如果循環(huán)已經(jīng)運行,那么這個積分的值和之前的錯誤仍然是原來的值。
這可以通過在循環(huán)開始之前設(shè)置積分值和前一個錯誤的值來確定。
六、總結(jié)
Proportional-你的誤差,在真實值和預(yù)期值之間。
error = (target value) – (sensor reading)
Integral – 先前誤差的運行和,用于在誤差很小的時候進行精細的調(diào)整。
integral = integral + error*dT
Derivative – 誤差的變化,用來預(yù)測下一個誤差可能是什么。
derivative = ( (current error) – (previous error) ) / dT
The loop – 所有的計算都需要在循環(huán)中運行-不要忘記包含它!
將三個組件放在一起,再加上一些對Kp、Ki和Kd的精確值,您將擁有一個非常一致和精確的控制器。
七、調(diào)試口訣
參數(shù)整定找最佳,從小到大順序查,先是比例后積分,最后再把微分加,
曲線振蕩很頻繁,比例度盤要放大,曲線漂浮繞大灣,比例度盤往小扳,
曲線偏離回復(fù)慢,積分時間往下降,曲線波動周期長,積分時間再加長,
曲線振蕩頻率快,先把微分降下來,動差大來波動慢,微分時間應(yīng)加長,
理想曲線兩個波,前高后低4比1
八、具體方法
(1)確定比例系數(shù)Kp
確定比例系數(shù)Kp時,首先去掉PID的積分項和微分項,可以令Ti=0、Td=0,使之成為純比例調(diào)節(jié)。輸入設(shè)定為系統(tǒng)允許輸出最大值的60%~70%,比例系數(shù)Kp由0開始逐漸增大,直至系統(tǒng)出現(xiàn)振蕩;再反過來,從此時的比例系數(shù)Kp逐漸減小,直至系統(tǒng)振蕩消失。記錄此時的比例系數(shù)Kp,設(shè)定PID的比例系數(shù)Kp為當(dāng)前值的60%~70%。
(2)確定積分時間常數(shù)Ti
比例系數(shù)Kp確定之后,設(shè)定一個較大的積分時間常數(shù)Ti,然后逐漸減小Ti,直至系統(tǒng)出現(xiàn)振蕩,然后再反過來,逐漸增大Ti,直至系統(tǒng)振蕩消失。記錄此時的Ti,設(shè)定PID的積分時間常數(shù)Ti為當(dāng)前值的150%~180%。
(3) 確定微分時間常數(shù)Td
微分時間常數(shù)Td一般不用設(shè)定,為0即可,此時PID調(diào)節(jié)轉(zhuǎn)換為PI調(diào)節(jié)。如果需要設(shè)定,則與確定Kp的方法相同,取不振蕩時其值的30%。
(4) 系統(tǒng)空載、帶載聯(lián)調(diào)
對PID參數(shù)進行微調(diào),直到滿足性能要求。
以上就是詳解PID控制器原理的詳細內(nèi)容,更多關(guān)于PID控制器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++17 使用 std::string_view避免字符串拷貝優(yōu)化程序性能
這篇文章主要介紹了C++17 使用 std::string_view避免字符串拷貝優(yōu)化程序性能,幫助大家提高程序運行速度,感興趣的朋友可以了解下2020-10-10