从sympy求解二阶微分方程组

时间:2020-04-05 14:32:38

标签: scipy sympy ode differential-equations

我正在使用二阶拉格朗日方程组进行多重自由度动力学问题。我用sympy来了解运动方程。现在,这些方程在计算导数之后变得相当长,尽管sym​​py简化无法进一步简化。我的问题实际上是从这里如何解决这个三阶ode系统。我不知道如何转换这些方程式,因此它们可以与scipy.odeint()一起使用。我想到了替代,但是有很多符号。因此,Im寻找phi0,phi1和phi2以及它们的一阶和二阶导数。初始条件均为phi [0] = 0和所有dphi [0] = 0。 我希望有一种方法可以解决此问题,而不会从头开始。提前致谢。

def derivativeLagranga(Lagrange,n):
"""left side of lagrange"""
f0 = sym.Function('f0')(t)
f1 = sym.Function('f1')(t)
f2 = sym.Function('f2')(t)
f3 = sym.Function('f3')(t)
L_i = []
L_it = []
L_j =[]
L_leva = []
x=0
y=0
for i in range(0,n-1):
    x = Lagrange.diff(kot[i].diff(t))
    L_i.append(x)
for i in range(0,n-1):
    x = L_i[i].diff(t)
    x = x.replace(sym.sin(kot[i]),kot[i])
    L_it.append(x)
for i in range(0,n-1):
    x = L.diff(kot[i])
    L_j.append(x)

for i in range(0,n-1):
    x = L_it[i]+L_j[i]
    L_leva.append(x)



return L_left

left_side_L = derivativeLagranga(Lagrange, n)

f0 = sym.simplify(left_side_L[0].subs(values))
f1 = leva_stran_L[1].subs(values)
f2 = leva_stran_L[2].subs(values)

f0

所以我的f0是方程式之一,我无法复制输出,所以我将发布图片。

54.51345(−(????0(?)+????1(?)−????2(?))sin(?0(?)+?1(?)−?2(?))+2.0?0(?)????0(?))(0.5(????0(?)+????1(?)−????2(?))cos(?0(?)+?1(?)−?2(?))−1.0cos(?0(?))????0(?)−1.0cos(?1(?))????1(?)+1.0cos(?2(?))????2(?))+54.51345((????0(?)+????1(?)−????2(?))cos(?0(?)+?1(?)−?2(?))+cos(?0(?))????0(?))(0.5(????0(?)+????1(?)−????2(?))sin(?0(?)+?1(?)−?2(?))+0.5?0(?)????0(?)+0.5sin(?1(?))????1(?)+1.0sin(?2(?))????2(?))+54.51345(2.0(????0(?)+????1(?)−????2(?))cos(?0(?)+?1(?)−?2(?))+cos(?0(?))????0(?))(1.0(????0(?)+????1(?)−????2(?))sin(?0(?)+?1(?)−?2(?))+0.5?0(?)????0(?)+0.5sin(?1(?))????1(?)+1.0sin(?2(?))????2(?)+0.5sin(?4(?))????4(?))−54.51345(2.0cos(?0(?))????0(?)+1.0cos(?1(?))????1(?))?0(?)????0(?)+54.51345(?0(?)+sin(?0(?)+?1(?)−?2(?)))((0.5????0(?)+0.5????1(?)−0.5????2(?))(????0(?)+????1(?)−????2(?))cos(?0(?)+?1(?)−?2(?))+(0.5?2??2?0(?)+0.5?2??2?1(?)−0.5?2??2?2(?))sin(?0(?)+?1(?)−?2(?))+0.5?0(?)?2??2?0(?)+0.5sin(?1(?))?2??2?1(?)+1.0sin(?2(?))?2??2?2(?)+0.5cos(?0(?))(????0(?))2+0.5cos(?1(?))(????1(?))2+1.0cos(?2(?))(????2(?))2)+54.51345(?0(?)+2.0sin(?0(?)+?1(?)−?2(?)))((????0(?)+????1(?)−????2(?))(1.0????0(?)+1.0????1(?)−1.0????2(?))cos(?0(?)+?1(?)−?2(?))+(1.0?2??2?0(?)+1.0?2??2?1(?)−1.0?2??2?2(?))sin(?0(?)+?1(?)−?2(?))+0.5?0(?)?2??2?0(?)+0.5sin(?1(?))?2??2?1(?)+1.0sin(?2(?))?2??2?2(?)+0.5sin(?4(?))?2??2?4(?)+0.5cos(?0(?))(????0(?))2+0.5cos(?1(?))(????1(?))2+1.0cos(?2(?))(????2(?))2+0.5cos(?4(?))(????4(?))2)+54.51345(cos(?0(?)+?1(?)−?2(?))−2.0cos(?0(?)))(−(0.5????0(?)+0.5????1(?)−0.5????2(?))(????0(?)+????1(?)−????2(?))sin(?0(?)+?1(?)−?2(?))+(0.5?2??2?0(?)+0.5?2??2?1(?)−0.5?2??2?2(?))cos(?0(?)+?1(?)−?2(?))+1.0?0(?)(????0(?))2+1.0sin(?1(?))(????1(?))2−1.0sin(?2(?))(????2(?))2−1.0cos(?0(?))?2??2?0(?)−1.0cos(?1(?))?2??2?1(?)+1.0cos(?2(?))?2??2?2(?))+54.51345(0.5?0(?)????0(?)+0.5sin(?1(?))????1(?)+0.5sin(?2(?))????2(?))cos(?0(?))????0(?)−54.51345(2.0cos(?0(?))????0(?)+2.0cos(?1(?))????1(?)−1.0cos(?2(?))????2(?))?0(?)????0(?)+54.51345(−2.0?0(?)(????0(?))2−1.0sin(?1(?))(????1(?))2+2.0cos(?0(?))?2??2?0(?)+1.0cos(?1(?))?2??2?1(?))cos(?0(?))+54.51345(−2.0?0(?)(????0(?))2−2.0sin(?1(?))(????1(?))2+1.0sin(?2(?))(????2(?))2+2.0cos(?0(?))?2??2?0(?)+2.0cos(?1(?))?2??2?1(?)−1.0cos(?2(?))?2??2?2(?))cos(?0(?))+54.51345(0.5?0(?)?2??2?0(?)+0.5sin(?1(?))?2??2?1(?)+0.5sin(?2(?))?2??2?2(?)+0.5cos(?0(?))(????0(?))2+0.5cos(?1(?))(????1(?))2+0.5cos(?2(?))(????2(?))2)?0(?)−2123.406????0(?)+45.427875?2??2?0(?)−2123.406????1(?)+9.085575?2??2?1(?)−9.085575?2??2?2(?)

然后lambdify输出:

NameError                                 Traceback (most recent call last)
<ipython-input-14-ee077b324a2e> in <module>
      2 en2 = sym.lambdify([kot_0,kot_1,kot_2],f1)
      3 en3 = sym.lambdify([kot_0,kot_1,kot_2],f2)
----> 4 en1(kot_0,kot_1,kot_2)
      5 
      6 

<lambdifygenerated-4> in _lambdifygenerated(_Dummy_227, _Dummy_226, _Dummy_225)
      9   # Derivative
     10   # Derivative
---> 11 22.722525*_Dummy_227**2*Derivative(_Dummy_227, (t, 2)) + 90.8901*_Dummy_227*(-0.5*cos(_Dummy_226)*Derivative(_Dummy_226, t) - 1.0*cos(_Dummy_227)*Derivative(_Dummy_227, t))*Derivative(_Dummy_227, t) + 90.8901*_Dummy_227*(0.5*cos(_Dummy_225)*Derivative(_Dummy_225, t) - 1.0*cos(_Dummy_226)*Derivative(_Dummy_226, t) - 1.0*cos(_Dummy_227)*Derivative(_Dummy_227, t))*Derivative(_Dummy_227, t) - 22.722525*_Dummy_227*(-_Dummy_227*Derivative(_Dummy_227, (t, 2)) - sin(_Dummy_225)*Derivative(_Dummy_225, (t, 2)) - sin(_Dummy_226)*Derivative(_Dummy_226, (t, 2)) - cos(_Dummy_225)*Derivative(_Dummy_225, t)**2 - cos(_Dummy_226)*Derivative(_Dummy_226, t)**2 - cos(_Dummy_227)*Derivative(_Dummy_227, t)**2) + 0.5*Jm*(-2*Derivative(_Dummy_225, (t, 2)) + 2*Derivative(_Dummy_226, (t, 2)) + 2*Derivative(_Dummy_227, (t, 2))) + 1.0*Jm*Derivative(_Dummy_227, (t, 2)) + 45.44505*(-1.0*_Dummy_227 - 2.0*sin(-_Dummy_225 + _Dummy_226 + _Dummy_227))*(-0.5*_Dummy_227*Derivative(_Dummy_227, (t, 2)) + (-Derivative(_Dummy_225, t) + Derivative(_Dummy_226, t) + Derivative(_Dummy_227, t))*(1.0*Derivative(_Dummy_225, t) - 1.0*Derivative(_Dummy_226, t) - 1.0*Derivative(_Dummy_227, t))*cos(-_Dummy_225 + _Dummy_226 + _Dummy_227) + (1.0*Derivative(_Dummy_225, (t, 2)) - 1.0*Derivative(_Dummy_226, (t, 2)) - 1.0*Derivative(_Dummy_227, (t, 2)))*sin(-_Dummy_225 + _Dummy_226 + _Dummy_227) - 1.0*sin(_Dummy_225)*Derivative(_Dummy_225, (t, 2)) - 0.5*sin(_Dummy_226)*Derivative(_Dummy_226, (t, 2)) - 0.5*sin(varphi_4(t))*Der

https://imgur.com/a/2UOW0NR

EDIT2:因此,经过一番简化,我知道了如何得到3个常微分方程。但是它们是sympy形式,我该如何用数值方法解决它们?

−800000.0?0+800000.0?2−1770.174?˙0+242.3736?¨0−1770.174?˙1+166.63185?¨1−75.74175?¨2+4245.8661 

−1200000.0?1+400000.0?2−1770.174?˙0+166.63185?¨0−1770.174?˙1+151.4835?¨1−75.74175?¨2+2830.5774 

800000.0?0+400000.0?1−2000000.0?2−75.74175?¨0−75.74175?¨1+60.5934?¨2−1415.2887

1 个答案:

答案 0 :(得分:0)

我将以双摆为例介绍Euler-Lagrange形式主义的执行,将其作为非平凡,完整的标准示例,并且在不使用sympy.physics中的专用功能的情况下,仅使用基本微分和sympy的代码编写工具。希望它足够通用,因为第二部分应该独立于问题,因此也可以直接应用于您的情况。

from sympy import sin, cos, Symbol, symbols, solve
from sympy.utilities.lambdify import lambdify

物理模型的拉格朗日

首先使用两个角度作为主要相关函数对拉格朗日算子进行物理设置,并通过笛卡尔坐标构造动能和势能。

# Symbols for the parameters of the problem
t,m1,m2,l1,l2,g = symbols("t,m_1 m_2 l_1 l_2 g")
# Variables of the problem
th1, th2 = Function("θ_1")(t), Function("θ_2")(t)
x1,y1 = l1*sin(th1), -l1*cos(th1)
x2,y2 = x1+l2*sin(th2), y1-l2*cos(th2)

# kinetic energy
vx1,vy1,vx2,vy2 = ( xx.diff(t) for xx in (x1,y1,x2,y2))
K1 = m1/2*(vx1**2+vy1**2)
K2 = m2/2*(vx2**2+vy2**2)
K = K1+K2
# potential energy
V = g*(m1*y1+m2*y2)
# Lagrangian
L = K - V
L = L.expand().simplify()

要获得抽象处理,请使用抽象参数数组和坐标向量

params = [m1, l1, m2, l2, g]
q = [th1, th2]
dotq = [ qq.diff(t) for qq in q]

从Euler-Lagrange到一阶ODE系统

如果使用角度的二阶导数,结果表达式将变得很混乱,人们会使用更结构化的方法,并希望使用冲量变量进行相应的更快的评估

pk = diff(L, dotqk)
d(pk)/dt = diff(L, qk)

其中第一个关系被视为方程式系统,用于根据dotq计算p

准备新变量,准备用简单变量替换功能符号。

N = len(q)
p = [ Symbol(f"p_{k+1}") for k in range(N)]
dotq_func, dotq = dotq,  [ Symbol(f"Dq_{k+1}") for k in range(N)]
q_func, q = q, [ Symbol(f"q_{k+1}") for k in range(N)]

现在用简单变量替换所有函数项

L=L.subs(list(zip(dotq_func, dotq))).subs(list(zip(q_func, q)))

enter image description here

现在建立从dotq计算q,p的函数

p_eqns = [ p[k] - L.diff(dotq[k]) for k in range(N)]
dotq_expr = solve(p_eqns, dotq)
dotq_func = lambdify([*q, *p, *params],[ dotq_expr[dq] for dq in dotq])

接下来生成一个计算p的导数的函数

dotp_expr = [ L.diff(q[k]) for k in range(N)]
dotp_func = lambdify([*q,*dotq,*params],dotp_expr)

现在将生成的函数组装成一个完整的ODE函数,并解决一个测试问题以确认其正常工作

def odefunc(t,u,args):
    q, p = u[:N], u[N:]
    dotq = dotq_func(*q, *p, *args)
    dotp = dotp_func(*q, *dotq, *args)
    return [*dotq, *dotp]

通过数值解确定

myparams = [1,10,5,5,9.81]
t = np.linspace(0,25,301)

u = odeint(odefunc,[2,1.2, 0,0],t,args=(myparams,), tfirst=True)

%matplotlib inline
plt.figure(figsize=(8,5))
plt.plot(t,u[:,0],t,u[:,1]); 
plt.grid(); plt.show()

这将产生以下角度函数图 enter image description here

Outlook

如果动力学项(在经典力学中通常如此)保证在速度上是二次方的,则可以预期会有更好的性能。然后,通过直接提取这种二次形式的矩阵,可以将由脉冲到速度的转换中的系统解委托给数值线性系统解算器,而不必保留矩阵求逆的符号表达式。这些可能在较大的尺寸上很大。