Skip to content

Commit 8d8d256

Browse files
committed
check friction in io.put_model
1 parent 87ec0ad commit 8d8d256

3 files changed

Lines changed: 74 additions & 20 deletions

File tree

mujoco_warp/_src/collision_driver_test.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -882,26 +882,27 @@ def test_hfield_maxconpair(self):
882882
np.testing.assert_equal(d.nacon.numpy()[0], 4)
883883

884884
def test_min_friction(self):
885-
_, _, _, d = test_data.fixture(
886-
xml="""
887-
<mujoco>
888-
<worldbody>
889-
<body>
890-
<geom type="sphere" size=".1" friction="0 0 0"/>
891-
<joint type="slide"/>
892-
</body>
893-
<body>
894-
<geom type="sphere" size=".1" friction="0 0 0"/>
895-
<joint type="slide"/>
896-
</body>
897-
</worldbody>
898-
<keyframe>
899-
<key qpos="0 .1"/>
900-
</keyframe>
901-
</mujoco>
902-
""",
903-
keyframe=0,
904-
)
885+
with self.assertWarns(UserWarning):
886+
_, _, _, d = test_data.fixture(
887+
xml="""
888+
<mujoco>
889+
<worldbody>
890+
<body>
891+
<geom type="sphere" size=".1" friction="0 0 0"/>
892+
<joint type="slide"/>
893+
</body>
894+
<body>
895+
<geom type="sphere" size=".1" friction="0 0 0"/>
896+
<joint type="slide"/>
897+
</body>
898+
</worldbody>
899+
<keyframe>
900+
<key qpos="0 .1"/>
901+
</keyframe>
902+
</mujoco>
903+
""",
904+
keyframe=0,
905+
)
905906

906907
self.assertEqual(d.nacon.numpy()[0], 1)
907908
np.testing.assert_allclose(d.contact.friction.numpy()[0], types.MJ_MINMU)

mujoco_warp/_src/io.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# ==============================================================================
1515

1616
import dataclasses
17+
import warnings
1718
from typing import Any, Optional, Sequence, Union
1819

1920
import mujoco
@@ -153,6 +154,21 @@ def not_implemented(objtype, objid, geomtype):
153154
if not_implemented(objtype, objid, types.GeomType.BOX) and not_implemented(reftype, refid, types.GeomType.BOX):
154155
raise NotImplementedError(f"Collision sensors with box-box collisions are not implemented.")
155156

157+
def _check_friction(name: str, id_: int, condim: int, friction, checks):
158+
for min_condim, indices in checks:
159+
if condim >= min_condim:
160+
for idx in indices:
161+
if friction[idx] < types.MJ_MINMU:
162+
warnings.warn(
163+
f"{name} {id_}: friction[{idx}] ({friction[idx]}) < MJ_MINMU ({types.MJ_MINMU}) with condim={condim} may cause NaN"
164+
)
165+
166+
for geomid in range(mjm.ngeom):
167+
_check_friction("geom", geomid, mjm.geom_condim[geomid], mjm.geom_friction[geomid], [(3, [0]), (4, [1]), (6, [2])])
168+
169+
for pairid in range(mjm.npair):
170+
_check_friction("pair", pairid, mjm.pair_dim[pairid], mjm.pair_friction[pairid], [(3, [0]), (4, [1, 2]), (6, [3, 4])])
171+
156172
# create opt
157173
opt_kwargs = {f.name: getattr(mjm.opt, f.name, None) for f in dataclasses.fields(types.Option)}
158174
if hasattr(mjm.opt, "impratio"):

mujoco_warp/_src/io_test.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,43 @@ def test_contact_sensor_maxmatch(self):
582582

583583
self.assertEqual(m.opt.contact_sensor_maxmatch, 5)
584584

585+
@parameterized.parameters(
586+
'<worldbody><geom type="sphere" size=".1" condim="3" friction="0 0.1 0.1"/></worldbody>',
587+
'<worldbody><geom type="sphere" size=".1" condim="4" friction="1 0 0.1"/></worldbody>',
588+
'<worldbody><geom type="sphere" size=".1" condim="6" friction="1 1 0"/></worldbody>',
589+
"""
590+
<worldbody>
591+
<geom name="g1" type="sphere" size=".1"/>
592+
<geom name="g2" type="sphere" size=".1" pos="0.5 0 0"/>
593+
</worldbody>
594+
<contact>
595+
<pair geom1="g1" geom2="g2" condim="3" friction="0 1 1 1 1"/>
596+
</contact>
597+
""",
598+
"""
599+
<worldbody>
600+
<geom name="g1" type="sphere" size=".1"/>
601+
<geom name="g2" type="sphere" size=".1" pos="0.5 0 0"/>
602+
</worldbody>
603+
<contact>
604+
<pair geom1="g1" geom2="g2" condim="4" friction="1 0 0 1 1"/>
605+
</contact>
606+
""",
607+
"""
608+
<worldbody>
609+
<geom name="g1" type="sphere" size=".1"/>
610+
<geom name="g2" type="sphere" size=".1" pos="0.5 0 0"/>
611+
</worldbody>
612+
<contact>
613+
<pair geom1="g1" geom2="g2" condim="6" friction="1 1 1 0 0"/>
614+
</contact>
615+
""",
616+
)
617+
def test_small_friction_warning(self, xml):
618+
"""Tests that a warning is raised for small friction values."""
619+
with self.assertWarns(UserWarning):
620+
mjwarp.put_model(mujoco.MjModel.from_xml_string(f"<mujoco>{xml}</mujoco>"))
621+
585622
@parameterized.product(active=["true", "false"], make_data=[True, False])
586623
def test_eq_active(self, active, make_data):
587624
mjm, mjd, m, d = test_data.fixture(

0 commit comments

Comments
 (0)