Polynomial stiffness and damping https://youtu.be/aKa3ZlEF9_Y
PiperOrigin-RevId: 884607673 Change-Id: If8088dbf37fed1055304778a7eb84dec52cba920
This commit is contained in:
committed by
Copybara-Service
parent
aec1b45dce
commit
efae9157a7
@@ -1881,6 +1881,29 @@ Euler integrator, semi-implicit in velocity.
|
||||
mujoco.mj_readSensor(model, data, 0, delay, result, interp=0)
|
||||
np.testing.assert_array_equal(result, [1, 0, 0, 0])
|
||||
|
||||
def test_poly_stiffness(self):
|
||||
xml = r"""
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<geom type="sphere" size="0.1"/>
|
||||
<joint name="slide" type="slide" stiffness="1 2 3"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
"""
|
||||
model = mujoco.MjModel.from_xml_string(xml)
|
||||
data = mujoco.MjData(model)
|
||||
self.assertEqual(model.jnt_stiffness[0], 1)
|
||||
np.testing.assert_array_equal(
|
||||
model.jnt_stiffnesspoly[0], [2, 3])
|
||||
|
||||
x = 0.5
|
||||
data.qpos[0] = x
|
||||
mujoco.mj_forward(model, data)
|
||||
expected = -(1*x + 2*x*abs(x) + 3*x**3)
|
||||
np.testing.assert_allclose(data.qfrc_spring[0], expected)
|
||||
|
||||
def _assert_attributes_equal(self, actual_obj, expected_obj, attr_to_compare):
|
||||
for name in attr_to_compare:
|
||||
actual_value = getattr(actual_obj, name)
|
||||
|
||||
@@ -486,13 +486,20 @@ def generate_add() -> None:
|
||||
f'py::object& {f.name}',
|
||||
f'py::arg("{f.name}") = py::none()',
|
||||
)
|
||||
if f.name == 'size' and f.type.extents[0] == 3:
|
||||
if (
|
||||
f.name == 'size'
|
||||
and f.type.extents[0] == 3
|
||||
or f.name in ('stiffness', 'damping')
|
||||
):
|
||||
return (
|
||||
f'set_array_size(out->{f.name}, {f.name});',
|
||||
'array_size',
|
||||
(
|
||||
f'set_array_padded(out->{f.name}, {f.name},'
|
||||
f' {f.type.extents[0]}, "{f.name}");'
|
||||
),
|
||||
'array_padded',
|
||||
f.name,
|
||||
'list[float]',
|
||||
f'std::optional<std::vector<double>>& {f.name}',
|
||||
'Optional[list[float]]',
|
||||
f'std::optional<py::object>& {f.name}',
|
||||
f'py::arg("{f.name}") = py::none()',
|
||||
)
|
||||
return (
|
||||
@@ -782,6 +789,34 @@ def generate_add() -> None:
|
||||
}
|
||||
};
|
||||
"""
|
||||
elif t == 'array_padded':
|
||||
code += """\n
|
||||
auto set_array_padded = [](auto&& des, const std::optional<py::object>& obj, int size, const char* name) {
|
||||
if (obj.has_value() && !obj->is_none()) {
|
||||
std::vector<double> array;
|
||||
if (py::isinstance<py::int_>(*obj) || py::isinstance<py::float_>(*obj)) {
|
||||
array.push_back(py::cast<double>(*obj));
|
||||
} else if (py::isinstance<py::str>(*obj)) {
|
||||
throw pybind11::type_error(
|
||||
std::string(name) + " should be a numeric scalar or list.");
|
||||
} else {
|
||||
try {
|
||||
array = py::cast<std::vector<double>>(*obj);
|
||||
} catch (const py::cast_error&) {
|
||||
throw pybind11::type_error(
|
||||
std::string(name) + " should be a numeric scalar or list.");
|
||||
}
|
||||
}
|
||||
if (array.empty() || array.size() > static_cast<size_t>(size)) {
|
||||
std::string msg = std::string(name) + " should be a list/array of size 1 to " + std::to_string(size) + ".";
|
||||
throw pybind11::value_error(msg);
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
des[i] = (i < static_cast<int>(array.size())) ? array[i] : 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
"""
|
||||
elif t == 'char_array':
|
||||
code += """\n
|
||||
auto set_char_array = [](auto&& des, py::object& obj, int size, const char* name) {
|
||||
@@ -814,20 +849,6 @@ def generate_add() -> None:
|
||||
}
|
||||
};
|
||||
"""
|
||||
elif t == 'array_size':
|
||||
code += """\n
|
||||
auto set_array_size = [](auto&& des, const std::optional<std::vector<double>>& array) {
|
||||
if (array.has_value()) {
|
||||
if (array->size() < 1 || array->size() > 3) {
|
||||
std::string msg = "size should be a list/array of size 1, 2, or 3.";
|
||||
throw pybind11::value_error(msg);
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
des[i] = (i < array->size()) ? array->at(i) : 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
"""
|
||||
elif t == 'value':
|
||||
code += """\n
|
||||
auto set_value = [](auto&& des, auto&& val) {
|
||||
|
||||
@@ -70,6 +70,7 @@ PYBIND11_MODULE(_constants, pymodule) {
|
||||
X(mjNSENS);
|
||||
X(mjNSOLVER);
|
||||
X(mjNISLAND);
|
||||
X(mjNPOLY);
|
||||
|
||||
// from mjvisualize.h
|
||||
X(mjNGROUP);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#ifndef MUJOCO_PYTHON_INDEXER_XMACRO_H_
|
||||
#define MUJOCO_PYTHON_INDEXER_XMACRO_H_
|
||||
|
||||
#include <mujoco/mjxmacro.h>
|
||||
#include <mujoco/mjxmacro.h> // IWYU pragma: keep
|
||||
|
||||
#define MJMODEL_ACTUATOR \
|
||||
X( int, actuator_, trntype, nu, 1 ) \
|
||||
@@ -132,6 +132,7 @@
|
||||
X( mjtNum, jnt_, pos, njnt, 3 ) \
|
||||
X( mjtNum, jnt_, axis, njnt, 3 ) \
|
||||
X( mjtNum, jnt_, stiffness, njnt, 1 ) \
|
||||
X( mjtNum, jnt_, stiffnesspoly, njnt, mjNPOLY ) \
|
||||
X( mjtNum, jnt_, range, njnt, 2 ) \
|
||||
X( mjtNum, jnt_, margin, njnt, 1 ) \
|
||||
X( mjtNum, jnt_, user, njnt, MJ_M(nuser_jnt) ) \
|
||||
@@ -145,6 +146,7 @@
|
||||
X( mjtNum, dof_, frictionloss, nv, 1 ) \
|
||||
X( mjtNum, dof_, armature, nv, 1 ) \
|
||||
X( mjtNum, dof_, damping, nv, 1 ) \
|
||||
X( mjtNum, dof_, dampingpoly, nv, mjNPOLY ) \
|
||||
X( mjtNum, dof_, invweight0, nv, 1 ) \
|
||||
X( mjtNum, dof_, M0, nv, 1 )
|
||||
|
||||
@@ -253,7 +255,9 @@
|
||||
X( mjtNum, tendon, _range, ntendon, 2 ) \
|
||||
X( mjtNum, tendon, _margin, ntendon, 1 ) \
|
||||
X( mjtNum, tendon, _stiffness, ntendon, 1 ) \
|
||||
X( mjtNum, tendon, _stiffnesspoly, ntendon, mjNPOLY ) \
|
||||
X( mjtNum, tendon, _damping, ntendon, 1 ) \
|
||||
X( mjtNum, tendon, _dampingpoly, ntendon, mjNPOLY ) \
|
||||
X( mjtNum, tendon, _frictionloss, ntendon, 1 ) \
|
||||
X( mjtNum, tendon, _lengthspring, ntendon, 1 ) \
|
||||
X( mjtNum, tendon, _length0, ntendon, 1 ) \
|
||||
|
||||
@@ -1732,9 +1732,17 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='stiffness coefficient',
|
||||
doc='linear stiffness coefficient',
|
||||
array_extent=('njnt',),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='jnt_stiffnesspoly',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='high-order stiffness coefficients',
|
||||
array_extent=('njnt', 'mjNPOLY'),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='jnt_range',
|
||||
type=PointerType(
|
||||
@@ -1852,9 +1860,17 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='damping coefficient',
|
||||
doc='linear damping coefficient',
|
||||
array_extent=('nv',),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='dof_dampingpoly',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='high-order damping coefficients',
|
||||
array_extent=('nv', 'mjNPOLY'),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='dof_invweight0',
|
||||
type=PointerType(
|
||||
@@ -4036,17 +4052,33 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='stiffness coefficient',
|
||||
doc='linear stiffness coefficient',
|
||||
array_extent=('ntendon',),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='tendon_stiffnesspoly',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='high-order stiffness coefficients',
|
||||
array_extent=('ntendon', 'mjNPOLY'),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='tendon_damping',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='damping coefficient',
|
||||
doc='linear damping coefficient',
|
||||
array_extent=('ntendon',),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='tendon_dampingpoly',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='high-order damping coefficients',
|
||||
array_extent=('ntendon', 'mjNPOLY'),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='tendon_armature',
|
||||
type=PointerType(
|
||||
@@ -7301,8 +7333,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='stiffness',
|
||||
type=ValueType(name='double'),
|
||||
doc='stiffness coefficient',
|
||||
type=ArrayType(
|
||||
inner_type=ValueType(name='double'),
|
||||
extents=(3,),
|
||||
),
|
||||
doc='stiffness coefficients',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='springref',
|
||||
@@ -7371,8 +7406,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='damping',
|
||||
type=ValueType(name='double'),
|
||||
doc='damping coefficient',
|
||||
type=ArrayType(
|
||||
inner_type=ValueType(name='double'),
|
||||
extents=(3,),
|
||||
),
|
||||
doc='damping coefficients',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='frictionloss',
|
||||
@@ -8934,8 +8972,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='stiffness',
|
||||
type=ValueType(name='double'),
|
||||
doc='stiffness coefficient',
|
||||
type=ArrayType(
|
||||
inner_type=ValueType(name='double'),
|
||||
extents=(3,),
|
||||
),
|
||||
doc='stiffness coefficients',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='springlength',
|
||||
@@ -8947,8 +8988,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='damping',
|
||||
type=ValueType(name='double'),
|
||||
doc='damping coefficient',
|
||||
type=ArrayType(
|
||||
inner_type=ValueType(name='double'),
|
||||
extents=(3,),
|
||||
),
|
||||
doc='damping coefficients',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='frictionloss',
|
||||
|
||||
@@ -169,7 +169,7 @@ class SpecsTest(absltest.TestCase):
|
||||
|
||||
# Add tendon.
|
||||
tendon = spec.add_tendon(stiffness=2, springlength=[0.1, 0.2])
|
||||
self.assertEqual(tendon.stiffness, 2)
|
||||
np.testing.assert_array_equal(tendon.stiffness, [2, 0, 0])
|
||||
np.testing.assert_array_equal(tendon.springlength, [0.1, 0.2])
|
||||
|
||||
# Add actuator.
|
||||
@@ -478,14 +478,14 @@ class SpecsTest(absltest.TestCase):
|
||||
body.add_geom(size=[])
|
||||
self.assertEqual(
|
||||
str(cm.exception),
|
||||
'size should be a list/array of size 1, 2, or 3.',
|
||||
'size should be a list/array of size 1 to 3.',
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
body.add_geom(size=[1, 2, 3, 4])
|
||||
self.assertEqual(
|
||||
str(cm.exception),
|
||||
'size should be a list/array of size 1, 2, or 3.',
|
||||
'size should be a list/array of size 1 to 3.',
|
||||
)
|
||||
|
||||
def test_load_xml(self):
|
||||
|
||||
Reference in New Issue
Block a user