Python 用迭代法估计弹簧上的速度

Python 用迭代法估计弹簧上的速度,python,math,physics,numerical-methods,differential-equations,Python,Math,Physics,Numerical Methods,Differential Equations,问题是: 考虑一个具有质量和弹簧的系统,如图所示。弹簧的刚度和物体的质量是已知的。因此,如果弹簧被拉伸,弹簧施加的力可以计算出来,瞬时加速度可以根据牛顿运动定律估算出来。将加速度积分两次,得到弹簧移动的距离,并从初始长度中减去该距离,得到一个新位置,以计算加速度并再次开始循环。因此,当加速度线性下降时,速度在某个值上趋于平稳,在该点之后的所有情况下,弹簧压缩和减速被忽略 我的问题是如何用python编写代码。到目前为止,我已经编写了一些伪代码 instantaneous_acceleration

问题是:

考虑一个具有质量和弹簧的系统,如图所示。弹簧的刚度和物体的质量是已知的。因此,如果弹簧被拉伸,弹簧施加的力可以计算出来,瞬时加速度可以根据牛顿运动定律估算出来。将加速度积分两次,得到弹簧移动的距离,并从初始长度中减去该距离,得到一个新位置,以计算加速度并再次开始循环。因此,当加速度线性下降时,速度在某个值上趋于平稳,在该点之后的所有情况下,弹簧压缩和减速被忽略

我的问题是如何用python编写代码。到目前为止,我已经编写了一些伪代码

instantaneous_acceleration = lambda x: 5*x/10    # a = kx/m
delta_time = 0.01     #10 milliseconds
a[0] = instantaneous_acceleration(12)     #initial acceleration when stretched to 12 m
v[0] = 0        #initial velocity 0 m/s
s[0] = 12       #initial length 12 m
i = 1
while a[i] > 12:
    v[i] = a[i-1]*delta_time + v[i-1]      #calculate the next velocity
    s[i] = v[i]*delta_time + s[i-1]        #calculate the next position
    a[i] = instantaneous_acceleration (s[i])          #use the position to derive the new accleration
    i = i + 1

非常感谢您提供的任何帮助或提示。

如果您要提前积分-这是一个好主意,而且绝对是可行的方法-那么您可以将方程作为t的函数写下来:

x'' = -kx/m
x'' + (k/m)x = 0
r^2 + k/m = 0
r^2 = -(k/m)
r = i*sqrt(k/m)
x(t) = A*e^(i*sqrt(k/m)t)
     = A*cos(sqrt(k/m)t + B) + i*A*sin(sqrt(k/m)t + B)
     = A*cos(sqrt(k/m)t + B)
从初始条件我们知道

x(0) = 12 = A*cos(B)
v(0) = 0 = -sqrt(k/m)*A*sin(B)
只有当我们选择A=0或B=0或B=Pi时,这些方程中的第二个才是真的

  • 如果A=0,则第一个方程没有解
  • 如果B=0,则第一个方程的解A=12
  • 如果B=Pi,第一个方程的解A=-12
我们可能更喜欢B=0和A=12。这给

x(t) =  12*cos(sqrt(k/m)t)
v(t) = -12*sqrt(k/m)*sin(sqrt(k/m)t)
a(t) = -12*(k/m)cos(sqrt(k/m)t)
因此,在任何增量时间t[n+1]=t[n]+dt,我们可以简单地计算t[n]的精确位置、速度和加速度,而不会累积任何漂移或误差

综上所述,如果你对给定一个任意常微分方程的x(t)、v(t)和a(t)的数值计算感兴趣,那么答案就难多了。有很多很好的方法可以被称为数值积分。欧拉方法是最简单的:

// initial conditions

        t[0] = 0
        x[0] = …
       x'[0] = …
         …
  x^(n-1)[0] = …
    x^(n)[0] = 0


// iterative step

  x^(n)[k+1] = f(x^(n-1)[k], …, x'[k], x[k], t[k])
x^(n-1)[k+1] = x^(n-1)[k] + dt * x^(n)[k]
             …
     x'[k+1] = x'[k] + dt * x''[k]
      x[k+1] = x[k] + dt * x'[k]
      t[k+1] = t[k] + dt
选择的dt值越小,运行固定时间所需的时间就越长,但得到的结果越精确。这基本上是对函数及其所有导数进行黎曼和,直到ODE中涉及的最高导数

更准确的版本是辛普森规则,它做的是相同的事情,但取最后一个时间量的平均值(而不是任一端点的值;上面的示例使用间隔的开始)。保证区间内的平均值比任一端点更接近区间内的真值(除非函数在该区间内为常数,在这种情况下,辛普森至少也一样好)

对于常微分方程来说,最好的标准数值积分方法(假设不需要像蛙跳法这样的方法来提高稳定性)可能是龙格-库塔法。一个足够阶数的自适应时间步长龙格库塔方法通常可以做到这一点,并给出准确的答案。不幸的是,解释Runge-Kutta方法的数学可能过于先进和耗时,无法在这里介绍,但您可以在网上或数字配方(Numeric Recipes)中找到有关这些和其他先进技术的信息,这是一系列关于数字方法的书籍,其中包含许多非常有用的代码示例


即使是龙格-库塔方法也基本上是通过在时间量子内对函数值的精确猜测来工作的。他们只是用更复杂的方法来做,这可以证明减少了每一步的误差。

分析方法是获得服从胡克定律的简单系统速度的最简单方法

然而,如果你想要一个物理上精确的数值/迭代方法,我强烈建议不要使用标准的Euler或runge-kutta方法(Patrick87建议)。[更正:如果加速度项的符号已更正,则OPs方法为辛一阶方法。]

您可能希望使用哈密顿方法和合适的辛积分器,如二阶蛙跳(Patrick87也建议)

对于胡克定律,可以表示哈密顿量H=T(p)+V(q),其中p是动量(与速度相关),q是位置(与弦离平衡的距离相关)

有动能T和势能V

T(p)=0.5*p^2/m

V(q)=0.5*k*q^2

您只需要这两个表达式的导数来模拟系统

dT/dp=p/m

dV/dq=k*q

我提供了一个详细的示例(尽管是针对另一个二维系统),其中包括一阶和四阶方法的实现: 在方法0和方法1下


这些是辛积分器,具有能量保持特性,这意味着您可以在没有耗散误差的情况下执行长时间模拟。

力中存在符号误差,对于弹簧或任何其他振动,它应始终与激励方向相反。纠正这一点会立即产生振荡。然而,您的循环条件现在将永远不会得到满足,因此您还必须进行调整

通过将方法从当前的辛欧拉方法提升到蛙跳Verlet,可以立即提高方法的阶数。您只需将
v[i]
的解释更改为
t[i]-dt/2
处的速度。然后,第一次更新使用<代码> T [I-1 ] <代码>中的加速度,用中点公式计算<代码> t[i-1 ] +dt/ 2=t[i] -dt/ 2</代码>的速度。然后在下一行中,位置更新是一个类似的中点公式,使用位置时间之间中间时间的速度。为了获得这一优势,您只需在代码中进行更改,就可以在
t[0]-dt/2时使用
t[0]
处的泰勒展开将初始速度设置为1

瞬时加速度=λx:-5*x/10#a=kx/m delta#u时间=0.01#10毫秒 s0
w=0.5**0.5; dt=delta_time;
fig, ax = plt.subplots(3,1,figsize=(5,3*1.5))
for g, y in zip(ax,(s-12*np.cos(w*t),v+12*w*np.sin(w*(t-dt/2)),a+12*w**2*np.cos(w*t))): 
    g.plot(t,y); g.grid();
plt.tight_layout(); plt.show();