Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
11bd200
Hold MuJoCo kinematic roots with mocap welds
adenzler-nvidia Oct 20, 2026
2990e4f
Let MuJoCo kinematic roots start dynamic
adenzler-nvidia Oct 20, 2026
7188124
Add MuJoCo kinematic_switchable_roots option
adenzler-nvidia Oct 20, 2026
50aef79
Fold MuJoCo kinematic targets into the state push
adenzler-nvidia Oct 20, 2026
a435d0b
Compensate gravity on MuJoCo kinematic roots
adenzler-nvidia Oct 21, 2026
021457b
Harden MuJoCo kinematic welds against edge cases
adenzler-nvidia Oct 21, 2026
0890091
Reject massless MuJoCo kinematic roots
adenzler-nvidia Oct 21, 2026
4e79c0a
Drive massless kinematic roots as MuJoCo mocap bodies
adenzler-nvidia Oct 21, 2026
5036c5e
Merge remote-tracking branch 'upstream/main' into adenzler/mujoco-kin…
adenzler-nvidia Oct 21, 2026
ae4c132
Drop the kinematic armature from dampratio resolution
adenzler-nvidia Oct 21, 2026
11cda9c
Document opting MuJoCo roots into kinematic switching
adenzler-nvidia Oct 21, 2026
5bdf634
Remove an accidentally committed example plot
adenzler-nvidia Oct 21, 2026
2c4edd8
Drop gravity compensation on MuJoCo kinematic roots
adenzler-nvidia Oct 21, 2026
91d7b4a
Drop the kinematic_switchable_roots solver option
adenzler-nvidia Oct 21, 2026
4a9f9c3
Reuse Newton FK for MuJoCo kinematic targets
adenzler-nvidia Oct 21, 2026
e97000d
Only switch roots that start kinematic in MuJoCo
adenzler-nvidia Oct 22, 2026
6beccd3
Wake MuJoCo worlds when mocap roots move
adenzler-nvidia Oct 22, 2026
85d0eba
Keep the update_data_interval contract for MuJoCo mocaps
adenzler-nvidia Oct 22, 2026
4b0b668
Use an undamped MuJoCo kinematic weld
adenzler-nvidia Oct 22, 2026
883bf9d
Correct MuJoCo kinematic weld stiffness and rationale
adenzler-nvidia Oct 22, 2026
71161b0
Link the MuJoCo kinematic switching example
adenzler-nvidia Oct 22, 2026
1bf655c
Merge remote-tracking branch 'upstream/main' into adenzler/mujoco-kin…
adenzler-nvidia Oct 22, 2026
b2f67ba
Copy body flags before mutating them in a weld test
adenzler-nvidia Oct 26, 2026
8107f02
Let any MuJoCo root turn kinematic, deprecated
adenzler-nvidia Oct 26, 2026
6364efa
Lock MuJoCo kinematic roots with stiffness-free welds
adenzler-nvidia Oct 26, 2026
fce46d9
Merge remote-tracking branch 'upstream/main' into adenzler/mujoco-kin…
adenzler-nvidia Oct 26, 2026
d7030ac
Compute MuJoCo constants with kinematic roots locked
adenzler-nvidia Oct 26, 2026
b2cef45
Tidy MuJoCo kinematic root docstring and meaninertia guard
adenzler-nvidia Oct 26, 2026
53521b0
Drop kinematic roots from the zero-mass export fragment
adenzler-nvidia Oct 26, 2026
23a8a95
Clarify what dynamic descendants see of mocap roots
adenzler-nvidia Oct 26, 2026
ba33ba5
Keep MuJoCo kinematic welds out of saved MJCF
adenzler-nvidia Oct 26, 2026
e52a0a5
Fix MuJoCo state sync for worlds without MuJoCo coordinates
adenzler-nvidia Oct 26, 2026
4440362
Warn only when a MuJoCo root switches to kinematic
adenzler-nvidia Oct 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/+mujoco-cpu-fixed-root-mocap-7d2a91c4.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `SolverMuJoCo` with `use_mujoco_cpu=True` ignoring fixed-root pose updates from `joint_X_p` and `joint_X_c`, which left MuJoCo colliding against the original pose.
2 changes: 1 addition & 1 deletion changelog/+mujoco-exact-mass-3e8b5c17.changed.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Raise MuJoCo's compile error in `SolverMuJoCo` for a body with joint DOFs that has no mass or inertia and no rigidly attached body with mass, matching native MuJoCo. This includes kinematic roots and massless links in jointed chains that previously ran only because MuJoCo inferred mass from their shapes; give such bodies a small mass and inertia, or attach them to a massive body with a fixed joint.
Raise MuJoCo's compile error in `SolverMuJoCo` for a body with joint DOFs that has no mass or inertia and no rigidly attached body with mass, matching native MuJoCo. This includes massless links in jointed chains that previously ran only because MuJoCo inferred mass from their shapes; give such bodies a small mass and inertia, or attach them to a massive body with a fixed joint.
1 change: 1 addition & 0 deletions changelog/+mujoco-kinematic-weld-5c1e8a2f.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Change `SolverMuJoCo` to hold kinematic roots with a weld instead of a large internal armature, so they keep their authored `joint_armature` and no longer loosen solver convergence. Every movable root gets an inactive weld so `body_flags` can still switch it at runtime; pass `kinematic_switchable_roots=False` to prepare only roots that are kinematic when the solver is created. Making a non-root body kinematic now raises `ValueError`. Kinematic roots without mass or inertia, which MuJoCo cannot simulate as movable bodies, are driven as mocap bodies like fixed roots and cannot be switched to dynamic.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deprecate making a root kinematic in `SolverMuJoCo` that was dynamic in every world when the solver was created. Mark the root kinematic before creating the solver and switch it to dynamic afterwards; `SolverMuJoCo(kinematic_switchable_roots=False)` opts into the future behavior, which no longer prepares other roots.
1 change: 1 addition & 0 deletions changelog/+mujoco-mocap-root-wake-3b9d07e2.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `SolverMuJoCo` with sleeping enabled leaving bodies asleep after the fixed root beneath them moved through `joint_X_p` or `joint_X_c`.
12 changes: 10 additions & 2 deletions docs/concepts/articulations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,16 @@ Implementation details differ by coordinate formulation:
PADMM and DVI backends keep the input :attr:`newton.State.body_q`. Under LOX, write a velocity
that reaches the target pose within the step.

In :class:`~newton.solvers.SolverMuJoCo`, kinematic DOFs are regularized with a
large internal armature value; see :ref:`Kinematic Links and Fixed Roots <mujoco-kinematic-links-and-fixed-roots>` for details.
:class:`~newton.solvers.SolverMuJoCo` follows MuJoCo, which has no prescribed degrees of freedom: it holds
kinematic roots with a weld that locks their acceleration. This has user-visible consequences:

- Making a root kinematic that was dynamic when the solver was created is deprecated. To start a root dynamic,
mark it kinematic before creating the solver and switch it to dynamic afterwards (see
:ref:`Switching kinematic roots at runtime <mujoco-kinematic-switching>`).
- A kinematic root without mass or inertia is driven as a MuJoCo mocap body. It cannot be switched to dynamic, and
contact friction and its dynamic descendants do not see its velocity.

See :ref:`Kinematic Links and Fixed Roots <mujoco-kinematic-links-and-fixed-roots>` for details.

.. _Joint types:

Expand Down
112 changes: 100 additions & 12 deletions docs/solvers/mujoco.rst
Original file line number Diff line number Diff line change
Expand Up @@ -419,8 +419,7 @@ Newton's per-DOF :attr:`~newton.Model.joint_target_mode` creates MuJoCo general

For MJCF position-only drives with ``dampratio`` and the default joint-target
control mapping, solver construction resolves damping using each world's
initial physical inertia, authored armature, and position gain, excluding
synthetic armature used to lock kinematic bodies. It replaces the imported
initial physical inertia, authored armature, and position gain. It replaces the imported
damping-ratio placeholder with the resulting coefficient and initializes zero entries in
:attr:`~newton.Model.joint_target_kd`; nonzero explicit damping is preserved.
Subsequent gain edits use the joint-target arrays and
Expand Down Expand Up @@ -1085,13 +1084,6 @@ Caveats
Newton's :attr:`~newton.Model.joint_velocity_limit` has no MuJoCo equivalent and is
ignored.

**Kinematic-root armature override.**
DOFs of kinematic articulation roots have their
:attr:`~newton.Model.joint_armature` replaced with a very large
internal value (``1e10``) so MuJoCo treats them as effectively
prescribed. The user-supplied armature on those DOFs is silently
discarded. See `Kinematic links and fixed roots`_.

**Collision filtering has a 32-bit capacity.**
The solver creates MuJoCo masks that reproduce Newton's allowed collision
pairs when they fit in 32 bits. See `Collision filtering`_ for the behavior
Expand Down Expand Up @@ -1125,24 +1117,120 @@ During Newton-to-MuJoCo conversion (at
by joint type:

- **Kinematic roots with non-fixed joints** become ordinary MuJoCo
joints with the same Newton joint type and DOFs. A very large
internal armature is assigned to those DOFs so MuJoCo treats them
as prescribed, effectively infinite-mass coordinates.
joints with the same Newton joint type, DOFs, mass, and armature, held
by a weld; see `Kinematic welds`_.
- **Roots attached to world with a fixed joint** become MuJoCo mocap
bodies (whether kinematic or not). MuJoCo has no joint coordinates
for a fixed root, so Newton drives the pose through
``mjData.mocap_pos`` and ``mjData.mocap_quat`` instead. A D6 root
with all axes locked (e.g. imported from a generic USD
``PhysicsJoint``) is treated the same way.
- **Kinematic roots without mass or inertia** also become mocap bodies
without MuJoCo joints, posed from :attr:`~newton.State.joint_q` every
step. MuJoCo requires mass of movable bodies, so such a root can never
be dynamic. Mocap bodies have zero velocity in MuJoCo: contact friction
against the root and its dynamic descendants follow its pose but do not
see its velocity.
Give the body mass if that coupling matters.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- **World-attached shapes that are not part of an articulation**
remain ordinary static MuJoCo geometry rather than mocap bodies.

Mocap bodies posed from Newton joints, both fixed roots and massless
kinematic roots, are updated together with the joint state; a joint-frame
notification re-poses fixed roots immediately. MuJoCo does not wake sleeping
bodies resting on a moving mocap body, so when sleeping is enabled, moving one
of these roots wakes its world.

If you edit :attr:`~newton.Model.joint_X_p` or :attr:`~newton.Model.joint_X_c`
for a fixed-root articulation after constructing the solver, call
:meth:`~newton.solvers.SolverBase.notify_model_changed` with the
:attr:`~newton.ModelFlags.JOINT_PROPERTIES` flag to
synchronize the updated fixed-root poses into MuJoCo.

Kinematic welds
~~~~~~~~~~~~~~~

MuJoCo has no prescribed degrees of freedom. Each kinematic root is held by a
weld equality to the world that is active exactly while the root is kinematic:

- The weld has neither stiffness nor damping, so rather than pulling the root
toward a pose it locks the root's acceleration: through each step the root
keeps the pose and velocity pushed from :attr:`~newton.State.joint_q` and
:attr:`~newton.State.joint_qd`, under every integrator.
- MuJoCo computes its inverse-weight constants, which scale constraint
softness, with kinematic roots locked. The weld and contacts therefore treat
a kinematic root as immovable, and joint limits of its dynamic descendants
behave as on a fixed base.

Unlike a large armature, the weld leaves the mass matrix, and with it the
solver's convergence tolerance, physical. Kinematic roots keep their authored
:attr:`~newton.Model.joint_armature`. Switching a root between kinematic and
dynamic recomputes these constants, as other inertial updates do.

.. _mujoco-kinematic-switching:

Switching kinematic roots at runtime
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

MuJoCo cannot add constraints to a compiled model, and all MuJoCo Warp worlds
share one model, so :class:`~newton.solvers.SolverMuJoCo` prepares an inactive
weld for every movable root when it is created.
:attr:`~newton.Model.body_flags` can then switch roots between kinematic and
dynamic in either direction, per world, followed by
``notify_model_changed(ModelFlags.BODY_PROPERTIES)``.

Making a root kinematic that was dynamic in every world when the solver was
created is deprecated. Pass ``kinematic_switchable_roots=False`` to adopt the
future behavior now: only roots that are kinematic in some world at creation
get a weld, which keeps the exported MuJoCo model free of welds for roots that
never turn kinematic. To start a root dynamic and make it kinematic
later, mark it kinematic before creating the solver and switch it to dynamic
right after:

.. testcode:: mujoco-kinematic-switching

from newton.solvers import SolverMuJoCo

builder = newton.ModelBuilder()
crate = builder.add_body(xform=wp.transform((0.0, 0.0, 1.0), wp.quat_identity()), is_kinematic=True)
builder.add_shape_box(crate, hx=0.1, hy=0.1, hz=0.1)
model = builder.finalize()
solver = SolverMuJoCo(model, kinematic_switchable_roots=False)

# Start dynamic.
body_flags = model.body_flags.numpy()
body_flags[crate] = int(newton.BodyFlags.DYNAMIC)
model.body_flags.assign(body_flags)
solver.notify_model_changed(newton.ModelFlags.BODY_PROPERTIES)

# Later: take over the crate's motion.
body_flags[crate] = int(newton.BodyFlags.KINEMATIC)
model.body_flags.assign(body_flags)
solver.notify_model_changed(newton.ModelFlags.BODY_PROPERTIES)

Each prepared root adds a weld to the exported MuJoCo model. An inactive weld
adds no constraint rows. The default ``njmax`` reserves the
six rows of each weld whose root is kinematic in some world but not in the
first; roots made kinematic later share ``njmax`` with contacts.

Errors and warnings
~~~~~~~~~~~~~~~~~~~

- Making a non-root body kinematic raises :class:`ValueError`, as does making
a massless kinematic root dynamic or, with ``kinematic_switchable_roots=False``,
making a root kinematic that was dynamic in every world at creation.
- Switching a root to kinematic that was dynamic in every world at creation
issues a :class:`~newton.exceptions.NewtonDeprecationWarning`.
- An explicit ``njmax`` too small for the welds that are inactive in the
first world, or a switch that leaves kinematic roots needing more weld rows
than ``njmax`` has room for, issues a
:class:`~newton.exceptions.NewtonWarning`; constraints may overflow.

Warnings fire once per switch, comparing against the flags of the previous
notification. These checks run on the host for eager notifications only:
notifications replayed from a CUDA graph neither warn nor detect invalid
flags.


Updating joint force properties
-------------------------------
Expand Down
7 changes: 5 additions & 2 deletions newton/_src/solvers/mujoco/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
HINGE_CONNECT_HALF_SPAN = 0.05
"""Distance [m] along the hinge axis from a revolute loop joint's anchor to each of its two CONNECT points."""

KINEMATIC_ARMATURE = 1.0e10
"""Large MuJoCo armature value used to make kinematic-body DOFs effectively immovable."""
KINEMATIC_LOCK_INERTIA_RATIO = 1.0e4
"""Armature, relative to a DOF's own inertia, that locks kinematic root DOFs while MuJoCo computes its constants.

Large enough for constraint regularization to treat the root as immovable, small enough
to keep its weld and contact rows conditioned for MuJoCo Warp's float32 CG solver."""

MJ_MINMU = 1.0e-5
"""MuJoCo's minimum friction coefficient clamp."""
Expand Down
Loading
Loading