From cdf5bcb693b0dd8769ffaeac12ebf7cd30c94344 Mon Sep 17 00:00:00 2001 From: Sam Haves Date: Tue, 15 Jul 2025 09:29:28 -0700 Subject: [PATCH] Add inheritrange attribute to mjcPhysics MjcActuator PiperOrigin-RevId: 783363012 Change-Id: I7d0476aae70c3acc3ed4808a8741b7eb12948dba --- .../experimental/usd/mjcPhysics/actuator.h | 34 +++++++++++++++++++ .../experimental/usd/mjcPhysics/tokens.h | 4 +++ src/experimental/usd/mjcPhysics/actuator.cpp | 13 ++++++- .../usd/mjcPhysics/generatedSchema.usda | 3 ++ src/experimental/usd/mjcPhysics/schema.usda | 6 ++++ src/experimental/usd/mjcPhysics/tokens.cpp | 2 ++ .../usd/plugins/mjcf/mujoco_to_usd.cc | 3 ++ src/experimental/usd/usd_to_mjspec.cc | 5 +++ .../usd/plugins/mjcf/mjcf_file_format_test.cc | 29 ++++++++++++++++ 9 files changed, 98 insertions(+), 1 deletion(-) diff --git a/include/mujoco/experimental/usd/mjcPhysics/actuator.h b/include/mujoco/experimental/usd/mjcPhysics/actuator.h index f32ec693..49d793ff 100644 --- a/include/mujoco/experimental/usd/mjcPhysics/actuator.h +++ b/include/mujoco/experimental/usd/mjcPhysics/actuator.h @@ -763,6 +763,40 @@ class MjcPhysicsActuator : public UsdTyped { UsdAttribute CreateMjcActEarlyAttr(VtValue const &defaultValue = VtValue(), bool writeSparsely = false) const; + public: + // --------------------------------------------------------------------- // + // MJCINHERITRANGE + // --------------------------------------------------------------------- // + /// Automatically set the actuator’s ctrlrange to match the transmission + /// target’s range. The default value means disabled. A positive value X sets + /// the ctrlrange around the midpoint of the target range, scaled by X. For + /// example if the target joint has range of [0, 1], then a value of 1.0 will + /// set ctrlrange to [0, 1]; values of 0.8 and 1.2 will set the ctrlrange to + /// [0.1, 0.9] and [-0.1, 1.1], respectively. Values smaller than 1 are useful + /// for not hitting the limits; values larger than 1 are useful for + /// maintaining control authority at the limits (being able to push on them). + /// This attribute is exclusive with ctrlrange and available only for joint + /// and tendon transmissions which have range defined. + /// + /// | || + /// | -- | -- | + /// | Declaration | `uniform double mjc:inheritRange = 0` | + /// | C++ Type | double | + /// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Double | + /// | \ref SdfVariability "Variability" | SdfVariabilityUniform | + MJCPHYSICS_API + UsdAttribute GetMjcInheritRangeAttr() const; + + /// See GetMjcInheritRangeAttr(), and also + /// \ref Usd_Create_Or_Get_Property for when to use Get vs Create. + /// If specified, author \p defaultValue as the attribute's default, + /// sparsely (when it makes sense to do so) if \p writeSparsely is \c true - + /// the default for \p writeSparsely is \c false. + MJCPHYSICS_API + UsdAttribute CreateMjcInheritRangeAttr( + VtValue const &defaultValue = VtValue(), + bool writeSparsely = false) const; + public: // --------------------------------------------------------------------- // // MJCTARGET diff --git a/include/mujoco/experimental/usd/mjcPhysics/tokens.h b/include/mujoco/experimental/usd/mjcPhysics/tokens.h index 8ad5004e..ad97d3c1 100644 --- a/include/mujoco/experimental/usd/mjcPhysics/tokens.h +++ b/include/mujoco/experimental/usd/mjcPhysics/tokens.h @@ -353,6 +353,10 @@ struct MjcPhysicsTokensType { /// /// MjcPhysicsMeshCollisionAPI const TfToken mjcInertia; + /// \brief "mjc:inheritRange" + /// + /// MjcPhysicsActuator + const TfToken mjcInheritRange; /// \brief "mjc:jointInParent" /// /// MjcPhysicsActuator diff --git a/src/experimental/usd/mjcPhysics/actuator.cpp b/src/experimental/usd/mjcPhysics/actuator.cpp index 34ee3fdf..5e63b3cc 100644 --- a/src/experimental/usd/mjcPhysics/actuator.cpp +++ b/src/experimental/usd/mjcPhysics/actuator.cpp @@ -332,6 +332,17 @@ UsdAttribute MjcPhysicsActuator::CreateMjcActEarlyAttr( /* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely); } +UsdAttribute MjcPhysicsActuator::GetMjcInheritRangeAttr() const { + return GetPrim().GetAttribute(MjcPhysicsTokens->mjcInheritRange); +} + +UsdAttribute MjcPhysicsActuator::CreateMjcInheritRangeAttr( + VtValue const &defaultValue, bool writeSparsely) const { + return UsdSchemaBase::_CreateAttr( + MjcPhysicsTokens->mjcInheritRange, SdfValueTypeNames->Double, + /* custom = */ false, SdfVariabilityUniform, defaultValue, writeSparsely); +} + UsdRelationship MjcPhysicsActuator::GetMjcTargetRel() const { return GetPrim().GetRelationship(MjcPhysicsTokens->mjcTarget); } @@ -385,7 +396,7 @@ const TfTokenVector &MjcPhysicsActuator::GetSchemaAttributeNames( MjcPhysicsTokens->mjcDynType, MjcPhysicsTokens->mjcGainType, MjcPhysicsTokens->mjcBiasType, MjcPhysicsTokens->mjcDynPrm, MjcPhysicsTokens->mjcGainPrm, MjcPhysicsTokens->mjcBiasPrm, - MjcPhysicsTokens->mjcActEarly, + MjcPhysicsTokens->mjcActEarly, MjcPhysicsTokens->mjcInheritRange, }; static TfTokenVector allNames = _ConcatenateAttributeNames( UsdTyped::GetSchemaAttributeNames(true), localNames); diff --git a/src/experimental/usd/mjcPhysics/generatedSchema.usda b/src/experimental/usd/mjcPhysics/generatedSchema.usda index 344f9bc5..f0bbb966 100644 --- a/src/experimental/usd/mjcPhysics/generatedSchema.usda +++ b/src/experimental/usd/mjcPhysics/generatedSchema.usda @@ -358,6 +358,9 @@ class MjcActuator "MjcActuator" ( displayName = "Group" doc = "Integer MuJoCo group to which the transmission belongs." ) + uniform double mjc:inheritRange = 0 ( + doc = "Automatically set the actuator’s ctrlrange to match the transmission target’s range. The default value means disabled. A positive value X sets the ctrlrange around the midpoint of the target range, scaled by X. For example if the target joint has range of [0, 1], then a value of 1.0 will set ctrlrange to [0, 1]; values of 0.8 and 1.2 will set the ctrlrange to [0.1, 0.9] and [-0.1, 1.1], respectively. Values smaller than 1 are useful for not hitting the limits; values larger than 1 are useful for maintaining control authority at the limits (being able to push on them). This attribute is exclusive with ctrlrange and available only for joint and tendon transmissions which have range defined." + ) uniform bool mjc:jointInParent = 0 ( doc = "If true and applied to ball and free joints, the 3d rotation axis given by gear is defined in the parent frame (which is the world frame for free joints) rather than the child frame." ) diff --git a/src/experimental/usd/mjcPhysics/schema.usda b/src/experimental/usd/mjcPhysics/schema.usda index a26a172d..f0669d95 100644 --- a/src/experimental/usd/mjcPhysics/schema.usda +++ b/src/experimental/usd/mjcPhysics/schema.usda @@ -668,10 +668,12 @@ class MjcActuator "MjcActuator" doc = "If true, the control input to this actuator is automatically clamped to ctrlrange at runtime. If false, control input clamping is disabled. If 'auto' and autolimits is set in compiler, control clamping will automatically be set to true if ctrlrange is defined without explicitly setting this attribute to 'true'. Note that control input clamping can also be globally disabled with the clampctrl attribute of option/flag." allowedTokens = ["false", "true", "auto"] ) + uniform token mjc:forceLimited = "auto" ( doc = "If true, the force output of this actuator is automatically clamped to forcerange at runtime. If false, force clamping is disabled. If 'auto' and autolimits is set in compiler, force clamping will automatically be set to true if forcerange is defined without explicitly setting this attribute to 'true'." allowedTokens = ["false", "true", "auto"] ) + uniform token mjc:actLimited = "auto" ( doc = "If true, the internal state (activation) associated with this actuator is automatically clamped to actrange at runtime. If false, activation clamping is disabled. If 'auto' and autolimits is set in compiler, activation clamping will automatically be set to true if actrange is defined without explicitly setting this attribute to 'true'. See the Activation clamping section for more details." allowedTokens = ["false", "true", "auto"] @@ -765,6 +767,10 @@ class MjcActuator "MjcActuator" uniform bool mjc:actEarly = False ( doc = "If true, force computation will use the next value of the activation variable rather than the current one. Setting this flag reduces the delay between the control and accelerations by one time-step." ) + + uniform double mjc:inheritRange = 0 ( + doc = "Automatically set the actuator’s ctrlrange to match the transmission target’s range. The default value means disabled. A positive value X sets the ctrlrange around the midpoint of the target range, scaled by X. For example if the target joint has range of [0, 1], then a value of 1.0 will set ctrlrange to [0, 1]; values of 0.8 and 1.2 will set the ctrlrange to [0.1, 0.9] and [-0.1, 1.1], respectively. Values smaller than 1 are useful for not hitting the limits; values larger than 1 are useful for maintaining control authority at the limits (being able to push on them). This attribute is exclusive with ctrlrange and available only for joint and tendon transmissions which have range defined." + ) } diff --git a/src/experimental/usd/mjcPhysics/tokens.cpp b/src/experimental/usd/mjcPhysics/tokens.cpp index a5fa8ef3..db19379a 100644 --- a/src/experimental/usd/mjcPhysics/tokens.cpp +++ b/src/experimental/usd/mjcPhysics/tokens.cpp @@ -88,6 +88,7 @@ MjcPhysicsTokensType::MjcPhysicsTokensType() mjcGear("mjc:gear", TfToken::Immortal), mjcGroup("mjc:group", TfToken::Immortal), mjcInertia("mjc:inertia", TfToken::Immortal), + mjcInheritRange("mjc:inheritRange", TfToken::Immortal), mjcJointInParent("mjc:jointInParent", TfToken::Immortal), mjcLengthRangeMax("mjc:lengthRange:max", TfToken::Immortal), mjcLengthRangeMin("mjc:lengthRange:min", TfToken::Immortal), @@ -233,6 +234,7 @@ MjcPhysicsTokensType::MjcPhysicsTokensType() mjcGear, mjcGroup, mjcInertia, + mjcInheritRange, mjcJointInParent, mjcLengthRangeMax, mjcLengthRangeMin, diff --git a/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc b/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc index 8941e196..28c68f65 100644 --- a/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc +++ b/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc @@ -1180,6 +1180,9 @@ class ModelWriter { WriteUniformAttribute(actuator_path, pxr::SdfValueTypeNames->Bool, MjcPhysicsTokens->mjcActEarly, (bool)actuator->actearly); + WriteUniformAttribute(actuator_path, pxr::SdfValueTypeNames->Double, + MjcPhysicsTokens->mjcInheritRange, + actuator->inheritrange); WriteUniformAttribute( actuator_path, pxr::SdfValueTypeNames->DoubleArray, diff --git a/src/experimental/usd/usd_to_mjspec.cc b/src/experimental/usd/usd_to_mjspec.cc index 3fe22e04..ff3c57ef 100644 --- a/src/experimental/usd/usd_to_mjspec.cc +++ b/src/experimental/usd/usd_to_mjspec.cc @@ -853,6 +853,11 @@ void ParseMjcPhysicsActuator(mjSpec* spec, mj_act->actearly = (int)act_early; } + auto inherit_range_attr = tran.GetMjcInheritRangeAttr(); + if (act_early_attr.HasAuthoredValue()) { + act_early_attr.Get(&mj_act->inheritrange); + } + auto ref_site_rel = tran.GetMjcRefSiteRel(); if (ref_site_rel.HasAuthoredTargets()) { pxr::SdfPathVector targets; diff --git a/test/experimental/usd/plugins/mjcf/mjcf_file_format_test.cc b/test/experimental/usd/plugins/mjcf/mjcf_file_format_test.cc index 63c018a4..f5066232 100644 --- a/test/experimental/usd/plugins/mjcf/mjcf_file_format_test.cc +++ b/test/experimental/usd/plugins/mjcf/mjcf_file_format_test.cc @@ -1675,6 +1675,35 @@ TEST_F(MjcfSdfFileFormatPluginTest, TestMjcPhysicsActuator) { pxr::VtDoubleArray{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}); } +TEST_F(MjcfSdfFileFormatPluginTest, TestMjcPhysicsPositionActuator) { + static constexpr char xml[] = R"( + + + + + + + + + + + + )"; + auto stage = OpenStageWithPhysics(xml); + + EXPECT_PRIM_VALID(stage, "/test/Actuators/position"); + EXPECT_PRIM_IS_A(stage, "/test/Actuators/position", + pxr::MjcPhysicsActuator); + EXPECT_REL_HAS_TARGET(stage, "/test/Actuators/position.mjc:target", + "/test/body/hinge"); + ExpectAttributeEqual(stage, "/test/Actuators/position.mjc:inheritRange", + 1.0); +} + TEST_F(MjcfSdfFileFormatPluginTest, TestMjcPhysicsJointActuator) { static constexpr char xml[] = R"(