odeint对于包含降级功能的ODE返回错误结果

时间:2019-02-19 13:07:57

标签: python scipy ode odeint

我正在尝试对ODE建模:

  

enter image description here

我实现了:

import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
m = 1
k = 1
M = 0.1
b = 1
Fmax = 1
def dXdt(X,t):
    return [X[1], - b * X[1] / m - k * X[0] / m - M * np.sign(X[1]) / m + Fmax / m ]

X0 = [1, 2]
ts = np.linspace(0, 10, 200)
Xs = odeint(dXdt, X0, ts)
plt.plot(ts, Xs[:, 0])

结果:

  

enter image description here

这与我从Modelica(OpenModelica)获得的内容相矛盾:

model model1
//constants
  parameter Real m = 1;
  parameter Real k = 1;
  parameter Real b = 1;
  parameter Real M = 0.1;
  parameter Real Fmax = 1;
//variables
  Real x, v, a;
initial equation
  x = 1;
  v = 2;
equation
  v = der(x);
  a = der(v);
  m * a + b * v + k * x + M * sign(v) = Fmax;
end model1;
  

enter image description here

如果您能帮助我知道我的错误在哪里以及如何解决,我将不胜感激。

1 个答案:

答案 0 :(得分:1)

根据x'的符号,您的系统具有3个平滑子系统或阶段。只要积分停留在这些平稳阶段内,步长控制器就会按预期工作。但是,在相位变化的时刻,步长控制器会看到巨大的变化和其用于调整步长的数量的波动,从而需要减小步长。

接下来是odeintlsode中的方法是隐式的,并且隐式方法的假设是等式的右边再次足够微分,至少一次。隐式求解器无法到达任何地方,您可以在峰值中观察到。


一种解决方案是,通过使每个阶段都超出其边界来消除不连续性,并使用ODE求解器的事件机制来找到跨越边界的点。为此,引入符号/相位选择器参数S并求解系统

m*x''+b*x'+k*x+M*s = F

使用函数e(t)=x'(t)作为事件函数。

# Define differential equation
m,b,k,M,F = 1., 1., 1., 0.1, 1.
def fun(t, x, S):
    dx = [x[1], (F-b*x[1]-k*x[0]-M*S)/m]
    return np.asarray(dx)

# Define event function and make it a terminal event
def event(t, x):
    return x[1]
event.terminal = True

t = 0
x = [1.,2.]; 
S = np.sign(u[1]);
tend = 10

由于我们需要在事件位置更改相位选择器,因此事件的方式必须为terminal。然后循环遍历各个阶段,并将它们组合成一个整体解决方案,就像chthonicdaemon对问题“ How to use if statement in a differential equation (SciPy)?”的回答一样。

要获得确定的行为,请确保在每个事件中都跨越每个事件的相位边界(如果加速度非零(并且几乎总是非零))。

ts = []
xs = []
eps=1e-8
for _ in range(50):
    sol = solve_ivp(lambda t,u:fun(t,u,S), (t, tend), x, events=event, atol=1e-12, rtol=1e-8, max_step=0.01);
    ts.append(sol.t)
    xs.append(sol.y)
    if sol.status == 1: # Event was hit
        # New start time for integration
        t = sol.t[-1]
        # Reset initial state
        x = sol.y[:, -1].copy()
        # ensure the crossing of the phase boundary
        dx = fun(t,x,S)
        dt = -(eps*S+x[1])/dx[1]; # should be minimal
        if dt > 0: t += dt; x += dt*dx;
        # new phase parameter
        S = np.sign(x[1]);
        # stop the iteration if it stalls 
        if t-sol.t[0] <5e-12: break
    else:
        break

# We have to stitch together the separate simulation results for plotting
t=np.concatenate(ts);
x=np.concatenate(xs, axis=1);

然后绘制解决方案,例如如下所示。集成停滞在t=4.7880468x=0.9453532之间。在x'=0上的这一点附近,x''=-0.0453532的加速度为x'x''=0.15464678的加速度为x',轻微负x''=0.05464678x'=0 }。没有平衡的立场,也没有及时前进的方法。由于强制穿过边界,因此在数值计算中动力学可以及时进行,但是eps的大小越小,该振荡的幅度越小,因此波长也越小。如果振荡波长太小,积分环路中的最后一个条件将结束积分(eps要小得多)。

enter image description here