Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multibody joints doc wip #132

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 34 additions & 96 deletions docs/user_guides/templates/joint_constraints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,70 +7,38 @@ sidebar_label: Joint constraints
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

One of the most appealing features of a physics engine is to simulate articulations. Articulations, aka. _joints_, allow
the restriction of the motion of one body relative to another. For example, one well-known joint is the ball-in-socket
joint also known as the ball joint: it allows one object to rotate freely with regard to the other but not to translate.
This is typically used to simulate shoulders of a ragdoll.

## Fundamental concepts

Joints can be modeled in various ways but let's talk about the concept of **Degrees Of Freedom (DOF)** first. In 3D, a
rigid-body is capable of translating along the 3 coordinates axes $\mathbf{x}$, $\mathbf{y}$ and $\mathbf{z}$, and to
rotate along those three axes as well. Therefore, a rigid-body is said to have **3 translational DOF** and **3 rotational DOF**.
We can also say a 3D rigid-body has a total of **6 DOF**. The 2D case is similar but with less possibilities of movements:
a 2D rigid-body has 2 translational DOF and only 1 rotational DOF (which forms a total of 3 DOF). The number of **relative DOF**
of a body wrt. another body is the number of possible relative translations and rotations.
The goal of a joint is to reduce the number of DOF a body has. For example, the aforementioned ball joint removes all relative
translations between two bodies. Therefore, it allows only the 3 rotational DOF in 3D simulations or the 1 rotational DOF
in 2D simulations. Other joints exist allowing other combinations of relative DOF. Note that because there are less possible
motions in 2D, some joints are only defined in 3D. This is illustrated by empty cells in the following table for joints
that are not defined in 2D:

| Joint | Allowed DOF in 2D | Allowed DOF in 3D |
|-----------------|-------------------|---------------|
| _Fixed joint_ | None | None
| _Free joint_ | All | All
| _Prismatic joint_ | 1 Translation | 1 Translation |
| _Revolute joint_ | 1 Rotation | 1 Rotation |
| _Ball joint_ | 1 Rotation | 3 Rotations |
| _Cartesian joint_ | 2 Translations | 3 Translations |
| _Planar joint_ | | 2 Translations + 1 Rotation |
| _Cylindrical joint_ | | 1 Translation + 1 Rotation (along the same axis) |
| _Pin-slot joint_ | | 1 Translation + 1 Rotation (along different axes) |
| _Rectangular joint_ | | 2 Translations |
| _Universal joint_ | | 2 Rotations |

In 3D, a special _Helical joint_ also exists: it allows only one DOF which is a bit special as it is the combination of
a rotation and a translation. In other words, a body attached to the ground by an helical joint will only be able to translate
and rotate simultaneously: any translation induce automatically a rotation and vice-versa.
In practice, there are two main ways of modeling joints:

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

In practice, there are two main ways of modeling joints:
## Reduced-coordinates approach

1. The **reduced-coordinates approach** encodes the reduction of DOF directly into the equations of motion. For example,
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.
2. The **constraints-based approach** (or full-coordinates approach) is the most commonly available approach on other

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.

This description shows only one aspect of the difference between the reduced-coordinates approach and the constraints-based
approach. More generally, the reduced-coordinates approach favors accuracy while the constraints-based approach favors
versatility.
Rapier implements this approach through `ImpulseJointSet`,
where each joint is attached to two distinct rigid-bodies identified by their rigid-body handles.

:::warning
Currently, Rapier only implements the constraints-based approach. A reduced-coordinates implementation will be added to
a future version of Rapier.
:::
-------

<!--
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:

Expand All @@ -90,7 +58,7 @@ right models a necklace with five pearls. It has a total of 15 rotational DOF (d

<center>
<br/>
![Tree-like assembly](img/assembly_examples.svg)
![Tree-like assembly](/img/assembly_examples.svg)
<br/>
</center>

Expand All @@ -112,8 +80,8 @@ it is possible to combine both approaches by using joint constraints only to clo
for details.
:::

The use of the reduced-coordinates approach is detailed in the [multibodies](./advanced_collision_detection.mdx#multibodies) section and demonstrated by
the [Multibody joints](/all_examples3/?multibody) demo. The constraints-based approach is detailed in the
The use of the reduced-coordinates approach is detailed in the [multibodies](./joint_constraints.mdx#multibodies) section and demonstrated by
the Multibody joints example from the [demo](/demos3d). The constraints-based approach is detailed in the
[joint constraints](./advanced_collision_detection.mdx#joint-constraints) section and demonstrated by the [Joint constraints](/all_examples3/?constraints) demo.

## Multibodies
Expand Down Expand Up @@ -246,31 +214,14 @@ let multibody_handle = body_set.insert(multibody);
</div>
</div>


The following table summarizes the types corresponding to the joints mentioned at the beginning of this chapter that
can be used for the `MultibodyDesc`:

| Joint name | Multibody joint type on **nphysics** |
|------------|---------|
| _Fixed joint_ | [`FixedJoint`](/rustdoc/nphysics3d/joint/struct.FixedJoint.html)|
| _Prismatic joint_ | [`PrismaticJoint`](/rustdoc/nphysics3d/joint/struct.PrismaticJoint.html) |
| _Revolute joint_ | [`RevoluteJoint`](/rustdoc/nphysics3d/joint/struct.RevoluteJoint.html) |
| _Ball joint_ | [`BallJoint`](/rustdoc/nphysics3d/joint/struct.BallJoint.html) |
| _Cartesian joint_ | [`CartesianJoint`](/rustdoc/nphysics3d/joint/struct.CartesianJoint.html) |
| _Planar joint_ | [`PlanarJoint`](/rustdoc/nphysics3d/joint/struct.PlanarJoint.html) |
| _Cylindrical joint_ | [`CylindricalJoint`](/rustdoc/nphysics3d/joint/struct.CylindricalJoint.html) |
| _Pin-slot joint_ | [`PinSlotJoint`](/rustdoc/nphysics3d/joint/struct.PinSlotJoint.html) |
| _Rectangular joint_ | [`RectangularJoint`](/rustdoc/nphysics3d/joint/struct.RectangularJoint.html) |
| _Universal joint_ | [`UniversalJoint`](/rustdoc/nphysics3d/joint/struct.UniversalJoint.html) |

:::note
The first multibody link of a multibody is necessarily attached to an implicit fixed ground. Note however that
"attached" is a bit misleading here. Indeed if `joint` is set to an instance of `FreeJoint`, then this first
multibody link will have all the possible degrees of freedom, making it completely free to perform any movement.
:::

:::warning
The `FreeJoint` can be used only by the first link of the multibody otherwise, the creation of the
The `FreeJoint` can be used only by the first link of the multibody, otherwise, the creation of the
multibody will panic.
:::

Expand Down Expand Up @@ -305,6 +256,11 @@ You may refer to the [code](https://github.com/rustsim/nphysics/blob/master/exam
The removal of a multibody from the body set uses the same method as the removal of a rigid-body: `body_set.remove(handle)`.
It is not possible to remove a single link of a multibody without removing the whole multibody altogether.

<hidden>

<technote>This part should belong in a GenericJoint section within joints.mdx.</technote>


### Multibody joint limits and motors
It is often desirable to limit the amplitude of movement a multibody link can have with regard to its parent. For
example we might want to limit the minimum and maximum value for the DOF of a prismatic joint in order to simulate a
Expand Down Expand Up @@ -352,37 +308,15 @@ For the moment, joint motors are only implemented for multibody joints with DOF
:::note
Joints with no angular DOF will not have the methods related to the angular motors. Similarly, joints with no linear DOF will not have the methods related to the linear motors. Joints with several angular or linear DOF will have those methods with an index appended to their name, e.g., the `.enable_angular_motor_1()` method of an universal joint will enable a motor for its first angular DOF.
:::
-->

## Joint constraints
Joint constraints implement the constraints-based approach. The following table summarizes the types corresponding to the joints mentioned at the beginning of this chapter:

| Joint name | Joint constraint type on **Rapier** |
|------------|---------|
| _Fixed joint_ | `dynamics::FixedJoint` |
| _Prismatic joint_ | `dynamics::PrismaticJoint` |
| _Revolute joint_ | `dynamics::RevoluteJoint` |
| _Ball joint_ | `dynamics::BallJoint` |

<!--
| _Cartesian joint_ | `dynamics::CartesianJoint` |
| _Planar joint_ | `dynamics::PlanarJoint` |
| _Cylindrical joint_ | `dynamics::CylindricalJoint` |
| _Pin-slot joint_ | `dynamics::PinSlotJoint` |
| _Rectangular joint_ | `dynamics::RectangularJoint` |
| _Universal joint_ | `dynamics::UniversalJoint` |
-->
</hidden>

:::note
Other joints will be added in the future.
:::
## Combining joint constraints

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 both
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.
Expand All @@ -391,11 +325,15 @@ Combining multibodies and joint constraints is a useful way of combining the sta
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:

<hidden>

<!--
<center>
![Loop-closing constraint](img/loop_closing_joint.svg)
![Loop-closing constraint](/img/loop_closing_joint.svg)
</center>
-->
</hidden>

:::note
Note that using the world to create multibody links removes any risk of inadvertently creating multibody links attached in such a way that they would form anything but a tree structure.
:::
-->
:::
36 changes: 20 additions & 16 deletions docs/user_guides/templates/joints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,26 @@ in 2D simulations. Other joints exist allowing other combinations of relative DO
motions in 2D, some joints are only defined in 3D. This is illustrated by empty cells in the following table for joints
that are not defined in 2D:

| Joint | Allowed DOF in 2D | Allowed DOF in 3D |
|-----------------|-------------------|---------------|
| _Fixed joint_ | None | None
| _Free joint_ | All | All
| _Prismatic joint_ | 1 Translation | 1 Translation |
| _Revolute joint_ | 1 Rotation | 1 Rotation |
| _Spherical joint_ | 1 Rotation | 3 Rotations |
| _Cartesian joint_ | 2 Translations | 3 Translations |
| _Planar joint_ | | 2 Translations + 1 Rotation |
| _Cylindrical joint_ | | 1 Translation + 1 Rotation (along the same axis) |
| _Pin-slot joint_ | | 1 Translation + 1 Rotation (along different axes) |
| _Rectangular joint_ | | 2 Translations |
| _Universal joint_ | | 2 Rotations |

Currently, Rapier supports **fixed**, **spherical**, **prismatic**, and **revolute** joints. Joints must be inserted into an
`ImpulseJointSet`. Each joint is attached to two distinct rigid-bodies identified by their rigid-body handles.
| Joint | Allowed DOF in 2D | Allowed DOF in 3D | Rapier support
|-----------------|-------------------|---------------|---------------|
| _Fixed joint_ | None | None| Yes |
| _Free joint_ | All | All | Through GenericJoint |
| _Prismatic joint_ | 1 Translation | 1 Translation | Yes |
| _Revolute joint_ | 1 Rotation | 1 Rotation | Yes |
| _Spherical joint_ | 1 Rotation | 3 Rotations | Yes|
| _Cartesian joint_ | 2 Translations | 3 Translations | Through GenericJoint |
| _Planar joint_ | | 2 Translations + 1 Rotation | Through GenericJoint |
| _Cylindrical joint_ | | 1 Translation + 1 Rotation (along the same axis) | Through GenericJoint |
| _Pin-slot joint_ | | 1 Translation + 1 Rotation (along different axes) | Through GenericJoint |
| _Rectangular joint_ | | 2 Translations | Through GenericJoint |
| _Universal joint_ | | 2 Rotations | Through GenericJoint |

In 3D, a special _Helical joint_ also exists: it allows only one DOF which is a bit special as it is the combination of
a rotation and a translation. In other words, a body attached to the ground by an helical joint will only be able to translate
and rotate simultaneously: any translation induce automatically a rotation and vice-versa.

Joints must be inserted into either an `ImpulseJointSet` or a `MultibodyJointSet`.
The difference between those is explained in [next section](./joint_constraints.mdx).

## Fixed joint
A fixed joint ensures that two rigid-bodies don't move relative to each other. Fixed joints are characterized by
Expand Down
4 changes: 4 additions & 0 deletions sidebar_docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ let template = {
'user_guides/templates_injected/rigid_bodies',
'user_guides/templates_injected/colliders',
'user_guides/templates_injected/joints',
'user_guides/templates_injected/joint_constraints',
'user_guides/templates_injected/character_controller',
'user_guides/templates_injected/scene_queries',
'user_guides/templates_injected/advanced_collision_detection',
Expand All @@ -31,6 +32,7 @@ let specialized_guides = {
'user_guides/rust/rigid_bodies',
'user_guides/rust/colliders',
'user_guides/rust/joints',
'user_guides/rust/joint_constraints',
'user_guides/rust/character_controller',
'user_guides/rust/scene_queries',
'user_guides/rust/advanced_collision_detection',
Expand All @@ -47,6 +49,7 @@ let specialized_guides = {
'user_guides/bevy_plugin/rigid_bodies',
'user_guides/bevy_plugin/colliders',
'user_guides/bevy_plugin/joints',
'user_guides/bevy_plugin/joint_constraints',
'user_guides/bevy_plugin/character_controller',
'user_guides/bevy_plugin/scene_queries',
'user_guides/bevy_plugin/advanced_collision_detection',
Expand All @@ -57,6 +60,7 @@ let specialized_guides = {
'user_guides/javascript/rigid_bodies',
'user_guides/javascript/colliders',
'user_guides/javascript/joints',
'user_guides/javascript/joint_constraints',
'user_guides/javascript/character_controller',
'user_guides/javascript/scene_queries',
'user_guides/javascript/advanced_collision_detection_js',
Expand Down
Binary file added static/img/assembly_examples.afdesign
Binary file not shown.
Loading
Loading