Airport Curb-to-Gate Analytics: Finding Bottlenecks with Real Data

Dhiren Panchal

Architecture at a glance

Vision stack that survives reality

Detection & tracking

  • Detector: PeopleNet / YOLOv8-s INT8 (TensorRT) at 20–30 FPS on Jetson NX.

  • Tracker: NvDCF (appearance + motion) or DeepSORT at queue heads; KLT/PyrLK is fine for corridor flow.

  • ReID: optional for short-range multi-camera handoff; avoid identity persistence to keep privacy simple.

From pixels to people flow

  • Calibrate each zone with a homography (H) to map image plane to floor plane.

  • Compute speed/density in meters, not pixels.

  • Use zone polygons to generate enter/exit events when a track crosses the boundary with direction.

# pseudo: boundary crossing → event
def boundary_events(tracks, polygon, t):
    for trk in tracks:  # trk = {id, x, y} in image coords
        p_prev = to_world(H, trk.prev_xy)
        p_now  = to_world(H, trk.now_xy)
        was_in = point_in_poly(p_prev, polygon)
        is_in  = point_in_poly(p_now, polygon)
        if (not was_in) and is_in:
            yield {"type":"enter","track_id":trk.id,"ts":t}
        if was_in and (not is_in):
            yield {"type":"exit","track_id":trk.id,"ts":t}

3D vs 2D

  • Escalator landings benefit from stereo/time-of-flight (occlusion hell).

  • Long corridors are fine with mono + homography as long as you validate scale.

Privacy

  • No frames off device; only events and aggregates.

  • Hash MACs on edge with a rotating salt if you use Wi-Fi/BLE presence.

  • Keep retention tight (e.g., raw tracks ≤ 24h; rollups ≥ 13 months).

Queue measurement that ops trusts

Queue head camera

  • Place overhead at the service boundary (ID check, security, bag-drop).

  • Virtual line (polyline) classifies join and served events for each track_id.

Deriving wait and service

  • Wait time for a person = served_ts – join_ts.

  • Service rate μ = completed serves per minute (lane-level if you can).

  • Throughput is what leaves the queue, not what arrives.

-- TimescaleDB: 5-min rolling wait percentiles per checkpoint
SELECT time_bucket('5 min', served_ts) AS bucket,
       checkpoint_id,
       percentile_cont(0.5) WITHIN GROUP (ORDER BY served_ts - join_ts) AS wait_p50,
       percentile_cont(0.9) WITHIN GROUP (ORDER BY served_ts - join_ts) AS wait_p90,
       count(*) / 5.0 AS throughput_pax_per_min
FROM queue_events
WHERE event_type='served'
GROUP BY bucket, checkpoint_id
ORDER BY bucket DESC

Sanity with Little’s Law

  • L=λ×WL = \lambda \times W should roughly hold over stable windows.

  • If measured queue length L drifts from λW\lambda W, revisit join/served line geometry or occlusions.

Path analytics across zones

Measurement points (MPs)

  • Every handoff is an MP: door_3_in, escalator_A_land, pre_id_exit, sec_recomp_entry, dutyfree_north, gate_16_hall.

  • Each MP emits enter/exit events with track_id, ts, direction.

Segment travel times

  • Stitch consecutive MPs by track_id within a timeout (e.g., 20 minutes).

  • Compute P50/P90 for each segment; these respond faster to interventions than means.

-- Segment time from pre-security exit to security entry
WITH ex AS (
  SELECT track_id, ts AS t_exit
  FROM mp_events WHERE mp='pre_id_exit' AND dir='out'
),
en AS (
  SELECT track_id, ts AS t_entry
  FROM mp_events WHERE mp='security_entry' AND dir='in'
)
SELECT time_bucket('5 min', en.t_entry) AS bucket,
       percentile_cont(0.5) WITHIN GROUP (ORDER BY en.t_entry - ex.t_exit) AS t_seg_p50,
       percentile_cont(0.9) WITHIN GROUP (ORDER BY en.t_entry - ex.t_exit) AS t_seg_p90
FROM ex JOIN en USING (track_id)
WHERE en.t_entry > ex.t_exit AND en.t_entry - ex.t_exit < INTERVAL '20 min'
GROUP BY bucket
ORDER BY bucket DESC

OD matrix (shares)

  • For each origin MP, bucket the next destination MP observed; normalize to get path shares.

  • Use this to quantify how many pax detour through duty-free vs straight-through.

Detecting bottlenecks before they bite

Shock index

  • For any zone, compare 5-min arrivals to 5-min service capacity.

shock=λ5minμ5min\text{shock} = \frac{\lambda_{5\text{min}}}{\mu_{5\text{min}}}

  • Sustained shock > 1.2 predicts visible spillback.

Density at escalator landings

  • Convert tracked positions to world coords; compute pax/m² in a landing polygon; smooth with EWMA.

  • Alert when density > local safety limit for two consecutive windows.

Corridor speed

  • Track median speed in m/s over sliding windows; < 0.6 m/s signals retail pinch or crowd formation.

# Example alert rule (Prom/Alertmanager style)
- alert: EscalatorLandingHighDensity
  expr: ewma_density_pax_m2{zone="esc_A_land"} > SAFETY_LIMIT
  for: 2m
  labels:
    severity: high
  annotations:
    summary: "Escalator A landing congestion"
    runbook: "open Stair B, deploy marshal, redirect at Door 5"

Flight-aware forecasting that ops believes

Inputs

  • Live schedule with STD/ETD, gate, belt on/off; rolling load-factor estimates; bank definitions.

  • Historical segment times and queue μ by hour-of-week and season.

Method

  • Use quantile regression or gradient boosted trees to predict arrival waves per zone and wait P90 at checkpoints.

  • Update every 5–10 minutes as flight events drift.

# pseudo: quantile model for security wait P90
features = [
  hour_of_week, season, bank_id,
  arrivals_5min_pred, arrivals_15min_pred,
  lanes_open, lane_mix_premium, staff_experience_score,
  recent_mu (exp avg)
]
y = wait_p90_next_15min
model = XGBoost(quantile=0.9).fit(X_train, y_train)
p90_pred = model.predict(X_now)

Expose lead time to breach: “Security P90 projected to exceed 12 min in 18 minutes if lanes stay at 6.”

Calibration and accuracy that stands up in audits

Placement & homography

  • At install, lay out 3–5 markers on floor to solve H; validate against tape-measured distances.

  • Re-calibrate after any camera bump or fit-out.

Manual audits

  • Quarterly two-observer counts at each MP for 30–60 min; reconcile to target ±3–5% absolute error.

  • For queues, sample 100 served passengers; compute CI of wait estimate.

Drift watch

  • Monitor false joins/serves via sudden spikes in λ\lambda without corresponding L; inspect video locally (on device).

Data model that won’t paint you into a corner

Events (immutable, append-only)

-- mp_events
(mp text, dir text, track_id text, ts timestamptz, x_m real, y_m real)

-- queue_events
(checkpoint_id text, event_type text, track_id text, ts timestamptz)

-- flight_events
(flight_id text, event text, ts timestamptz, gate text, stand text)

Rollups (materialized views)

-- per-zone 5-min features
CREATE MATERIALIZED VIEW zone_rollup AS
SELECT time_bucket('5 min', ts) AS bucket,
       zone_id,
       count_if(event_type='enter')/5.0 AS entry_rate_pmin,
       percentile_cont(0.5) WITHIN GROUP (ORDER BY dwell) AS dwell_p50,
       percentile_cont(0.9) WITHIN GROUP (ORDER BY dwell) AS dwell_p90
FROM zone_events
GROUP BY bucket,

Turning insights into action (and proving they worked)

Alert lifecycle

  • Alert → ack (who/when) → runbook action (what) → resolve → auto-create a row in ops_changes.

  • Weekly kaizen: pick top 2 recurring alerts; run reversible experiments (stanchions, door routing).

Causal checks

  • Use CUSUM or difference-in-differences on segment P90 before/after interventions.

  • If effect size < agreed threshold (e.g., −15% P90), revert and document.

Example dashboards engineers and floor teams both use

Tiles

  • Security wait P50/P90 and lanes open.

  • Curb entry rate vs design capacity.

  • Escalator landing density (live + 30-min forecast).

  • Duty-free corridor median speed.

  • Gate boarding “jet bridge idle” timer.

Map layer

  • Zone polygons heat-colored by shock index; arrows sized by OD share.

Drilldowns

  • Click a zone → last 24h arrivals, service, density, interventions with outcomes.

Safety, CX, and ROI you can quantify

  • Safety: keep landing density under local standard; faster clears during disruptions.

  • CX: reduce perceived wait via smooth segments even if checkpoint μ is fixed.

  • Ops: fewer emergency lane opens; smarter break scheduling; documented runbooks.

  • Commercial: corridor speed correlates with conversion; measure it, then shape it.

Back-of-envelope

Annual Value Δminutes saved/pax×pax/day×365×value/minute

Tie “value/minute” to missed-connection reduction or retail conversion gain to avoid hand-waving.

Field notes that save months

  • Put a camera at the escalator landing, not just the comb plate. That’s where density actually spikes.

  • The pre-ID corridor is the silent killer; repack tables create micro-dwells—measure before moving them.

  • Forced duty-free pinch points need a measurable bypass; watch corridor speed, not just sales.

  • PRM handling improves the whole system; model it as an explicit cohort with its own SLA.

Drop-in artifacts

Alert thresholds (tune locally)

security_wait_p90:
  warn: 10m
  crit: 12m
  for: 15m
escalator_landing_density_pax_m2:
  warn: 2.5
  crit: 3.5
  for: 2m
dutyfree_corridor_speed_m_s:
  warn: 0.75
  crit: 0.60
  for: 3m
shock_index:
  warn: 1.0
  crit: 1.2
  for

Kafka topic keys


Runbook snippet


FAQ for the skeptics

Do I really need ReID?
Only for multi-camera handoff where segments are long; otherwise rely on per-zone counts and short-range stitching with time/space gates.

Can Wi-Fi replace cameras?
No. It’s great for coarse dwell and OD shares but won’t give lane-level μ or queue wait accuracy.

How accurate is accurate enough?
±3–5% at zone level gets you reliable ops decisions. When you publish to regulators (e.g., APC), add a sampling plan and CI.

What if flights slide around all day?
The forecast updates every 5–10 minutes; your method doesn’t change, only the timing of shocks.