phenomLi / Blog

Comments, Thoughts, Conclusions, Ideas, and the progress.
219 stars 17 forks source link

碰撞求解(二):使用冲量 #35

Open phenomLi opened 5 years ago

phenomLi commented 5 years ago

上一次讲了基于向量的碰撞求解方法,该方法基本上只适用于无方向的球体间的完全弹性碰撞,灵活性较低。今天介绍一种基于冲量的碰撞求解方法,该方法更加精确,强大,而且能模拟非弹性碰撞。


注意:该文章只涉及一定量数学,要求:

(虽然看着很多公式,但是大部分都是课本现成的物理公式,我们要做的工作只是把这些公式重新组合整理一下而已)


什么是冲量

冲量(impulse)是一个力学上的矢量,记作符号J用于描述物体动量的变化,冲量是一个过程量,比如一个物体t=0时动量5,t=1时动量10,那么该物体在0到1时的冲量为5。


但是我们毕竟不是搞物理的,不需要对某些概念这么深究。我本人简单地将冲量看作为 “ 一股一瞬间改变速度的力 ”,而且该“力”可以作用在物体的任何点上。这就是冲量最大的优点,如果冲量的作用点不过质心,那么物体便会发生旋转。


另外,使用冲量的另一个优点是,冲量可以很容易和力建立联系:


两个小球间的碰撞

现在,我们先将问题简化,考虑两个小球间的碰撞。假设小球为AB,如图: 其中小球A的质量为massA,碰撞时速度为VA,碰撞后速度为V'A。小球B的质量为massB,碰撞时速度为VB,碰撞后速度为V'B我们要求的就是改变小球速度的冲量J,下面我们一步步来推导出J


先从小球的速度入手,我们引入 相对速度(relative velocity) 这个概念:AB间的相对速度即A的速度减去B的速度,即 公式一

其中VABAB的相对速度(对于相对速度的具体理解,可以看下面的评论)。


在碰撞求解中,我们关注的是碰撞法线方向的相对速度,即公式一应该用碰撞法线n表示,也就是说,我们想知道从AB沿碰撞法向的相对速度,得到公式二


下一步,我们引入恢复系数(coefficient of restitution)。恢复系数是一个控制碰撞弹性程度的量,用符号e表示,取值范围[0, 1],若e = 1,表示完全弹性碰撞,碰撞后两物体不损失能量;若e = 0,表示完全非弹性碰撞,两物体碰撞后粘在一起,能量和为0。e通常取两物体恢复系数的积的平方根:

e = Math.sqrt(A.restitution * B.restitution);


现在,根据牛顿恢复定律,有公式三 该公式表明了碰撞后的速度等于碰撞前的速度乘以恢复系数。


现在我们将公式二公式三组合一下,得到公式四 即: 注意,我们要在右边引入一个负号。因为在在牛顿恢复定律中,碰撞后的速度V',实际上是指向碰撞前速度V的相反方向。


目前,我们还没有出现冲量J,别急,下面重点开始了。我们现在要做的就是将冲量J和速度V联系起来。首先看公式五 该公式描述了碰撞后速度 V' 和碰撞前速度V的关系。又因为冲量J是动量的变化量,有公式六

现在终于出现了J,为了得到 V' 关于J的表达式,我们将公式五和公式六进行组合,得到公式七 因为AB发生碰撞时,A将被推向与B相反的方向,所以:

显然,求 V' 的关键是求出J。将公式四公式七进行组合,整理,推出公式八

我们观察这个式子,只有J是未知的,顺理成章地将式子整理一下,将J移到式子左边,其他量移到等式右边,得到J的表达式:


解决了,虽然有点复杂,但是不难理解。将J代入公式七便可以求出小球的碰撞后速度。但是故事还没结束,我们现在的情况只是两个小球间的碰撞,两个小球间的碰撞用向量方法就可解决,搞这么多公式的推导,目的是将碰撞求解抽象到适用于所有情况


求解复杂的碰撞

有了以上基础,我们来看复杂一点的碰撞求解。例如下面两个矩形的情况:

求解该碰撞的关键在于碰撞点。我们可以视这个碰撞点为两个分别在两个矩形碰撞点位置的小球间的碰撞,那么,对于该碰撞点,便可以转换为两个小球间碰撞的情况。但是问题在于,我们只知道两个矩形的速度,所以要解决问题,需要求出碰撞点分别基于两个矩形的速度: 其中V为线速度,w为角速度,r为碰撞点到矩形质心的距离向量。求出碰撞点速度后再求出J,便可以将J作用在碰撞点上产生摩擦力,加速度和扭矩,使矩形发生位移或旋转。

最后

基于冲量的碰撞求解可以适用于更多情况,而且更加真实,但是并不是完美的。冲量法往往伴随抖动,特别在大量物体堆叠时,抖动会更加明显。碰撞求解不是一个简单的内容,基于冲量的方法仍然不够抽象,接下来有时间我会再介绍一种更高阶的碰撞求解方法。

AllenPocketGamer commented 4 years ago

thanks

Dreamtowards commented 4 years ago

"若相对速度 > 0,则两对象有分离趋势,若 < 0,则有接近趋势" 这是真的嘛。。。?但若A,B调换位置 再进行相对速度的 <>0比较,似乎两者之一的结果会违反这句。。。

phenomLi commented 4 years ago

"若相对速度 > 0,则两对象有分离趋势,若 < 0,则有接近趋势" 这是真的嘛。。。?但若A,B调换位置 再进行相对速度的 <>0比较,似乎两者之一的结果会违反这句。。。

这个“相对速度”在《游戏物理引擎开发》里面被称为“分离速度”,但是我感觉应该是指“接近速度”,是两个刚体准备发送正碰时的速度,分离速度和接近速度都统称为相对速度。

不过我文章中的确有错误。应该是VAB * n > 0表示两刚体速度有分离趋势不会碰撞,< 0表示有碰撞趋势。文中没有点乘法向量n,然而n是关键。假设有刚体A,B,VAB = VB - VA,通常默认情况下法线方向指向“假设A,B会发生碰撞后的B速度方向”。

比如说A,B速度相反,迎面运动,这时A,B必定会发生碰撞,此时法线方向如下图所示,是B碰撞后的速度方向没错。此时VAB*n < 0。 微信截图_20200315102433

假设A,B速度同向,那么A,B是否会发生碰撞取决于他们的速度大小,但是确定法线n时仍然假设A,B会发生碰撞,仍取如下图的方向(因为这样做才能判断A,B之后是否会碰撞)。此时若|VB| > |VA|,那么VAB n > 0,两者不会碰撞;若|VB| < |VA|,VAB n < 0,两者会发生碰撞。 微信截图_20200315102411

当然对于这个法线方向并不是绝对的,你也可以取A的碰撞后方向,那么你下面代码的正负号都要取反。

我当时看书时看到:

// Do not resolve if velocities are separating
    if(relativeVel > 0)
      return;

也是有点不解,之后查阅了不少资料加思考才弄明白。文章中跑步的例子也也举得不严谨,感谢提出。

Dreamtowards commented 4 years ago

我很激动真。、的能可以得到回复与指点。、! (replyling

上面指点到了很多事情,不过我有一点点想稍微先说一点“其他”的233...well..

相对 的感想

我感觉相对速度rel_linvel 更多的表示的是 一个速度A相对于另一个速度B 的差异/方向/幅度。 虽然可能看上去不是很搭边。。但在Wikipedia上有一句这样的说明 image

V[A rel B] = A - B

"A相对于B的速度"的表示法是比"A在B永远是静止的坐标系统中的速度"更为简洁。

题外话 AB ? A-B ? B-A

image

A rel B = A - B

A rel B = A + -B = A相对于B的偏移 WikipediaReference: "A相对于B(的速度)"的表示法是比"A在B永远是静止的坐标系统中(的速度)"更为简洁。

A rel Origin

试想 A 相对于原点,那会是什么样的呢。概念上似乎是的确相对于原点 虽然数值没有变化。

相对于 [某对象],可想像自己的视野/位置就在那个相对的[某对象]的地方 - 相对坐标系原点的位置,并看着那个被相对的"前者"。

A相对于B -- A相对于原点 - 亥📄,你是否想试试站在别人的角度想一想看

A to B (= B rel A )= B - A

通常很多地方写 dirAB 都表示 B-A,指从A到B的方向/幅度。 实际上 A to B = A至B的方向/幅度 = B rel A = B相对于A的位置 = B - A。经过 B - A 之后,坐标系原点变成了A的位置 你正在A的位置看着B相对于你 相对于原点。

image

相对速度 与 物体 分/合 趋势/倾向

我们不能真的预测/判断物体的分/合倾向,实际上的几何体可能很复杂。但是我们应该可以通过两个物体的位置和速度来判断他们的运动分/合倾向。

是的,应该需要位置。试想在一个一维-垂直轴(up-axis)情况下,有两个物体A和B,A在往下运动,B在往上运动 且他们不会卡在同一个位置。但是即使他们的速度一直保持不变,若调换他们的位置 原本的分/合情况就会变得截然相反 - A在上面 B在下面的话 他们为合倾向,调换位置 A在下面 B在上面 那么他们将为分倾向。相似的情况 - 假设他们往同一个方向运动 但持有不同的速度和位置,那么可能会出现赶不上了(慢速在后)和"快"赶上了(慢速在前)的情况。

(A.pos rel B.pos) DOT (A.vel rel B.rel)

A相对于B的位置 DOT A相对于B的速度

注释: 由于我们在拿速度和位置做比较,那么为了更好理解,对于速度,我们或许可以在这想象一下 速度为(物体)下一刻的位置变化。那么或许可以将式子改成这样: A相对于B的位置 DOT A(下一刻的位置变化)相对于B下一刻的位置变化

当你计算完DOT左边右边的减法后 打算随后进行DOT计算时,想象尼现在的视角(突然)变成了B的位置 坐标系以B的位置为原点。并且你将看到两个点:B相对于你的位置 以及B(下一刻的位置变化)相对于你下一刻的位置变化。

假设这两个点(分别连接至原点所形成的角)的夹角小与90度(都在同一边),那么说明在相对情况下 A目前在往他那边移动(分的趋势)。若大于90度,那么说明A正在往我们这边移动(合的趋势)。若等于90度,那么可能A和B的速度或位置均为0 或A与我们“平行”移动

法线 n

碰撞相关的法线的话。。似乎感觉法线通常是在发生/检测到碰撞后才产生的。。通过碰撞检测 找出对应碰撞面的法线。而没有发生碰撞时 似乎就不是很好想如何得出碰撞法线。。

通常默认情况下法线方向指向“假设A,B会发生碰撞后的B速度方向”。 我的大概理解: “假设A,B会发生碰撞,那么法线n为碰撞后(碰撞已处理)的B的速度方向”。

假设是这样的话,那么感觉可能出现了一个“先有鸡蛋”的问题。。 因为通常解决碰撞等处理历程中 总是需要用到碰撞法向,但是在这里的碰撞法向的获取 (却)需要以那些需要该法向的处理例程的结果作为材料。。。 但可能这里的法线不是指碰撞法线 另外的.. 也可能是我没有理解对。。。welll。。

phenomLi commented 4 years ago

我很激动真。、的能可以得到回复与指点。、! (replyling

上面指点到了很多事情,不过我有一点点想稍微先说一点“其他”的233...well..

相对 的感想

我感觉相对速度rel_linvel 更多的表示的是 一个速度A相对于另一个速度B 的差异/方向/幅度。 虽然可能看上去不是很搭边。。但在Wikipedia上有一句这样的说明 image

V[A rel B] = A - B

"A相对于B的速度"的表示法是比"A在B永远是静止的坐标系统中的速度"更为简洁。

题外话 AB ? A-B ? B-A

image

A rel B = A - B

A rel B = A + -B = A相对于B的偏移 WikipediaReference: "A相对于B(的速度)"的表示法是比"A在B永远是静止的坐标系统中(的速度)"更为简洁。

A rel Origin

试想 A 相对于原点,那会是什么样的呢。概念上似乎是的确相对于原点 虽然数值没有变化。

相对于 [某对象],可想像自己的视野/位置就在那个相对的[某对象]的地方 - 相对坐标系原点的位置,并看着那个被相对的"前者"。

A相对于B -- A相对于原点 - 亥📄,你是否想试试站在别人的角度想一想看

A to B (= B rel A )= B - A

通常很多地方写 dirAB 都表示 B-A,指从A到B的方向/幅度。 实际上 A to B = A至B的方向/幅度 = B rel A = B相对于A的位置 = B - A。经过 B - A 之后,坐标系原点变成了A的位置 你正在A的位置看着B相对于你 相对于原点。

image

相对速度 与 物体 分/合 趋势/倾向

我们不能真的预测/判断物体的分/合倾向,实际上的几何体可能很复杂。但是我们应该可以通过两个物体的位置和速度来判断他们的运动分/合倾向。

是的,应该需要位置。试想在一个一维-垂直轴(up-axis)情况下,有两个物体A和B,A在往下运动,B在往上运动 且他们不会卡在同一个位置。但是即使他们的速度一直保持不变,若调换他们的位置 原本的分/合情况就会变得截然相反 - A在上面 B在下面的话 他们为合倾向,调换位置 A在下面 B在上面 那么他们将为分倾向。相似的情况 - 假设他们往同一个方向运动 但持有不同的速度和位置,那么可能会出现赶不上了(慢速在后)和"快"赶上了(慢速在前)的情况。

(A.pos rel B.pos) DOT (A.vel rel B.rel)

A相对于B的位置 DOT A相对于B的速度

注释: 由于我们在拿速度和位置做比较,那么为了更好理解,对于速度,我们或许可以在这想象一下 速度为(物体)下一刻的位置变化。那么或许可以将式子改成这样: A相对于B的位置 DOT A(下一刻的位置变化)相对于B下一刻的位置变化

当你计算完DOT左边右边的减法后 打算随后进行DOT计算时,想象尼现在的视角(突然)变成了B的位置 坐标系以B的位置为原点。并且你将看到两个点:B相对于你的位置 以及B(下一刻的位置变化)相对于你下一刻的位置变化。

假设这两个点(分别连接至原点所形成的角)的夹角小与90度(都在同一边),那么说明在相对情况下 A目前在往他那边移动(分的趋势)。若大于90度,那么说明A正在往我们这边移动(合的趋势)。若等于90度,那么可能A和B的速度或位置均为0 或A与我们“平行”移动

法线 n

碰撞相关的法线的话。。似乎感觉法线通常是在发生/检测到碰撞后才产生的。。通过碰撞检测 找出对应碰撞面的法线。而没有发生碰撞时 似乎就不是很好想如何得出碰撞法线。。

通常默认情况下法线方向指向“假设A,B会发生碰撞后的B速度方向”。 我的大概理解: “假设A,B会发生碰撞,那么法线n为碰撞后(碰撞已处理)的B的速度方向”。

假设是这样的话,那么感觉可能出现了一个“先有鸡蛋”的问题。。 因为通常解决碰撞等处理历程中 总是需要用到碰撞法向,但是在这里的碰撞法向的获取 (却)需要以那些需要该法向的处理例程的结果作为材料。。。 但可能这里的法线不是指碰撞法线 另外的.. 也可能是我没有理解对。。。welll。。

我不是很理解你上面关于相对速度的,可能你把他想得太复杂了。

我给你看看《游戏物理引擎开发》里面的相关代码: 微信截图_20200316110703

里面VAB * n就是在法线方向上的分离速度的意思。我本人也不过只是一个搬砖码农😂,对于数学物理的知识也忘得差不多了,不过我想这里“相对速度”指的是物理学里面正碰问题的一个概念,和平时的“相对速度”有所区别: 微信截图_20200316105922

但是我觉得你不必在这个概念上过于深究,因为在真正商用级的物理引擎上(即使是2d),这种碰撞求解方法已经很少被使用了,现在的碰撞求解基本上被抽象成一种不平等约束,用一种叫sequential impulses的方法求解,上面截图的那段代码被这种方法取代了,该方法是box2d创始人提出的,能有更好的堆叠稳定性。

至于法线问题,当然法线是在碰撞检测阶段求出的。你可能没有想明白整个物理引擎的流程😁,碰撞求解是在碰撞检测阶段之后的,碰撞检测筛选出真正发生碰撞的刚体对,并同时求出碰撞法线和碰撞点(box2d中称作碰撞流形),然后这些碰撞对被送往下一阶段即碰撞处理阶段进行求解。这里所说的“碰撞”只是我们现实生活的概念,计算机不知道什么是“碰撞”,所以我们筛选出的刚体只是在空间(或者2d平面)几何意义上相交的物体,这些相交的物体并不是全都需要进行碰撞求解,比如两个刚体存在微小相交但是已有分离趋势,这时就不需作处理。

如果你也对物理引擎有兴趣的话,大家可以多多交流,有人能和我讨论这些内容我也很高兴。

conanjunn commented 3 years ago

看box2d-lite源码时,一直纠结于这个公式是怎么推导出来的。。。 看了这篇文章真是豁然开朗

THZthz commented 1 year ago

我写了一个2d物理模拟的玩具(https://github.com/THZthz/physics), 但是在物体堆叠时遇到了一个问题:

image 就是在这种情况下物体会往两侧滑落, 但不会有抖动问题 :(

这是我在帧之间碰撞点的缓存查询: image

碰撞约束求解用了累积冲量法, 为了稳定性用了slop和baumgart stabilization

phenomLi commented 1 year ago

我写了一个2d物理模拟的玩具(https://github.com/THZthz/libopus/tree/master/sources/physics/opus), 但是在物体堆叠时遇到了一个问题:

image 就是在这种情况下物体会往两侧滑落, 但不会有抖动问题 :(

这是我在帧之间碰撞点的缓存查询: image

碰撞约束求解用了累积冲量法, 为了稳定性用了slop和baumgart stabilization

可以去知乎找一下ACRL这个同学交流一下,我已经很多忘记了