接下来从 rigid case 进入 deformable case(从刚体情况进入可变形情况)
rigid frames → deformable object
一个或多个刚体点/刚体 frame 整体移动→一个 3D 物体可以发生形变
Adding a material law for deformation 现在要给物体加入"材料定律",让它能按照材料属性发生变形。
5-elasticity.scn
XML
<?xml version="1.0"?>
<!-- Adding a material law for deformation -->
<Node name="root" dt="0.01" gravity="0 0 0">
<!-- Needed to use UniformMass / MeshMatrixMass -->
<RequiredPlugin name="Sofa.Component.Mass"/>
<!-- Needed to use ConstantForceField -->
<RequiredPlugin name="Sofa.Component.MechanicalLoad"/>
<!-- Needed to use MechanicalObject -->
<RequiredPlugin name="Sofa.Component.StateContainer"/>
<!-- Needed to use CGLinearSolver -->
<RequiredPlugin name="Sofa.Component.LinearSolver.Iterative"/>
<!-- Needed to use EulerImplicitSolver -->
<RequiredPlugin name="Sofa.Component.ODESolver.Backward"/>
<!-- Needed to use MeshGmshLoader -->
<RequiredPlugin name="Sofa.Component.IO.Mesh"/>
<!-- Needed to use TetrahedronSetTopologyContainer / TetrahedronSetGeometryAlgorithms -->
<RequiredPlugin name="Sofa.Component.Topology.Container.Dynamic"/>
<!-- Needed to use TetrahedronFEMForceField -->
<RequiredPlugin name="Sofa.Component.SolidMechanics.FEM.Elastic"/>
<DefaultAnimationLoop computeBoundingBox="false"/>
<MeshGmshLoader name="meshLoaderCoarse" filename="mesh/liver.msh" />
<!-- Optional: transform the loaded mesh position -->
<!--
<TransformEngine name="transformer"
input_position="@meshLoaderCoarse.position"
translation="0 2 0"
rotation="0 0 0" />
-->
<Node name="Liver">
<EulerImplicitSolver />
<CGLinearSolver iterations="200" tolerance="1e-09" threshold="1e-09"/>
<TetrahedronSetTopologyContainer name="topo" src="@../meshLoaderCoarse" />
<TetrahedronSetGeometryAlgorithms template="Vec3d" name="GeomAlgo" />
<MechanicalObject template="Vec3d"
name="MechanicalModel"
showObject="1" />
<TetrahedronFEMForceField name="FEM"
youngModulus="1000"
poissonRatio="0.4" />
<MeshMatrixMass massDensity="1" />
<ConstantForceField totalForce="1 0 0" />
</Node>
</Node>
学习简化版代码:
XML
<Node name="root" dt="0.01" gravity="0 0 0">
<MeshGmshLoader name="meshLoaderCoarse" filename="mesh/liver.msh" />
<Node name="Liver">
<EulerImplicitSolver />
<CGLinearSolver iterations="200" tolerance="1e-09" threshold="1e-09"/>
<TetrahedronSetTopologyContainer name="topo" src="@../meshLoaderCoarse" />
<TetrahedronSetGeometryAlgorithms template="Vec3d" name="GeomAlgo" />
<MechanicalObject template="Vec3d" name="MechanicalModel" showObject="1"/>
<TetrahedronFEMForceField name="FEM" youngModulus="1000" poissonRatio="0.4" />
<MeshMatrixMass massDensity="1" />
<ConstantForceField totalForce="1 0 0" />
</Node>
</Node>
1.deformable object 可形变物体
在前面 SOFA 场景里,什么东西决定了对象的类型、决定了自由度的性质?
答案是:MechanicalObject 的 template
XML
<MechanicalObject template="Rigid3d" ... />
它表示对象是 3D 刚体,状态包含:位置 x y z + 姿态 orientation / quaternion
现在进入可变形物体后,最关键变化是:
XML
<MechanicalObject template="Vec3d" name="MechanicalModel" showObject="1"/>
从 Rigid3d 改成 Vec3d,这是从刚体到可变形物体时主要改变的东西之一。
区别是:
(1)Rigid3d:
每个自由度 = 位置 xyz + 姿态 quaternion
适合刚体 frame。
(2)Vec3d:
每个自由度 = 位置 xyz
适合可变形体的网格节点。
对于可变形物体,每个节点只是空间中的一个点,它可以移动,但没有自己的刚体姿态。
所以现在的未知量是:每个 mesh node 的 x、y、z 位置
2.TetrahedronSetTopologyContainer(四面体拓扑容器)
如果我们要做一个普通的 3D 可变形物体,就需要一个真正的 3D 有体积的网格。
前面 liver.msh 文件不只包含点,还包含:points / nodes、edges、triangles、tetrahedra
其中 tetrahedra 是四面体单元,是 3D 体积元素。要在 3D 空间里求解力学平衡方程,需要这些 3D 元素作为空间支撑。
可变形仿真不是让一堆独立点乱飞,而是让一个由四面体连接起来的体积物体发生形变。
之前只关心点,所以用:
XML
<PointSetTopologyContainer name="topo" src="@../meshLoaderCoarse" />
现在要做 3D 可变形体,需要完整的四面体拓扑,所以换成:
XML
<TetrahedronSetTopologyContainer name="topo" src="@../meshLoaderCoarse" />
它同样从 meshLoaderCoarse 里读取数据,但这次不再只读取点,而是读取完整的拓扑层级:tetrahedra、triangles、edges、points。
这些信息先由 MeshGmshLoader 加载,然后由 TetrahedronSetTopologyContainer 存储。
3.MechanicalObject 仍然不用手写 position
和之前一样,现在也不需要在 MechanicalObject 里手写:position="..."
XML
<TetrahedronSetTopologyContainer name="topo" src="@../meshLoaderCoarse" />
<MechanicalObject template="Vec3d" name="MechanicalModel" showObject="1"/>
MechanicalObject 会在同一个 Liver 节点里找到 topology container,然后自动用里面的节点位置初始化自己的 position。
MeshGmshLoader 读 mesh
→ TetrahedronSetTopologyContainer 保存四面体拓扑和点位置
→ MechanicalObject 自动拿这些点位置作为初始状态
4.TetrahedronSetGeometryAlgorithms 工具包
XML
<TetrahedronSetGeometryAlgorithms template="Vec3d" name="GeomAlgo" />
它不是主要的物理组件,而是一个 toolkit,工具包 。
它提供几何计算能力,比如:某个四面体的体积是多少、某个三角形的面积是多少、两个点之间的距离是多少。这些几何信息会被后面的物理组件使用。
5.负责物理的组件
后面这两个组件会用到上面的几何算法:
XML
<TetrahedronFEMForceField name="FEM" youngModulus="1000" poissonRatio="0.4" />
<MeshMatrixMass massDensity="1" />
(1)MeshMatrixMass(网格质量矩阵组件)
XML
<MeshMatrixMass massDensity="1" />
这个组件根据体网格和质量密度,进行空间积分,构建质量矩阵。给它一个 mass density,它就能在整个 3D 域上积分,得到 mass matrix。
UniformMass:
前面那种简单平均分配总质量。
MeshMatrixMass:
根据 3D 网格体积和质量密度,计算更合理的质量分布。
(2)TetrahedronFEMForceField
XML
<TetrahedronFEMForceField name="FEM" youngModulus="1000" poissonRatio="0.4" />
TetrahedronFEMForceField 组件 实现的是可变形体的 材料模型 / 本构力学模型。
它定义了:物体如何变形、物体变形后产生什么内部力,这些内部力就是材料本身抵抗变形的力。
- 刚体以前只有:质量 + 外力 → 整体运动
- 可变形体现在多了:材料模型 → 产生内部弹性力 → 发生形变但又抵抗形变
这个 constitutive mechanical model(材料本构模型) 是 linear elasticity 线弹性 模型。
线弹性可以理解成,形变不太大时,力和位移/应变近似成线性关系。类似弹簧里的胡克定律:F = kx
力学里还有很多材料模型,比如一些 hyperelastic model,超弹性模型 ,包括 Mooney-Rivlin、Ogden 等,它们属于非线性材料模型。但这里这个不是那些复杂非线性模型,而是 linear elasticity,线弹性模型。
(a).youngModulus 杨氏模量
youngModulus="1000" 这是 杨氏模量。它代表材料有多硬,单位和压力一样,通常是 Pascal/ Pa。
- Young's modulus 大:材料更硬,不容易变形
- Young's modulus 小:材料更软,更容易变形
(b).poissonRatio 泊松比
poissonRatio="0.4" 这是 泊松比。它和材料的横向变形/可压缩性有关。比如你压缩一个方向,另一个方向会膨胀多少。
poissonRatio 控制材料被拉伸/压缩时,横向会怎么变。
6.刚体和可变形体的对比
前面刚体:
XML
<MechanicalObject template="Rigid3d" .../>
<UniformMass totalMass="1"/>
<ConstantForceField totalForce="1 0 0 0 0 0"/>
它只有一个刚体 frame。受到力以后不会变形,只会整体运动。
现在可变形体:
XML
<TetrahedronSetTopologyContainer .../>
<MechanicalObject template="Vec3d" .../>
<TetrahedronFEMForceField .../>
<MeshMatrixMass .../>
<ConstantForceField totalForce="1 0 0"/>
它是一个 3D mesh。这个网格作为空间积分域,用来计算质量、材料内部力和外力。
ConstantForceField 现在只有 3 个数
- 前面刚体
Rigid3d时外力是 totalForce="1 0 0 0 0 0",因为刚体有 3 个平移力 + 3 个力矩 - 现在
Vec3d时外力变成 totalForce="1 0 0",因为每个节点只有 x y z 三个平移自由度
没有刚体姿态,也没有力矩自由度。现在没有 frame / orientation 的概念,只有空间中的点,所以 force 自身也只有 X、Y、Z 三个分量。
| 刚体阶段 | 可变形体阶段 |
|---|---|
Rigid3d |
Vec3d |
| 一个刚体 frame,有位置和姿态 | mesh 节点,只有 x/y/z 位置 |
PointSetTopologyContainer |
TetrahedronSetTopologyContainer |
| 只用点 | 用四面体体网格 |
UniformMass |
MeshMatrixMass |
| 简单质量 | 根据质量密度和体积积分质量矩阵 |
| 没有材料模型 | TetrahedronFEMForceField 定义材料变形规律 |
totalForce="1 0 0 0 0 0" |
totalForce="1 0 0" |
7.SOFA 不管单位,单位一致性要自己负责
SOFA 不会自动知道你用的是米、毫米、千克还是克,SOFA 只拿数值去计算,所以必须保证所有输入单位是一致的。
比如用国际单位制:
长度:m;质量:kg;时间:s;力:N;杨氏模量:Pa;密度:kg/m³;重力加速度:m/s²
但如果mesh 是毫米单位,又把 Young modulus 当成 Pa 来填,仿真结果就会错。网格单位、杨氏模量、质量密度、重力加速度这些都必须在同一套单位体系里。
所以很多错误都来自 mesh。
因为 mesh 可能来自扫描、MRI、3D scan 或别的软件,里面的尺寸单位有时并不清楚。它可能是毫米,也可能是米,也可能只是某种缩放后的无单位坐标。
所以以后拿到一个 .msh、.obj、.vtk 文件,不要只问"能不能加载",还要问:
这个 mesh 的坐标单位到底是什么?
它的大小是真实尺寸吗?
这会直接影响材料参数、质量、重力和仿真稳定性。