Skip to content

fix: FK/IK geometry, build/consume path and simulator physics; correct docs and roadmap specs - #19

Merged
gabrielfrasantos merged 16 commits into
mainfrom
claude/inspiring-heisenberg-lvoq52
Sep 30, 2026
Merged

gabrielfrasantos merged 16 commits into
mainfrom
claude/inspiring-heisenberg-lvoq52

Conversation

@gabrielfrasantos

@gabrielfrasantos gabrielfrasantos commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR fixes the problems found in a full review of the library, the simulator, the docs, the requirements files and the roadmap specs.

The shipped dynamics were already correct: RNEA, ABA, Euler-Lagrange and Newton-Euler all match an independent Lagrangian reference on random 3D chains. The defects were elsewhere.

Shipped code

  • ForwardKinematics dropped the base offset (links[0].parentToJoint).
  • It also computed the tool point as 2 · jointToCoM of the last link. That is wrong whenever the center of mass is not at the middle of the link.
  • InverseKinematics inherited both errors, so it converged to the wrong joint angles without reporting anything.

Build

  • cmake --build --preset host, the command in README, AGENTS.md and DEPLOYMENT.md, failed because no build preset named host existed.
  • Using the library from another project through FetchContent, as the README describes, failed at configure time (Unknown CMake command "emil_build_for").
  • OPTIMIZE_FOR_SPEED expanded to nothing: CMake defined RoboticsToolbox_ENABLE_OPTIMIZATIONS, but the macro checks NumericalToolbox_ENABLE_OPTIMIZATIONS.
  • numerical-toolbox and ui-cpp were pinned to main.

Simulator

  • The inertia about the joint axis was 0.001 instead of mL²/12. With the default arm, the initial accelerations were 36.97 / −79.02 rad/s² instead of 25.75 / −37.25.
  • The RNEA readout mixed the state after the step with the acceleration from before it. It showed up to 4.3 N·m with no torque applied.
  • Closing the window destroyed the form model while QtFormView still referenced it.

Tests

  • No multi-link test actually exercised gravity: every 2-link test had joint axes parallel to gravity.
  • The ABA "round-trip" tests used hard-coded torques and never called RNEA.
  • The FK tests could not catch a sign error or a wrong rotation-composition order.

Docs

  • The FK walkthrough had a sign error.
  • The IK doc claimed damping biases the converged solution (it only slows convergence), and quoted a wrong iteration count.
  • The ABA walkthrough said both joints accelerate downward; the elbow actually accelerates the other way (q̈ = g·[9/7, −12/7]).
  • Several links pointed to numerical-toolbox pages that don't exist here.
  • TESTING.md and the roadmap index described numerical-toolbox algorithms, not this repo's.

Roadmap specs. Five were mathematically wrong:

  • Manipulability: √det(JJᵀ) with a 6×N Jacobian is always 0 when N < 6.
  • Impedance control: the law doesn't produce the stated impedance, and the sign of the external force was wrong.
  • Redundancy resolution: a damped null-space projector is not exact.
  • TOPP: the pseudocode reads values before computing them, indexes out of range, and cited the wrong paper.
  • Cable tensions: the structure matrix came from a serial-arm Jacobian, and the spec relied on a QP solver that doesn't exist.

Other specs were inconsistent: numerical-toolbox CMake macros, conflicting twist orderings, DH without joint offsets, an analytical IK that ignored arm offsets, and a Delta robot modelled with prismatic legs.

Breaking changes

  • kinematics::ForwardKinematics and kinematics::InverseKinematics now take an explicit tool offset (in the last link's frame) as a constructor argument. The chain now starts at links[0].parentToJoint.
  • The Newton-Euler results are now BodyAcceleration / BodyWrench, because they are body-frame quantities. SpatialAcceleration / SpatialForce remain as deprecated aliases.

Changes

  1. Critical fixes
    • FK/IK use the base offset and take an explicit tool offset.
    • Added the host build preset.
    • EMIL, numerical-toolbox and ui-cpp are fetched whenever the parent project hasn't already provided their targets, and EMIL helpers that only apply to standalone builds are guarded.
  2. Major fixes
    • The optimization macro is now actually enabled, and dependencies are pinned to commit SHAs.
    • Simulator: correct inertia tensors, a consistent RNEA readout, and a fixed window teardown.
    • New tests: analytic references, a 3D RNEA→ABA inversion with gravity active, an RNEA vs Euler-Lagrange cross-check, FK base/tool offsets and composition order, and TestRobotArmSimulator.
  3. Docs and requirements
    • Every doc error above is fixed, and TESTING.md is scoped to this repo's algorithms.
    • validate-docs.py now also checks section order and relative links.
    • Stale VS Code entries removed, sonar version aligned, CI test-log path fixed.
  4. Roadmap specs
    • The flawed specs are corrected.
    • New specs, each with its math checked numerically: SE(3) (M6), the geometric Jacobian with J̇q̇ and the JacobianProvider interface (M8), CRBA (M28), a chain-dynamics adapter (M29), full-pose kinematics (M30), the Coriolis matrix and parameter regressor (M31), RNEA with an external tool wrench (M32), a cubic spline (M33), a forward-dynamics integrator (M34), and complete momentum-observer (M16) and parameter-identification (M22) specs.
    • The analytical IK spec (M21) now uses the OPW closed form, which handles industrial arm offsets.
    • ROADMAP.md and the index are rewritten, and AGENTS.md now states the kinematics/dynamics conventions.
  5. Code quality
    • Comments removed.
    • Precondition checks added: unit joint axis, positive articulated inertia, positive mass.
    • math::CrossProduct / DotProduct reused, and Cholesky used for the mass matrix and inertia tensor.
    • Simulator: Stop now pauses and Start resumes; a precise timer matches dt.
  6. CI
    • macOS and Windows host builds use the EMIL setup: ccache on macOS; sccache with the Ninja generator, msvc-dev-cmd and embedded debug info (/Z7) on Windows. Failed tests are re-run with output and test logs are uploaded per OS.
  7. Lint
    • All MegaLinter findings fixed: clang-format include order, prettier JSON, table alignment, and every markdownlint rule (code-fence languages, table style, top-level headings, line length). markdownlint now reports 0 errors across all 131 Markdown files.

Verification

  • host (with the Qt simulator), host-single-Debug, coverage and host-RelWithDebInfo all build with warnings as errors, and every test passes:
    • kinematics: 18
    • dynamics: 27
    • simulator application: 14
    • simulator view: all pass
  • CI is green on Ubuntu, macOS and Windows, along with MegaLinter, SonarCloud (quality gate passed, 96.8% coverage on new code), validate-docs and the booklet build.
  • A standalone consumer project built through FetchContent configures, builds and runs. With the original CMake the same project fails.
  • Python scripts (double precision) checked the math:
    • RNEA against a Lagrangian reference: 1e-8.
    • ABA∘RNEA identity: 1e-14.
    • CRBA against a mass matrix built from RNEA: 4e-16.
    • Modified-RNEA Coriolis matrix against Christoffel symbols: 4e-11; the regressor: 4e-15.
    • External wrench against −Jᵀw: 3e-15.
    • J̇q̇ against a finite difference: 5e-7.
    • SE(3) Exp∘Log: 6e-14.
    • OPW IK on four industrial parameter sets: 2e-12.

Remaining lint notes

  • lychee reports two links it cannot check from the runner, and neither fails the lint job: the Buss IK survey PDF (a TLS chain issue in the runner) and the templated Sonar scanner download URL in static-analysis.yml.
  • prettier would reformat CMakePresets.json and .devcontainer/devcontainer.json. The lines it would change predate this PR, and the check reports 0 errors.

Note

Some commits are labelled wip(roadmap). They are intermediate snapshots made while the spec work was in progress, so squash-merging is recommended.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh

- ForwardKinematics starts the chain at links[0].parentToJoint instead of
  the origin, and takes an explicit tool offset (last-link frame) instead of
  deriving the tool point from 2 * jointToCoM of the last link.
- InverseKinematics takes the tool offset in its constructor, asserts
  maxIterations > 0, and no longer keeps a mutable solver.
- FK/IK tests cover base offset, tool offset, right-hand sign and rotation
  composition order; simulator adapted to the new FK API.
- CMake fetches EMIL / numerical-toolbox / ui-cpp whenever the targets are
  absent (not only standalone) and guards standalone-only EMIL helpers, so
  the README FetchContent snippet configures; header libraries use
  ROBOTICS_TOOLBOX_EXCLUDE_FROM_ALL.
- Add the `host` build preset referenced by README/AGENTS/DEPLOYMENT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
…or physics

- Define NumericalToolbox_ENABLE_OPTIMIZATIONS (the macro OPTIMIZE_FOR_SPEED
  actually checks) and NUMERICAL_TOOLBOX_ENABLE_ASSERTIONS for tests; drop
  the unused ROBOTICS_TOOLBOX_* definitions and option.
- Pin numerical-toolbox and ui-cpp to commit SHAs.
- Simulator: rods get mL^2/12 about the transverse axes (the joint axis
  previously got 0.001); RNEA readout uses the same (q, qdot, qddot) that ABA
  used, so it reproduces the applied torque; torque group labelled in 0.1 N·m;
  window deletes the form view before the form model it references.
- Add TestRobotArmSimulator (readout == applied torque, hanging rest,
  analytic horizontal release, tool position).
- Replace golden-value ABA round-trips with analytic references and a
  3D RNEA -> ABA inversion under gravity; add an RNEA vs Euler-Lagrange
  cross-check and an analytic elbow-coupling check; use math::Tolerance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
- ForwardKinematics: template section order, right-hand sign in the
  walkthrough (Ry(pi/4)x = [0.707, 0, -0.707]), base/tool offsets, rotation
  composition, no code identifiers.
- InverseKinematics: iterative DLS converges exactly for reachable targets
  (damping slows, it does not bias), tight step bound ||e||/(2*lambda),
  measured iteration counts, preconditions checked in every build.
- ArticulatedBodyAlgorithm: horizontal release gives qddot = g*[9/7, -12/7]
  (elbow moves up), D -> 0 means unbounded acceleration, bias-force step,
  realistic ABA vs mass-matrix crossover.
- RecursiveNewtonEuler / EulerLagrange / NewtonEuler: sourced operation
  counts (Hollerbach 1980), mass-matrix conditioning, Coriolis/energy remarks,
  CoM-frame requirement; replace links to numerical-toolbox pages that do
  not exist here.
- TESTING.md scoped to robotics families; testing instructions example,
  DEPLOYMENT/AGENTS build notes, README contributing and consume notes.
- validate-docs.py now also checks section order and relative links.
- Remove stale VS Code launch/tasks entries, align sonar version (and let
  release-please bump it), fix the macOS/Windows test-log upload path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
- Remove comments from headers and tests (AGENTS.md rule); compute the
  Euler-Lagrange test inertias instead of hard-coding 0.083.
- Precondition checks: unit joint axis (debug assert in FK, RNEA, ABA via
  HasUnitJointAxis), positive articulated inertia D in ABA and positive mass
  in Newton-Euler (really_assert).
- Reuse math::CrossProduct / math::DotProduct; solve the SPD mass matrix and
  inertia tensor with Cholesky; drop math::detail dimension checks.
- Newton-Euler results are BodyAcceleration / BodyWrench (they are body-frame
  quantities, not spatial ones); the old names stay as deprecated aliases.
- Simulator: Stop pauses and Start resumes (Reset applies new parameters),
  precise 8 ms timer matching dt, missing <span> include, consistent test
  gating, no -Wno-error on GCC/Clang.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
- New specs with numerically verified math: SE(3) transform (M6), CRBA
  (M28), chain dynamics model (M29), chain pose kinematics / frame chain
  (M30), Coriolis matrix + inertial regressor (M31, Christoffel-consistent
  modified RNEA), RNEA with external tool wrench (M32), momentum observer
  (M16), base-parameter identification (M22), forward-dynamics integrator
  (M34).
- M8 becomes GeometricJacobian over a model-independent frame chain, with
  J̇q̇ and the JacobianProvider seam (was SpatialJacobian on DH only).
- M1 evolves the shipped link compatibly (prismatic branches, limits,
  armature); M4 gains a compensation cap and a sign-consistent test plan.
- ROADMAP.md / roadmap index: robotics items only, fixed modules and
  dependencies, (v; ω) convention, TOPP-RA citation, no dangling
  numerical-toolbox item numbers. AGENTS.md: kinematics/dynamics conventions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
…inematics specs

Intermediate state while the per-domain spec corrections are being applied;
superseded by the reviewed follow-up commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
Impedance (stiffness form and inertia shaping with a defined f_ext sign),
operational-space control with full joint-space gravity/Coriolis and J̇q̇,
hybrid control in a constraint frame with anti-windup, cable tensions from
anchor geometry with Pott's closed form, computed torque through one
inverse-dynamics call, Slotine-Li on the M31 regressor; robotics_* deployment
macros and JacobianProvider / model interfaces throughout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
Shared TrajectoryTypes, S-curve closed form for every rest-to-rest case,
duration-constrained trapezoid for multi-axis synchronization, SLERP on
SE3Transform/Quaternion with twist output and a template time law, TOPP-RA
rewrite (interval controllable sets, Fourier-Motzkin stage projection, exact
stage timing, Pham & Pham 2018), and the new M33 cubic spline spec; reference
numbers verified numerically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
DH with joint offsets, modified convention and FrameChain output;
manipulability on a JacobianProvider with the correct Gram determinant;
pose IK on the SE(3) log map with weighting and fresh final error;
redundancy resolution with an exact rank-thresholded projector; PoE in
(v; ω) with space-to-geometric conversion; OPW closed-form IK replacing the
offset-free Pieper sketch (verified on four industrial parameter sets);
separate Stewart-Gough and Delta kinematics; world-frame mobile-manipulator
Jacobian; continuum kinematics without 1/κ singularities.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

✅⚠️MegaLinter analysis: Success with warnings

Descriptor Linter Files Fixed Errors Max errors Warnings Elapsed time
✅ ACTION actionlint 7 0 0 0.6s
✅ CPP clang-format 35 0 0 0 0.78s
✅ CPP cppcheck 35 0 0 1.2s
✅ DOCKERFILE hadolint 1 0 0 0.45s
✅ JSON jsonlint 8 0 0 0.1s
✅ JSON prettier 8 2 0 0 0.49s
✅ MARKDOWN markdownlint 131 0 0 0 2.06s
✅ MARKDOWN markdown-table-formatter 131 0 0 0 0.2s
⚠️ SPELL lychee 165 2 0 1.78s
✅ YAML prettier 12 0 0 0 0.53s
✅ YAML yamllint 12 0 0 0.59s

Detailed Issues

⚠️ SPELL / lychee - 2 errors
[ERROR] failed to verify TLS certificate: invalid peer certificate: UnknownIssuer
📝 Summary
---------------------
🔍 Total..........104
🔗 Unique..........42
✅ Successful......92
⏳ Timeouts.........0
🔀 Redirected.......5
👻 Excluded.........9
❓ Unknown..........0
🚫 Errors...........2
⛔ Unsupported......2

Errors in .github/workflows/static-analysis.yml
[403] https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$ (at 37:21) | Rejected status code: 403 Forbidden

Errors in doc/kinematics/InverseKinematics.md
[ERROR] https://mathweb.ucsd.edu/~sbuss/ResearchWeb/ikmethods/iksurvey.pdf (at 124:169) | SSL certificate not trusted. Use --insecure if site is trusted

Hint: Followed 5 redirects. You might want to consider replacing redirecting URLs with the resolved URLs. Use verbose mode (`-v`/`-vv`) to see redirection details.
Hint: You can configure accepted/rejected response codes with `-a` or `--accept`

Notices

⚠️ Your configuration references items that have been removed from MegaLinter and are ignored: REPOSITORY_GITLEAKS, REPOSITORY_KICS. See Removed linters to find their replacements.

See detailed reports in MegaLinter artifacts

Your project could benefit from a custom flavor, which would allow you to run only the linters you need, and thus improve runtime performances. (Skip this info by defining FLAVOR_SUGGESTIONS: false)

  • Documentation: Custom Flavors
  • Command: npx mega-linter-runner@10.1.0 --custom-flavor-setup --custom-flavor-linters ACTION_ACTIONLINT,CPP_CPPCHECK,CPP_CLANG_FORMAT,DOCKERFILE_HADOLINT,JSON_JSONLINT,JSON_PRETTIER,MARKDOWN_MARKDOWNLINT,MARKDOWN_MARKDOWN_TABLE_FORMATTER,SPELL_LYCHEE,YAML_PRETTIER,YAML_YAMLLINT

MegaLinter is provided by OX Security
Show us your support by starring ⭐ the repository

- macOS keeps ccache; Windows switches to sccache with the Ninja generator
  (the Visual Studio generator ignores compiler launchers), MSVC environment
  from msvc-dev-cmd, embedded debug info (/Z7, which sccache can cache) and
  CC/CXX=cl so run-cmake skips its vcpkg-based MSVC setup.
- fail-fast off, failed tests re-run with output, per-OS test-log artifacts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
Label every fenced code block (cpp or text), realign the README and
ROADMAP tables, add top-level headings to the agent and prompt files,
and wrap lines longer than 400 characters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVYp84T87JiVaz77Rbgbnh
@sonarqubecloud

Copy link
Copy Markdown

@gabrielfrasantos gabrielfrasantos changed the title Fix FK/IK geometry, build/consume path and simulator physics; correct docs and roadmap specs fix: FK/IK geometry, build/consume path and simulator physics; correct docs and roadmap specs Sep 30, 2026
@gabrielfrasantos
gabrielfrasantos merged commit c6712b4 into main Sep 30, 2026
12 checks passed
@gabrielfrasantos
gabrielfrasantos deleted the claude/inspiring-heisenberg-lvoq52 branch September 30, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants