代码之家  ›  专栏  ›  技术社区  ›  user2747808

Python重力模拟器表现怪异

  •  1
  • user2747808  · 技术社区  · 13 年前

    我正在用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
    

    编辑:针对冲击燃烧器重新编写的代码:

    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 回复  |  直到 13 年前
        1
  •  2
  •   shockburner    13 年前

    我的猜测是,你的问题源于积分方法中的数值错误。看起来你使用的是欧拉方法,因为它是一阶积分方法,所以容易出现较大的数值误差。我会推荐 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
    ...
    
        2
  •  0
  •   Geek34    13 年前

    我以前也遇到过这个问题。如果你直接去龙格库塔,一切都会自行解决的。本pdf将解释如何结合该方法: http://spiff.rit.edu/richmond/nbody/OrbitRungeKutta4.pdf 祝你好运

    推荐文章