Skip to content

Commit e3bd727

Browse files
quaglacopybara-github
authored andcommitted
Add 2D and 3D stretching elasto-plasticity (yield) for interpolated flexes (dof="trilinear" and dof="quadratic").
### Physical Model - **Cell / Face Plastic Engineering Strain (`d->plastic`):** - **3D Volume Interpolation (`interp > 0`, `elastic2d="none"`):** Each grid cell $e$ stores a 6-component symmetric plastic engineering strain tensor $\varepsilon^p\_e = (\varepsilon^p\_{xx}, \varepsilon^p\_{yy}, \varepsilon^p\_{zz}, \gamma^p\_{xy}, \gamma^p\_{xz}, \gamma^p\_{yz})$ (`6 * cx * cy * cz` entries per flex, allocated for the full `cellcount` grid). - **2D Shell Interpolation (`interp < 0`, `dim="2"` with `elastic2d="stretch"` or `"both"`):** Each outer grid face $f$ stores a 3-component in-plane plastic engineering strain tensor $\varepsilon^p\_f = (\varepsilon^p\_{11}, \varepsilon^p\_{22}, \gamma^p\_{12})$ (`6 * (cy*cz + cx*cz + cx*cy)` entries per flex, i.e. 3 per outer face). With `elastic2d="bend"`, `yield` is ignored (bending plasticity is not supported for interpolated flexes) and the flex remains purely elastic. - **Corotated Polar Decomposition on the Physical Jacobian:** - In `mj_flexPassiveInterp` (and `mujoco_warp`'s `compute_interp_cell_quat` / `compute_interp_face_quat`), the cell/face rotation $R\_e$ is the standard polar decomposition of the physical Jacobian $J\_e = \partial x / \partial X$ at the cell/face center, obtained by scaling the columns of the parametric gradient $\partial x / \partial \xi$ by the reference cell extents $(1/\Delta x, 1/\Delta y, 1/\Delta z)$ (and the face tangents $t\_1 / \Delta\_0$, $t\_2 / \Delta\_1$ before the normal cross product in 2D). It does not read `d->plastic`: since the plastic strain is additive in the corotated frame, $R\_e$ is the exact rigid rotation at any rotated plastic rest configuration, including non-cubic cells. - For large rotations (non-positive trace of the Jacobian), the iterative polar decomposition (`mju_flexPolarRotation` / `mat33_to_quat_polar`) is warm-started from the Gram-Schmidt rotation of the Jacobian columns, so it does not stall at the identity saddle point (e.g. 180 degree rotations, which are exact for cells with consistent physical scaling). - Using the precomputed cell/face stiffness matrix $K\_e$, the nodal displacement vector in the local corotated frame is offset by the plastic deformation $\Delta d\_i = R\_e^T x\_i - X\_i - \varepsilon^p\_e (X\_i - X\_0)$, leaving $K\_e$ invariant while shifting the zero-force configuration. - **Return Mapping (`advanceFinish` / `_next_plastic_flex_interp`):** - At the end of each step, the cell-center corotated total strain $\varepsilon^{\text{tot}} = \mathrm{sym}(R\_e^T J\_e - I)$ is decomposed into elastic and plastic parts $\varepsilon^{\text{el}}\_e = \varepsilon^{\text{tot}} - \varepsilon^p\_e$, and radial return projects $\varepsilon^{\text{el}}\_e$ onto the von Mises ($J\_2$) yield surface (3D isochoric in 3D, plane-stress with out-of-plane thinning in tension in 2D) with threshold $\varepsilon\_y =$ `flex_yield[f]`. - To prevent degenerate cell Jacobians under extreme repeated loading, $\varepsilon^p\_e$ is bounded in Frobenius norm ($\|\varepsilon^p\_e\|\_F \le 0.35$, guaranteeing $\lambda\_{\min}(I + \varepsilon^p\_e) > 0.71$). - **Dimensionless Keyframe Scaling:** Because $\varepsilon^p\_e$ is a dimensionless engineering strain, `ScaleKeyframe` leaves interpolated flex entries invariant under spatial scaling (`flex_interp[f] != 0`). PiperOrigin-RevId: 1010658301 Change-Id: I41a16150e9dfa56610794d27669e43d32599b6ca
1 parent c39fe6f commit e3bd727

19 files changed

Lines changed: 1043 additions & 72 deletions

‎doc/XMLreference.rst‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4876,15 +4876,21 @@ and **quadratic**.
48764876
.. _flex-elasticity-yield:
48774877

48784878
:at:`yield`: :at-val:`real(1), "0"`
4879-
Dimensionless plastic yield strain threshold :math:`\varepsilon_y` for non-interpolated 2D and 3D flexes. If zero,
4880-
the flex is purely elastic. If positive, each element's elastic stretching strain tensor is bounded by the von Mises
4881-
(:math:`J_2`) yield criterion (3D isochoric in 3D, plane-stress with out-of-plane thinning in tension in 2D) via
4882-
radial return mapping, and for 2D shells with bending enabled (:ref:`elastic2d<flex-elasticity-elastic2d>` **bend**
4883-
or **both**), each initially flat interior hinge's outer-fiber bending strain
4884-
:math:`\frac{t}{2} |\Delta\kappa_e| \le \varepsilon_y` is bounded via return mapping in dihedral angle space. The
4885-
plastic state in ``mjData.plastic`` starting at ``flex_plasticadr[f]`` stores ``3 * elemnum`` (2D) or
4886-
``6 * elemnum`` (3D) stretching entries when stretching is enabled, followed by ``edgenum`` bending entries when 2D
4887-
bending is enabled (with boundary edges remaining ``0``).
4879+
Dimensionless plastic yield strain threshold :math:`\varepsilon_y` for 2D and 3D flexes. If zero, the flex is purely
4880+
elastic. If positive, each element's (or interpolated cell's/face's) elastic stretching strain tensor is bounded by
4881+
the von Mises (:math:`J_2`) yield criterion (3D isochoric in 3D, plane-stress with out-of-plane thinning in tension
4882+
in 2D) via radial return mapping, and for non-interpolated 2D shells with bending enabled
4883+
(:ref:`elastic2d<flex-elasticity-elastic2d>` **bend** or **both**), each interior hinge's outer-fiber bending strain
4884+
:math:`\frac{t}{2} |\Delta\kappa_e| \le \varepsilon_y` is bounded via return mapping in dihedral angle space
4885+
(assuming an initially flat rest configuration). In ``mjData.plastic`` at ``flex_plasticadr[f]``, a non-interpolated
4886+
flex stores its stretching plastic metric offsets first (``3 * elemnum`` entries in 2D or ``6 * elemnum`` entries in
4887+
3D when stretching is active), followed by ``edgenum`` bending plastic entries in 2D when bending is active. An
4888+
interpolated flex with :ref:`cellcount<body-flexcomp-cellcount>` :math:`(c_x, c_y, c_z)` stores one dimensionless
4889+
plastic stretching strain per grid cell (``6 * cx * cy * cz`` entries in 3D) or per outer grid face in 2D shell mode
4890+
(``3 * 2 * (cy*cz + cx*cz + cx*cy)`` entries); the elastic strain is measured in the corotated frame of the polar
4891+
decomposition of the physical deformation gradient :math:`J_e = \partial x / \partial X`. Bending plasticity is not
4892+
supported for interpolated flexes: with :ref:`elastic2d<flex-elasticity-elastic2d>` **bend**, :at:`yield` is
4893+
ignored and the flex remains purely elastic.
48884894

48894895
.. _flex-elasticity-elastic2d:
48904896

‎doc/changelog.rst‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Significant new features
2525
:width: 40%
2626
:target: https://github.com/google-deepmind/mujoco/blob/main/model/flex/clay.xml
2727

28-
- Added continuum elasto-plasticity (:ref:`yield<flex-elasticity-yield>`) for non-interpolated 2D and 3D
28+
- Added continuum elasto-plasticity (:ref:`yield<flex-elasticity-yield>`) for 2D and 3D
2929
:ref:`flexes<deformable-flex>`, with volumetric-deviatoric (von Mises :math:`J_2`) stretching and 2D shell bending
3030
return mapping in ``mjData.plastic``.
3131

‎mjx/mujoco/mjx/third_party/mujoco_warp/_src/constraint.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,7 @@ def kernel(
16431643
cell_quat = face_quat_in[worldid, cell_idx]
16441644
else:
16451645
cell_quat = support.compute_interp_cell_quat(
1646+
flex_node0,
16461647
flexnode_xpos_in,
16471648
1,
16481649
ci,

‎mjx/mujoco/mjx/third_party/mujoco_warp/_src/derivative.py‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2563,6 +2563,7 @@ def kernel(
25632563
flex_nodeadr: wp.array[int],
25642564
flex_stiffnessadr: wp.array[int],
25652565
flex_nodebodyid: wp.array[int],
2566+
flex_node0: wp.array[wp.vec3],
25662567
flex_stiffness: wp.array[float],
25672568
flex_damping: wp.array[float],
25682569
flex_centered: wp.array[bool],
@@ -2621,7 +2622,7 @@ def kernel(
26212622
if shell_mode:
26222623
cell_quat = face_quat_in[worldid, flex_faceadr[f] + ci]
26232624
else:
2624-
cell_quat = support.compute_interp_cell_quat(flexnode_xpos_in, order_abs, ci, cj, ck, cy, cz, ny_g, nz_g, nstart, worldid)
2625+
cell_quat = support.compute_interp_cell_quat(flex_node0, flexnode_xpos_in, order_abs, ci, cj, ck, cy, cz, ny_g, nz_g, nstart, worldid)
26252626
R = wp.quat_to_matrix(cell_quat)
26262627
RT = wp.transpose(R)
26272628

@@ -2900,6 +2901,7 @@ def kernel(
29002901
flex_nodeadr: wp.array[int],
29012902
flex_stiffnessadr: wp.array[int],
29022903
flex_nodebodyid: wp.array[int],
2904+
flex_node0: wp.array[wp.vec3],
29032905
flex_stiffness: wp.array[float],
29042906
flex_damping: wp.array[float],
29052907
flex_faceadr: wp.array[int],
@@ -2969,7 +2971,7 @@ def kernel(
29692971
if shell_mode:
29702972
cell_quat = face_quat_in[worldid, flex_faceadr[f] + ci]
29712973
else:
2972-
cell_quat = support.compute_interp_cell_quat(flexnode_xpos_in, order_abs, ci, cj, ck, cy, cz, ny_g, nz_g, nstart, worldid)
2974+
cell_quat = support.compute_interp_cell_quat(flex_node0, flexnode_xpos_in, order_abs, ci, cj, ck, cy, cz, ny_g, nz_g, nstart, worldid)
29732975
cell_quat_inv = wp.quat(-cell_quat[0], -cell_quat[1], -cell_quat[2], cell_quat[3])
29742976

29752977
op1 = order_abs + 1
@@ -3475,6 +3477,7 @@ def eff_build(m: Model, d: Data):
34753477
m.flex_nodeadr,
34763478
m.flex_stiffnessadr,
34773479
m.flex_nodebodyid,
3480+
m.flex_node0,
34783481
m.flex_stiffness,
34793482
m.flex_damping,
34803483
m.flex_centered,
@@ -3608,6 +3611,7 @@ def eff_shift(m: Model, d: Data):
36083611
m.flex_nodeadr,
36093612
m.flex_stiffnessadr,
36103613
m.flex_nodebodyid,
3614+
m.flex_node0,
36113615
m.flex_stiffness,
36123616
m.flex_damping,
36133617
m.flex_faceadr,
@@ -3963,6 +3967,7 @@ def eff_mul_m(
39633967
m.flex_nodeadr,
39643968
m.flex_stiffnessadr,
39653969
m.flex_nodebodyid,
3970+
m.flex_node0,
39663971
m.flex_stiffness,
39673972
m.flex_damping,
39683973
m.flex_faceadr,

0 commit comments

Comments
 (0)
⌂ 换网址