dynibo 0.5.2

Tree-structured robot kinematics and dynamics with runtime-size workspace APIs
Documentation
# Dynamics

Dynibo's dynamics operations use the manipulator equation:

$$
\tau = M(q)\dot\nu + C(q,\nu)\nu + g(q).
$$

For a fixed base, outputs contain scalar joint forces. For a floating base,
they begin with a six-element world-frame root wrench followed by joint forces.

## Mass matrix

`mass_matrix` computes the symmetric `G x G` generalized inertia matrix in
column-major order. Fixed joints occupy no row or column, but their subtree
inertia contributes to moving ancestors.

## Velocity-product forces

`velocity_product_forces` computes Coriolis and centrifugal generalized forces.
Gravity, prescribed base acceleration, and external loads are excluded.

## Gravity

With no external loads, `gravity` is the zero-velocity, zero-acceleration
inverse-dynamics term:

$$
g(q) = \tau(q,0,0).
$$

The operation can include link-local external loads without requiring nonzero
joint velocity or acceleration.

## Inverse dynamics

`inverse_dynamics` uses recursive Newton--Euler dynamics and includes joint
state, supplied base motion, gravity, and optional external loads. With a
stationary base and no loads, it satisfies the manipulator equation above.

## Forward dynamics

`forward_dynamics` uses the linear-time articulated-body algorithm (ABA) to
solve

$$
\dot\nu = M(q)^{-1}\left(\tau-C(q,\nu)\nu-g(q)-\tau_{\mathrm{load}}\right).
$$

For a floating base, input forces and output accelerations begin with
world-frame angular and linear base components. The supplied `BaseState` pose
and velocity participate in the calculation; its acceleration is ignored
because acceleration is the result. A singular joint or floating-base articulated
inertia produces a solver error rather than non-finite acceleration.

The floating-base solve equilibrates the six inertia coordinates by their
diagonal scales before Cholesky factorization. Cholesky solves also estimate
the scaled reciprocal one-norm condition number, which must exceed
`sqrt(f64::EPSILON)`; an ill-conditioned scaled matrix has a distinct
Rust error from a singular matrix. A normalized residual check rejects
inaccurate solves, and non-finite intermediate results are numerical errors.
Small rotational inertia relative to mass alone does not imply singularity.

## Calling the operations

=== "Rust"

    ```rust
    robot.mass_matrix(&q, &mut mass)?;
    robot.velocity_product_forces(&q, &qd, &mut velocity)?;
    robot.gravity(&q, &loads, &mut gravity)?;
    robot.inverse_dynamics(&q, &qd, &qdd, &loads, &mut forces)?;
    robot.forward_dynamics(&q, &qd, &forces, &loads, &mut accelerations)?;
    ```

=== "Python"

    ```python
    mass = robot.mass_matrix(q)
    velocity = robot.velocity_product_forces(q, qd)
    gravity = robot.gravity(q, loads)
    forces = robot.inverse_dynamics(q, qd, qdd, loads)
    accelerations = robot.forward_dynamics(q, qd, forces, loads)
    ```

=== "C++"

    ```cpp
    const auto mass = robot.mass_matrix(q);
    const auto velocity = robot.velocity_product_forces(q, qd);
    const auto gravity = robot.gravity(q, loads);
    const auto forces = robot.inverse_dynamics(q, qd, qdd, loads);
    const auto accelerations = robot.forward_dynamics(q, qd, forces, loads);
    ```

=== "C"

    ```c
    check(dynibo_mass_matrix(
        robot, workspace, q, J, mass, G * G));
    check(dynibo_gravity(
        robot, workspace, q, J, loads, load_count, gravity, G));
    check(dynibo_inverse_dynamics(
        robot, workspace, q, qd, qdd, J,
        loads, load_count, forces, G));
    check(dynibo_forward_dynamics(
        robot, workspace, q, qd, J, forces, G,
        loads, load_count, accelerations, G));
    ```

See [External Loads](external-loads.md) before supplying loads and [Frames and
Spatial Vectors](frames-and-spatial-vectors.md) before interpreting base wrench
or matrix results.