Skip to content

Orbital Collision Risk Multi-Agent System

Python Agents Google ADK Gemini LightGBM

A three-stage ADK multi-agent system for real-time satellite collision-risk assessment. Screens live conjunctions, classifies risk with an ESA-trained model (ROC-AUC 0.86) via a custom MCP server, and renders results on a 3D globe.

0.82
accuracy
0.86
roc-auc / 5
0.71
f1
0.74
recall
0.68
precision

overview

Low Earth orbit is increasingly congested with debris, and the risk of cascading collisions (the Kessler syndrome) is a growing operational concern. This project answers a time-critical question for satellite operators: given an object and the current state of the catalog, what is about to come dangerously close?

The system is a three-stage multi-agent pipeline built with Google’s Agent Development Kit. A coordinator runs a data agent (live conjunction screening and risk classification), an analysis agent (network centrality in the debris swarm), and a briefing agent (natural-language operational summary). The orbital and machine-learning engines are exposed to the agents through a custom MCP server.

The risk classifier is trained on the ESA Collision Avoidance Challenge dataset (13,154 real conjunction events), reaching a ROC-AUC of 0.86 using only features reproducible from public orbital data, a deliberate trade-off favoring deployability over peak accuracy. Results are rendered live on an interactive 3D globe.

architecture

The system is an ADK SequentialAgent coordinating three specialized sub-agents. The orbital and machine-learning engines are not embedded in the agents; they are exposed through a custom MCP server, keeping the reasoning layer decoupled from the analytical layer.

NORAD ID │ ▼ SequentialAgent (coordinator) ├── data_agent ──▶ MCP: analyze_conjunctions (Skyfield + KDTree + LightGBM) ├── analysis_agent ──▶ MCP: network_role (NetworkX centrality) └── briefing_agent ──▶ natural-language briefing (Gemini) │ ▼ Operational briefing + 3D CesiumJS visualization

State flows between sub-agents through ADK’s shared session: each agent writes its result to an output key, and the next agent reads it. The briefing agent never recomputes anything; it synthesizes the prior two outputs.

Agent Responsibility Tool
data_agent Screen conjunctions and classify each approach analyze_conjunctions
analysis_agent Measure centrality in the conjunction network network_role
briefing_agent Synthesize the operational briefing none (reads state)

Orbital elements are fetched live from CelesTrak in GP/JSON (OMM) format rather than legacy TLE, which is exhausting its five-digit catalog numbering.

fetch_gp.py
def fetch_gp(group: str | None = None, catnr: int | None = None) -> pl.DataFrame:
"""Fetch GP elements from CelesTrak in JSON (OMM) format."""
params = {"FORMAT": "json"}
if group is not None:
params["GROUP"] = group
else:
params["CATNR"] = str(catnr)
response = httpx.get(_GP_URL, params=params, timeout=30.0, follow_redirects=True)
response.raise_for_status()
return pl.DataFrame(response.json())

Objects are propagated with SGP4 via Skyfield. A two-stage screen finds close approaches: a KDTree spatial filter discards distant pairs cheaply, then a refinement step computes the exact minimum distance and the real relative velocity at the time of closest approach.

The spatial filter is the operational standard for conjunction screening. It scales from one-vs-all (a single target against the catalog) to all-on-all (the full catalog against itself) without changing the design.

The classifier is trained on the ESA Collision Avoidance Challenge dataset: real Conjunction Data Messages from 2015 to 2019, with the operational risk labels that were withheld during the original competition. The label is the actionable flag of the final message per event.

A deliberate trade-offESA’s data has 103 features, including orbit-determination covariance that public TLE data does not contain. Training on all of them would produce an accurate model that cannot run on live pipeline output. Only the three features reproducible from public data were used (minimum distance, relative speed, time to closest approach), trading peak accuracy for real-world deployability.

The three-tier formulation proved unreliable (the high-risk class was 2.7% of events, unlearnable from geometric features alone), so the model was reframed as the binary decision operators actually make: maneuver, or not.

results

Metric Value
Training events 13,154
Accuracy 0.82
ROC-AUC 0.86
F1 (actionable) 0.71
Recall (actionable) 0.74
Precision (actionable) 0.68

The ROC-AUC of 0.86 shows the model ranks risk reliably, independent of any threshold. In an operational system the threshold shifts toward recall, since a missed collision costs far more than a false alarm. All numbers are reproducible via scripts/risk/evaluate.py.

Agent-Demo

A single number cannot rank orbital risk. The system reports two complementary axes:

  • Collision risk: per conjunction, from the ML classifier.
  • Structural criticality: from network centrality. A high-centrality object is a hub whose fragmentation would propagate the debris cascade.

An object can be high-risk but peripheral, or low-risk but a structural hub. The analysis agent fuses both into its verdict.

Results are rendered live with CesiumJS. The propagated orbits are exported as CZML (using geodetic lon/lat/height to avoid inertial-vs-fixed-frame ambiguity), and the globe shows the target, its risk neighbors, and conjunction lines that appear at each time of closest approach. The viewer runs token-free using Cesium’s bundled Natural Earth imagery.

Cesium-Demo

The conjunction screen uses a coarse time grid; reported closest-approach times are candidates rather than refined TCAs. The classifier omits covariance by design, trading accuracy for deployability. Risk and centrality are recomputed per query; caching would reduce latency. An all-on-all network mode would map cascade risk across the full catalog.