Component wiring · how it all connects

Pipeline integration.

How the APEX detector chain, telemetry adapter, IGPP fallback, and controller bolt together inside the sim client. This is the plumbing — less about algorithms, more about data flow, concurrency, and error handling.

Pattern
Async pipeline · single-frame tick
no queues
Entry
race_pipeline.py
orchestrator
Sim adapter
Stub today · real adapter May 2026
transport TBD
Budget
<50 ms per tick
Orin NX target

§ 01Component responsibilities

ComponentOwnsEmits
sim_client (stub → real)Transport to the sim. Connection, handshake, recv/send.(frame_bgr, telemetry) per tick
ApexDetectorChainPhase 1 YOLO + Phase 2 keypoints modelsDetection(bbox, corners, conf)
PnPSolverCamera intrinsics, gate-3D templateGate pose in camera frame
IGPPEKF16D EKF state, IMU propagation, vision updateSmoothed gate pose + visibility flag
SAMDRefinerK-frame window of (pose, corners)Refined depth when baseline sufficient
TargetTrackerWhich gate is "next"Single target Detection per tick
ControllerPID (VQ1) or PPO (VQ2)ControlCmd(throttle, roll, pitch, yaw)
LoggerJSONL + PNG capture(async, off-path)

§ 02Data flow (per tick)

sim_client.next_frame()
    │
    ▼
(frame_bgr, telemetry)
    │
    ▼  ┌─► ApexDetectorChain ─► detections
    │  │                           │
    │  │                           ▼
    │  │                     TargetTracker ─► target
    │  │                                        │
    │  │                                        ▼
    │  │                                  PnP ─► gate_cam
    │  │                                           │
    │  │     ┌───────────────────────────────────┘
    │  │     │           ┌─► SAMD.refine  ───┐
    │  │     ▼           │                   ▼
    │  └─► IGPP.update_from_vision(gate_cam) gate_cam_refined
    │                              │
    ▼                              ▼
IGPP.update_from_imu(telemetry)
    │                              │
    └──────────┬───────────────────┘
               ▼
           Controller.step(gate_cam_refined, telemetry)
               │
               ▼
           ControlCmd
               │
               ▼
       sim_client.send(cmd)
               │
               ▼   (async)
           Logger.write

§ 03Concurrency

§ 04Error handling

FailureHandlingUser-visible effect
Detector returns emptyTargetTracker marks "lost", state → SEEKPilot slow-forwards + yaw sweep
PnP solve fails (degenerate corners)Skip PnP, use IGPP predictionSmooth continuity; no command jump
Telemetry payload malformedUse last valid telemetry, log warningOne-frame stale; recoverable
sim_client.send failsRetry once; if still fails, log + escalateSim assumed dropped; we abort run
IGPP EKF diverges (P matrix large)Reset to last confident vision stateOne-frame discontinuity, auto-recovers
CUDA OOM mid-runPipeline catches, logs, crashes the runAttempt logged as failed; restart

§ 05Config surface (race_config.py)

@dataclass
class RaceConfig:
    vision: VisionConfig        # detector mode, conf thresholds, SAMD window
    controller: ControllerConfig  # PID gains, PPO weights path
    connection: ConnectionConfig   # sim host/port, anti-cheat token, heartbeat
    race: RaceRuleConfig        # max_time_s, gate sequence, retry policy
    logging: LoggingConfig      # dirs, compression, frame capture rate
    gate: GateConfig            # width_m, height_m, transit threshold

    @classmethod
    def from_yaml(cls, path): ...
    def snapshot(self) -> dict: ...   # save with every race log
Every race log includes the full config snapshot. Reproducibility is not a nice-to-have for AIGP — Anduril can request code review. A reviewer should be able to re-run any artifact from deterministic inputs.
PIPELINE-INTEGRATION · v2.0 2026-04-21 · ← Index · Architecture · Race pipeline