是否有可能在大小和质量方面进行逼真的 n 体太阳系模拟?
Is it possible to make realistic n-body solar system simulation in matter of size and mass?
重要说明:这个问题 与 "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)
然后它 "ADDS" 速度 * 时间增量。
它变成类似(0,0,000000000000000000005)的东西并保持增加。当float为0时,加上这么小的值是没有问题的。但如果浮点数类似于 23500,那么它不会将小值相加。
不知道是unity的问题还是c#的float问题
这就是为什么我不能让它使用小值的原因。如果我能克服这个问题,我的问题就迎刃而解了。
////////////////////////////////////////// ////////////////////////////////
我一直在开发 n 体物理学来模拟我们的太阳系,所以我一直在收集周围的数据以使其尽可能逼真。但是数据大小有问题。我搜索了互联网的每一点,我找不到一个单一的解释人们是如何克服这个问题的。 (如果他们是的话)所以我在这里尝试拍摄。
因此,为了保持行星之间的距离、半径和 "mass" 的比例不变,我创建了一个 excel 文件来计算所有数据。 (因为为什么会有人把 "what would be the earth's mass if it had " 那个“半径图”放到互联网上?)
我会把ss作为附件。它基本上 "normalizes" 或换句话说 "scales" 每个 属性 行星的给定参考。在这种情况下,我将参考设为 "earth's radius."
我在 unity 中工作,你知道,你不能在 unity 中使用 "too big" 或 "too small" 值。所以我不得不缩小太阳系,"a lot!"
所以我用牛顿万有引力定律,就是F = GMm/r^2,
为简单起见,我直接从所有其他物体计算给定物体的 a = GM/r^2。
因此,地球重力加速度的实际值 "towards sun" 大约为 0,000006 km/s^2,这在统一处理时甚至是非常小的值,但它可以工作。然而,要获得这个值,1 我需要将地球的半径(比例)设置为 6371 个单位,将太阳的比例设置为 696,342!,这太大了,无法统一渲染。
所以我说,让地球的半径为1,单位为单位。
所以,当半径改变时,一切都会改变,质量,距离......
我保留了行星的密度,并根据新体积和新半径计算质量。
所有的计算都在附件中
所以事情是,当我将地球的半径设为 1 时,对太阳的引力加速度大约为 0,0000000000009
这是可笑的小。当然,Unity 不适用于该值。
所以,如果我改为增加地球的半径,那么太阳的质量和半径会变得非常大,然后我又无法使用它。
我不知道其他人是如何解决这个问题的,他们做了什么来克服这个问题,但正如我从这里看到的那样,看起来不可能对太阳系进行逼真的 n 体模拟。 (至少统一)
所以我需要有 10 个代表 post 图片 -_-,我会给 link 代替。
http://berkaydursun.com/solar_system_simulator/data.PNG
还有一个目录是使用 n 体计算但具有 UNREALISTIC 值的工作实验太阳系模拟。
它工作得很好,它甚至看起来有点接近真实,但不,它没有正确的比例 ^^
如果你愿意,可以在这里测试 http://berkaydursun.com/solar_system_simulator/
编辑:WoW 我几乎每段都以 "So" ^^
开头
正如您所发现的那样,缩小规模不一定会有帮助。以下是一些关于使用浮点数时需要考虑的事项的好读物:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
基本上,从第一原理(牛顿定律)进行模拟对数值精度不利,因为你没有将重要影响的规模概念灌输到数值方法中,所以你最终会抛出一大堆不同尺度下的不同效果在一起,结果精度低。
通常像行星、卫星等的星历表不以牛顿定律开始,它们首先假设轨道是开普勒轨道,然后进行小的微扰修正。
这是一个计算行星位置的算法(半帝国)。
http://ssd.jpl.nasa.gov/txt/aprx_pos_planets.pdf
如果你想做一个 N 体模拟,你似乎需要更高的精度。如果 unity 阻止您使用双精度,那么我建议在纯 C# 中进行计算,然后在工作完成后转换为单精度。
我也编写了 sol 系统模拟程序,所以这是我的见解:
正在渲染
我使用 OpenGL,缩放比例为 1:1。所有单位都是 SI,所以 [m,s,kg,...]。问题从 Z-buffer 开始。通常的 Z-buffer 位宽是 16/24/32 bit
,这远不是您需要的。我正在从 0.1m 渲染到 1000 AU 那么如何克服这个问题?
我确实通过结合 Z-sorting 和 Z-buffering( Z-sort 是必要的,因为透明环......和其他效果)。所以首先我渲染最远的部分直到 zfar=1000AU
。天穹投影在 z=750AU
距离处,然后清除 Z-buffer 并渲染 objects 到 zfar=0.1AU
。然后再次清除 Z-buffer 并渲染接近 objects 直到 zfar=100000 m
.
要完成这项工作,您必须拥有尽可能精确的投影矩阵。 The gluPerspective
has unprecise cotangens 所以它需要修复相关元素(让我很长时间才能发现)。 Z near
值取决于 Z-buffer 位宽。如果编码正确,那么即使使用缩放 10000x
也能很好地工作。我将此程序用作我的 telescope 的 navigation/searcher 的 objects :) 从我的主页实时查看。我结合了 3D 星星、天体、船只、真实地面(通过 DTM 和卫星纹理)。它甚至可以输出 red-cyan 立体图 :)。可以从地表、大气层、space ...(不只是锁定到地球)进行渲染。没有其他第 3 方库然后使用 OpenGL。这是它的样子:
如您所见,它在任何高度或缩放下都可以正常工作,气氛是这样完成的atmosphere scattering shader
模拟
我没有使用 n-body 重力模拟,因为为此你需要大量很难获得的数据(而且几乎不可能达到所需的精度) .计算必须非常精确。
我使用 开普勒方程,所以请看这些:
如果您仍想使用重力模型,则使用来自 NASA 的 JPL horizons。我认为他们在 C/C++ 中也有源代码,但他们使用的参考框架与我的地图不兼容,所以它对我来说无法使用。
总的来说,开普勒方程误差较大,但不会随着时间增加太多。重力模型更精确,但它的误差随着时间的推移而增加,你需要不断更新 astro body 数据才能使其工作......
[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
四舍五入仍然存在,但不再累加。您必须 select max
重视集成本身是安全的,并且添加 xxx0+xxx1
必须是安全的。因此,如果一次拆分的数字差异太大,则拆分两次或更多...
- 赞:
xxx0+=yyy*dt; if (fabs(xxx0>max0))... if (fabs(xxx1>max1))...
然而,这并没有充分利用添加变量的动态范围来发挥其全部潜力。有这样的替代方法:
[Edit2] 星星
- The Colors of the Stars我见过的最好的星图
- Star B-V color index to apparent RGB color所有星表使用B-V索引
- Using Stelar catalogs还有明星名字cross-referencelink有
- Skybox: combine different star data
[Edit3] 进一步提高 Newton D'ALembert 积分精度
迭代积分的基本问题是,基于当前 body 位置施加基于重力的加速度会导致更大的轨道,因为在积分步骤 dt
中,位置会发生一点变化,这在计算中没有考虑到天真的整合。要解决这个问题,请看这张图片:
假设我们的body在圆形轨道上并且处于0度位置。我没有使用基于当前位置的加速度方向,而是使用 0.5*dt
之后的位置。这增加了一点点加速度,导致更高的精度(对应于开普勒轨道)。通过这个调整,我能够成功地将 2 body 系统从开普勒轨道转换为牛顿达朗贝尔轨道。 (为 n-body 执行此操作是下一步)。只有不受潮汐影响和/或卫星影响的 2 body 系统才能与我们太阳系的真实数据进行粗略关联。要构建自己的虚构数据,您可以使用开普勒圆轨道和 contripeda重力平衡力:
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
是 body 质量,M
是焦点 body 质量(太阳)。为了在可接受的公差范围内保持精度(对我而言),积分步骤 dt
应该是:
dt = 0.000001*T
所以要将新的 body 用于测试,只需将其放在:
pos = (a,0,0)
vel = (0,sqrt(G*M/a),0)
主焦点 body(太阳)位于:
pos = (0,0,0)
vel = (0,0,0)
这会将您的 body 置于圆形轨道中,这样您就可以比较开普勒与牛顿达朗贝尔来评估模拟的精度。
重要说明:这个问题 与 "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) 然后它 "ADDS" 速度 * 时间增量。 它变成类似(0,0,000000000000000000005)的东西并保持增加。当float为0时,加上这么小的值是没有问题的。但如果浮点数类似于 23500,那么它不会将小值相加。
不知道是unity的问题还是c#的float问题
这就是为什么我不能让它使用小值的原因。如果我能克服这个问题,我的问题就迎刃而解了。
////////////////////////////////////////// ////////////////////////////////
我一直在开发 n 体物理学来模拟我们的太阳系,所以我一直在收集周围的数据以使其尽可能逼真。但是数据大小有问题。我搜索了互联网的每一点,我找不到一个单一的解释人们是如何克服这个问题的。 (如果他们是的话)所以我在这里尝试拍摄。
因此,为了保持行星之间的距离、半径和 "mass" 的比例不变,我创建了一个 excel 文件来计算所有数据。 (因为为什么会有人把 "what would be the earth's mass if it had " 那个“半径图”放到互联网上?) 我会把ss作为附件。它基本上 "normalizes" 或换句话说 "scales" 每个 属性 行星的给定参考。在这种情况下,我将参考设为 "earth's radius."
我在 unity 中工作,你知道,你不能在 unity 中使用 "too big" 或 "too small" 值。所以我不得不缩小太阳系,"a lot!"
所以我用牛顿万有引力定律,就是F = GMm/r^2, 为简单起见,我直接从所有其他物体计算给定物体的 a = GM/r^2。
因此,地球重力加速度的实际值 "towards sun" 大约为 0,000006 km/s^2,这在统一处理时甚至是非常小的值,但它可以工作。然而,要获得这个值,1 我需要将地球的半径(比例)设置为 6371 个单位,将太阳的比例设置为 696,342!,这太大了,无法统一渲染。
所以我说,让地球的半径为1,单位为单位。 所以,当半径改变时,一切都会改变,质量,距离...... 我保留了行星的密度,并根据新体积和新半径计算质量。 所有的计算都在附件中
所以事情是,当我将地球的半径设为 1 时,对太阳的引力加速度大约为 0,0000000000009 这是可笑的小。当然,Unity 不适用于该值。
所以,如果我改为增加地球的半径,那么太阳的质量和半径会变得非常大,然后我又无法使用它。
我不知道其他人是如何解决这个问题的,他们做了什么来克服这个问题,但正如我从这里看到的那样,看起来不可能对太阳系进行逼真的 n 体模拟。 (至少统一)
所以我需要有 10 个代表 post 图片 -_-,我会给 link 代替。 http://berkaydursun.com/solar_system_simulator/data.PNG 还有一个目录是使用 n 体计算但具有 UNREALISTIC 值的工作实验太阳系模拟。 它工作得很好,它甚至看起来有点接近真实,但不,它没有正确的比例 ^^ 如果你愿意,可以在这里测试 http://berkaydursun.com/solar_system_simulator/
编辑:WoW 我几乎每段都以 "So" ^^
开头正如您所发现的那样,缩小规模不一定会有帮助。以下是一些关于使用浮点数时需要考虑的事项的好读物:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
基本上,从第一原理(牛顿定律)进行模拟对数值精度不利,因为你没有将重要影响的规模概念灌输到数值方法中,所以你最终会抛出一大堆不同尺度下的不同效果在一起,结果精度低。
通常像行星、卫星等的星历表不以牛顿定律开始,它们首先假设轨道是开普勒轨道,然后进行小的微扰修正。
这是一个计算行星位置的算法(半帝国)。 http://ssd.jpl.nasa.gov/txt/aprx_pos_planets.pdf
如果你想做一个 N 体模拟,你似乎需要更高的精度。如果 unity 阻止您使用双精度,那么我建议在纯 C# 中进行计算,然后在工作完成后转换为单精度。
我也编写了 sol 系统模拟程序,所以这是我的见解:
正在渲染
我使用 OpenGL,缩放比例为 1:1。所有单位都是 SI,所以 [m,s,kg,...]。问题从 Z-buffer 开始。通常的 Z-buffer 位宽是
16/24/32 bit
,这远不是您需要的。我正在从 0.1m 渲染到 1000 AU 那么如何克服这个问题?我确实通过结合 Z-sorting 和 Z-buffering( Z-sort 是必要的,因为透明环......和其他效果)。所以首先我渲染最远的部分直到
zfar=1000AU
。天穹投影在z=750AU
距离处,然后清除 Z-buffer 并渲染 objects 到zfar=0.1AU
。然后再次清除 Z-buffer 并渲染接近 objects 直到zfar=100000 m
.要完成这项工作,您必须拥有尽可能精确的投影矩阵。 The
gluPerspective
has unprecise cotangens 所以它需要修复相关元素(让我很长时间才能发现)。Z near
值取决于 Z-buffer 位宽。如果编码正确,那么即使使用缩放10000x
也能很好地工作。我将此程序用作我的 telescope 的 navigation/searcher 的 objects :) 从我的主页实时查看。我结合了 3D 星星、天体、船只、真实地面(通过 DTM 和卫星纹理)。它甚至可以输出 red-cyan 立体图 :)。可以从地表、大气层、space ...(不只是锁定到地球)进行渲染。没有其他第 3 方库然后使用 OpenGL。这是它的样子:如您所见,它在任何高度或缩放下都可以正常工作,气氛是这样完成的atmosphere scattering shader
模拟
我没有使用 n-body 重力模拟,因为为此你需要大量很难获得的数据(而且几乎不可能达到所需的精度) .计算必须非常精确。
我使用 开普勒方程,所以请看这些:
如果您仍想使用重力模型,则使用来自 NASA 的 JPL horizons。我认为他们在 C/C++ 中也有源代码,但他们使用的参考框架与我的地图不兼容,所以它对我来说无法使用。
总的来说,开普勒方程误差较大,但不会随着时间增加太多。重力模型更精确,但它的误差随着时间的推移而增加,你需要不断更新 astro body 数据才能使其工作......
[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
四舍五入仍然存在,但不再累加。您必须 select max
重视集成本身是安全的,并且添加 xxx0+xxx1
必须是安全的。因此,如果一次拆分的数字差异太大,则拆分两次或更多...
- 赞:
xxx0+=yyy*dt; if (fabs(xxx0>max0))... if (fabs(xxx1>max1))...
然而,这并没有充分利用添加变量的动态范围来发挥其全部潜力。有这样的替代方法:
[Edit2] 星星
- The Colors of the Stars我见过的最好的星图
- Star B-V color index to apparent RGB color所有星表使用B-V索引
- Using Stelar catalogs还有明星名字cross-referencelink有
- Skybox: combine different star data
[Edit3] 进一步提高 Newton D'ALembert 积分精度
迭代积分的基本问题是,基于当前 body 位置施加基于重力的加速度会导致更大的轨道,因为在积分步骤 dt
中,位置会发生一点变化,这在计算中没有考虑到天真的整合。要解决这个问题,请看这张图片:
假设我们的body在圆形轨道上并且处于0度位置。我没有使用基于当前位置的加速度方向,而是使用 0.5*dt
之后的位置。这增加了一点点加速度,导致更高的精度(对应于开普勒轨道)。通过这个调整,我能够成功地将 2 body 系统从开普勒轨道转换为牛顿达朗贝尔轨道。 (为 n-body 执行此操作是下一步)。只有不受潮汐影响和/或卫星影响的 2 body 系统才能与我们太阳系的真实数据进行粗略关联。要构建自己的虚构数据,您可以使用开普勒圆轨道和 contripeda重力平衡力:
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
是 body 质量,M
是焦点 body 质量(太阳)。为了在可接受的公差范围内保持精度(对我而言),积分步骤 dt
应该是:
dt = 0.000001*T
所以要将新的 body 用于测试,只需将其放在:
pos = (a,0,0)
vel = (0,sqrt(G*M/a),0)
主焦点 body(太阳)位于:
pos = (0,0,0)
vel = (0,0,0)
这会将您的 body 置于圆形轨道中,这样您就可以比较开普勒与牛顿达朗贝尔来评估模拟的精度。