Python引力模拟器表现得很奇怪

时间:2013-09-04 17:47:03

标签: python physics gravity

我正在使用Python进行重力模拟(确切地说是使用VPython进行3D渲染)并且我确信代码没有任何问题,但是当两个对象彼此靠近时它会表现得很奇怪。

我的灵感来自http://testtubegames.com/gravity.html。请注意如何放置两个没有速度的行星,它们彼此相向移动,超车,减速并向后转。在我的程序中,它们超车并减速,但只是与距离成比例,所以从技术上来说它无论如何都不应该回头。

我意识到如果r(距离)太接近0,法则f = G *(m1 * m2)/ r ** 2将不起作用,所以我已经包含了一个max-out,所以如果它小于1则设置为1(顺便说一下单位不是像素),但它仍然不起作用。

简单的逻辑也表明对象不应该以这种方式作出反应,因此接下来的事情是我必须遗漏一些东西。

以下是代码摘录:

from visual import *
a = sphere(x=-10,mass=10, vel=vector())
b = sphere(x=10, mass=10, vel=vector())

while 1:
    rate(20)

    #distance between the two objects, a and b, where a.r.mag would be the magnitude of the vector
    a.r = b.pos - a.pos
    b.r = a.pos - b.pos

    a.force = a.r
    if a.r.mag > 1:
        a.force.mag = (a.mass * b.mass) / a.r.mag**2
    else:
        a.force.mag = (a.mass * b.mass) / 1
    a.vel = a.vel + a.force / a.mass


    b.force = b.r
    if b.r.mag > 1:
        b.force.mag = (a.mass * b.mass) / b.r.mag**2
    else:
        b.force.mag = (a.mass * b.mass) / 1
    b.vel = b.vel + b.force / b.mass

    a.pos = a.pos + a.vel
    b.pos = b.pos + b.vel

编辑:代码重写以回应shockburner:

from visual import *
import sys

limit2 = sys.float_info.min
limit = limit2**0.5
timestep = 0.0005

a = sphere(x=-5,mass=10, vel=vector())
b = sphere(x=5, mass=10, vel=vector())

def force(ob1, ob2):
    ob1.r = ob2.pos - ob1.pos
    ob1.force = ob1.r + vector()
    if ob1.r.mag > limit:
        ob1.force.mag = (ob1.mass * ob2.mass) / ob1.r.mag2
    else:
        ob1.force.mag = (ob1.mass * ob2.mass) / limit2
    return ob1.force

while 1:
    rt = int(1/timestep)
    rate(rt)

    a.acc = force(a, b) / a.mass
    b.acc = force(b, a) / b.mass

    a.pos = a.pos + timestep * (a.vel + timestep * a.acc / 2)
    b.pos = b.pos + timestep * (b.vel + timestep * b.acc / 2)

    a.acc1 = force(a,b) / a.mass
    b.acc1 = force(b,a) / b.mass

    a.vel = a.vel + timestep * (a.acc + a.acc1) / 2
    b.vel = b.vel + timestep * (b.acc + b.acc1) / 2

任何有关正确方向的帮助或指针都会非常感激,如果答案结果变得愚蠢(通常情况下就是这种情况),请记住我是个白痴。

2 个答案:

答案 0 :(得分:2)

我的猜测是你的问题源于积分方法中的数值错误。看来你正在使用易于出现大数值误差的Euler方法,因为它是一阶积分方法。我建议velocity verlet用于数值积分轨道,因为它是一种二阶方法,它还能保持机器精度的总能量(动能+重力势)。这种节能通常使得速度verlet比4阶Runge–Kutta更稳定,因为束缚轨道保持束缚。

此外,您可能需要考虑使用动态时间步骤,而不是静态步骤。当粒子闭合在一起时,速度和位置变化更快。因此,为了减少您的数字误差,您需要采取更小的时间步。

最后,我会让你的限制器(if a.r.mag > 1:)尽可能小/实用。我会尝试以下方法:

import sys
limit2 = sys.float_info.min
limit = limit2**.5
...
if a.r.mag > limit:
    a.force.mag = (a.mass * b.mass) / a.r.mag**2
else:
    a.force.mag = (a.mass * b.mass) / limit2
...

答案 1 :(得分:0)

我之前也遇到过这个问题。如果你直接去Runge-Kutta,一切都会自行解决。这个pdf将解释如何合并方法:http://spiff.rit.edu/richmond/nbody/OrbitRungeKutta4.pdf。祝你好运!