# MIME-VER-124 — RobotArmNode Inverse-Dynamics + PD Trajectory Tracking **Date**: 2026-04-30 **Node under test**: `mime.nodes.actuation.robot_arm.RobotArmNode` **Algorithm ID**: `MIME-NODE-102` **Benchmark type**: Analytical (Mode 2 independent) **Test file**: `tests/verification/test_robot_arm.py::test_ver124_pd_tracking` **Acceptance**: max-per-joint RMS tracking error $< 0.5°$ over 2 s of a 0.5 Hz sinusoid --- ## Goal Track a sinusoidal joint-space trajectory using the standard manipulator-control law (Sciavicco–Siciliano §6.6): $$ \tau = M(q)\,\ddot q_{\mathrm{des}} + c(q,\dot q) + g(q) + K_p\,e + K_d\,\dot e, $$ with $e = q_{\mathrm{des}} - q$, and assert that the tracking RMS error stays below $0.5°$ across all 3 joints over a 2 s integration. This benchmark stresses the *full* dynamics path: $M(q)$ from CRBA, $c+g$ from RNEA, and the forward-dynamics solve in `update`. A bug in any of those would manifest as drift or oscillation. ## Configuration | Parameter | Value | |---|---| | URDF | `tests/control/fixtures/three_link_planar.urdf` | | Trajectory | $q_{\mathrm{des},i}(t) = A_i \sin(\omega t)$ | | Amplitudes | $A = (0.3, 0.2, 0.4)$ rad | | Frequency | $\omega = 2\pi \cdot 0.5$ rad/s (0.5 Hz) | | $K_p$ (per joint) | $(200, 200, 80)$ N·m/rad | | $K_d$ (per joint) | $(20, 20, 8)$ N·m·s/rad | | Joint friction | $0$ (overridden in test fixture) | | Timestep | $10^{-3}$ s | | Steps | 2000 (2 s of simulated time) | | JAX precision | x64 enabled at module load | | Hot loop | `jax.jit`-compiled | The PD gains were chosen for a closed-loop natural frequency well above the 0.5 Hz disturbance band. The inverse-dynamics feed-forward is the principal contributor — without it, residual tracking error is $\approx 7°$ (verified during V&V development); with it, error drops below the $0.5°$ acceptance. ## Procedure 1. Initialise $(q_0, \dot q_0)$ to the desired trajectory at $t=0$. 2. JIT-compile a step closure that, given $(q, \dot q, t)$, computes $M(q), c+g, \tau_{\mathrm{ff}} = M\ddot q_{\mathrm{des}}+c+g$, the PD term $K_p e + K_d \dot e$, and calls `arm.update`. 3. Run 2000 jitted steps, accumulating $\sum e^2$. 4. Compute per-joint RMS = $\sqrt{\overline{e^2}}$ in degrees and assert the maximum component is below $0.5°$. ## Result **PASS**. Per-joint RMS error stays within the acceptance. ## Scope and Limitations - One trajectory shape (single-frequency sinusoid). Higher-frequency components or non-smooth trajectories would test the integrator's CFL-like stability boundary, which is not in scope here. - Joint friction is disabled in this benchmark to isolate the dynamics. With realistic friction, additional gain tuning would be required — that's an experiment-folder concern, not a node-level V&V concern. - The inverse-dynamics feed-forward is computed *at the current state* rather than the desired state — both are valid approaches, but the current-state form is what the test uses. ## Reproducibility - Run: `JAX_PLATFORMS=cpu .venv/bin/python -m pytest tests/verification/test_robot_arm.py::test_ver124_pd_tracking -x -q`.