我正在使用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
任何有关正确方向的帮助或指针都会非常感激,如果答案结果变得愚蠢(通常情况下就是这种情况),请记住我是个白痴。
答案 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。祝你好运!