PID控制算法系列(2)-串級PID的理解與實現
致謝
本系列部落格對下面的文章及視訊有著不同程度的借鑑與理解,在這裡我真誠地感謝這些樂於分享的大大們。
理解PID的原理以及控制效果:https://www.bilibili.com/video/BV1xQ4y1T7yv
理解PID的程式碼實現:https://www.bilibili.com/video/BV1Af4y1L7Lz
理解串級PID的原理:https://zhuanlan.zhihu.com/p/135396298
brettbeauregard的部落格:http://brettbeauregard.com/blog/
正文
一、串級PID
1.1 原理理解
有單級PID,那麼也就一定會有串級PID。其實串級PID就是在原先的PID控制塊的前面再接一個PID控制塊,叫做外環
前面的控制塊裡面放著什麼,以及其他的輸入都先不用管,一步步來。
首先需要回答的第一個問題就是我們為什麼需要串級的PID?
還是使用上面四輪小車作為例子說明。先不管我們建立的第一個PID模型,只看小車這一個整體,我們可以控制的是小車的速度。而小車這一個系統的任務可以有許多,其中就比如到達某一個具體的位置。我們雖然能夠使用單級的PID系統控制小車的速度在某一時刻儘可能快地達到期望值,但是我們是沒有辦法控制小車的加速度的,除非我們每時每刻都能改變小車速度的期望值。我們所希望的是,當我們給定一個位置,也就是給定一個位移的時候,小車能夠儘快地到達,其中小車的運動必然是一開始用較大的加速度做加速運動,快到終點了再用一定的加速度做減速運動,注意,是小車能夠自行完成這樣的任務。
顯然單級PID是無法做到這一點的,所以需要在它的前邊再新增一級PID控制器,這個控制器的功能只有一個,那就是提供一個合適的速度變化曲線,使得單級的PID具備前面所說的先加速,後減速的功能。
用數學公式去理解,設位移是S,也就是整個系統最後需要達到的目標,第一級PID直接控制了V,也就是控制了小車當前的速度,在沒有外界干預的情況下它只能做勻速直線運動,也就是
\[S=vt \]那麼這個小車到達S的方法是什麼呢?是彈射啟動與急剎。在啟動瞬間達到速度V並且在通過終點的瞬間急剎,從而完成S的位移。但是學過高中物理的人都知道這是不可能的,小車達到速度V需要時間,從V降低速度到0也需要時間。
所以引入第二個PID控制器,它的目的前面說過,是找到合適的曲線來引導速度的變化,也就是小車有了一定的加速度,所以有:
現在就好了,小車就能在一個加速度增大的加速運動與一個加速度逐漸減小的減速運動中(可能是這樣吧)平滑地到達終點了,這與上面單級PID不同,這是物理上真實可以實現的。
所以也有一句話是這麼說的,外環的微分就是內環。在這個例子中外環實際上提供的就是一條速度變化曲線,在這條曲線上任意一點取微分,就得到了速度,也就是內環控制的物理量。
那麼這個合適的速度變化曲線,我們上哪找呢?其實大部分使用的曲線都類似下面這兩種:
這裡的曲線並不是指真的需要輸入這種曲線,而是在這個PID控制器中,當引數設定好後,輸出的控制結果就類似這種曲線。同時一般在外環中只會引入P(比例),而不使用I(積分)與D(微分),原因是新增I後響應會變慢,新增D後又對噪聲敏感,我們要的只是一個大概的曲線,有比例係數控制就足夠好了。
現在我們就擁有了一臺能夠實現下面功能的小車:
給定一個位置,小車可以實現自動控制加減速以一個儘可能短的時間到達終點,期間不需要人的干預
也就是下圖的樣子:
但是外環PID想要直接獲取到當前的位移是比較困難的,所以做下面的處理來間接獲取:
而小車的期望位置則由時間內油門搖桿偏移量的積分來求得。
所以現在我們就有了一個比較好的小車模型。需要注意的是,在這個模型中小車的轉向是不受PID控制的,因為並沒有相關的陀螺儀感測器來反饋角度與角速度的資料,也就是說小車的轉向只能與人眼構成一個閉環系統,我們覺得它轉得慢了,就加大油門量;覺得它轉的快了,就降低油門量。
在油門量方面,如果通過遙杆來控制,那麼小車能夠保持直線行駛(沒有PID的小車四個輪子的轉速沒辦法達到一致);如果是通過其他的裝置(例如上位機等)控制,那麼小車不僅能夠保證直線行,還能知道自己走了多遠,還有多遠沒走,你讓它往前開一米,它就能精確地到達一米的位置。
串級的PID多數用於二輪平衡小車上,在平衡小車上我們就沒有辦法通過搖桿來控制小車的速度實現小車的平衡了,必須由小車自己結合角度的PID進行融合控制。
那麼單純看二輪平衡小車的位置環設定大概就是下面這樣:
- 當小車靜止的時候,小車的期望位置就是原點,反饋的位置就是小車時間內的速度的積分;當小車運動的時候,小車的期望位置就是搖桿的偏移量在時間內的積分,反饋的位置同上。
1.2 程式碼實現
//串級PID
//定義所需要的變數
float Kp_ex; //外環P
float Kp, Ki, Kd; //內環PID
float Motor1_speed; //電機當前的速度
float Position_now; //小車當前的位置,由當前速度積分得出
float target_speed; //我們需要電機達到的速度,外環的輸出,也就是內環的輸入
float target_position; //我們需要小車達到的位置,由搖桿積分得出
float err_position_now; //當前小車位置的誤差
float err_position_last; //上一次小車位置的誤差
float err_speed_now; //當前速度與電機期望值的差,也就是當前的誤差值
float err_speed_last; //上一次計算的誤差值
float err_speed_i; //積分項,將累計時間內所有的誤差值
float output; //經過PID演算法後輸出的資料,其實是一個控制PWM佔空比的引數,我們只需要直接放在輸出PWM的函式裡就好了
//外環PID
err_position_now = target_position - position_now;
target_speed = Kp_ex *err_position_now;//外環的輸出就是內環的輸入
//速度的限幅處理略
err_position_last = err_position_now;
//內環PID
err_speed_now = target_speed - Motor1_speed;
errspeed__i += err_speed_now;
if(err_speed_i > x) err_speed_i = x;//這裡需要設定一個極限值,以防積分變數溢位,其實一般來說並不會達到這個極限值,因為誤差並不都是正數
//沒有對D進行操作
output = Kp * err_speed_now + Ki * err_speed_i +Kd * (err_speed_now - err_speed_last);//PID公式
if(output > x) output = x;//這裡需要對輸出也進行一個限制,這裡倒不是限制溢位,而是PWM輸出的引數本身就有最大值,超過了就無效了。
TIM_SetCompare1(TIMx, output);//對產生PWM的定時器通道一進行輸出
err_speed_last = err_speed_now;//將使用完的當前誤差值賦給上一次的誤差值,進行下一輪迴圈