Replace the flex metric factorization with a block preconditioner
Every step, the flex block of the implicit effective metric M + K was factorized by sparse Cholesky, because K depends on the configuration. On model/flex/bag.xml, added here, that is roughly half the step, against a comparable share for the constraint solve it exists to accelerate. Keep only the metric's per-vertex 3x3 diagonal blocks, prefactored. Neither consumer needs the exact inverse: the CG constraint solver only wants a preconditioner, and qacc_smooth can come from an iterative solve using those blocks. They are O(n) to build and to apply, but weaker, so CG runs about twice the iterations and qacc_smooth becomes an iteration rather than a direct solve. Net, the bag model steps roughly twice as fast. The preconditioner, by metric state. Inactive, meaning no flex elasticity or an explicit integrator: M^-1, unchanged. Bending only (nefmK == 0): M^-1 plus the exact constant bending factor from mj_setConst on the dofs it covers, unchanged; that factor is built at model compile time and costs nothing per step. Per-step stiffness: M^-1 plus the 3x3 blocks, where before it was a per-step sparse Cholesky, or, when M couples across the flex block, an inner PCG of up to 50 iterations run once per outer CG iteration. Only models carrying per-step stretch stiffness change in wall-clock. Both ponchos hold their timing and take slightly fewer CG iterations than before, because the preconditioner is now symmetric: it applies M^-1 and the covered blocks to disjoint sets of dofs, where previously the two overlapped and the operator was not symmetric, which PCG requires. mjd_effSolve is the accurate solve of (M + K)x = b; what used to carry that name only preconditions and is now mjd_effPrec. Its CG guarded the division by pAp with mjMINVAL, an absolute floor on a quantity that scales with the square of the right-hand side, so a small b aborted the solve while the curvature was healthy: four flex models were quietly left short of tolerance. For an SPD metric the guard is positivity, and with that the same solves converge. The qacc_smooth call site in mj_fwdAcceleration is textually unchanged but now reaches the iterative solve, which converges on opt.tolerance rather than a hardcoded threshold, floored in mjUSESINGLE builds where the squared target is unreachable in float. Reaching the iteration cap names the ill-conditioned flex stiffness and then reports it through mjWARN_INERTIA, rather than returning an under-converged result. Covered dofs are located by walking the covered rows of the stiffness matrix, as they need not be 3-aligned from dof 0: any joint declared before a flexcomp shifts them. mjData.efm_L_rownnz, efm_L_rowadr and efm_L_colind described the sparsity of the deleted factorization and are removed: left NULL with nonzero mjxmacro extents they made the Python bindings hand back uninitialized arrays. efm_active loses the value 2 for the same reason, nothing selects a solve path on preconditioner exactness any more. Both are recorded under breaking changes. model/flex/bag.xml is added because no shipped model carried per-step stretch stiffness. The ponchos are bending-only and trampoline.xml uses an explicit integrator, so the metric never activates there. It is excluded from WriteReadCompareTest: stretch stiffness amplifies rest geometry that XML rounds on save.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
<!-- Copyright 2026 DeepMind Technologies Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<mujoco model="Bag">
|
||||
<compiler angle="radian" meshdir="asset"/>
|
||||
<option timestep="0.001" jacobian="sparse" integrator="implicitfast" solver="CG"
|
||||
iterations="2000" tolerance="1e-4"/>
|
||||
<statistic extent="2.5" center="0 0 1"/>
|
||||
|
||||
<visual>
|
||||
<map force="0.1" zfar="30"/>
|
||||
<global offwidth="2560" offheight="1440" elevation="-20" azimuth="120"/>
|
||||
</visual>
|
||||
|
||||
<asset>
|
||||
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0"
|
||||
width="512" height="512"/>
|
||||
<texture name="texplane" type="2d" builtin="checker" rgb1=".2 .3 .4" rgb2=".1 0.15 0.2"
|
||||
width="512" height="512" mark="cross" markrgb=".8 .8 .8"/>
|
||||
<material name="matplane" reflectance="0.3" texture="texplane" texrepeat="10 10"
|
||||
texuniform="true"/>
|
||||
<model name="humanoid" file="../humanoid/humanoid.xml"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<geom name="ground" type="plane" size="0 0 1" material="matplane" condim="1"/>
|
||||
|
||||
<!-- Both lights are directional. A positional light inside the bag's closed volume (as in
|
||||
model/flex/scene.xml, which puts one at z=2) blacks out the cloth around it. -->
|
||||
<light directional="true" diffuse=".7 .7 .7" specular=".1 .1 .1" pos="0 0 4" dir="0 0 -1"/>
|
||||
<light directional="true" diffuse=".4 .4 .4" specular=".1 .1 .1" pos="3 -3 4" dir="-.6 .6 -.6"/>
|
||||
|
||||
<!-- The bag, scaled up so the humanoid fits and held open by pinning the ring of vertices
|
||||
around its mouth. It hangs well above the floor, so the cloth catches the humanoid. -->
|
||||
<body name="bag_container" euler="1.57 0 0" pos="0 0 1.5">
|
||||
<flexcomp name="bag" type="mesh" file="bag.obj" dim="2" scale="3.0 3.09 3.0"
|
||||
radius="0.003" mass="5" rgba="0.6 0.6 0.62 0.6">
|
||||
<elasticity young="3e6" poisson="0.3" thickness="0.002" elastic2d="stretch" damping="5e-3"/>
|
||||
<pin id="193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
|
||||
213 214 215 216 239 240 241 242 243 244 245 246 247 248 249 250
|
||||
251 252 253 254 255 256 257 258 259 260 267 268 269 270 271 272
|
||||
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
|
||||
661 662 663 664 665 666 667 668 671 672 673 675 677 678 679 682
|
||||
687 688 689 690 711 712 713 714 715 716 717 718 719 720 721 722
|
||||
723 724 725 726 727 728 729 730 755 756 758 761 762 764 766 770
|
||||
772 773 803 804 805 806 813 815 819 820 821 822 1003 1005 1006
|
||||
1009 1010 1013 1014 1015 1017 1019 1021 1024 1025 1051 1052
|
||||
1054 1056 1059 1061 1063 1065 1066 1069 1070"/>
|
||||
<!-- priority=1: the flex parameters win outright instead of averaging with the
|
||||
humanoid's softer solref/solimp, which let limbs sink ~35mm into the cloth. -->
|
||||
<contact selfcollide="none" contype="1" conaffinity="1" priority="1"
|
||||
solref="0.004 1" solimp="0.99 0.999 0.001"/>
|
||||
</flexcomp>
|
||||
</body>
|
||||
|
||||
<!-- The humanoid, dropped above the bag's mouth; its torso sits at z=1.282 in its
|
||||
own model. It is the stock 40kg humanoid, hence the heavy, stiff cloth. -->
|
||||
<frame pos="0 0 1.618">
|
||||
<attach model="humanoid" body="torso" prefix="humanoid_"/>
|
||||
</frame>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
Reference in New Issue
Block a user