本文为课程CS417 Physics based animation的课程笔记。本课程从欧拉拉格朗日方程以及数值算法出发,介绍了如何模拟弹簧质点系统,有限元,刚体模拟以及流体模拟(流体还没做,摆烂)。对于课程内的公式,本文在力所能及的范围内适当补充了推导过程。
1.Euler-Lagrange
任何力学系统应满足的法则,给出广义坐标时能快速对系统受力分析。
两点之间直线最短:
对力学系统,定义广义坐标 ,广义速度 ,能量函数 ,动能函数 ,势能函数 ,有:
当物体运动的起始点和结束点 给定时,泛函 取得最小时系统稳定。当 稳定时,其偏导数趋近于0:
(1.3)便是Euler-Lagrange方程。对于任意力学系统,均需满足该方程。当 时,称 为系统广义力。
下面,我们在一维弹簧质点系统验证欧拉拉格朗日方程的正确性:
为位置, 为速度, 为质点动能, 为弹簧势能。
而(1.4) 为牛顿第二定律对应的胡克定律方程。
2.Numeric Method
一般来说动力学系统涉及2阶微分方程 将 打包为一个向量 我们可以将微分方程转为一阶的微分方程: ,对于该方程我们有几种数值解法
2.1 Forward Euler:
将 一阶泰勒展开:
2.2Runge-Kutta
当 时,退化为Forward Euler
当 时,退化为 Heun’s Method:
最为常用的Runge-Kutta方法为:
2.3 Backward Euler:
当函数 是线性变换时,我们可以利用这点推导出逆向Euler公式:
Backward Euler的优点之一是,它会让系统整体能量不断减少。

从Phase Space可以看到,每一步迭代 的坐标都落到轨迹圆的内部,一直迭代下去直到系统静止。这种现象让模拟器数值十分稳定(不会让物体速度越来越快),而且有助于模拟阻力。
2.4 Sycmplectic Euler:
结合了Forward Euler和Backward Euler的优点和缺点。它能保证系统整体的能量不会随着时间变化而太大的变化。
它的做法十分简单,用Forward Euler更新一步,再由Backward Euler更新一步。例如:在弹簧质点系统中,用Forward Euler更新速度,接着,用Backward Euler更新位置:
3.Mass-Spring System 3D
3.1 双质点单弹簧
假设第一个质点坐标维 第二个为,系统广义坐标为 ,弹簧劲度系数为,质点质量矩阵为 ,弹簧初始长度为则弹簧的潜在能为:
其中( 也成立,且不会对公式推导产生影响)
由广义力
在积分时使用backward euler算法:
由于 非线性函数,使用Linearly-Implicit euler 近似,将(3.4)代入(3.3)得:
求解线性系统(3.5)就可得到 ,公式去掉 项便退化为forward euler。
求解时涉及
关于 :
由于 对称,
有上述结论,我们建立了求解单弹簧质点系统的所有数学工具。
3.2 多质点系统
多质点系统可通过分解为多个双质点系统求解。
令 , ,若第个弹簧连接 两点,则,潜在能可以写为:
其中
对广义力公式也就有
通过些公式,公式(3.5) 可扩展到高维。在每次迭代中,找出所有弹簧单个计算 和 乘上矩阵 后将其叠加起来,带入(3.5)中求解线性系统,便可求解。计算 和 的代码为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| void assemble_forces(Eigen::VectorXd &f, Eigen::Ref<const Eigen::VectorXd> q, Eigen::Ref<const Eigen::VectorXd> qdot, Eigen::Ref<const Eigen::MatrixXd> V, Eigen::Ref<const Eigen::MatrixXi> E, Eigen::Ref<const Eigen::VectorXd> l0s, double mass, double k) {
f.resize(V.rows() * 3); f.setZero();
for (uint32_t i = 0;i < E.rows();i++) { uint32_t v0 = E(i, 0), v1 = E(i, 1); Eigen::Vector3d q0(q(v0 * 3), q(v0 * 3 + 1), q(v0 * 3 + 2)); Eigen::Vector3d q1(q(v1 * 3), q(v1 * 3 + 1), q(v1 * 3 + 2));
double l0 = l0s(i); Eigen::Vector6d spring_force; dV_spring_particle_particle_dq(spring_force, q0, q1, l0, k); spring_force = -spring_force;
f(3 * v0) += spring_force(0); f(3 * v0 + 1) += spring_force(1); f(3 * v0 + 2) += spring_force(2); f(3 * v1) += spring_force(3); f(3 * v1 + 1) += spring_force(4); f(3 * v1 + 2) += spring_force(5); } };
void assemble_stiffness(Eigen::SparseMatrixd &K, Eigen::Ref<const Eigen::VectorXd> q, Eigen::Ref<const Eigen::VectorXd> qdot, Eigen::Ref<const Eigen::MatrixXd> V, Eigen::Ref<const Eigen::MatrixXi> E, Eigen::Ref<const Eigen::VectorXd> l0, double k) { K.resize(V.rows() * 3, V.rows() * 3); K.setZero(); triplets.clear(); accumlatedTriplets.clear();
{ Timer timer("compute stiffness per-spring"); for (uint32_t i = 0; i < E.rows(); i++) { uint32_t v0 = E(i, 0), v1 = E(i, 1); Eigen::Vector3d q0(q(v0 * 3), q(v0 * 3 + 1), q(v0 * 3 + 2)); Eigen::Vector3d q1(q(v1 * 3), q(v1 * 3 + 1), q(v1 * 3 + 2)); double l = l0(i);
Eigen::Matrix66d H; d2V_spring_particle_particle_dq2(H, q0, q1, l, k); H = -H;
SetK(v0, v1, triplets, H); } } { Timer timer("sort triplets"); std::sort(triplets.begin(), triplets.end(), [&](const Tri& lhs, const Tri& rhs) { if (lhs.row() < rhs.row()) { return true; } else if (lhs.row() > rhs.row()) { return false; } return lhs.col() < rhs.col(); } ); triplets.push_back(Tri(V.rows() * 3 + 1, V.rows() * 3 + 1, 0)); }
{ Timer timer("accumulation"); uint32_t row = triplets[0].row(), col = triplets[0].col(); double val = triplets[0].value(); for (uint32_t i = 1; i < triplets.size(); i++) { Tri& tri = triplets[i]; if (tri.row() == row && tri.col() == col) { val += tri.value(); } else { if (abs(val) > 1) { accumlatedTriplets.push_back(Tri(row, col, val)); } row = tri.row(), col = tri.col(), val = tri.value(); } } K.setFromTriplets(accumlatedTriplets.begin(), accumlatedTriplets.end()); }
|
为了视觉效果,我们需要在一些质点添加定点约束 :
其中, 会从中挑选出非定点坐标,而 包含所有定点坐标,若点为顶点, ,否则 。在求和 时需要用完整坐标 。当 更新速度时,需要 将其带入(3.5)并求解线性系统。


4. Finite Element Method
与弹簧质点系统不同,有限元假设一个物体由多个实心小四棱锥构成。每个四棱锥处处密度均匀且相等。
4.1 单个四棱锥

有限元中,系统的动能和势能主要四棱锥的运动和形变产生,这通过对四棱锥内每个质点的位置积分得到。四棱锥每个质点的位置通过其时间 和未形变时位置 的函数描述 。 为质点在 空间的坐标,而 为质点在 空间下的坐标。
对四棱锥的四个顶点 ,可以通过权重满足 ,描述其内部任意一点坐标
该方程在 空间中也成立。对于四棱锥内任意一点 ,加上归一化约束我们能通过求解一下方程得到
消去 得到:
对每个质点用 (4.2)解出 且给出时刻四顶点坐标后,可用(4.1)求出其在 时刻的位置 。
因此可以用任意时刻四个顶点位置表示系统广义坐标
对(4.1)两边使用向量化算子 ,有:
其中 , 为克罗内克积
对每个四棱锥内质点,其动能有
其中 为密度,
对(4.4)积分有
其中积分区域 为reference 空间下的四棱锥, 有:
动能可以在预计算阶段通过逐个积分 并在模拟过程中带入(4.5)中求得。对单个积分有:
其中 积分区域
由(4.2)可得:
由行列式的几何意义, 行列式的值为3个列向量构成的平行六面体的体积,也就是3个列向量所构成4棱锥的体积的6倍。
其中 为四棱锥在reference空间下的体积而:
由于选取的任意性,积分值应关于索引对称,因此:
将其带入(4.6)便可求得矩阵
对于系统弹性势能由每个体积微元的形变定义

其中在deform 空间下
其中 表示一个体积微元的形变,而函数 描述了该形变下体微元的弹性势能。常用的 为Neohookean Strain Energy Density,课程中使用的形式为:
其中 C,D为材料参数。
令 ,𝟙 由公式(4.2)和 的线性约束有:
将(4.9),(4.10)带入(4.1)得
则 有
其中有𝟙
两边同时取向量化算子 有:
其中 为四棱锥广义坐标,
而课程中给出的公式为:
这里的结论和课程中的不同,原因是这里采用以列为主展开而课程默认以行为主展开。两个结论均正确。潜在能公式可写为:
对整个reference空间积分,由于 与 无关,因此:
对于有多个有限元四棱锥组成的网格体,我们可以用类似弹簧质点系统的方式计算整个系统的动能和势能。
对于整个网格体,其广义坐标为所有顶点的位置 ,为了能单独研究每个四棱锥的能量,定义选择矩阵
其中 为四棱锥 四个顶点。而根据(4.5)和(4.12),系统的动能和势能公式能表示为
对其求导有:
5. Backward Implicit Euler Optimization
与弹簧质点类似,我们同样使用backward euler求解有限元:
这里 定义为:
将非线性方程求解问题转为优化问题,令 (5.1)可以写为:
其中由有:
则将(5.4)带入(5.3)并对等式左边积分有:
因此,问题转化为:
常见的优化算法见优化算法
对于牛顿法:
其中 当前正在搜索的 的值。
6. Cloth(FEM 2D)
和3维FEM类似,2D FEM可用三角形网格表示每个有限元
这里方程的维度高于未知数的维度,原因在于位置 不一定和 在一个平面上。因此,我们需要让 在平面 上的投影 满足:

关于如何求得投影值,我们可以将问题转为优化问题解决。令 ,则 在平面上对应点为 到平面的最近点,也就满足:
令𝟙 , 有
令 对 求导有:
当 为0时,即为所求:
可用 点在平面的投影坐标 和其到平面的距离 来表示其到有限元顶点的距离函数
其中 为平面法线。这种表示方式相比于单纯的三角面片可以表示布料的厚度。
对于系统动能我们有:
由于布料很薄。我们可以假定积分值在 上恒定,且布料处处厚度相等。令布料厚度为 :
其中积分域 为布料对应的三角形区域。类似(4.3)的推导过程,我们能得到
类似(4.6.1),(4.6.2)有:
TODO…
7. Rigidbody
可用和deformable的FEM类似方法分析刚体。
由于刚体内任意两点的相对距离不会随时间变化:
因此,我们可以用旋转矩阵 描述刚体的转动:
(7.1)对时间求导有:
其中 为该点线速度。将(-2.5)带入(7.2)可得:
令广义坐标 带入得:
利用公式(7.3)可计算刚体内任意一点在世界空间下的线速度 对整个刚体积分,有:
为了方便研究,取reference space下的坐标为 ,质心坐标 满足 ,则:
将(7.5)带入(7.4)得:
其中:
为刚体转动惯量
为刚体质量。带入(7.5)得:
由于刚体无形变,弹性势能为0。因此,系统潜在能
带入(1.3)得(这公式我不会推,直接抄结论):
其中 为系统外力, 为系统外力矩。
得到 的显式更新公式:
由于 有:
则:
9.Rigidbody Contact
对于单刚体系统,其速度更新公式为:
整理后有:
其中:
对于双刚体系统当两刚体相撞时有:

撞击点, 同理。
撞击处的应力 其中, 为力度大小, 为撞击点法线的反方向。
将 转到reference space,求解reference space下的力矩:
将该力矩转到world space有:
则对刚体A除了外力 和外力矩 ,应存在撞击处的力和力矩。将 带入(9.1)中有:
由(7.3)有:
为 的Jacobian 矩阵。对任意广义速度 有:
将(9.3)带入(9.2)中得到:
对于多刚体系统

每个刚体有多个接触点,(9.5)可以变形为:
其中A为研究的当前刚体, 为刚体 的第个接触点。刚体 与 在点i接触
(9.6)需满足约束:
令 则:
对单个接触点i将其与其它接触点分离有:
其中 为系统未约束速度,而 为系统约束产生的速度其中 为系统除接触点i外对刚体A产生的约束力
将公式(7.3)带入约束(9.7.2)得:
将公式(9.9)带入约束(9.10)得:
令 ,$\gamma_i = (g_i^B)^T(\dot q_B^+\Delta t M_B^{-1} f_i^B) + (g_i^A)^T (\dot q_A^ + \Delta t M_A^{-1} f_i^A)$ 则约束(9.7)可变为:
解得:
对于整个系统,可利用Projected Gauss-Seidel法求解线性方程组(9.11.2):
- 对系统所有contact,初始化
- 进入循环,直到 满足 约束(9.11)
- 循环遍历每个contact点:
- 根据当前 计算,
- 将, 带入(9.12)中,计算
关于Gauss-Seidel详细说明可以看Appx 4
Appx 2. Time Derivative of Rotation Matrix

绕轴 旋转的质点 的线速度为:
定义向量 为质点的角速度 有:
将 写成矩阵相乘形式带入(-2.2)中有:
解该常微分方程,得:
(且的谱半径 )
则当 极小时:
对任意时刻:
(-2.4)有:
Appx 4 Gauss-Seidel and Rigidbody Contact Solving
TODO: