本系列旨在以我自己寫的PID lib為例,講一下PID的幾點基本優化,PID的基本原理網上有很多資料,因此本系列將不會涉及PID的基本實現原理,在這里特別推薦Matlab tech talk的PID教程:https://ww2.mathworks.cn/videos/series/understanding-pid-control.html。
由於筆者大一在讀,還沒有學習自動控制原理等課程,因此本系列將不會從自控原理角度展開,相反的,本系列將試圖從“直覺”展開,通過直觀的描述讓大家從直覺上感受並理解PID的一些包括微分先行、積分分離等基礎的優化。
由於筆者水平有限,文中難免存在一些不足和錯誤之處,誠請各位批評指正。
1 積分分離
1.1 問題
在我的PID庫與PID基本優化(二)中提到過,在目標信號瞬間發生變化時,會導致微分項輸出出現異常。這樣的情況不僅會導致微分項出現問題,同樣會影響積分項的穩定性。例如在系統啟動或者設定值被人為大幅度改變時,由於誤差瞬間增大,誤差的積分會迅速累積,從而導致積分項輸出過大,進而產生較大的超調,嚴重時甚至會引起系統振盪:

1.2 解決方案
在消除微分沖擊問題時,我們通過將誤差的微分拆解成兩項,去掉其中引起沖擊的一項以解決問題。但對於積分項來說,將誤差的積分拆成兩項並刪掉目標信號的積分是不現實的,因為這樣積分控制就失去了消除靜態誤差的能力。
因此我們需要換一種思路,既然在目標信號瞬間變化時,誤差的瞬間增大導致了積分的過分積累。我們只需要在誤差超過一定閾值時停止積分過程,當誤差小於這個閾值,降至合理范圍的時候,我們再繼續積分過程,這樣就避免了由於目標信號變化引起的巨大誤差累積到積分項中。
從圖二中可以看到,積分項是在控制過程開始一段時間后開始積累,避免了過分累積,從而減小了超調:

1.3 代碼實現
庫中並沒有實現積分分離,而是選擇了過渡效果更平緩的變積分策略,因此以下代碼並未在庫中出現
static void f_Integral_Separation(PID_TypeDef *pid)
{
//首先判斷該周期內積分項是否為積累趨勢
//只有本次積分為積累趨勢才會取消積分項
//在本篇結束前會詳細分析這么處理的意義
if (pid->Err * pid->Iout > 0)
{
if (ABS(pid->Err) <= pid->MaxErr)
return; //完整積分
else
pid->ITerm = 0;//取消積分環節
}
}
2 變速積分
2.1 問題
變速積分與積分分離解決的問題相同,但在積分分離中,我們對誤差只有積和不積兩種對策,但0和1之間的過渡未免太突兀了,因此我們引入變速積分的概念。
2.2 解決方案
變速積分與積分分離解決的問題的方式大致相同,可以將變速積分理解為積分分離的一個再優化。變速積分用連續函數代替積分分離中單純的0,1切換。
這個連續函數可以是線性的也可以是非線性的,一般我們使用一個比較簡單的形式:
2.3 代碼實現
static void f_Changing_Integral_Rate(PID_TypeDef *pid)
{
if (pid->Err * pid->Iout > 0)
{
if (ABS(pid->Err) <= pid->ScalarB)
return; //完整積分
if (ABS(pid->Err) <= (pid->ScalarA + pid->ScalarB))
//使用線性函數過渡
pid->ITerm *= (pid->ScalarA - ABS(pid->Err) + pid->ScalarB) / pid->ScalarA;
else
pid->ITerm = 0;//取消積分環節
}
}
3 最后的問題
在積分分離與變速積分的代碼實現中,我都會在算法開始前判斷下當前周期下積分是否為累積趨勢,只有在累積趨勢下才會通過一些方法抑制積分的過度積累。目的是為了避免在控制信號發生變化,導致誤差瞬間反向時,由於誤差過大導致積分限制算法發揮作用,這樣一來積分無法迅速減小。在誤差反向過程中,積分居高不下會導致PID輸出持續保持較高水平,從而導致響應嚴重滯后。
這樣的滯后在某些控制頻率較低的場合下甚至會導致超調和振盪,下圖為前文PID參數保持不變時將目標值歸零后控制曲線的變化:

因此,需要在積分分離或變速積分在對積分項進行處理前先判斷一下積分是否會累積,如果當前周期的誤差會導致積分減小,則無需抑制積分減小的趨勢,因為這兩種算法歸根結底是為了避免積分過度積累而非積分的快速變化。