google-deepmind / google-deepmind/mujoco
USD: four defects in the Newton schemas v0.4.0 adoption (units, semantics, unread attribute)
- Dominant language
- C++
- Stars
- 15.2k
- Forks
- 1.8k
- Avg merge
- 10d 16h
- Merged PRs (30d)
- 25
Description
### Summary
`39e44588` ("Update USD support in MuJoCo to Newton USD schemas v0.4.0", unreleased — it sits above the `Version 3.11.0` heading in `doc/changelog.rst`) introduces four defects in the new `newton:` read paths. Two are angle-unit conversions that are simply missing; the third maps a distance-valued attribute onto a force-valued field; the fourth silently ignores an attribute that controls whether a constraint is active at all.
I wrote the original adoption in #3156, so some of this is my mess to begin with — flagging it now while the change is still unreleased and cheap to correct.
Schema references below are [newton-usd-schemas v0.4.0](https://github.com/newton-physics/newton-usd-schemas), the version `src/experimental/usd/CMakeLists.txt` now pins.
---
### 1. `newton:mimicCoef0` is authored in degrees, read as radians
The schema declares angle-valued units:
> `newton:mimicCoef0` — *"Offset added after scaling the leader joint's position/angle. […] Units: distance or degrees (matches the joint type for single-DOF joints)"*
`plugin/usd_decoder/usd_decoder.cc:2166-2172` assigns it straight into `mjEQ_JOINT` `data[0]`, which is radians for hinge and ball joints:
```cpp
if (newton_coef0 && newton_coef0.HasAuthoredValue()) {
float val;
newton_coef0.Get(&val);
eq->data[0] = val; // no degrees -> radians conversion
}
```
For an angular follower the offset is therefore wrong by a factor of 180/π ≈ 57.3.
Two details make this bite harder than the others:
- The `newton:` attribute takes **precedence** over `mjc:coef0` here, so authoring the deprecated attribute is not a workaround.
- `mujoco-usd-converter` writes `newton:mimicCoef0` with an explicit `np.degrees()` for hinge/ball followers and does not author `mjc:coef0` at all, so there is no fallback path for assets it produces.
Newton hit the identical bug on its own importer and fixed it in [newton-physics/newton#3728](https://github.com/newton-physics/newton/pull/3728).
`newton:mimicCoef1` is declared dimensionless and is correctly read as-is.
### 2. `newton:damping` is authored per-degree, read per-radian
> `newton:damping` — *"Units: effort * seconds / degrees (angular DOFs) or effort * seconds / distance (linear DOFs)"*
`usd_decoder.cc:1832-1836`:
```cpp
} else if (newton_damping_authored) {
float damping;
newton_damping_attr.Get(&damping);
mj_joint->damping[0] = damping; // per-degree value into a per-radian field
}
```
`mjsJoint::damping` is per-radian for angular DOFs, so this needs a ×180/π on hinge and ball joints.
This one is currently masked: precedence in `ParseMjcPhysicsJointAPI` is the **opposite** of the mimic case — `mjc:damping` wins when both are authored — so producers that write both (as the converter does) still get correct behaviour today, with a deprecation warning. It becomes a silent 57.3× under-damping the moment `mjc:damping` is removed, which the changelog states is the plan.
`newton:armature` (*mass · distance²*) and `newton:friction` (*effort*) carry no per-angle term and are correctly read as-is.
### 3. `newton:contactAdhesion` is a distance, `geom.adhesion` is a force
These are different physical quantities:
| | definition | units |
| --- | --- | --- |
| `newton:contactAdhesion` | *"Contact adhesion distance. When two surfaces are within this distance, an attractive force pulls them together."* | distance |
| `mjsGeom::adhesion` ([XMLreference](https://mujoco.readthedocs.io/en/stable/XMLreference.html#body-geom-adhesion)) | *"Adhesive force of contacts with this geom, in units of force."* | force |
`usd_decoder.cc:1920-1929` assigns one to the other:
```cpp
// Contact adhesion: newton:contactAdhesion -> geom->adhesion
if (val >= 0.0f) {
geom->adhesion = val;
}
```
Newton's own implementation agrees with its schema — `Model.shape_material_ka` is documented as *"Shape contact adhesion distance [m]"*. So a value authored as metres of interaction range is consumed as newtons of pull-off force, and no scalar conversion can reconcile the two.
Notably, MuJoCo already documents the correct target for the distance concept, in the `adhesion` entry itself:
> "To let adhesion act across a small separation (attraction at a distance), set `gap` to the desired interaction range. This can be used to model magnets."
which suggests `newton:contactAdhesion` → `geom.gap`, and that MJCF's force-valued `adhesion` has no generic equivalent in the Newton schema at all.
### 4. `newton:mimicEnabled` is never read, and `physics:jointEnabled` is not a substitute
`NewtonMimicAPI` has four members. Three are read; `newton:mimicEnabled` does not appear in `plugin/usd_decoder/newton_tokens.h` at all, and is not referenced anywhere under `plugin/` or `src/`.
`eq->active` is instead derived from a different attribute entirely (`usd_decoder.cc:2094-2097`):
```cpp
void ParseJointEnabled(mjsEquality* eq, const pxr::UsdPhysicsJoint& joint) {
bool jointEnabled = true;
joint.GetJointEnabledAttr().Get(&jointEnabled);
eq->active = jointEnabled ? 1 : 0;
}
```
These control different things:
- `physics:jointEnabled` (UsdPhysics) — whether the **joint** is enabled.
- `newton:mimicEnabled` — whether the **mimic constraint** is active. Per the schema: *"When disabled, the follower joint moves independently, as though the mimic constraint was not applied."*
So the natural authoring of "an ordinary, enabled joint whose mimic coupling is switched off" — `physics:jointEnabled = true` with `newton:mimicEnabled = false` — yields an **active** equality constraint in MuJoCo, silently coupling two joints the author explicitly decoupled. There is no diagnostic, since the attribute is never inspected.
This currently goes unnoticed for `mujoco-usd-converter` output only because that producer happens to author both attributes from the same source flag; any producer writing `NewtonMimicAPI` alone hits it.
---
### Secondary: inconsistent `mjc:` vs `newton:` precedence
Where both spellings exist, the two code paths disagree about which wins:
- `ParseMjcPhysicsJointAPI` (`usd_decoder.cc:1812-1849`) — deprecated `mjc:` wins, `newton:` is the fallback.
- The mimic path (`usd_decoder.cc:2166-2178`) — `newton:` wins, deprecated `mjc:` is the fallback.
Whichever is intended, having both makes migration behaviour hard to reason about: for joint gains a producer must drop `mjc:` to get the new path, and for mimic coefficients it must drop `newton:` to avoid it.
### Environment
Observed by reading `gdm/main` at `4929f2cd`; `39e44588` is not in a tagged release. Cross-checked against `mujoco-usd-converter` 0.5.0 (pinned `mujoco>=3.11.0,<3.12`) and newton-usd-schemas v0.4.0.
Contributor guide
Assessment
This issue has not been assessed yet.