這是一篇我在學習PID控制算法的過程中的學習記錄。在一開始學習PID的時候,我也看了市面上許多的資料,好的資料固然有,但是更多的是不知所云。(有的是寫的太過深奧,有的則是照搬挪用,對原理則一問三不知)這一直讓我對PID摸不著頭腦。所以我打算從0開始去一層層學習它,直到自己掌握它的精髓。我不認為我有多聰明,所以相當多的部分我都寫的很詳細,覺得過于冗余的小伙伴可以跳著看。
我并不是計算機科學\電控類專業的學生,所以對知識的理解可能有不到位的地方,還請大家指正。
?最后,希望這篇長文對大家有所幫助,這也是我完成它的目標之一。
1.1 單級PID的原理理解
這里強烈建議還不理解PID基本原理的同學觀看視頻:【自動控制原理】12_PID控制器_Matlab/Simulink仿真【開場三分鐘閑話】_嗶哩嗶哩_bilibili
單級PID也就是只使用一個PID控制塊。一般講到PID,大家都喜歡用調節洗澡水的水溫來舉例:
-
Kp(比例項),即希望調節到的溫度與現在的溫度差的越大,我們擰動加熱旋鈕的幅度就越大,這個差值越小,我們擰動加熱旋鈕的幅度就越小;
-
Ki(積分項),即P比例調節控制了一段時間之后,發現實際值與期望值還是有誤差,那就繼續擰懂加熱旋鈕,這是一個在時間上不斷累積的過程,故為積分項;
-
Kd(微分項),當實際值與期望值的誤差的導數,也就是水溫變化的速度,這一項即控制達到期望值,即控制當前水溫到達目標水溫的速度。
然后將上面三項相加,就得到了基本的PID控制:
即下圖
談談我對于PID控制的一些理解:
-
首先面對一個需要PID的控制系統的時候,增大P(比例控制系數)可以使得系統的反應靈敏,但是相應地會無法避免地產生穩態誤差并且單純使用比例控制的系統無法消除這種誤差;
-
在得到一個較為靈敏同時擁有較小的穩態誤差的系統時,這時引入I(積分控制項),積分控制可以有效地消除穩態誤差,但是會使得系統達到穩態的時間延長,也是就是出現了震蕩;
-
在PI控制完成之后,一般來說對于一些對時間不是很敏感的系統都已經擁有較好的控制表現了,對于時間要求苛刻的系統就需要引入D(微分控制項)來進一步縮短系統達到穩態的時間(減少震蕩);
-
D項的一個很不友好的特性就是對環境噪聲極其敏感,引入D之后很容易就會使得先前已經穩定的系統突然變混亂起來,所以通常也需要較多的時間類來獲取試驗D的取值。
以上就是一個簡單的PID控制系統的介紹。從實現來看,其實有著兩種不同的實現方式,一種叫位置式PID,另外一種則為增量式PID。其中位置式PID我較少在平衡控制等項目中見過,而增量式PID則大量運用在電機的控制上(驅動平衡類項目),下面來簡單說一下這兩種PID控制的實現。
1.2 單級PID的代碼實現
其中我們知道PID的公式如下:
但是我們會選用下面這個式子來進行實際的工程運用。可以看到除了積分變為累加之外(畢竟現實中數據都是一段一段不是連續的),D(微分項)中的形式變了,這是為了避免一個被稱為“微分沖擊”的現象,具體的原因在系列3中會有提到,這里可以暫時不去管它。
這里使用的是以tank_like模型(即四個電機的角度固定,靠轉動的差速轉彎的小車模型,與之相對的是Car_like模型,前面兩個輪子可以偏轉一定角度轉彎)的四輪小車為例,需要控制其四輪轉動同步(也就是能走直線)。
tank_like模型與Car_like模型:
為了能夠判斷小車每個輪子的轉動方向以及轉動的幅度,需要使用帶有編碼器的電機,或者使用光電門也是同樣的原理。這樣就能獲取到小車每一個輪子的實時速度值,與目標的速度 作差 后就是這個PID系統的輸入。
? 而小車上單片機向電機輸入的PWM波(可以控制小車電機油門),就這個PID系統的輸出。
如下圖:
//位置式PID
//定義所需要的變量
float Kp, Ki, Kd;
float Motor1_speed;//電機當前的速度
float target_speed;//我們需要電機達到的速度
float err_now;//當前速度與電機期望值的差,也就是當前的誤差值
float err_last;//上一次計算的誤差值
float err_Sum;//積分項,將累計時間內所有的誤差值
float output;//經過PID算法后輸出的數據,其實是一個控制PWM占空比的參數,我們只需要直接放在輸出PWM的函數里就好了//PID算法
err_now = target_speed -Motor1_speed;
err_Sum += err_now;
if(err_Sum > x) err_Sum = x;//這里需要設定一個極限值,以防積分變量溢出,其實一般來說并不會達到這個極限值,因為誤差并不都是正數
//沒有對D進行操作
output = Kp * err_now + Ki * err_Sum +Kd * (err_now - err_last);//PID公式
if(output > x) output = x;//這里需要對輸出也進行一個限制,這里倒不是限制溢出,而是PWM輸出的參數本身就有最大值,超過了就無效了。
TIM_SetCompare1(TIMx, output);//對產生PWM的定時器通道一進行輸出
err_last = err_now;//將使用完的當前誤差值賦給上一次的誤差值,進行下一輪循環
增量式PID的原理就是不直接使用每一次PID產生的輸出,而是將兩次PID產生的輸出相減,得到一個PID增量,再使用這個增量對系統進行輸出。與位置式PID不同的地方就在于因為需要使用到前后兩次的PID公式,那么也就需要當前誤差、上一次誤差與上上次的誤差。在得到輸出的增量之后就類似使用積分項一樣直接將時間上的每一次增量相加,得到的結果在TIM_SetCompare1()中輸出就可以,代碼就不贅述了。