实现一维碰撞检测的最佳方法是什么?

时间:2010-06-09 19:54:38

标签: c# .net algorithm simulation collision-detection

我正在编写一个模拟软件,需要一种有效的方法来测试沿线的碰撞。

模拟的是火车穿越轨道上的几个开关。当车轮在开关N英寸范围内时,开关打开,然后在车轮离开时关闭。由于所有车轮尺寸相同,并且所有开关尺寸相同,我可以将它们表示为沿轨道的单个坐标X.一旦设定,开关距离和车轮距离就不会相互改变。

当通过将X坐标放在列表中并遍历它们时通过蛮力完成这是一个相当微不足道的问题,但我需要一种有效的方法,因为它需要非常准确,即使列车正在移动在高速。有大量有关2D碰撞检测的教程,但我不确定这种独特的1D场景的最佳方法。


显然我的数据看起来有些混乱。

我正在模拟单个网站,而不是整个区域。火车可以是任何长度的,有不同类型的汽车,但只有一列火车。我的列车数据格式为{48,96,508,556,626,674,...},表示从火车前方(0)到车轴中心的距离。

(训练数据更有可能以 Car 对象的有序列表的形式出现在我的身上,每个对象都有一个长度和一个表示轴的整数列表距离汽车前部的距离,但所有这些都汇总到一个列表中,因为所有车轴都与我相同。)

我的开关都在几百英尺范围内,并且经常被火车完全覆盖。开关可以在几百英尺到几英寸之间的任何间隔,并且与火车的形式相同:{{ 1}},表示从站点开始到交换机中心的距离。

最后,我知道车轮启动开关的距离,以英寸为单位。

例如,使用上述样本数据和8英寸的激活距离,当列车到达X = 40时,X = 0处的第一个开关将激活,这意味着火车距离现场40英寸。当火车到达X = 48时,X = 8的开关也被激活。在X = 56时,第一个开关熄灭,而在X = 64时,第二个开关也熄灭。不同的轴在穿过现场时打开和关闭不同的开关。

火车通常以低于10英里/小时的速度行驶,但可以高得多。 (现在我们的模拟时速仅为30英里/小时,但更高会更好。)

4 个答案:

答案 0 :(得分:5)

列出所有开关坐标的排序列表,并使用列表中的二进制搜索查找最近的开关。然后检查它是否有多远,以及它是否是碰撞。

O(log n)

另一种选择是利用火车沿着轨道移动的事实,并且只能接近两个开关,一个在后面,一个在前面。

构造所有开关的双向链表,并定位一个额外的节点,以在链表中的正确位置表示列车。然后只检查火车前往的开关附近。

O(1)

要节省内存,请将已排序的坐标存储在数组中,然后只需跟踪列车所在的索引。

答案 1 :(得分:1)

将您的开关位置和灵敏度范围预处理到轨道段列表中。每个段都有一个长度,每个段之间有一组开关'开'或'关'事件。

switch_on ( 0 ), ( length: 8 ), switch_on ( 1 ), // x = zero here 
segment ( length: 8 ), switch_off ( 0 ),
segment ( length: 8 ), switch_off ( 1 ),
segment ( length: 488 ), switch_on ( 2 ),
segment ( length: 8 ), switch_on ( 3 ),
segment ( length: 8 ), switch_off ( 2 ),
segment ( length: 8 ), switch_off ( 3 ),
...

对于每个车轴,其当前位置也与其所在的轨道段一起表示。

如果您正在进行基于事件的模拟,则应安排下一个事件,以确定从轴到其当前轨道段末端的最小值。这与列车速度无关,而且准确(如果列车速度更快,您不会错过开关)。如有必要,将事件存储在堆中(通常不到30左右就不值得,如果需要,可以对事件进行调度)。

处理事件将是O(无轴)。大多数步骤将涉及一个或两个开关状态更改和位置更新。在每个事件中,一个轴将导致一个开关接通或断开(根据数据同时开关导致两个事件,零时间间隔),并且需要比较其段的末端的所有轴时间。您可以假设所有车轴都以相同的速度行驶;就处理事件而言无关紧要,它只计算到达相关轴的下一个开关的时间。

如果您正在进行固定时间步模拟,则处理所有在步骤结束时发生的事件,然后将一个事件移动到它们到达结束时的点。步。

答案 2 :(得分:0)

将交换机列表存储为Ben指示的双向链表。

将指针保留在车轮对象(或结构中,假设有一个)到下一个开关和前一个开关相对于当前位置。当轮子放在轨道上时,初始化它们。

当您在每个开关上移动时,将轮对象中的“下一个”和“上一个”开关换成新的“下一个”和“上一个”,可以通过检查双向链接列表快速获得。

这可以避免所有搜索,除非可能是初始放置轮子。

此外,“开关”结构可用于将接近指针保持回所有将其列为“前一个”或“下一个”的车轮。 (这里有一个互斥体,所以要小心谁更新了这个。)这可以快速更新谁接近任何给定的开关及其距离。

答案 3 :(得分:0)

假设Axle-to-Axle距离总是大于激活距离,并且在火车进入后路线不会经常变化,您应该能够通过预先计算加快速度。基本上,对于每个开关,计算它将切换的列车行驶距离列表,然后在列车前进时浏览列表。

伪代码:

axles  = {48,96,508,556,626,674,...}
switches ={0,8,512,520,...}
activate = 8
float toggledist[num_switches]
boolean switchState[num_switches]={false,false,false,...}
int idx[num_switches]

for (i in switches)
  n = 0
  for (a in axles)
    toggledist[n++] = switches[i]+axles[a]-activate
    toggledist[n++] = switches[i]+axles[a]+activate

travel= 0.0f;

each (cycle)
  travel += TrainVelocity*time;
  for (i in switches)
    while (trigger>=toggledist[idx[i]])
      switchState[i]=!switchState[i];
      //additional processing for switch change here, if needed
      idx[i]++;