Add sphere-ellipsoid, sphere-cylinder. #2126
PiperOrigin-RevId: 683747371 Change-Id: Ic11803bb395944380d25ce848d9fbcf19afaad77
This commit is contained in:
committed by
Copybara-Service
parent
8256546432
commit
fa22e6d001
@@ -18,6 +18,7 @@ MJX
|
|||||||
^^^
|
^^^
|
||||||
- Added ``mocap_pos`` and ``mocap_quat`` in kinematics.
|
- Added ``mocap_pos`` and ``mocap_quat`` in kinematics.
|
||||||
- Added support for :ref:`spatial tendons <tendon-spatial>` with pulleys and external sphere and cylinder wrapping.
|
- Added support for :ref:`spatial tendons <tendon-spatial>` with pulleys and external sphere and cylinder wrapping.
|
||||||
|
- Add sphere-cylinder and sphere-ellipsoid collision functions (:github:issue:`2126`).
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ from mujoco.mjx._src.collision_sdf import capsule_ellipsoid
|
|||||||
from mujoco.mjx._src.collision_sdf import cylinder_cylinder
|
from mujoco.mjx._src.collision_sdf import cylinder_cylinder
|
||||||
from mujoco.mjx._src.collision_sdf import ellipsoid_cylinder
|
from mujoco.mjx._src.collision_sdf import ellipsoid_cylinder
|
||||||
from mujoco.mjx._src.collision_sdf import ellipsoid_ellipsoid
|
from mujoco.mjx._src.collision_sdf import ellipsoid_ellipsoid
|
||||||
|
from mujoco.mjx._src.collision_sdf import sphere_cylinder
|
||||||
|
from mujoco.mjx._src.collision_sdf import sphere_ellipsoid
|
||||||
from mujoco.mjx._src.collision_types import FunctionKey
|
from mujoco.mjx._src.collision_types import FunctionKey
|
||||||
from mujoco.mjx._src.types import Contact
|
from mujoco.mjx._src.types import Contact
|
||||||
from mujoco.mjx._src.types import Data
|
from mujoco.mjx._src.types import Data
|
||||||
@@ -89,6 +91,8 @@ _COLLISION_FUNC = {
|
|||||||
(GeomType.HFIELD, GeomType.MESH): hfield_convex,
|
(GeomType.HFIELD, GeomType.MESH): hfield_convex,
|
||||||
(GeomType.SPHERE, GeomType.SPHERE): sphere_sphere,
|
(GeomType.SPHERE, GeomType.SPHERE): sphere_sphere,
|
||||||
(GeomType.SPHERE, GeomType.CAPSULE): sphere_capsule,
|
(GeomType.SPHERE, GeomType.CAPSULE): sphere_capsule,
|
||||||
|
(GeomType.SPHERE, GeomType.CYLINDER): sphere_cylinder,
|
||||||
|
(GeomType.SPHERE, GeomType.ELLIPSOID): sphere_ellipsoid,
|
||||||
(GeomType.SPHERE, GeomType.BOX): sphere_convex,
|
(GeomType.SPHERE, GeomType.BOX): sphere_convex,
|
||||||
(GeomType.SPHERE, GeomType.MESH): sphere_convex,
|
(GeomType.SPHERE, GeomType.MESH): sphere_convex,
|
||||||
(GeomType.CAPSULE, GeomType.CAPSULE): capsule_capsule,
|
(GeomType.CAPSULE, GeomType.CAPSULE): capsule_capsule,
|
||||||
|
|||||||
@@ -242,6 +242,29 @@ class EllipsoidCollisionTest(parameterized.TestCase):
|
|||||||
_assert_attr_eq(
|
_assert_attr_eq(
|
||||||
dx.contact, d.contact, field.name, 'ellipsoid-ellipsoid', 1e-5)
|
dx.contact, d.contact, field.name, 'ellipsoid-ellipsoid', 1e-5)
|
||||||
|
|
||||||
|
_ELLIPSOID_SPHERE = """
|
||||||
|
<mujoco>
|
||||||
|
<worldbody>
|
||||||
|
<body>
|
||||||
|
<geom size=".15 .03 .05" type="ellipsoid"/>
|
||||||
|
</body>
|
||||||
|
<body pos="0 0 0.08">
|
||||||
|
<freejoint/>
|
||||||
|
<geom size=".05" type="sphere"/>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_sphere_ellipsoid(self):
|
||||||
|
"""Tests ellipsoid capsule contact."""
|
||||||
|
d, dx = _collide(self._ELLIPSOID_SPHERE)
|
||||||
|
d.contact.pos[0][2] = 0.03 # MJX finds the point on the surface
|
||||||
|
self.assertLess(dx.contact.dist[0], 0)
|
||||||
|
for field in dataclasses.fields(Contact):
|
||||||
|
_assert_attr_eq(
|
||||||
|
dx.contact, d.contact, field.name, 'ellipsoid-sphere', 1e-4)
|
||||||
|
|
||||||
_ELLIPSOID_CAPSULE = """
|
_ELLIPSOID_CAPSULE = """
|
||||||
<mujoco>
|
<mujoco>
|
||||||
<worldbody>
|
<worldbody>
|
||||||
@@ -535,6 +558,29 @@ class CylinderTest(absltest.TestCase):
|
|||||||
for field in dataclasses.fields(Contact):
|
for field in dataclasses.fields(Contact):
|
||||||
_assert_attr_eq(dx.contact, d.contact, field.name, 'cylinder_plane', 1e-5)
|
_assert_attr_eq(dx.contact, d.contact, field.name, 'cylinder_plane', 1e-5)
|
||||||
|
|
||||||
|
_SPHERE_CYLINDER = """
|
||||||
|
<mujoco>
|
||||||
|
<worldbody>
|
||||||
|
<body>
|
||||||
|
<geom size=".15 .05" type="cylinder"/>
|
||||||
|
</body>
|
||||||
|
<body pos="0 0 0.12">
|
||||||
|
<freejoint/>
|
||||||
|
<geom size=".15" type="sphere"/>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_sphere_cylinder(self):
|
||||||
|
"""Tests sphere cylinder contact."""
|
||||||
|
d, dx = _collide(self._SPHERE_CYLINDER)
|
||||||
|
d.contact.pos[0][2] = 0.05 # MJX finds the deepest point on the surface
|
||||||
|
self.assertLess(dx.contact.dist[0], 0)
|
||||||
|
for field in dataclasses.fields(Contact):
|
||||||
|
_assert_attr_eq(
|
||||||
|
dx.contact, d.contact, field.name, 'sphere-cylinder', 1e-4)
|
||||||
|
|
||||||
|
|
||||||
class ConvexTest(absltest.TestCase):
|
class ConvexTest(absltest.TestCase):
|
||||||
"""Tests the convex contact functions."""
|
"""Tests the convex contact functions."""
|
||||||
@@ -558,7 +604,9 @@ class ConvexTest(absltest.TestCase):
|
|||||||
np.testing.assert_array_less(dx.contact.dist[:2], 0)
|
np.testing.assert_array_less(dx.contact.dist[:2], 0)
|
||||||
np.testing.assert_array_less(-dx.contact.dist[2:], 0)
|
np.testing.assert_array_less(-dx.contact.dist[2:], 0)
|
||||||
# extract the contact points with penetration
|
# extract the contact points with penetration
|
||||||
c = jax.tree_util.tree_map(lambda x: jp.take(x, jp.array([0, 1]), axis=0), dx.contact)
|
c = jax.tree_util.tree_map(
|
||||||
|
lambda x: jp.take(x, jp.array([0, 1]), axis=0), dx.contact
|
||||||
|
)
|
||||||
c = c.replace(dim=c.dim[[0, 1]], efc_address=c.efc_address[[0, 1]])
|
c = c.replace(dim=c.dim[[0, 1]], efc_address=c.efc_address[[0, 1]])
|
||||||
for field in dataclasses.fields(Contact):
|
for field in dataclasses.fields(Contact):
|
||||||
_assert_attr_eq(c, d.contact, field.name, 'box_plane', 1e-5)
|
_assert_attr_eq(c, d.contact, field.name, 'box_plane', 1e-5)
|
||||||
|
|||||||
@@ -196,6 +196,21 @@ def _optim(
|
|||||||
return dist, pos, math.make_frame(n)
|
return dist, pos, math.make_frame(n)
|
||||||
|
|
||||||
|
|
||||||
|
@collider(ncon=1)
|
||||||
|
def sphere_ellipsoid(s: GeomInfo, e: GeomInfo) -> Collision:
|
||||||
|
""""Calculates contact between a sphere and an ellipsoid."""
|
||||||
|
x0 = 0.5 * (s.pos + e.pos)
|
||||||
|
return _optim(_sphere, _ellipsoid, s, e, x0)
|
||||||
|
|
||||||
|
|
||||||
|
@collider(ncon=1)
|
||||||
|
def sphere_cylinder(s: GeomInfo, c: GeomInfo) -> Collision:
|
||||||
|
""""Calculates contact between a sphere and a cylinder."""
|
||||||
|
# TODO: implement analytical version.
|
||||||
|
x0 = 0.5 * (s.pos + c.pos)
|
||||||
|
return _optim(_sphere, _cylinder, s, c, x0)
|
||||||
|
|
||||||
|
|
||||||
@collider(ncon=1)
|
@collider(ncon=1)
|
||||||
def capsule_ellipsoid(c: GeomInfo, e: GeomInfo) -> Collision:
|
def capsule_ellipsoid(c: GeomInfo, e: GeomInfo) -> Collision:
|
||||||
""""Calculates contact between a capsule and an ellipsoid."""
|
""""Calculates contact between a capsule and an ellipsoid."""
|
||||||
|
|||||||
@@ -193,22 +193,6 @@ class ModelIOTest(parameterized.TestCase):
|
|||||||
</tendon>
|
</tendon>
|
||||||
</mujoco>"""))
|
</mujoco>"""))
|
||||||
|
|
||||||
def test_cylinder_not_implemented(self):
|
|
||||||
with self.assertRaises(NotImplementedError):
|
|
||||||
mjx.put_model(mujoco.MjModel.from_xml_string("""
|
|
||||||
<mujoco>
|
|
||||||
<worldbody>
|
|
||||||
<body>
|
|
||||||
<freejoint/>
|
|
||||||
<geom type="cylinder" size="0.05 0.05"/>
|
|
||||||
</body>
|
|
||||||
<body>
|
|
||||||
<freejoint/>
|
|
||||||
<geom size="0.05"/>
|
|
||||||
</body>
|
|
||||||
</worldbody>
|
|
||||||
</mujoco>"""))
|
|
||||||
|
|
||||||
def test_margin_gap_mesh_not_implemented(self):
|
def test_margin_gap_mesh_not_implemented(self):
|
||||||
with self.assertRaises(NotImplementedError):
|
with self.assertRaises(NotImplementedError):
|
||||||
mjx.put_model(mujoco.MjModel.from_xml_string("""
|
mjx.put_model(mujoco.MjModel.from_xml_string("""
|
||||||
|
|||||||
Reference in New Issue
Block a user