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 objects, then one needs to be able to easily specify the characteristics of up to 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.
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
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
The default collision behavior can also be controlled using the method
In addition, a default collision behavior can be set for generic groups of collidables using
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.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|
A call to one of the setDefaultCollisionBehavior() methods will override the effects of previous calls. So for instance, the code sequence
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
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
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
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
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
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:
behaviors specified using setCollisionBehavior() involving two specific collidables.
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.
default behaviors specified using setDefaultCollisionBehavior().
An override behavior specified with setCollisionBehavior() can later be removed using
and all override behaviors in a MechModel can be removed with
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.
A simple model illustrating collision between two jointed rigid bodies and a plane is defined in
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:
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.
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:
A boolean that determines if collisions are enabled.
A double giving the coefficient of Coulomb friction, typically in the range . 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.
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.
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.
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.
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.
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.
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:
|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.
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:
|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:
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:
For behaviors that are already set, one may use getDefaultCollisionBehavior() or getCollisionBehavior() to obtain the behavior and then set the desired properties directly:
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:
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()).
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.
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.
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
or, for a specific collidable C, by calling either
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:
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:
All collisions disabled: the collidable will not collide with anything.
Internal (self) collisions enabled: the collidable may only collide with other Collidables with which it shares a common ancestor.
External collisions enabled: the collidable may only collide with other Collidables with which it does not share a common ancestor.
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.
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():
An alternate version of setCollisionResponse() creates the response component internally and returns it:
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:
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:
However, as with behaviors, the same response cannot be added to an application twice:
The complete set of methods for managing collision responses are similar to those for behaviors,
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:
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
and then used in some runtime code as follows:
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:
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.
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:
The first line enables default collisions within mechB (with ), 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: