约束模型#

一旦 碰撞检测 产生了接触,刚体求解器就要决定这些接触、关节限位和等式约束对物体施加什么力。它是刚体求解器三层中的第三层。求解器把所有约束汇集到一个系统中,每一步求解一次,得到能同时满足它们的广义加速度;这里采用允许轻微违反的软约束形式。

关于接触从何而来,参见 碰撞检测;关于声明和开关约束的 API,参见 刚体约束。

约束系统#

记 \(a = \ddot q\) 为所有自由度(DOF)的广义加速度,\(M(q)\) 为关节空间质量矩阵,\(a^{\text{unc}} = M^{-1}\tau\) 为在没有约束激活时、物体在施加力和被动力 \(\tau\) 作用下会产生的无约束加速度。每个约束向雅可比矩阵 \(J\) 贡献一行,把加速度映射到该约束的局部坐标,并为该坐标给出一个目标 \(a_{\text{ref}}\)。

求解器寻找一个加速度:它在质量度量下最接近 \(a^{\text{unc}}\),同时把 \(Ja\) 推向 \(a_{\text{ref}}\):

\[ \min_{a}\; \tfrac12\,(a - a^{\text{unc}})^\top M\,(a - a^{\text{unc}}) \;+\; \tfrac12\,(Ja - a_{\text{ref}})^\top D\,(Ja - a_{\text{ref}}). \]

对角矩阵 \(D\) 设定每个约束的刚度:较大的 \(D_{ii}\) 把 \(Ja\) 几乎拉到 \(a_{\text{ref}}\) 上,较小的 \(D_{ii}\) 则允许它漂移。惩罚是有限的,因此约束从不会被精确强制执行。这正是这里软的含义。

目标函数是二次的,因此其梯度和 Hessian 关于 \(a\) 为常数:

\[ g = M\,(a - a^{\text{unc}}) + J^\top D\,(Ja - a_{\text{ref}}), \qquad H = M + J^\top D\,J. \]

等式约束双向作用,且始终存在。接触与关节限位是不等式:接触只能推,关节限位只在关节到达限位后才产生阻力。这样的行仅在违反时才活跃,摩擦行则受摩擦锥限制。下面的线搜索会在求解过程中决定哪些不等式行处于活跃状态。

约束类型#

每个约束都归结为相同的三个要素:一行雅可比 \(J\)、一个参考加速度 \(a_{\text{ref}}\),以及一个对角刚度 \(D_{ii}\)。它们的区别在于这些要素如何构建。

接触与摩擦#

每个接触点会展开为三到十行约束组成的块,具体取决于 friction_cone 选择的锥以及启用了哪些额外的摩擦轴。max_contacts 限制点的数量。在两种锥下,参考加速度都由穿透深度驱动,因此接触越深,推回越强。

金字塔锥(默认)。 当 friction_cone=gs.friction_cone.pyramidal 时,一个点变成四行,用金字塔近似库仑摩擦锥。金字塔的每条棱都混合法向与切向方向,而不是将它们分开。设接触法线为 \(\mathbf n\)、切向为 \(\mathbf d_1, \mathbf d_2\)、摩擦系数为 \(\mu\),四条棱方向为

\[ \pm\,\mu\,\mathbf d_1 - \mathbf n \qquad\text{and}\qquad \pm\,\mu\,\mathbf d_2 - \mathbf n . \]

任意棱上的非负乘子产生的力,其切向分量被法向分量的 \(\mu\) 倍所限制,从而使合力保持在金字塔内 \(|\mathbf f_t| \le \mu f_n\)。金字塔使摩擦极限依赖于方向。

椭圆锥。 当 friction_cone=gs.friction_cone.elliptic 时,法向行与切向行保持分离,摩擦在每个方向上都受其欧几里得极限 \(\sqrt{f_{t_1}^2 + f_{t_2}^2} \le \mu f_n\) 限制,因此该极限是各向同性的。impratio 是切向与法向的阻抗比,在此锥下默认为 100。椭圆锥不支持可微分仿真。

接触解析。 contact_resolution 设定接触的法向力与摩擦力如何相互解析。

  • gs.contact_resolution.convex: MuJoCo 的公式。接触是单个凸代价,其中摩擦极限 \(|\mathbf f_t| \le \mu f_n\) 联合约束法向与切向部分,因此切向需求可以通过提高 \(f_n\) 来满足。

  • gs.contact_resolution.signorini: 摩擦受接触已经形成的法向力限制,因此该力跟随接触的法向状态,而不是切向需求。求解器通过逐次近似来解析接触,这会额外消耗迭代。

signorini 需要椭圆锥(其行分离为法向行和摩擦圆盘)以及 Newton 求解器(它达到逐次近似的不动点)。若未设置,contact_resolution 在这两项条件都满足时解析为 signorini,否则解析为 convex;enable_mujoco_compatibility 会保持 convex。在不可用的地方请求 signorini 会在构建时抛出异常。

扭转摩擦与滚动摩擦。 点接触不传递力矩,所以默认情况下没有什么能阻止物体原地打转,或球滚动到停下。enable_torsional_friction 为每个点增加一行,抵抗绕法线的旋转;enable_rolling_friction 需要扭转摩擦,再增加两行,抵抗绕切向轴的滚动。强度按几何体由 gs.materials.Rigid 的系数 friction_torsional 与 friction_rolling 设定,二者都是以米为单位的有效接触斑半径;接触对取两者中较大的值。这些行会在每次接触上增加求解开销。

关节限位#

转动关节或移动关节可以带有位置下限和上限。当关节处于其范围内时,不存在约束。当到某个限位的有符号距离变为负值,

\[ \phi = q - q_{\min} < 0 \qquad\text{or}\qquad \phi = q_{\max} - q < 0, \]

求解器会生成一个单自由度不等式,其雅可比 \(J = \pm 1\),参考加速度将关节推回其范围内。enable_joint_limit 全局开启和关闭这一机制。

范围来自模型文件,构建场景后可用 entity.set_dofs_limit(lower, upper, dofs_idx_local) 修改。若只想在某些环境中修改,请传入 envs_idx,并在 RigidOptions 上设置 batch_dofs_info=True,与其他逐环境 DOF 属性的做法相同。

还有一类相关的行用于建模关节中的干摩擦:摩擦损耗系数非零的 DOF 会获得一个约束,在有界力的范围内抵抗运动,与任何限位无关。

等式约束#

等式约束是完整约束,只要存在就把物体绑定在一起。Genesis World 支持三种。

  • Connect: 把不同物体上的两个点固定到同一世界位置,移除 3 个平移 DOF,如同球窝关节。

  • Weld: 让两个坐标系保持固定的相对位姿,移除全部 6 个 DOF。这就是 吸盘夹爪示例 在运行时切换的约束。

  • Joint: 耦合两个标量关节,使一个通过四次多项式跟随另一个,移除 1 个 DOF,如同齿轮机构。

每种约束都会把自己的行写入 \(J\),使求解把受约束的相对速度驱动到零。disable_constraint=True 会关闭所有约束,包括接触。

参考加速度与柔软度#

参考加速度使约束表现得像临界阻尼弹簧,而不是刚性墙。对于当前违反量为 \(\phi\)、变化率为 \(\dot\phi\) 的约束,

\[ a_{\text{ref}} = -b\,\dot\phi - k\,\phi . \]

增益 \(b\) 与 \(k\) 来自一个时间常数和一个阻尼比:\(\phi\) 在约 constraint_timeconst 秒内无过冲地衰减到零。较短的时间常数使约束更刚硬、修正更快,代价是条件数变差。刚度项 \(D_{ii}\) 会按约束的逆惯量缩放,因此轻物体和重物体的响应保持一致。增益与刚度项都按约束从其 sol_params 计算,sol_params 携带 MJCF 模型提供的 solref 与 solimp 值。

求解系统#

求解器迭代地计算广义加速度。constraint_solver 设定算法;两种变体最小化相同的目标函数,并共享相同的线搜索。

  • gs.constraint_solver.Newton(默认):构造 Hessian \(H = M + J^\top D J\),并通过 Cholesky 分解求解 \(H\,\Delta a = -g\) 来执行牛顿步。在 CPU 上,它可以利用 \(H\) 的带状结构进行稀疏分解(sparse_solve);在 GPU 上则使用密集分块分解。它经过少数几次迭代即可收敛,每次迭代都承担分解的开销。

  • gs.constraint_solver.CG:加速度空间中的预处理共轭梯度,使用质量矩阵作为预条件子,并采用 Polak–Ribière 更新。它只需要矩阵-向量乘积,从不需要显式 Hessian,因此在 DOF 或约束很多的场景中内存占用很低。

每次迭代先提出一个搜索方向,然后由线搜索选取沿该方向最小化目标函数的步长 \(\alpha\)。目标函数限制在一条直线上时是二次的,所以精确最小值可以闭式得到,而线搜索会处理不等式行随 \(\alpha\) 变化在活跃与非活跃之间切换的情况。ls_iterations 与 ls_tolerance 限制线搜索的工作量。

当梯度范数与每次迭代的代价改善都低于一个缩放后的容差,或达到 iterations 次迭代后,求解停止:

\[ \varepsilon = \texttt{tolerance} \cdot \overline{m} \cdot n_{\text{dof}}, \]

其中 \(\overline{m}\) 是平均惯量。每一步在可用时从上一 step 的解热启动,否则从无约束加速度 \(a^{\text{unc}}\) 热启动。

另见#