mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 20:21:17 +00:00
4fdf1968af
Batch 5a of the cycle-1 doc sync. For each of the four foundation helpers (imu_preintegrator, se3_utils, lightglue_runtime, wgs_converter): - Append "Cycle-1 operational reality" section to the existing common-helpers/<NN>_*.md, documenting what the shipped implementation actually exposes vs. the design- intent sketch (interfaces, exception types, public constants, AZ-task lineage). Specific cycle-1 facts captured per helper: - imu_preintegrator (AZ-276): make_imu_preintegrator factory, BMI088-class noise defaults, single ImuPreintegrationError exception, actual return type is PreintegratedCombinedMeasurements (consumer builds the CombinedImuFactor), destructive reset_with_bias semantics, first-sample-not-integrated dt=0 handling. - se3_utils (AZ-277): SE3 = gtsam.Pose3 re-export, Se3InvalidMatrixError, strict caller-orthogonalisation invariant, _DEFAULT_ROT_ATOL=1e-6 and small-angle Taylor cutoff for exp_map, is_valid_rotation predicate, strict dtype=float64 everywhere. - lightglue_runtime (AZ-278 / R14 fix): EngineHandle Protocol-typed constructor, LightGlueRuntimeError + LightGlueConcurrentAccessError, non-blocking concurrent- access guard (raises rather than serialises), match_batch equal-length precondition, composition-root single-instance into C2.5 + C3. - wgs_converter (AZ-279 + AZ-490): WEB_MERCATOR_MAX_LAT_DEG and MAX_ZOOM constants, WgsConversionError, ECEF arrays are ndarray(3,) float64, new horizontal_distance_m method (AZ-490 takeoff-origin bounded-delta gate), slippy-map tile math hand-rolled to match satellite-provider on-disk layout. Two contract files (imu_preintegrator.md and wgs_converter.md) need follow-up minor revisions to match shipped surface; queued for the next contracts-folder sweep, noted inline in each helper's new section. Also refresh D-CROSS-CVE-1 opencv-pin leftover replay timestamp (8-min debounce — gtsam upstream state cannot change in that window). Bumps _docs/_autodev_state.md sub_step detail. Co-authored-by: Cursor <cursoragent@cursor.com>
3.9 KiB
3.9 KiB
Common Helper — LightGlueRuntime
Purpose
Shared LightGlue inference handle. C2.5 (Re-rank) does single-pair LightGlue matching for inlier counting on K=10 candidates per frame; C3 (CrossDomainMatcher) does the heavier matching pass on the surviving N=3 candidates. Both use the same LightGlue engine; sharing the engine avoids paying the engine-build / GPU-memory cost twice.
Used By
- C2.5 — Inlier-based Re-rank.
- C3 — Cross-domain Matcher.
Interface (sketch)
class LightGlueRuntime:
def __init__(engine_handle: EngineHandle): ...
def match(features_a: KeypointSet, features_b: KeypointSet) -> CorrespondenceSet
def match_batch(features_a_list, features_b_list) -> list[CorrespondenceSet]
def descriptor_dim() -> int
Implementation Notes
- Owned by the composition root; the same instance is constructor-injected into both C2.5 and C3.
- Backed by C7's
InferenceRuntime.deserialize_engine(LIGHTGLUE_ENGINE_CACHE_ENTRY)at takeoff. - Single CUDA stream; concurrent calls forbidden — composition root binds the runtime to the single F3 hot-path thread.
Caveats
- The features fed in MUST come from the same backbone as the LightGlue engine was trained for (DISK in production-default; ALIKED / XFeat in alternates). Mixing backbones is a runtime error caught by the matcher's input shape check.
Cycle-1 operational reality
The shipped surface in src/gps_denied_onboard/helpers/lightglue_runtime.py (AZ-278) is the structural fix for R14 (re-rank vs. matcher double-load of the LightGlue engine). Composition root wires ONE LightGlueRuntime instance and constructor-injects it into both C2.5 (InlierBasedReranker) and C3 (CrossDomainMatcher).
- Constructor —
LightGlueRuntime(engine_handle: EngineHandle).EngineHandleis aProtocolfrom_types/manifests.py(descriptor_dim, forward(...)) — Layer 1 helper invariant means we do NOT importgps_denied_onboard.components.*. C7'sInferenceRuntime.deserialize_engine(LIGHTGLUE_ENGINE_CACHE_ENTRY)returns the concrete handle at takeoff; the composition root passes it in. - Construction guards —
LightGlueRuntimeErroris raised for:engine_handle is None;engine_handlemissing thedescriptor_dimProtocol attribute;descriptor_dim < 1. - Descriptor-dim mismatch — both
matchandmatch_batchvalidate everyKeypointSet.descriptorsagainst the engine'sdescriptor_dimand raiseLightGlueRuntimeErroron mismatch (catches "DISK features fed into an ALIKED-trained LightGlue" regressions). - Concurrent-access guard is non-blocking — the runtime owns a
threading.Lockbut never.acquire(blocking=True). Concurrent entry raisesLightGlueConcurrentAccessErrorimmediately rather than serialising. This is intentional: if you see this exception, the composition root wired the runtime into more than one thread by mistake — fix the composition, do NOT add blocking serialisation. The lock guards the body of bothmatchandmatch_batch;descriptor_dim()is lock-free. match_batchequal-length precondition —LightGlueRuntimeErroriflen(features_a_list) != len(features_b_list). Iteration useszip(..., strict=True). Indexed validation labels (features_a_list[i]) so a downstream test failure points to the offending pair.descriptor_dim()accessor — returns the engine's descriptor dim as a plainint(cached on construction so per-call overhead is one attribute lookup).- Public exceptions —
LightGlueRuntimeError(construction / descriptor-dim mismatch / batch-length mismatch) andLightGlueConcurrentAccessError(composition-root violation). Both subclassRuntimeError.
Cycle-1 task lineage
- AZ-278 — initial helper, contract producer.
- R14 structural fix: composition-root single-instance injection into C2.5 + C3 lands in
runtime_root/airborne_bootstrap.py(thelightglue_runtimepre-constructed key consumed by both_C2_5_STRATEGIESand_C3_STRATEGIES).