静止接触
多个刚体相互接触时,力相互影响,需要建立方程组,同时求解。
算法
刚体上一点的加速度
刚体移动x(t),旋转R(t),那么其上的一点p0:
p(t)=R(t)p0+x(t)
速度:
p˙(t)=R˙(t)p0+x˙(t)=ω(t)×R(t)p0+v(t)=ω(t)×(R(t)p0+x(t)−x(t))+v(t)=ω(t)×(p(t)−x(t))+v(t)=ω(t)×r(t)+v(t)
其中,r(t)=p(t)−x(t)
加速度:
p¨(t)=ω˙(t)×r(t)+ω(t)×r˙(t)+v˙(t)=ω˙(t)×r(t)+ω(t)×(p˙(t)−x˙(t))+v˙(t)=ω˙(t)×r(t)+ω(t)×(ω(t)×r(t)+v(t)−v(t))+v˙(t)=ω˙(t)×r(t)+ω(t)×(ω(t)×r(t))+v˙(t)
刚体上一点的角加速度
刚体上一点的角速度即刚体的角速度ω(t),由角动量L(t)=I(t)ω(t),有:
ω(t)=I−1L(t)
角加速度:
ω˙(t)=I˙−1L(t)+I−1(t)L˙(t)
角动量的变化率等于力矩L˙(t)=τ(t),而转动惯量:
I−1(t)=R(t)Ibody−1R(t)T
I˙−1(t)=R˙(t)Ibody−1R(t)T+R(t)Ibody−1R˙(t)T
因为R˙(t)=ω(t)×R(t),有:
R˙(t)T=(ω(t)×R(t))T=R(t)T(ω(t)∗)T=−R(t)Tω(t)∗
因此:
I˙−1(t)=R˙(t)Ibody−1R(t)T+R(t)Ibody−1R˙(t)T=ω(t)×R(t)Ibody−1R(t)T+R(t)Ibody−1(−R(t)Tω(t)∗)=ω(t)×I−1(t)−I−1(t)ω(t)∗
ω˙(t)=I˙−1L(t)+I−1(t)L˙(t)=(ω(t)×I−1(t)−I−1(t)ω(t)∗)L(t)+I−1(t)L˙(t)=ω(t)×I−1(t)L(t)−I−1(t)ω(t)∗L(t)+I−1(t)L˙(t)=ω(t)×ω(t)−I−1(t)ω(t)∗L(t)+I−1(t)L˙(t)=−I−1(t)ω(t)∗L(t)+I−1(t)L˙(t)=−I−1(t)ω(t)×L(t)+I−1(t)L˙(t)=I−1(t)L(t)×ω(t)+I−1(t)L˙(t)=I−1(t)(L(t)×ω(t)+L˙(t))
碰撞
在t时刻,第i个接触点处的两个刚体a和b的距离:
di(t)=n^i(t)⋅(pa(t)−pb(t))
要满足静止的条件,有:
di(t)=d˙i(t)=d¨i(t)=0
d˙i(t)=n^˙i(t)⋅(pa(t)−pb(t))+n^i(t)⋅(p˙a(t)−p˙b(t))
d¨i(t)=n^¨(t)⋅(pa(t)−pb(t))+2n^˙i(t)⋅(p˙a(t)−p˙b(t))+n^i(t)⋅(p¨a(t)−p¨b(t))
因为接触di(t)=0,即pa(t)−pb(t)=0,所以:
d¨i(t)=2n^˙i(t)⋅(p˙a(t)−p˙b(t))+n^i(t)⋅(p¨a(t)−p¨b(t))
第一项2n^˙i(t)⋅(p˙a(t)−p˙b(t))只与速度相关。
由前面刚体上一点的加速度,有:
p¨a(t)=ω˙a(t)×ra(t)+ωa(t)×(ωa(t)×ra(t))+v˙a(t)
第j个接触对刚体a的于pj处施加力fjn^j(t),则其有影响:
v˙a(t)=ma−1fin^j(t)
ω˙a(t)=Ia−1(t)L(t)×ωa(t)+Ia−1(t)L˙a(t)=Ia−1(t)La(t)×ωa(t)+Ia−1(t)τa(t)=Ia−1(t)La(t)×ωa(t)+Ia−1(t)(pj−xa(t))×fjn^j(t)
故:
p¨a(t)=[Ia−1(t)La(t)×ωa(t)+Ia−1(t)(pj−xa(t))×fjn^j(t)]×ra(t)+ωa(t)×(ωa(t)×ra(t))+ma−1fin^j(t)=fi(ma−1n^j(t)+Ia−1(t)(pj−xa(t))×n^j(t)×ra(t))+Ia−1(t)La(t)×ωa(t)×ra(t)+ωa(t)×(ωa(t)×ra(t))
第一项与fi有关,第二项无关
非接触力合力对刚体a于第i个接触点处有影响:
p¨aext(t)=ma−1Fa(t)+Ia−1(t)τa(t)×ra
把所有对刚体a有影响的力产生的加速度求和:
p¨a(t)=j∑p¨aj(t)+p¨aext(t)
刚体b同理
所以整理与fi有关的项,和无关的项:
d¨i(t)=2n^˙i(t)⋅(p˙a(t)−p˙b(t))+n^i(t)⋅(p¨a(t)−p¨b(t))=j∑ajfj+bi
整理成矩阵的形式:
⎣⎡d¨1⋮d¨i⋮d¨m⎦⎤=0
整理成矩阵的形式:
⎣⎡a11ai1am1⋯⋮⋯⋮⋯a1maimamm⎦⎤⎣⎡f1⋮fi⋮fm⎦⎤Ax=⎣⎡b1⋮bi⋮bm⎦⎤=b
其中:
bi=2n^˙i(t0)⋅(p˙a(t0)−p˙b(t0))+n^i(t0)[ma−1Fa(t)+Ia−1(t)τa(t)×ra+Ia−1(t)La(t)×ωa(t)×ra(t)+ωa(t)×(ωa(t)×ra(t))]+n^i(t0)[mb−1Fb(t)+Ib−1(t)τb(t)×rb+Ib−1(t)Lb(t)×ωb(t)×rb(t)+ωb(t)×(ωb(t)×rb(t))]
aij=n^i(t0)[ma−1n^j(t0)+Ia−1(t0)(pj−xa(t0))×n^j(t0)×ra(t0)+mb−1n^j(t0)+Ib−1(t)(pj−xb(t0))×n^j(t0)×rb(t0)]
程序
计算bi:
VectorXf ForceBasedDynamics::ComputeBVector(std::vector<Contact> &C)
{
VectorXf B(C.size());
B.setZero();
for (int i = 0; i < C.size(); ++i)
{
Contact &c = C[i];
Rigidbody &a = c.a_;
Rigidbody &b = c.b_;
Vector3f &n = c.normal_;
Vector3f ra = c.position_ - a.GetPosition();
Vector3f rb = c.position_ - b.GetPosition();
Vector3f f_ext_a{0.F, g, 0.F};
Vector3f f_ext_b{0.F, g, 0.F};
Vector3f t_ext_a{0.F, 0.F, 0.F};
Vector3f t_ext_b{0.F, 0.F, 0.F};
Vector3f a_ext_part, a_vel_part, b_ext_part, b_vel_part;
a_ext_part = f_ext_a * a.GetMassInv() + ((a.GetInertiaMomentInv() * t_ext_a).cross(ra));
b_ext_part = f_ext_b * b.GetMassInv() + ((b.GetInertiaMomentInv() * t_ext_b).cross(rb));
if (a.GetMassInv() == 0.F)
{
a_vel_part = Vector3f::Zero();
}
else
{
a_vel_part = (a.GetAngularVelocity().cross(a.GetAngularVelocity().cross(ra))) +
((a.GetInertiaMomentInv() *
(a.GetInertiaMomentInv().inverse() * a.GetAngularVelocity().cross(a.GetAngularVelocity())))
.cross(ra));
}
if (b.GetMassInv() == 0.F)
{
b_vel_part = Vector3f::Zero();
}
else
{
b_vel_part = (b.GetAngularVelocity().cross(b.GetAngularVelocity().cross(rb))) +
((b.GetInertiaMomentInv() *
(b.GetInertiaMomentInv().inverse() * b.GetAngularVelocity().cross(b.GetAngularVelocity())))
.cross(rb));
}
float k1 = n.dot((a_ext_part + a_vel_part) - (b_ext_part + b_vel_part));
Vector3f ndot = computeNdot(c);
Vector3f pta = a.GetVelocity() + a.GetAngularVelocity().cross(ra);
Vector3f ptb = b.GetVelocity() + b.GetAngularVelocity().cross(rb);
float k2 = 2.f * ndot.dot(pta - ptb);
B(i) = k1 + k2;
}
return B;
}
计算aij:
float compute_aij(const Contact &ci, const Contact &cj)
{
if ((&ci.a_ != &cj.a_) && (&ci.a_ != &cj.b_) && (&ci.b_ != &cj.a_) && (&ci.b_ != &cj.b_))
{
return 0;
}
Rigidbody &a = ci.a_;
Rigidbody &b = ci.b_;
const Vector3f &ni = ci.normal_;
const Vector3f &nj = cj.normal_;
const Vector3f &pi = ci.position_;
const Vector3f &pj = cj.position_;
Vector3f ra = pi - a.GetPosition();
Vector3f rb = pi - b.GetPosition();
Vector3f force_on_a = {0.F, 0.F, 0.F};
Vector3f torque_on_a = {0.F, 0.F, 0.F};
if (&cj.a_ == &ci.a_)
{
force_on_a = nj;
torque_on_a = (pj - a.GetPosition()).cross(nj);
}
else if (&cj.b_ == &ci.a_)
{
force_on_a = -nj;
torque_on_a = (pj - a.GetPosition()).cross(-nj);
}
Vector3f force_on_b = {0.F, 0.F, 0.F};
Vector3f torque_on_b = {0.F, 0.F, 0.F};
if (&cj.a_ == &ci.b_)
{
force_on_b = nj;
torque_on_b = (pj - b.GetPosition()).cross(nj);
}
else if (&cj.b_ == &ci.b_)
{
force_on_b = -nj;
torque_on_b = (pj - b.GetPosition()).cross(-nj);
}
Vector3f a_linear = force_on_a * a.GetMassInv(), a_angular = (a.GetInertiaMomentInv() * torque_on_a).cross(ra);
Vector3f b_linear = force_on_b * b.GetMassInv(), b_angular = (b.GetInertiaMomentInv() * torque_on_b).cross(rb);
return ni.dot((a_linear + a_angular) - (b_linear + b_angular));
}
Vector3f computeNdot(const Contact &c)
{
return c.b_.GetAngularVelocity().cross(c.normal_);
}
建构A矩阵:
Eigen::MatrixXf ForceBasedDynamics::BuildMatrix(std::vector<Contact> &C)
{
MatrixXf M(C.size(), C.size());
for (int i = 0; i < C.size(); ++i)
{
for (int j = 0; j < C.size(); ++j)
{
M(i, j) = compute_aij(C[i], C[j]);
assert(!isnan(M(i, j)));
}
}
return M;
}
迭代求解:
const size_t kIterateCount = 50;
Eigen::VectorXf CraneMath::solveGS(Eigen::MatrixXf M, Eigen::VectorXf b) {
Eigen::VectorXf x(b.size());
x.setOnes();
int n = 0;
while (n < kIterateCount) {
for (int i = 0; i < b.size(); i++) {
float delta = 0.F;
for (int j = 0; j < b.size(); j++) {
if (i != j) {
delta += M(i, j) * x(j);
}
}
x(i) = (b(i) - delta) / M(i, i);
}
n++;
}
return x;
}
测试

参考
- Baraff D. Physically based modeling: Rigid body simulation[J]. SIGGRAPH Course Notes, ACM SIGGRAPH, 2001, 2(1): 2-1.