jaxsim 0.4.3.dev364__py3-none-any.whl → 0.4.3.dev365__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
jaxsim/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.4.3.dev364'
16
- __version_tuple__ = version_tuple = (0, 4, 3, 'dev364')
15
+ __version__ = version = '0.4.3.dev365'
16
+ __version_tuple__ = version_tuple = (0, 4, 3, 'dev365')
@@ -1,4 +1,10 @@
1
- from .collision import BoxCollision, CollidablePoint, CollisionShape, SphereCollision
1
+ from .collision import (
2
+ BoxCollision,
3
+ CollidablePoint,
4
+ CollisionShape,
5
+ MeshCollision,
6
+ SphereCollision,
7
+ )
2
8
  from .joint import JointDescription, JointGenericAxis, JointType
3
9
  from .link import LinkDescription
4
10
  from .model import ModelDescription
@@ -154,3 +154,22 @@ class SphereCollision(CollisionShape):
154
154
  return False
155
155
 
156
156
  return hash(self) == hash(other)
157
+
158
+
159
+ @dataclasses.dataclass
160
+ class MeshCollision(CollisionShape):
161
+ center: jtp.VectorLike
162
+
163
+ def __hash__(self) -> int:
164
+ return hash(
165
+ (
166
+ hash(tuple(self.center.tolist())),
167
+ hash(self.collidable_points),
168
+ )
169
+ )
170
+
171
+ def __eq__(self, other: MeshCollision) -> bool:
172
+ if not isinstance(other, MeshCollision):
173
+ return False
174
+
175
+ return hash(self) == hash(other)
@@ -0,0 +1,104 @@
1
+ import numpy as np
2
+ import trimesh
3
+
4
+ VALID_AXIS = {"x": 0, "y": 1, "z": 2}
5
+
6
+
7
+ def extract_points_vertices(mesh: trimesh.Trimesh) -> np.ndarray:
8
+ """
9
+ Extracts the vertices of a mesh as points.
10
+ """
11
+ return mesh.vertices
12
+
13
+
14
+ def extract_points_random_surface_sampling(mesh: trimesh.Trimesh, n) -> np.ndarray:
15
+ """
16
+ Extracts N random points from the surface of a mesh.
17
+
18
+ Args:
19
+ mesh: The mesh from which to extract points.
20
+ n: The number of points to extract.
21
+
22
+ Returns:
23
+ The extracted points (N x 3 array).
24
+ """
25
+
26
+ return mesh.sample(n)
27
+
28
+
29
+ def extract_points_uniform_surface_sampling(
30
+ mesh: trimesh.Trimesh, n: int
31
+ ) -> np.ndarray:
32
+ """
33
+ Extracts N uniformly sampled points from the surface of a mesh.
34
+
35
+ Args:
36
+ mesh: The mesh from which to extract points.
37
+ n: The number of points to extract.
38
+
39
+ Returns:
40
+ The extracted points (N x 3 array).
41
+ """
42
+
43
+ return trimesh.sample.sample_surface_even(mesh=mesh, count=n)[0]
44
+
45
+
46
+ def extract_points_select_points_over_axis(
47
+ mesh: trimesh.Trimesh, axis: str, direction: str, n: int
48
+ ) -> np.ndarray:
49
+ """
50
+ Extracts N points from a mesh along a specified axis. The points are selected based on their position along the axis.
51
+
52
+ Args:
53
+ mesh: The mesh from which to extract points.
54
+ axis: The axis along which to extract points.
55
+ direction: The direction along the axis from which to extract points. Valid values are "higher" and "lower".
56
+ n: The number of points to extract.
57
+
58
+ Returns:
59
+ The extracted points (N x 3 array).
60
+ """
61
+
62
+ dirs = {"higher": np.s_[-n:], "lower": np.s_[:n]}
63
+ arr = mesh.vertices
64
+
65
+ # Sort rows lexicographically first, then columnar.
66
+ arr.sort(axis=0)
67
+ sorted_arr = arr[dirs[direction]]
68
+ return sorted_arr
69
+
70
+
71
+ def extract_points_aap(
72
+ mesh: trimesh.Trimesh,
73
+ axis: str,
74
+ upper: float | None = None,
75
+ lower: float | None = None,
76
+ ) -> np.ndarray:
77
+ """
78
+ Extracts points from a mesh along a specified axis within a specified range. The points are selected based on their position along the axis.
79
+
80
+ Args:
81
+ mesh: The mesh from which to extract points.
82
+ axis: The axis along which to extract points.
83
+ upper: The upper bound of the range.
84
+ lower: The lower bound of the range.
85
+
86
+ Returns:
87
+ The extracted points (N x 3 array).
88
+
89
+ Raises:
90
+ AssertionError: If the lower bound is greater than the upper bound.
91
+ """
92
+
93
+ # Check bounds.
94
+ upper = upper if upper is not None else np.inf
95
+ lower = lower if lower is not None else -np.inf
96
+ assert lower < upper, "Invalid bounds for axis-aligned plane"
97
+
98
+ # Logic.
99
+ points = mesh.vertices[
100
+ (mesh.vertices[:, VALID_AXIS[axis]] >= lower)
101
+ & (mesh.vertices[:, VALID_AXIS[axis]] <= upper)
102
+ ]
103
+
104
+ return points
@@ -334,6 +334,18 @@ def extract_model_data(
334
334
 
335
335
  collisions.append(sphere_collision)
336
336
 
337
+ if collision.geometry.mesh is not None and int(
338
+ os.environ.get("JAXSIM_COLLISION_MESH_ENABLED", "0")
339
+ ):
340
+ logging.warning("Mesh collision support is still experimental.")
341
+ mesh_collision = utils.create_mesh_collision(
342
+ collision=collision,
343
+ link_description=links_dict[link.name],
344
+ method=utils.meshes.extract_points_vertices,
345
+ )
346
+
347
+ collisions.append(mesh_collision)
348
+
337
349
  return SDFData(
338
350
  model_name=sdf_model.name,
339
351
  link_descriptions=links,
@@ -1,12 +1,21 @@
1
1
  import os
2
+ import pathlib
3
+ from collections.abc import Callable
4
+ from typing import TypeVar
2
5
 
3
6
  import numpy as np
4
7
  import numpy.typing as npt
5
8
  import rod
9
+ import trimesh
10
+ from rod.utils.resolve_uris import resolve_local_uri
6
11
 
7
12
  import jaxsim.typing as jtp
13
+ from jaxsim import logging
8
14
  from jaxsim.math import Adjoint, Inertia
9
15
  from jaxsim.parsers import descriptions
16
+ from jaxsim.parsers.rod import meshes
17
+
18
+ MeshMappingMethod = TypeVar("MeshMappingMethod", bound=Callable[..., npt.NDArray])
10
19
 
11
20
 
12
21
  def from_sdf_inertial(inertial: rod.Inertial) -> jtp.Matrix:
@@ -202,3 +211,47 @@ def create_sphere_collision(
202
211
  return descriptions.SphereCollision(
203
212
  collidable_points=collidable_points, center=center_wrt_link
204
213
  )
214
+
215
+
216
+ def create_mesh_collision(
217
+ collision: rod.Collision,
218
+ link_description: descriptions.LinkDescription,
219
+ method: MeshMappingMethod = None,
220
+ ) -> descriptions.MeshCollision:
221
+
222
+ file = pathlib.Path(resolve_local_uri(uri=collision.geometry.mesh.uri))
223
+ _file_type = file.suffix.replace(".", "")
224
+ mesh = trimesh.load_mesh(file, file_type=_file_type)
225
+
226
+ if mesh.is_empty:
227
+ raise RuntimeError(f"Failed to process '{file}' with trimesh")
228
+
229
+ mesh.apply_scale(collision.geometry.mesh.scale)
230
+ logging.info(
231
+ msg=f"Loading mesh {collision.geometry.mesh.uri} with scale {collision.geometry.mesh.scale}, file type '{_file_type}'"
232
+ )
233
+
234
+ if method is None:
235
+ method = meshes.VertexExtraction()
236
+ logging.debug("Using default Vertex Extraction method for mesh wrapping")
237
+ else:
238
+ logging.debug(f"Using method {method} for mesh wrapping")
239
+
240
+ points = method(mesh=mesh)
241
+ logging.debug(f"Extracted {len(points)} points from mesh")
242
+
243
+ W_H_L = collision.pose.transform() if collision.pose is not None else np.eye(4)
244
+
245
+ # Extract translation from transformation matrix
246
+ W_p_L = W_H_L[:3, 3]
247
+ mesh_points_wrt_link = points @ W_H_L[:3, :3].T + W_p_L
248
+ collidable_points = [
249
+ descriptions.CollidablePoint(
250
+ parent_link=link_description,
251
+ position=point,
252
+ enabled=True,
253
+ )
254
+ for point in mesh_points_wrt_link
255
+ ]
256
+
257
+ return descriptions.MeshCollision(collidable_points=collidable_points, center=W_p_L)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jaxsim
3
- Version: 0.4.3.dev364
3
+ Version: 0.4.3.dev365
4
4
  Summary: A differentiable physics engine and multibody dynamics library for control and robot learning.
5
5
  Author-email: Diego Ferigo <dgferigo@gmail.com>, Filippo Luca Ferretti <filippoluca.ferretti@outlook.com>
6
6
  Maintainer-email: Filippo Luca Ferretti <filippo.ferretti@iit.it>, Alessandro Croci <alessandro.croci@iit.it>
@@ -69,6 +69,7 @@ Requires-Dist: pptree
69
69
  Requires-Dist: optax>=0.2.3
70
70
  Requires-Dist: qpax
71
71
  Requires-Dist: rod>=0.3.3
72
+ Requires-Dist: trimesh
72
73
  Requires-Dist: typing-extensions; python_version < "3.12"
73
74
  Provides-Extra: all
74
75
  Requires-Dist: jaxsim[style,testing,viz]; extra == "all"
@@ -1,5 +1,5 @@
1
1
  jaxsim/__init__.py,sha256=opgtbhhd1kDsHI4H1vOd3loMPDRi884yQ3tohfFGfNc,3382
2
- jaxsim/_version.py,sha256=oVKctISvwW5BE5qS8_uvJRJEQ5goquTFM3lihueDyJ0,428
2
+ jaxsim/_version.py,sha256=MAn1zPonARBalCehiaTUL7_k9OAe5L1w_cFS6kmRIWI,428
3
3
  jaxsim/exceptions.py,sha256=vSoScaRD4nvh6jltgK9Ry5pKnE0O5hb4_yI_pk_fvR8,2175
4
4
  jaxsim/logging.py,sha256=STI-D_upXZYX-ZezLrlJJ0UlD5YspST0vZ_DcIwkzO4,1553
5
5
  jaxsim/typing.py,sha256=2HXy9hgazPXjofi1vLQ09ZubPtgVmg80U9NKmZ6NYiI,761
@@ -37,14 +37,15 @@ jaxsim/mujoco/utils.py,sha256=vZ8afASNOSxnxVW9p_1U1J_n-9nVhnBDqlV5k8c1GkM,8256
37
37
  jaxsim/mujoco/visualizer.py,sha256=nD6SNWmn-nxjjjIY9oPAHvL2j8q93DJDjZeepzke_DQ,6988
38
38
  jaxsim/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
39
  jaxsim/parsers/kinematic_graph.py,sha256=MJkJ7AW1TdLZmxibuiVrTfn6jHjh3OVhEF20DqwsCnM,34748
40
- jaxsim/parsers/descriptions/__init__.py,sha256=PbIlunVfb59pB5jSX97YVpMAANRZPRkJ0X-hS14rzv4,221
41
- jaxsim/parsers/descriptions/collision.py,sha256=BQeIG-TKi4SVny23w6riDrQ5itC6VRwEMBX6HgAXHxA,3973
40
+ jaxsim/parsers/descriptions/__init__.py,sha256=N_hp8cGI5FyEFiuNx9a-CAGCr0F0QpYEpdMHvwB7_1g,261
41
+ jaxsim/parsers/descriptions/collision.py,sha256=mLcwj61of16MVJf64I95E5SiQZST3uyTEfS0ImhGBSI,4419
42
42
  jaxsim/parsers/descriptions/joint.py,sha256=2KWLP4ILPMV8q1X0J7aS3GGFeZn4zXan0dqGOWc7XuQ,4365
43
43
  jaxsim/parsers/descriptions/link.py,sha256=Eh0W5qL7_Uw0GV-BkNKXhm9Q2dRTfIWCX5D-87zQkxA,3711
44
44
  jaxsim/parsers/descriptions/model.py,sha256=I2Vsbv8Josl4Le7b5rIvhqA2k9Bbv5JxMqwytayxds0,9833
45
45
  jaxsim/parsers/rod/__init__.py,sha256=G2vqlLajBLUc4gyzXwsEI2Wsi4TMOIF9bLDFeT6KrGU,92
46
- jaxsim/parsers/rod/parser.py,sha256=EXcbtr_vMjAaUzQjfQlD1zLLYLAZXrNeFHaiZVlLwFI,13976
47
- jaxsim/parsers/rod/utils.py,sha256=czQ2Y1_I9zGO0y2XDotHSqDorVH6zEcPhkuelApjs3k,5697
46
+ jaxsim/parsers/rod/meshes.py,sha256=a5qUFkHe3gPXMKRnvPx3BDvlEHT2vEJqcLYLXhJFCdA,2847
47
+ jaxsim/parsers/rod/parser.py,sha256=OJdKM2hVyED9nw7iRpxCRFZx9Z6rKmVflhkItGTzRns,14499
48
+ jaxsim/parsers/rod/utils.py,sha256=-bVIbO8AOnImkVbf8XA3XOweoTny7m85ACLkR0d_p2E,7505
48
49
  jaxsim/rbda/__init__.py,sha256=kmy4G9aMkrqPNGdLSaSV3k15dpF52vBEUQXDFDuKIxU,337
49
50
  jaxsim/rbda/aba.py,sha256=w7ciyxB0IsmueatT0C7PcBQEl9dyiH9oqJgIi3xeTUE,8983
50
51
  jaxsim/rbda/collidable_points.py,sha256=0PFLzxWKtRg8-JtfNhGlSjBMv1J98tiLymOdvlvAak4,5325
@@ -65,8 +66,8 @@ jaxsim/utils/__init__.py,sha256=Y5zyoRevl3EMVQadhZ4EtSwTEkDt2vcnFoRhPJjKTZ0,215
65
66
  jaxsim/utils/jaxsim_dataclass.py,sha256=TGmTQV2Lq7Q-2nLoAEaeNtkPa_qj0IKkdBm4COj46Os,11312
66
67
  jaxsim/utils/tracing.py,sha256=eEY28MZW0Lm_jJNt1NkFqZz0ek01tvhR46OXZYCo7tc,532
67
68
  jaxsim/utils/wrappers.py,sha256=ZY7olSORzZRvSzkdeNLj8yjwUIAt9L0Douwl7wItjpk,4008
68
- jaxsim-0.4.3.dev364.dist-info/LICENSE,sha256=eaYdFmdeMbiIoIiPzEK0MjP1S9wtFXjXNR5er49uLR0,1546
69
- jaxsim-0.4.3.dev364.dist-info/METADATA,sha256=l0SzDTLIIyjVPsCNDnZQ3UX-L50mUBLD_VfkU0lflSE,17513
70
- jaxsim-0.4.3.dev364.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
71
- jaxsim-0.4.3.dev364.dist-info/top_level.txt,sha256=LxGMA8FLtXjQ6oI7N5gd_R_oSUHxpXxUEOfT1xS_ni0,7
72
- jaxsim-0.4.3.dev364.dist-info/RECORD,,
69
+ jaxsim-0.4.3.dev365.dist-info/LICENSE,sha256=eaYdFmdeMbiIoIiPzEK0MjP1S9wtFXjXNR5er49uLR0,1546
70
+ jaxsim-0.4.3.dev365.dist-info/METADATA,sha256=_oydr6ibnTCcmb-aQwfcEFX1ZlVSnnj7N-rUzv2-C50,17536
71
+ jaxsim-0.4.3.dev365.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
72
+ jaxsim-0.4.3.dev365.dist-info/top_level.txt,sha256=LxGMA8FLtXjQ6oI7N5gd_R_oSUHxpXxUEOfT1xS_ni0,7
73
+ jaxsim-0.4.3.dev365.dist-info/RECORD,,