8

I am writing a physics engine/simulator which incorporates 3D space flight, planetary/stellar gravitation, ship thrust and relativistic effects. So far, it is going very well, however, one thing that I need help with is the math of the collision detection algorithm.

The iterative simulation of movement that I am using is basically as follows:

(Note: 3D Vectors are ALL CAPS.)

For each obj

    obj.ACC = Sum(all acceleration influences)

    obj.POS = obj.POS + (obj.VEL * dT) + (obj.ACC * dT^2)/2     (*EQ.2*)

    obj.VEL = obj.VEL + (obj.ACC * dT)

Next

Where:

obj.ACC is the acceleration vector of the object
obj.POS is the position or location vector of the object
obj.VEL is the velocity vector of the object

obj.Radius is the radius (scalar) of the object

dT is the time delta or increment

What I basically need to do is to find some efficient formula that derives from (EQ.2) above for two objects (obj1, obj2) and tell if they ever collide, and if so, at what time. I need the exact time both so that I can determine if it is in this particular time increment (because acceleration will be different at different time increments) and also so that I can locate the exact position (which I know how to do, given the time)

For this engine, I am modelling all objects as spheres, all this formula/algorithm needs to do is to figure out at what points:

(obj1.POS - obj2.POS).Distance = (obj1.Radius + obj2.Radius)

where .Distance is a positive scalar value. (You can also square both sides if this is easier, to avoid the square root function implicit in the .Distance calculation).

(yes, I am aware of many, many other collision detection questions, however, their solutions all seem to be very particular to their engine and assumptions, and none appear to match my conditions: 3D, spheres, and acceleration applied within the simulation increments. Let me know if I am wrong.)


Some Clarifications:

1) It is not sufficient for me to check for Intersection of the two spheres before and after the time increment. In many cases their velocities and position changes will far exceed their radii.

2) RE: efficiency, I do not need help (at this point anyway) with respect to determine likely candidates for collisions, I think that I have that covered.


Another clarification, which seems to be coming up a lot:

3) My equation (EQ.2) of incremental movement is a quadratic equation that applies both Velocity and Acceleration:

obj.POS = obj.POS + (obj.VEL * dT) + (obj.ACC * dT^2)/2

In the physics engines that I have seen, (and certainly every game engine that I ever heard of) only linear equations of incremental movement that apply only Velocity:

obj.POS = obj.POS + (obj.VEL * dT)

This is why I cannot use the commonly published solutions for collision detection found on StackOverflow, on Wikipedia and all over the Web, such as finding the intersection/closest approach of two line segments. My simulation deals with variable accelerations that are fundamental to the results, so what I need is the intersection/closest approach of two parabolic segments.

4

4 回答 4

5

在 AShelley 提到的网页上,最近点方法是针对两个物体匀速运动的情况而开发的。但是,我相信在两个物体都以恒定的非零加速度(二次时间相关)移动的情况下,可以使用相同的矢量微积分方法得出结果。

在这种情况下,距离平方函数的时间导数是 3 阶(三次)而不是 1 阶。因此,对于最近接近时间将有 3 种解决方案,这并不奇怪,因为两个对象的路径都是弯曲的,因此可能有多个交叉点。对于此应用程序,您可能希望使用在当前模拟步骤定义的时间间隔内的最早的 t 值(如果存在这样的时间)。

我制定了导数方程,它应该给出最接近的时间:

0 = |D_ACC|^2 * t^3 + 3 * dot(D_ACC, D_VEL) * t^2 + 2 * [ |D_VEL|^2 + dot(D_POS, D_ACC) ] * t + 2 * dot(D_POS, D_VEL)

在哪里:

D_ACC = ob1.ACC-obj2.ACC

D_VEL = ob1.VEL-obj2.VEL(更新前)

D_POS = ob1.POS-obj2.POS(也在更新前)

dot(A, B) = A.x*B.x + A.y*B.y + A.z*B.z

(请注意,幅度的平方|A|^2可以使用 来计算dot(A, A)

要为t解决这个问题,您可能需要使用类似于Wikipedia 上的公式。

当然,这只会给你最接近的时刻。此时您需要测试距离(使用公式 2 之类的方法)。如果大于(obj1.Radius + obj2.Radius),则可以忽略(即没有冲突)。但是,如果距离更小,则意味着球体在此之前发生碰撞。然后,您可以使用迭代搜索在更早的时间测试距离。也有可能提出另一个(甚至更复杂的)推导,它考虑了大小,或者可能找到一些其他的分析解决方案,而不诉诸迭代求解。

编辑:由于更高阶,方程的一些解实际上是最远分离的时刻。我相信在所有情况下,3 个解决方案中的 1 个或 3 个解决方案中的 2 个都将是最远分离的时间。您可以通过评估关于时间的二阶导数(通过将一阶导数设置为零找到的 t 值)来分析测试您是处于最小值还是最大值:

D''(t) = 3 * |D_ACC|^2 * t^2 + 6 * dot(D_ACC, D_VEL) * t + 2 * [ |D_VEL|^2 + dot(D_POS, D_ACC) ]

如果二阶导数的计算结果为正数,那么您知道距离在给定时间 t 内处于最小值,而不是最大值。

于 2009-12-16T19:26:07.057 回答
2

在每个球体的开始位置和结束位置之间画一条线。如果生成的线段与球体相交,那么球体肯定会在某个点发生碰撞,并且一些聪明的数学可以找到碰撞发生的时间。还要确保检查线段之间的最小距离(如果它们不相交)是否小于 2*radius。这也将指示碰撞。

从那里你可以倒退你的增量时间,以准确地发生在碰撞中,这样你就可以正确计算力。

您是否考虑过使用已经完成这项工作的物理库?许多库使用更先进、更稳定(更好的积分器)系统来求解您正在使用的方程组。子弹物理学浮现在脑海。

于 2009-12-16T17:53:42.537 回答
2

op询问碰撞时间。稍微不同的方法将精确计算它......

请记住,位置投影方程为:

NEW_POS=POS+VEL*t+(ACC*t^2)/2

如果我们POSD_POS=POS_A-POS_BVEL和替换对象D_VEL=VEL_A-VEL_B,我们得到:ACC=ACC_A-ACC_BAB

$D_NEW_POS=D_POS+D_VEL*t+(D_ACC*t^2)/2

这是对象之间矢量距离的公式。为了得到它们之间的平方标量距离,我们可以取这个方程的平方,展开后的样子:

distsq(t) = D_POS^2+2*dot(D_POS,D_VEL)*t + (dot(D_POS, D_ACC)+D_VEL^2)*t^2 + dot(D_VEL,D_ACC)*t^3 + D_ACC^2*t^4/4

为了找到发生碰撞的时间,我们可以将方程设置为等于半径和的平方并求解t

0 = D_POS^2-(r_A+r_B)^2 + 2*dot(D_POS,D_VEL)*t + (dot(D_POS, D_ACC)+D_VEL^2)*t^2 + dot(D_VEL,D_ACC)*t^3 + D_ACC^2*t^4/4

现在,我们可以使用四次公式求解方程。

四次公式将产生 4 个根,但我们只对根感兴趣。如果存在双实根,则两个对象恰好在一个时间点接触边缘。如果有两个真正的根,那么对象在根1和根2之间不断重叠(即根1是碰撞开始的时间,根2是碰撞停止的时间)。四个实根意味着对象在根对 1,2 和 3,4 之间连续碰撞两次。

在R中,我曾经polyroot()解决如下:

# initial positions
POS_A=matrix(c(0,0),2,1)
POS_B=matrix(c(2,0),2,1)
# initial velocities
VEL_A=matrix(c(sqrt(2)/2,sqrt(2)/2),2,1)
VEL_B=matrix(c(-sqrt(2)/2,sqrt(2)/2),2,1)
# acceleration
ACC_A=matrix(c(sqrt(2)/2,sqrt(2)/2),2,1)
ACC_B=matrix(c(0,0),2,1)
# radii
r_A=.25
r_B=.25
# deltas
D_POS=POS_B-POS_A
D_VEL=VEL_B-VEL_A
D_ACC=ACC_B-ACC_A


# quartic coefficients
z=c(t(D_POS)%*%D_POS-r*r, 2*t(D_POS)%*%D_VEL, t(D_VEL)%*%D_VEL+t(D_POS)%*%D_ACC, t(D_ACC)%*%D_VEL, .25*(t(D_ACC)%*%D_ACC))
# get roots
roots=polyroot(z)
# In this case there are only two real roots...
root1=as.numeric(roots[1])
root2=as.numeric(roots[2])

# trajectory over time
pos=function(p,v,a,t){
  T=t(matrix(t,length(t),2))
  return(t(matrix(p,2,length(t))+matrix(v,2,length(t))*T+.5*matrix(a,2,length(t))*T*T))
}

# plot A in red and B in blue
t=seq(0,2,by=.1) # from 0 to 2 seconds.
a1=pos(POS_A,VEL_A,ACC_A,t)
a2=pos(POS_B,VEL_B,ACC_B,t)
plot(a1,type='o',col='red')
lines(a2,type='o',col='blue')

# points of a circle with center 'p' and radius 'r'
circle=function(p,r,s=36){
  e=matrix(0,s+1,2)
  for(i in 1:s){
    e[i,1]=cos(2*pi*(1/s)*i)*r+p[1]
    e[i,2]=sin(2*pi*(1/s)*i)*r+p[2]
  }
  e[s+1,]=e[1,]
  return(e)
}

# plot circles with radius r_A and r_B at time of collision start in black
lines(circle(pos(POS_A,VEL_A,ACC_A,root1),r_A))
lines(circle(pos(POS_B,VEL_B,ACC_B,root1),r_B))
# plot circles with radius r_A and r_B at time of collision stop in gray
lines(circle(pos(POS_A,VEL_A,ACC_A,root2),r_A),col='gray')
lines(circle(pos(POS_B,VEL_B,ACC_B,root2),r_B),col='gray')

生成的 R 图显示物体的轨迹和开始/停止碰撞位置

对象A遵循从左下角到右上角的红色轨迹。对象B遵循从右下角到左上角的蓝色轨迹。两个物体在时间 0.9194381 和时间 1.167549 之间不断碰撞。两个黑色圆圈刚刚接触,显示重叠的开始 - 重叠在时间上继续,直到对象到达灰色圆圈的位置。

于 2018-12-28T17:34:05.570 回答
1

似乎您想要最接近的方法 (CPA)。如果它小于半径之和,则发生碰撞。链接中有示例代码。您可以使用当前速度计算每一帧,并检查 CPA 时间是否小于您的刻度大小。您甚至可以缓存 cpa 时间,并且仅在对任一项目应用加速时才更新。

于 2009-12-16T17:42:47.660 回答