Skip to content

Commit 7cc6917

Browse files
Libero0809duburcqaYilingQiao
authored
[FEATURE] SAP coupler and Hydroelastic contact model. (#1295)
--------- Co-authored-by: Alexis DUBURCQ <alexis.duburcq@gmail.com> Co-authored-by: YilingQiao <49262224+YilingQiao@users.noreply.github.com>
1 parent 377ef30 commit 7cc6917

File tree

8 files changed

+1338
-103
lines changed

8 files changed

+1338
-103
lines changed

genesis/engine/coupler.py

Lines changed: 915 additions & 1 deletion
Large diffs are not rendered by default.

genesis/engine/entities/fem_entity.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import taichi as ti
33
import torch
44

5+
import igl
6+
57
import genesis as gs
68
import genesis.utils.element as eu
79
import genesis.utils.geom as gu
@@ -11,6 +13,7 @@
1113
from genesis.utils.misc import to_gs_tensor
1214

1315
from .base_entity import Entity
16+
from genesis.engine.coupler import SAPCoupler
1417

1518

1619
@ti.data_oriented
@@ -82,6 +85,9 @@ def __init__(self, scene, solver, material, morph, surface, idx, v_start=0, el_s
8285
unique_el = all_el[unique_idcs]
8386
self._surface_el_np = unique_el[cnt == 1].astype(gs.np_int)
8487

88+
if isinstance(self.sim.coupler, SAPCoupler):
89+
self.compute_pressure_field()
90+
8591
self.init_tgt_vars()
8692
self.init_ckpt()
8793

@@ -371,6 +377,7 @@ def _add_to_solver(self, in_backward=False):
371377
mat_mu=self._material.mu,
372378
mat_lam=self._material.lam,
373379
mat_rho=self._material.rho,
380+
mat_friction_mu=self._material.friction_mu,
374381
n_surfaces=self._n_surfaces,
375382
v_start=self._v_start,
376383
el_start=self._el_start,
@@ -382,6 +389,29 @@ def _add_to_solver(self, in_backward=False):
382389
)
383390
self.active = True
384391

392+
def compute_pressure_field(self):
393+
"""
394+
Compute the pressure field for the FEM entity based on its tetrahedral elements.
395+
396+
For hydroelastic contact: https://drake.mit.edu/doxygen_cxx/group__hydroelastic__user__guide.html
397+
398+
Notes
399+
-----
400+
https://github.com/RobotLocomotion/drake/blob/master/geometry/proximity/make_mesh_field.cc
401+
TODO: Add margin support
402+
Drake's implementation of margin seems buggy.
403+
"""
404+
init_positions = self.init_positions.cpu().numpy()
405+
signed_distance, *_ = igl.signed_distance(init_positions, init_positions, self._surface_tri_np)
406+
unsigned_distance = np.abs(signed_distance)
407+
max_distance = np.max(unsigned_distance)
408+
if max_distance < gs.EPS:
409+
gs.raise_exception(
410+
f"Pressure field max distance is too small: {max_distance}. "
411+
"This might be due to a mesh having no internal vertices."
412+
)
413+
self.pressure_field_np = unsigned_distance / max_distance * self.material._hydroelastic_modulus # normalize
414+
385415
# ------------------------------------------------------------------------------------
386416
# ---------------------------- checkpoint and buffer ---------------------------------
387417
# ------------------------------------------------------------------------------------

genesis/engine/materials/FEM/base.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,32 @@ class Base(Material):
2323
Poisson ratio, describing the material's volume change under stress. Default is 0.2.
2424
rho: float, optional
2525
Material density (kg/m^3). Default is 1000.
26+
hydroelastic_modulus: float, optional
27+
Hydroelastic modulus for hydroelastic contact. Default is 1e7.
28+
friction_mu: float, optional
29+
Friction coefficient. Default is 0.1.
30+
hessian_invariant: bool, optional
31+
If True, Hessian is computed only once. Default is False.
2632
"""
2733

2834
def __init__(
2935
self,
30-
E=1e6, # Young's modulus
31-
nu=0.2, # Poisson's ratio
32-
rho=1000.0, # density (kg/m^3)
36+
E=1e6,
37+
nu=0.2,
38+
rho=1000.0,
39+
hydroelastic_modulus=1e7,
40+
friction_mu=0.1,
41+
hessian_invariant=False,
3342
):
3443
super().__init__()
3544

3645
self._E = E
3746
self._nu = nu
3847
self._rho = rho
48+
self._hydroelastic_modulus = hydroelastic_modulus
49+
self._friction_mu = friction_mu
50+
self.hessian_invariant = hessian_invariant
51+
self.hessian_ready = False
3952

4053
# lame parameters: https://github.com/taichi-dev/taichi_elements/blob/d19678869a28b09a32ef415b162e35dc929b792d/engine/mpm_solver.py#L203
4154
self._mu = E / (2.0 * (1.0 + nu))
@@ -84,3 +97,13 @@ def lam(self):
8497
def rho(self):
8598
"""The rest density."""
8699
return self._rho
100+
101+
@property
102+
def contact_stiffness(self):
103+
"""The contact stiffness."""
104+
return self._hydroelastic_modulus
105+
106+
@property
107+
def friction_mu(self):
108+
"""The friction coefficient."""
109+
return self._friction_mu

genesis/engine/materials/FEM/elastic.py

Lines changed: 97 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ class Elastic(Base):
2727
Poisson ratio, describing the material's volume change under stress. Default is 0.2.
2828
rho: float, optional
2929
Material density (kg/m^3). Default is 1000.
30+
hydroelastic_modulus: float, optional
31+
Hydroelastic modulus for hydroelastic contact. Default is 1e7.
32+
friction_mu: float, optional
33+
Friction coefficient. Default is 0.1.
3034
model: str, optional
3135
Constitutive model to use for stress computation. Options are:
3236
- 'linear': Linear elasticity model
@@ -39,23 +43,26 @@ def __init__(
3943
E=1e6, # Young's modulus
4044
nu=0.2, # Poisson's ratio
4145
rho=1000.0, # density (kg/m^3)
46+
hydroelastic_modulus=1e7, # hydroelastic_modulus for hydroelastic contact
47+
friction_mu=0.1,
4248
model="linear",
4349
):
44-
super().__init__(E, nu, rho)
50+
super().__init__(E, nu, rho, hydroelastic_modulus, friction_mu)
4551

4652
if model == "linear":
4753
self.update_stress = self.update_stress_linear
4854
self.compute_energy_gradient_hessian = self.compute_energy_gradient_hessian_linear
55+
self.compute_energy_gradient = self.compute_energy_gradient_linear
4956
self.compute_energy = self.compute_energy_linear
50-
elif model == "stable_neohookean":
51-
self.update_stress = self.update_stress_stable_neohookean
52-
self.compute_energy_gradient_hessian = self.compute_energy_gradient_hessian_stable_neohookean
53-
self.compute_energy = self.compute_energy_stable_neohookean
54-
elif model == "stable_neohooken":
55-
gs.logger.warning("The 'stable_neohooken' model is deprecated. Use 'stable_neohookean' instead.")
57+
self.hessian_invariant = True
58+
elif model in ("stable_neohookean", "stable_neohooken"):
5659
self.update_stress = self.update_stress_stable_neohookean
5760
self.compute_energy_gradient_hessian = self.compute_energy_gradient_hessian_stable_neohookean
61+
self.compute_energy_gradient = self.compute_energy_gradient_stable_neohookean
5862
self.compute_energy = self.compute_energy_stable_neohookean
63+
self.hessian_invariant = False
64+
if model == "stable_neohooken":
65+
gs.logger.warning("The 'stable_neohooken' model is deprecated. Use 'stable_neohookean' instead.")
5966
else:
6067
gs.raise_exception(f"Unrecognized constitutive model: {model}")
6168

@@ -147,6 +154,48 @@ def compute_energy_gradient_hessian_linear(self, mu, lam, J, F, actu, m_dir, i_e
147154
hessian_field[i_b, 1, 2, i_e][1, 2] = hessian_field[i_b, 2, 1, i_e][2, 1] = lam
148155
return energy, gradient
149156

157+
@ti.func
158+
def compute_energy_gradient_linear(self, mu, lam, J, F, actu, m_dir, i_e, i_b):
159+
"""
160+
Compute the energy, gradient for linear elasticity.
161+
162+
Parameters
163+
----------
164+
mu: float
165+
The first Lame parameter (shear modulus).
166+
lam: float
167+
The second Lame parameter (related to volume change).
168+
J: float
169+
The determinant of the deformation gradient F.
170+
F: ti.Matrix
171+
The deformation gradient matrix.
172+
actu: ti.Matrix
173+
The activation matrix (not used in linear elasticity).
174+
m_dir: ti.Matrix
175+
The material direction (not used in linear elasticity).
176+
177+
Returns
178+
-------
179+
energy: float
180+
The computed energy.
181+
gradient: ti.Matrix
182+
The gradient of the energy with respect to the deformation gradient F.
183+
184+
Notes
185+
-------
186+
This implementation assumes small deformations and linear stress-strain relationship.
187+
It is adapted from the HOBAKv1 implementation for linear elasticity:
188+
https://github.com/theodorekim/HOBAKv1/blob/8420c51b795735d8fb912e0f8810f935d96fb636/src/Hyperelastic/Volume/LINEAR.cpp
189+
"""
190+
I = ti.Matrix.identity(dt=gs.ti_float, n=3)
191+
eps = 0.5 * (F + F.transpose()) - I
192+
trEps = eps.trace()
193+
energy = mu * eps.norm_sqr() + 0.5 * lam * trEps**2
194+
195+
gradient = 2.0 * mu * eps + lam * trEps * I
196+
197+
return energy, gradient
198+
150199
@ti.func
151200
def compute_energy_linear(self, mu, lam, J, F, actu, m_dir):
152201
"""
@@ -176,8 +225,7 @@ def compute_energy_linear(self, mu, lam, J, F, actu, m_dir):
176225
-------
177226
This implementation assumes small deformations and linear stress-strain relationship.
178227
It is adapted from the HOBAKv1 implementation for linear elasticity:
179-
https://github.com/theodorekim/HOBAKv1/blob/main/src/Hyperelastic/Volume/LINEAR.cpp
180-
228+
https://github.com/theodorekim/HOBAKv1/blob/8420c51b795735d8fb912e0f8810f935d96fb636/src/Hyperelastic/Volume/LINEAR.cpp
181229
"""
182230
I = ti.Matrix.identity(dt=gs.ti_float, n=3)
183231
eps = 0.5 * (F + F.transpose()) - I
@@ -228,6 +276,45 @@ def compute_energy_gradient_hessian_stable_neohookean(self, mu, lam, J, F, actu,
228276
"""
229277
raise NotImplementedError("Hessian computation is not implemented for stable_neohookean model.")
230278

279+
@ti.func
280+
def compute_energy_gradient_stable_neohookean(self, mu, lam, J, F, actu, m_dir, i_e, i_b):
281+
"""
282+
Compute the energy, gradient for the stable Neo-Hookean model.
283+
284+
Parameters
285+
----------
286+
mu: float
287+
The first Lame parameter (shear modulus).
288+
lam: float
289+
The second Lame parameter (related to volume change).
290+
J: float
291+
The determinant of the deformation gradient F.
292+
F: ti.Matrix
293+
The deformation gradient matrix.
294+
actu: ti.Matrix
295+
The activation matrix (not used in stable Neo-Hookean).
296+
m_dir: ti.Matrix
297+
The material direction (not used in stable Neo-Hookean).
298+
299+
Returns
300+
-------
301+
energy: float
302+
The computed energy.
303+
gradient: ti.Matrix
304+
The gradient of the energy with respect to the deformation gradient F.
305+
306+
Raises
307+
-------
308+
NotImplementedError
309+
This implementation does not compute the Gradient for the stable Neo-Hookean model.
310+
311+
Notes
312+
-------
313+
This implementation is adapted from the HOBAKv1 stable Neo-Hookean model:
314+
https://github.com/theodorekim/HOBAKv1/blob/8420c51b795735d8fb912e0f8810f935d96fb636/src/Hyperelastic/Volume/SNH.cpp
315+
"""
316+
raise NotImplementedError("Gradient computation is not implemented for stable_neohookean model.")
317+
231318
@ti.func
232319
def compute_energy_stable_neohookean(self, mu, lam, J, F, actu, m_dir):
233320
"""
@@ -256,7 +343,7 @@ def compute_energy_stable_neohookean(self, mu, lam, J, F, actu, m_dir):
256343
Notes
257344
-------
258345
This implementation is adapted from the HOBAKv1 stable Neo-Hookean model:
259-
https://github.com/theodorekim/HOBAKv1/blob/main/src/Hyperelastic/Volume/SNH.cpp
346+
https://github.com/theodorekim/HOBAKv1/blob/8420c51b795735d8fb912e0f8810f935d96fb636/src/Hyperelastic/Volume/SNH.cpp
260347
"""
261348
_lambda = lam + mu
262349
_alpha = 1.0 + mu / _lambda

genesis/engine/simulator.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from genesis.options.solvers import (
99
AvatarOptions,
1010
CouplerOptions,
11+
SAPCouplerOptions,
1112
FEMOptions,
1213
MPMOptions,
1314
PBDOptions,
@@ -19,7 +20,7 @@
1920
)
2021
from genesis.repr_base import RBC
2122

22-
from .coupler import Coupler
23+
from .coupler import Coupler, SAPCoupler
2324
from .entities import HybridEntity
2425
from .solvers import (
2526
AvatarSolver,
@@ -133,7 +134,10 @@ def __init__(
133134
self._active_solvers: list[Solver] = gs.List()
134135

135136
# coupler
136-
self._coupler = Coupler(self, self.coupler_options)
137+
if isinstance(self.coupler_options, SAPCouplerOptions):
138+
self._coupler = SAPCoupler(self, self.coupler_options)
139+
else:
140+
self._coupler = Coupler(self, self.coupler_options)
137141

138142
# states
139143
self._queried_states = QueriedStates()

0 commit comments

Comments
 (0)