4 Mechanical Models II

4.5 Collision Handling

Collision handling in ArtiSynth is implemented by a collision handling mechanism build into MechModel. Collisions are disabled by default, but can be enabled between rigid and deformable bodies (finite element models in particular), and more generally between any body that implements the interface Collidable.

It is important to understand that collision handling is both computationally expensive and, due to it’s discontinuous nature, less accurate than other aspects of the simulation. ArtiSynth therefore provides a number of ways to selectively control collision handling between different pairs of bodies.

Collision handling is also challenging because if collisions are enabled among n objects, then one needs to be able to easily specify the characteristics of up to O(n^{2}) collision interactions, while also managing the fact that these interactions are highly transient. In ArtiSynth, collision handling is done by a CollisionManager that is a sub-component of each MechModel. The collision manager maintains default collision behaviors among certain groups of collidable objects, while also allowing the user to override the default behaviors by setting override behaviors for specific component pairings.

4.5.1 Enabling collisions in code

Collisions can be enabled as either a default behavior between all bodies, a default behavior between certain groups of bodies, or a specific override behavior between individual pairs of bodies.

This section describes how to enable collisions in code. However, it is also possible to set some aspects of collision behavior interactively within the ArtiSynth GUI. See the section “Collision handling” in the ArtiSynth User Interface Guide.

The default collision behavior between all collidables can be controlled using the method

  setDefaultCollisionBehavior (enabled, mu);

where enabled is true or false depending on whether collisions are enabled, and mu is the coefficient of Coulomb (or dry) friction. The mu value is ignored if enabled is false. mu can also be left undefined by specifying a value less than 0, in which case the friction coefficient is inherited from the global friction value accessed by the methods

  setFriction (mu);
  double getFriction();

The default collision behavior can also be controlled using the method

  setDefaultCollisionBehavior (behavior);

where behavior is a CollisionBehavior object that specifies both enabled and mu, along with other, more detailed collision properties (see Section 4.5.3).

In addition, a default collision behavior can be set for generic groups of collidables using

  setDefaultCollisionBehavior (group0, group1, enabled, mu);
  setDefaultCollisionBehavior (group0, group1, behavior);

where group0 and group1 are static instances of the special Collidable subclass Collidable.Group that represents the groups described in Table 4.3. The groups Collidable.Rigid and Collidable.Deformable denote collidables that are rigid (such as RigidBody) or deformable (such as FEM models like FemModel3d). (If a collidable is deformable, then its isDeformable() method returns true.) The group Collidable.AllBodies denotes both rigid and deformable bodies, and Collidable.Self is used to enabled self-collision. which is described in greater detail in Section 4.5.4. mu again is ignored if enabled is false, and if mu is less than zero, the friction is undefined and inherited from the global value accessed using setFriction(mu) and getFriction().

Collidable group description
Collidable.Rigid rigid collidables (e.g., rigid bodies)
Collidable.Deformable deformable collidables (e.g, FEM models)
Collidable.AllBodies rigid and deformable collidables
Collidable.Self enables self-intersection for composite deformable collidables
Collidable.All rigid and deformable collidables and self-intersection
Table 4.3: Collision group types.

A call to one of the setDefaultCollisionBehavior() methods will override the effects of previous calls. So for instance, the code sequence

  setDefaultCollisionBehavior (true, 0);
  setDefaultCollisionBehavior (Collidable.Deformable, Collidable.Rigid, false, 0);
  setDefaultCollisionBehavior (true, 0.2);

will initially enable collisions between all bodies with a friction coefficient of 0, then disable collisions between deformable and rigid bodies, and finally re-enable collisions between all bodies with a friction coefficient of 0.2.

The default collision behavior between any pair of collidable groups can be queried using

  CollisionBehavior getDefaultCollisionBehavior (group0, group1);

For this call, group0 and group1 are restricted to the primary groups Collidable.Rigid, Collidable.Deformable, and Collidabe.Self, since individual behaviors are not maintained for the composite groups Collidable.AllBodies and Collidable.All.

In addition to default behaviors, overriding behaviors for individual collidables or pairs of collidables can be controlled using

  setCollisionBehavior (collidable0, collidable1, enabled, mu);
  setCollisionBehavior (collidable0, collidable1, behavior);

where collidable0 is an individual collidable component (such as a rigid body or FEM model), and collidable1 is either another individual component, or one of the groups of Table 4.3. As with default behaviors, mu is ignored if enabled is false, and if mu is less than zero, the friction is undefined and inherited from the global value accessed using setFriction(mu) and getFriction().

As an example of setting individual collision behaviors, the calls

  RigidBody bodA;
  FemModel3d femB;
  setCollisionBehavior (bodA, Collidable.Deformable, true, 0.1);
  setCollisionBehavior (femB, Collidable.AllBodies, true, 0.0);
  setCollisionBehavior (bodA, femB, false, 0.0);

will enable collisions between bodA and all deformable collidables (with friction 0.1), as well as femB and all deformable and rigid collidables (with friction 0.0), while specifically disabling collisions between bodA and femB.

The setCollisionBehavior() methods work by adding a CollisionBehavior object (Section4.5.3) to the collision manager as a sub-component. With setCollisionBehavior(collidable0,collidable1,behavior), the behavior object is created and supplied by the application. With setCollisionBehavior(enable,mu), the behavior object is created automatically and returned by the method. Once an override behavior has been specified, then it can be queried using

  getCollisionBehavior (collidable0, collidable1);

This method will return null if no override behavior for the pair in questions has been previously set using one of the setCollisionBehavior() methods.

Because behaviors are proper components, it is not permissible to add them to the collision manager twice. Specifically, the following will produce an error:

   CollisionBehavior behav = new CollisionBehavior();
   behav.setDrawIntersectionContours (true);
   mesh.setCollisionBehavior (col0, col1, behav);
   mesh.setCollisionBehavior (col2, col3, behav); // ERROR

However, if desired, a new behavior can be created from an existing one:

   CollisionBehavior behav = new CollisionBehavior();
   behav.setDrawIntersectionContours (true);
   mesh.setCollisionBehavior (col0, col1, behav);
   behav = new CollisionBehavior(behav);
   mesh.setCollisionBehavior (col2, col3, behav); // OK

To determine the collision behavior (default or override) that actually controls a specific pair of collidables, one may use the method

  getActingCollisionBehavior (collidable0, collidable1);

where collidable0 and collidable1 must both be specific collidable components and cannot be a group (such as Collidable.Rigid or Collidable.All). If one of the collidables is a compound collidable (Section 4.5.4), or has a collidability setting (Section 4.5.5) that prevents collisions, there may be no consistent acting behavior, in which case the method returns null.

Collision behaviors take priority over each other in the following order:

  1. 1.

    behaviors specified using setCollisionBehavior() involving two specific collidables.

  2. 2.

    behaviors specified using setCollisionBehavior() involving one specific collidable and a group of collidables (indicated by a Collidable.Group), with later specifications taking priority over earlier ones.

  3. 3.

    default behaviors specified using setDefaultCollisionBehavior().

An override behavior specified with setCollisionBehavior() can later be removed using

  clearCollisionBehavior (collidable0, collidable1);

and all override behaviors in a MechModel can be removed with

  clearCollisionBehaviors ();

Note that this latter call does not remove default behaviors specified with setDefaultCollisionBehavior().

Note: It is usually necessary to ensure that collisions are disabled between adjacent bodies connected by joints, since otherwise these would be forced into a state of permanent collision.

4.5.2 Example: Collision with a plane

Figure 4.4: JointedCollide model loaded into ArtiSynth.

A simple model illustrating collision between two jointed rigid bodies and a plane is defined in

  artisynth.demos.tutorial.JointedCollide

This model is simply a subclass of RigidBodyJoint that overrides the build() method to add an inclined plane and enable collisions between it and the two connected bodies:

1    public void build (String[] args) {
2
3       super.build (args);
4
5       bodyB.setDynamic (true);  // allow bodyB to fall freely
6
7       // create and add the inclined plane
8       RigidBody base = RigidBody.createBox ("base", 25, 25, 2, 0.2);
9       base.setPose (new RigidTransform3d (5, 0, 0, 0, 1, 0, -Math.PI/8));
10       base.setDynamic (false);
11       mech.addRigidBody (base);
12
13       // turn on collisions
14       mech.setDefaultCollisionBehavior (true, 0.20);
15       mech.setCollisionBehavior (bodyA, bodyB, false);
16    }

The superclass build() method called at line 3 creates everything contained in RigidBodyJoint. The remaining code then alters that model: bodyB is set to be dynamic (line 5) so that it will fall freely, and an inclined plane is created from a thin box that is translated and rotated and then set to be be non-dynamic (lines 8-11). Finally, collisions are enabled by setting the default collision behavior (line 14), and then specifically disabling collisions between bodyA and bodyB (line 15). As indicated above, the latter step is necessary because the joint would otherwise keep the two bodies in a permanent state of collision.

To run this example in ArtiSynth, select All demos > tutorial > JointedCollide from the Models menu. The model should load and initially appear as in Figure 4.4. Running the model (Section 1.5.3) will cause the jointed assembly to collide with and slide off the inclined plane.

4.5.3 Collision behaviors

As mentioned above, the CollisionBehavior objects described above can be used to control other aspects of the contact and collision beyond friction and enabling. In particular, a CollisionBehavior exports the following properties:

enabled

A boolean that determines if collisions are enabled.

friction

A double giving the coefficient of Coulomb friction, typically in the range [0,0.5]. The default value is 0. Setting friction to a non-zero value increases the simulation time, since extra constraints must be added to the system to accommodate the friction.

penetrationTol

A double controlling the amount of interpenetration that is permitted in order to ensure contact stability (see Section 4.6.4). If not specified, the system will inherit this property from the MechModel, which computes a default penetration tolerance based on the overall size of the model.

compliance

A double which adds a compliance (inverse stiffness) to the collision behavior, so that the contact has a “springiness”. The default value for this is 0 (no compliance). See Section 4.6.3.

damping

A double which, if compliance is non-zero, specifies a damping to accompany the compliant behavior. The default value is 0. When compliance is specified, it is usually necessary to set the damping to a non-zero value to prevent bouncing. See Section 4.6.3.

collisionPointTol

A double which, for contacts between rigid bodies, specifies a minimum distance between contact points that is used to reduce the number of contacts. If not explicitly specified, the system computes a default value for this based on the overall size of the model.

reduceConstraints

A boolean which, if true, indicates that the system should try to reduce the number of contacts between deformable bodies with limited degrees of freedom, so that the resulting contact problem is not ill-conditioned. The default value is false. See Section 4.6.3.

method

An instance of CollisionBehavior.Method that controls how the contact constraints for the collision response are generated. There are several methods available, and some are experimental. The more standard methods are:

Method Constraint generation
VERTEX_PENETRATION constraints generated from interpenetrating vertices
CONTOUR_REGION constraints generated from planes fit to each mesh contact region
INACTIVE no constraints generated

These are described in more detail in Section 4.6.1.

colliderType

An instance of CollisionManager.ColliderType that specifies the underlying mechanism used to determine the collision information between two meshes. The choice of collider may restrict which collision methods (described above) are allowed. Collider types available at present include:

Type Description
AJL_CONTOUR uses mesh intersection contour to find penetrating regions and vertices
TRI_INTERSECTION uses triangle intersections to find penetrating regions and vertices
SIGNED_DISTANCE uses a signed distance field to find penetrating vertices

These are described in more detail in Section 4.6.1.

In addition to the above, CollisionBehavior exports other properties that control the rendering of collisions (Section 4.6.5).

It should be noted that most collision behavior properties are also properties of the MechModel’s collision manager, which can be accessed in code using getCollisionManager() (or graphically via the navigation panel). Since collision behaviors are sub-components of the collision manager, properties set in the collision manager will be inherited by any behaviors for which they have not been explicitly set. This is the easiest way to specify behavior properties generally, as for example:

  CollisionManager cm = mech.getCollisionManager();
  cm.setReduceConstraints (true);

Some other properties, like penetrationTol, are properties not of the collision manager, but of the MechModel, and so can be set globally there.

To set collision properties in a more fine-grained manner, one can either call setDefaultCollisionBehavior() or setCollisionBehavior() with an appropriately set CollisionBehavior object:

  RigidBody bodA;
  CollisionBehavior behav = new CollisionBehavior (enabled, mu);
  behav.setPenetrationTol (0.001);
  setDefaultCollisionBehavior (Collidable.Deformable, Collidable.Rigid, behav);
  behav.setPenetrationTol (0.003);
  setCollisionBehavior (bodA, Collidable.Rigid, behav);

For behaviors that are already set, one may use getDefaultCollisionBehavior() or getCollisionBehavior() to obtain the behavior and then set the desired properties directly:

  RigidBody bodA;
  CollisionBehavior behav;
  behav = getDefaultCollisionBehavior (Collidable.Deformable, Collidable.Rigid);
  behav.setPenetrationTol (0.001);
  behav = getCollisionBehavior (bodA, Collidable.Rigid);
  behav.setPenetrationTol (0.003);

Note however that getDefaultCollisionBehavior() only works for Collidable.Rigid, Collidable.Deformable, and Collidable.Self, and that getCollisionBehavior() only works for a collidable pair that has been previously specified with setCollisionBehavior(). One may also use getActingCollisionBehavior() (described above) to obtain the behavior (default or otherwise) responsible for a specific pair of collidables, although in some instances no such single behavior exists and the method will then return null.

There are two constructors for CollisionBehavior:

  CollisionBehavior ();
  CollisionBehavior (boolean enable, double mu);

The first creates a behavior with the enabled property set to false and the other properties set to their default (generally inherited) values. The second creates a behavior with the enabled and friction properties explicitly specified by enabled and mu, and other properties set to their default values. If mu is less than zero or enabled is false, then the friction property is set to be inherited so that its value is controlled by the global friction property in the collision manager (and accessed using the MechModel methods setFriction(mu) and getFriction()).

4.5.4 Self-collision and collidable hierarchies

At present, ArtiSynth does not support the detection or handling of self-collision within single meshes. However, self-collision can still be effected by allowing a collidable to have multiple sub-collidables and then enabling collisions between some or all of these.

Any descendant component of a Collidable A which is itself Collidable is considered to be a sub-collidable of A. Certain types of components maintain sub-collidables by default. For example, some components (such as finite element models; Section 6) maintain a list of meshes in a child component list named meshes; these can be used to implement self-collision as described below. A collidable that contains sub-collidables is known as a compound collidable, and its isCompound() method will return true. Likewise, if a component is a sub-collidable, then its getCollidableAncestor()) method will return its nearest collidable ancestor.

A collidable does not need to be an immediate child component of a collidable A in order to be a sub-collidable of A; it need only be a descendant of A.

At present, collidable hierarchies are assumed to have a depth no greater than one (i.e., no sub-collidable is itself a compound collidable), and also sub-collidables are assumed to have the same type (rigid or deformable) as the ancestor.

Figure 4.5: A collection of collidable components, where A possesses sub-collidables A1, A2, and A3, B is solitary, and C possesses sub-collidables C1 and C2. Internal collisions are enabled among those sub-collidables which are shaded gray.

In general, an ArtiSynth model will contain a collection of collidables, some of which are compound and others which are solitary (Figure 4.5). Within a collection of collidables:

  • Actual collisions happen only between leaf collidables; compound collidables are used only for grouping purposes.

  • Leaf collidables are also instances of the sub-interface CollidableBody, which provides the additional information needed to actually determine collisions and compute the response (Section 4.6).

  • By default, the sub-collidables of a compound component A will only collide among themselves if self-collision is specified for A (via either a default or override collision behavior). If self-collision is specified for A, then collisions may occur only among those sub-collidables for which internal collisions are enabled. Internal collisions are enabled for a collidable if its collidable property (Section 4.5.5) is set to either ALL or INTERNAL.

  • Self-collision is also only possible among the sub-collidables of A if A is itself deformable; i.e., its isDeformable() method returns true.

  • Sub-collidables may collide with collidables outside their hierarchy if their collidable property is set to either ALL or EXTERNAL.

  • Collision among specific pairs of sub-collidables may also be explicitly enabled or disabled with an override behavior set using one of the setCollisionBehavior() methods.

  • Specifying a collision behavior among two compound collidables A and B which are not within the same hierarchy will cause that behavior to be specified among all sub-collidables of A and B whose collidable property enables the collision.

Subject to the above conditions, self-collisions can be enabled among the sub-collidables of all deformable collidables by calling

   setDefaultCollisionBehavior (Collidable.DEFORMABLE, Collidable.SELF, true, mu);

or, for a specific collidable C, by calling either

   setDefaultCollisionBehavior (C, Collidable.SELF, true, mu);
   ... OR ...
   setDefaultCollisionBehavior (C, C, true, mu);

For a more complete example, refer to Figure 4.5, assume that components A, B and C are deformable, and that self-collision is allowed among those sub-collidables which are shaded gray (A1 and A2 for A, B1 and B2 for B). Then:

   // Set default collision among deformable components with friction = 0.2:
   setDefaultCollisionBehavior (
      Collidable.DEFORMABLE, Collidable.DEFORMABLE, true, 0.2);
   // Collisions are now enabled between A1, A2, and A3 and each of B, C1, and
   // C2, and between B and C1 and C2, but not among A1, A2, and A3 or C1 and C2.
   // Enable self-collision between A1 and A2 and B1 and B2 with friction = 0:
   setDefaultCollisionBehavior (Collidable.DEFORMABLE, Collidable.SELF, true, 0);
   // Specifically disable collision between B and A3:
   setCollisionBehavior (B, A3, false);
   // Specifically enable collision between A3 and C with friction = 0.3:
   setCollisionBehavior (A3, C, true, 0.3);
   // This behavior will be applied between A3 and each of C1 and C2.
   // Disable self-collision within A:
   setCollisionBehavior (A, A, false);
   // This will disable all self-collisions among A1, A2 and A3.

4.5.5 Collidability

Each collidable component maintains a collidable property (which can be queried using getCollidable()) which specifically enables or disables the ability of that collidable to collide with other collidables.

The collidable property value is of the enumerated type Collidable.Collidability, which has four possible settings:

OFF

All collisions disabled: the collidable will not collide with anything.

INTERNAL

Internal (self) collisions enabled: the collidable may only collide with other Collidables with which it shares a common ancestor.

EXTERNAL

External collisions enabled: the collidable may only collide with other Collidables with which it does not share a common ancestor.

ALL

All collisions (both self and external) enabled: the collidable may collide with any other Collidable.

Note that collidability only enables collisions. In order for collisions to actually occur between two collidables, a default or override collision behavior must also be specified for them in the MechModel.

4.5.6 Collision response

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. Applications can track this 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 sub-collidables.

If desired, applications can also instantiate responses themselves and add them to the collision manager:

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

However, as with 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,

   setCollisionResponse (collidable0, collidable1, response);
   setCollisionResponse (collidable0, collidable1);
   getCollisionResponse (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.

Information that can be queried by a CollisionResponse component includes whether or not the collidable is in collision, contact forces acting on the vertices of the colliding meshes, the penetration regions of each mesh associated with the collision, and the underlying CollisionHandler objects that maintain the contact constraints between each colliding mesh. This information may be queried with the following methods:

  // Queries if the collidables associated with this response are in contact
  boolean inContact();
  // Returns a map specifying the vertex-specific contact forces acting on all
  // the deformable bodies associated with the first or second collidable, as
  // specified by cidx=0 or cidx=1.
  Map<Vertex3d,Vector3d> getContactForces(int cidx);
  // Returns a map specifying the vertex-specific contact pressures acting on all
  // the deformable bodies associated with the first or second collidable, as
  // specified by cidx=0 or cidx=1.
  Map<Vertex3d,Vector3d> getContactForces(int cidx);
  // Returns a list of all the penetration regions on either the first
  // or second collidable, as specified by cidx=0 or cidx=1. Penetration
  // regions are available only if the collision manager’s collider
  // type is set to AJL_CONTOUR.
  ArrayList<PenetrationRegion> getPenetrationRegions(int cidx);
  // Returns the CollisionHandlers for all currently active collisions
  // associated with the collidables of this response. Note that the
  // body ordering in the handlers may be reversed.
  ArrayList<CollisionHandler> getHandlers();

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);

4.5.7 Nested MechModels

It is possible in ArtiSynth for one MechModel to contain other nested MechModels within its component hierarchy. This raises the question of how collisions within the nested models are controlled. The general rule for this is the following:

The collision behavior for two collidables colA and colB is determined by whatever behavior (either default or override) is specified by the lowest MechModel in the component hierarchy that contains both colA and colB.

Figure 4.6: A MechModel containing collidables B1 and B2, nested within another containing collidables A1 and A2.

For example, consider Figure 4.6 where we have a MechModel (mechB) containing collidables B1 and B2, nested within another MechModel (mechA) containing collidables A1 and A2. Then consider the following code fragment:

   mechB.setDefaultCollisionBehavior (true, 0.2);
   mechA.setCollisionBehavior (B1, Collidable.AllBodies, true, 0.0);
   mechA.setCollisionBehavior (B1, A1, false);
   mechA.setCollisionBehavior (B1, B2, false); // Error!

The first line enables default collisions within mechB (with \mu=0), controlling the interaction between B1 and B2. However, collisions are still disabled within mechA, meaning A1 and A2 will not collide either with each other or with B1 or B2. The second line enables collisions between B1 and any other body within mechA (i.e., A1 and A2), while the third line disables collisions between B1 and A1. Finally, the fourth line results in an error, since B1 and B2 are both contained within mechB and so their collision behavior cannot be controlled from mechA.

While it is not legal to specify a specific behavior for collidables contained a MechModel from a higher level MechModel, it is legal to create collision response components for the same pair within both models. So the following code fragment would be allowed and would create response components in both mechA and mechB:

   mechB.setCollisionResponse (B1, B2);
   mechA.setCollisionResponse (B1, B2);