Skip to content

Commit 90c487d

Browse files
committed
[Examples] Add a script to access contact forces
1 parent 09c18ed commit 90c487d

1 file changed

Lines changed: 133 additions & 0 deletions

File tree

examples/access_contact_forces.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Required import for python
2+
import Sofa
3+
import numpy as np
4+
import math
5+
6+
7+
# Choose in your script to activate or not the GUI
8+
USE_GUI = True
9+
10+
11+
def main():
12+
import SofaRuntime
13+
import Sofa.Gui
14+
15+
root = Sofa.Core.Node("root")
16+
createScene(root)
17+
Sofa.Simulation.initRoot(root)
18+
19+
if not USE_GUI:
20+
import SofaQt
21+
22+
for iteration in range(10):
23+
Sofa.Simulation.animate(root, root.dt.value)
24+
else:
25+
Sofa.Gui.GUIManager.Init("myscene", "qglviewer")
26+
Sofa.Gui.GUIManager.createGUI(root, __file__)
27+
Sofa.Gui.GUIManager.SetDimension(1080, 1080)
28+
Sofa.Gui.GUIManager.MainLoop(root)
29+
Sofa.Gui.GUIManager.closeGUI()
30+
31+
32+
def createScene(root):
33+
root.gravity=[0, -9.81, 0]
34+
root.dt=0.02
35+
36+
root.addObject("RequiredPlugin", pluginName=[ 'Sofa.Component.Collision.Detection.Algorithm',
37+
'Sofa.Component.Collision.Detection.Intersection', 'Sofa.Component.Collision.Geometry',
38+
'Sofa.Component.Collision.Response.Contact', 'Sofa.Component.Constraint.Projective',
39+
'Sofa.Component.IO.Mesh','Sofa.Component.LinearSolver.Iterative',
40+
'Sofa.Component.Mapping.Linear', 'Sofa.Component.Mass', 'Sofa.Component.ODESolver.Backward',
41+
'Sofa.Component.SolidMechanics.FEM.Elastic','Sofa.Component.StateContainer',
42+
'Sofa.Component.Topology.Container.Dynamic','Sofa.Component.Visual',
43+
'Sofa.GL.Component.Rendering3D','Sofa.Component.Constraint.Lagrangian.Correction',
44+
'Sofa.Component.Constraint.Lagrangian.Solver', 'Sofa.Component.MechanicalLoad',
45+
'Sofa.Component.LinearSolver.Direct','Sofa.Component.AnimationLoop'
46+
])
47+
48+
root.addObject('FreeMotionAnimationLoop')
49+
# Constraint solver computing the constraint/contact forces, stored in the constraint space (normal , tangential_1, tangential_2)
50+
constraint_solver = root.addObject('GenericConstraintSolver', maxIterations=1000, tolerance=1e-6, computeConstraintForces=True)
51+
52+
root.addObject('VisualStyle', displayFlags="showCollisionModels hideVisualModels showForceFields")
53+
root.addObject('CollisionPipeline', name="CollisionPipeline")
54+
root.addObject('BruteForceBroadPhase', name="BroadPhase")
55+
root.addObject('BVHNarrowPhase', name="NarrowPhase")
56+
root.addObject('DiscreteIntersection')
57+
root.addObject('CollisionResponse', name="CollisionResponse", response="FrictionContactConstraint", responseParams="mu=0.1")
58+
59+
root.addObject('MeshOBJLoader', name="LiverSurface", filename="mesh/liver-smooth.obj")
60+
61+
liver = root.addChild('Liver')
62+
liver.addObject('EulerImplicitSolver', name="cg_odesolver", rayleighStiffness=0.1, rayleighMass=0.1)
63+
liver.addObject('SparseLDLSolver', name="linear_solver")
64+
liver.addObject('MeshGmshLoader', name="meshLoader", filename="mesh/liver.msh")
65+
liver.addObject('TetrahedronSetTopologyContainer', name="topo", src="@meshLoader")
66+
# Liver MechanicalObject where the constraint/contact forces will be stored in the (x,y,z) coordinate system
67+
liverMO = liver.addObject('MechanicalObject', name="dofs", src="@meshLoader")
68+
liver.addObject('TetrahedronSetGeometryAlgorithms', template="Vec3d", name="GeomAlgo")
69+
liver.addObject('DiagonalMass', name="Mass", massDensity=1.0)
70+
liver.addObject('TetrahedralCorotationalFEMForceField', template="Vec3d", name="FEM", method="large", poissonRatio=0.3, youngModulus=3000, computeGlobalMatrix=False)
71+
liver.addObject('FixedProjectiveConstraint', name="FixedConstraint", indices="3 39 64")
72+
73+
# Forcefield only used for visualization purposes (of the contact forces)
74+
CFF = liver.addObject('ConstantForceField', name="CFF", forces=[0,0,0], showArrowSize=10)
75+
liver.addObject('LinearSolverConstraintCorrection')
76+
77+
visu = liver.addChild('Visu')
78+
visu.addObject('OglModel', name="VisualModel", src="@../../LiverSurface")
79+
visu.addObject('BarycentricMapping', name="VisualMapping", input="@../dofs", output="@VisualModel")
80+
81+
surf = liver.addChild('Surf')
82+
surf.addObject('SphereLoader', name="sphereLoader", filename="mesh/liver.sph")
83+
surf.addObject('MechanicalObject', name="spheres", position="@sphereLoader.position")
84+
surf.addObject('SphereCollisionModel', name="CollisionModel", listRadius="@sphereLoader.listRadius")
85+
surf.addObject('BarycentricMapping', name="CollisionMapping", input="@../dofs", output="@spheres")
86+
87+
88+
particle = root.addChild('Particle')
89+
particle.addObject('EulerImplicitSolver')
90+
particle.addObject('CGLinearSolver', threshold='1e-09', tolerance='1e-09', iterations='200')
91+
# Particle MechanicalObject where the constraint/contact forces will be stored in the (x,y,z) coordinate system
92+
particleMO = particle.addObject('MechanicalObject', showObject=True, position=[-2, 10, 0, 0, 0, 0, 1], name=f'ParticleDoFs', template='Rigid3d')
93+
particle.addObject('UniformMass', totalMass=1)
94+
particle.addObject('ConstantForceField', name="CFF", totalForce=[0, -1, 0, 0, 0, 0] )
95+
particle.addObject('SphereCollisionModel', name="SCM", radius=1.0 )
96+
particle.addObject('UncoupledConstraintCorrection')
97+
98+
# Python controller accessing and displaying the contact forces in the ConstantForceField
99+
root.addObject(AccessContactForces('AccessContactForces', name='AccessContactForces',
100+
constraint_solver=constraint_solver,
101+
soft_liver=liverMO,
102+
forcefield_visu=CFF,
103+
root_node=root))
104+
105+
106+
class AccessContactForces(Sofa.Core.Controller):
107+
108+
def __init__(self, *args, **kwargs):
109+
Sofa.Core.Controller.__init__(self, *args, **kwargs)
110+
self.constraint_solver = kwargs.get("constraint_solver")
111+
self.soft_liver = kwargs.get("soft_liver")
112+
self.forcefield_visu = kwargs.get("forcefield_visu")
113+
self.root_node = kwargs.get("root_node")
114+
115+
116+
def onAnimateEndEvent(self, event):
117+
118+
lambda_vector = self.constraint_solver.constraintForces.value
119+
if(len(lambda_vector) > 0):
120+
print("Forces in the contact space (n, t1, t2) = "+str(lambda_vector)+" (at time = "+str(round(self.root_node.time.value,3))+")")
121+
122+
self.forcefield_visu.forces.value = -self.soft_liver.getData("lambda").value
123+
visuScale = self.forcefield_visu.showArrowSize.value
124+
fact =8.0/np.max(lambda_vector)
125+
self.forcefield_visu.showArrowSize.value = fact
126+
else:
127+
self.forcefield_visu.forces.value = np.zeros((181,3))
128+
129+
130+
131+
# Function used only if this script is called from a python environment
132+
if __name__ == '__main__':
133+
main()

0 commit comments

Comments
 (0)