Skip to main content

Multi-target tracking over gri per-target estimators: ingest/routing, measurement-space scoring, GNN + MFA/MHT association, track lifecycle, LMB existence, and Poisson-binomial cardinality

Project description

gri-multitrack

Multi-target geolocation tracking. gri-multitrack combines the gri per-target Kalman-IMM (gri-kalman, fed by gri-obs observables) with a multi-target policy layer: ingest/routing of the feed-in data tiers, measurement-space scoring, per-scan association, track lifecycle, existence (Labeled Multi-Bernoulli), and a Poisson-binomial count distribution.

The governing seam is "gri scores, gri-multitrack assigns": gri-kalman owns per-target estimation and the measurement-space likelihoods; gri-multitrack owns everything multi-target. The multi-target layer is DIY on numpy/scipy -- gri-multitrack is Stone-Soup-free (see the "Architecture pivot" in AGENTS.md). The primary tracker class is MultiTracker (the "Crucible" name now belongs to the companion 3D app).

See PLAN.md for the live program plan (state, backlog, decisions) and AGENTS.md for repo guidance. Scenario generation, replay harnesses, scoring, and the replay viewer live in the sibling gri-tracksim repo, which depends on this engine and serializes its outputs (the engine itself is serialization-free).

Status

v1 of the original build plan is complete (see PLAN.md): ingest -> score -> associate -> gri IMM update -> lifecycle -> LMB output, on geos, raw TDOAs, and presence events, plus the outlier stream (clutter floors, per-observation dispositions, the extensible cure catalog), the stationary convolve resolver, split/merge with lineage, and batch RTS retrospectives.

Implemented:

  • Ingest + router for the feed-in tiers (geo Ell, observables, presence).
  • Per-track adapter over any gri Tracker (default SmartSegmentedIMM; CV / CoordinatedTurn / Static bank).
  • Measurement-space scoring seam (Gaussian innovation + chi-squared gate).
  • GNN scaffold associator (local scipy Hungarian) for bring-up.
  • MFA tracker (MfaTracker): a local hypothesis-oriented MHT that defers at ambiguous crossings and resolves via accumulated kinematic likelihood -- the v1 associator (loose-coupled; not Stone Soup's MFA). Standalone engine mirroring MultiTracker.
  • Track lifecycle: birth from geos, M-of-N confirm, patient deletion.
  • Existence r_i + Poisson-binomial count distribution.
  • Presence ("is it on") -> coast(t) + existence bump.
  • LMB output: labeled tracks + count distribution + top-level per-observation dispositions + the per-scan association diagnostic (gates / marginals / hypotheses), associator-agnostic. See Output.

Also implemented since the skeleton:

  • Per-kind clutter likelihood floors and per-observation DISPOSITIONS (assigned / birthed / clutter / cured); the user-extensible Cure Protocol.
  • The stationary resolver (convolve as the live estimator of a locked track; the cluster answer as resolved), split/merge with TrackEvent lineage, and smoothed_tracks() batch retrospectives.
  • Serialization, GOSPA/OSPA metrics, and the replay viewer live in gri-tracksim (the engine stays serialization-free).

Notable design choices

  • The per-track estimator is any gri-kalman Tracker; the default is SmartSegmentedIMM. gri-kalman exposes one uniform interface (update(ell, t) / update_observable / predict / coast / smoothed_track / result / is_initialized) across IMM, SmartIMM, SegmentedIMM, and SmartSegmentedIMM. gri-multitrack defaults to the maneuver-segmenting, outlier-rejecting SmartSegmentedIMM the design calls for; pass tracker_factory=make_imm (or any Tracker factory) to swap it. The choice is isolated to gri_multitrack/track.py.
  • Stone-Soup-free; multi-target is DIY on numpy/scipy. Both associators are local (GNN over scipy linear_sum_assignment; MFA a local hypothesis-oriented MHT). Stone Soup's MFA is filter-coupled and would cost the gri IMM, and is heavy (~48 MB of deps + ortools); see the CLAUDE.md "Architecture pivot". If LAP speed ever matters, add lapsolver/lap (tiny) -- not ortools. Stone Soup remains only as an optional dev-time GOSPA/OSPA cross-check.

Install

Uses uv with editable path dependencies on the sibling gri repos (in ../../foss/).

uv sync                     # core (Stone-Soup-free)
uv sync --extra crosscheck  # optional: Stone Soup, for a dev-time GOSPA/OSPA check only

Run

uv run python examples/two_target_demo.py   # end-to-end demo
uv run pytest                                # tests
uv run ruff check gri_multitrack test              # lint
uv run ty check                              # type check

Quick use

from gri_multitrack import MultiTracker

tracker = MultiTracker()
# each item is (payload, time_s); payload is an Ell, a gri-obs observable,
# or a PresenceObs.
outputs = tracker.process([(ell0, 0.0), (tdoa1, 1.0), (presence, 2.0)])

final = outputs[-1]
for t in final.tracks:
    print(t.label, t.existence, t.is_stationary, t.mode_probabilities)
print(final.count_distribution)  # Poisson-binomial P(N=k)

Output

A TrackerOutput per scan, in the same unified surface a single-target gri-kalman tracker reports (a single-target tracker is the degenerate one-track case), so the two are read interchangeably.

  • output.tracksLabeledTrack records (a gri-kalman TrackEstimate plus the stationary fields). Each carries state as an EllVel (position + velocity + 6x6 covariance; .ell for the position-only Ell), mode_probabilities, existence (r_i), confirmed, hits, parent (split lineage), is_stationary / stationary_locked / resolved (the convolver's cluster answer), and a bound predict.

  • Where are the unused / outlier observations? Top-level output.dispositions, one Disposition per observation. Each has index, used, verdict, track, confidence. The outlier bucket is:

    outliers = [d for d in output.dispositions if not d.used]
    

    Verdicts: assigned (absorbed by a track), birthed (seeded a new track), clutter (explained better as clutter — used=False), cured (repaired by a Cure then absorbed; d.cure names it, d.variant is the repaired measurement). The committal GNN reports confidence=1.0; the MFA reports the world-agreement mass with provisional=True.

  • output.count_distribution / expected_count / most_likely_count — the Poisson-binomial cardinality over the existences.

  • output.events — this scan's split / merge TrackEvents.

  • output.association — the per-scan diagnostic (gates / marginals / hypotheses); None when not computed.

  • output.worlds — the MFA's surviving global hypotheses with weights (the MHT-only confidence surface); empty for the committal GNN.

  • Prediction: track.predict(dt_s) returns a PredictedState at any horizon (a locked-stationary track predicts its convolved fix). Smoothing is opt-in: tracker.smoothed_tracks() returns the per-label RTS retrospective (best given all data, refining the past); the live tracks are the filtered best-given-data-so-far.

Layout

  • gri_multitrack/ingest.py -- feed-in types, routing, scan grouping.
  • gri_multitrack/track.py -- per-track adapter over the gri IMM.
  • gri_multitrack/scoring.py -- measurement-space likelihood + gate ("gri scores").
  • gri_multitrack/association.py -- GNN scaffold + Associator protocol.
  • gri_multitrack/lifecycle.py -- birth / confirm / delete / existence.
  • gri_multitrack/cardinality.py -- Poisson-binomial count distribution.
  • gri_multitrack/output.py -- Labeled Multi-Bernoulli output records.
  • gri_multitrack/tracker.py -- the MultiTracker orchestrator.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gri_multitrack-0.3.1.tar.gz (66.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gri_multitrack-0.3.1-py3-none-any.whl (51.6 kB view details)

Uploaded Python 3

File details

Details for the file gri_multitrack-0.3.1.tar.gz.

File metadata

  • Download URL: gri_multitrack-0.3.1.tar.gz
  • Upload date:
  • Size: 66.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for gri_multitrack-0.3.1.tar.gz
Algorithm Hash digest
SHA256 bc14a06b661da2c487847b4ae09c8b95b60d9e379238babe1a856bf59a5ea841
MD5 86ec4817c499ff1fcc1acb438ea2a52d
BLAKE2b-256 d24dfbdf82722f0d46bdfc7189fca0b53f68f817e48d7c9b285f9f3e648e2793

See more details on using hashes here.

File details

Details for the file gri_multitrack-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: gri_multitrack-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 51.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for gri_multitrack-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e0f52a5717abce868ba84ed03720360aa73630f271e47bd73aa0fd1b3a213bc6
MD5 f642a2186491988e1f47fd7b2489d3dd
BLAKE2b-256 c06d48678a65415759276d150fdb09e699ef3bf40a7d5dd5eb4e12f969f574b0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page