Import google-deepmind/mujoco_warp from GitHub.
#MIGRATION_3P_PY_MUJOCO_WARP__DEFAULT - 19dde09e179dbbaf4d07084185074f6a774b3255 update derivative.py by Taylor Howell <taylorhowell@google.com> - 04bc115c3766e974ef000cca16d40bacb92c01d2 fix _ray_hfield by Taylor Howell <taylorhowell@google.com> - 7fcbf8ddba08c9b9d33b0fdd4dd3fe39e40a0bd7 Use sparsity structures in `mjModel` rather than `mjData` by Yuval Tassa <6387526+yuvaltassa@users.noreply.github.com> - bfd2e4fe3cb5ba5dee97445ddc1d072344f443ec Fix override for enums (#617) by Kenny-Vilella <163487060+Kenny-Vilella@users.noreply.github.com> - f4dbf5345bd5dcae60c415361dd425605b672e5c Add keyframe selection to viewer (#616) by Kenny-Vilella <163487060+Kenny-Vilella@users.noreply.github.com> - 0e7edf9e8fd337502586929b2e4dd3d51201f462 add prunning to multiccd by Kyle Bayes <42924292+kbayes@users.noreply.github.com> - 791c30237cbdecf876bc53ac0b3dcff8b8b1317d format by Kyle Bayes <42924292+kbayes@users.noreply.github.com> - 7a7bae6c1390df3f29058e90f25f9ed5caab3fb1 Fix override in viewer and testspeed (#620) by Kenny-Vilella <163487060+Kenny-Vilella@users.noreply.github.com> - 9e230c229c7dd721fbf6fa5fe69fc98717895500 add + 0.0 by Taylor Howell <taylorhowell@google.com> - 8a70a5e8839b84c11601bf1a98f9e7d1d31603f1 Update the test_cost unit test to test more properties (#... by Kenny-Vilella <163487060+Kenny-Vilella@users.noreply.github.com> - cf317c60845b24ddba4b6814dbac9eba32b4c8b0 parallel linesearch with log scale for step sizes (#575) by Taylor Howell <taylorhowell@google.com> - b73e4edbc0d331d1e4f15c006d4535cc59595e70 add better reporting (#600) by adenzler-nvidia <116633880+adenzler-nvidia@users.noreply.github.com> - 007ad0b204ed4d48584024cce4676ac25a5695b5 Optimization: Fusing kernels around update_gradient_JTDAJ... by Kenny-Vilella <163487060+Kenny-Vilella@users.noreply.github.com> - 640a89fcf452b9564508cacdeb30ecef3a83aede Adding static to broadphase filter (#621) by Kenny-Vilella <163487060+Kenny-Vilella@users.noreply.github.com> - daca09ff3261db2713abc912191d6cf76437b8b7 Add reporting of converged worlds in testspeed (#619) by Kenny-Vilella <163487060+Kenny-Vilella@users.noreply.github.com> - 33830bddd95a7f0d3febe1b090398ddadeb86ee2 Fix call to benchmark from asv (#627) by Kenny-Vilella <163487060+Kenny-Vilella@users.noreply.github.com> - 382eb65f7bb45798698f4025a980287ab1ec84c7 Update flex_bending dimensions (#628) by Alessio Quaglino <102665781+quagla@users.noreply.github.com> - c56b8875439cfaa93c1393f0a6e2ad057e029064 contact sensor maxforce reduction (#602) by Taylor Howell <taylorhowell@google.com> - 4f731941cb9dea397d4f71cf8151100b83eae576 A few airspeed velocity improvements. (#626) by Erik Frey <misc@fawx.com> PiperOrigin-RevId: 797054114 Change-Id: I380e0b9446f94202b2389e2a760d6de5f0ec5af7
This commit is contained in:
committed by
Copybara-Service
parent
fd96de330e
commit
bf04103b6f
+162
-158
@@ -29,7 +29,9 @@ from mujoco.mjx.third_party.mujoco_warp._src.types import Data
|
|||||||
from mujoco.mjx.third_party.mujoco_warp._src.types import DisableBit
|
from mujoco.mjx.third_party.mujoco_warp._src.types import DisableBit
|
||||||
from mujoco.mjx.third_party.mujoco_warp._src.types import GeomType
|
from mujoco.mjx.third_party.mujoco_warp._src.types import GeomType
|
||||||
from mujoco.mjx.third_party.mujoco_warp._src.types import Model
|
from mujoco.mjx.third_party.mujoco_warp._src.types import Model
|
||||||
|
from mujoco.mjx.third_party.mujoco_warp._src.warp_util import cache_kernel
|
||||||
from mujoco.mjx.third_party.mujoco_warp._src.warp_util import event_scope
|
from mujoco.mjx.third_party.mujoco_warp._src.warp_util import event_scope
|
||||||
|
from mujoco.mjx.third_party.mujoco_warp._src.warp_util import kernel as nested_kernel
|
||||||
|
|
||||||
wp.set_module_options({"enable_backward": False})
|
wp.set_module_options({"enable_backward": False})
|
||||||
|
|
||||||
@@ -248,54 +250,56 @@ def _obb_filter(
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@wp.func
|
def _broadphase_filter(opt_broadphase_filter: int):
|
||||||
def _broadphase_filter(
|
@wp.func
|
||||||
# Model:
|
def func(
|
||||||
opt_broadphase_filter: int,
|
# Model:
|
||||||
geom_aabb: wp.array2d(dtype=wp.vec3),
|
geom_aabb: wp.array2d(dtype=wp.vec3),
|
||||||
geom_rbound: wp.array2d(dtype=float),
|
geom_rbound: wp.array2d(dtype=float),
|
||||||
geom_margin: wp.array2d(dtype=float),
|
geom_margin: wp.array2d(dtype=float),
|
||||||
# Data in:
|
# Data in:
|
||||||
geom_xpos_in: wp.array2d(dtype=wp.vec3),
|
geom_xpos_in: wp.array2d(dtype=wp.vec3),
|
||||||
geom_xmat_in: wp.array2d(dtype=wp.mat33),
|
geom_xmat_in: wp.array2d(dtype=wp.mat33),
|
||||||
# In:
|
# In:
|
||||||
geom1: int,
|
geom1: int,
|
||||||
geom2: int,
|
geom2: int,
|
||||||
worldid: int,
|
worldid: int,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
# 1: plane
|
# 1: plane
|
||||||
# 2: sphere
|
# 2: sphere
|
||||||
# 4: aabb
|
# 4: aabb
|
||||||
# 8: obb
|
# 8: obb
|
||||||
|
|
||||||
center1 = geom_aabb[geom1, 0]
|
center1 = geom_aabb[geom1, 0]
|
||||||
center2 = geom_aabb[geom2, 0]
|
center2 = geom_aabb[geom2, 0]
|
||||||
size1 = geom_aabb[geom1, 1]
|
size1 = geom_aabb[geom1, 1]
|
||||||
size2 = geom_aabb[geom2, 1]
|
size2 = geom_aabb[geom2, 1]
|
||||||
rbound1 = geom_rbound[worldid, geom1]
|
rbound1 = geom_rbound[worldid, geom1]
|
||||||
rbound2 = geom_rbound[worldid, geom2]
|
rbound2 = geom_rbound[worldid, geom2]
|
||||||
margin1 = geom_margin[worldid, geom1]
|
margin1 = geom_margin[worldid, geom1]
|
||||||
margin2 = geom_margin[worldid, geom2]
|
margin2 = geom_margin[worldid, geom2]
|
||||||
xpos1 = geom_xpos_in[worldid, geom1]
|
xpos1 = geom_xpos_in[worldid, geom1]
|
||||||
xpos2 = geom_xpos_in[worldid, geom2]
|
xpos2 = geom_xpos_in[worldid, geom2]
|
||||||
xmat1 = geom_xmat_in[worldid, geom1]
|
xmat1 = geom_xmat_in[worldid, geom1]
|
||||||
xmat2 = geom_xmat_in[worldid, geom2]
|
xmat2 = geom_xmat_in[worldid, geom2]
|
||||||
|
|
||||||
if rbound1 == 0.0 or rbound2 == 0.0:
|
if rbound1 == 0.0 or rbound2 == 0.0:
|
||||||
if opt_broadphase_filter & int(BroadphaseFilter.PLANE.value):
|
if wp.static(opt_broadphase_filter & int(BroadphaseFilter.PLANE.value)):
|
||||||
return _plane_filter(rbound1, rbound2, margin1, margin2, xpos1, xpos2, xmat1, xmat2)
|
return _plane_filter(rbound1, rbound2, margin1, margin2, xpos1, xpos2, xmat1, xmat2)
|
||||||
else:
|
else:
|
||||||
if opt_broadphase_filter & int(BroadphaseFilter.SPHERE.value):
|
if wp.static(opt_broadphase_filter & int(BroadphaseFilter.SPHERE.value)):
|
||||||
if not _sphere_filter(rbound1, rbound2, margin1, margin2, xpos1, xpos2):
|
if not _sphere_filter(rbound1, rbound2, margin1, margin2, xpos1, xpos2):
|
||||||
return False
|
return False
|
||||||
if opt_broadphase_filter & int(BroadphaseFilter.AABB.value):
|
if wp.static(opt_broadphase_filter & int(BroadphaseFilter.AABB.value)):
|
||||||
if not _aabb_filter(center1, center2, size1, size2, margin1, margin2, xpos1, xpos2, xmat1, xmat2):
|
if not _aabb_filter(center1, center2, size1, size2, margin1, margin2, xpos1, xpos2, xmat1, xmat2):
|
||||||
return False
|
return False
|
||||||
if opt_broadphase_filter & int(BroadphaseFilter.OBB.value):
|
if wp.static(opt_broadphase_filter & int(BroadphaseFilter.OBB.value)):
|
||||||
if not _obb_filter(center1, center2, size1, size2, margin1, margin2, xpos1, xpos2, xmat1, xmat2):
|
if not _obb_filter(center1, center2, size1, size2, margin1, margin2, xpos1, xpos2, xmat1, xmat2):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
return func
|
||||||
|
|
||||||
|
|
||||||
@wp.func
|
@wp.func
|
||||||
@@ -413,82 +417,81 @@ def _sap_range(
|
|||||||
sap_range_out[worldid, geomid] = limit - geomid
|
sap_range_out[worldid, geomid] = limit - geomid
|
||||||
|
|
||||||
|
|
||||||
@wp.kernel
|
@cache_kernel
|
||||||
def _sap_broadphase(
|
def _sap_broadphase(broadphase_filter):
|
||||||
# Model:
|
@nested_kernel
|
||||||
ngeom: int,
|
def kernel(
|
||||||
opt_broadphase_filter: int,
|
# Model:
|
||||||
geom_type: wp.array(dtype=int),
|
ngeom: int,
|
||||||
geom_aabb: wp.array2d(dtype=wp.vec3),
|
geom_type: wp.array(dtype=int),
|
||||||
geom_rbound: wp.array2d(dtype=float),
|
geom_aabb: wp.array2d(dtype=wp.vec3),
|
||||||
geom_margin: wp.array2d(dtype=float),
|
geom_rbound: wp.array2d(dtype=float),
|
||||||
nxn_pairid: wp.array(dtype=int),
|
geom_margin: wp.array2d(dtype=float),
|
||||||
# Data in:
|
nxn_pairid: wp.array(dtype=int),
|
||||||
nworld_in: int,
|
# Data in:
|
||||||
nconmax_in: int,
|
nworld_in: int,
|
||||||
geom_xpos_in: wp.array2d(dtype=wp.vec3),
|
nconmax_in: int,
|
||||||
geom_xmat_in: wp.array2d(dtype=wp.mat33),
|
geom_xpos_in: wp.array2d(dtype=wp.vec3),
|
||||||
sap_sort_index_in: wp.array2d(dtype=int), # kernel_analyzer: ignore
|
geom_xmat_in: wp.array2d(dtype=wp.mat33),
|
||||||
sap_cumulative_sum_in: wp.array(dtype=int), # kernel_analyzer: ignore
|
sap_sort_index_in: wp.array2d(dtype=int), # kernel_analyzer: ignore
|
||||||
# In:
|
sap_cumulative_sum_in: wp.array(dtype=int), # kernel_analyzer: ignore
|
||||||
nsweep_in: int,
|
# In:
|
||||||
# Data out:
|
nsweep_in: int,
|
||||||
collision_pair_out: wp.array(dtype=wp.vec2i),
|
# Data out:
|
||||||
collision_hftri_index_out: wp.array(dtype=int),
|
collision_pair_out: wp.array(dtype=wp.vec2i),
|
||||||
collision_pairid_out: wp.array(dtype=int),
|
collision_hftri_index_out: wp.array(dtype=int),
|
||||||
collision_worldid_out: wp.array(dtype=int),
|
collision_pairid_out: wp.array(dtype=int),
|
||||||
ncollision_out: wp.array(dtype=int),
|
collision_worldid_out: wp.array(dtype=int),
|
||||||
):
|
ncollision_out: wp.array(dtype=int),
|
||||||
worldgeomid = wp.tid()
|
):
|
||||||
|
worldgeomid = wp.tid()
|
||||||
|
|
||||||
nworldgeom = nworld_in * ngeom
|
nworldgeom = nworld_in * ngeom
|
||||||
nworkpackages = sap_cumulative_sum_in[nworldgeom - 1]
|
nworkpackages = sap_cumulative_sum_in[nworldgeom - 1]
|
||||||
|
|
||||||
while worldgeomid < nworkpackages:
|
while worldgeomid < nworkpackages:
|
||||||
# binary search to find current and next geom pair indices
|
# binary search to find current and next geom pair indices
|
||||||
i = _binary_search(sap_cumulative_sum_in, worldgeomid, 0, nworldgeom)
|
i = _binary_search(sap_cumulative_sum_in, worldgeomid, 0, nworldgeom)
|
||||||
j = i + worldgeomid + 1
|
j = i + worldgeomid + 1
|
||||||
|
|
||||||
if i > 0:
|
if i > 0:
|
||||||
j -= sap_cumulative_sum_in[i - 1]
|
j -= sap_cumulative_sum_in[i - 1]
|
||||||
|
|
||||||
worldid = i // ngeom
|
worldid = i // ngeom
|
||||||
i = i % ngeom
|
i = i % ngeom
|
||||||
j = j % ngeom
|
j = j % ngeom
|
||||||
|
|
||||||
# get geom indices and swap if necessary
|
# get geom indices and swap if necessary
|
||||||
geom1 = sap_sort_index_in[worldid, i]
|
geom1 = sap_sort_index_in[worldid, i]
|
||||||
geom2 = sap_sort_index_in[worldid, j]
|
geom2 = sap_sort_index_in[worldid, j]
|
||||||
|
|
||||||
# find linear index of (geom1, geom2) in upper triangular nxn_pairid
|
# find linear index of (geom1, geom2) in upper triangular nxn_pairid
|
||||||
if geom2 < geom1:
|
if geom2 < geom1:
|
||||||
idx = upper_tri_index(ngeom, geom2, geom1)
|
idx = upper_tri_index(ngeom, geom2, geom1)
|
||||||
else:
|
else:
|
||||||
idx = upper_tri_index(ngeom, geom1, geom2)
|
idx = upper_tri_index(ngeom, geom1, geom2)
|
||||||
|
|
||||||
if nxn_pairid[idx] < -1:
|
|
||||||
worldgeomid += nsweep_in
|
worldgeomid += nsweep_in
|
||||||
continue
|
if nxn_pairid[idx] < -1:
|
||||||
|
continue
|
||||||
|
|
||||||
if _broadphase_filter(
|
if broadphase_filter(geom_aabb, geom_rbound, geom_margin, geom_xpos_in, geom_xmat_in, geom1, geom2, worldid):
|
||||||
opt_broadphase_filter, geom_aabb, geom_rbound, geom_margin, geom_xpos_in, geom_xmat_in, geom1, geom2, worldid
|
_add_geom_pair(
|
||||||
):
|
geom_type,
|
||||||
_add_geom_pair(
|
nxn_pairid,
|
||||||
geom_type,
|
nconmax_in,
|
||||||
nxn_pairid,
|
geom1,
|
||||||
nconmax_in,
|
geom2,
|
||||||
geom1,
|
worldid,
|
||||||
geom2,
|
idx,
|
||||||
worldid,
|
collision_pair_out,
|
||||||
idx,
|
collision_hftri_index_out,
|
||||||
collision_pair_out,
|
collision_pairid_out,
|
||||||
collision_hftri_index_out,
|
collision_worldid_out,
|
||||||
collision_pairid_out,
|
ncollision_out,
|
||||||
collision_worldid_out,
|
)
|
||||||
ncollision_out,
|
|
||||||
)
|
|
||||||
|
|
||||||
worldgeomid += nsweep_in
|
return kernel
|
||||||
|
|
||||||
|
|
||||||
def _segmented_sort(tile_size: int):
|
def _segmented_sort(tile_size: int):
|
||||||
@@ -591,12 +594,12 @@ def sap_broadphase(m: Model, d: Data):
|
|||||||
# estimate number of overlap checks
|
# estimate number of overlap checks
|
||||||
# assumes each geom has 5 other geoms (batched over all worlds)
|
# assumes each geom has 5 other geoms (batched over all worlds)
|
||||||
nsweep = 5 * nworldgeom
|
nsweep = 5 * nworldgeom
|
||||||
|
broadphase_filter = _broadphase_filter(m.opt.broadphase_filter)
|
||||||
wp.launch(
|
wp.launch(
|
||||||
kernel=_sap_broadphase,
|
kernel=_sap_broadphase(broadphase_filter),
|
||||||
dim=nsweep,
|
dim=nsweep,
|
||||||
inputs=[
|
inputs=[
|
||||||
m.ngeom,
|
m.ngeom,
|
||||||
m.opt.broadphase_filter,
|
|
||||||
m.geom_type,
|
m.geom_type,
|
||||||
m.geom_aabb,
|
m.geom_aabb,
|
||||||
m.geom_rbound,
|
m.geom_rbound,
|
||||||
@@ -620,50 +623,51 @@ def sap_broadphase(m: Model, d: Data):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@wp.kernel
|
@cache_kernel
|
||||||
def _nxn_broadphase(
|
def _nxn_broadphase(broadphase_filter):
|
||||||
# Model:
|
@nested_kernel
|
||||||
opt_broadphase_filter: int,
|
def kernel(
|
||||||
geom_type: wp.array(dtype=int),
|
# Model:
|
||||||
geom_aabb: wp.array2d(dtype=wp.vec3),
|
geom_type: wp.array(dtype=int),
|
||||||
geom_rbound: wp.array2d(dtype=float),
|
geom_aabb: wp.array2d(dtype=wp.vec3),
|
||||||
geom_margin: wp.array2d(dtype=float),
|
geom_rbound: wp.array2d(dtype=float),
|
||||||
nxn_geom_pair: wp.array(dtype=wp.vec2i),
|
geom_margin: wp.array2d(dtype=float),
|
||||||
nxn_pairid: wp.array(dtype=int),
|
nxn_geom_pair: wp.array(dtype=wp.vec2i),
|
||||||
# Data in:
|
nxn_pairid: wp.array(dtype=int),
|
||||||
nconmax_in: int,
|
# Data in:
|
||||||
geom_xpos_in: wp.array2d(dtype=wp.vec3),
|
nconmax_in: int,
|
||||||
geom_xmat_in: wp.array2d(dtype=wp.mat33),
|
geom_xpos_in: wp.array2d(dtype=wp.vec3),
|
||||||
# Data out:
|
geom_xmat_in: wp.array2d(dtype=wp.mat33),
|
||||||
collision_pair_out: wp.array(dtype=wp.vec2i),
|
# Data out:
|
||||||
collision_hftri_index_out: wp.array(dtype=int),
|
collision_pair_out: wp.array(dtype=wp.vec2i),
|
||||||
collision_pairid_out: wp.array(dtype=int),
|
collision_hftri_index_out: wp.array(dtype=int),
|
||||||
collision_worldid_out: wp.array(dtype=int),
|
collision_pairid_out: wp.array(dtype=int),
|
||||||
ncollision_out: wp.array(dtype=int),
|
collision_worldid_out: wp.array(dtype=int),
|
||||||
):
|
ncollision_out: wp.array(dtype=int),
|
||||||
worldid, elementid = wp.tid()
|
|
||||||
|
|
||||||
geom = nxn_geom_pair[elementid]
|
|
||||||
geom1 = geom[0]
|
|
||||||
geom2 = geom[1]
|
|
||||||
|
|
||||||
if _broadphase_filter(
|
|
||||||
opt_broadphase_filter, geom_aabb, geom_rbound, geom_margin, geom_xpos_in, geom_xmat_in, geom1, geom2, worldid
|
|
||||||
):
|
):
|
||||||
_add_geom_pair(
|
worldid, elementid = wp.tid()
|
||||||
geom_type,
|
|
||||||
nxn_pairid,
|
geom = nxn_geom_pair[elementid]
|
||||||
nconmax_in,
|
geom1 = geom[0]
|
||||||
geom1,
|
geom2 = geom[1]
|
||||||
geom2,
|
|
||||||
worldid,
|
if broadphase_filter(geom_aabb, geom_rbound, geom_margin, geom_xpos_in, geom_xmat_in, geom1, geom2, worldid):
|
||||||
elementid,
|
_add_geom_pair(
|
||||||
collision_pair_out,
|
geom_type,
|
||||||
collision_hftri_index_out,
|
nxn_pairid,
|
||||||
collision_pairid_out,
|
nconmax_in,
|
||||||
collision_worldid_out,
|
geom1,
|
||||||
ncollision_out,
|
geom2,
|
||||||
)
|
worldid,
|
||||||
|
elementid,
|
||||||
|
collision_pair_out,
|
||||||
|
collision_hftri_index_out,
|
||||||
|
collision_pairid_out,
|
||||||
|
collision_worldid_out,
|
||||||
|
ncollision_out,
|
||||||
|
)
|
||||||
|
|
||||||
|
return kernel
|
||||||
|
|
||||||
|
|
||||||
@event_scope
|
@event_scope
|
||||||
@@ -681,11 +685,11 @@ def nxn_broadphase(m: Model, d: Data):
|
|||||||
`contype`/`conaffinity`, parent-child relationships, and explicit `<exclude>` tags.
|
`contype`/`conaffinity`, parent-child relationships, and explicit `<exclude>` tags.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
broadphase_filter = _broadphase_filter(m.opt.broadphase_filter)
|
||||||
wp.launch(
|
wp.launch(
|
||||||
_nxn_broadphase,
|
_nxn_broadphase(broadphase_filter),
|
||||||
dim=(d.nworld, m.nxn_geom_pair_filtered.shape[0]),
|
dim=(d.nworld, m.nxn_geom_pair_filtered.shape[0]),
|
||||||
inputs=[
|
inputs=[
|
||||||
m.opt.broadphase_filter,
|
|
||||||
m.geom_type,
|
m.geom_type,
|
||||||
m.geom_aabb,
|
m.geom_aabb,
|
||||||
m.geom_rbound,
|
m.geom_rbound,
|
||||||
|
|||||||
+113
-54
@@ -41,6 +41,7 @@ polyindices = wp.types.vector(MAX_POLYVERT, dtype=int)
|
|||||||
mat43 = wp.types.matrix(shape=(4, 3), dtype=float)
|
mat43 = wp.types.matrix(shape=(4, 3), dtype=float)
|
||||||
mat63 = wp.types.matrix(shape=(6, 3), dtype=float)
|
mat63 = wp.types.matrix(shape=(6, 3), dtype=float)
|
||||||
|
|
||||||
|
# box-box currently supports up to 8 contacts
|
||||||
MULTI_CONTACT_COUNT = 8
|
MULTI_CONTACT_COUNT = 8
|
||||||
mat3c = wp.types.matrix(shape=(MULTI_CONTACT_COUNT, 3), dtype=float)
|
mat3c = wp.types.matrix(shape=(MULTI_CONTACT_COUNT, 3), dtype=float)
|
||||||
|
|
||||||
@@ -94,7 +95,7 @@ class SupportPoint:
|
|||||||
|
|
||||||
|
|
||||||
@wp.func
|
@wp.func
|
||||||
def discrete_geoms(g1: int, g2: int):
|
def _discrete_geoms(g1: int, g2: int):
|
||||||
return (g1 == int(GeomType.MESH.value) or g1 == int(GeomType.BOX.value) or g1 == int(GeomType.HFIELD.value)) and (
|
return (g1 == int(GeomType.MESH.value) or g1 == int(GeomType.BOX.value) or g1 == int(GeomType.HFIELD.value)) and (
|
||||||
g2 == int(GeomType.MESH.value) or g2 == int(GeomType.BOX.value) or g2 == int(GeomType.HFIELD.value)
|
g2 == int(GeomType.MESH.value) or g2 == int(GeomType.BOX.value) or g2 == int(GeomType.HFIELD.value)
|
||||||
)
|
)
|
||||||
@@ -597,7 +598,7 @@ def _gjk(
|
|||||||
use_margin: bool,
|
use_margin: bool,
|
||||||
):
|
):
|
||||||
"""Find distance within a tolerance between two geoms."""
|
"""Find distance within a tolerance between two geoms."""
|
||||||
is_discrete = discrete_geoms(geomtype1, geomtype2)
|
is_discrete = _discrete_geoms(geomtype1, geomtype2)
|
||||||
cutoff2 = cutoff * cutoff
|
cutoff2 = cutoff * cutoff
|
||||||
simplex = mat43()
|
simplex = mat43()
|
||||||
simplex1 = mat43()
|
simplex1 = mat43()
|
||||||
@@ -1203,7 +1204,7 @@ def _polytope4(
|
|||||||
@wp.func
|
@wp.func
|
||||||
def _epa(tolerance: float, epa_iterations: int, pt: Polytope, geom1: Geom, geom2: Geom, geomtype1: int, geomtype2: int):
|
def _epa(tolerance: float, epa_iterations: int, pt: Polytope, geom1: Geom, geom2: Geom, geomtype1: int, geomtype2: int):
|
||||||
"""Recover penetration data from two geoms in contact given an initial polytope."""
|
"""Recover penetration data from two geoms in contact given an initial polytope."""
|
||||||
is_discrete = discrete_geoms(geomtype1, geomtype2)
|
is_discrete = _discrete_geoms(geomtype1, geomtype2)
|
||||||
upper = FLOAT_MAX
|
upper = FLOAT_MAX
|
||||||
upper2 = FLOAT_MAX
|
upper2 = FLOAT_MAX
|
||||||
idx = int(-1)
|
idx = int(-1)
|
||||||
@@ -1309,9 +1310,60 @@ def _epa(tolerance: float, epa_iterations: int, pt: Polytope, geom1: Geom, geom2
|
|||||||
return 0.0, wp.vec3(), wp.vec3(), -1
|
return 0.0, wp.vec3(), wp.vec3(), -1
|
||||||
|
|
||||||
|
|
||||||
|
@wp.func
|
||||||
|
def _area4(a: wp.vec3, b: wp.vec3, c: wp.vec3, d: wp.vec3):
|
||||||
|
"""Computes area of a quadrilateral embedded in 3D space."""
|
||||||
|
return 0.5 * wp.norm_l2(wp.cross(a - d, d - b) + wp.cross(b - c, c - a))
|
||||||
|
|
||||||
|
|
||||||
|
@wp.func
|
||||||
|
def _next(n: int, i: int):
|
||||||
|
"""Returns (i + 1) mod n for 0 <= i <= n - 1."""
|
||||||
|
return wp.where(i == n - 1, 0, i + 1)
|
||||||
|
|
||||||
|
|
||||||
|
@wp.func
|
||||||
|
def _polygon_quad(polygon: polyclip, npolygon: int):
|
||||||
|
"""Returns the indices of a quadrilateral of maximum area in a convex polygon."""
|
||||||
|
b = _next(npolygon, 0)
|
||||||
|
c = _next(npolygon, b)
|
||||||
|
d = _next(npolygon, c)
|
||||||
|
res = wp.vec4i(0, b, c, d)
|
||||||
|
m = _area4(polygon[0], polygon[b], polygon[c], polygon[d])
|
||||||
|
for a in range(npolygon):
|
||||||
|
while True:
|
||||||
|
m_next = _area4(polygon[a], polygon[b], polygon[c], polygon[_next(npolygon, d)])
|
||||||
|
if m_next <= m:
|
||||||
|
break
|
||||||
|
m = m_next
|
||||||
|
d = _next(npolygon, d)
|
||||||
|
res = wp.vec4i(a, b, c, d)
|
||||||
|
while True:
|
||||||
|
m_next = _area4(polygon[a], polygon[b], polygon[_next(npolygon, c)], polygon[d])
|
||||||
|
if m_next <= m:
|
||||||
|
break
|
||||||
|
m = m_next
|
||||||
|
c = _next(npolygon, c)
|
||||||
|
res = wp.vec4i(a, b, c, d)
|
||||||
|
while True:
|
||||||
|
m_next = _area4(polygon[a], polygon[_next(npolygon, b)], polygon[c], polygon[d])
|
||||||
|
if m_next <= m:
|
||||||
|
break
|
||||||
|
m = m_next
|
||||||
|
b = _next(npolygon, b)
|
||||||
|
res = wp.vec4i(a, b, c, d)
|
||||||
|
if b == a:
|
||||||
|
b = _next(npolygon, b)
|
||||||
|
if c == b:
|
||||||
|
c = _next(npolygon, c)
|
||||||
|
if d == c:
|
||||||
|
d == _next(npolygon, d)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
# return number (1, 2 or 3) of dimensions of a simplex; reorder vertices if necessary
|
# return number (1, 2 or 3) of dimensions of a simplex; reorder vertices if necessary
|
||||||
@wp.func
|
@wp.func
|
||||||
def feature_dim(face: wp.vec3i, vert_index: wp.array(dtype=int), vert: wp.array(dtype=wp.vec3)):
|
def _feature_dim(face: wp.vec3i, vert_index: wp.array(dtype=int), vert: wp.array(dtype=wp.vec3)):
|
||||||
v1i = vert_index[face[0]]
|
v1i = vert_index[face[0]]
|
||||||
v2i = vert_index[face[1]]
|
v2i = vert_index[face[1]]
|
||||||
v3i = vert_index[face[2]]
|
v3i = vert_index[face[2]]
|
||||||
@@ -1335,7 +1387,7 @@ def feature_dim(face: wp.vec3i, vert_index: wp.array(dtype=int), vert: wp.array(
|
|||||||
|
|
||||||
# find two normals that are facing each other within a tolerance, return 1 if found
|
# find two normals that are facing each other within a tolerance, return 1 if found
|
||||||
@wp.func
|
@wp.func
|
||||||
def aligned_faces(vert1: polyverts, len1: int, vert2: polyverts, len2: int):
|
def _aligned_faces(vert1: polyverts, len1: int, vert2: polyverts, len2: int):
|
||||||
res = wp.vec2i()
|
res = wp.vec2i()
|
||||||
for i in range(len1):
|
for i in range(len1):
|
||||||
for j in range(len2):
|
for j in range(len2):
|
||||||
@@ -1349,7 +1401,7 @@ def aligned_faces(vert1: polyverts, len1: int, vert2: polyverts, len2: int):
|
|||||||
# find two normals that are perpendicular to each other within a tolerance
|
# find two normals that are perpendicular to each other within a tolerance
|
||||||
# return 1 if found
|
# return 1 if found
|
||||||
@wp.func
|
@wp.func
|
||||||
def aligned_face_edge(edge: polyverts, nedge: int, face: polyverts, nface: int):
|
def _aligned_face_edge(edge: polyverts, nedge: int, face: polyverts, nface: int):
|
||||||
res = wp.vec2i()
|
res = wp.vec2i()
|
||||||
for i in range(nface):
|
for i in range(nface):
|
||||||
for j in range(nedge):
|
for j in range(nedge):
|
||||||
@@ -1362,7 +1414,7 @@ def aligned_face_edge(edge: polyverts, nedge: int, face: polyverts, nface: int):
|
|||||||
|
|
||||||
# find up to n <= 2 common integers of two arrays, return n
|
# find up to n <= 2 common integers of two arrays, return n
|
||||||
@wp.func
|
@wp.func
|
||||||
def intersect1(a1: wp.array(dtype=int), a2: wp.array(dtype=int), start1: int, start2: int, len1: int, len2: int):
|
def _intersect1(a1: wp.array(dtype=int), a2: wp.array(dtype=int), start1: int, start2: int, len1: int, len2: int):
|
||||||
count = int(0)
|
count = int(0)
|
||||||
res = wp.vec2i()
|
res = wp.vec2i()
|
||||||
for i in range(start1, start1 + len1):
|
for i in range(start1, start1 + len1):
|
||||||
@@ -1376,7 +1428,7 @@ def intersect1(a1: wp.array(dtype=int), a2: wp.array(dtype=int), start1: int, st
|
|||||||
|
|
||||||
|
|
||||||
@wp.func
|
@wp.func
|
||||||
def intersect2(a1: wp.vec2i, a2: wp.array(dtype=int), start2: int, len1: int, len2: int):
|
def _intersect2(a1: wp.vec2i, a2: wp.array(dtype=int), start2: int, len1: int, len2: int):
|
||||||
count = int(0)
|
count = int(0)
|
||||||
res = wp.vec2i()
|
res = wp.vec2i()
|
||||||
for i in range(len1):
|
for i in range(len1):
|
||||||
@@ -1391,7 +1443,7 @@ def intersect2(a1: wp.vec2i, a2: wp.array(dtype=int), start2: int, len1: int, le
|
|||||||
|
|
||||||
# compute possible polygon normals of a mesh given up to 3 vertices
|
# compute possible polygon normals of a mesh given up to 3 vertices
|
||||||
@wp.func
|
@wp.func
|
||||||
def mesh_normals(
|
def _mesh_normals(
|
||||||
# In:
|
# In:
|
||||||
feature_dim: int,
|
feature_dim: int,
|
||||||
feature_index: wp.vec3i,
|
feature_index: wp.vec3i,
|
||||||
@@ -1420,10 +1472,10 @@ def mesh_normals(
|
|||||||
v3_num = polymapnum[vertadr + v3]
|
v3_num = polymapnum[vertadr + v3]
|
||||||
|
|
||||||
faceset = wp.vec2i()
|
faceset = wp.vec2i()
|
||||||
n, edgeset = intersect1(polymap, polymap, v1_adr, v2_adr, v1_num, v2_num)
|
n, edgeset = _intersect1(polymap, polymap, v1_adr, v2_adr, v1_num, v2_num)
|
||||||
if n == 0:
|
if n == 0:
|
||||||
return 0, normals, indices
|
return 0, normals, indices
|
||||||
n, faceset = intersect2(edgeset, polymap, v3_adr, n, v3_num)
|
n, faceset = _intersect2(edgeset, polymap, v3_adr, n, v3_num)
|
||||||
if n == 0:
|
if n == 0:
|
||||||
return 0, normals, indices
|
return 0, normals, indices
|
||||||
|
|
||||||
@@ -1440,7 +1492,7 @@ def mesh_normals(
|
|||||||
v2_num = polymapnum[vertadr + v2]
|
v2_num = polymapnum[vertadr + v2]
|
||||||
|
|
||||||
# up to two faces as two vertices define an edge
|
# up to two faces as two vertices define an edge
|
||||||
n, edgeset = intersect1(polymap, polymap, v1_adr, v2_adr, v1_num, v2_num)
|
n, edgeset = _intersect1(polymap, polymap, v1_adr, v2_adr, v1_num, v2_num)
|
||||||
if n == 0:
|
if n == 0:
|
||||||
return 0, normals, indices
|
return 0, normals, indices
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
@@ -1462,7 +1514,7 @@ def mesh_normals(
|
|||||||
|
|
||||||
# compute normal directional vectors along possible edges given by up to two vertices
|
# compute normal directional vectors along possible edges given by up to two vertices
|
||||||
@wp.func
|
@wp.func
|
||||||
def mesh_edge_normals(
|
def _mesh_edge_normals(
|
||||||
# In:
|
# In:
|
||||||
dim: int,
|
dim: int,
|
||||||
mat: wp.mat33,
|
mat: wp.mat33,
|
||||||
@@ -1511,7 +1563,7 @@ def mesh_edge_normals(
|
|||||||
|
|
||||||
# try recovering box normal from collision normal
|
# try recovering box normal from collision normal
|
||||||
@wp.func
|
@wp.func
|
||||||
def box_normals2(mat: wp.mat33, n: wp.vec3):
|
def _box_normals2(mat: wp.mat33, n: wp.vec3):
|
||||||
normals = polyverts()
|
normals = polyverts()
|
||||||
indices = polyindices()
|
indices = polyindices()
|
||||||
|
|
||||||
@@ -1540,7 +1592,7 @@ def box_normals2(mat: wp.mat33, n: wp.vec3):
|
|||||||
|
|
||||||
# compute possible face normals of a box given up to 3 vertices
|
# compute possible face normals of a box given up to 3 vertices
|
||||||
@wp.func
|
@wp.func
|
||||||
def box_normals(feature_dim: int, feature_index: wp.vec3i, mat: wp.mat33, dir: wp.vec3):
|
def _box_normals(feature_dim: int, feature_index: wp.vec3i, mat: wp.mat33, dir: wp.vec3):
|
||||||
normals = polyverts()
|
normals = polyverts()
|
||||||
indices = polyindices()
|
indices = polyindices()
|
||||||
|
|
||||||
@@ -1568,7 +1620,7 @@ def box_normals(feature_dim: int, feature_index: wp.vec3i, mat: wp.mat33, dir: w
|
|||||||
indices[0] = indices[0] + 1
|
indices[0] = indices[0] + 1
|
||||||
if c == 1:
|
if c == 1:
|
||||||
return 1, normals, indices
|
return 1, normals, indices
|
||||||
return box_normals2(mat, dir)
|
return _box_normals2(mat, dir)
|
||||||
if feature_dim == 2:
|
if feature_dim == 2:
|
||||||
c = 0
|
c = 0
|
||||||
x = float((v1 & 1) and (v2 & 1)) - float(not (v1 & 1) and not (v2 & 1))
|
x = float((v1 & 1) and (v2 & 1)) - float(not (v1 & 1) and not (v2 & 1))
|
||||||
@@ -1588,7 +1640,7 @@ def box_normals(feature_dim: int, feature_index: wp.vec3i, mat: wp.mat33, dir: w
|
|||||||
c += 1
|
c += 1
|
||||||
if c == 2:
|
if c == 2:
|
||||||
return 2, normals, indices
|
return 2, normals, indices
|
||||||
return box_normals2(mat, dir)
|
return _box_normals2(mat, dir)
|
||||||
|
|
||||||
if feature_dim == 1:
|
if feature_dim == 1:
|
||||||
x = wp.where(v1 & 1, 1.0, -1.0)
|
x = wp.where(v1 & 1, 1.0, -1.0)
|
||||||
@@ -1606,7 +1658,7 @@ def box_normals(feature_dim: int, feature_index: wp.vec3i, mat: wp.mat33, dir: w
|
|||||||
|
|
||||||
# compute possible edge normals for box for edge collisions
|
# compute possible edge normals for box for edge collisions
|
||||||
@wp.func
|
@wp.func
|
||||||
def box_edge_normals(dim: int, mat: wp.mat33, pos: wp.vec3, size: wp.vec3, v1: wp.vec3, v2: wp.vec3, v1i: int):
|
def _box_edge_normals(dim: int, mat: wp.mat33, pos: wp.vec3, size: wp.vec3, v1: wp.vec3, v2: wp.vec3, v1i: int):
|
||||||
normals = polyverts()
|
normals = polyverts()
|
||||||
endverts = polyverts()
|
endverts = polyverts()
|
||||||
|
|
||||||
@@ -1635,7 +1687,7 @@ def box_edge_normals(dim: int, mat: wp.mat33, pos: wp.vec3, size: wp.vec3, v1: w
|
|||||||
|
|
||||||
# recover face of a box from its index
|
# recover face of a box from its index
|
||||||
@wp.func
|
@wp.func
|
||||||
def box_face(mat: wp.mat33, pos: wp.vec3, size: wp.vec3, idx: int):
|
def _box_face(mat: wp.mat33, pos: wp.vec3, size: wp.vec3, idx: int):
|
||||||
res = polyverts()
|
res = polyverts()
|
||||||
|
|
||||||
# compute global coordinates of the box face and face normal
|
# compute global coordinates of the box face and face normal
|
||||||
@@ -1680,7 +1732,7 @@ def box_face(mat: wp.mat33, pos: wp.vec3, size: wp.vec3, idx: int):
|
|||||||
|
|
||||||
# recover mesh polygon from its index, return number of edges
|
# recover mesh polygon from its index, return number of edges
|
||||||
@wp.func
|
@wp.func
|
||||||
def mesh_face(
|
def _mesh_face(
|
||||||
# In:
|
# In:
|
||||||
mat: wp.mat33,
|
mat: wp.mat33,
|
||||||
pos: wp.vec3,
|
pos: wp.vec3,
|
||||||
@@ -1706,19 +1758,19 @@ def mesh_face(
|
|||||||
|
|
||||||
|
|
||||||
@wp.func
|
@wp.func
|
||||||
def plane_normal(v1: wp.vec3, v2: wp.vec3, n: wp.vec3):
|
def _plane_normal(v1: wp.vec3, v2: wp.vec3, n: wp.vec3):
|
||||||
v3 = v1 + n
|
v3 = v1 + n
|
||||||
res = wp.cross(v2 - v1, v3 - v1)
|
res = wp.cross(v2 - v1, v3 - v1)
|
||||||
return wp.dot(res, v1), res
|
return wp.dot(res, v1), res
|
||||||
|
|
||||||
|
|
||||||
@wp.func
|
@wp.func
|
||||||
def halfspace(a: wp.vec3, n: wp.vec3, p: wp.vec3):
|
def _halfspace(a: wp.vec3, n: wp.vec3, p: wp.vec3):
|
||||||
return wp.dot(p - a, n) > -1e-10
|
return wp.dot(p - a, n) > -1e-10
|
||||||
|
|
||||||
|
|
||||||
@wp.func
|
@wp.func
|
||||||
def plane_intersect(pn: wp.vec3, pd: float, a: wp.vec3, b: wp.vec3):
|
def _plane_intersect(pn: wp.vec3, pd: float, a: wp.vec3, b: wp.vec3):
|
||||||
res = wp.vec3()
|
res = wp.vec3()
|
||||||
ab = b - a
|
ab = b - a
|
||||||
temp = wp.dot(pn, ab)
|
temp = wp.dot(pn, ab)
|
||||||
@@ -1734,7 +1786,7 @@ def plane_intersect(pn: wp.vec3, pd: float, a: wp.vec3, b: wp.vec3):
|
|||||||
|
|
||||||
# clip a polygon against another polygon
|
# clip a polygon against another polygon
|
||||||
@wp.func
|
@wp.func
|
||||||
def polygon_clip(face1: polyverts, nface1: int, face2: polyverts, nface2: int, n: wp.vec3, dir: wp.vec3):
|
def _polygon_clip(face1: polyverts, nface1: int, face2: polyverts, nface2: int, n: wp.vec3, dir: wp.vec3):
|
||||||
witness1 = mat3c()
|
witness1 = mat3c()
|
||||||
witness2 = mat3c()
|
witness2 = mat3c()
|
||||||
|
|
||||||
@@ -1746,10 +1798,10 @@ def polygon_clip(face1: polyverts, nface1: int, face2: polyverts, nface2: int, n
|
|||||||
pn = polyverts()
|
pn = polyverts()
|
||||||
pd = polyvec()
|
pd = polyvec()
|
||||||
for i in range(nface1 - 1):
|
for i in range(nface1 - 1):
|
||||||
pdi, pni = plane_normal(face1[i], face1[i + 1], n)
|
pdi, pni = _plane_normal(face1[i], face1[i + 1], n)
|
||||||
pd[i] = pdi
|
pd[i] = pdi
|
||||||
pn[i] = pni
|
pn[i] = pni
|
||||||
pdi, pni = plane_normal(face1[nface1 - 1], face1[0], n)
|
pdi, pni = _plane_normal(face1[nface1 - 1], face1[0], n)
|
||||||
pd[nface1 - 1] = pdi
|
pd[nface1 - 1] = pdi
|
||||||
pn[nface1 - 1] = pni
|
pn[nface1 - 1] = pni
|
||||||
|
|
||||||
@@ -1770,8 +1822,8 @@ def polygon_clip(face1: polyverts, nface1: int, face2: polyverts, nface2: int, n
|
|||||||
Q = wp.where(i < npolygon - 1, polygon[i + 1], polygon[0])
|
Q = wp.where(i < npolygon - 1, polygon[i + 1], polygon[0])
|
||||||
|
|
||||||
# determine if P and Q are in the halfspace of the clipping edge
|
# determine if P and Q are in the halfspace of the clipping edge
|
||||||
inside1 = halfspace(face1[e], pn[e], P)
|
inside1 = _halfspace(face1[e], pn[e], P)
|
||||||
inside2 = halfspace(face1[e], pn[e], Q)
|
inside2 = _halfspace(face1[e], pn[e], Q)
|
||||||
|
|
||||||
# PQ entirely outside the clipping edge, skip
|
# PQ entirely outside the clipping edge, skip
|
||||||
if not inside1 and not inside2:
|
if not inside1 and not inside2:
|
||||||
@@ -1784,7 +1836,7 @@ def polygon_clip(face1: polyverts, nface1: int, face2: polyverts, nface2: int, n
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# add new vertex to clipped polygon where PQ intersects the clipping edge
|
# add new vertex to clipped polygon where PQ intersects the clipping edge
|
||||||
t, res = plane_intersect(pn[e], pd[e], P, Q)
|
t, res = _plane_intersect(pn[e], pd[e], P, Q)
|
||||||
if t >= 0.0 and t <= 1.0:
|
if t >= 0.0 and t <= 1.0:
|
||||||
clipped[nclipped] = res
|
clipped[nclipped] = res
|
||||||
nclipped += 1
|
nclipped += 1
|
||||||
@@ -1804,6 +1856,13 @@ def polygon_clip(face1: polyverts, nface1: int, face2: polyverts, nface2: int, n
|
|||||||
if npolygon < 1:
|
if npolygon < 1:
|
||||||
return 0, witness1, witness2
|
return 0, witness1, witness2
|
||||||
|
|
||||||
|
if npolygon > 4:
|
||||||
|
quad = _polygon_quad(polygon, npolygon)
|
||||||
|
for i in range(4):
|
||||||
|
witness2[i] = polygon[quad[i]]
|
||||||
|
witness1[i] = witness2[i] - dir
|
||||||
|
return 4, witness1, witness2
|
||||||
|
|
||||||
# no pruning needed
|
# no pruning needed
|
||||||
for i in range(npolygon):
|
for i in range(npolygon):
|
||||||
witness2[i] = polygon[i]
|
witness2[i] = polygon[i]
|
||||||
@@ -1813,7 +1872,7 @@ def polygon_clip(face1: polyverts, nface1: int, face2: polyverts, nface2: int, n
|
|||||||
|
|
||||||
# recover multiple contacts from EPA polytope
|
# recover multiple contacts from EPA polytope
|
||||||
@wp.func
|
@wp.func
|
||||||
def multicontact(
|
def _multicontact(
|
||||||
pt: Polytope, face: wp.vec3i, x1: wp.vec3, x2: wp.vec3, geom1: Geom, geom2: Geom, geomtype1: int, geomtype2: int
|
pt: Polytope, face: wp.vec3i, x1: wp.vec3, x2: wp.vec3, geom1: Geom, geom2: Geom, geomtype1: int, geomtype2: int
|
||||||
):
|
):
|
||||||
witness1 = mat3c()
|
witness1 = mat3c()
|
||||||
@@ -1845,40 +1904,40 @@ def multicontact(
|
|||||||
polymap = geom2.mesh_polymap
|
polymap = geom2.mesh_polymap
|
||||||
|
|
||||||
# get dimensions of features of geoms 1 and 2
|
# get dimensions of features of geoms 1 and 2
|
||||||
nface1, feature_index1, feature_vertex1 = feature_dim(face, pt.vert_index1, pt.vert1)
|
nface1, feature_index1, feature_vertex1 = _feature_dim(face, pt.vert_index1, pt.vert1)
|
||||||
nface2, feature_index2, feature_vertex2 = feature_dim(face, pt.vert_index2, pt.vert2)
|
nface2, feature_index2, feature_vertex2 = _feature_dim(face, pt.vert_index2, pt.vert2)
|
||||||
|
|
||||||
dir = x2 - x1
|
dir = x2 - x1
|
||||||
dir_neg = -dir
|
dir_neg = -dir
|
||||||
|
|
||||||
# get all possible face normals for each geom
|
# get all possible face normals for each geom
|
||||||
if geomtype1 == int(GeomType.BOX.value):
|
if geomtype1 == int(GeomType.BOX.value):
|
||||||
nnorms1, n1, idx1 = box_normals(nface1, feature_index1, geom1.rot, dir_neg)
|
nnorms1, n1, idx1 = _box_normals(nface1, feature_index1, geom1.rot, dir_neg)
|
||||||
elif geomtype1 == int(GeomType.MESH.value):
|
elif geomtype1 == int(GeomType.MESH.value):
|
||||||
nnorms1, n1, idx1 = mesh_normals(
|
nnorms1, n1, idx1 = _mesh_normals(
|
||||||
nface1, feature_index1, geom1.rot, geom1.vertadr, geom1.mesh_polyadr, polynormal, polymapadr, polymapnum, polymap
|
nface1, feature_index1, geom1.rot, geom1.vertadr, geom1.mesh_polyadr, polynormal, polymapadr, polymapnum, polymap
|
||||||
)
|
)
|
||||||
if geomtype2 == int(GeomType.BOX.value):
|
if geomtype2 == int(GeomType.BOX.value):
|
||||||
nnorms2, n2, idx2 = box_normals(nface2, feature_index2, geom2.rot, dir)
|
nnorms2, n2, idx2 = _box_normals(nface2, feature_index2, geom2.rot, dir)
|
||||||
elif geomtype2 == int(GeomType.MESH.value):
|
elif geomtype2 == int(GeomType.MESH.value):
|
||||||
nnorms2, n2, idx2 = mesh_normals(
|
nnorms2, n2, idx2 = _mesh_normals(
|
||||||
nface2, feature_index2, geom2.rot, geom2.vertadr, geom2.mesh_polyadr, polynormal, polymapadr, polymapnum, polymap
|
nface2, feature_index2, geom2.rot, geom2.vertadr, geom2.mesh_polyadr, polynormal, polymapadr, polymapnum, polymap
|
||||||
)
|
)
|
||||||
|
|
||||||
# determine if any two face normals match
|
# determine if any two face normals match
|
||||||
is_edge_contact_geom1 = 0
|
is_edge_contact_geom1 = 0
|
||||||
is_edge_contact_geom2 = 0
|
is_edge_contact_geom2 = 0
|
||||||
nres, res = aligned_faces(n1, nnorms1, n2, nnorms2)
|
nres, res = _aligned_faces(n1, nnorms1, n2, nnorms2)
|
||||||
if not nres:
|
if not nres:
|
||||||
# check if edge-face collision
|
# check if edge-face collision
|
||||||
if nface1 < 3 and nface1 <= nface2:
|
if nface1 < 3 and nface1 <= nface2:
|
||||||
nnorms1 = 0
|
nnorms1 = 0
|
||||||
if geomtype1 == int(GeomType.BOX.value):
|
if geomtype1 == int(GeomType.BOX.value):
|
||||||
nnorms1, n1, endverts = box_edge_normals(
|
nnorms1, n1, endverts = _box_edge_normals(
|
||||||
nface1, geom1.rot, geom1.pos, geom1.size, feature_vertex1[0], feature_vertex1[1], feature_index1[0]
|
nface1, geom1.rot, geom1.pos, geom1.size, feature_vertex1[0], feature_vertex1[1], feature_index1[0]
|
||||||
)
|
)
|
||||||
elif geomtype1 == int(GeomType.MESH.value):
|
elif geomtype1 == int(GeomType.MESH.value):
|
||||||
nnorms1, n1, endverts = mesh_edge_normals(
|
nnorms1, n1, endverts = _mesh_edge_normals(
|
||||||
nface1,
|
nface1,
|
||||||
geom1.rot,
|
geom1.rot,
|
||||||
geom1.pos,
|
geom1.pos,
|
||||||
@@ -1895,7 +1954,7 @@ def multicontact(
|
|||||||
feature_vertex1[1],
|
feature_vertex1[1],
|
||||||
feature_index1[0],
|
feature_index1[0],
|
||||||
)
|
)
|
||||||
nres, res = aligned_face_edge(n1, nnorms1, n2, nnorms2)
|
nres, res = _aligned_face_edge(n1, nnorms1, n2, nnorms2)
|
||||||
if not nres:
|
if not nres:
|
||||||
return 1, witness1, witness2
|
return 1, witness1, witness2
|
||||||
is_edge_contact_geom1 = 1
|
is_edge_contact_geom1 = 1
|
||||||
@@ -1904,11 +1963,11 @@ def multicontact(
|
|||||||
elif nface2 < 3:
|
elif nface2 < 3:
|
||||||
nnorms2 = 0
|
nnorms2 = 0
|
||||||
if geomtype2 == int(GeomType.BOX.value):
|
if geomtype2 == int(GeomType.BOX.value):
|
||||||
nnorms2, n2, endverts = box_edge_normals(
|
nnorms2, n2, endverts = _box_edge_normals(
|
||||||
nface2, geom2.rot, geom2.pos, geom2.size, feature_vertex2[0], feature_vertex2[1], feature_index2[0]
|
nface2, geom2.rot, geom2.pos, geom2.size, feature_vertex2[0], feature_vertex2[1], feature_index2[0]
|
||||||
)
|
)
|
||||||
elif geomtype2 == int(GeomType.MESH.value):
|
elif geomtype2 == int(GeomType.MESH.value):
|
||||||
nnorms2, n2, endverts = mesh_edge_normals(
|
nnorms2, n2, endverts = _mesh_edge_normals(
|
||||||
nface2,
|
nface2,
|
||||||
geom2.rot,
|
geom2.rot,
|
||||||
geom2.pos,
|
geom2.pos,
|
||||||
@@ -1925,7 +1984,7 @@ def multicontact(
|
|||||||
feature_vertex2[1],
|
feature_vertex2[1],
|
||||||
feature_index2[0],
|
feature_index2[0],
|
||||||
)
|
)
|
||||||
nres, res = aligned_face_edge(n2, nnorms2, n1, nnorms1)
|
nres, res = _aligned_face_edge(n2, nnorms2, n1, nnorms1)
|
||||||
if not nres:
|
if not nres:
|
||||||
return 1, witness1, witness2
|
return 1, witness1, witness2
|
||||||
is_edge_contact_geom2 = 1
|
is_edge_contact_geom2 = 1
|
||||||
@@ -1944,9 +2003,9 @@ def multicontact(
|
|||||||
else:
|
else:
|
||||||
ind = wp.where(is_edge_contact_geom2, idx1[j], idx1[i])
|
ind = wp.where(is_edge_contact_geom2, idx1[j], idx1[i])
|
||||||
if geomtype1 == int(GeomType.BOX.value):
|
if geomtype1 == int(GeomType.BOX.value):
|
||||||
nface1, face1 = box_face(geom1.rot, geom1.pos, geom1.size, ind)
|
nface1, face1 = _box_face(geom1.rot, geom1.pos, geom1.size, ind)
|
||||||
elif geomtype1 == int(GeomType.MESH.value):
|
elif geomtype1 == int(GeomType.MESH.value):
|
||||||
nface1, face1 = mesh_face(
|
nface1, face1 = _mesh_face(
|
||||||
geom1.rot, geom1.pos, geom1.vertadr, geom1.mesh_polyadr, vert, polyvertadr, polyvertnum, polyvert, ind
|
geom1.rot, geom1.pos, geom1.vertadr, geom1.mesh_polyadr, vert, polyvertadr, polyvertnum, polyvert, ind
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1957,9 +2016,9 @@ def multicontact(
|
|||||||
nface2 = 2
|
nface2 = 2
|
||||||
else:
|
else:
|
||||||
if geomtype2 == int(GeomType.BOX.value):
|
if geomtype2 == int(GeomType.BOX.value):
|
||||||
nface2, face2 = box_face(geom2.rot, geom2.pos, geom2.size, idx2[j])
|
nface2, face2 = _box_face(geom2.rot, geom2.pos, geom2.size, idx2[j])
|
||||||
elif geomtype2 == int(GeomType.MESH.value):
|
elif geomtype2 == int(GeomType.MESH.value):
|
||||||
nface2, face2 = mesh_face(
|
nface2, face2 = _mesh_face(
|
||||||
geom2.rot, geom2.pos, geom2.vertadr, geom2.mesh_polyadr, vert, polyvertadr, polyvertnum, polyvert, idx2[j]
|
geom2.rot, geom2.pos, geom2.vertadr, geom2.mesh_polyadr, vert, polyvertadr, polyvertnum, polyvert, idx2[j]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1971,20 +2030,20 @@ def multicontact(
|
|||||||
# face1 is an edge; clip face1 against face2
|
# face1 is an edge; clip face1 against face2
|
||||||
if is_edge_contact_geom1:
|
if is_edge_contact_geom1:
|
||||||
approx_dir = wp.norm_l2(dir) * n2[j]
|
approx_dir = wp.norm_l2(dir) * n2[j]
|
||||||
return polygon_clip(face2, nface2, face1, nface1, n2[j], approx_dir)
|
return _polygon_clip(face2, nface2, face1, nface1, n2[j], approx_dir)
|
||||||
|
|
||||||
# face2 is an edge; clip face2 against face1
|
# face2 is an edge; clip face2 against face1
|
||||||
if is_edge_contact_geom2:
|
if is_edge_contact_geom2:
|
||||||
approx_dir = -wp.norm_l2(dir) * n1[j]
|
approx_dir = -wp.norm_l2(dir) * n1[j]
|
||||||
return polygon_clip(face1, nface1, face2, nface2, n1[j], approx_dir)
|
return _polygon_clip(face1, nface1, face2, nface2, n1[j], approx_dir)
|
||||||
|
|
||||||
# face-face collision
|
# face-face collision
|
||||||
approx_dir = wp.norm_l2(dir) * n2[j]
|
approx_dir = wp.norm_l2(dir) * n2[j]
|
||||||
return polygon_clip(face1, nface1, face2, nface2, n1[i], approx_dir)
|
return _polygon_clip(face1, nface1, face2, nface2, n1[i], approx_dir)
|
||||||
|
|
||||||
|
|
||||||
@wp.func
|
@wp.func
|
||||||
def inflate(dist: float, x1: wp.vec3, x2: wp.vec3, margin1: float, margin2: float):
|
def _inflate(dist: float, x1: wp.vec3, x2: wp.vec3, margin1: float, margin2: float):
|
||||||
n = wp.normalize(x2 - x1)
|
n = wp.normalize(x2 - x1)
|
||||||
if margin1 > 0.0:
|
if margin1 > 0.0:
|
||||||
x1 += margin1 * n
|
x1 += margin1 * n
|
||||||
@@ -2021,9 +2080,9 @@ def ccd(
|
|||||||
face_map: wp.array(dtype=int),
|
face_map: wp.array(dtype=int),
|
||||||
horizon: wp.array(dtype=int),
|
horizon: wp.array(dtype=int),
|
||||||
):
|
):
|
||||||
|
"""General convex collision detection via GJK/EPA."""
|
||||||
witness1 = mat3c()
|
witness1 = mat3c()
|
||||||
witness2 = mat3c()
|
witness2 = mat3c()
|
||||||
"""General convex collision detection via GJK/EPA."""
|
|
||||||
margin1 = 0.0
|
margin1 = 0.0
|
||||||
margin2 = 0.0
|
margin2 = 0.0
|
||||||
|
|
||||||
@@ -2044,7 +2103,7 @@ def ccd(
|
|||||||
witness1[0] = result.x1
|
witness1[0] = result.x1
|
||||||
witness2[0] = result.x2
|
witness2[0] = result.x2
|
||||||
return result.dist, 1, witness1, witness2
|
return result.dist, 1, witness1, witness2
|
||||||
dist, x1, x2 = inflate(result.dist, result.x1, result.x2, margin1, margin2)
|
dist, x1, x2 = _inflate(result.dist, result.x1, result.x2, margin1, margin2)
|
||||||
witness1[0] = x1
|
witness1[0] = x1
|
||||||
witness2[0] = x2
|
witness2[0] = x2
|
||||||
return dist, 1, witness1, witness2
|
return dist, 1, witness1, witness2
|
||||||
@@ -2151,7 +2210,7 @@ def ccd(
|
|||||||
and (geomtype1 == int(GeomType.BOX.value) or (geomtype1 == int(GeomType.MESH.value) and geom1.mesh_polyadr > -1))
|
and (geomtype1 == int(GeomType.BOX.value) or (geomtype1 == int(GeomType.MESH.value) and geom1.mesh_polyadr > -1))
|
||||||
and (geomtype2 == int(GeomType.BOX.value) or (geomtype2 == int(GeomType.MESH.value) and geom2.mesh_polyadr > -1))
|
and (geomtype2 == int(GeomType.BOX.value) or (geomtype2 == int(GeomType.MESH.value) and geom2.mesh_polyadr > -1))
|
||||||
):
|
):
|
||||||
num, w1, w2 = multicontact(pt, pt.face[idx], x1, x2, geom1, geom2, geomtype1, geomtype2)
|
num, w1, w2 = _multicontact(pt, pt.face[idx], x1, x2, geom1, geom2, geomtype1, geomtype2)
|
||||||
if num > 0:
|
if num > 0:
|
||||||
return dist, num, w1, w2
|
return dist, num, w1, w2
|
||||||
witness1[0] = x1
|
witness1[0] = x1
|
||||||
|
|||||||
@@ -424,7 +424,7 @@ class GJKTest(absltest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
_, ncon, _, _ = _geom_dist(m, d, 0, 1, MAX_ITERATIONS, multiccd=True)
|
_, ncon, _, _ = _geom_dist(m, d, 0, 1, MAX_ITERATIONS, multiccd=True)
|
||||||
self.assertEqual(ncon, 5)
|
self.assertEqual(ncon, 4)
|
||||||
|
|
||||||
def test_box_box_ccd2(self):
|
def test_box_box_ccd2(self):
|
||||||
"""Test box-box multiccd 2."""
|
"""Test box-box multiccd 2."""
|
||||||
@@ -441,7 +441,7 @@ class GJKTest(absltest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
_, ncon, _, _ = _geom_dist(m, d, 0, 1, MAX_ITERATIONS, multiccd=True)
|
_, ncon, _, _ = _geom_dist(m, d, 0, 1, MAX_ITERATIONS, multiccd=True)
|
||||||
self.assertEqual(ncon, 5)
|
self.assertEqual(ncon, 4)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+1
-18
@@ -129,18 +129,6 @@ def _qderiv_tendon_damping(
|
|||||||
qM_integration_out[worldid, dofjid, dofiid] -= qderiv
|
qM_integration_out[worldid, dofjid, dofiid] -= qderiv
|
||||||
|
|
||||||
|
|
||||||
@wp.kernel
|
|
||||||
def _qfrc_forward(
|
|
||||||
# Data in:
|
|
||||||
qfrc_smooth_in: wp.array2d(dtype=float),
|
|
||||||
qfrc_constraint_in: wp.array2d(dtype=float),
|
|
||||||
# Data out:
|
|
||||||
qfrc_integration_out: wp.array2d(dtype=float),
|
|
||||||
):
|
|
||||||
worldid, dofid = wp.tid()
|
|
||||||
qfrc_integration_out[worldid, dofid] = qfrc_smooth_in[worldid, dofid] + qfrc_constraint_in[worldid, dofid]
|
|
||||||
|
|
||||||
|
|
||||||
@event_scope
|
@event_scope
|
||||||
def deriv_smooth_vel(m: Model, d: Data, flg_forward: bool = True):
|
def deriv_smooth_vel(m: Model, d: Data, flg_forward: bool = True):
|
||||||
"""Analytical derivative of smooth forces w.r.t. velocities.
|
"""Analytical derivative of smooth forces w.r.t. velocities.
|
||||||
@@ -193,12 +181,7 @@ def deriv_smooth_vel(m: Model, d: Data, flg_forward: bool = True):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if flg_forward:
|
if flg_forward:
|
||||||
wp.launch(
|
wp.copy(d.qfrc_integration, d.efc.Ma)
|
||||||
_qfrc_forward,
|
|
||||||
dim=(d.nworld, m.nv),
|
|
||||||
inputs=[d.qfrc_smooth, d.qfrc_constraint],
|
|
||||||
outputs=[d.qfrc_integration],
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
# qfrc = qM @ qacc
|
# qfrc = qM @ qacc
|
||||||
mul_m(m, d, d.qfrc_integration, d.qacc, d.inverse_mul_m_skip, d.qM_integration)
|
mul_m(m, d, d.qfrc_integration, d.qacc, d.inverse_mul_m_skip, d.qM_integration)
|
||||||
|
|||||||
+6
-19
@@ -319,11 +319,7 @@ def _euler_damp_qfrc_sparse(
|
|||||||
opt_timestep: wp.array(dtype=float),
|
opt_timestep: wp.array(dtype=float),
|
||||||
dof_Madr: wp.array(dtype=int),
|
dof_Madr: wp.array(dtype=int),
|
||||||
dof_damping: wp.array2d(dtype=float),
|
dof_damping: wp.array2d(dtype=float),
|
||||||
# Data in:
|
|
||||||
qfrc_smooth_in: wp.array2d(dtype=float),
|
|
||||||
qfrc_constraint_in: wp.array2d(dtype=float),
|
|
||||||
# Data out:
|
# Data out:
|
||||||
qfrc_integration_out: wp.array2d(dtype=float),
|
|
||||||
qM_integration_out: wp.array3d(dtype=float),
|
qM_integration_out: wp.array3d(dtype=float),
|
||||||
):
|
):
|
||||||
worldid, tid = wp.tid()
|
worldid, tid = wp.tid()
|
||||||
@@ -331,7 +327,6 @@ def _euler_damp_qfrc_sparse(
|
|||||||
|
|
||||||
adr = dof_Madr[tid]
|
adr = dof_Madr[tid]
|
||||||
qM_integration_out[worldid, 0, adr] += timestep * dof_damping[worldid, tid]
|
qM_integration_out[worldid, 0, adr] += timestep * dof_damping[worldid, tid]
|
||||||
qfrc_integration_out[worldid, tid] = qfrc_smooth_in[worldid, tid] + qfrc_constraint_in[worldid, tid]
|
|
||||||
|
|
||||||
|
|
||||||
def _euler_sparse(m: Model, d: Data):
|
def _euler_sparse(m: Model, d: Data):
|
||||||
@@ -343,11 +338,8 @@ def _euler_sparse(m: Model, d: Data):
|
|||||||
m.opt.timestep,
|
m.opt.timestep,
|
||||||
m.dof_Madr,
|
m.dof_Madr,
|
||||||
m.dof_damping,
|
m.dof_damping,
|
||||||
d.qfrc_smooth,
|
|
||||||
d.qfrc_constraint,
|
|
||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
d.qfrc_integration,
|
|
||||||
d.qM_integration,
|
d.qM_integration,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -358,7 +350,7 @@ def _euler_sparse(m: Model, d: Data):
|
|||||||
d.qLD_integration,
|
d.qLD_integration,
|
||||||
d.qLDiagInv_integration,
|
d.qLDiagInv_integration,
|
||||||
d.qacc_integration,
|
d.qacc_integration,
|
||||||
d.qfrc_integration,
|
d.efc.Ma,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -371,8 +363,7 @@ def _tile_euler_dense(tile: TileSet):
|
|||||||
opt_timestep: wp.array(dtype=float),
|
opt_timestep: wp.array(dtype=float),
|
||||||
# Data in:
|
# Data in:
|
||||||
qM_in: wp.array3d(dtype=float),
|
qM_in: wp.array3d(dtype=float),
|
||||||
qfrc_smooth_in: wp.array2d(dtype=float),
|
efc_Ma_in: wp.array2d(dtype=float),
|
||||||
qfrc_constraint_in: wp.array2d(dtype=float),
|
|
||||||
# In:
|
# In:
|
||||||
adr_in: wp.array(dtype=int),
|
adr_in: wp.array(dtype=int),
|
||||||
# Data out:
|
# Data out:
|
||||||
@@ -388,14 +379,10 @@ def _tile_euler_dense(tile: TileSet):
|
|||||||
damping_scaled = damping_tile * timestep
|
damping_scaled = damping_tile * timestep
|
||||||
qm_integration_tile = wp.tile_diag_add(M_tile, damping_scaled)
|
qm_integration_tile = wp.tile_diag_add(M_tile, damping_scaled)
|
||||||
|
|
||||||
qfrc_smooth_tile = wp.tile_load(qfrc_smooth_in[worldid], shape=(TILE_SIZE,), offset=(dofid,))
|
Ma_tile = wp.tile_load(efc_Ma_in[worldid], shape=(TILE_SIZE,), offset=(dofid,))
|
||||||
qfrc_constraint_tile = wp.tile_load(qfrc_constraint_in[worldid], shape=(TILE_SIZE,), offset=(dofid,))
|
|
||||||
|
|
||||||
qfrc_tile = qfrc_smooth_tile + qfrc_constraint_tile
|
|
||||||
|
|
||||||
L_tile = wp.tile_cholesky(qm_integration_tile)
|
L_tile = wp.tile_cholesky(qm_integration_tile)
|
||||||
qacc_tile = wp.tile_cholesky_solve(L_tile, qfrc_tile)
|
qacc_integration_tile = wp.tile_cholesky_solve(L_tile, Ma_tile)
|
||||||
wp.tile_store(qacc_integration_out[worldid], qacc_tile, offset=(dofid))
|
wp.tile_store(qacc_integration_out[worldid], qacc_integration_tile, offset=(dofid))
|
||||||
|
|
||||||
return euler_dense
|
return euler_dense
|
||||||
|
|
||||||
@@ -413,7 +400,7 @@ def euler(m: Model, d: Data):
|
|||||||
wp.launch_tiled(
|
wp.launch_tiled(
|
||||||
_tile_euler_dense(tile),
|
_tile_euler_dense(tile),
|
||||||
dim=(d.nworld, tile.adr.size),
|
dim=(d.nworld, tile.adr.size),
|
||||||
inputs=[m.dof_damping, m.opt.timestep, d.qM, d.qfrc_smooth, d.qfrc_constraint, tile.adr],
|
inputs=[m.dof_damping, m.opt.timestep, d.qM, d.efc.Ma, tile.adr],
|
||||||
outputs=[d.qacc_integration],
|
outputs=[d.qacc_integration],
|
||||||
block_dim=m.block_dim.euler_dense,
|
block_dim=m.block_dim.euler_dense,
|
||||||
)
|
)
|
||||||
|
|||||||
+10
-6
@@ -25,7 +25,6 @@ import mujoco_warp as mjwarp
|
|||||||
|
|
||||||
from mujoco.mjx.third_party.mujoco_warp._src import test_util
|
from mujoco.mjx.third_party.mujoco_warp._src import test_util
|
||||||
from mujoco.mjx.third_party.mujoco_warp._src.types import BiasType
|
from mujoco.mjx.third_party.mujoco_warp._src.types import BiasType
|
||||||
from mujoco.mjx.third_party.mujoco_warp._src.types import DisableBit
|
|
||||||
from mujoco.mjx.third_party.mujoco_warp._src.types import GainType
|
from mujoco.mjx.third_party.mujoco_warp._src.types import GainType
|
||||||
from mujoco.mjx.third_party.mujoco_warp._src.types import IntegratorType
|
from mujoco.mjx.third_party.mujoco_warp._src.types import IntegratorType
|
||||||
|
|
||||||
@@ -133,14 +132,13 @@ class ForwardTest(parameterized.TestCase):
|
|||||||
mjm, mjd, _, _ = test_util.fixture("pendula.xml", kick=True, eulerdamp=eulerdamp, sparse=sparse)
|
mjm, mjd, _, _ = test_util.fixture("pendula.xml", kick=True, eulerdamp=eulerdamp, sparse=sparse)
|
||||||
self.assertTrue((mjm.dof_damping > 0).any())
|
self.assertTrue((mjm.dof_damping > 0).any())
|
||||||
|
|
||||||
mjd.qvel[:] = 1.0
|
|
||||||
mjd.qacc[:] = 1.0
|
|
||||||
mujoco.mj_forward(mjm, mjd)
|
|
||||||
|
|
||||||
m = mjwarp.put_model(mjm)
|
m = mjwarp.put_model(mjm)
|
||||||
d = mjwarp.put_data(mjm, mjd)
|
d = mjwarp.put_data(mjm, mjd)
|
||||||
|
mujoco.mj_forward(mjm, mjd)
|
||||||
|
|
||||||
mujoco.mj_Euler(mjm, mjd)
|
mujoco.mj_Euler(mjm, mjd)
|
||||||
|
|
||||||
|
mjwarp.solve(m, d) # compute efc.Ma
|
||||||
mjwarp.euler(m, d)
|
mjwarp.euler(m, d)
|
||||||
|
|
||||||
_assert_eq(d.qpos.numpy()[0], mjd.qpos, "qpos")
|
_assert_eq(d.qpos.numpy()[0], mjd.qpos, "qpos")
|
||||||
@@ -220,9 +218,11 @@ class ForwardTest(parameterized.TestCase):
|
|||||||
m = mjwarp.put_model(mjm)
|
m = mjwarp.put_model(mjm)
|
||||||
d = mjwarp.put_data(mjm, mjd)
|
d = mjwarp.put_data(mjm, mjd)
|
||||||
|
|
||||||
mjwarp.implicit(m, d)
|
|
||||||
mujoco.mj_implicit(mjm, mjd)
|
mujoco.mj_implicit(mjm, mjd)
|
||||||
|
|
||||||
|
mjwarp.solve(m, d) # compute efc.Ma
|
||||||
|
mjwarp.implicit(m, d)
|
||||||
|
|
||||||
_assert_eq(d.qpos.numpy()[0], mjd.qpos, "qpos")
|
_assert_eq(d.qpos.numpy()[0], mjd.qpos, "qpos")
|
||||||
_assert_eq(d.act.numpy()[0], mjd.act, "act")
|
_assert_eq(d.act.numpy()[0], mjd.act, "act")
|
||||||
|
|
||||||
@@ -230,6 +230,8 @@ class ForwardTest(parameterized.TestCase):
|
|||||||
mjm, mjd, m, d = test_util.fixture("actuation/position.xml", keyframe=0, integrator=IntegratorType.IMPLICITFAST, kick=True)
|
mjm, mjd, m, d = test_util.fixture("actuation/position.xml", keyframe=0, integrator=IntegratorType.IMPLICITFAST, kick=True)
|
||||||
|
|
||||||
mujoco.mj_implicit(mjm, mjd)
|
mujoco.mj_implicit(mjm, mjd)
|
||||||
|
|
||||||
|
mjwarp.solve(m, d) # compute efc.Ma
|
||||||
mjwarp.implicit(m, d)
|
mjwarp.implicit(m, d)
|
||||||
|
|
||||||
_assert_eq(d.qpos.numpy()[0], mjd.qpos, "qpos")
|
_assert_eq(d.qpos.numpy()[0], mjd.qpos, "qpos")
|
||||||
@@ -239,6 +241,8 @@ class ForwardTest(parameterized.TestCase):
|
|||||||
mjm, mjd, m, d = test_util.fixture("tendon/damping.xml", keyframe=0, integrator=IntegratorType.IMPLICITFAST, kick=True)
|
mjm, mjd, m, d = test_util.fixture("tendon/damping.xml", keyframe=0, integrator=IntegratorType.IMPLICITFAST, kick=True)
|
||||||
|
|
||||||
mujoco.mj_implicit(mjm, mjd)
|
mujoco.mj_implicit(mjm, mjd)
|
||||||
|
|
||||||
|
mjwarp.solve(m, d) # compute efc.Ma
|
||||||
mjwarp.implicit(m, d)
|
mjwarp.implicit(m, d)
|
||||||
|
|
||||||
_assert_eq(d.qpos.numpy()[0], mjd.qpos, "qpos")
|
_assert_eq(d.qpos.numpy()[0], mjd.qpos, "qpos")
|
||||||
|
|||||||
+3
-2
@@ -129,8 +129,8 @@ def put_model(mjm: mujoco.MjModel) -> types.Model:
|
|||||||
raise NotImplementedError("Contact sensor: only geom1-geom2 matching is implemented.")
|
raise NotImplementedError("Contact sensor: only geom1-geom2 matching is implemented.")
|
||||||
|
|
||||||
# reduction
|
# reduction
|
||||||
if (mjm.sensor_intprm[is_contact_sensor, 1] != 1).any():
|
if (~((mjm.sensor_intprm[is_contact_sensor, 1] == 1) | (mjm.sensor_intprm[is_contact_sensor, 1] == 2))).any():
|
||||||
raise NotImplementedError(f"Contact sensor: only mindist reduction is implemented.")
|
raise NotImplementedError(f"Contact sensor: only mindist and maxforce reduction are implemented.")
|
||||||
|
|
||||||
# TODO(team): remove after _update_gradient for Newton uses tile operations for islands
|
# TODO(team): remove after _update_gradient for Newton uses tile operations for islands
|
||||||
nv_max = 60
|
nv_max = 60
|
||||||
@@ -462,6 +462,7 @@ def put_model(mjm: mujoco.MjModel) -> types.Model:
|
|||||||
impratio=create_nmodel_batched_array(np.array(mjm.opt.impratio), dtype=float, expand_dim=False),
|
impratio=create_nmodel_batched_array(np.array(mjm.opt.impratio), dtype=float, expand_dim=False),
|
||||||
is_sparse=bool(is_sparse),
|
is_sparse=bool(is_sparse),
|
||||||
ls_parallel=False,
|
ls_parallel=False,
|
||||||
|
ls_parallel_min_step=1.0e-6, # TODO(team): determine good default setting
|
||||||
gjk_iterations=MJ_CCD_ITERATIONS,
|
gjk_iterations=MJ_CCD_ITERATIONS,
|
||||||
epa_iterations=MJ_CCD_ITERATIONS,
|
epa_iterations=MJ_CCD_ITERATIONS,
|
||||||
broadphase=int(broadphase),
|
broadphase=int(broadphase),
|
||||||
|
|||||||
@@ -366,7 +366,6 @@ class IOTest(parameterized.TestCase):
|
|||||||
'<contact geom1="plane"/>',
|
'<contact geom1="plane"/>',
|
||||||
'<contact geom2="plane"/>',
|
'<contact geom2="plane"/>',
|
||||||
'<contact site="site"/>',
|
'<contact site="site"/>',
|
||||||
'<contact reduce="maxforce"/>',
|
|
||||||
'<contact reduce="netforce"/>',
|
'<contact reduce="netforce"/>',
|
||||||
'<contact geom1="plane" geom2="sphere"/>',
|
'<contact geom1="plane" geom2="sphere"/>',
|
||||||
)
|
)
|
||||||
|
|||||||
+15
-3
@@ -477,11 +477,23 @@ def _flex_bending(
|
|||||||
if v[3] == -1:
|
if v[3] == -1:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
frc = wp.mat(0.0, shape=(4, 3))
|
||||||
|
if flex_bending[17 * edgeid + 16]:
|
||||||
|
v0 = flexvert_xpos_in[worldid, v[0]]
|
||||||
|
v1 = flexvert_xpos_in[worldid, v[1]]
|
||||||
|
v2 = flexvert_xpos_in[worldid, v[2]]
|
||||||
|
v3 = flexvert_xpos_in[worldid, v[3]]
|
||||||
|
frc[1] = wp.cross(v2 - v0, v3 - v0)
|
||||||
|
frc[2] = wp.cross(v3 - v0, v1 - v0)
|
||||||
|
frc[3] = wp.cross(v1 - v0, v2 - v0)
|
||||||
|
frc[0] = -(frc[1] + frc[2] + frc[3])
|
||||||
|
|
||||||
force = wp.mat(0.0, shape=(nvert, 3))
|
force = wp.mat(0.0, shape=(nvert, 3))
|
||||||
for i in range(nvert):
|
for i in range(nvert):
|
||||||
for j in range(nvert):
|
for x in range(3):
|
||||||
for x in range(3):
|
for j in range(nvert):
|
||||||
force[i, x] -= flex_bending[17*edgeid + 4*i + j] * flexvert_xpos_in[worldid, v[j]][x]
|
force[i, x] -= flex_bending[17 * edgeid + 4 * i + j] * flexvert_xpos_in[worldid, v[j]][x]
|
||||||
|
force[i, x] -= flex_bending[17 * edgeid + 16] * frc[i, x]
|
||||||
|
|
||||||
for i in range(nvert):
|
for i in range(nvert):
|
||||||
bodyid = flex_vertbodyid[flex_vertadr[f] + v[i]]
|
bodyid = flex_vertbodyid[flex_vertadr[f] + v[i]]
|
||||||
|
|||||||
+6
-6
@@ -510,20 +510,20 @@ def _ray_hfield(
|
|||||||
y = safe_div(lpnt[1] + all[i] * lvec[1] + size[1], dy)
|
y = safe_div(lpnt[1] + all[i] * lvec[1] + size[1], dy)
|
||||||
y0 = wp.max(0.0, wp.min(float(nrow - 2), wp.floor(y)))
|
y0 = wp.max(0.0, wp.min(float(nrow - 2), wp.floor(y)))
|
||||||
if i == 1:
|
if i == 1:
|
||||||
z0 = hfield_data[adr + int(wp.round(y0)) * nrow + ncol - 1]
|
z0 = hfield_data[adr + int(wp.round(y0 + 0.0)) * ncol + ncol - 1]
|
||||||
z1 = hfield_data[adr + int(wp.round(y0 + 1.0)) * nrow + ncol - 1]
|
z1 = hfield_data[adr + int(wp.round(y0 + 1.0)) * ncol + ncol - 1]
|
||||||
else:
|
else:
|
||||||
z0 = hfield_data[adr + int(wp.round(y0)) * nrow]
|
z0 = hfield_data[adr + int(wp.round(y0 + 0.0)) * ncol]
|
||||||
z1 = hfield_data[adr + int(wp.round(y0 + 1.0)) * nrow]
|
z1 = hfield_data[adr + int(wp.round(y0 + 1.0)) * ncol]
|
||||||
# side normal to y-axis
|
# side normal to y-axis
|
||||||
else:
|
else:
|
||||||
y = safe_div(lpnt[0] + all[i] * lvec[0] + size[0], dx)
|
y = safe_div(lpnt[0] + all[i] * lvec[0] + size[0], dx)
|
||||||
y0 = wp.max(0.0, wp.min(float(ncol - 2), wp.floor(y)))
|
y0 = wp.max(0.0, wp.min(float(ncol - 2), wp.floor(y)))
|
||||||
if i == 3:
|
if i == 3:
|
||||||
z0 = hfield_data[adr + int(wp.round(y0)) + (nrow - 1) * ncol]
|
z0 = hfield_data[adr + int(wp.round(y0 + 0.0)) + (nrow - 1) * ncol]
|
||||||
z1 = hfield_data[adr + int(wp.round(y0 + 1.0)) + (nrow - 1) * ncol]
|
z1 = hfield_data[adr + int(wp.round(y0 + 1.0)) + (nrow - 1) * ncol]
|
||||||
else:
|
else:
|
||||||
z0 = hfield_data[adr + int(wp.round(y0))]
|
z0 = hfield_data[adr + int(wp.round(y0 + 0.0))]
|
||||||
z1 = hfield_data[adr + int(wp.round(y0 + 1.0))]
|
z1 = hfield_data[adr + int(wp.round(y0 + 1.0))]
|
||||||
|
|
||||||
# check if point is below line segments
|
# check if point is below line segments
|
||||||
|
|||||||
+40
-4
@@ -1986,14 +1986,22 @@ def _sensor_tactile(
|
|||||||
@wp.kernel
|
@wp.kernel
|
||||||
def _contact_match(
|
def _contact_match(
|
||||||
# Model:
|
# Model:
|
||||||
|
opt_cone: int,
|
||||||
sensor_objid: wp.array(dtype=int),
|
sensor_objid: wp.array(dtype=int),
|
||||||
sensor_refid: wp.array(dtype=int),
|
sensor_refid: wp.array(dtype=int),
|
||||||
|
sensor_intprm: wp.array2d(dtype=int),
|
||||||
sensor_contact_adr: wp.array(dtype=int),
|
sensor_contact_adr: wp.array(dtype=int),
|
||||||
# Data in:
|
# Data in:
|
||||||
|
njmax_in: int,
|
||||||
ncon_in: wp.array(dtype=int),
|
ncon_in: wp.array(dtype=int),
|
||||||
contact_dist_in: wp.array(dtype=float),
|
contact_dist_in: wp.array(dtype=float),
|
||||||
|
contact_frame_in: wp.array(dtype=wp.mat33),
|
||||||
|
contact_friction_in: wp.array(dtype=vec5),
|
||||||
|
contact_dim_in: wp.array(dtype=int),
|
||||||
contact_geom_in: wp.array(dtype=wp.vec2i),
|
contact_geom_in: wp.array(dtype=wp.vec2i),
|
||||||
|
contact_efc_address_in: wp.array2d(dtype=int),
|
||||||
contact_worldid_in: wp.array(dtype=int),
|
contact_worldid_in: wp.array(dtype=int),
|
||||||
|
efc_force_in: wp.array2d(dtype=float),
|
||||||
# Data out:
|
# Data out:
|
||||||
sensor_contact_nmatch_out: wp.array2d(dtype=int),
|
sensor_contact_nmatch_out: wp.array2d(dtype=int),
|
||||||
sensor_contact_matchid_out: wp.array3d(dtype=int),
|
sensor_contact_matchid_out: wp.array3d(dtype=int),
|
||||||
@@ -2009,6 +2017,7 @@ def _contact_match(
|
|||||||
# sensor information
|
# sensor information
|
||||||
objid = sensor_objid[sensorid]
|
objid = sensor_objid[sensorid]
|
||||||
refid = sensor_refid[sensorid]
|
refid = sensor_refid[sensorid]
|
||||||
|
reduce = sensor_intprm[sensorid, 1]
|
||||||
|
|
||||||
# contact information
|
# contact information
|
||||||
geom = contact_geom_in[contactid]
|
geom = contact_geom_in[contactid]
|
||||||
@@ -2022,8 +2031,27 @@ def _contact_match(
|
|||||||
contactmatchid = wp.atomic_add(sensor_contact_nmatch_out[worldid], contactsensorid, 1)
|
contactmatchid = wp.atomic_add(sensor_contact_nmatch_out[worldid], contactsensorid, 1)
|
||||||
sensor_contact_matchid_out[worldid, contactsensorid, contactmatchid] = contactid
|
sensor_contact_matchid_out[worldid, contactsensorid, contactmatchid] = contactid
|
||||||
|
|
||||||
# TODO(thowell): alternative criteria
|
if reduce == 1: # mindist
|
||||||
sensor_contact_criteria_out[worldid, contactsensorid, contactmatchid] = contact_dist_in[contactid]
|
sensor_contact_criteria_out[worldid, contactsensorid, contactmatchid] = contact_dist_in[contactid]
|
||||||
|
elif reduce == 2: # maxforce
|
||||||
|
contact_force = support.contact_force_fn(
|
||||||
|
opt_cone,
|
||||||
|
njmax_in,
|
||||||
|
ncon_in,
|
||||||
|
contact_frame_in,
|
||||||
|
contact_friction_in,
|
||||||
|
contact_dim_in,
|
||||||
|
contact_efc_address_in,
|
||||||
|
efc_force_in,
|
||||||
|
worldid,
|
||||||
|
contactid,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
force_magnitude = (
|
||||||
|
contact_force[0] * contact_force[0] + contact_force[1] * contact_force[1] + contact_force[2] * contact_force[2]
|
||||||
|
)
|
||||||
|
sensor_contact_criteria_out[worldid, contactsensorid, contactmatchid] = -force_magnitude
|
||||||
|
# TODO(thowell): netforce
|
||||||
|
|
||||||
# contact direction
|
# contact direction
|
||||||
if geom1geom0:
|
if geom1geom0:
|
||||||
@@ -2155,20 +2183,28 @@ def sensor_acc(m: Model, d: Data):
|
|||||||
if m.sensor_contact_adr.size:
|
if m.sensor_contact_adr.size:
|
||||||
# match criteria
|
# match criteria
|
||||||
d.sensor_contact_nmatch.zero_()
|
d.sensor_contact_nmatch.zero_()
|
||||||
d.sensor_contact_matchid.zero_()
|
d.sensor_contact_matchid.fill_(-1)
|
||||||
d.sensor_contact_criteria.zero_()
|
d.sensor_contact_criteria.fill_(1.0e32)
|
||||||
|
|
||||||
wp.launch(
|
wp.launch(
|
||||||
_contact_match,
|
_contact_match,
|
||||||
dim=(m.sensor_contact_adr.size, d.nconmax),
|
dim=(m.sensor_contact_adr.size, d.nconmax),
|
||||||
inputs=[
|
inputs=[
|
||||||
|
m.opt.cone,
|
||||||
m.sensor_objid,
|
m.sensor_objid,
|
||||||
m.sensor_refid,
|
m.sensor_refid,
|
||||||
|
m.sensor_intprm,
|
||||||
m.sensor_contact_adr,
|
m.sensor_contact_adr,
|
||||||
|
d.njmax,
|
||||||
d.ncon,
|
d.ncon,
|
||||||
d.contact.dist,
|
d.contact.dist,
|
||||||
|
d.contact.frame,
|
||||||
|
d.contact.friction,
|
||||||
|
d.contact.dim,
|
||||||
d.contact.geom,
|
d.contact.geom,
|
||||||
|
d.contact.efc_address,
|
||||||
d.contact.worldid,
|
d.contact.worldid,
|
||||||
|
d.efc.force,
|
||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
d.sensor_contact_nmatch,
|
d.sensor_contact_nmatch,
|
||||||
|
|||||||
+12
-16
@@ -411,7 +411,7 @@ class SensorTest(parameterized.TestCase):
|
|||||||
@parameterized.parameters(
|
@parameterized.parameters(
|
||||||
'type="sphere" size=".1"',
|
'type="sphere" size=".1"',
|
||||||
'type="capsule" size=".1 .1" euler="0 89 89"',
|
'type="capsule" size=".1 .1" euler="0 89 89"',
|
||||||
'type="box" size=".1 .1 .1" euler=".02 .05 .1"',
|
'type="box" size=".1 .11 .12" euler=".02 .05 .1"',
|
||||||
)
|
)
|
||||||
def test_contact_sensor(self, geom):
|
def test_contact_sensor(self, geom):
|
||||||
"""Test contact sensor."""
|
"""Test contact sensor."""
|
||||||
@@ -419,22 +419,18 @@ class SensorTest(parameterized.TestCase):
|
|||||||
contact_sensor = ""
|
contact_sensor = ""
|
||||||
|
|
||||||
# data combinations
|
# data combinations
|
||||||
field = ["found", "force", "torque", "dist", "pos", "normal", "tangent"]
|
datas = ["found", "force dist normal", "torque pos tangent", "found force torque dist pos normal tangent"]
|
||||||
datas = itertools.chain.from_iterable([itertools.combinations(field, i) for i in range(len(field))])
|
|
||||||
datas = list(datas)
|
|
||||||
|
|
||||||
for num in [1, 2, 3, 4, 5]:
|
for geoms in [
|
||||||
for geoms in [
|
'geom1="plane" geom2="geom"',
|
||||||
'geom1="plane" geom2="geom"',
|
'geom1="geom" geom2="plane"',
|
||||||
'geom1="geom" geom2="plane"',
|
'geom1="sphere" geom2="plane"',
|
||||||
'geom1="plane" geom2="sphere"',
|
'geom1="geom" geom2="sphere"',
|
||||||
'geom1="sphere" geom2="plane"',
|
]:
|
||||||
'geom1="geom" geom2="sphere"',
|
for num in [1, 3, 5]:
|
||||||
'geom1="sphere" geom2="geom"',
|
for reduce in ["mindist", "maxforce"]:
|
||||||
]:
|
for data in datas:
|
||||||
for data in datas:
|
contact_sensor += f'<contact {geoms} num="{num}" reduce="{reduce}" data="{data}"/>'
|
||||||
data = " ".join(data)
|
|
||||||
contact_sensor += f'<contact {geoms} num="{num}" reduce="mindist" data="{data}"/>'
|
|
||||||
|
|
||||||
_MJCF = f"""
|
_MJCF = f"""
|
||||||
<mujoco>
|
<mujoco>
|
||||||
|
|||||||
+92
-71
@@ -720,11 +720,18 @@ def _linesearch_iterative(m: types.Model, d: types.Data):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@wp.func
|
||||||
|
def _log_scale(min_value: float, max_value: float, num_values: int, i: int) -> float:
|
||||||
|
step = (wp.log(max_value) - wp.log(min_value)) / wp.max(1.0, float(num_values - 1))
|
||||||
|
return wp.exp(wp.log(min_value) + float(i) * step)
|
||||||
|
|
||||||
|
|
||||||
@wp.kernel
|
@wp.kernel
|
||||||
def linesearch_parallel_fused(
|
def linesearch_parallel_fused(
|
||||||
# Model:
|
# Model:
|
||||||
nlsp: int,
|
nlsp: int,
|
||||||
opt_impratio: wp.array(dtype=float),
|
opt_impratio: wp.array(dtype=float),
|
||||||
|
opt_ls_parallel_min_step: float,
|
||||||
# Data in:
|
# Data in:
|
||||||
njmax_in: int,
|
njmax_in: int,
|
||||||
ncon_in: wp.array(dtype=int),
|
ncon_in: wp.array(dtype=int),
|
||||||
@@ -750,7 +757,7 @@ def linesearch_parallel_fused(
|
|||||||
if efc_done_in[worldid]:
|
if efc_done_in[worldid]:
|
||||||
return
|
return
|
||||||
|
|
||||||
alpha = float(alphaid) / float(nlsp - 1)
|
alpha = _log_scale(opt_ls_parallel_min_step, 1.0, nlsp, alphaid)
|
||||||
|
|
||||||
out = _eval_cost(efc_quad_gauss_in[worldid], alpha)
|
out = _eval_cost(efc_quad_gauss_in[worldid], alpha)
|
||||||
|
|
||||||
@@ -846,6 +853,7 @@ def linesearch_parallel_fused(
|
|||||||
def linesearch_parallel_best_alpha(
|
def linesearch_parallel_best_alpha(
|
||||||
# Model:
|
# Model:
|
||||||
nlsp: int,
|
nlsp: int,
|
||||||
|
opt_ls_parallel_min_step: float,
|
||||||
# Data in:
|
# Data in:
|
||||||
efc_done_in: wp.array(dtype=bool),
|
efc_done_in: wp.array(dtype=bool),
|
||||||
efc_cost_candidate_in: wp.array2d(dtype=float),
|
efc_cost_candidate_in: wp.array2d(dtype=float),
|
||||||
@@ -867,7 +875,7 @@ def linesearch_parallel_best_alpha(
|
|||||||
best_cost = cost
|
best_cost = cost
|
||||||
bestid = i
|
bestid = i
|
||||||
|
|
||||||
efc_alpha_out[worldid] = float(bestid) / float(nlsp - 1)
|
efc_alpha_out[worldid] = _log_scale(opt_ls_parallel_min_step, 1.0, nlsp, bestid)
|
||||||
|
|
||||||
|
|
||||||
def _linesearch_parallel(m: types.Model, d: types.Data):
|
def _linesearch_parallel(m: types.Model, d: types.Data):
|
||||||
@@ -877,6 +885,7 @@ def _linesearch_parallel(m: types.Model, d: types.Data):
|
|||||||
inputs=[
|
inputs=[
|
||||||
m.nlsp,
|
m.nlsp,
|
||||||
m.opt.impratio,
|
m.opt.impratio,
|
||||||
|
m.opt.ls_parallel_min_step,
|
||||||
d.njmax,
|
d.njmax,
|
||||||
d.ncon,
|
d.ncon,
|
||||||
d.ne,
|
d.ne,
|
||||||
@@ -900,7 +909,7 @@ def _linesearch_parallel(m: types.Model, d: types.Data):
|
|||||||
wp.launch(
|
wp.launch(
|
||||||
linesearch_parallel_best_alpha,
|
linesearch_parallel_best_alpha,
|
||||||
dim=(d.nworld),
|
dim=(d.nworld),
|
||||||
inputs=[m.nlsp, d.efc.done, d.efc.cost_candidate],
|
inputs=[m.nlsp, m.opt.ls_parallel_min_step, d.efc.done, d.efc.cost_candidate],
|
||||||
outputs=[d.efc.alpha],
|
outputs=[d.efc.alpha],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1589,26 +1598,6 @@ def update_gradient_grad(
|
|||||||
wp.atomic_add(efc_grad_dot_out, worldid, grad * grad)
|
wp.atomic_add(efc_grad_dot_out, worldid, grad * grad)
|
||||||
|
|
||||||
|
|
||||||
@wp.kernel
|
|
||||||
def update_gradient_zero_h_lower(
|
|
||||||
# Model:
|
|
||||||
dof_tri_row: wp.array(dtype=int),
|
|
||||||
dof_tri_col: wp.array(dtype=int),
|
|
||||||
# Data in:
|
|
||||||
efc_done_in: wp.array(dtype=bool),
|
|
||||||
# Data out:
|
|
||||||
efc_h_out: wp.array3d(dtype=float),
|
|
||||||
):
|
|
||||||
worldid, elementid = wp.tid()
|
|
||||||
|
|
||||||
if efc_done_in[worldid]:
|
|
||||||
return
|
|
||||||
|
|
||||||
rowid = dof_tri_row[elementid]
|
|
||||||
colid = dof_tri_col[elementid]
|
|
||||||
efc_h_out[worldid, rowid, colid] = 0.0
|
|
||||||
|
|
||||||
|
|
||||||
@wp.kernel
|
@wp.kernel
|
||||||
def update_gradient_set_h_qM_lower_sparse(
|
def update_gradient_set_h_qM_lower_sparse(
|
||||||
# Model:
|
# Model:
|
||||||
@@ -1627,32 +1616,11 @@ def update_gradient_set_h_qM_lower_sparse(
|
|||||||
|
|
||||||
i = qM_fullm_i[elementid]
|
i = qM_fullm_i[elementid]
|
||||||
j = qM_fullm_j[elementid]
|
j = qM_fullm_j[elementid]
|
||||||
efc_h_out[worldid, i, j] = qM_in[worldid, 0, elementid]
|
efc_h_out[worldid, i, j] += qM_in[worldid, 0, elementid]
|
||||||
|
|
||||||
|
|
||||||
@wp.kernel
|
@wp.kernel
|
||||||
def update_gradient_copy_lower_triangle(
|
def update_gradient_JTDAJ_sparse(
|
||||||
# Model:
|
|
||||||
dof_tri_row: wp.array(dtype=int),
|
|
||||||
dof_tri_col: wp.array(dtype=int),
|
|
||||||
# Data in:
|
|
||||||
qM_in: wp.array3d(dtype=float),
|
|
||||||
efc_done_in: wp.array(dtype=bool),
|
|
||||||
# Data out:
|
|
||||||
efc_h_out: wp.array3d(dtype=float),
|
|
||||||
):
|
|
||||||
worldid, elementid = wp.tid()
|
|
||||||
|
|
||||||
if efc_done_in[worldid]:
|
|
||||||
return
|
|
||||||
|
|
||||||
rowid = dof_tri_row[elementid]
|
|
||||||
colid = dof_tri_col[elementid]
|
|
||||||
efc_h_out[worldid, rowid, colid] = qM_in[worldid, rowid, colid]
|
|
||||||
|
|
||||||
|
|
||||||
@wp.kernel
|
|
||||||
def update_gradient_JTDAJ(
|
|
||||||
# Data in:
|
# Data in:
|
||||||
njmax_in: int,
|
njmax_in: int,
|
||||||
nefc_in: wp.array(dtype=int),
|
nefc_in: wp.array(dtype=int),
|
||||||
@@ -1673,12 +1641,14 @@ def update_gradient_JTDAJ(
|
|||||||
dofi = (int(sqrt(float(1 + 8 * elementid))) - 1) // 2
|
dofi = (int(sqrt(float(1 + 8 * elementid))) - 1) // 2
|
||||||
dofj = elementid - (dofi * (dofi + 1)) // 2
|
dofj = elementid - (dofi * (dofi + 1)) // 2
|
||||||
|
|
||||||
sum_h = float(0.0)
|
# To optimize the loop, data for the next iteration is prefetched
|
||||||
|
# This allows to parallelize memory load and computation to hide memory latency
|
||||||
|
efc_state = efc_state_in[worldid, 0]
|
||||||
efc_D = efc_D_in[worldid, 0]
|
efc_D = efc_D_in[worldid, 0]
|
||||||
# TODO(team): sparse efc_J
|
# TODO(team): sparse efc_J
|
||||||
efc_Ji = efc_J_in[worldid, 0, dofi]
|
efc_Ji = efc_J_in[worldid, 0, dofi]
|
||||||
efc_Jj = efc_J_in[worldid, 0, dofj]
|
efc_Jj = efc_J_in[worldid, 0, dofj]
|
||||||
efc_state = efc_state_in[worldid, 0]
|
sum_h = float(0.0)
|
||||||
for efcid in range(min(njmax_in, nefc) - 1):
|
for efcid in range(min(njmax_in, nefc) - 1):
|
||||||
if efc_state == int(types.ConstraintState.QUADRATIC.value) and efc_D != 0.0:
|
if efc_state == int(types.ConstraintState.QUADRATIC.value) and efc_D != 0.0:
|
||||||
sum_h += efc_Ji * efc_Jj * efc_D
|
sum_h += efc_Ji * efc_Jj * efc_D
|
||||||
@@ -1689,9 +1659,60 @@ def update_gradient_JTDAJ(
|
|||||||
efc_Jj = efc_J_in[worldid, jj, dofj]
|
efc_Jj = efc_J_in[worldid, jj, dofj]
|
||||||
efc_state = efc_state_in[worldid, jj]
|
efc_state = efc_state_in[worldid, jj]
|
||||||
|
|
||||||
|
# Adding the contribution from the last constraint row
|
||||||
if efc_state == int(types.ConstraintState.QUADRATIC.value) and efc_D != 0.0:
|
if efc_state == int(types.ConstraintState.QUADRATIC.value) and efc_D != 0.0:
|
||||||
sum_h += efc_Ji * efc_Jj * efc_D
|
sum_h += efc_Ji * efc_Jj * efc_D
|
||||||
efc_h_out[worldid, dofi, dofj] += sum_h
|
|
||||||
|
efc_h_out[worldid, dofi, dofj] = sum_h
|
||||||
|
|
||||||
|
|
||||||
|
@wp.kernel
|
||||||
|
def update_gradient_JTDAJ_dense(
|
||||||
|
# Data in:
|
||||||
|
njmax_in: int,
|
||||||
|
nefc_in: wp.array(dtype=int),
|
||||||
|
qM_in: wp.array3d(dtype=float),
|
||||||
|
efc_J_in: wp.array3d(dtype=float),
|
||||||
|
efc_D_in: wp.array2d(dtype=float),
|
||||||
|
efc_state_in: wp.array2d(dtype=int),
|
||||||
|
efc_done_in: wp.array(dtype=bool),
|
||||||
|
# Data out:
|
||||||
|
efc_h_out: wp.array3d(dtype=float),
|
||||||
|
):
|
||||||
|
worldid, elementid = wp.tid()
|
||||||
|
|
||||||
|
if efc_done_in[worldid]:
|
||||||
|
return
|
||||||
|
|
||||||
|
nefc = nefc_in[worldid]
|
||||||
|
|
||||||
|
dofi = (int(sqrt(float(1 + 8 * elementid))) - 1) // 2
|
||||||
|
dofj = elementid - (dofi * (dofi + 1)) // 2
|
||||||
|
|
||||||
|
# To optimize the loop, data for the next iteration is prefetched
|
||||||
|
# This allows to parallelize memory load and computation to hide memory latency
|
||||||
|
efc_state = efc_state_in[worldid, 0]
|
||||||
|
efc_D = efc_D_in[worldid, 0]
|
||||||
|
# TODO(team): sparse efc_J
|
||||||
|
efc_Ji = efc_J_in[worldid, 0, dofi]
|
||||||
|
efc_Jj = efc_J_in[worldid, 0, dofj]
|
||||||
|
sum_h = float(0.0)
|
||||||
|
for efcid in range(min(njmax_in, nefc) - 1):
|
||||||
|
if efc_state == int(types.ConstraintState.QUADRATIC.value) and efc_D != 0.0:
|
||||||
|
sum_h += efc_Ji * efc_Jj * efc_D
|
||||||
|
|
||||||
|
jj = efcid + 1
|
||||||
|
efc_D = efc_D_in[worldid, jj]
|
||||||
|
efc_Ji = efc_J_in[worldid, jj, dofi]
|
||||||
|
efc_Jj = efc_J_in[worldid, jj, dofj]
|
||||||
|
efc_state = efc_state_in[worldid, jj]
|
||||||
|
|
||||||
|
# Adding the contribution from the last constraint row
|
||||||
|
if efc_state == int(types.ConstraintState.QUADRATIC.value) and efc_D != 0.0:
|
||||||
|
sum_h += efc_Ji * efc_Jj * efc_D
|
||||||
|
|
||||||
|
qM = qM_in[worldid, dofi, dofj]
|
||||||
|
efc_h_out[worldid, dofi, dofj] = qM + sum_h
|
||||||
|
|
||||||
|
|
||||||
# TODO(thowell): combine with JTDAJ ?
|
# TODO(thowell): combine with JTDAJ ?
|
||||||
@@ -1904,11 +1925,19 @@ def _update_gradient(m: types.Model, d: types.Data):
|
|||||||
smooth.solve_m(m, d, d.efc.Mgrad, d.efc.grad)
|
smooth.solve_m(m, d, d.efc.Mgrad, d.efc.grad)
|
||||||
elif m.opt.solver == types.SolverType.NEWTON:
|
elif m.opt.solver == types.SolverType.NEWTON:
|
||||||
# h = qM + (efc_J.T * efc_D * active) @ efc_J
|
# h = qM + (efc_J.T * efc_D * active) @ efc_J
|
||||||
|
lower_triangle_dim = int(m.nv * (m.nv + 1) / 2)
|
||||||
if m.opt.is_sparse:
|
if m.opt.is_sparse:
|
||||||
wp.launch(
|
wp.launch(
|
||||||
update_gradient_zero_h_lower,
|
update_gradient_JTDAJ_sparse,
|
||||||
dim=(d.nworld, m.dof_tri_row.size),
|
dim=(d.nworld, lower_triangle_dim),
|
||||||
inputs=[m.dof_tri_row, m.dof_tri_col, d.efc.done],
|
inputs=[
|
||||||
|
d.njmax,
|
||||||
|
d.nefc,
|
||||||
|
d.efc.J,
|
||||||
|
d.efc.D,
|
||||||
|
d.efc.state,
|
||||||
|
d.efc.done,
|
||||||
|
],
|
||||||
outputs=[d.efc.h],
|
outputs=[d.efc.h],
|
||||||
)
|
)
|
||||||
wp.launch(
|
wp.launch(
|
||||||
@@ -1919,28 +1948,20 @@ def _update_gradient(m: types.Model, d: types.Data):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
wp.launch(
|
wp.launch(
|
||||||
update_gradient_copy_lower_triangle,
|
update_gradient_JTDAJ_dense,
|
||||||
dim=(d.nworld, m.dof_tri_row.size),
|
dim=(d.nworld, lower_triangle_dim),
|
||||||
inputs=[m.dof_tri_row, m.dof_tri_col, d.qM, d.efc.done],
|
inputs=[
|
||||||
|
d.njmax,
|
||||||
|
d.nefc,
|
||||||
|
d.qM,
|
||||||
|
d.efc.J,
|
||||||
|
d.efc.D,
|
||||||
|
d.efc.state,
|
||||||
|
d.efc.done,
|
||||||
|
],
|
||||||
outputs=[d.efc.h],
|
outputs=[d.efc.h],
|
||||||
)
|
)
|
||||||
|
|
||||||
lower_triangle_dim = int(m.nv * (m.nv + 1) / 2)
|
|
||||||
# TODO(team): Investigate whether d.efc.h initialization can be merged into this kernel
|
|
||||||
wp.launch(
|
|
||||||
update_gradient_JTDAJ,
|
|
||||||
dim=(d.nworld, lower_triangle_dim),
|
|
||||||
inputs=[
|
|
||||||
d.njmax,
|
|
||||||
d.nefc,
|
|
||||||
d.efc.J,
|
|
||||||
d.efc.D,
|
|
||||||
d.efc.state,
|
|
||||||
d.efc.done,
|
|
||||||
],
|
|
||||||
outputs=[d.efc.h],
|
|
||||||
)
|
|
||||||
|
|
||||||
if m.opt.cone == types.ConeType.ELLIPTIC:
|
if m.opt.cone == types.ConeType.ELLIPTIC:
|
||||||
# Optimization: launching update_gradient_JTCJ with limited number of blocks on a GPU.
|
# Optimization: launching update_gradient_JTCJ with limited number of blocks on a GPU.
|
||||||
# Profiling suggests that only a fraction of blocks out of the original
|
# Profiling suggests that only a fraction of blocks out of the original
|
||||||
|
|||||||
+28
-9
@@ -42,8 +42,8 @@ def _assert_eq(a, b, name):
|
|||||||
|
|
||||||
class SolverTest(parameterized.TestCase):
|
class SolverTest(parameterized.TestCase):
|
||||||
@parameterized.product(cone=tuple(ConeType), solver_=tuple(SolverType))
|
@parameterized.product(cone=tuple(ConeType), solver_=tuple(SolverType))
|
||||||
def test_cost(self, cone, solver_):
|
def test_constraint_update(self, cone, solver_):
|
||||||
"""Tests cost function is correct."""
|
"""Tests _update_constraint function is correct."""
|
||||||
for keyframe in range(3):
|
for keyframe in range(3):
|
||||||
mjm, mjd, m, d = test_util.fixture(
|
mjm, mjd, m, d = test_util.fixture(
|
||||||
"constraints.xml",
|
"constraints.xml",
|
||||||
@@ -60,14 +60,37 @@ class SolverTest(parameterized.TestCase):
|
|||||||
mujoco.mj_constraintUpdate(mjm, mjd, jaref - mjd.efc_aref, cost, 0)
|
mujoco.mj_constraintUpdate(mjm, mjd, jaref - mjd.efc_aref, cost, 0)
|
||||||
return cost
|
return cost
|
||||||
|
|
||||||
mj_cost = cost(mjd.qacc)
|
mjd_cost = cost(mjd.qacc)
|
||||||
|
|
||||||
# solve with 0 iterations just initializes constraints and costs and then exits
|
# solve with 0 iterations just initializes constraints and costs and then exits
|
||||||
|
d.efc.force.zero_()
|
||||||
|
d.qfrc_constraint.zero_()
|
||||||
mjwarp.solve(m, d)
|
mjwarp.solve(m, d)
|
||||||
|
|
||||||
mjwarp_cost = d.efc.cost.numpy()[0] - d.efc.gauss.numpy()[0]
|
# Get the ordering indices based on efc_force, efc_state for MJWarp
|
||||||
|
nefc = d.nefc.numpy()[0]
|
||||||
|
efc_force = d.efc.force.numpy()[0, :nefc]
|
||||||
|
efc_state = d.efc.state.numpy()[0, :nefc]
|
||||||
|
# Get the ordering indices based on efc_force, efc_state for MuJoCo
|
||||||
|
mjd_efc_force = mjd.efc_force[:nefc]
|
||||||
|
mjd_efc_state = mjd.efc_state[:nefc]
|
||||||
|
|
||||||
_assert_eq(mjwarp_cost, mj_cost, name="cost")
|
# Create sorting keys using lexsort (more efficient for multiple keys)
|
||||||
|
d_sort_indices = np.lexsort((efc_force, efc_state))
|
||||||
|
mjd_sort_indices = np.lexsort((mjd_efc_force, mjd_efc_state))
|
||||||
|
|
||||||
|
efc_cost = d.efc.cost.numpy()[0] - d.efc.gauss.numpy()[0]
|
||||||
|
qfrc_constraint = d.qfrc_constraint.numpy()[0]
|
||||||
|
|
||||||
|
efc_sorted_force = efc_force[d_sort_indices]
|
||||||
|
efc_sorted_state = efc_state[d_sort_indices]
|
||||||
|
mjd_sorted_force = mjd_efc_force[mjd_sort_indices]
|
||||||
|
mjd_sorted_state = mjd_efc_state[mjd_sort_indices]
|
||||||
|
|
||||||
|
_assert_eq(efc_sorted_state, mjd_sorted_state, "efc_state")
|
||||||
|
_assert_eq(efc_sorted_force, mjd_sorted_force, "efc_force")
|
||||||
|
_assert_eq(efc_cost, mjd_cost, "cost")
|
||||||
|
_assert_eq(qfrc_constraint, mjd.qfrc_constraint, "qfrc_constraint")
|
||||||
|
|
||||||
def test_init_linesearch(self):
|
def test_init_linesearch(self):
|
||||||
"""Test linesearch initialization."""
|
"""Test linesearch initialization."""
|
||||||
@@ -160,10 +183,6 @@ class SolverTest(parameterized.TestCase):
|
|||||||
def test_parallel_linesearch(self, cone):
|
def test_parallel_linesearch(self, cone):
|
||||||
"""Test that iterative and parallel linesearch leads to equivalent results."""
|
"""Test that iterative and parallel linesearch leads to equivalent results."""
|
||||||
|
|
||||||
# TODO(team): Enable this case when elliptic/parallel linesearch is working
|
|
||||||
if cone == ConeType.ELLIPTIC:
|
|
||||||
return
|
|
||||||
|
|
||||||
_, _, m, d = test_util.fixture(
|
_, _, m, d = test_util.fixture(
|
||||||
"humanoid/humanoid.xml",
|
"humanoid/humanoid.xml",
|
||||||
cone=cone,
|
cone=cone,
|
||||||
|
|||||||
+3
-40
@@ -167,45 +167,6 @@ def mul_m(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@wp.kernel
|
|
||||||
def xfrc_accumulate_kernel(
|
|
||||||
# Model:
|
|
||||||
nbody: int,
|
|
||||||
body_parentid: wp.array(dtype=int),
|
|
||||||
body_rootid: wp.array(dtype=int),
|
|
||||||
dof_bodyid: wp.array(dtype=int),
|
|
||||||
# Data in:
|
|
||||||
xfrc_applied_in: wp.array2d(dtype=wp.spatial_vector),
|
|
||||||
xipos_in: wp.array2d(dtype=wp.vec3),
|
|
||||||
subtree_com_in: wp.array2d(dtype=wp.vec3),
|
|
||||||
cdof_in: wp.array2d(dtype=wp.spatial_vector),
|
|
||||||
# Out:
|
|
||||||
out: wp.array2d(dtype=float),
|
|
||||||
):
|
|
||||||
"""Accumulate applied forces on the subtree of a dof."""
|
|
||||||
worldid, dofid = wp.tid()
|
|
||||||
cdof = cdof_in[worldid, dofid]
|
|
||||||
rotational_cdof = wp.spatial_top(cdof)
|
|
||||||
jac = wp.spatial_vector(cdof[3], cdof[4], cdof[5], cdof[0], cdof[1], cdof[2])
|
|
||||||
|
|
||||||
bodyid = dof_bodyid[dofid]
|
|
||||||
accumul = float(0.0)
|
|
||||||
|
|
||||||
for child in range(bodyid, nbody):
|
|
||||||
# any body that is in the subtree of dof_bodyid is part of the jacobian
|
|
||||||
parentid = child
|
|
||||||
while parentid != 0 and parentid != bodyid:
|
|
||||||
parentid = body_parentid[parentid]
|
|
||||||
if parentid == 0:
|
|
||||||
continue # body is not part of the subtree
|
|
||||||
offset = xipos_in[worldid, child] - subtree_com_in[worldid, body_rootid[child]]
|
|
||||||
cross_term = wp.cross(rotational_cdof, offset)
|
|
||||||
xfrc_applied = xfrc_applied_in[worldid, child]
|
|
||||||
accumul += wp.dot(jac, xfrc_applied) + wp.dot(cross_term, wp.spatial_top(xfrc_applied))
|
|
||||||
|
|
||||||
out[worldid, dofid] += accumul
|
|
||||||
|
|
||||||
|
|
||||||
@wp.kernel
|
@wp.kernel
|
||||||
def _apply_ft(
|
def _apply_ft(
|
||||||
# Model:
|
# Model:
|
||||||
@@ -232,6 +193,9 @@ def _apply_ft(
|
|||||||
accumul = float(0.0)
|
accumul = float(0.0)
|
||||||
|
|
||||||
for bodyid in range(dofbodyid, nbody):
|
for bodyid in range(dofbodyid, nbody):
|
||||||
|
ft_body = ft_in[worldid, bodyid]
|
||||||
|
if ft_body == wp.spatial_vector():
|
||||||
|
continue
|
||||||
# any body that is in the subtree of dofbodyid is part of the jacobian
|
# any body that is in the subtree of dofbodyid is part of the jacobian
|
||||||
parentid = bodyid
|
parentid = bodyid
|
||||||
while parentid != 0 and parentid != dofbodyid:
|
while parentid != 0 and parentid != dofbodyid:
|
||||||
@@ -240,7 +204,6 @@ def _apply_ft(
|
|||||||
continue # body is not part of the subtree
|
continue # body is not part of the subtree
|
||||||
offset = xipos_in[worldid, bodyid] - subtree_com_in[worldid, body_rootid[bodyid]]
|
offset = xipos_in[worldid, bodyid] - subtree_com_in[worldid, body_rootid[bodyid]]
|
||||||
cross_term = wp.cross(rotational_cdof, offset)
|
cross_term = wp.cross(rotational_cdof, offset)
|
||||||
ft_body = ft_in[worldid, bodyid]
|
|
||||||
accumul += wp.dot(jac, ft_body) + wp.dot(cross_term, wp.spatial_top(ft_body))
|
accumul += wp.dot(jac, ft_body) + wp.dot(cross_term, wp.spatial_top(ft_body))
|
||||||
|
|
||||||
if flg_add:
|
if flg_add:
|
||||||
|
|||||||
+15
-13
@@ -196,7 +196,7 @@ def benchmark(
|
|||||||
event_trace: bool = False,
|
event_trace: bool = False,
|
||||||
measure_alloc: bool = False,
|
measure_alloc: bool = False,
|
||||||
measure_solver_niter: bool = False,
|
measure_solver_niter: bool = False,
|
||||||
) -> Tuple[float, float, dict, list, list, list]:
|
) -> Tuple[float, float, dict, list, list, list, int]:
|
||||||
"""Benchmark a function of Model and Data.
|
"""Benchmark a function of Model and Data.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -217,22 +217,20 @@ def benchmark(
|
|||||||
list: Number of contacts.
|
list: Number of contacts.
|
||||||
list: Number of constraints.
|
list: Number of constraints.
|
||||||
list: Number of solver iterations.
|
list: Number of solver iterations.
|
||||||
|
int: Number of converged worlds.
|
||||||
"""
|
"""
|
||||||
jit_beg = time.perf_counter()
|
|
||||||
|
|
||||||
fn(m, d)
|
|
||||||
|
|
||||||
jit_end = time.perf_counter()
|
|
||||||
jit_duration = jit_end - jit_beg
|
|
||||||
wp.synchronize()
|
|
||||||
|
|
||||||
trace = {}
|
trace = {}
|
||||||
ncon, nefc, solver_niter = [], [], []
|
ncon, nefc, solver_niter = [], [], []
|
||||||
|
|
||||||
with warp_util.EventTracer(enabled=event_trace) as tracer:
|
with warp_util.EventTracer(enabled=event_trace) as tracer:
|
||||||
# capture the whole function as a CUDA graph
|
# capture the whole function as a CUDA graph
|
||||||
|
jit_beg = time.perf_counter()
|
||||||
with wp.ScopedCapture() as capture:
|
with wp.ScopedCapture() as capture:
|
||||||
fn(m, d)
|
fn(m, d)
|
||||||
|
jit_end = time.perf_counter()
|
||||||
|
jit_duration = jit_end - jit_beg
|
||||||
|
|
||||||
graph = capture.graph
|
graph = capture.graph
|
||||||
|
|
||||||
time_vec = np.zeros(nstep)
|
time_vec = np.zeros(nstep)
|
||||||
@@ -258,14 +256,15 @@ def benchmark(
|
|||||||
else:
|
else:
|
||||||
trace = tracer.trace()
|
trace = tracer.trace()
|
||||||
if measure_alloc:
|
if measure_alloc:
|
||||||
ncon.append(d.ncon.numpy()[0])
|
ncon.append(np.max([d.ncon.numpy()[0], d.ncollision.numpy()[0]]))
|
||||||
nefc.append(np.sum(d.nefc.numpy()))
|
nefc.append(np.max(d.nefc.numpy()))
|
||||||
if measure_solver_niter:
|
if measure_solver_niter:
|
||||||
solver_niter.append(d.solver_niter.numpy())
|
solver_niter.append(d.solver_niter.numpy())
|
||||||
|
|
||||||
|
nsuccess = np.sum(~np.any(np.isnan(d.qpos.numpy()), axis=1))
|
||||||
run_duration = np.sum(time_vec)
|
run_duration = np.sum(time_vec)
|
||||||
|
|
||||||
return jit_duration, run_duration, trace, ncon, nefc, solver_niter
|
return jit_duration, run_duration, trace, ncon, nefc, solver_niter, nsuccess
|
||||||
|
|
||||||
|
|
||||||
class BenchmarkSuite:
|
class BenchmarkSuite:
|
||||||
@@ -322,17 +321,20 @@ class BenchmarkSuite:
|
|||||||
mujoco.mj_forward(mjm, mjd)
|
mujoco.mj_forward(mjm, mjd)
|
||||||
|
|
||||||
wp.init()
|
wp.init()
|
||||||
|
if os.environ.get("ASV_CACHE_KERNELS", "false").lower() == "false":
|
||||||
|
wp.clear_kernel_cache()
|
||||||
|
|
||||||
free_before = wp.get_device().free_memory
|
free_before = wp.get_device().free_memory
|
||||||
m = io.put_model(mjm)
|
m = io.put_model(mjm)
|
||||||
d = io.put_data(mjm, mjd, self.batch_size, self.nconmax, self.njmax)
|
d = io.put_data(mjm, mjd, self.batch_size, self.nconmax, self.njmax)
|
||||||
|
free_after = wp.get_device().free_memory
|
||||||
|
|
||||||
jit_duration, _, trace, _, _, solver_niter = benchmark(forward.step, m, d, 1000, True, False, True)
|
jit_duration, _, trace, _, _, solver_niter, _ = benchmark(forward.step, m, d, 1000, True, False, True)
|
||||||
metrics = {
|
metrics = {
|
||||||
"jit_duration": jit_duration,
|
"jit_duration": jit_duration,
|
||||||
"solver_niter_mean": np.mean(solver_niter),
|
"solver_niter_mean": np.mean(solver_niter),
|
||||||
"solver_niter_p95": np.quantile(solver_niter, 0.95),
|
"solver_niter_p95": np.quantile(solver_niter, 0.95),
|
||||||
"device_memory_allocated": free_before - wp.get_device().free_memory,
|
"device_memory_allocated": free_before - free_after,
|
||||||
}
|
}
|
||||||
|
|
||||||
def tree_flatten(d, parent_k=""):
|
def tree_flatten(d, parent_k=""):
|
||||||
|
|||||||
+3
-1
@@ -557,6 +557,7 @@ class Option:
|
|||||||
gjk_iterations: number of Gjk iterations in the convex narrowphase
|
gjk_iterations: number of Gjk iterations in the convex narrowphase
|
||||||
epa_iterations: number of Epa iterations in the convex narrowphase
|
epa_iterations: number of Epa iterations in the convex narrowphase
|
||||||
ls_parallel: evaluate engine solver step sizes in parallel
|
ls_parallel: evaluate engine solver step sizes in parallel
|
||||||
|
ls_parallel_min_step: minimum step size for solver linesearch
|
||||||
wind: wind (for lift, drag, and viscosity)
|
wind: wind (for lift, drag, and viscosity)
|
||||||
has_fluid: True if wind, density, or viscosity are non-zero at put_model time
|
has_fluid: True if wind, density, or viscosity are non-zero at put_model time
|
||||||
density: density of medium
|
density: density of medium
|
||||||
@@ -587,7 +588,8 @@ class Option:
|
|||||||
is_sparse: bool
|
is_sparse: bool
|
||||||
gjk_iterations: int # warp only
|
gjk_iterations: int # warp only
|
||||||
epa_iterations: int # warp only
|
epa_iterations: int # warp only
|
||||||
ls_parallel: bool
|
ls_parallel: bool # warp only
|
||||||
|
ls_parallel_min_step: float # warp only
|
||||||
wind: wp.array(dtype=wp.vec3)
|
wind: wp.array(dtype=wp.vec3)
|
||||||
has_fluid: bool
|
has_fluid: bool
|
||||||
density: wp.array(dtype=float)
|
density: wp.array(dtype=float)
|
||||||
|
|||||||
+6
-4
@@ -50,6 +50,7 @@ _ENGINE = flags.DEFINE_enum_class("engine", EngineOptions.WARP, EngineOptions, "
|
|||||||
_NCONMAX = flags.DEFINE_integer("nconmax", None, "Maximum number of contacts.")
|
_NCONMAX = flags.DEFINE_integer("nconmax", None, "Maximum number of contacts.")
|
||||||
_NJMAX = flags.DEFINE_integer("njmax", None, "Maximum number of constraints per world.")
|
_NJMAX = flags.DEFINE_integer("njmax", None, "Maximum number of constraints per world.")
|
||||||
_OVERRIDE = flags.DEFINE_multi_string("override", [], "Model overrides (notation: foo.bar = baz)", short_name="o")
|
_OVERRIDE = flags.DEFINE_multi_string("override", [], "Model overrides (notation: foo.bar = baz)", short_name="o")
|
||||||
|
_KEYFRAME = flags.DEFINE_integer("keyframe", 0, "keyframe to initialize simulation.")
|
||||||
_DEVICE = flags.DEFINE_string("device", None, "override the default Warp device")
|
_DEVICE = flags.DEFINE_string("device", None, "override the default Warp device")
|
||||||
|
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ def _override(model: Union[mjw.Model, mujoco.MjModel]):
|
|||||||
|
|
||||||
if key in enum_fields:
|
if key in enum_fields:
|
||||||
try:
|
try:
|
||||||
val = str(enum_fields[key][val.upper()])
|
val = int(enum_fields[key][val.upper()])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise app.UsageError(f"Unrecognized enum value: {val}")
|
raise app.UsageError(f"Unrecognized enum value: {val}")
|
||||||
|
|
||||||
@@ -110,13 +111,12 @@ def _override(model: Union[mjw.Model, mujoco.MjModel]):
|
|||||||
raise app.UsageError(f"Unrecognized model field: {key}")
|
raise app.UsageError(f"Unrecognized model field: {key}")
|
||||||
if i < len(attrs) - 1:
|
if i < len(attrs) - 1:
|
||||||
obj = getattr(obj, attr)
|
obj = getattr(obj, attr)
|
||||||
else:
|
elif key not in enum_fields:
|
||||||
try:
|
try:
|
||||||
val = type(getattr(obj, attr))(ast.literal_eval(val))
|
val = type(getattr(obj, attr))(ast.literal_eval(val))
|
||||||
except (SyntaxError, ValueError):
|
except (SyntaxError, ValueError):
|
||||||
raise app.UsageError(f"Unrecognized value for field: {key}")
|
raise app.UsageError(f"Unrecognized value for field: {key}")
|
||||||
|
setattr(obj, attr, val)
|
||||||
setattr(obj, attr, val)
|
|
||||||
|
|
||||||
|
|
||||||
def _compile_step(m, d):
|
def _compile_step(m, d):
|
||||||
@@ -138,6 +138,8 @@ def _main(argv: Sequence[str]) -> None:
|
|||||||
|
|
||||||
mjm = _load_model(epath.Path(argv[1]))
|
mjm = _load_model(epath.Path(argv[1]))
|
||||||
mjd = mujoco.MjData(mjm)
|
mjd = mujoco.MjData(mjm)
|
||||||
|
if mjm.nkey > 0 and _KEYFRAME.value > -1:
|
||||||
|
mujoco.mj_resetDataKeyframe(mjm, mjd, _KEYFRAME.value)
|
||||||
mujoco.mj_forward(mjm, mjd)
|
mujoco.mj_forward(mjm, mjd)
|
||||||
|
|
||||||
if _ENGINE.value == EngineOptions.C:
|
if _ENGINE.value == EngineOptions.C:
|
||||||
|
|||||||
@@ -333,6 +333,7 @@ def _forward_shim(
|
|||||||
opt__iterations: int,
|
opt__iterations: int,
|
||||||
opt__ls_iterations: int,
|
opt__ls_iterations: int,
|
||||||
opt__ls_parallel: bool,
|
opt__ls_parallel: bool,
|
||||||
|
opt__ls_parallel_min_step: float,
|
||||||
opt__ls_tolerance: wp.array(dtype=float),
|
opt__ls_tolerance: wp.array(dtype=float),
|
||||||
opt__magnetic: wp.array(dtype=wp.vec3),
|
opt__magnetic: wp.array(dtype=wp.vec3),
|
||||||
opt__run_collision_detection: bool,
|
opt__run_collision_detection: bool,
|
||||||
@@ -734,6 +735,7 @@ def _forward_shim(
|
|||||||
_m.opt.iterations = opt__iterations
|
_m.opt.iterations = opt__iterations
|
||||||
_m.opt.ls_iterations = opt__ls_iterations
|
_m.opt.ls_iterations = opt__ls_iterations
|
||||||
_m.opt.ls_parallel = opt__ls_parallel
|
_m.opt.ls_parallel = opt__ls_parallel
|
||||||
|
_m.opt.ls_parallel_min_step = opt__ls_parallel_min_step
|
||||||
_m.opt.ls_tolerance = opt__ls_tolerance
|
_m.opt.ls_tolerance = opt__ls_tolerance
|
||||||
_m.opt.magnetic = opt__magnetic
|
_m.opt.magnetic = opt__magnetic
|
||||||
_m.opt.run_collision_detection = opt__run_collision_detection
|
_m.opt.run_collision_detection = opt__run_collision_detection
|
||||||
@@ -1662,6 +1664,7 @@ def _forward_jax_impl(m: types.Model, d: types.Data):
|
|||||||
m.opt.iterations,
|
m.opt.iterations,
|
||||||
m.opt.ls_iterations,
|
m.opt.ls_iterations,
|
||||||
m.opt._impl.ls_parallel,
|
m.opt._impl.ls_parallel,
|
||||||
|
m.opt._impl.ls_parallel_min_step,
|
||||||
m.opt.ls_tolerance,
|
m.opt.ls_tolerance,
|
||||||
m.opt.magnetic,
|
m.opt.magnetic,
|
||||||
m.opt._impl.run_collision_detection,
|
m.opt._impl.run_collision_detection,
|
||||||
@@ -2358,6 +2361,7 @@ def _step_shim(
|
|||||||
opt__iterations: int,
|
opt__iterations: int,
|
||||||
opt__ls_iterations: int,
|
opt__ls_iterations: int,
|
||||||
opt__ls_parallel: bool,
|
opt__ls_parallel: bool,
|
||||||
|
opt__ls_parallel_min_step: float,
|
||||||
opt__ls_tolerance: wp.array(dtype=float),
|
opt__ls_tolerance: wp.array(dtype=float),
|
||||||
opt__magnetic: wp.array(dtype=wp.vec3),
|
opt__magnetic: wp.array(dtype=wp.vec3),
|
||||||
opt__run_collision_detection: bool,
|
opt__run_collision_detection: bool,
|
||||||
@@ -2773,6 +2777,7 @@ def _step_shim(
|
|||||||
_m.opt.iterations = opt__iterations
|
_m.opt.iterations = opt__iterations
|
||||||
_m.opt.ls_iterations = opt__ls_iterations
|
_m.opt.ls_iterations = opt__ls_iterations
|
||||||
_m.opt.ls_parallel = opt__ls_parallel
|
_m.opt.ls_parallel = opt__ls_parallel
|
||||||
|
_m.opt.ls_parallel_min_step = opt__ls_parallel_min_step
|
||||||
_m.opt.ls_tolerance = opt__ls_tolerance
|
_m.opt.ls_tolerance = opt__ls_tolerance
|
||||||
_m.opt.magnetic = opt__magnetic
|
_m.opt.magnetic = opt__magnetic
|
||||||
_m.opt.run_collision_detection = opt__run_collision_detection
|
_m.opt.run_collision_detection = opt__run_collision_detection
|
||||||
@@ -3739,6 +3744,7 @@ def _step_jax_impl(m: types.Model, d: types.Data):
|
|||||||
m.opt.iterations,
|
m.opt.iterations,
|
||||||
m.opt.ls_iterations,
|
m.opt.ls_iterations,
|
||||||
m.opt._impl.ls_parallel,
|
m.opt._impl.ls_parallel,
|
||||||
|
m.opt._impl.ls_parallel_min_step,
|
||||||
m.opt.ls_tolerance,
|
m.opt.ls_tolerance,
|
||||||
m.opt.magnetic,
|
m.opt.magnetic,
|
||||||
m.opt._impl.run_collision_detection,
|
m.opt._impl.run_collision_detection,
|
||||||
|
|||||||
@@ -306,7 +306,6 @@ _e = mjwarp.Constraint(
|
|||||||
**{f.name: None for f in dataclasses.fields(mjwarp.Constraint) if f.init}
|
**{f.name: None for f in dataclasses.fields(mjwarp.Constraint) if f.init}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ffi.format_args_for_warp
|
@ffi.format_args_for_warp
|
||||||
def _tendon_shim(
|
def _tendon_shim(
|
||||||
# Model
|
# Model
|
||||||
@@ -488,8 +487,6 @@ def _tendon_jax_impl(m: types.Model, d: types.Data):
|
|||||||
@ffi.marshal_jax_warp_callable
|
@ffi.marshal_jax_warp_callable
|
||||||
def tendon(m: types.Model, d: types.Data):
|
def tendon(m: types.Model, d: types.Data):
|
||||||
return _tendon_jax_impl(m, d)
|
return _tendon_jax_impl(m, d)
|
||||||
|
|
||||||
|
|
||||||
@tendon.def_vmap
|
@tendon.def_vmap
|
||||||
@ffi.marshal_custom_vmap
|
@ffi.marshal_custom_vmap
|
||||||
def tendon_vmap(unused_axis_size, is_batched, m, d):
|
def tendon_vmap(unused_axis_size, is_batched, m, d):
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ class OptionWarp(PyTreeNode):
|
|||||||
has_fluid: bool
|
has_fluid: bool
|
||||||
is_sparse: bool
|
is_sparse: bool
|
||||||
ls_parallel: bool
|
ls_parallel: bool
|
||||||
|
ls_parallel_min_step: float
|
||||||
run_collision_detection: bool
|
run_collision_detection: bool
|
||||||
sdf_initpoints: int
|
sdf_initpoints: int
|
||||||
sdf_iterations: int
|
sdf_iterations: int
|
||||||
@@ -885,6 +886,7 @@ _NDIM = {
|
|||||||
'opt__iterations': 0,
|
'opt__iterations': 0,
|
||||||
'opt__ls_iterations': 0,
|
'opt__ls_iterations': 0,
|
||||||
'opt__ls_parallel': 0,
|
'opt__ls_parallel': 0,
|
||||||
|
'opt__ls_parallel_min_step': 0,
|
||||||
'opt__ls_tolerance': 1,
|
'opt__ls_tolerance': 1,
|
||||||
'opt__magnetic': 2,
|
'opt__magnetic': 2,
|
||||||
'opt__run_collision_detection': 0,
|
'opt__run_collision_detection': 0,
|
||||||
@@ -1002,6 +1004,7 @@ _NDIM = {
|
|||||||
'iterations': 0,
|
'iterations': 0,
|
||||||
'ls_iterations': 0,
|
'ls_iterations': 0,
|
||||||
'ls_parallel': 0,
|
'ls_parallel': 0,
|
||||||
|
'ls_parallel_min_step': 0,
|
||||||
'ls_tolerance': 1,
|
'ls_tolerance': 1,
|
||||||
'magnetic': 2,
|
'magnetic': 2,
|
||||||
'run_collision_detection': 0,
|
'run_collision_detection': 0,
|
||||||
@@ -1470,6 +1473,7 @@ _BATCH_DIM = {
|
|||||||
'opt__iterations': False,
|
'opt__iterations': False,
|
||||||
'opt__ls_iterations': False,
|
'opt__ls_iterations': False,
|
||||||
'opt__ls_parallel': False,
|
'opt__ls_parallel': False,
|
||||||
|
'opt__ls_parallel_min_step': False,
|
||||||
'opt__ls_tolerance': True,
|
'opt__ls_tolerance': True,
|
||||||
'opt__magnetic': True,
|
'opt__magnetic': True,
|
||||||
'opt__run_collision_detection': False,
|
'opt__run_collision_detection': False,
|
||||||
@@ -1587,6 +1591,7 @@ _BATCH_DIM = {
|
|||||||
'iterations': False,
|
'iterations': False,
|
||||||
'ls_iterations': False,
|
'ls_iterations': False,
|
||||||
'ls_parallel': False,
|
'ls_parallel': False,
|
||||||
|
'ls_parallel_min_step': False,
|
||||||
'ls_tolerance': True,
|
'ls_tolerance': True,
|
||||||
'magnetic': True,
|
'magnetic': True,
|
||||||
'run_collision_detection': False,
|
'run_collision_detection': False,
|
||||||
|
|||||||
Reference in New Issue
Block a user