Skip to content

feat(trajectory_planning): add BEV-only multi-sample trajectory scorer and shifted inference schedule - #170

Open
riita10069 wants to merge 4 commits into
mainfrom
restore/pr-76-trajectory-scorer
Open

feat(trajectory_planning): add BEV-only multi-sample trajectory scorer and shifted inference schedule#170
riita10069 wants to merge 4 commits into
mainfrom
restore/pr-76-trajectory-scorer

Conversation

@riita10069

@riita10069 riita10069 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

Phase 1, training-free upgrade to the flow-matching driving policy: multi-sample
scoring against the BEV/map data we already have. Implements #75.

What's in here

  • TrajectoryComplianceScorer: zero-trainable-params, draws K samples from any
    BasePlanner, re-ranks by drivable-area compliance + kinematic comfort.
  • FlowMatchingPlanner.sample_k_trajectories(): batch-repeat K independent noise
    draws per scene in one forward call.

Round 1 — riita10069's review, contract bug found

  • sample_and_score unpacked planner.forward() as (trajectory, ego_hidden) —
    BasePlanner.forward() only ever returns one tensor. Never caught because the
    test's FakePlanner matched the same wrong contract, and the whole test file
    lived at repo-root tests/ — never collected by CI at all (make test only
    globs Model/tests/). Fixed both the contract and a second latent bug (map
    fixture off-by-one) this uncovered; moved the file into Model/tests/.
  • Swapped decode_trajectory_to_xy for the canonical integrate_trajectory, now
    using each sample's real starting speed from egomotion_history instead of a
    flat 5 m/s guess for every scene.
  • Removed PROPOSAL_diffusion_driving_policy.md (belongs in Discussion Discussion: BEV-only trajectory scoring + shifted inference schedule for FlowMatchingPlanner #75).

Round 2 — #161 landed, calibration redesigned on real geometry

#161 (route-conditioned navigation inputs) resolved #148/#149, but changed
more than the calibration: map_input (rendered RGB) doesn't exist any more —
map_context is a 14-channel semantic raster now. Separately, riita10069's
comment on #17 confirmed BEV segmentation labels are headed to a separate
PandaSet track, not landing on KITScenes route-training soon — so this
couldn't wait on that either. Rebuilt directly on what #161 already ships:

  • drivable_area_compliance reads map_context's DRIVABLE_AREA channel directly
    (Model.navigation.geometry.MapChannel, a real binary mask) instead of an
    RGB-tolerance colour heuristic.
  • project_xy_to_bev_pixel reimplements NavigationRasterGeometry.ego_to_pixel's
    exact formula for batched torch use — cross-checked against the real
    geometry object in tests, not just internal consistency.
  • ScorerConfig's calibration now comes from
    Model.navigation.geometry.DEFAULT_NAVIGATION_GEOMETRY
    (geometry_id="kitscenes-v3-bev-1m-v1", 256×256 @ 1.0 m/px) — Correct me If I am Wrong.
  • Renamed map_input → map_context throughout, matching ReactiveE2E's naming.

Testing done

test_trajectory_scorer.py — 22 tests, including a direct cross-check against
DEFAULT_NAVIGATION_GEOMETRY.ego_to_pixel and a test proving compliance reads
the binary channel, not anything colour-adjacent. test_trajectory_planning.py
still 38 passed/1 GPU-only skip. Rebased cleanly onto main post-#161 (only
overlap was flow_matching_planner.py, non-conflicting). ruff clean.

Checklist

…r and shifted inference schedule

Signed-off-by: Flagbusted <justthefourofus@proton.me>
…lanner

Returns [B, K, 128] via repeat_interleave + single forward() call.
K samples per scene are independent — each row draws fresh
x_0 ~ N(0, I_128) inside forward(). 12 unit tests added.

When issue #17 (BEV seg aux loss) lands, drivable_area_compliance
can upgrade from pixel-colour heuristic to label-based lookup
with no scorer API change.

Implements #75

Signed-off-by: FLagbusted <justthefourofus@proton.me>
…real contract

sample_and_score unpacked self.planner(...) as (trajectory, ego_hidden) —
BasePlanner.forward() has only ever returned a single trajectory tensor
(see base.py's own docstring). Every test here passed anyway because the
local FakePlanner test double returned a 2-tuple to match the WRONG
contract, so this was never exercised against a real planner. Wiring
TrajectoryComplianceScorer(real_flow_matching_planner, ...) and calling
sample_and_score would have failed immediately with an unpack error.

FutureState (the only place ego_hidden was ever consumed) isn't called
from AutoE2E.forward() any more — WorldActionModel.predict_future
superseded it — so there's nothing downstream expecting a second value.
sample_and_score now returns (trajectory, scores).

Also, per @riita10069's review on #76:
  - decode_trajectory_to_xy now wraps the canonical
    Model.evaluation.metrics.integrate_trajectory instead of duplicating
    the bicycle-model integration.
  - Both decode_trajectory_to_xy and kinematic_comfort_score now take a
    real per-row initial_speed (extract_initial_speed(), reading the
    speed channel out of egomotion_history) instead of a fixed
    ScorerConfig.initial_speed=5.0 placeholder applied to every sample
    regardless of how fast the ego actually was. Added a regression test
    (test_different_initial_speeds_change_selection) that would have
    caught this.
  - Flagged in the module docstring: do not calibrate
    pixels_per_meter/ego_row/ego_col/drivable_rgb against
    Model/data_parsing/kit_scenes/map.py as it stands today without
    checking #148/#149 first — both open, both assigned to riita10069,
    both change the geometry this scorer would be calibrated against.

Moved tests/test_trajectory_scorer.py -> Model/tests/ (per review) and
discovered why it mattered beyond tidiness: python -m pytest Model/tests -v
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0 -- /usr/bin/python
cachedir: .pytest_cache
rootdir: /home/claude/auto_fsd2/Model
configfile: pytest.ini
plugins: anyio-4.14.2
collecting ... collected 596 items / 3 errors / 6 deselected / 6 skipped / 590 selected

==================================== ERRORS ====================================
___________ ERROR collecting tests/test_dataset_publication_tasks.py ___________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_dataset_publication_tasks.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_dataset_publication_tasks.py:10: in <module>
    from botocore.exceptions import ClientError
E   ModuleNotFoundError: No module named 'botocore'
_________________ ERROR collecting tests/test_overlay_tasks.py _________________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_overlay_tasks.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_overlay_tasks.py:9: in <module>
    from botocore.exceptions import ClientError
E   ModuleNotFoundError: No module named 'botocore'
______________ ERROR collecting tests/test_training_checkpoint.py ______________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_training_checkpoint.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_training_checkpoint.py:11: in <module>
    from botocore.exceptions import ClientError
E   ModuleNotFoundError: No module named 'botocore'
=========================== short test summary info ============================
ERROR Model/tests/test_dataset_publication_tasks.py
ERROR Model/tests/test_overlay_tasks.py
ERROR Model/tests/test_training_checkpoint.py
!!!!!!!!!!!!!!!!!!! Interrupted: 3 errors during collection !!!!!!!!!!!!!!!!!!!!
================== 6 skipped, 6 deselected, 3 errors in 3.18s ================== / CI only run
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
rootdir: /home/claude/auto_fsd2/Model
configfile: pytest.ini
plugins: anyio-4.14.2
collected 518 items / 12 errors / 6 deselected / 6 skipped / 512 selected

==================================== ERRORS ====================================
______________ ERROR collecting tests/test_dataset_publication.py ______________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_dataset_publication.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_dataset_publication.py:10: in <module>
    from Platform.pipelines.dataset_publication import (
E   ModuleNotFoundError: No module named 'Platform'
___________ ERROR collecting tests/test_dataset_publication_tasks.py ___________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_dataset_publication_tasks.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_dataset_publication_tasks.py:10: in <module>
    from botocore.exceptions import ClientError
E   ModuleNotFoundError: No module named 'botocore'
_______________ ERROR collecting tests/test_grad_accumulation.py _______________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_grad_accumulation.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_grad_accumulation.py:23: in <module>
    from Platform.pipelines.training_checkpoint import (
E   ModuleNotFoundError: No module named 'Platform'
______________ ERROR collecting tests/test_kitscenes_recovery.py _______________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_kitscenes_recovery.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_kitscenes_recovery.py:9: in <module>
    from Platform.pipelines.kitscenes_recovery import (
E   ModuleNotFoundError: No module named 'Platform'
_______________ ERROR collecting tests/test_overlay_artifact.py ________________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_overlay_artifact.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_overlay_artifact.py:10: in <module>
    from Platform.pipelines.overlay import (
E   ModuleNotFoundError: No module named 'Platform'
_______________ ERROR collecting tests/test_overlay_inference.py _______________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_overlay_inference.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_overlay_inference.py:8: in <module>
    from Platform.pipelines.inference import (
E   ModuleNotFoundError: No module named 'Platform'
____________ ERROR collecting tests/test_overlay_reproducibility.py ____________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_overlay_reproducibility.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_overlay_reproducibility.py:7: in <module>
    from Platform.pipelines.reproducibility import (
E   ModuleNotFoundError: No module named 'Platform'
_________________ ERROR collecting tests/test_overlay_store.py _________________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_overlay_store.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_overlay_store.py:7: in <module>
    from Platform.pipelines.overlay_store import (
E   ModuleNotFoundError: No module named 'Platform'
_________________ ERROR collecting tests/test_overlay_tasks.py _________________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_overlay_tasks.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_overlay_tasks.py:9: in <module>
    from botocore.exceptions import ClientError
E   ModuleNotFoundError: No module named 'botocore'
______________ ERROR collecting tests/test_training_checkpoint.py ______________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_training_checkpoint.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_training_checkpoint.py:11: in <module>
    from botocore.exceptions import ClientError
E   ModuleNotFoundError: No module named 'botocore'
_______________ ERROR collecting tests/test_trajectory_scorer.py _______________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_trajectory_scorer.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_trajectory_scorer.py:17: in <module>
    from Model.model_components.trajectory_planning.trajectory_scorer import (
E   ModuleNotFoundError: No module named 'Model'
___________ ERROR collecting tests/test_trajectory_visualization.py ____________
ImportError while importing test module '/home/claude/auto_fsd2/Model/tests/test_trajectory_visualization.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Model/tests/test_trajectory_visualization.py:17: in <module>
    from Platform.pipelines.overlay import write_overlay
E   ModuleNotFoundError: No module named 'Platform'
=========================== short test summary info ============================
ERROR Model/tests/test_dataset_publication.py
ERROR Model/tests/test_dataset_publication_tasks.py
ERROR Model/tests/test_grad_accumulation.py
ERROR Model/tests/test_kitscenes_recovery.py
ERROR Model/tests/test_overlay_artifact.py
ERROR Model/tests/test_overlay_inference.py
ERROR Model/tests/test_overlay_reproducibility.py
ERROR Model/tests/test_overlay_store.py
ERROR Model/tests/test_overlay_tasks.py
ERROR Model/tests/test_training_checkpoint.py
ERROR Model/tests/test_trajectory_scorer.py
ERROR Model/tests/test_trajectory_visualization.py
!!!!!!!!!!!!!!!!!!! Interrupted: 12 errors during collection !!!!!!!!!!!!!!!!!!!
================= 6 skipped, 6 deselected, 12 errors in 2.21s ================== (see Makefile), so this entire file — all 15
original tests — was never collected by CI. Neither the contract bug
above nor a pre-existing off-by-one in the map fixture (the drivable
rectangle stopped one row short of ego_row, so the 'origin should always
score compliant' assumption was silently false) had ever actually run.
Fixed the fixture and added 5 new tests (extract_initial_speed coverage,
initial-speed-changes-decode, the contract regression test above);
20/20 pass now, none of them previously executed by CI.

Removed PROPOSAL_diffusion_driving_policy.md from repo root per review —
belongs in Discussion #75, not a tracked file in this PR.

Full re-run: Model/tests/test_trajectory_scorer.py (20),
Model/tests/test_trajectory_planning.py + test_reasoning_coupling.py
(51 passed, 1 GPU-only skip). ruff clean.

Signed-off-by: FLagbusted <justthefourofus@proton.me>
… not guessed calibration (#161)

#149/#148 landed via #161 (route-conditioned navigation inputs) while
this PR was waiting on calibration. That changes what needs fixing here
from 'verify a guessed calibration' to 'the input format itself changed':
map_input (a rendered RGB image) is now map_context, a 14-channel
semantic raster (Model.navigation.geometry.MapChannel) -- there is no
pixel colour to fuzzy-match against any more.

Per riita10069's comment on #17 (BEV segmentation labels are being
redirected to a separate PandaSet pretraining track, not landing on
KITScenes route-training soon), this can't wait for learned segmentation
labels either -- rebuilt directly on what #161 already provides:

  - project_xy_to_bev_pixel now reimplements
    NavigationRasterGeometry.ego_to_pixel's exact formula (same signs,
    same -0.5 pixel-center offset) in torch, instead of a hand-derived
    formula against guessed constants. Cross-checked against the real
    geometry object directly in tests, not just self-consistency.
  - ScorerConfig's calibration fields are now sourced directly from
    Model.navigation.geometry.DEFAULT_NAVIGATION_GEOMETRY
    (geometry_id=kitscenes-v3-bev-1m-v1) -- the actual values
    map_context is rasterized with, not a placeholder.
  - drivable_area_compliance reads map_context's DRIVABLE_AREA channel
    (a real binary 0/1 mask, one of BINARY_MAP_CHANNELS) directly,
    replacing the RGB-tolerance colour-distance heuristic entirely.
  - Renamed the scorer's own map_input param to map_context throughout,
    matching ReactiveE2E.forward()'s naming post-#161.

Rebased onto main post-#161 -- clean, no conflicts (this branch's only
overlap with #161's changes was via flow_matching_planner.py, and
sample_k_trajectories doesn't touch anything #161 changed).

Tests rewritten against the new 14-channel shape and real geometry:
added a direct cross-check against
DEFAULT_NAVIGATION_GEOMETRY.ego_to_pixel on both the origin and random
points (not just internal self-consistency), and a test proving
compliance now reads the binary channel rather than any color-adjacent
heuristic. 22/22 passing, full test_trajectory_planning.py still
38 passed/1 skipped. ruff clean.

Signed-off-by: FLagbusted <justthefourofus@proton.me>
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.

[Feature]: Map rendering for KITScenes

2 participants