Skip to content

Commit 4b1ae0f

Browse files
committed
perf: use CRBA + backslash for forward dynamics (34μs → 5.4μs)
1 parent e49ad41 commit 4b1ae0f

2 files changed

Lines changed: 27 additions & 28 deletions

File tree

docs/src/man/benchmarks.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,34 @@ This page compares the performance of ModernRoboticsBook.jl against [Pinocchio](
88
|----------|----------------------:|---------------:|--------------------:|-------------:|
99
| Forward kinematics | 0.31 μs | **0.30 μs** | 0.86 μs | **~3x faster** |
1010
| Jacobian | 0.39 μs | **0.36 μs** | 1.0 μs | **~3x faster** |
11-
| Inverse dynamics | 3.68 μs || 0.64 μs | ~6x slower |
12-
| Mass matrix | 22.2 μs | | 0.65 μs | ~34x slower |
13-
| Gravity forces | 3.73 μs || 0.48 μs | ~8x slower |
14-
| Forward dynamics | 34.2 μs || 2.82 μs | ~12x slower |
11+
| Inverse dynamics | 3.81 μs || 0.64 μs | ~6x slower |
12+
| Mass matrix (CRBA) | 1.11 μs | **0.91 μs** | 0.65 μs | ~1.4x slower |
13+
| Gravity forces | 3.80 μs || 0.48 μs | ~8x slower |
14+
| Forward dynamics | 5.38 μs || 2.82 μs | ~2x slower |
1515

1616
*Measured on Apple M2 (16 GB), Julia 1.12, Python 3.13. Julia timings are median values from BenchmarkTools.jl.*
1717

1818
## Analysis
1919

2020
Forward kinematics and Jacobian computation are **~3x faster than Pinocchio** thanks to Julia's StaticArrays, which eliminate heap allocations for small fixed-size matrices (4×4, 6×6). The in-place variants (`forward_kinematics_space!`, `jacobian_space!`, etc.) achieve **zero allocations**.
2121

22-
The remaining gap in dynamics functions comes from algorithmic differences:
22+
The remaining gap in dynamics functions comes from allocation overhead in the RNEA (inverse dynamics) implementation, which uses heap-allocated workspace arrays. The algorithmic gap has been largely closed:
2323

2424
### Algorithms
2525

26-
- **Mass matrix**: ModernRoboticsBook.jl calls `inverse_dynamics` *n* times with unit accelerations (the textbook approach). Pinocchio uses the **Composite Rigid Body Algorithm (CRBA)**, which computes the full matrix in a single pass.
27-
- **Forward dynamics**: ModernRoboticsBook.jl explicitly forms and inverts the mass matrix. Pinocchio uses the **Articulated Body Algorithm (ABA)**, which solves for joint accelerations in O(n) without forming the mass matrix.
26+
- **Mass matrix**: Uses the **Composite Rigid Body Algorithm (CRBA)**, which computes the full matrix in a single backward pass over composite spatial inertias — the same algorithm as Pinocchio. The in-place variant achieves zero allocations and is within ~1.4x of Pinocchio.
27+
- **Forward dynamics**: Computes `M \ (τ - RNEA(q, dq, 0, g, F))` using CRBA for the mass matrix and a single RNEA call for the bias forces. Pinocchio uses the **Articulated Body Algorithm (ABA)**, which solves for joint accelerations in O(n) without forming the mass matrix.
28+
- **Inverse dynamics / gravity forces**: Uses the Recursive Newton-Euler Algorithm (RNEA), the same O(n) algorithm as Pinocchio. The ~6x gap is due to allocation overhead in the current implementation.
2829

2930
### Allocations
3031

3132
| Function | Allocations | Memory | In-place |
3233
|----------|------------:|-------:|---------:|
3334
| Forward kinematics | 2 | 208 B | **0 (0 B)** |
3435
| Jacobian | 2 | 384 B | **0 (0 B)** |
35-
| Inverse dynamics | 186 | 13.4 KiB ||
36-
| Mass matrix | 1,114 | 80.7 KiB | |
37-
| Forward dynamics | 1,702 | 125.6 KiB ||
36+
| Inverse dynamics | 188 | 13.6 KiB ||
37+
| Mass matrix (CRBA) | 9 | 4.6 KiB | **0 (0 B)** |
38+
| Forward dynamics | 205 | 18.8 KiB ||
3839

3940
### When does this matter?
4041

src/ModernRoboticsBook.jl

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,11 +1715,16 @@ Computes forward dynamics in the space frame for an open chain robot.
17151715
\\ddot{\\theta} = M(\\theta)^{-1} \\left[ \\tau - c(\\theta,\\dot{\\theta}) - g(\\theta) - J^T(\\theta) \\mathcal{F}_{\\text{tip}} \\right]
17161716
```
17171717
1718-
where ``M`` is the [mass matrix](@ref mass_matrix), ``c`` captures
1719-
[velocity-dependent (Coriolis/centrifugal) forces](@ref velocity_quadratic_forces),
1720-
``g`` captures [gravitational forces](@ref gravity_forces), and the last term
1721-
captures [end-effector contact forces](@ref end_effector_forces). Each of these
1722-
terms is computed via [`inverse_dynamics`](@ref) with appropriate inputs.
1718+
where ``M`` is the [mass matrix](@ref mass_matrix) (computed via CRBA), and the
1719+
right-hand side is computed via a single [`inverse_dynamics`](@ref) call with zero
1720+
accelerations that captures velocity-dependent, gravitational, and tip wrench forces.
1721+
1722+
!!! details "Educational note"
1723+
The textbook computes forward dynamics by explicitly forming ``M^{-1}`` and calling
1724+
[`inverse_dynamics`](@ref) separately for Coriolis, gravity, and tip wrench terms.
1725+
This implementation combines these into a single RNEA call and uses `M \\ b` (LU
1726+
factorization) instead of explicit inversion, which is both faster and more
1727+
numerically stable.
17231728
17241729
# Arguments
17251730
- `joint_positions`: the ``n``-vector of joint variables.
@@ -1753,27 +1758,20 @@ function forward_dynamics(
17531758
spatial_inertias::AbstractVector,
17541759
screw_axes::AbstractMatrix,
17551760
)
1756-
LA.inv(mass_matrix(joint_positions, link_frames, spatial_inertias, screw_axes)) * (
1757-
joint_torques - velocity_quadratic_forces(
1761+
# Use the explicit M⁻¹(τ - c - g - J^T F_tip) approach with CRBA for the mass matrix
1762+
mass = mass_matrix(joint_positions, link_frames, spatial_inertias, screw_axes)
1763+
tau_rhs =
1764+
joint_torques - inverse_dynamics(
17581765
joint_positions,
17591766
joint_velocities,
1760-
link_frames,
1761-
spatial_inertias,
1762-
screw_axes,
1763-
) - gravity_forces(
1764-
joint_positions,
1767+
zeros(length(joint_positions)),
17651768
gravity,
1766-
link_frames,
1767-
spatial_inertias,
1768-
screw_axes,
1769-
) - end_effector_forces(
1770-
joint_positions,
17711769
tip_wrench,
17721770
link_frames,
17731771
spatial_inertias,
17741772
screw_axes,
17751773
)
1776-
)
1774+
return mass \ tau_rhs
17771775
end
17781776

17791777
"""

0 commit comments

Comments
 (0)