Throw an error for margin/gap with meshes.
PiperOrigin-RevId: 615937263 Change-Id: I3289cf1b1fec93385cc78a01e53866427453db59
This commit is contained in:
committed by
Copybara-Service
parent
02c62c118c
commit
2386353b02
@@ -11,6 +11,7 @@ MJX
|
|||||||
1. Improved performance of SAT for convex collisions.
|
1. Improved performance of SAT for convex collisions.
|
||||||
2. Fixed bug for sphere/capsule-convex deep penetration.
|
2. Fixed bug for sphere/capsule-convex deep penetration.
|
||||||
3. Fixed bug where ``mjx.Data`` produced by ``mjx.put_data`` had different treedef than ``mjx.make_data``.
|
3. Fixed bug where ``mjx.Data`` produced by ``mjx.put_data`` had different treedef than ``mjx.make_data``.
|
||||||
|
4. Throw an error for margin/gap for convex mesh collisions, since they are not supported.
|
||||||
|
|
||||||
Version 3.1.3 (March 5th, 2024)
|
Version 3.1.3 (March 5th, 2024)
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|||||||
+10
-10
@@ -215,6 +215,14 @@ The following features are **in development** and coming soon:
|
|||||||
|
|
||||||
* - Category
|
* - Category
|
||||||
- Feature
|
- Feature
|
||||||
|
* - :ref:`Geom <mjtGeom>`
|
||||||
|
- ``SDF``, ``HFIELD``, ``ELLIPSOID``, ``CYLINDER``
|
||||||
|
* - :ref:`Condim <coContact>`
|
||||||
|
- 1, 4, 6
|
||||||
|
* - :ref:`Constraint <mjtConstraint>`
|
||||||
|
- :ref:`Frictionloss <coFriction>`, ``CONTACT_FRICTIONLESS``, ``CONTACT_ELLIPTIC``, ``FRICTION_DOF``
|
||||||
|
* - :ref:`Integrator <mjtIntegrator>`
|
||||||
|
- ``IMPLICIT``, ``IMPLICITFAST``
|
||||||
* - Dynamics
|
* - Dynamics
|
||||||
- :ref:`Inverse <mj_inverse>`
|
- :ref:`Inverse <mj_inverse>`
|
||||||
* - :ref:`Transmission <mjtTrn>`
|
* - :ref:`Transmission <mjtTrn>`
|
||||||
@@ -227,16 +235,8 @@ The following features are **in development** and coming soon:
|
|||||||
- ``MUSCLE``
|
- ``MUSCLE``
|
||||||
* - :ref:`Tendon Wrapping <mjtWrap>`
|
* - :ref:`Tendon Wrapping <mjtWrap>`
|
||||||
- ``NONE``, ``JOINT``, ``PULLEY``, ``SITE``, ``SPHERE``, ``CYLINDER``
|
- ``NONE``, ``JOINT``, ``PULLEY``, ``SITE``, ``SPHERE``, ``CYLINDER``
|
||||||
* - :ref:`Geom <mjtGeom>`
|
|
||||||
- ``HFIELD``, ``ELLIPSOID``, ``CYLINDER``
|
|
||||||
* - :ref:`Constraint <mjtConstraint>`
|
|
||||||
- :ref:`Frictionloss <coFriction>`, ``CONTACT_FRICTIONLESS``, ``CONTACT_ELLIPTIC``, ``FRICTION_DOF``
|
|
||||||
* - :ref:`Integrator <mjtIntegrator>`
|
|
||||||
- ``IMPLICIT``, ``IMPLICITFAST``
|
|
||||||
* - :ref:`Cone <mjtCone>`
|
* - :ref:`Cone <mjtCone>`
|
||||||
- ``ELLIPTIC``
|
- ``ELLIPTIC``
|
||||||
* - :ref:`Condim <coContact>`
|
|
||||||
- 1, 4, 6
|
|
||||||
* - Fluid Model
|
* - Fluid Model
|
||||||
- :ref:`flEllipsoid`
|
- :ref:`flEllipsoid`
|
||||||
* - :ref:`Tendons <tendon>`
|
* - :ref:`Tendons <tendon>`
|
||||||
@@ -258,6 +258,8 @@ The following features are **unsupported**:
|
|||||||
|
|
||||||
* - Category
|
* - Category
|
||||||
- Feature
|
- Feature
|
||||||
|
* - :ref:`margin<body-geom-margin>` and :ref:`gap<body-geom-gap>`
|
||||||
|
- Unimplemented for collisions with ``Mesh`` :ref:`Geom <mjtGeom>`.
|
||||||
* - :ref:`Transmission <mjtTrn>`
|
* - :ref:`Transmission <mjtTrn>`
|
||||||
- ``TRN_JOINTINPARENT``, ``TRN_SLIDERCRANK``, ``TRN_BODY``
|
- ``TRN_JOINTINPARENT``, ``TRN_SLIDERCRANK``, ``TRN_BODY``
|
||||||
* - :ref:`Actuator Dynamics <mjtDyn>`
|
* - :ref:`Actuator Dynamics <mjtDyn>`
|
||||||
@@ -270,8 +272,6 @@ The following features are **unsupported**:
|
|||||||
- ``PGS``
|
- ``PGS``
|
||||||
* - :ref:`Sensors <mjtSensor>`
|
* - :ref:`Sensors <mjtSensor>`
|
||||||
- ``PLUGIN``, ``USER``
|
- ``PLUGIN``, ``USER``
|
||||||
* - :ref:`Geom <mjtGeom>`
|
|
||||||
- ``SDF``
|
|
||||||
|
|
||||||
.. _MjxSharpBits:
|
.. _MjxSharpBits:
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
# pylint:disable=g-importing-member
|
# pylint:disable=g-importing-member
|
||||||
from mujoco.mjx._src.collision_driver import collision
|
from mujoco.mjx._src.collision_driver import collision
|
||||||
|
from mujoco.mjx._src.collision_driver import get_params
|
||||||
from mujoco.mjx._src.collision_driver import ncon
|
from mujoco.mjx._src.collision_driver import ncon
|
||||||
from mujoco.mjx._src.constraint import count_constraints
|
from mujoco.mjx._src.constraint import count_constraints
|
||||||
from mujoco.mjx._src.constraint import make_constraint
|
from mujoco.mjx._src.constraint import make_constraint
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
"""Collide geometries."""
|
"""Collide geometries."""
|
||||||
|
|
||||||
from typing import Callable, Dict, Optional, Sequence, Tuple, Union
|
from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union
|
||||||
|
|
||||||
import jax
|
import jax
|
||||||
from jax import numpy as jp
|
from jax import numpy as jp
|
||||||
@@ -187,6 +187,31 @@ def _dynamic_params(
|
|||||||
return SolverParams(friction, solref, solreffriction, solimp, margin, gap)
|
return SolverParams(friction, solref, solreffriction, solimp, margin, gap)
|
||||||
|
|
||||||
|
|
||||||
|
def get_params(
|
||||||
|
m: Union[Model, mujoco.MjModel], candidates: Sequence[Candidate]
|
||||||
|
) -> Tuple[List[int], List[int], SolverParams]:
|
||||||
|
"""Gets solver params for a list of collision candidates."""
|
||||||
|
# group sol params by different candidate types
|
||||||
|
typ_cands = {}
|
||||||
|
for c in candidates:
|
||||||
|
typ = (c.ipair > -1, c.geomp > -1)
|
||||||
|
typ_cands.setdefault(typ, []).append(c)
|
||||||
|
|
||||||
|
geom1, geom2, params = [], [], []
|
||||||
|
for (pair, priority), candidates in typ_cands.items():
|
||||||
|
geom1.extend([c.geom1 for c in candidates])
|
||||||
|
geom2.extend([c.geom2 for c in candidates])
|
||||||
|
if pair:
|
||||||
|
params.append(_pair_params(m, candidates))
|
||||||
|
elif priority:
|
||||||
|
params.append(_priority_params(m, candidates))
|
||||||
|
else:
|
||||||
|
params.append(_dynamic_params(m, candidates))
|
||||||
|
|
||||||
|
params = jax.tree_map(lambda *x: jp.concatenate(x), *params)
|
||||||
|
return geom1, geom2, params
|
||||||
|
|
||||||
|
|
||||||
def _pair_info(
|
def _pair_info(
|
||||||
m: Model, d: Data, geom1: Sequence[int], geom2: Sequence[int]
|
m: Model, d: Data, geom1: Sequence[int], geom2: Sequence[int]
|
||||||
) -> Tuple[GeomInfo, GeomInfo, Sequence[Dict[str, Optional[int]]]]:
|
) -> Tuple[GeomInfo, GeomInfo, Sequence[Dict[str, Optional[int]]]]:
|
||||||
@@ -276,24 +301,7 @@ def _collide_geoms(
|
|||||||
if not fn:
|
if not fn:
|
||||||
return Contact.zero()
|
return Contact.zero()
|
||||||
|
|
||||||
# group sol params by different candidate types
|
geom1, geom2, params = get_params(m, candidates)
|
||||||
typ_cands = {}
|
|
||||||
for c in candidates:
|
|
||||||
typ = (c.ipair > -1, c.geomp > -1)
|
|
||||||
typ_cands.setdefault(typ, []).append(c)
|
|
||||||
|
|
||||||
geom1, geom2, params = [], [], []
|
|
||||||
for (pair, priority), candidates in typ_cands.items():
|
|
||||||
geom1.extend([c.geom1 for c in candidates])
|
|
||||||
geom2.extend([c.geom2 for c in candidates])
|
|
||||||
if pair:
|
|
||||||
params.append(_pair_params(m, candidates))
|
|
||||||
elif priority:
|
|
||||||
params.append(_priority_params(m, candidates))
|
|
||||||
else:
|
|
||||||
params.append(_dynamic_params(m, candidates))
|
|
||||||
|
|
||||||
params = jax.tree_map(lambda *x: jp.concatenate(x), *params)
|
|
||||||
g1, g2, in_axes = _pair_info(m, d, geom1, geom2)
|
g1, g2, in_axes = _pair_info(m, d, geom1, geom2)
|
||||||
|
|
||||||
# Run a crude version of broadphase.
|
# Run a crude version of broadphase.
|
||||||
|
|||||||
@@ -138,8 +138,7 @@ def _validate(m: mujoco.MjModel):
|
|||||||
raise NotImplementedError('Tendons are not supported.')
|
raise NotImplementedError('Tendons are not supported.')
|
||||||
|
|
||||||
# check collision geom types
|
# check collision geom types
|
||||||
candidate_set = collision_driver.collision_candidates(m)
|
for (g1, g2, *_), c in collision_driver.collision_candidates(m).items():
|
||||||
for g1, g2, *_ in candidate_set:
|
|
||||||
g1, g2 = mujoco.mjtGeom(g1), mujoco.mjtGeom(g2)
|
g1, g2 = mujoco.mjtGeom(g1), mujoco.mjtGeom(g2)
|
||||||
if g1 == mujoco.mjtGeom.mjGEOM_PLANE and g2 in (
|
if g1 == mujoco.mjtGeom.mjGEOM_PLANE and g2 in (
|
||||||
mujoco.mjtGeom.mjGEOM_PLANE,
|
mujoco.mjtGeom.mjGEOM_PLANE,
|
||||||
@@ -149,6 +148,12 @@ def _validate(m: mujoco.MjModel):
|
|||||||
continue
|
continue
|
||||||
if collision_driver.get_collision_fn((g1, g2)) is None:
|
if collision_driver.get_collision_fn((g1, g2)) is None:
|
||||||
raise NotImplementedError(f'({g1}, {g2}) collisions not implemented.')
|
raise NotImplementedError(f'({g1}, {g2}) collisions not implemented.')
|
||||||
|
*_, params = collision_driver.get_params(m, c)
|
||||||
|
margin_gap = not np.allclose(np.concatenate([params.margin, params.gap]), 0)
|
||||||
|
if mujoco.mjtGeom.mjGEOM_MESH in (g1, g2) and margin_gap:
|
||||||
|
raise NotImplementedError(
|
||||||
|
f'Margin and gap not implemented for ({g1}, {g2})'
|
||||||
|
)
|
||||||
|
|
||||||
# TODO(erikfrey): warn for high solver iterations, nefc, etc.
|
# TODO(erikfrey): warn for high solver iterations, nefc, etc.
|
||||||
|
|
||||||
|
|||||||
@@ -91,10 +91,16 @@ def put_model(m: mujoco.MjModel, device=None) -> types.Model:
|
|||||||
raise NotImplementedError('only condim=3 is supported')
|
raise NotImplementedError('only condim=3 is supported')
|
||||||
|
|
||||||
# check collision geom types
|
# check collision geom types
|
||||||
for g1, g2, *_ in collision_driver.collision_candidates(m):
|
for (g1, g2, *_), c in collision_driver.collision_candidates(m).items():
|
||||||
|
g1, g2 = mujoco.mjtGeom(g1), mujoco.mjtGeom(g2)
|
||||||
if collision_driver.get_collision_fn((g1, g2)) is None:
|
if collision_driver.get_collision_fn((g1, g2)) is None:
|
||||||
g1, g2 = mujoco.mjtGeom(g1), mujoco.mjtGeom(g2)
|
|
||||||
raise NotImplementedError(f'({g1}, {g2}) has no collision function')
|
raise NotImplementedError(f'({g1}, {g2}) has no collision function')
|
||||||
|
*_, params = collision_driver.get_params(m, c)
|
||||||
|
margin_gap = not np.allclose(np.concatenate([params.margin, params.gap]), 0)
|
||||||
|
if mujoco.mjtGeom.mjGEOM_MESH in (g1, g2) and margin_gap:
|
||||||
|
raise NotImplementedError(
|
||||||
|
f'Margin and gap not implemented for ({g1}, {g2})'
|
||||||
|
)
|
||||||
|
|
||||||
for enum_field, enum_type, mj_type in (
|
for enum_field, enum_type, mj_type in (
|
||||||
(m.actuator_biastype, types.BiasType, mujoco.mjtBias),
|
(m.actuator_biastype, types.BiasType, mujoco.mjtBias),
|
||||||
|
|||||||
@@ -209,6 +209,25 @@ class ModelIOTest(parameterized.TestCase):
|
|||||||
</worldbody>
|
</worldbody>
|
||||||
</mujoco>"""))
|
</mujoco>"""))
|
||||||
|
|
||||||
|
def test_margin_gap_mesh_not_implemented(self):
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
mjx.put_model(mujoco.MjModel.from_xml_string("""
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<mesh name="box" vertex="-1 -1 -1 1 -1 -1 1 1 -1 1 1 1 1 -1 1 -1 1 -1 -1 1 1 -1 -1 1" scale="1 1 1"/>
|
||||||
|
</asset>
|
||||||
|
<worldbody>
|
||||||
|
<body>
|
||||||
|
<freejoint/>
|
||||||
|
<geom type="mesh" mesh="box" margin="0.3"/>
|
||||||
|
</body>
|
||||||
|
<body>
|
||||||
|
<freejoint/>
|
||||||
|
<geom size="0.05"/>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>"""))
|
||||||
|
|
||||||
|
|
||||||
class DataIOTest(parameterized.TestCase):
|
class DataIOTest(parameterized.TestCase):
|
||||||
"""IO tests for mjx.Data."""
|
"""IO tests for mjx.Data."""
|
||||||
|
|||||||
Reference in New Issue
Block a user