更改时间步长在解决一组常微分方程时为什么会导致结果为零?

huangapple go评论43阅读模式
英文:

Why does changing my time step result in zeros when solving a system of ODEs?

问题

我已正确解决了一组常微分方程(ODEs),以计算给定转向输入的汽车轨迹:

A 是描述车辆参数的矩阵;b 是基于车辆参数的向量;u 是转向输入;V 是速度。参数 AbV 不随时间变化。转向输入(弧度)是时间相关的步进输入,如代码中所示。

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint

# 模型参数
m = 1500 # 质量
I = 2500 # 转动惯量
lf = 1.1
lr = 1.6
Cf = 55000 # 轮胎刚度
Cr = 60000
V = 27.77
A = np.array([[-2*(Cf+Cr)/(m*V), (2*(Cr*lr-Cf*lf)/(m*V**2))-1, 0, 0, 0], [2*(Cr*lr-Cf*lf)/I, -2*((Cf*lf**2)+(Cr*lr**2))/(I*V), 0, 0, 0]])
b = np.array([(2*Cf)/(m*V), (2*Cf*lf)/I])

def func(x, t):
    x1 = x[0]
    x2 = x[1]
    x3 = x[2]
    x4 = x[3]
    x5 = x[4]
    
    if t > 1 and t < 1.5:  # 转向输入
        u = 0.2
    elif t > 3.5 and t < 4:
        u = -0.2
    elif t > 8 and t < 8.5:
        u = -0.2
    elif t > 10.5 and t < 11:
        u = 0.2
    else:
        u = 0

    dx1dt = np.dot(A[0, :], x) + b[0] * u
    dx2dt = np.dot(A[1, :], x) + b[1] * u
    dx3dt = V * np.cos(x5 + x1)
    dx4dt = V * np.sin(x5 + x1)
    dx5dt = x2
    return [dx1dt, dx2dt, dx3dt, dx4dt, dx5dt]

x0 = [0, 0, 0, 0, 0] # 初始条件
t = np.arange(0, 20, 0.005) # 时间步进,从0到20秒,间隔为0.005秒
x = odeint(func, x0, t)

x1 = x[:, 0]
x2 = x[:, 1]
x3 = x[:, 2]
x4 = x[:, 3]
x5 = x[:, 4]

plt.plot(x3, x4) # x3是x位置,x4是y位置
plt.xlim([0, 600])
plt.ylim([0, 40])

现在,根据时间步进的不同,我得到完全不同的结果:

  • 对于0.005和0.08,我得到正确的解决方案。
  • 对于0.03,我得到一个只有零的解决方案。
  • 对于0.08,我得到一个不正确的非零解决方案。

为什么改变时间步进会导致其他变量为零?如何在事先不知道正确解决方案的情况下确定哪些时间步进值提供正确的解决方案?

英文:

I have correctly solved a system of ODEs to calculate a car’s trajectory from a given steering input:

A is a matrix describing the vehicle parameters; b is a vector based on vehicle parameters; u is the steering input; and V is the velocity. The parameters A, b and V are not time dependent. The steering input (radians) is a time-dependent step input as shown in the code.

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
# model parameters
m = 1500 #mass
I = 2500 #moment inertia
lf = 1.1
lr = 1.6
Cf = 55000 #tire stiffness
Cr = 60000
V = 27.77    
A = np.array([[-2*(Cf+Cr)/(m*V), (2*(Cr*lr-Cf*lf)/(m*V**2))-1,0,0,0],[2*(Cr*lr-Cf*lf)/I, -2*((Cf*lf**2)+(Cr*lr**2))/(I*V),0,0,0]])
b = np.array([(2*Cf)/(m*V),(2*Cf*lf)/I])
def func(x,t):
x1 = x[0]
x2 = x[1]
x3 = x[2]
x4 = x[3]
x5 = x[4]
if t&gt;1 and t&lt;1.5:  # steering inputs
u = .2
elif t&gt;3.5 and t&lt;4:
u=-.2
elif t&gt;8 and t&lt;8.5:
u = -.2
elif t&gt;10.5 and t&lt;11:
u = .2
else:
u=0
dx1dt = np.dot(A[0,:],x) +b[0]*u 
dx2dt = np.dot(A[1,:],x) + b[1]*u
dx3dt = V*np.cos(x5+x1)
dx4dt = V*np.sin(x5+x1)
dx5dt = x2
return [dx1dt, dx2dt,dx3dt,dx4dt,dx5dt]
x0 =[0,0,0,0,0] # initial conditions
t = np.arange(0,20,.005) #timestep 0 to 20 seconds at .005sec interval
x = odeint(func,x0,t)
x1 = x[:,0]
x2 = x[:,1]
x3 = x[:,2]
x4 = x[:,3]
x5 = x[:,4]
plt.plot(x3,x4) # x3 is x position, x4 is y position
plt.xlim([0,600])
plt.ylim([0,40])

Now, depending on the timestep, I get totally different results:

  • For 0.005 and 0.08, I get a correct solution.
  • For 0.03, I get a solution that is only zero.
  • For 0.08, I get an incorrect non-zero solution.

Why would changing the timestep result in a zeros for the other variables?
How can one know which timestep values provide a correct solution without knowing the correct solution beforehand?

答案1

得分: 1

以下是翻译好的部分:

  • u 突然在与您的积分时间相比非常小的时间尺度上发生变化。
  • odeintt 参数不是规定强制积分步骤,而是指定解决方案进行评估的时间点。odeint 可能决定采取比 t 中给定的更粗糙的步骤,然后在给定的 t 点上插值结果。但是,t 确实会影响步骤大小的猜测,因此实际采取的步骤。(我不能完全确定,因为我找不到这方面的文档,但这是积分器的常见行为,与您的观察完全吻合。)

结果,odeint 可能完全跳过 u 不同的区间(转向输入),因此您的汽车计算为直行而不是转弯。例如,在这种情况下,您的函数 func 在 1 到 1.5 之间的任何时间都不会被评估,因此 odeint 不可能知道它在该区间内改变了行为。

为了避免这种情况,设置参数 hmax,它控制了最大步长,以便至少有一个步骤必须位于转向区间内:

x = odeint(func, x0, t, hmax=0.49)

自适应积分器将负责进一步按比例减小步骤。

英文:

What you observe is probably due to the following:

  • u suddenly changes on very small time scales compared to your integration time.
  • The t argument of odeint is not prescribing mandatory integration steps, but rather points at which the solution is evaluated. odeint may decide to take coarser steps than given in t and then interpolate the result at the points given in t. However, t does affect the guess of step sizes and thus the steps actually taken. (I am not completely sure about this since I cannot find it documented, but it’s a common behaviour for integrators and perfectly matches your observations.)

As a result, odeint may completely step over the intervals where u is different (steering inputs) and thus your car is computed to drive straight instead of taking a turn. For example in such a case, your function func simply never gets evaluated for any time between 1 and 1.5 and thus odeint cannot possibly know that it changes its behaviour in that interval.

To avoid this, set the parameter hmax which governs the maximum step size such that at least one step has to be within a steering interval:

x = odeint(func,x0,t,hmax=0.49)

The adaptive integrator will then take care to further scale down the steps accordingly.

huangapple
  • 本文由 发表于 2023年2月8日 23:55:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/75388398.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定