7 Contact and Collision

7.8 Monitoring collisions

Sometimes, an application may wish to know details about the collision interactions between a specific collidable and one or more other collidables. These details may include items such as contact forces or the penetration regions between the opposing meshes. This section describes ways in which these details can be observed through the use of collision responses.

7.8.1 Collision responses

Applications can track collision information by creating CollisionResponse components for the collidables in question and adding them to the MechModel using setCollisionResponse():

   CollisionResponse resp = new CollisionResponse();
   mech.setCollisionResponse (collidable0, collidable1, resp);

An alternate version of setCollisionResponse() creates the response component internally and returns it:

   CollisionResponse resp = mech.setCollisionResponse (collidable0, collidable1);

During every simulation step, the MechModel will update its response components to reflect the current collision conditions between the associated collidables.

The first collidable specified for a collision response must be a specific collidable component, while the second may be either another collidable or a group of collidables represented by a Collidable.Group:

  CollisionResponse r0, r1, r2;
  RigidBody bodA;
  FemModel3d femB;
  // collision information between bodA and femB only:
  r0 = setCollisionResponse (bodA, femB);
  // collision information between femB and all rigid bodies:
  r1 = setCollisionResponse (femB, Collidable.Rigid);
  // collision information between femB and all bodies and self-collisions:
  r2 = setCollisionResponse (femB, Collidable.All);

When a compound collidable is specified, the response component will collect collision information for all its subcollidables.

As as with collision behaviors, the same response cannot be added to an application twice:

   CollisionResponse resp = new CollisionResponse();
   mech.setCollisionResponse (femA, femB, resp);
   mech.setCollisionResponse (femC, femD, resp); // ERROR

The complete set of methods for managing collision responses are similar to those for behaviors,

   getCollisionResponse (collidable0, collidable1)
   setCollisionResponse (collidable0, collidable1, response)
   setCollisionResponse (collidable0, collidable1)
   clearCollisionResponse (collidable0, collidable1)
   clearCollisionResponses ()

where getCollisionResponse() and clearCollisionResponse() respectively return and clear response components that have been previously set using one of the setCollisionResponse() methods.

7.8.2 Available information

Information that can be obtained from a CollisionResponse component includes whether or not the collidable is in collision, the current contact points, forces and pressures acting on the vertices of the colliding meshes, and the underlying CollisionHandler objects that maintain the contact constraints between each colliding mesh. Much of the available information is similar to that which can be displayed using collision rendering (Section 7.5).

The information methods provided by CollisionResponse are listed below. Many take a cidx argument to request information for either the first or second collidable, which refer, respectively, to the collidables specified by the arguments collidable0 and collidable1 in the setCollisionResponse() methods.

  • boolean inContact()

    Queries if the collidables associated with the response are in contact. Note that this may be true even if the method getContactData() returns an empty list; this can occur, for instance, for vertex penetration contact when the collision meshes intersect in a small region without any interpenetrating vertices.

  • List<ContactData> getContactData()

    Returns a list of the most recently computed contacts between the collidables. Each contact is described by a ContactData object supplying information about the contact points on each collidable, the normal, and contact and friction forces. If there are no current contacts, the returned list is empty. For more details on this information, consult the API documentation for ContactData and ContactPoint.

  • Map<Vertex3d,Vector3d> getContactForces(int cidx)

    Returns a map of the contact forces acting the on the vertices of the collision meshes of either the first or second collidable, as specified by setting cidx to 0 or 1. This information is most useful and accurate when using vertex penetration contact (Section 7.4.1.2).

  • Map<Vertex3d,Vector3d> getContactForces(int cidx)

    Returns a map of the contact pressures acting the on the vertices of the collision meshes of either the first or second collidable, as specified by setting cidx to 0 or 1. This information is most useful and accurate when using vertex penetration contact (Section 7.4.1.2).

  • ArrayList<PenetrationRegion> getPenetrationRegions(int cidx)

    Returns a list of all the penetration regions on either the first or second collidable, as specified by setting cidx to 0 or 1. Penetration regions are available only if the collision manager’s collider type is set to AJL_CONTOUR (Section 7.4.2).

  • ArrayList<CollisionHandler> getHandlers()

    Returns the CollisionHandlers for all currently active collisions associated with the collidables of the response.

A typical usage scenario for collision responses is to create them before the simulation is run, possibly in the root model build() method, and then query them when the simulation is running, such from the apply() method of a Monitor (Section 5.3). For example, in the root model build() method, the response could be created with the call

   CollisionResponse resp = mech.setCollisionResponse (femA, femB);

and then used in some runtime code as follows:

   Map<Vertex3d,Vector3d> collMap = resp.getContactForces (0);

If for some reason it is difficult to store a reference to the response between its construction and its use, thengetCollisionResponse() can be used to retrieve it:

   CollisionResponse resp = mech.getCollisionResponse (femA, femB);
   Map<Vertex3d,Vector3d> collMap = resp.getContactForces (0);

As with contact rendering, the information returned by a collision response may sometimes appear to lag the simulation by one time step. That is because contacts are computed at the end of each time step i, and then used to compute the contact forces during the next step i+1. The information returned by a response at the end of step i is thus based on contacts detected at the end of step i-1, along with contact forces that are computed during step i and used to calculate the updated positions and velocities at the end of that step.

7.8.3 Example: monitoring contact forces

Figure 7.19: ContactForceMonitor showing the contact forces as the ball collides with the plane.

A simple example of using a CollisionResponse is given by

  artisynth.demos.tutorial.ContactForceDemo

This shows an FEM model of a ball rolling down an inclined plane, with a collision response combined with a Monitor used to print out the contact positions and forces at each time step. Contact forces are also rendered in the viewer. The model’s source code, excluding include statements, is shown below:

1 public class ContactForceMonitor extends RootModel {
2
3    CollisionResponse myResp;
4
5    private class ContactMonitor extends MonitorBase {
6       public void apply (double t0, double t1) {
7          // get the contacts from the collision response and print their
8          // positions and forces.
9          List<ContactData> cdata = myResp.getContactData();
10          if (cdata.size() > 0) {
11             System.out.println (
12                "num contacts: "+ cdata.size() + ", time=" + t0);
13             for (ContactData cd : cdata) {
14                System.out.print (
15                   " pos:   " + cd.getPosition0().toString("%8.3f"));
16                System.out.println (
17                   ", force: " + cd.getContactForce().toString("%8.1f"));
18             }
19          }
20       }
21    }
22
23    public void build (String[] args) {
24       MechModel mech = new MechModel ("mech");
25       addModel (mech);
26
27       // create an FEM ball model
28       FemModel3d ball =
29          FemFactory.createIcosahedralSphere (
30             null, /*radius=*/0.7, /*ndivisions=*/1, /*quality=*/2);
31       ball.setName ("ball");
32       ball.setSurfaceRendering (SurfaceRender.Shaded);
33       mech.addModel (ball);
34       // reposition the ball to an appropriate "drop" position
35       ball.transformGeometry (new RigidTransform3d (-0.5, 0, 2.5, 0, 0.1, 0.1));
36
37       // create an inclined plane for the ball to collide with
38       RigidBody plane = RigidBody.createBox (
39          "plane", 4.0, 2.5, 0.25, /*density=*/1000);
40       plane.setPose (new RigidTransform3d (0, 0, 0, 0, Math.PI/5, 0));
41       plane.setDynamic (false);
42       mech.addRigidBody (plane);
43
44       // Enable collisions between the ball and the plane. Specifying the ball
45       // first means contact forces will be rendered in the direction acting on
46       // the ball.
47       mech.setCollisionBehavior (ball, plane, true, 0.3);
48       myResp = mech.setCollisionResponse (ball, plane);
49
50       // add a monitor to print out contact positions and forces
51       addMonitor (new ContactMonitor());
52
53       // Render properties: set the collision manager to render contact and
54       // friction forces, with a scale factor of 0.0001
55       CollisionManager cm = mech.getCollisionManager();
56       cm.setDrawContactForces (true);
57       cm.setDrawFrictionForces (true);
58       cm.setContactForceLenScale (0.0001);
59       RenderProps.setVisible (cm, true);
60       RenderProps.setSolidArrowLines (cm, 0.02, Color.BLUE);
61       // Render properties: for the ball, make the elements invisible, and
62       // render its surface as a wire frame to make it easy to see through
63       RenderProps.setVisible (ball.getElements(), false);
64       RenderProps.setLineColor (ball, new Color (.8f, .8f, 1f));
65       RenderProps.setFaceStyle (ball, FaceStyle.NONE);
66       RenderProps.setDrawEdges (ball, true);
67       RenderProps.setEdgeWidth (ball, 2); // wire frame edge width
68       RenderProps.setFaceColor (plane, new Color (.7f, 1f, .7f));
69    }
70 }

The build method creates a simple FEM ball and inclined plane, and positions the ball to an appropriate drop position (lines 27-42). The collisions are enabled between the ball and the plate, along with a CollisionResponse that is stored in the global reference myResp to allow it to be accessed from the monitor (lines 47-48).

The monitor itself is implemented by the class ContactMonitor, which is created by subclassing MonitorBase and overriding the apply() method to print out the contact information (lines 5-21). It does this by using the response’s getContactData() method to obtain a list of the contacts, and if there are any, printing the number of contacts, the time step start time (t0), and the position and force of each contact. An instance of the monitor is created and added to the root model at line 51.

Rendering is set so that the collision manager renders both the contact and friction forces in the viewer (lines 55-60), using blue arrows with a radius of 0.02 (line 60) and a scale factor for the arrow length of 0.0001 (line 58). To make the ball easy to see through, its elements are made invisible, and instead is rendered using only its surface mesh, with face rendering disabled and edges drawn with a width of 2 (lines 63-67).

To run this example in ArtiSynth, select All demos > tutorial > ContactForceMonitor from the Models menu. Running the model will result in the ball colliding with the plane, showing the contact and friction forces as blue arrows (Figure 7.19, while the monitor prints out the contact positions and forces to the console at each time step, producing output like this:

  num contacts: 2, time=0.64
   pos:     -0.918    0.059    0.821, force:  17508.5      0.0  24098.4
   pos:     -0.760   -0.285    0.706, force:  11607.9      0.0  15976.9
  num contacts: 2, time=0.65
   pos:     -0.918    0.059    0.821, force:  16989.6      0.0  23384.2
   pos:     -0.760   -0.285    0.706, force:  11955.8      0.0  16455.7
  num contacts: 3, time=0.66
   pos:     -0.918    0.059    0.821, force:  16465.4      0.0  22662.7
   pos:     -0.760   -0.285    0.706, force:  11880.9      0.0  16352.7
   pos:     -0.695    0.404    0.659, force:    661.1      0.0    910.