這一篇部落格非常重要,因為這篇部落格是為了糾正之前的幾篇關於pid的matlab程式設計的錯誤
錯誤之處:
1.每一次的取樣週期內,也就是每一次的for迴圈內,反饋的訊號在時序上應該是屬於這一次取樣週期的,也就是反饋的訊號應該是屬於本次迴圈。之前的犯的錯誤就是每一次取樣週期(for迴圈)內都是將上一次取樣週期(for迴圈)內的輸出作為反饋訊號然後進行求差值,這是不對的。
在程式上的體現就是:
erro=
y_d(k)
- y(k)//對的
erro=
y_d(k)
- y(k-
1)//錯的
上面那個就是對的,輸入的參考訊號和反饋的訊號在時序上因該是一致的,都屬於當前取樣時刻。而下面那個就是錯的,因為這裡的反饋訊號用的是上乙個取樣週期內的輸出。其實這個問題我最早在學習自控時就想過,只是當時沒管他。
而採用erro=y_d(k) - y(k-1)這種錯的形式就會導致erro這個值慢慢就錯了,相應導致此時的pid輸出錯誤,後面就引起一系列的錯誤。
2.因為1的原因,所以控制開始的時候初始反饋訊號不能像我之前寫的直接令其等於0,而是應該根據物件模型算這個初始反饋訊號,再去求差值。
3.綜上所述,在for迴圈裡面首先應該幹的事情是先根據模型算此時此刻的反饋,然後求差值,然後計算pid輸出。而不是一來就利用上一時刻的輸出來求差值然後計算pid輸出,然後計算模型輸出,之前犯的錯誤就是這麼造成的。
修改的程式應該為:
for k=1:
1:200time
(k)=k*ts;
%儲存時間,用於後面畫圖
y(k)=
-den(2
)*y_1+
num(2)
*pi_out_5;
%控制物件
y_feed=
y(k)
;erro
(k)=
y_d(k)
- y_feed;
erro_middle =
(erro
(k)+ erro_1 )/2
;if m==
0%採用變速積分pid
if(abs
(erro
(k))
)>
1beta
(k)=0;
endif
(abs
(erro
(k))
)<=1&
(abs
(erro
(k))
)>
0.6beta
(k)=(1
-abs
(erro
(k)))/
0.4;
endif
(abs
(erro
(k))
)<=
0.6beta
(k)=1;
end
endif m==
1%不採用積分分離
beta
(k)=1;
enderro_total = erro_total + erro_middle*ts;
pi_out =
erro
(k)*k_p+k_d*
(erro
(k)-erro_1 )
/ts + k_i*
beta
(k)*erro_total;
%對pid的輸出進行限幅
if pi_out>=
10 pi_out =10;
endif pi_out<=-10
pi_out =-10
; end
%更新系統輸出狀態
y_1=
y(k)
;%更新pid輸出狀態
pi_out_5=pi_out_4;
pi_out_4=pi_out_3;
pi_out_3=pi_out_2;
pi_out_2=pi_out_1;
pi_out_1=pi_out;
%更新訊號差值狀態
erro_1=
erro
(k);
end
對比之前的錯誤程式:
for k=1:
1:200time
(k)=k*ts;
%儲存時間,用於後面畫圖
y(k)=
-den(2
)*y_1+
num(2)
*pi_out_5;
%控制物件
y_feed=
y(k)
;erro
(k)=
y_d(k)
- y_feed;
erro_middle =
(erro
(k)+ erro_1 )/2
;if m==
0%採用變速積分pid
if(abs
(erro
(k))
)>
1beta
(k)=0;
endif
(abs
(erro
(k))
)<=1&
(abs
(erro
(k))
)>
0.6beta
(k)=(1
-abs
(erro
(k)))/
0.4;
endif
(abs
(erro
(k))
)<=
0.6beta
(k)=1;
end
endif m==
1%不採用積分分離
beta
(k)=1;
enderro_total = erro_total + erro_middle*ts;
pi_out =
erro
(k)*k_p+k_d*
(erro
(k)-erro_1 )
/ts + k_i*
beta
(k)*erro_total;
%對pid的輸出進行限幅
if pi_out>=
10 pi_out =10;
endif pi_out<=-10
pi_out =-10
; end
y(k)=
-den(2
)*y_1+
num(2)
*pi_out_5;
%控制物件
y_feed=
y(k)
;%更新系統輸出狀態
y_1=
y(k)
;%更新pid輸出狀態
pi_out_5=pi_out_4;
pi_out_4=pi_out_3;
pi_out_3=pi_out_2;
pi_out_2=pi_out_1;
pi_out_1=pi_out;
%更新訊號差值狀態
erro_1=
erro
(k);
end
發現就是下面的這句話順序問題,如果你放在最後,就有問題了,此時你每次for迴圈開始用的y_feed都是上一次for的,而如果你放在開頭,則每次for迴圈開始用的y_feed都是這一次的,就不存在時序問題,所以要注意。
y
(k)=
-den(2
)*y_1+
num(2)
*pi_out_5;
%控制物件
y_feed=
y(k)
;