fix(navigation): score frontiers by info-gain per A* path cost (#1255) - #2830
fix(navigation): score frontiers by info-gain per A* path cost (#1255)#2830samuelokpor wants to merge 1 commit into
Conversation
Greptile SummaryThis PR updates frontier selection to score goals by expected information gain over real travel cost. The main changes are:
Confidence Score: 5/5Safe to merge with minimal risk. The changed scorer aligns A* blocking with the configured occupancy threshold, keeps unknown cells costly but traversable, and filters unreachable frontiers before selection. Files Needing Attention: No files require special attention.
What T-Rex did
Important Files Changed
Reviews (5): Last reviewed commit: "fix(navigation): score frontiers by info..." | Re-trigger Greptile |
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #2830 +/- ##
==========================================
- Coverage 71.10% 68.83% -2.27%
==========================================
Files 897 997 +100
Lines 80290 93895 +13605
Branches 7183 9230 +2047
==========================================
+ Hits 57088 64635 +7547
- Misses 21319 27090 +5771
- Partials 1883 2170 +287
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 240 files with indirect coverage changes 🚀 New features to boost your workflow:
|
|
Hey @samuelokpor I'm sorry this PR went under our radar, thanks for your work, we will test this |
1ac7a51 to
ff9d5ae
Compare
|
Thanks @leshy no worries at all. I've rebased onto current main, so this is clean to test now. One note: #3272 removed I see @metrox-eth's #3627 re-adds a test file at that path — if that lands first I'm happy to rebase on top of it and put my two tests back there. Additionally, agreed with his read that the two changes are complementary (his |
|
Sounds good @samuelokpor. Either order works for me too, and if yours lands first I'll rebase #3627 on top. Your two tests are welcome in the test file mine re-adds, the reference BFS in there might even be useful for them. Nice work on the scorer. |
|
First contribution here: we've been using dimOS for about a week, so corrections welcome. We have an offline harness for comparing exploration strategies on recorded maps, and pointed it at this PR. Both arms are the real files, unmodified: stock = Goal dispersion: reproduces. Median robot→goal distance drops on every map in both configs (12/12). Big office: 8.39 → 6.16 m, goals beyond 5 m 79.8 % → 58.3 %. Head-to-head on identical inputs (same frontiers, costmap and history): same pick about half the time; when they differ, #2830 takes the nearer frontier in 143 of 164 decisions. Travel at given coverage: reproduces on the large map only. Big office: path to 80 % of visible ceiling 24.8 → 20.9 m (−15.6 %), total path −7.6 %, better on 10 of 12 paired starts. One caveat against our own favorable number: the 45 s goal timeout in the shipped config mostly truncates stock's long goals; in the config without it, the advantage shrinks. Our own flat could not test this leg at all: a geodesic check shows only 0–2.4 % of its visible area lies beyond one lidar range of walking (13.8 % on bigoffice). Nothing to save there. Two observations. (1) Both scorers issue goals at frontiers our 46 cm body cannot reach. The selector inflates the costmap by a fixed 0.25 m. Is that meant to be robot-specific, or should callers pre-inflate to their own footprint? (#2830 abandons such goals faster, unreachable-goal churn median 6.5 to 2, which helped us.) (2) The added A* runs once per candidate, so its cost tracks frontier count: +1.5 % at 15 clusters, +27 % at 29. The overall hot spot remains Caveats: Happy to rerun any configuration on either map set. |
|
Thanks both, this looks really cool, I'll test irl etc |
|
Follow up on the rerun, plus a correction to our own earlier comment: the "45 s goal timeout in the shipped config" we mentioned was our deployment's value, not upstream's (default is 15 s). Corrected in the repo. Where this PR lands in our runs: removing explored_goals_score clearly helps goal dispersion (our first comment stands), and that term is indeed half of the root cause. But at short lidar range the cross-map swings remain (11 vs 11 and 22 vs 20 in our mid-start runs), because the other half is the direction term making U-turns free. So this PR looks necessary but not sufficient for #1255; full analysis and data there: #1255 |





Problem
The frontier explorer jumps across the map — it explores ~1 m in one spot, then walks to the opposite side of the building for another ~1 m, then back, wasting a lot of travel.
Root cause is in
_compute_comprehensive_frontier_score. The weighted sum gave 30% to a "distance from explored goals" term that rewards frontiers far from anywhere already visited (i.e. it incentivizes teleporting across the map), and its distance term peaked at 5 m so it penalized nearby frontiers. Distance was also straight-line, so a frontier just past a wall looked cheap even when it was a long detour to reach.Closes #1255
Solution
Replace the weighted sum with a single objective — information gained per unit of real travel — plus heading continuity and an anti-revisit term:
score = info_gain / (1 + path_cost), wherepath_costis the A* route length over the (inflated) costmap via the existingmin_cost_astar(which already supports traversing unknown cells) — so "near" means near to actually reach, not as the crow flies.*= 1 + 0.5 * heading_alignmentfor smooth sweeps instead of zig-zags.safe_distanceof an already-explored goal.Iteration for context: v1 (reward far-from-explored) → ping-pong; v2 (pure nearest-first) → stalls at the first wall; v3 (info-gain / A* path cost) → smooth full-map sweep.
How to Test
Run the Go2 office sim and trigger exploration:
The robot sweeps the space in a continuous loop instead of ping-ponging, and self-terminates on no-information-gain. Existing
test_wavefront_frontier_goal_selector.pypasses.Measured travel distance to reach a given map coverage (same office sim, old scorer vs new):
Contributor License Agreement