Skip to content

feat(localization): add ScanMatcher (E5) - #40

Merged
gabrielfrasantos merged 3 commits into
mainfrom
claude/determined-einstein-bznlpi
Nov 6, 2026
Merged

gabrielfrasantos merged 3 commits into
mainfrom
claude/determined-einstein-bznlpi

Conversation

@gabrielfrasantos

Copy link
Copy Markdown
Contributor

Bumps numerical-toolbox to 182e068, which brings in PointSetAlignment (NT67, embedded-pro/numerical-toolbox-cpp#368). Then it ships E5, the 2-D scan matcher that NT67 unblocked.

Commit Item
ff2e14b + 6de0285 Pin bump for NT67. The first commit pinned the upstream PR's commit; the second repoints the pin to the merge commit 182e068.
eddd41b E5 ScanMatcher: point-to-point and point-to-line ICP between laser scans.

The E5 commit includes the header, coverage .cpp, the spec's 6 tests, doc, index rows, and removal of the spec and its roadmap rows. robotics.localization now links numerical.solver and numerical.optimization.

E5 in short

  • Association: brute-force nearest-neighbour pairing, gated by maxCorrespondenceDistance. Pairs outside the gate get zero weight.
  • Point-to-point step: the weighted planar PointSetAlignment (NT67).
  • Point-to-line step: a LeastSquaresProblem on the normal distances, solved with LevenbergMarquardt and ConstantDamping (NT64) using SE(2) left retraction.
  • Degeneracy check: before the point-to-line step, the eigenvalues of JᵀJ (JacobiEigenSolver) are checked. A ratio below 1e-6, as in a corridor, makes Match return nullopt.
  • Convergence: stops when the per-iteration increment is below the translation and rotation tolerances.

Deviations from the spec

  • Segment normals: each point takes the normal of the nearest reference segment between consecutive scan points. Segments longer than the correspondence gate are skipped as gaps between walls.
    • The spec's rule ("the closer of j − 1 / j + 1") picks the wrong wall in single precision. That happens at the room's duplicated corner point, and at the junction where scan order jumps from one wall to the next (a bogus diagonal "segment").
    • With the spec's rule, the half-spacing test landed 2 mm off. With the new rule, every test passes, including the spec's exact iteration counts (3 for point-to-point, 2 for point-to-line).
  • Point-to-line pairing: the paired point is the start of that segment, which lies on the wall's line.
  • Extra assertions (spec edge cases), which keep coverage at 100%:
    • A current scan larger than MaxPoints is refused.
    • Stopping at maxIterations = 1 reports converged == false.
  • Enum naming: IcpMetric enumerators are PascalCase (PointToPoint, PointToLine), as in earlier waves.
  • No step-failure check: with λ² > 0, the LM normal matrix is always positive definite, so a failed step cannot happen and is not checked.

Verification

  • Debug and coverage ctest: 7/7.
  • 100% merged line coverage on ScanMatcher.hpp.
  • Clang -Wall -Wextra -Werror -Wshadow with -DROBOTICS_TOOLBOX_COVERAGE_BUILD.
  • clang-format.
  • validate-docs OK; no doc line over 400 characters; tables aligned.
  • Not run locally: Qt preset, MSVC, cppcheck, SonarCloud.

Generated by Claude Code

2-D ICP between laser scans with gated brute-force nearest-neighbour
association. The point-to-point step is the weighted planar
PointSetAlignment; the point-to-line step runs Levenberg-Marquardt on the
normal distances with SE(2) left retraction, after an eigenvalue-ratio
check of J^T J refuses corridor-like degenerate geometry. Normals come
from the nearest reference segment, skipping segments longer than the
correspondence gate so wall junctions in scan order and duplicate corner
points cannot produce bogus normals.

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

github-actions Bot commented Nov 6, 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.68s
✅ CPP clang-format 204 0 0 0 2.15s
✅ CPP cppcheck 204 0 0 4.5s
✅ DOCKERFILE hadolint 1 0 0 0.69s
✅ JSON jsonlint 8 0 0 0.11s
✅ JSON prettier 8 2 0 0 0.46s
✅ MARKDOWN markdownlint 88 0 0 0 2.48s
✅ MARKDOWN markdown-table-formatter 88 0 0 0 0.26s
⚠️ SPELL lychee 135 2 0 2.49s
✅ YAML prettier 12 0 0 0 0.57s
✅ 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..........361
🔗 Unique.........128
✅ Successful.....349
⏳ 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 125: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

@sonarqubecloud

sonarqubecloud Bot commented Nov 6, 2026

Copy link
Copy Markdown

@gabrielfrasantos
gabrielfrasantos merged commit de4c164 into main Nov 6, 2026
12 checks passed
@gabrielfrasantos
gabrielfrasantos deleted the claude/determined-einstein-bznlpi branch November 6, 2026 08:32
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