Skip to content

fix(mobile): export complete landmark coordinate pairs from EKF - #720

Open
Afloat16 wants to merge 3 commits into
petercorke:mainfrom
Afloat16:fix/ekf-export-landmark-offsets
Open

Afloat16 wants to merge 3 commits into
petercorke:mainfrom
Afloat16:fix/ekf-export-landmark-offsets

Conversation

@Afloat16

@Afloat16 Afloat16 commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Summary

EKF.get_map() uses a landmark's first-observation ordinal as its coordinate offset. After the first landmark, exported rows combine coordinates from adjacent landmarks. For example, a learned map [(-3, 8), (7, -4), (2, 3)] is returned as [(-3, 8), (8, 7), (7, -4)] in both mapping and SLAM.

Reuse landmark_x() to obtain each coordinate pair with the existing complete-state indexing, while preserving first-observation order and a separate returned array. Add end-to-end noiseless mapping and SLAM regressions, including nonzero initial pose, different map layouts, empty and single-observation controls, and returned-array independence.

Validation

  • Exact base: 968ed048639c01c9032e256565731e2703de77fe.
  • Corrected regressions on the base: 4 failed, 8 passed. Individual landmark estimates and the first exported row are correct controls.
  • Focused mobile and EKF tests: 29 passed.
  • Complete configured native suite with Swift, collision and QP dependencies: 856 passed, 3 existing explicit skips, 17 subtests passed on Linux/Python 3.12.14. Both compiled extensions are present.
  • Ruff formatting, the new test's lint, and git diff --check pass. The existing EKF source diagnostics decrease from 8 to 7, with no new diagnostics.
  • The configured Sphinx HTML build succeeds and prints the same 21 existing runblock errors at the same four locations as the base. Those examples are not claimed as passing.
  • Other operating systems, Python versions, physical hardware and browser/Pyodide execution were not tested locally.

Checklist

  • PR title follows Conventional Commits
  • Tests pass locally (pytest), with the existing skips listed above
  • Added tests for this change
  • Changed method and new test use modern type hints
  • Docstring updated
  • Change is focused
  • No project-specific data or notebooks added

AI assistance: prepared with OpenAI Codex (gpt-6.1-sol). The validation above was executed in the native environment.

Use landmark_x to export each complete learned coordinate pair in first-observation order, preserving mapping and SLAM state offsets. Add native noiseless regression coverage and clarify the public map contract.

AI assistance: OpenAI Codex (gpt-6.1-sol).
@codacy-production

codacy-production Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 7 complexity · 0 duplication

Metric Results
Complexity 7
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Resolve the two actual Codacy bare-assert findings in the EKF map regression tests with equivalent numpy.testing.assert_equal checks. The focused and complete configured native suites pass.

AI assistance: OpenAI Codex (gpt-6.1-sol).
@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 0.00%. Comparing base (2de1e3e) to head (ed68731).

Files with missing lines Patch % Lines
src/roboticstoolbox/mobile/EKF.py 0.00% 2 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##            main    #720   +/-   ##
=====================================
  Coverage   0.00%   0.00%           
=====================================
  Files        143     143           
  Lines      14785   14782    -3     
=====================================
+ Misses     14785   14782    -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@petercorke

Copy link
Copy Markdown
Owner

Thanks for your interest in RTB, and for taking the time to create a PR. It'd help me if you could provide some context or a small code example for this. TBH the whole first paragraph is pretty cryptic.

Merge current upstream main into fix/ekf-export-landmark-offsets, retaining the upstream NDArray annotation and the existing landmark coordinate-pair fix and regressions.

Validated with the configured native test suite and coverage, mypy, Sphinx HTML build, and an independent review of the exact tree. Existing unrelated lint and documentation diagnostics are preserved.

AI assistance: OpenAI Codex (gpt-6.1-sol).
@Afloat16

Copy link
Copy Markdown
Contributor Author

A concrete example is exporting a map after the EKF has learned three landmarks. The individual estimates are correct, but get_map() mixes coordinates from adjacent landmarks after the first row.

Here is a stationary, noiseless mapping example:

import numpy as np
from roboticstoolbox import Bicycle, EKF, LandmarkMap, RangeBearingSensor

robot = Bicycle(x0=[1.0, -2.0, 0.4])
robot.control = [0.0, 0.0]
points = np.array([[-3.0, 7.0, 2.0], [8.0, -4.0, 3.0]])
sensor = RangeBearingSensor(robot, LandmarkMap(points),
                            covar=np.zeros((2, 2)))
ekf = EKF(robot=(robot, None), sensor=(sensor, np.eye(2) * 0.01),
          animate=False)
ekf.run(T=2.0)

ids = list(ekf.landmarks)  # first-observation order
print(np.array([ekf.landmark_x(i) for i in ids]).round(6).tolist())
print(ekf.get_map().round(6).tolist())

The individual estimates are [[2, 3], [7, -4], [-3, 8]]. Before the fix, the exported map is [[2, 3], [3, 7], [7, -4]]; with this PR, the two outputs agree.

The cause is that the landmark dictionary records observation order 0, 1, 2, while each landmark occupies two state entries. get_map() was using that order directly as an offset. Reusing landmark_x() applies the existing two-entry indexing and the vehicle-pose offset for SLAM.

I rechecked this small example on Linux/Python 3.12.14 with the exact submitted and upstream EKF.py files, using the RTB 1.4.4 wheel for the supporting classes. This is a focused example check, separate from the earlier full-tree validation recorded in the PR.

This branch has not been deployed

No deployments
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