10

重要提示:这个问题与“PhysX”完全没有关系,它是一个计算机游戏物理系统(对球类游戏等街机游戏中的物理很有用);PhysX 是 Unity3D 和其他游戏引擎内置的系统;PhysX 在这里完全无关紧要。

/////////////////更新(先读底部)//////////////////

我一直在记录值并搜索确切的问题所在,我想我找到了。我的代码中有这样的东西

Velocity += Acceleration * Time.deltaTime;
position += Velocity * Time.deltaTime;

现在的加速度大约是 0,0000000000000009..。随着代码的流动,速度应该会增加,浮动没有问题。但在开始时,地球的初始位置是 (0,0,23500f) 你可以在我最后给出的图表中看到这一点。

现在,当我将 speed * timedelta (此时类似于 0,00000000000000005)添加到 23500 的位置时,它基本上不会添加它。位置仍然是 (0, 0, 23500) 而不是 (0,0, 23500.00000000000005),因此地球不会移动,因此加速度不会改变。

如果我将地球的初始位置设置为 0,0,0 并且仍然将加速度设置为 0.0000000000000000009 以假设它的位置是(0,0,23500)然后“添加”速度* timedelta。它变得类似于 (0,0,000000000000000000005) 并保持增加。当 float 为 0 时,添加这么小的值是没有问题的。但如果浮点数类似于 23500,则它不会将小值相加。

我不知道这究竟是统一的问题还是c#的浮动。

这就是为什么我不能让它与小值一起工作。如果我能克服这一点,我的问题就会得到解决。

///////////////////////////////////////// /////////////////////////

我一直在开发 n 体物理学来模拟我们的太阳系,所以我一直在收集数据以使其尽可能逼真。但是数据大小存在问题。我搜索了互联网的每一点,我找不到人们如何克服这一点的单一解释。(如果他们是的话)所以我在这里尝试我的镜头。

因此,为了保持行星之间的距离、半径和“质量”的比率固定,我创建了一个 Excel 文件来计算所有数据。(因为为什么有人会在互联网上发布“如果地球上有“那个”半径图的话,地球的质量会是多少?)我会把 ss 作为附件。它基本上将行星的每个属性“标准化”或换句话说“缩放”到给定的参考。在这种情况下,我将参考作为“地球的半径”。

我在团结中工作,你知道,你不能在团结中使用“太大”或“太小”的价值观。所以我不得不把太阳系缩小,“很多!”

所以我使用牛顿万有引力定律,即 F = GMm/r^2,为了简单起见,我直接从所有其他物体中计算给定物体的 a = GM/r^2。

因此,地球“朝向太阳”的重力加速度的实际值大约为 0,000006 km/s^2,这对于统一使用来说是非常小的值,但它可以工作。然而,要获得这个值,1 我需要将地球的半径(比例)设置为 6371 单位,将太阳的比例设置为 696,342!,这太大而无法统一渲染。

所以我说,让地球的半径为 1,以单位为单位。所以,当半径发生变化时,一切都会发生变化,质量、距离……我保持了行星的密度,并从新的体积和新的半径计算质量。所有的计算都在附件中。

所以问题是,当我将地球的半径设为 1 时,对太阳的引力加速度变成了 0,0000000000009 之类的东西,这非常小。当然,Unity 不适用于该值。

所以,如果我改为增加地球的半径,那么太阳的质量和半径就会变得大得离谱,然后我就无法使用它。

我不知道其他人是如何解决这个问题的,他们做了什么来克服这个问题,但正如我从这里看到的,看起来不可能对太阳系进行逼真的 n 体模拟。(至少统一)

所以我需要有 10 个代表来发布图片-_-,我会提供链接。 http://berkaydursun.com/solar_system_simulator/data.PNG 另外一个目录是具有 n 体计算但具有 UNREALISTIC 值的工作实验太阳系模拟。它工作得很好,甚至看起来有点接近真实,但是不,它没有正确的比率^^如果你愿意,你可以在这里测试它http://berkaydursun.com/solar_system_simulator/

编辑:哇,我几乎每个段落都以“So”开头^^

4

2 回答 2

16

我也做过程序 sol 系统模拟,所以这里是我的见解:

  1. 渲染

    我使用1 :1缩放的OpenGL 。所有单位都是SI所以[m,s,kg,...]。问题从Z-buffer开始。通常的Z 缓冲区位宽远不及您所需要的。我从0.1m 渲染到 1000 AU那么如何克服这个问题?16/24/32 bit

    我确实通过一次结合Z 排序Z 缓冲(Z 排序是必要的,因为透明环......和其他效果)渲染 3 个截锥体来管理它。所以首先我将最远的部分渲染到zfar=1000AU. 天穹投影在z=750AU远处,然后清除Z 缓冲区并将对象渲染到zfar=0.1AU. 然后再次清除Z 缓冲区并将近距离物体渲染到zfar=100000 m.

    要完成这项工作,您必须拥有尽可能精确的投影矩阵。有不精确的gluPerspective余切,所以它需要修复相关元素(让我很长时间才能发现)。Z near值取决于Z 缓冲区位宽。如果编码正确,那么即使使用 zoom 也能很好地工作10000x。我使用这个程序作为我的望远镜对象的导航/搜索器:) 从我的家庭视图实时。我结合了 3D 星星、天体、船只、真实地面(通过 DTM 和卫星纹理)。它甚至能够输出红青色浮雕:)。可以从表面、大气、太空……(不仅仅是锁定到地球)渲染。没有其他第 3 方库,则使用 OpenGL。这是它的样子:

    图像 图像 图像 图像 图像

    如您所见,它在任何高度都可以正常工作或缩放大气,就像这种大气散射着色器一样

  2. 模拟

    我没有使用n 体重力模拟,因为为此您需要大量很难获得的数据(而且几乎不可能达到所需的精度)。计算必须非常精确

    我改用开普勒方程,所以看看这些:

    如果您仍想使用重力模型,请使用NASAJPL 视野。我认为他们在那里也有 C/C++ 的源代码,但他们使用的参考框架与我的地图不兼容,所以它对我来说是不可用的。

    一般来说,开普勒方程的误差较大,但不会随时间增加太多。重力模型更精确,但它的误差随着时间的推移而上升,您需要不断更新天文体数据才能使其工作......

[edit1] 积分精度

您当前的实现是这样的:

// object variables
double  acc[3],vel[3],pos[3];
// timer iteration
double dt=timer.interval;
for (int i=0;i<3;i++)
 {
 vel[i]+=acc[i]*dt;
 pos[i]+=vel[i]*dt;
 }

问题是当您添加非常小和非常大的值时,它们会在添加之前转移到相同的指数,这将舍入重要数据为避免这种情况,只需将其更改为:

// object variables
double          vel0[3],pos0[3]; // low
double          vel1[3],pos1[3]; // high
double  acc [3],vel [3],pos [3]; // full
// timer iteration
double dt =timer.interval;
double max=10.0; // precision range constant
for (int i=0;i<3;i++)
 {
 vel0[i]+=acc[i]*dt; if (fabs(vel0[i]>=max)) { vel1[i]+=vel0[i]; vel0[i]=0.0; } vel[i]=vel0[i]+vel1[i];
 pos0[i]+=vel[i]*dt; if (fabs(pos0[i]>=max)) { pos1[i]+=pos0[i]; pos0[i]=0.0; } pos[i]=pos0[i]+pos1[i];
 }

现在xxx0被集成到max并且整个东西被添加到xxx1

四舍五入仍然存在,但不再是累积的。您必须选择max集成本身是安全的并且添加xxx0+xxx1必须是安全的值。因此,如果数字对于一次拆分而言差异太大,则拆分两次或更多...

  • 像:xxx0+=yyy*dt; if (fabs(xxx0>max0))... if (fabs(xxx1>max1))...

然而,这并没有充分利用添加变量的动态范围来充分发挥其潜力。有这样的替代方案:

[Edit2] 星星

[Edit3] 进一步提高 Newton D'ALembert 积分精度

迭代积分的基本问题是,基于当前身体位置应用基于重力的加速度将导致更大的轨道,因为在积分步骤期间dt,位置会发生一些变化,这在天真的积分中没有考虑到。为了解决这个问题,看看这张照片:

一体化

假设我们的身体处于圆形轨道并处于 0 度位置。我没有使用基于当前位置的加速度方向,而是使用 position after 0.5*dt。这增加了小一点的加速度,从而导致更高的精度(对应于开普勒轨道)。通过这个调整,我能够成功地将 Kepler 轨道转换为 Newton D'Alembert for 2 body system。(为 n-body 执行此操作是下一步)。只有不受潮汐效应和/或卫星影响的 2 个天体系统才能与我们太阳系的真实数据进行粗略相关。要构建自己的虚构数据,您可以使用开普勒圆形轨道和三足平衡重力:

G = 6.67384e-11;
v = sqrt(G*M/a);                           // orbital speed
T = sqrt((4.0*M_PI*M_PI*a*a*a)/(G*(m+M))); // orbital period

其中a是圆形轨道半径m是身体质量 ,M是焦点身体质量(太阳)。为了保持可接受公差的精度(对我来说),集成步骤dt应该是:

dt = 0.000001*T

因此,要放置新的身体进行测试,只需将其放在:

pos = (a,0,0)
vel = (0,sqrt(G*M/a),0)

当主焦体(太阳)位于:

pos = (0,0,0)
vel = (0,0,0)

这会将您的身体置于圆形轨道上,因此您可以比较 Kepler 与 Newton D'Alembert 来评估您的模拟精度。

于 2015-01-19T08:58:50.030 回答
0

正如您所发现的,缩小规模不一定有帮助。以下是一些关于使用浮点数时要考虑的事项的好读物:http: //docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

基本上从第一原理(牛顿定律)进行模拟对数值准确性不利,因为您不会在数值方法中灌输重要影响的规模,因此您最终会在不同的规模上抛出一大堆不同的影响在一起,结果是精度低。

通常情况下,行星、卫星等的星历表不是从牛顿定律开始的,它们首先假设轨道是开普勒的,然后是小的微扰校正。

这是一个计算行星位置的算法(半帝国)。 http://ssd.jpl.nasa.gov/txt/aprx_pos_planets.pdf

如果您想进行 N 体模拟,您似乎需要更高的精度。如果统一阻止您使用双精度,那么我建议在纯 C# 中进行计算,然后在工作完成后转换为单精度。

于 2015-01-18T22:53:49.967 回答