分离被碰撞物体, 求碰撞冲量

如果已经知道了两个相互碰撞的物体进行碰撞前的 线速度, 角速度, 质心坐标, 被碰撞点的坐标, 碰撞方向, 质量, 转动惯量, 恢复系数, 那么可以用以下公式求得两个物体对对方造成的碰撞冲量大小:

J = -vr (e + 1) / ( 1/m1 + 1/m2 + n * ((r1 × n) / I1) × r1 + n * ((r2 × n) / I2) × r2 )

其中:

m1与m2是两物体的质量

I1与I2是两物体的转动惯量

e是恢复系数

r1r2是从物体质心指向被碰撞点的向量, 也就是力臂

vr = v1' - v2'

v1' = v1 + r1×ω1, 即把角速度转成线速度也加到速度上来进行计算

n是两物体的碰撞方向向量, 上一篇有提到如何求

 

求得J之后, 将冲量分别施加在两物体上可以得到物体被碰撞后的线速度和角速度:

v1+ = v1- + (Jn) / m1

ω1+ω1- + (r1 × Jn) / I1

v2+ = v2- + (- Jn) / m2

ω2+ = ω2- + (r2 × - Jn) / I2

 

上面的公式可以在《游戏开发物理学》的103页找到, 有写如何推导. 也可以看这里的最后一部分.

 

applyImpulse: function (oImpulse, oPosition) {
                this.velocity = this.velocity.add(oImpulse.mul(this.inverseMass));
                this.angularVelocity += oPosition.sub(this.centroid).cross(oImpulse) * this.inverseInertia;
            }
applyCollisionImpulse: function (bodyA, bodyB, contactPoint, normal, e) {
                var cp = contactPoint,
                    rA = cp.sub(bodyA.centroid),
                    rB = cp.sub(bodyB.centroid),
                    vA = bodyA.velocity.add(rA.scalarCross(bodyA.angularVelocity)),
                    vB = bodyB.velocity.add(rB.scalarCross(bodyB.angularVelocity)),
                    invIA = bodyA.inverseInertia,
                    invIB = bodyB.inverseInertia,
                    invMA = bodyA.inverseMass,
                    invMB = bodyB.inverseMass,
                    vr = vA.sub(vB),
                    rsnA = rA.cross(normal),
                    rsnB = rB.cross(normal),
                    kn = invMA + invMB + rsnA * rsnA * invIA + rsnB * rsnB * invIB,
                    jn = -(1 + e) * vr.dot(normal) / kn,
                    impulse = normal.mul(jn);

                    bodyA.applyImpulse(impulse, cp);
                    bodyB.applyImpulse(impulse.negate(), cp);
                }

2D向量与标量叉乘可以这样算:

scalarCross: function (num) {
                return new Vector2D(-num * this.y, num * this.x);
            }

 

如果希望添加摩擦力, 可以这样修改:

applyCollisionImpulse: function (bodyA, bodyB, cp, normal, e, friction) {
                var rA = cp.sub(bodyA.centroid),
                    rB = cp.sub(bodyB.centroid),
                    vA = bodyA.velocity.add(rA.scalarCross(bodyA.angularVelocity)),
                    vB = bodyB.velocity.add(rB.scalarCross(bodyB.angularVelocity)),
                    invIA = bodyA.inverseInertia,
                    invIB = bodyB.inverseInertia,
                    invMA = bodyA.inverseMass,
                    invMB = bodyB.inverseMass,
                    vr = vA.sub(vB),
                    rsnA = rA.cross(normal),
                    rsnB = rB.cross(normal),
                    kn = invMA + invMB + rsnA * rsnA * invIA + rsnB * rsnB * invIB,
                    jn = -(1 + e) * vr.dot(normal) / kn,
                    impulse = normal.mul(jn);

                    bodyA.applyImpulse(impulse, cp);
                    bodyB.applyImpulse(impulse.negate(), cp);

                    vA = bodyA.velocity.add(rA.scalarCross(bodyA.angularVelocity));
                    vB = bodyB.velocity.add(rB.scalarCross(bodyB.angularVelocity));
                    vr = vA.sub(vB);
                    var tangent = vr.sub(normal.mul(vr.dot(normal))).normalize(),
                        rstA = rA.cross(tangent),
                        rstB = rB.cross(tangent),
                        kt = invMA + invMB + rstA * rstA * invIA + rstB * rstB * invIB,
                        jf = Math.min(vr.dot(tangent) / kt, Math.abs(jn * friction)),
                        tangentImpulse = tangent.mul(jf);

                    bodyA.applyImpulse(tangentImpulse.negate(), cp);
                    bodyB.applyImpulse(tangentImpulse, cp);
                }

其中jf = Math.min(vr.dot(tangent) / kt, Math.abs(jn * friction))中的jn * friction就是 μ * 压力冲量, vr.dot(tangent) / kt 是使得摩擦力恰好使切线方向速度变成0的冲量大小, 因为摩擦力会在相对速度为0时变成0, 不如此计算最大摩擦冲量的话施加摩擦冲量后可能会导致切向方向物体反向加速.

 

仅靠碰撞冲量实际无法完全分离两个物体, 因为物体可以相互嵌入而不是恰好接触. 已知两物体的相互嵌入距离的话可以用下面的方法修正物体的位置:

correctPosition: function (bodyA, bodyB, penetration) {
                var maxPenetration = 0.05,
                    correctPercent = 0.4,
                    correction,
                    tA,
                    tB;
                if (penetration <= maxPenetration) {
                    return;
                }
                correction = this.normal.mul((penetration - maxPenetration) / (bodyA.inverseMass + bodyB.inverseMass) 
                    * correctPercent);
                tA = correction.negate().mul(bodyA.inverseMass);
                tB = correction.mul(bodyB.inverseMass);
                bodyA.translate(tA.x, tA.y);
                bodyB.translate(tB.x, tB.y);
                return;
            }

这里有简单解释这个函数是如何工作的.

 

转载于:https://www.cnblogs.com/pngx/p/4260419.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在MATLAB中,可以通过模拟小球碰撞来研究物体之间的碰撞行为。下面是一个简单的示例代码,用于模拟两个小球的碰撞过程: ```matlab % 定义小球的初始位置、速度和半径 pos1 = [0, 0]; % 小球1的初始位置 vel1 = [1, 0]; % 小球1的初始速度 radius1 = 1; % 小球1的半径 pos2 = [5, 0]; % 小球2的初始位置 vel2 = [-1, 0]; % 小球2的初始速度 radius2 = 1; % 小球2的半径 % 模拟碰撞过程 numSteps = 100; % 模拟的步数 dt = 0.01; % 时间步长 for step = 1:numSteps % 计算小球之间的距离和相对速度 distance = norm(pos2 - pos1); relativeVel = vel2 - vel1; % 如果小球之间有碰撞发生 if distance <= radius1 + radius2 % 计算碰撞后的速度 normal = (pos2 - pos1) / distance; % 碰撞法线方向 impulse = dot(relativeVel, normal) * normal; % 碰撞冲量 vel1 = vel1 + impulse; % 更新小球1的速度 vel2 = vel2 - impulse; % 更新小球2的速度 end % 更新小球的位置 pos1 = pos1 + vel1 * dt; pos2 = pos2 + vel2 * dt; % 绘制小球的位置 plot(pos1(1), pos1(2), 'ro', 'MarkerSize', radius1*10); hold on; plot(pos2(1), pos2(2), 'bo', 'MarkerSize', radius2*10); xlim([-10, 10]); ylim([-10, 10]); axis equal; hold off; pause(0.01); end ``` 这段代码模拟了两个小球的碰撞过程。首先定义了两个小球的初始位置、速度和半径。然后通过循环模拟碰撞过程,计算小球之间的距离和相对速度,如果小球之间有碰撞发生,则根据碰撞法线方向和碰撞冲量更新小球的速度。最后更新小球的位置,并通过绘图函数将小球的位置可视化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值