Skip to content

Commit e8801d4

Browse files
authored
[MISC] Add MPR vs GJK benchmark. (#1988)
* Minor cleanup. * Add MPR vs GJK benchmark.
1 parent e2f47a1 commit e8801d4

File tree

7 files changed

+134
-155
lines changed

7 files changed

+134
-155
lines changed

examples/collision/pyramid.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def main():
3131
# create pyramid of boxes
3232
box_size = 0.25
3333
box_spacing = (1.0 - 1e-3 + 0.1 * (args.pile_type == "static")) * box_size
34-
box_pos_offset = (0 - 0.5, 1, 0.0) + 0.5 * np.array([box_size, box_size, box_size])
34+
box_pos_offset = (-0.5, 1, 0.0) + 0.5 * np.array([box_size, box_size, box_size])
3535
boxes = {}
3636
for i in range(args.num_cubes):
3737
for j in range(args.num_cubes - i):

examples/collision/tower.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import numpy as np
2-
import genesis as gs
31
import argparse
42

3+
import genesis as gs
4+
55

66
def main():
77
parser = argparse.ArgumentParser()
@@ -34,11 +34,11 @@ def main():
3434
num_stacks = 50
3535
for i in range(num_stacks):
3636
if i % 2 == 0: # horizontal stack
37-
box_size = np.array([box_width, box_length, box_height])
37+
box_size = (box_width, box_length, box_height)
3838
box_pos_0 = (-0.4 * box_length, 0, i * (box_height - 1e-3) + 0.5 * box_height)
3939
box_pos_1 = (+0.4 * box_length, 0, i * (box_height - 1e-3) + 0.5 * box_height)
4040
else: # vertical stack
41-
box_size = np.array([box_length, box_width, box_height])
41+
box_size = (box_length, box_width, box_height)
4242
box_pos_0 = (0, -0.4 * box_length, i * (box_height - 1e-3) + 0.5 * box_height)
4343
box_pos_1 = (0, +0.4 * box_length, i * (box_height - 1e-3) + 0.5 * box_height)
4444
for box_pos in (box_pos_0, box_pos_1):
@@ -68,8 +68,7 @@ def main():
6868
),
6969
)
7070
elif object_type == "cylinder":
71-
cylinder_radius = 2.0
72-
cylinder_height = 1.0
71+
cylinder_radius, cylinder_height = 2.0, 1.0
7372
scene.add_entity(
7473
morph=gs.morphs.Cylinder(
7574
radius=cylinder_radius,

genesis/engine/solvers/rigid/collider_decomp.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2370,9 +2370,10 @@ def func_convex_convex_contact(
23702370
for i_mpr in range(2):
23712371
if i_mpr == 1:
23722372
# Try without warm-start if no contact was detected using it.
2373-
# When penetration depth is very shallow, MPR may wrongly classify two geometries as not in
2374-
# contact while they actually are. This helps to improve contact persistence without increasing
2375-
# much the overall computational cost since the fallback should not be triggered very often.
2373+
# When penetration depth is very shallow, MPR may wrongly classify two geometries as not
2374+
# in contact while they actually are. This helps to improve contact persistence without
2375+
# increasing much the overall computational cost since the fallback should not be
2376+
# triggered very often.
23762377
is_mpr_guess_direction_available = (ti.abs(normal_ws) > EPS).any()
23772378
if (i_detection == 0) and not is_col and is_mpr_guess_direction_available:
23782379
normal_ws = ti.Vector.zero(gs.ti_float, 3)
@@ -2401,16 +2402,17 @@ def func_convex_convex_contact(
24012402
# Fallback on SDF if collision is detected by MPR but no collision direction was cached and the
24022403
# initial penetration is already quite large, because the contact information provided by MPR
24032404
# may be unreliable in such a case.
2404-
if is_col and penetration > tolerance:
2405-
# MPR cannot handle collision detection for fully enclosed geometries. Falling back to SDF.
2406-
# Note that SDF does not take into account to direction of interest. As such, it cannot be
2407-
# used reliably for anything else than the point of deepest penetration.
2408-
prefer_sdf = (
2409-
collider_info.mc_tolerance[None] * penetration
2410-
>= collider_info.mpr_to_sdf_overlap_ratio[None] * tolerance
2411-
)
2412-
if prefer_sdf or not is_mpr_guess_direction_available:
2413-
try_sdf = True
2405+
if ti.static(collider_static_config.ccd_algorithm == CCD_ALGORITHM_CODE.MPR):
2406+
if is_col and penetration > tolerance:
2407+
# MPR cannot handle collision detection for fully enclosed geometries. Falling back to
2408+
# SDF. Note that SDF does not take into account to direction of interest. As such, it
2409+
# cannot be used reliably for anything else than the point of deepest penetration.
2410+
prefer_sdf = (
2411+
collider_info.mc_tolerance[None] * penetration
2412+
>= collider_info.mpr_to_sdf_overlap_ratio[None] * tolerance
2413+
)
2414+
if prefer_sdf or not is_mpr_guess_direction_available:
2415+
try_sdf = True
24142416

24152417
### GJK, MJ_GJK
24162418
elif ti.static(
@@ -2608,7 +2610,7 @@ def func_convex_convex_contact(
26082610
errno,
26092611
)
26102612
if multi_contact:
2611-
# perturb geom_a around two orthogonal axes to find multiple contacts
2613+
# Perturb geom_a around two orthogonal axes to find multiple contacts
26122614
axis_0, axis_1 = func_contact_orthogonals(
26132615
i_ga,
26142616
i_gb,

genesis/engine/solvers/rigid/constraint_solver_decomp.py

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,10 @@ def func_equality_connect(
560560
for i_3 in range(3):
561561
n_con = ti.atomic_add(constraint_state.n_constraints[i_b], 1)
562562
ti.atomic_add(constraint_state.n_constraints_equality[i_b], 1)
563+
con_n_relevant_dofs = 0
563564

564565
if ti.static(static_rigid_sim_config.sparse_solve):
565-
for i_d_ in range(collider_state.jac_n_relevant_dofs[n_con, i_b]):
566+
for i_d_ in range(constraint_state.jac_n_relevant_dofs[n_con, i_b]):
566567
i_d = constraint_state.jac_relevant_dofs[n_con, i_d_, i_b]
567568
constraint_state.jac[n_con, i_d, i_b] = gs.ti_float(0.0)
568569
else:
@@ -1231,9 +1232,8 @@ def func_nt_hessian_direct(
12311232
n_entities = entities_info.n_links.shape[0]
12321233

12331234
# H = M + J'*D*J
1234-
for i_d1 in range(n_dofs):
1235-
for i_d2 in range(n_dofs):
1236-
constraint_state.nt_H[i_d1, i_d2, i_b] = gs.ti_float(0.0)
1235+
for i_d1, i_d2 in ti.ndrange(n_dofs, n_dofs):
1236+
constraint_state.nt_H[i_d1, i_d2, i_b] = gs.ti_float(0.0)
12371237

12381238
if ti.static(static_rigid_sim_config.sparse_solve):
12391239
for i_c in range(constraint_state.n_constraints[i_b]):
@@ -1251,17 +1251,16 @@ def func_nt_hessian_direct(
12511251
* constraint_state.active[i_c, i_b]
12521252
)
12531253
else:
1254-
for i_c in range(constraint_state.n_constraints[i_b]):
1255-
for i_d1 in range(n_dofs):
1256-
if ti.abs(constraint_state.jac[i_c, i_d1, i_b]) > EPS:
1257-
for i_d2 in range(i_d1 + 1):
1258-
constraint_state.nt_H[i_d1, i_d2, i_b] = (
1259-
constraint_state.nt_H[i_d1, i_d2, i_b]
1260-
+ constraint_state.jac[i_c, i_d2, i_b]
1261-
* constraint_state.jac[i_c, i_d1, i_b]
1262-
* constraint_state.efc_D[i_c, i_b]
1263-
* constraint_state.active[i_c, i_b]
1264-
)
1254+
for i_d1, i_c in ti.ndrange(n_dofs, constraint_state.n_constraints[i_b]):
1255+
if ti.abs(constraint_state.jac[i_c, i_d1, i_b]) > EPS:
1256+
for i_d2 in range(i_d1 + 1):
1257+
constraint_state.nt_H[i_d1, i_d2, i_b] = (
1258+
constraint_state.nt_H[i_d1, i_d2, i_b]
1259+
+ constraint_state.jac[i_c, i_d2, i_b]
1260+
* constraint_state.jac[i_c, i_d1, i_b]
1261+
* constraint_state.efc_D[i_c, i_b]
1262+
* constraint_state.active[i_c, i_b]
1263+
)
12651264

12661265
for i_d1 in range(n_dofs):
12671266
for i_d2 in range(i_d1 + 1, n_dofs):
@@ -1404,6 +1403,7 @@ def func_solve(
14041403
):
14051404
_B = constraint_state.grad.shape[1]
14061405
n_dofs = constraint_state.grad.shape[0]
1406+
14071407
ti.loop_config(serialize=static_rigid_sim_config.para_level < gs.PARA_LEVEL.ALL)
14081408
for i_b in range(_B):
14091409
# this safeguard seems not necessary in normal execution
@@ -1419,8 +1419,10 @@ def func_solve(
14191419
constraint_state=constraint_state,
14201420
static_rigid_sim_config=static_rigid_sim_config,
14211421
)
1422-
if constraint_state.improved[i_b] < 1:
1422+
if not constraint_state.improved[i_b]:
14231423
break
1424+
else:
1425+
constraint_state.improved[i_b] = False
14241426

14251427

14261428
@ti.func
@@ -1540,7 +1542,7 @@ def func_no_linesearch(i_b, constraint_state: array_class.ConstraintState):
15401542
func_ls_init(i_b)
15411543
n_dofs = constraint_state.search.shape[0]
15421544

1543-
constraint_state.improved[i_b] = 1
1545+
constraint_state.improved[i_b] = True
15441546
for i_d in range(n_dofs):
15451547
constraint_state.qacc[i_d, i_b] = constraint_state.qacc[i_d, i_b] + constraint_state.search[i_d, i_b]
15461548
constraint_state.Ma[i_d, i_b] = constraint_state.Ma[i_d, i_b] + constraint_state.mv[i_d, i_b]
@@ -1810,7 +1812,7 @@ def func_solve_body(
18101812
)
18111813

18121814
if ti.abs(alpha) < rigid_global_info.EPS[None]:
1813-
constraint_state.improved[i_b] = 0
1815+
constraint_state.improved[i_b] = False
18141816
else:
18151817
for i_d in range(n_dofs):
18161818
constraint_state.qacc[i_d, i_b] = (
@@ -1861,9 +1863,9 @@ def func_solve_body(
18611863
gradient += constraint_state.grad[i_d, i_b] * constraint_state.grad[i_d, i_b]
18621864
gradient = ti.sqrt(gradient)
18631865
if gradient < tol_scaled or improvement < tol_scaled:
1864-
constraint_state.improved[i_b] = 0
1866+
constraint_state.improved[i_b] = False
18651867
else:
1866-
constraint_state.improved[i_b] = 1
1868+
constraint_state.improved[i_b] = True
18671869

18681870
if ti.static(static_rigid_sim_config.solver_type == gs.constraint_solver.Newton):
18691871
for i_d in range(n_dofs):

genesis/engine/solvers/rigid/constraint_solver_decomp_island.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __init__(self, rigid_solver: "RigidSolver"):
4949
self.jac_n_relevant_dofs = ti.field(gs.ti_int, shape=(self.len_constraints_, self._B))
5050

5151
self.n_constraints = ti.field(gs.ti_int, shape=(self._B,))
52-
self.improved = ti.field(gs.ti_int, shape=(self._B,))
52+
self.improved = ti.field(gs.ti_bool, shape=(self._B,))
5353

5454
self.Jaref = ti.field(dtype=gs.ti_float, shape=(self.len_constraints_, self._B))
5555
self.Ma = ti.field(dtype=gs.ti_float, shape=(self._solver.n_dofs_, self._B))
@@ -566,7 +566,7 @@ def _func_solve(self, i_island: int, i_b: int):
566566
tol_scaled = (self._solver.meaninertia[i_b] * ti.max(1, self._solver.n_dofs)) * self.tolerance
567567
for it in range(self.iterations):
568568
self._func_solve_body(i_island, i_b)
569-
if self.improved[i_b] < 1:
569+
if not self.improved[i_b]:
570570
break
571571

572572
gradient = gs.ti_float(0.0)
@@ -868,9 +868,9 @@ def _func_solve_body(self, island, i_b):
868868
alpha = self._func_linesearch(island, i_b)
869869

870870
if ti.abs(alpha) < gs.EPS:
871-
self.improved[i_b] = 0
871+
self.improved[i_b] = False
872872
else:
873-
self.improved[i_b] = 1
873+
self.improved[i_b] = True
874874
for i_island_entity in range(self.contact_island.island_entity[island, i_b].n):
875875
i_e_ = self.contact_island.island_entity[island, i_b].start + i_island_entity
876876
i_e = self.contact_island.entity_id[i_e_, i_b]

genesis/utils/array_class.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,25 +109,31 @@ class StructRigidGlobalInfo(metaclass=BASE_METACLASS):
109109
def get_rigid_global_info(solver):
110110
_B = solver._B
111111

112+
mass_mat_shape = (solver.n_dofs_, solver.n_dofs_, _B)
113+
if math.prod(mass_mat_shape) > np.iinfo(np.int32).max:
114+
gs.raise_exception(
115+
f"Mass matrix shape (n_dofs={solver.n_dofs_}, n_dofs={solver.n_dofs_}, n_envs={_B}) is too large."
116+
)
117+
112118
return StructRigidGlobalInfo(
119+
envs_offset=V_VEC(3, dtype=gs.ti_float, shape=(_B,)),
120+
gravity=V_VEC(3, dtype=gs.ti_float, shape=(_B,)),
121+
meaninertia=V(dtype=gs.ti_float, shape=(_B,)),
113122
n_awake_dofs=V(dtype=gs.ti_int, shape=(_B,)),
114-
awake_dofs=V(dtype=gs.ti_int, shape=(solver.n_dofs_, _B)),
115123
n_awake_entities=V(dtype=gs.ti_int, shape=(_B,)),
116-
awake_entities=V(dtype=gs.ti_int, shape=(solver.n_entities_, _B)),
117124
n_awake_links=V(dtype=gs.ti_int, shape=(_B,)),
125+
awake_dofs=V(dtype=gs.ti_int, shape=(solver.n_dofs_, _B)),
126+
awake_entities=V(dtype=gs.ti_int, shape=(solver.n_entities_, _B)),
118127
awake_links=V(dtype=gs.ti_int, shape=(solver.n_links_, _B)),
119128
qpos0=V(dtype=gs.ti_float, shape=(solver.n_qs_, _B)),
120129
qpos=V(dtype=gs.ti_float, shape=(solver.n_qs_, _B)),
121130
links_T=V_MAT(n=4, m=4, dtype=gs.ti_float, shape=(solver.n_links_,)),
122-
envs_offset=V_VEC(3, dtype=gs.ti_float, shape=(_B,)),
123131
geoms_init_AABB=V_VEC(3, dtype=gs.ti_float, shape=(solver.n_geoms_, 8)),
124-
mass_mat=V(dtype=gs.ti_float, shape=(solver.n_dofs_, solver.n_dofs_, _B)),
125-
mass_mat_L=V(dtype=gs.ti_float, shape=(solver.n_dofs_, solver.n_dofs_, _B)),
126132
mass_mat_D_inv=V(dtype=gs.ti_float, shape=(solver.n_dofs_, _B)),
127133
mass_mat_mask=V(dtype=gs.ti_bool, shape=(solver.n_entities_, _B)),
128-
meaninertia=V(dtype=gs.ti_float, shape=(_B,)),
129134
mass_parent_mask=V(dtype=gs.ti_float, shape=(solver.n_dofs_, solver.n_dofs_)),
130-
gravity=V_VEC(3, dtype=gs.ti_float, shape=(_B,)),
135+
mass_mat=V(dtype=gs.ti_float, shape=mass_mat_shape),
136+
mass_mat_L=V(dtype=gs.ti_float, shape=mass_mat_shape),
131137
substep_dt=V_SCALAR_FROM(dtype=gs.ti_float, value=solver._substep_dt),
132138
iterations=V_SCALAR_FROM(dtype=gs.ti_int, value=solver._options.iterations),
133139
tolerance=V_SCALAR_FROM(dtype=gs.ti_float, value=solver._options.tolerance),
@@ -239,7 +245,7 @@ def get_constraint_state(constraint_solver, solver):
239245
ti_n_equalities=V(dtype=gs.ti_int, shape=(_B,)),
240246
n_constraints_equality=V(dtype=gs.ti_int, shape=(_B,)),
241247
n_constraints_frictionloss=V(dtype=gs.ti_int, shape=(_B,)),
242-
improved=V(dtype=gs.ti_int, shape=(_B,)),
248+
improved=V(dtype=gs.ti_bool, shape=(_B,)),
243249
cost_ws=V(dtype=gs.ti_float, shape=(_B,)),
244250
gauss=V(dtype=gs.ti_float, shape=(_B,)),
245251
cost=V(dtype=gs.ti_float, shape=(_B,)),

0 commit comments

Comments
 (0)