模拟许多粒子碰撞的有效方法?

时间:2012-10-24 09:05:49

标签: algorithm collision-detection simulation

我想编写一个模拟许多粒子碰撞的小程序,首先在2D中开始(我稍后将其扩展到3D),到(在3D中)模拟向Boltzmann分布的收敛,并且还要看看如何分布在2D中演变。

我还没有开始编程,所以请不要问代码示例,这是一个相当普遍的问题,应该可以帮助我开始。这个问题背后的物理问题对我没有任何问题,而是我必须模拟至少200-500个粒子才能达到相当好的速度分布。我想实时做到这一点。

现在,对于每个时间步,我将首先更新所有粒子的位置,然后检查碰撞,以更新新的速度矢量。然而,这包括很多检查,因为我必须看看每个粒子是否与其他每个粒子发生碰撞。 我发现this或多或少地发布了相同的问题,而且那里使用的方法也是我唯一能想到的问题。但是,我担心这不会在实时工作得很好,因为它会涉及太多的碰撞检查。

现在:即使这种方法在性能方面有所提升(比如40fps),有人可以想办法避免不必要的碰撞检查吗?

我自己的想法是将电路板(或3D:空间)分成具有至少粒子直径尺寸的正方形(立方体),并实现仅检查碰撞的方法,如果两个粒子的中心是在网格中的adjecent广场内...

我很乐意听到更多的想法,因为我希望尽可能多地增加粒子数量,并且仍在进行实时计算/模拟。

编辑:所有碰撞都是纯粹的弹性碰撞,没有任何其他力量对粒子进行处理。我将实现的初始情况由用户选择的一些变量决定,以选择随机起始位置和速度。

Edit2:我发现了关于粒子碰撞模拟的一篇很好且非常有用的论文here。希望它可以帮助一些对此更感兴趣的人。

6 个答案:

答案 0 :(得分:7)

如果你想到这一点,在计划上移动的粒子实际上是一个三维系统,其中三维是xy和时间(t)。

假设“时间步长”从t0变为t1。对于每个粒子,您可以根据当前粒子位置,速度和方向创建从P0(x0, y0, t0)P1(x1, y1, t1)的3D线段。

在3D网格中划分3D空间,并将每个3D线段链接到它穿过的单元格。

现在,应检查每个网格单元格。如果它链接到0或1段,则无需进一步检查(将其标记为已选中)。如果它包含2个或更多段,则需要检查它们之间的碰撞:计算3D碰撞点Pt,缩短这两个段以在此时结束(并删除它们不再交叉的单元格的链接) ,根据粒子的新方向/速度,创建从Pt到新计算的P1点的两个新段。将这些新线段添加到网格中,并将单元格标记为已选中。向网格添加线段会将所有交叉单元格转换为未选中状态。

当您的网格中没有未经检查的单元格时,您已经解决了时间步问。

修改

  • 对于3D粒子,请将上述解决方案调整为4D。
  • 在这种情况下,八度是一种很好的3D空间分区网格形式,因为您可以“冒泡”检查/取消检查状态以快速找到需要注意的单元格。

答案 1 :(得分:5)

空间划分的一个很好的高级例子是考虑乒乓球的比赛,并检测球和桨之间的碰撞。

假设球拍在屏幕的左上角,并且球在屏幕的左下角附近......

--------------------
|▌                 |
|                  |
|                  |
|     ○            |
--------------------

每次球移动时都没有必要检查碰撞。相反,将比赛场分成中间两个。球是球场的左侧吗? (矩形算法中的简单点)

  Left       Right
         |
---------|----------
|▌       |         |
|        |         |
|        |         |
|     ○  |         |
---------|----------
         |

如果答案是肯定的,再次分开左手边,这次是横向分开,所以我们有一个左上角和左下角分区。

   Left       Right
          |
 ---------|----------
 |▌       |         |
 |        |         |
-----------         |
 |        |         |
 |     ○  |         |
 ---------|----------
          |

这个球是否与屏幕一样位于屏幕的左上角?如果没有,不需要检查碰撞! 只需要测试位于同一分区中的对象是否相互冲突。通过一系列简单(且便宜)的内部矩形检查,您可以轻松地避免进行更昂贵的形状/几何碰撞检查。

您可以继续将空间拆分为越来越小的块,直到对象跨越两个分区。这是BSP背后的基本原理(在像Quake这样的早期3D游戏中开创的技术)。网络上有很多关于2维和3维空间划分的理论。

http://en.wikipedia.org/wiki/Space_partitioning

在2维中,您经常使用BSP或四叉树。在3个维度中,您经常使用八叉树。但是,基本原则保持不变。

答案 2 :(得分:1)

你可以按照“分而治之”的思路思考。这个想法是识别正交参数而不会相互影响。例如可以想到在2D(3D轴为3轴)的情况下沿2轴分离动量分量并独立计算碰撞/位置。识别这些参数的另一种方法可以是将彼此垂直移动的粒子分组。因此,即使它们受到影响,沿着这些线的净动量也不会改变。

我同意上面没有完全回答你的问题,但它传达了一个你可能会觉得有用的基本想法。

答案 3 :(得分:1)

让我们说在时间t,对于每个粒子,你有:

P   position
V   speed

和粒子A(i)和A(j)之间的N *(N-1)/ 2信息阵列,其中i <1。焦耳;您使用对称来评估上三角矩阵而不是完整的N *(N-1)网格。

    MAT[i][j] = { dx, dy, dz, sx, sy, sz }. 

这意味着对于粒子j,粒子j具有由三个分量dx,dy和dz组成的距离;和delta-vee乘以dt,即sx,sy,sz。

要移至瞬间t + dt,您可以根据速度暂时更新所有粒子的位置

px[i] += dx[i]  // px,py,pz make up vector P; dx,dy,dz is vector V premultiplied by dt
py[i] += dy[i]  // Which means that we could use "particle 0" as a fixed origin
pz[i] += dz[i]  // except you can't collide with the origin, since it's virtual

然后检查整个N *(N-1)/ 2阵列并暂时计算每对粒子之间的新相对距离。

dx1 = dx + sx
dy1 = dy + sy
dz1 = dz + sz
DN  = dx1*dx1+dy1*dy1+dz1*dz1  # This is the new distance

如果DN <粒径为D的D ^ 2,刚刚过去的dt中发生了碰撞。

然后你可以准确计算出这种情况发生的位置,即你计算碰撞的确切距离,你可以从旧的距离平方D2(dx * dx + dy * dy + dz * dz)和新DN进行计算:它

d't = [(SQRT(D2)-D)/(SQRT(D2)-SQRT(DN))]*dt

(在时间dt内以覆盖距离SQRT(D2)-SQRT(DN)的速度减小从SQRT(D2)到D的距离所需的时间)。 这使得假设从粒子i的参考框架看到的粒子j没有“过度”

这是一个更加沉重的计算,但是当你发生碰撞时你只需要它。

知道了d,并且d“t = dt-d't,你可以使用dx * d't / dt等在Pi和Pj上重复位置计算,并获得粒子i和j的精确位置P.在碰撞瞬间;你更新速度,然后将其整合为剩余的d“t并在时间结束时得到位置。

请注意,如果我们停在此处,如果发生三粒子碰撞,此方法将会中断,并且只会处理两粒子碰撞。

因此,我们只是标记在粒子(i,j)的d t处发生碰撞而不是运行计算,并且在运行结束时,我们保存发生碰撞的最小值,并且在谁之间。

即,我们检查粒子25和110并发现0.7 dt的碰撞;然后我们发现在0.3 dt时110和139之间发生碰撞。没有早于0.3 dt的碰撞。

我们进入碰撞更新阶段,并“碰撞”110和139并更新它们的位置和速度。然后对每个(i,110)和(i,139)重复2 *(N-2)计算。

我们将发现可能仍然存在与粒子25的碰撞,但是现在在0.5 dt,并且可能,例如,另一个在0.9和80之间的139和80之间。 0.5 dt是新的最小值,因此我们重复25到110之间的碰撞计算,并重复,在算法中每次碰撞都会有轻微的“减速”。

如此实施,现在唯一的风险是“鬼魂碰撞”,即粒子在D>在时间t-dt从目标起的直径,并且在D>在时间t,直径在另一侧

这只能通过选择一个dt来避免,以便在任何给定的dt中没有粒子的行程超过其自身直径的一半。实际上,您可以根据最快粒子的速度使用自适应dt。幽灵掠过碰撞仍然是可能的;进一步的改进是基于任何两个粒子之间的最近距离来减小dt。

这样,算法确实在碰撞附近显着减慢,但是当碰撞不太可能时,它会大大加速。如果两个粒子之间的最小距离(我们在循环期间几乎没有成本计算)是这样的,那么最快的粒子(我们也几乎没有任何成本发现)不能在不到50分的时间内覆盖它,那就是4900 %速度增加。

无论如何,在无冲突的通用情况下,我们现在已经完成了五个总和(实际上更像是由于数组索引的三十四个),三个产品和每个粒子对的几个赋值。如果我们包含(k,k)对来考虑粒子更新本身,我们到目前为止有一个很好的近似成本。

这种方法的优点是O(N ^ 2) - 它随着粒子的数量而变化 - 而不是O(M ^ 3) - 随着所涉及的空间体积而缩放。

我希望现代处理器上的C程序能够实时管理数万个数量级的粒子。

P.S。:这实际上非常类似于Nicolas Repiquet的方法,包括在多次碰撞的4D附近减速的必要性。

答案 4 :(得分:1)

直到两个粒子之间(或粒子与墙壁之间)发生碰撞,整合才是微不足道的。这里的方法是计算第一次碰撞的时间,积算到那时,然后计算第二次碰撞的时间,依此类推。我们将tw[i]定义为i粒子击中第一个墙所需的时间。尽管你必须考虑球体的直径,但它很容易计算。

计算两个粒子tc[i,j]i之间碰撞的时间j会花费更多的时间,并从距离的研究中得出{{1} }:

d

我们研究是否存在d^2=Δx(t)^2+Δy(t)^2+Δz(t)^2正为t,即d^2=D^2粒子的直径(或粒子的两个半径的总和,如果你想要它们不同的话) )。现在,考虑RHS总和的第一项,

D

Δx(t)^2=(x[i](t)-x[j](t))^2=

Δx(t)^2=(x[i](t0)-x[j](t0)+(u[i]-u[j])t)^2=

出现的新术语定义了Δx(t)^2=(x[i](t0)-x[j](t0))^2+2(x[i](t0)-x[j](t0))(u[i]-u[j])t + (u[i]-u[j])^2t^2坐标的两个粒子的运动定律,

x

x[i](t)=x[i](t0)+u[i]t

x[j](t)=x[j](t0)+u[j]t是初始配置的时间。然后让t0成为(u[i],v[i],w[i]) - 粒子的速度的三个分量。对其他三个坐标和总结做同样的事情,我们得到i中的二阶多项式方程,

t

,其中

at^2+2bt+c=0

a=(u[i]-u[j])^2+(v[i]-v[j])^2+(w[i]-w[j])^2

b=(x[i](t0)-x[j](t0))(u[i]-u[j]) + (y[i](t0)-y[j](t0))(v[i]-v[j]) + (z[i](t0)-z[j](t0))(w[i]-w[j])

现在,有许多标准来评估真实解决方案的存在等等......如果您想要优化它,可以在以后评估。在任何情况下,您都会获得c=(x[i](t0)-x[j](t0))^2 + (y[i](t0)-y[j](t0))^2 + (z[i](t0)-z[j](t0))^2-D^2,如果它是复数或负数,则将其设置为加号无穷大。要加快速度,请记住tc[i,j]是对称的,并且您还希望将tc[i,j]设置为无穷大以方便使用。

然后,您获取数组tc[i,i]和矩阵tmin的最小tw,并及时整合tc

您现在将tmin减去tmintw的所有元素。

如果与tc粒子的墙壁发生弹性碰撞,您只需翻转该粒子的速度,并仅为其他每个粒子重新计算itw[i] { {1}}。

如果两个粒子发生碰撞,您会为每个其他tc[i,k]重新计算ktw[i],tw[j]。在3D中评估弹性碰撞并非易事,也许你可以使用这个

http://www.atmos.illinois.edu/courses/atmos100/userdocs/3Dcollisions.html

关于流程如何扩展,您的初始开销为tc[i,k],tc[j,k]。然后,两个时间步长之间的积分为k,撞到墙或碰撞需要O(n^2)重新计算。但真正重要的是碰撞之间的平均时间如何与O(n)成比例。在统计物理学中应该有一个答案: - )

如果您想根据时间绘制属性,请不要忘记添加更多中间时间步。

答案 5 :(得分:0)

您可以定义粒子之间的排斥力,与1 /(距离平方)成比例。在每次迭代时,计算粒子对之间的所有力,加上作用在每个粒子上的所有力,计算粒子加速度,然后计算粒子速度,最后计算粒子新位置。冲突将以这种方式自然处理。但是处理粒子与墙之间的相互作用是另一个问题,必须以其他方式处理。