Skip to content

Commit 5d6a533

Browse files
committed
feat: export textbook algorithm variants (mass_matrix_rnea, forward_dynamics_rnea)
1 parent 6e05f03 commit 5d6a533

3 files changed

Lines changed: 95 additions & 16 deletions

File tree

docs/src/lib/dynamics.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ Modules = [ModernRoboticsBook]
1010
Pages = ["ModernRoboticsBook.jl"]
1111
Filter = t -> nameof(t) in [
1212
:ad, :inverse_dynamics, :inverse_dynamics!, :mass_matrix, :mass_matrix!,
13-
:velocity_quadratic_forces, :gravity_forces, :end_effector_forces,
14-
:forward_dynamics, :euler_step,
15-
:inverse_dynamics_trajectory, :forward_dynamics_trajectory,
13+
:mass_matrix_rnea, :velocity_quadratic_forces, :gravity_forces,
14+
:end_effector_forces, :forward_dynamics, :forward_dynamics_rnea,
15+
:euler_step, :inverse_dynamics_trajectory, :forward_dynamics_trajectory,
1616
]
1717
```

docs/src/man/benchmarks.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,16 @@ This page compares the performance of ModernRoboticsBook.jl against [Pinocchio](
44

55
## Results
66

7-
| Function | ModernRoboticsBook.jl | In-place (`!`) | Pinocchio 3.9 (C++) | vs Pinocchio |
7+
| Function | MRB.jl | In-place (`!`) | Pinocchio 3.9 (C++) | vs 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** |
1111
| Inverse dynamics (RNEA) | 1.15 μs | **1.0 μs** | 0.64 μs | ~1.6x slower |
1212
| Mass matrix (CRBA) | 1.14 μs | **0.91 μs** | 0.65 μs | ~1.4x slower |
13+
| Mass matrix (`mass_matrix_rnea`) | 6.79 μs ||||
1314
| Gravity forces | 1.19 μs || 0.48 μs | ~2.5x slower |
1415
| Forward dynamics (CRBA + RNEA) | 2.71 μs || 2.82 μs | **~1x (on par)** |
15-
16-
For reference, here are the timings of the textbook algorithms that were replaced by the optimized versions above:
17-
18-
| Function (textbook algorithm) | ModernRoboticsBook.jl | Pinocchio 3.9 (C++) | vs Pinocchio |
19-
|-------------------------------|----------------------:|--------------------:|-------------:|
20-
| Inverse dynamics (RNEA, unoptimized) | 3.81 μs | 0.64 μs | ~6x slower |
21-
| Mass matrix (n × RNEA) | 22.2 μs | 0.65 μs | ~34x slower |
22-
| Forward dynamics (M⁻¹ × ...) | 34.2 μs | 2.82 μs | ~12x slower |
16+
| Forward dynamics (`forward_dynamics_rnea`) | 5.44 μs ||||
2317

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

@@ -29,11 +23,13 @@ Forward kinematics and Jacobian computation are **~3x faster than Pinocchio** th
2923

3024
Dynamics functions are now **within 1.4–2.5x of Pinocchio**, with forward dynamics on par. Both libraries use the same algorithms (RNEA, CRBA); the remaining gap is due to Pinocchio's hand-tuned C++/Eigen spatial algebra vs our general Julia `SMatrix` operations.
3125

26+
The textbook algorithm variants (`mass_matrix_rnea`, `forward_dynamics_rnea`) are provided for educational purposes — they directly mirror the textbook equations but are slower than the optimized defaults.
27+
3228
### Algorithms
3329

3430
- **Inverse dynamics**: Uses the **Recursive Newton-Euler Algorithm (RNEA)**, the same O(n) algorithm as Pinocchio. The in-place variant achieves zero allocations.
35-
- **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.
36-
- **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.
31+
- **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. The textbook variant `mass_matrix_rnea` calls `inverse_dynamics` n times with unit accelerations.
32+
- **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. The textbook variant `forward_dynamics_rnea` explicitly forms M⁻¹ and calls RNEA separately for each term.
3733

3834
### Allocations
3935

src/ModernRoboticsBook.jl

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ export near_zero,
4040
inverse_dynamics!,
4141
mass_matrix,
4242
mass_matrix!,
43+
mass_matrix_rnea,
4344
velocity_quadratic_forces,
4445
gravity_forces,
4546
end_effector_forces,
4647
forward_dynamics,
48+
forward_dynamics_rnea,
4749
euler_step,
4850
inverse_dynamics_trajectory,
4951
forward_dynamics_trajectory,
@@ -1403,7 +1405,8 @@ Computes the mass matrix ``M(\\theta)`` of an open chain robot using the
14031405
are needed for unit acceleration of joint ``i``?" Each call produces one column
14041406
of ``M(\\theta)``. This builds intuition but requires ``n`` full Newton-Euler
14051407
passes. CRBA computes the same result with one forward pass and one backward
1406-
pass, exploiting the symmetry of ``M``.
1408+
pass, exploiting the symmetry of ``M``. The textbook algorithm is preserved
1409+
See [`mass_matrix_rnea`](@ref) for this textbook algorithm.
14071410
14081411
# Arguments
14091412
- `joint_positions`: the ``n``-vector of joint variables.
@@ -1500,6 +1503,44 @@ function mass_matrix!(
15001503
return M
15011504
end
15021505

1506+
"""
1507+
mass_matrix_rnea(joint_positions, link_frames, spatial_inertias, screw_axes)
1508+
1509+
Computes the mass matrix using the textbook algorithm (Chapter 8.2): calls
1510+
[`inverse_dynamics`](@ref) ``n`` times, each with a unit acceleration vector.
1511+
This is slower than [`mass_matrix`](@ref) (which uses CRBA) but directly
1512+
illustrates that column ``i`` of ``M(\\theta)`` is the torque needed for unit
1513+
acceleration of joint ``i``.
1514+
"""
1515+
function mass_matrix_rnea(
1516+
joint_positions::AbstractVector,
1517+
link_frames::AbstractVector,
1518+
spatial_inertias::AbstractVector,
1519+
screw_axes::AbstractMatrix,
1520+
)
1521+
n = length(joint_positions)
1522+
M = zeros(n, n)
1523+
ddq = zeros(n)
1524+
zero_dq = zeros(n)
1525+
zero_g = zeros(3)
1526+
zero_F = zeros(6)
1527+
for i = 1:n
1528+
ddq[i] = 1
1529+
M[:, i] = inverse_dynamics(
1530+
joint_positions,
1531+
zero_dq,
1532+
ddq,
1533+
zero_g,
1534+
zero_F,
1535+
link_frames,
1536+
spatial_inertias,
1537+
screw_axes,
1538+
)
1539+
ddq[i] = 0
1540+
end
1541+
return M
1542+
end
1543+
15031544
"""
15041545
velocity_quadratic_forces(joint_positions, joint_velocities, link_frames, spatial_inertias, screw_axes)
15051546
@@ -1765,7 +1806,7 @@ Computes forward dynamics in the space frame for an open chain robot.
17651806
[`inverse_dynamics`](@ref) separately for Coriolis, gravity, and tip wrench terms.
17661807
This implementation combines these into a single RNEA call and uses `M \\ b` (LU
17671808
factorization) instead of explicit inversion, which is both faster and more
1768-
numerically stable.
1809+
numerically stable. See [`forward_dynamics_rnea`](@ref) for the textbook algorithm.
17691810
17701811
# Arguments
17711812
- `joint_positions`: the ``n``-vector of joint variables.
@@ -1815,6 +1856,48 @@ function forward_dynamics(
18151856
return mass \ tau_rhs
18161857
end
18171858

1859+
"""
1860+
forward_dynamics_rnea(joint_positions, joint_velocities, joint_torques, gravity, tip_wrench, link_frames, spatial_inertias, screw_axes)
1861+
1862+
Computes forward dynamics using the textbook algorithm (Chapter 8.3): explicitly
1863+
forms ``M^{-1}`` and calls [`inverse_dynamics`](@ref) separately for Coriolis,
1864+
gravity, and tip wrench terms. This is slower than [`forward_dynamics`](@ref)
1865+
(which uses CRBA + single RNEA + backslash) but directly mirrors the textbook
1866+
equation ``\\ddot{\\theta} = M^{-1}[\\tau - c - g - J^T F_{\\text{tip}}]``.
1867+
"""
1868+
function forward_dynamics_rnea(
1869+
joint_positions::AbstractVector,
1870+
joint_velocities::AbstractVector,
1871+
joint_torques::AbstractVector,
1872+
gravity::AbstractVector,
1873+
tip_wrench::AbstractVector,
1874+
link_frames::AbstractVector,
1875+
spatial_inertias::AbstractVector,
1876+
screw_axes::AbstractMatrix,
1877+
)
1878+
LA.inv(mass_matrix(joint_positions, link_frames, spatial_inertias, screw_axes)) * (
1879+
joint_torques - velocity_quadratic_forces(
1880+
joint_positions,
1881+
joint_velocities,
1882+
link_frames,
1883+
spatial_inertias,
1884+
screw_axes,
1885+
) - gravity_forces(
1886+
joint_positions,
1887+
gravity,
1888+
link_frames,
1889+
spatial_inertias,
1890+
screw_axes,
1891+
) - end_effector_forces(
1892+
joint_positions,
1893+
tip_wrench,
1894+
link_frames,
1895+
spatial_inertias,
1896+
screw_axes,
1897+
)
1898+
)
1899+
end
1900+
18181901
"""
18191902
euler_step(joint_positions, joint_velocities, joint_accelerations, timestep)
18201903

0 commit comments

Comments
 (0)