前向动力学#
单步的第一层放置物体、构建质量矩阵,并求解 \(M(q)\,\ddot q = \tau\),得到它们在无接触作用下的加速度。随后 检测 与 约束求解 会修正该加速度。
运动学#
状态是 qpos 和 qvel,而不是每个连杆的位姿。前向运动学会从根部向外遍历每棵运动学树,把父连杆位姿与关节变换复合,从而将每条连杆、每个几何体和每个附着的坐标系放到世界空间。第二遍传播速度。检测读取几何体位姿,约束求解器读取由这些位姿构建的雅可比矩阵。传感器与渲染读取连杆坐标系。两遍都会在每个子步结束时重新运行,因此在两次 step 之间调用读取接口,观察到的是该 step 产生的状态。
质量矩阵#
求解器用复合刚体惯量组装 \(M\):从叶到根折叠,使每条连杆携带其子树的空间惯量,然后在每个子步分解一次。后续把力映射为加速度的阶段会复用该分解,包括约束求解器的每次迭代。
接触之前的力#
\(\tau\) 汇集了与接触无关的力:按每个自由度(DOF)的控制模式计算的驱动力,以及阻尼与刚度这类被动的关节力。在此基础上,它还会加入重力、科里奥利与离心项,以及自上一 step 以来通过 API 施加的任何外力。每个实体的 gravity_compensation 会缩放重力贡献。用分解后的 \(M\) 求解,即得到无约束加速度。
积分#
求解器先积分速度,再用新速度积分位置,因此顺序是半隐式而非显式:
对旧速度计算的阻尼力可能会过冲,因此每种方案都把关节阻尼并入有效质量。它们的区别在于还覆盖了什么,由 RigidOptions 上的 integrator 选择:
|
隐式阻尼之外还覆盖什么 |
开销 |
|---|---|---|
|
无。自由物体采用上面的更新。 |
每个子步一次分解。 |
|
速度执行器的偏置并入有效质量,独立的自由物体按隐式中点法则推进。 |
每个子步两次分解。 |
|
同上,并带入约束加速度和外力加速度。 |
每个子步一次分解;修正为近似值。 |
我们默认使用 approximate_implicitfast,因为它避免计算两次逆质量矩阵。该近似并不精确,因此当场景足够刚硬、差异会体现在轨迹中时,应选择 implicitfast。在 Euler 下,对无阻尼 DOF,速度更新恰好是 qvel += dt * qacc。
时间步与子步#
dt 是一次 scene.step() 推进的间隔,它决定了控制回路的速率。substeps 把它划分为 dt / substeps 次、每次经过全部三层,只在 dt 边界处暴露状态,因此物理可以以比控制器更细的间隔运行。两者的开销都线性增长,substeps 默认为 1。相对于场景过大的 step 会把状态推出有效范围;力或加速度中出现 NaN 时会抛出异常,而不是继续传播。
另见#
碰撞检测:在这一层移动物体之后查找接触。
约束模型:修正该加速度的求解过程。
RigidSolver:
RigidOptions与integrator的取值。模拟器:
SimOptions,dt和substeps所在之处。