刚体惯量张量计算需先求质心,再用定义式 $ I = \sum m_i (|r_i|^2 I_3 - r_i \otimes r_i) $ 求和;对三角面片则用面密度解析公式累加,并平移至整体质心;最终须对角化以适配物理引擎。

刚体离散质点模型下怎么算 inertia_tensor
直接用定义式求和最稳妥,尤其当你有顶点+质量分布(比如网格带密度或每个顶点附带质量)时。核心公式是:I = Σ m_i * (‖r_i‖² * I₃ − r_i ⊗ r_i),其中 r_i 是第 i 个质点相对于质心的位矢,I₃ 是 3×3 单位阵,⊗ 表示外积。
实操要点:
- 必须先算质心:
com = (Σ m_i * p_i) / (Σ m_i),再对每个点做r_i = p_i - com - 结果是 3×3 对称矩阵,只需存 6 个独立分量(如
Ixx, Iyy, Izz, Ixy, Ixz, Iyz),但计算时建议用std::array或glm::dmat3保持清晰 - 注意单位一致性:质量用 kg,长度用 m,结果才是 kg·m²
- 如果输入是三角面片(如 OBJ 网格),需按面积加权采样质点,或更准确地用三角形面片的解析惯量公式(见下一条)
三角面片组成的薄壳刚体怎么算 inertia_tensor
把每个三角形当作零厚度、均匀面密度的刚体,用解析解累加,比离散采样更准——尤其对表面建模的物体(如 CAD 外壳、角色盔甲)。
关键步骤:
- 先统一换算成单位面密度(σ = 1),算出面密度为 1 时的惯量张量
I₀,最后再乘总质量M(因为I ∝ σ ∝ M) - 单个三角形顶点为
v0, v1, v2,其质心c = (v0 + v1 + v2) / 3,面积A = 0.5 * ‖(v1−v0) × (v2−v0)‖ - 该三角形对原点的惯量(面密度为 1)有闭式:
I_tri = A/12 * (v0·v0 + v1·v1 + v2·v2 + c·c) * I₃ − A/12 * Σᵢⱼ v_i ⊗ v_j(具体系数见 Mirtich 1996 论文) - 别忘了平移:用平行轴定理把每个三角形的惯量从其自身质心移到整体质心 —— 即减去
A * (c ⊗ c)再加上A * ‖c‖² * I₃,但更稳的做法是先算所有三角形相对于世界原点的I,再整体平移至整体质心
glm::dmat3 或 std::array 存惯量张量要注意什么
物理引擎(如 Bullet、PhysX)通常要求惯量张量在**局部坐标系下且已对角化**(即主轴对齐),但你计算出来的是在模型坐标系下的完整张量,不能直接传。
常见疏漏:
- 没做对角化:用
symmetric_eigen_decompose()(或 Eigen 的SelfAdjointEigenSolver)分解得到特征向量矩阵R(即旋转)和对角阵D(即主惯量) - 传给引擎时,只送
D作为local_inertia,同时把R设为刚体的初始朝向(或用于后续转动更新) - 检查对称性:计算后手动验证
abs(I[1][0] - I[0][1]) 等,不对称说明r_i ⊗ r_i手动写错了顺序(应为r.x*r.x, r.x*r.y, ...) - 零质量或退化几何(如共线三点)会导致奇异矩阵,要提前过滤或加 eps 防崩
为什么用 double 而不是 float 算惯量
惯量张量涉及坐标的平方项,当模型尺寸大(如 1e3 m)或坐标值本身含小数偏移(如 12345.6789),float 的 23 位尾数会迅速丢失精度,导致 Ixx 和 Iyy 差几个数量级却算成相等,或者特征值分解失败。
真实案例中容易被忽略的点:
- 读取 OBJ/STL 时默认用
float解析顶点,但中间计算务必升到double - Bullet 的
btVector3默认是float,但btRigidBodyConstructionInfo中的m_localInertia接收的是btVector3,它只存对角元 —— 所以你必须自己完成对角化,再截断为float;这一步的截断误差要评估 - 如果整个管线都用
double(如自研引擎),就别省这点内存,惯量不是热路径,精度比速度重要
实际用的时候,最常卡住的地方不是公式,而是坐标系没对齐、质心没减干净、或者把面片当成实体体积来积分。尤其是从建模软件导出的网格,经常自带全局平移,得先中心归零再算。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











