2017-08-08 177 views
0

我不知道這個問題是否已經在SO之前問過了,我會繼續並將它發佈在這裏,我試圖用一個PID來解決一個簡單的系統控制器,我的微分方程系統如下。我基本上試圖編寫非常基本的PID算法。我的控制結構u依賴於誤差項的導數和積分。我對衍生術語沒有任何問題,它是在我的代碼中創建問題的積分術語。如果我在開頭 中指定s = 0,並在我的函數中使用它(如我的代碼中所述),該問題就會出現。有沒有辦法繞過它?我試着將s分配給全局變量,但它並沒有解決我的問題。簡而言之,我所做的是 - 我每次都添加狀態x1並乘以dt(用t表示)。Python:如何求解一個具有積分項的常微分方程

enter image description here

請幫我化解這個問題,PFA我的代碼附後。

import numpy as np 
from scipy.integrate import ode 
import matplotlib.pyplot as plt 
plt.style.use('bmh') 

t0=0 
y0=[0.1,0.2] 
kp,kd,ki=2,0.5,0.8 
s,told=0,0 
def pid(t,Y): 
    x1,x2=Y[0],Y[1] 
    e=x1-1 
    de=x2 
    s=(x1+s) 
    integral=s*(t-told) 
    told=t 
    #ie= 
    u=kp*e+kd*de+ki*integral 
    x1dot=x2 
    x2dot=u-5*x1-2*x2 
    return[x1dot,x2dot] 

solver=ode(pid).set_integrator('dopri5',rtol=1e-6,method='bdf',nsteps=1e5,max_step=1e-3) 
solver.set_initial_value(y0,t0) 
t1=10 
dt=5e-3 
sol = [ [yy] for yy in y0 ] 
t=[t0] 
while solver.successful() and solver.t<t1: 
    solver.integrate(solver.t+dt) 
    for k in range(2): sol[k].append(solver.y[k]); 
    t.append(solver.t) 
    print(len(sol[0])) 
    print(len(t)) 
x1=np.array(sol[0]) 
x2=np.array(sol[1]) 
e=x1-1 
de=x2 
u=kp*e+kd*de 
for k in range(2): 
    if k==0: 
     plt.subplot(2,1,k+1) 
     plt.plot(t,sol[k],label='x1') 
     plt.plot(t,sol[k+1],label='x2') 
     plt.legend(loc='lower right') 
    else: 
     plt.subplot(2,1,k+1) 
     plt.plot(t,u) 
plt.show() 
+0

究竟是什麼問題?你描述你試圖做的事情,但不是你確切的問題。也許包括控制檯的一些輸出? – SBylemans

+0

確切的問題是 - 文件「pid。py「,第14行,在pid中 s =(x1 + s) UnboundLocalError:在賦值之前引用的局部變量's' –

+0

問題是在定義中使用s,但這是該函數中的局部變量。不是使用你之前定義的s。 – SBylemans

回答

0

您正在對求解器及其訪問的時間步驟做出假設,但這些步驟並不合理。即使它是數學上的聲音(它應該看起來像integral = integral + e*(t-told),它提供了1階積分方法),但是如果你只是幸運地訂購,那麼你可以減少任何積分方法的順序,可能下降到1 2.

甲數學上正確的方法來實現這種系統是引入第三可變用於e積分,即,的衍生物是e。正確的訂單1系統必須是3維的可以看出這樣一個事實(消除e)您的系統有3個分化/整合操作。隨着你的系統變得

def pid(t,Y): 
    x1, x2, x3 =Y 
    e=x1-1 
    x1dot = x2 
    edot = x1dot 
    x3dot = e 
    u=kp*e+kd*edot+ki*x3 
    x2dot=u-5*x1-2*x2 
    return[x1dot, x2dot, x3dot] 

注意,有必要沒有全局動態變量,只有常數(這也可以作爲參數傳遞,無論似乎更高效或讀取)。

現在您還需要一個初始值,它從系統中看不到整合變量必須是什麼,您的代碼似乎暗示0

+0

非常感謝LutzL!你救了我很多麻煩!所以,如果我們在微分方程中有積分表達式,最好創建一個額外的狀態。 –

+0

是的。原則上,這與通過添加附加狀態變量將二階導數解析爲兩個一階導數相同。 – LutzL

+0

注意並不總是必須完成。如果你使用延遲微分方程(DDE)積分器,那麼你可以使用過去的積分方法將積分逼近到足夠高的階數,儘管我不知道Python中的DDE積分器是否足夠靈活, Julia的可以,而MATLAB的可以但精度低)。當然,哪種方法更好取決於問題。 –

0

首先,您需要將「s」變量包含到pid函數中。

' 高清PID(S,T,Y):... '

0

我現在看到的最簡單的方法是創建stold一個類,這個類的屬性:

class PIDSolver: 
    def __init__(self) 
     self.t0=0 
     self.y0=[0.1,0.2] 
     self.kp,self.kd,self.ki=2,0.5,0.8 
     self.s,self.told=0,0 

    def pid(t,Y): 
     x1,x2=Y[0],Y[1] 
     e=x1-1 
     de=x2 
     self.s=(x1+self.s) 
     integral=self.s*(t-self.told) 
     self.told=t 
     #ie= 
     u=self.kp*e+self.kd*de+self.ki*integral 
     x1dot=x2 
     x2dot=u-5*x1-2*x2 
     return[x1dot,x2dot] 

問題的第一部分。在解決方案的下一部分中使用pidsolver = PIDSolver()

+0

但是,我應該如何通過這個類作爲一個論據來支持scipy.integrate的ode方法?我創建了上面提到的這個類的一個實例,即pidsolver = PIDSolver(),然後在ode的參數中傳遞這個實例,但是我得到了錯誤 - 當我傳遞pidsolver時,它說 - 「_dop.error :處理回調fcn的參數列表失敗。「 –

+0

你需要傳遞一個函數來進行賦值,對吧?因此,將pidsolver.pid作爲參數傳遞給ode – SBylemans

+0

我已經這樣做了,並且引發了另一個錯誤 - 文件「pid.py」,第17行,在pid中 x1,x2 = Y [0],Y [1] TypeError :'float'對象沒有屬性'__getitem__' –

0

我自己通過使用set_f_params()方法和在itz參數中傳遞一個列表來解決這個問題。我還通過了pid()的第三個參數,即pid(t,Y,arg)。最後我分配了s,told=arg[0],arg[1]