Polynomial stiffness and damping https://youtu.be/aKa3ZlEF9_Y

PiperOrigin-RevId: 884607673
Change-Id: If8088dbf37fed1055304778a7eb84dec52cba920
This commit is contained in:
Yuval Tassa
2026-03-16 13:24:44 -07:00
committed by Copybara-Service
parent aec1b45dce
commit efae9157a7
38 changed files with 1093 additions and 176 deletions
+5
View File
@@ -492,6 +492,11 @@ to change.
- 10
- The maximal number of real-valued parameters used to define the bias of each actuator.
Determines the size of ``mjModel.actuator_biasprm``.
* - ``mjNPOLY``
- 2
- The number of nonlinear polynomial coefficients for joint and tendon stiffness and damping.
Determines the size of ``mjModel.{jnt,tendon}_{stiffness,damping}poly``. See
:ref:`polynomial forces<gePolynomial>`.
* - ``mjNFLUID``
- 12
- The number of per-geom fluid interaction parameters required by the ellipsoidal model.
+41 -13
View File
@@ -2268,9 +2268,14 @@ rotations as unit quaternions.
.. _body-joint-stiffness:
:at:`stiffness`: :at-val:`real, "0"`
Joint stiffness. If this value is positive, a spring will be created with equilibrium position given by springref
below. The spring force is computed along with the other passive forces.
:at:`stiffness`: :at-val:`real, "0 0 0"`
Joint stiffness coefficients :math:`a, b, c`. A positive :math:`a` produces the standard restorative linear spring
force :math:`f = -a x`, where :math:`x` is the joint displacement from equilibrium given by
:ref:`springref<body-joint-springref>`.
If the optional second and third components are set, they define a nonlinear
polynomial spring force :math:`f(x) = -(a x + b x^2 + c x^3)`.
See :ref:`Polynomial forces<gePolynomial>` for details.
.. _body-joint-range:
@@ -2373,12 +2378,18 @@ rotations as unit quaternions.
.. _body-joint-damping:
:at:`damping`: :at-val:`real, "0"`
Damping applied to all degrees of freedom created by this joint. Unlike friction loss which is computed by the
constraint solver, damping is simply a force linear in velocity. It is included in the passive forces. Despite this
simplicity, larger damping values can make numerical integrators unstable, which is why our Euler integrator handles
:at:`damping`: :at-val:`real, "0 0 0"`
Damping coefficients :math:`a, b, c`.
A positive :math:`a` produces the standard dissipative linear damping force :math:`f(v) = -a v`,
where :math:`v` is the joint velocity. Despite its simplicity,
larger damping values can make numerical integrators unstable, which is why our Euler integrator handles
damping implicitly. See :ref:`Integration <geIntegration>` in the Computation chapter.
If the optional second and third components are set, they define a nonlinear polynomial damping force
:math:`f(v) = -(a v + b v |v| + c v^3)`.
Note the anti-symmetrization of the quadratic term, ensuring that the force is an odd function of
velocity. See :ref:`Polynomial forces<gePolynomial>` for details.
.. _body-joint-frictionloss:
:at:`frictionloss`: :at-val:`real, "0"`
@@ -5025,15 +5036,32 @@ length X, as in the clip on the right of `this example model
.. _tendon-spatial-stiffness:
:at:`stiffness`: :at-val:`real, "0"`
Stiffness coefficient. A positive value generates a spring force (linear in position) acting along the tendon.
.. youtube:: aKa3ZlEF9_Y
:aspect: 2:1
:align: right
:width: 35%
:at:`stiffness`: :at-val:`real, "0 0 0"`
Tendon stiffness coefficients :math:`a, b, c`. A positive :math:`a` generates a linear spring force
:math:`f(x) = -a x`, acting along the tendon. Here :math:`x` is the tendon displacement
defined by :ref:`springlength<tendon-spatial-springlength>`.
If the optional second and third components are set, they define a nonlinear polynomial spring force
:math:`f(x) = -(a x + b x^2 + c x^3)`. See :ref:`Polynomial forces<gePolynomial>` for details.
The clip on the right is of
`this model <https://github.com/google-deepmind/mujoco/blob/main/test/engine/testdata/passive/poly_stiffness.xml>`__.
.. _tendon-spatial-damping:
:at:`damping`: :at-val:`real, "0"`
Damping coefficient. A positive value generates a damping force (linear in velocity) acting along the tendon. Unlike
joint damping which is integrated implicitly by the Euler method, tendon damping is not integrated implicitly, thus
joint damping should be used if possible.
:at:`damping`: :at-val:`real, "0 0 0"`
Damping coefficients :math:`a, b, c`.
A positive :math:`a` produces the standard dissipative linear damping force :math:`f(v) = -a v`.
If the optional second and third components are set, they define a nonlinear polynomial damping force
:math:`f(v) = -(a v + b v |v| + c v^3)`.
Note the anti-symmetrization of the quadratic term, ensuring that the force is an odd function of
velocity. See :ref:`Polynomial forces<gePolynomial>` for details.
.. image:: images/XMLreference/tendon_armature.gif
:width: 30%
+28 -2
View File
@@ -5,10 +5,35 @@ Changelog
Upcoming version (not yet released)
-----------------------------------
General
^^^^^^^
.. youtube:: aKa3ZlEF9_Y
:align: right
:width: 35%
- Stiffness in :ref:`joints<body-joint-stiffness>` and :ref:`tendons<tendon-spatial-stiffness>` and damping in
:ref:`joints<body-joint-damping>` and :ref:`tendons<tendon-spatial-damping>` now support nonlinear polynomial
:ref:`force profiles<gePolynomial>`. New ``mjModel`` arrays (``jnt_stiffnesspoly``, ``tendon_stiffnesspoly``,
``dof_dampingpoly``, ``tendon_dampingpoly``) hold higher-order coefficients. The existing scalar arrays
(``jnt_stiffness``, ``dof_damping``, etc.) continue to hold the linear coefficient and are unchanged.
The polynomial order is defined by the new constant :ref:`mjNPOLY<glNumericSizes>`. A future breaking C-API change
may unify the linear and higher-order coefficients into a single array.
.. admonition:: Breaking API changes
:class: attention
- The ``mjs`` layer fields ``stiffness`` and ``damping`` in :ref:`mjsJoint` and :ref:`mjsTendon` have
been widened from ``mjtNum`` scalars to ``mjtNum[mjNPOLY+1]`` arrays. The first element is the linear coefficient
(previously the scalar), and subsequent elements are the higher-order :ref:`polynomial<gePolynomial>` coefficients.
**Migration:** Replace assignments like ``joint.stiffness = val`` with ``joint.stiffness[0] = val``.
Bug fixes
^^^^^^^^^
- Compiler now correctly accounts for negative scaling when loading user specified mesh data.
- The compiler now correctly accounts for negative scaling when loading user specified mesh data.
Version 3.6.0 (March 10, 2026)
------------------------------
@@ -195,7 +220,8 @@ Version 3.4.0 (December 5, 2025)
General
^^^^^^^
.. youtube:: vct493lGQ8Q
.. youtube:: aKa3ZlEF9_Y
:aspect: 2:1
:align: right
:width: 35%
+53
View File
@@ -423,6 +423,59 @@ MuJoCo can compute three types of passive forces:
- Gravity compensation forces. See the body :ref:`gravcomp<body-gravcomp>` attribute for details.
- Fluid forces exerted by the surrounding medium. See the :doc:`Fluid forces <fluid>` chapter for details.
.. _gePolynomial:
Polynomial forces
^^^^^^^^^^^^^^^^^
Nonlinear stiffness (:ref:`joints<body-joint-stiffness>`, :ref:`tendons<tendon-spatial-stiffness>`) and
damping (:ref:`joints<body-joint-damping>`, :ref:`tendons<tendon-spatial-damping>`) are defined by polynomials
:math:`f` of order :ref:`mjNPOLY + 1<glNumericSizes>`. The actual force applied to the system is :math:`-f`, meaning
that sign-preserving functions yield a restorative (stiffness) or dissipative (damping) force.
The stiffness polynomial takes the standard form (where :math:`x` is displacement):
.. math::
f(x) = a x + b x^2 + c x^3 + \dots
The damping polynomial takes the anti-symmetrized form (where :math:`v` is velocity):
.. math::
f(v) = a v + b v |v| + c v^3 + \dots
**Anti-symmetrization**
The damping polynomial uses anti-symmetrized even-powered monomials (e.g., :math:`v^2 \rightarrow v|v|`) so that the
function is odd: :math:`f(-v) = -f(v)`. This guarantees the force reverses direction with velocity. This formulation
is also physically motivated, as some natural forms of damping (like fluid drag) display an anti-symmetric quadratic
profile.
In contrast, asymmetric (or rather, non-anti-symmetric) stiffness profiles are physically common (e.g., biological
fascia), making the standard polynomial form and its Taylor-series convenience more appropriate.
**Sign-preservation**
In both cases, sensible choices of coefficients often satisfy the **sign-preservation** condition
:math:`z \cdot f(z) \geq 0`. This condition is equivalent to requiring that the integral of :math:`f` (Potential
Energy for stiffness and Dissipation for damping) is globally convex with a minimum at the origin.
- For stiffness, violations of the condition create repulsive forces and/or multiple equilibria.
- For damping, violations create non-dissipative forces that inject mechanical energy into the system.
The sign-preservation condition is not enforced by the compiler; it is the user's responsibility to ensure it is
satisfied. The analytical conditions on the coefficients for orders up to 3 are:
.. math::
\begin{aligned}
\textrm{Standard:} \qquad & a \geq 0, \qquad c \geq 0, \qquad b^2 \leq 4 a c \\
\textrm{Anti-symmetrized:} \qquad & a \geq 0, \qquad c \geq 0, \qquad b < 0 \implies b^2 \leq 4 a c
\end{aligned}
**mjModel fields**
Although MJCF accepts the coefficients as a single array (as does the :ref:`mjs layer<mjsJoint>`),
the linear coefficient in ``mjModel`` is stored separately from the higher-order ones.
For example, if :ref:`joint/stiffness<body-joint-stiffness>` = "a b c",
then ``jnt_stiffness[i] = a``, ``jnt_stiffnesspoly[i*mjNPOLY] = b`` and ``jnt_stiffnesspoly[i*mjNPOLY + 1] = c``.
A future breaking change of the C-API may unify the linear and higher-order coefficients into a single array.
.. _geIntegration:
Numerical integration
+12 -8
View File
@@ -1179,7 +1179,8 @@ struct mjModel_ {
mjtNum* jnt_solimp; // constraint solver impedance: limit (njnt x mjNIMP)
mjtNum* jnt_pos; // local anchor position (njnt x 3)
mjtNum* jnt_axis; // local joint axis (njnt x 3)
mjtNum* jnt_stiffness; // stiffness coefficient (njnt x 1)
mjtNum* jnt_stiffness; // linear stiffness coefficient (njnt x 1)
mjtNum* jnt_stiffnesspoly; // high-order stiffness coefficients (njnt x mjNPOLY)
mjtNum* jnt_range; // joint limits (njnt x 2)
mjtNum* jnt_actfrcrange; // range of total actuator force (njnt x 2)
mjtNum* jnt_margin; // min distance for limit detection (njnt x 1)
@@ -1196,7 +1197,8 @@ struct mjModel_ {
mjtNum* dof_solimp; // constraint solver impedance:frictionloss (nv x mjNIMP)
mjtNum* dof_frictionloss; // dof friction loss (nv x 1)
mjtNum* dof_armature; // dof armature inertia/mass (nv x 1)
mjtNum* dof_damping; // damping coefficient (nv x 1)
mjtNum* dof_damping; // linear damping coefficient (nv x 1)
mjtNum* dof_dampingpoly; // high-order damping coefficients (nv x mjNPOLY)
mjtNum* dof_invweight0; // diag. inverse inertia in qpos0 (nv x 1)
mjtNum* dof_M0; // diag. inertia in qpos0 (nv x 1)
mjtNum* dof_length; // linear: 1; angular: approx. length scale (nv x 1)
@@ -1501,8 +1503,10 @@ struct mjModel_ {
mjtNum* tendon_range; // tendon length limits (ntendon x 2)
mjtNum* tendon_actfrcrange; // range of total actuator force (ntendon x 2)
mjtNum* tendon_margin; // min distance for limit detection (ntendon x 1)
mjtNum* tendon_stiffness; // stiffness coefficient (ntendon x 1)
mjtNum* tendon_damping; // damping coefficient (ntendon x 1)
mjtNum* tendon_stiffness; // linear stiffness coefficient (ntendon x 1)
mjtNum* tendon_stiffnesspoly; // high-order stiffness coefficients (ntendon x mjNPOLY)
mjtNum* tendon_damping; // linear damping coefficient (ntendon x 1)
mjtNum* tendon_dampingpoly; // high-order damping coefficients (ntendon x mjNPOLY)
mjtNum* tendon_armature; // inertia associated with tendon velocity (ntendon x 1)
mjtNum* tendon_frictionloss; // loss due to friction (ntendon x 1)
mjtNum* tendon_lengthspring; // spring resting length range (ntendon x 2)
@@ -2057,7 +2061,7 @@ typedef struct mjsJoint_ { // joint specification
int align; // align free joint with body com (mjtAlignFree)
// stiffness
double stiffness; // stiffness coefficient
double stiffness[mjNPOLY+1]; // stiffness coefficients
double springref; // spring reference value: qpos_spring
double springdamper[2]; // timeconst, dampratio
@@ -2072,7 +2076,7 @@ typedef struct mjsJoint_ { // joint specification
// dof properties
double armature; // armature inertia (mass for slider)
double damping; // damping coefficient
double damping[mjNPOLY+1]; // damping coefficients
double frictionloss; // friction loss
mjtNum solref_friction[mjNREF]; // solver reference: dof friction
mjtNum solimp_friction[mjNIMP]; // solver impedance: dof friction
@@ -2390,9 +2394,9 @@ typedef struct mjsTendon_ { // tendon specification
mjsElement* element; // element type
// stiffness, damping, friction, armature
double stiffness; // stiffness coefficient
double stiffness[mjNPOLY+1]; // stiffness coefficients
double springlength[2]; // spring resting length; {-1, -1}: use qpos_spring
double damping; // damping coefficient
double damping[mjNPOLY+1]; // damping coefficients
double frictionloss; // friction loss
mjtNum solref_friction[mjNREF]; // solver reference: tendon friction
mjtNum solimp_friction[mjNIMP]; // solver impedance: tendon friction