Skip to main content

Joint constraints

In practice, there are two main ways of modeling joints:

  • Reduced-coordinates approach.
  • Constraints-based approach.

Reduced-coordinates approach

The reduced-coordinates approach encodes the reduction of DOF directly into the equations of motion. For example, a 3D rigid-body attached to the ground with a revolute joint will have its position encoded by only one variable: the rotation angle. Therefore, integrating its motion only changes this one variable and doesn't need additional forces or mathematical constraints to be generated. The clear advantage is that there is no way for the physics engine to apply any motion other than that single rotation to this body, meaning there is no way the body shifts to a position that is not realistic, even if the dynamics solver does not converge completely.

Rapier implements this approach through MultibodyJointSet, where each joint is attached to its relevant rigid-bodies identified by their handle.

Constraints-based approach

The constraints-based approach (or full-coordinates approach) is the most commonly available approach on other physics engines for video-games and animations. Here, a 3D rigid-body attached to the ground with a revolute joint will still have its position encoded by 6 variables (3 for translations and 3 for rotations) just like any rigid-body without a joint. Then the integrator will add mathematical constraints to the dynamic system to ensure forces are applied to simulate the reduction of the number of DOF as imposed by the joints. In practice, this means that the rigid-body will break the joint constraint if the constraint solver does not converge completely.

Rapier implements this approach through ImpulseJointSet, where each joint is attached to two distinct rigid-bodies identified by their rigid-body handles.


More generally, the reduced-coordinates approach favors accuracy while the constraints-based approach favors versatility.

The following table compares the advantages and limitations of both approaches:

Reduced-coordinates approachConstraints-based approach
Joints cannot be violated at all.Joints can be violated if the solver does not converge.
Moderately large time-steps are possible.Moderately large time-steps may make the simulation explode.
Large assemblies are stable.Large assemblies easily break without a large number of solver iterations.
Adding/removing a joint is slow.Adding/removing a joint is fast.
Joint forces are never computed explicitly, thus cannot be retrieved.Joint forces are always computed and can be retrieved.
Topological restriction: bodies must be linked following a tree structure.The link between bodies can form any graph.

The following schematics illustrate a configuration that can be simulated by a multibody (left assembly with a tree structure), and one that cannot (right assembly with a graph structure). The assembly on the left models a SCARA robotic arm with 3 rotational DOF (due to three revolute joints) and 1 translational DOF (due to one prismatic joint). The assembly on the right models a necklace with five pearls. It has a total of 15 rotational DOF (due to five ball joints):


Tree-like assembly


"Which approach should I use?"

The choice of approach depends on the application. For robotics, the reduced-coordinates approach is generally preferred because of its accuracy and ease of use, e.g., for control, inverse kinematics, etc.


Video games traditionally favor the constraints-based approach since most existing physics libraries implement only this. Moreover if joint assemblies are small, and joints are frequently added and removed, the constraints-based approach will be more efficient. Some other physics libraries implement the reduced-coordinates approach as well but often using the Featherstone algorithm which is extremely unstable in practice.


Simulating closed loops like for a necklace cannot be achieved with the reduced-coordinates approach only. However, it is possible to combine both approaches by using joint constraints only to close the loops. Refer to the last section for details.

Multibodies

Multibodies implement the reduced-coordinates approach. A multibody is a set of multibody links attached together by a multibody joint.

Creating a multibody

The API to create a Multibody joint is similar to creating an Impulse Joint, refer to Joints, but insert those into a MultibodyJointSet.

See Rapier's example for a demonstration of both approaches.

Combining both

A joint constraint geometry is completely configured at its creation, and added to the joint set by the joint_set.insert(&mut bodies, body1, body2, joint, true) method by specifying the handles of the bodies the joint is attached to.

Combining multibodies and joint constraints is a useful way of combining the stability of multibodies with the flexibility of joint constraints. Indeed, one of the most appealing features of a multibody is its stability and ease of use (especially for robotics). However its greatest weakness is its inability to represent assemblies that do not match a tree structure, i.e., an articulated body composed of graph-like assembly of solids (each graph node being a solid and each graph edge being an articulation) cannot be simulated by a multibody. A common approach is thus to:

  1. Define a multibody from a spanning-tree of the graph.
  2. Create joint constraints for each articulation missing from this multibody to complete the graph. Those joint constraints are therefore attached to two multibody links. They are often called "loop-closing constraints" since they close the loops of the assembly's graph structure.

The following shows an example of combination of multibodies and joint constraints for the simulation of a necklace. It is composed of 5 pearls forming a single loop attached together by 5 ball joints. Since such a loop cannot be simulated by a multibody, we first start to create 5 multibody links attached together with 4 BallJoint. Only 4 joints can be added here since a 5th would close the loop. The 5th joint that closes the loop must be modeled as a joint constraint, here a BallConstraint between the first and the last link:

Loop-closing constraint