From 6d9361614b719025d00620da5420f35fbbe35a1a Mon Sep 17 00:00:00 2001 From: Aditya Singh Date: Wed, 5 Aug 2026 05:27:37 -0700 Subject: [PATCH] Allow armature and damping on actuator elements MuJoCo accepts armature and damping on general, motor, position, velocity, intvelocity, damper, cylinder and muscle actuators, and the section of the PyMJCF schema already listed both for exactly those eight elements. The elements themselves did not, so the attributes could only be set through a default class and writing them directly on an actuator raised AttributeError. Verified against MuJoCo 3.11.0 that all eight accept both attributes, that adhesion correctly rejects them, and that the parsed values reach actuator_armature and actuator_damping in the compiled model. --- dm_control/mjcf/element_test.py | 24 ++++++++++++++++++++++++ dm_control/mjcf/schema.xml | 16 ++++++++++++++++ dm_control/mjcf/xml_validation_test.py | 19 +++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/dm_control/mjcf/element_test.py b/dm_control/mjcf/element_test.py index a8deace1..8b5be9b8 100644 --- a/dm_control/mjcf/element_test.py +++ b/dm_control/mjcf/element_test.py @@ -800,6 +800,30 @@ def testDictLikeInterface(self): with self.assertRaisesRegex(TypeError, 'does not support item deletion'): del elem['foo'] + @parameterized.parameters('general', 'motor', 'position', 'velocity', + 'intvelocity', 'damper', 'cylinder', 'muscle') + def testActuatorArmatureAndDamping(self, actuator_type): + # MuJoCo accepts `armature` and `damping` on these actuators, and the + # section of the schema already allowed both, but the + # elements themselves did not list them. + xml_string = """ + + + + + + + + + <{} name="a" joint="j" armature="0.7" damping="2.5"/> + + +""".format(actuator_type) + mujoco = parser.from_xml_string(xml_string) + actuator = mujoco.find('actuator', 'a') + self.assertEqual(actuator.armature, 0.7) + np.testing.assert_array_equal(actuator.damping, [2.5]) + def testSetAndGetAttributes(self): mujoco = element.RootElement(model='test') diff --git a/dm_control/mjcf/schema.xml b/dm_control/mjcf/schema.xml index 029ceb88..94ddf4c1 100644 --- a/dm_control/mjcf/schema.xml +++ b/dm_control/mjcf/schema.xml @@ -2102,6 +2102,8 @@ + + @@ -2135,6 +2137,8 @@ + + @@ -2160,6 +2164,8 @@ + + @@ -2190,6 +2196,8 @@ + + @@ -2218,6 +2226,8 @@ + + @@ -2267,6 +2277,8 @@ + + @@ -2293,6 +2305,8 @@ + + @@ -2322,6 +2336,8 @@ + + diff --git a/dm_control/mjcf/xml_validation_test.py b/dm_control/mjcf/xml_validation_test.py index 58a0ad42..a10bced3 100644 --- a/dm_control/mjcf/xml_validation_test.py +++ b/dm_control/mjcf/xml_validation_test.py @@ -67,6 +67,25 @@ def testXmlFromZip(self): model = parser.from_zip(_ZIPPED_MODEL) validate(model.to_xml_string(), model.get_assets()) + def testActuatorArmatureAndDampingRoundTrip(self): + model = parser.from_xml_string(""" + + + + + + + + + + + +""") + validate(model.to_xml_string()) + mjmodel = wrapper.MjModel.from_xml_string(model.to_xml_string()) + self.assertEqual(mjmodel.actuator_armature[0], 0.7) + self.assertEqual(mjmodel.actuator_damping[0], 2.5) + if __name__ == '__main__': absltest.main()