15

我想编写一个模拟许多粒子碰撞的小程序,首先从 2D 开始(稍后我会将其扩展到 3D),以(在 3D 中)模拟向玻尔兹曼分布的收敛,并查看分布如何在 2D 中演变.

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

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

所以现在:即使这种方法在性能方面有效(比如 40fps),任何人都可以想出一种方法来避免不必要的碰撞检查吗?

我自己的想法是将板(或 3D:空间)拆分为尺寸至少为粒子直径的正方形(立方体),并实施一种仅在两个粒子的中心在相邻正方形内时检查碰撞的方法在网格中...

我很高兴听到更多的想法,因为我想尽可能多地增加粒子的数量,并且仍然可以进行实时计算/模拟。

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

Edit2:我在这里找到了一篇关于粒子碰撞模拟的很好且很有帮助的论文。希望它可以帮助一些对更深入感兴趣的人。

4

6 回答 6

9

如果你想一想,在一个平面上移动的粒子实际上是一个 3D 系统,其中三个维度是xy时间 ( t)。

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

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

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

当您的网格中没有更多未选中的单元格时,您已经解决了您的时间步长。

编辑

  • 对于 3D 粒子,将上述解决方案调整为 4D。
  • 在这种情况下,八叉树是 3D 空间划分网格的一种很好的形式,因为您可以“冒泡”选中/未选中状态以快速找到需要注意的单元格。
于 2012-10-24T09:45:16.620 回答
7

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

假设桨在屏幕的左上角,而球在屏幕的左下角附近......

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

每次球移动时都不需要检查碰撞。相反,将比赛场地从中间分成两部分。球在场地的左侧吗?(矩形内的简单点算法)

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

如果答案是肯定的,再次分割左侧,这次是水平分割,这样我们就有了左上角和左下角的分区。

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

这个球和球拍在屏幕的左上角吗?如果没有,则无需检查碰撞!只有位于同一分区中的对象才需要进行相互碰撞测试。通过在矩形内进行一系列简单(且便宜)的点检查,您可以轻松避免进行更昂贵的形状/几何碰撞检查。

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

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

在二维中,您通常会使用 BSP 或四叉树。在 3 维中,您通常会使用八叉树。然而,基本原理保持不变。

于 2012-10-24T10:04:33.013 回答
1

假设在时间 t,对于每个粒子,你有:

P   position
V   speed

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

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

这意味着对于粒子 j,粒子 j 的距离由三个分量 dx、dy 和 dz 组成;以及与 dt 相乘的 delta-vee,即 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^2 且粒子的直径为 D,则您在刚刚过去的 dt 中发生了碰撞。

然后,您可以准确计算发生这种情况的位置,即计算碰撞的确切 d't,您可以根据旧距离平方 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't,并且d"t = dt-d't,就可以用dx*d't/dt等对Pi和Pj重复位置计算,得到粒子i和j在瞬间的准确位置P碰撞;您更新速度,然后将其整合到剩余的 d"t 并获得时间 dt 结束时的位置。

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

因此,我们不运行计算,而是标记粒子 (i,j) 在 d't 发生碰撞,并且在运行结束时,我们保存发生碰撞的最小 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 dt 处可能会在 139 和 80 之间发生另一次碰撞。0.5 dt 是新的最小值,所以我们在 25 到 110 之间重复碰撞计算,然后重复,每次碰撞的算法都会稍微“减速”。

如此实施后,现在唯一的风险是“鬼碰撞”,即粒子在时间 t-dt 距离目标的直径为 D >,而在时间 t在另一侧的直径为 D > 。

你只能通过选择一个 dt 来避免这种情况,这样任何粒子在任何给定的 dt 中都不会超过其自身直径的一半。实际上,您可以使用基于最快粒子速度的自适应 dt。鬼扫视碰撞仍然是可能的;进一步的改进是根据任意两个粒子之间的最近距离来减少 dt。

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

无论如何,在无碰撞的通用情况下,我们现在已经完成了五次求和(实际上更像是三十四次,由于数组索引),每个粒子对的三个乘积和几个分配。如果我们包括 (k,k) 对来考虑粒子更新本身,那么到目前为止我们有一个很好的成本近似值。

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

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

PS:这实际上与 Nicolas Repiquet 的方法非常相似,包括在多次碰撞的 4D 附近减速的必要性。

于 2012-10-24T10:24:37.587 回答
1

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

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

于 2012-10-24T09:51:54.097 回答
1

直到两个粒子之间(或粒子与墙之间)发生碰撞,积分是微不足道的。这里的做法是计算第一次碰撞的时间,积分到那时,然后计算第二次碰撞的时间,以此类推。让我们定义第一个粒子撞击第一面墙tw[i]的时间。i尽管您必须考虑球体的直径,但计算起来很容易。

两个粒子之间tc[i,j]碰撞时间的计算需要更多的时间,并且从他们的距离的时间研究中得出:ijd

d^2=Δx(t)^2+Δy(t)^2+Δz(t)^2

我们研究是否存在t正数,即d^2=D^2粒子D的直径(或粒子的两个半径之和,如果您希望它们不同)。现在,考虑 RHS 总和的第一项,

Δ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

您现在减去和 的tmin所有元素。twtc

如果与第 -th 粒子的壁发生弹性碰撞i,您只需翻转该粒子的速度,然后仅重新计算tw[i]tc[i,k]k

如果两个粒子之间发生碰撞,则重新计算tw[i],tw[j]tc[i,k],tc[j,k]k在 3D 中对弹性碰撞的评估并非易事,也许你可以使用这个

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

关于流程如何扩展,您的初始开销为O(n^2). 那么两个时间步的积分是O(n),撞墙或者碰撞需要O(n)重新计算。但真正重要的是碰撞之间的平均时间如何随n. 在统计物理学的某个地方应该有一个答案:-)

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

于 2012-10-24T22:29:17.970 回答
0

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

于 2019-04-30T20:01:36.023 回答