Spaces:
Runtime error
Runtime error
| title: FlowTwin — Crowd Race Control | |
| emoji: 🏎️ | |
| colorFrom: red | |
| colorTo: gray | |
| sdk: gradio | |
| sdk_version: 4.26.0 | |
| app_file: app.py | |
| pinned: false | |
| # FlowTwin — Crowd Race Control | |
| **Predict. Simulate. Reroute.** | |
| An AI-powered crowd digital twin for Formula 1 venues. FlowTwin observes how | |
| spectators move, predicts where flow will break down, simulates candidate | |
| interventions against an identical copy of the current crowd state, and | |
| recommends the one that measurably performs best. | |
| > Formula 1 has spent decades turning telemetry into strategy. The cars are not | |
| > the only thing moving on race day. FlowTwin applies the same decision loop to | |
| > the hundreds of thousands of people moving through finite gates, corridors and | |
| > transport links. | |
| ``` | |
| SEE ──► PREDICT ──► SIMULATE ──► ACT ──► SEE again | |
| ``` | |
| --- | |
| ## The problem | |
| Crowd-flow failures at large venues are not a headcount problem. A venue can sell | |
| out successfully while individual parts of its network fail. UK HSE event-safety | |
| guidance is explicit that operators should monitor **spatial distribution** — | |
| entrances, exits, queues, concessions and pinch points — and anticipate problems | |
| rather than react to them. | |
| The 2022 Spanish Grand Prix is the case study this project is built around: | |
| a reported 277,836 weekend attendance, documented severe road and public-transport | |
| congestion, long concession queues, and Formula 1 publicly telling the promoter the | |
| situation was not acceptable. | |
| So the question FlowTwin answers is not *where is the crowd?* It is: | |
| **Where will crowd flow fail, why, and which intervention should an operator | |
| deploy before it does?** | |
| --- | |
| ## What it actually does | |
| | Layer | What it is | | |
| |---|---| | |
| | **Venue digital twin** | Directed weighted graph: gates, grandstands, concourses, concessions, exits, transport interfaces. Edges carry length, width, capacity and live state. | | |
| | **Crowd simulation** | Up to 40,000+ individual agents with their own walking speed, destination, route and compliance. Speed falls with local density; gates have per-minute throughput; corridors have finite storage, so congestion spills back upstream. | | |
| | **Crowd State Engine** | Per corridor and zone: occupancy, density, inflow, outflow, velocity, capacity utilisation, density growth, queue growth, opposing flow, and a composite risk score. | | |
| | **Prediction** | Gradient-boosted model trained on simulator ground truth, projecting density at +30 / +60 / +90 / +120 s and converting it into time-to-critical. | | |
| | **Dynamic routing** | Edge costs from distance, live travel time, congestion and risk; next-hop tables recomputed from live state with hysteresis and route commitment to stop oscillation. | | |
| | **Strategy Engine** | Candidate interventions generated from the venue topology around the detected bottleneck. | | |
| | **Counterfactual simulator** | Every candidate is applied to a byte-identical clone of the live state and rolled forward. Same seed, same starting state, one variable. | | |
| | **Optimizer** | Multi-objective score `J` over peak density, critical duration, travel time, risk, queue, throughput and reroute cost — normalised against the no-action outcome. | | |
| | **Race Control UI** | Animated venue map, live alerts with cause and lead time, strategy comparison table, and a "why this strategy" panel built from the same numbers that produced the score. | | |
| | **Perception** | A Hugging Face crowd model turns a real camera frame into the same observation schema the simulator produces, so everything downstream is identical in either mode. | | |
| **The recommendation is a measurement, not a rule.** No language model is | |
| anywhere in the decision path. | |
| --- | |
| ## Quick start | |
| Requires Python 3.10+. No Node build step — the dashboard is served by the | |
| backend. | |
| ```bash | |
| python -m venv .venv | |
| source .venv/bin/activate # Windows: .venv\Scripts\activate | |
| pip install -r backend/requirements-core.txt | |
| ./run.sh # Windows: run.bat | |
| ``` | |
| Open **http://127.0.0.1:8000**. | |
| Optional extras: | |
| ```bash | |
| pip install -r backend/requirements.txt # adds torch/transformers for perception | |
| python scripts/fetch_hf_model.py # download + verify the HF crowd model | |
| python scripts/train_predictor.py # retrain and re-validate the predictor | |
| python scripts/run_benchmarks.py --seeds 8 # regenerate the benchmark table | |
| python scripts/record_fallback.py # record demo-fallback runs | |
| cd backend && python -m pytest # test suite | |
| ``` | |
| --- | |
| ## The two demonstrations | |
| ### Simulation 1 — F1 Circuit Stress Test | |
| A fictional but realistically proportioned Grand Prix venue: four perimeter | |
| exits, six spectator zones, a full concourse ring, three concession clusters, two | |
| transport interfaces. 40,000 spectators leave at once over an 18-minute departure | |
| curve. At T+240 s, **Exit B loses half its throughput** — a scripted | |
| infrastructure failure that is a real change to the simulated network, not an | |
| annotation. | |
| What you see: normal flow → the East Concourse approach begins to compress → | |
| FlowTwin projects it going critical → eight candidate strategies are simulated → | |
| a recommendation with its reasoning → apply it → the crowd redistributes and the | |
| queue falls. | |
| This is the technical proof. | |
| ### Simulation 2 — Barcelona 2022 Counterfactual | |
| A simplified spectator and transport network for the Circuit de | |
| Barcelona-Catalunya, run at race-day scale under the documented 2022 conditions. | |
| **This is a counterfactual reconstruction using publicly documented event | |
| conditions and a synthetic crowd model. It is not a replay of original spectator | |
| telemetry, which is not public.** The dashboard separates the two explicitly: | |
| every documented fact carries its source, and every modelling assumption is | |
| labelled as one. Both lists are on screen throughout. | |
| | Documented | Modelled | | |
| |---|---| | |
| | 277,836 reported weekend attendance | Spectator distribution across stands | | |
| | 120,000+ reported on race day | Departure-mode split (rail / coach / car parks) | | |
| | Severe road and public-transport congestion reported | Corridor widths and capacities | | |
| | Long concession queues reported | Rail approach throughput | | |
| | F1 publicly called the situation not acceptable | Departure curve shape | | |
| | Circuit length 4.675 km, 2022 configuration | Schematic venue geometry | | |
| The question it answers is *"given the documented conditions, what would FlowTwin | |
| have recommended?"* — never *"this is what happened."* | |
| --- | |
| ## Measured results | |
| Generated by `scripts/run_benchmarks.py` across independent random seeds of the | |
| full simulation. Mean ± standard deviation. **No value here is entered by hand**; | |
| the numbers below are reproduced from `benchmarks/BENCHMARKS.md`, which the | |
| script rewrites on every run. | |
| Three arms on identical scenarios and seeds: | |
| - **Shortest path** — Baseline A: everyone walks the shortest route, no operator action. | |
| - **Static routing** — Baseline B: a capacity-aware plan computed before the event and never revised. | |
| - **FlowTwin** — the full loop: predict, evaluate candidates against clones of its own state, apply the measured optimum, repeat on a review cycle. | |
| See `benchmarks/BENCHMARKS.md` for the current table and | |
| `benchmarks/benchmark_results.json` for every individual run, including which | |
| intervention was chosen at each review point. | |
| ### What the results say | |
| **Simulation 1 — the safety gain is close to free.** Peak density at the | |
| degraded exit falls by roughly half, time spent in critical conditions goes to | |
| zero, and the largest queue falls by about 60% — while average journey time gets | |
| slightly *shorter*, not longer, and the venue still clears. | |
| **Barcelona — the safety gain costs something, and the numbers say so.** Peak | |
| density and maximum queue fall by 40–50% and critical exposure again goes to | |
| zero, but average journey time rises by a few per cent and the 95th percentile | |
| by more. That is the honest trade: relieving a saturated rail interface means | |
| walking some people further. The optimizer weights travel time explicitly, so | |
| this is a trade it made deliberately and reports, not one it hid. | |
| **The two scenarios get different answers.** On the circuit, the winning lever is | |
| usually a redirect or a staggered release — there is spare capacity at another | |
| exit. At Barcelona the winner is often a *destination split*, because you cannot | |
| reroute around a saturated rail terminus; you have to move demand to another | |
| mode. A system that returned "redirect 30%" to everything would not be doing the | |
| work. | |
| **Static routing is not always different from shortest path.** In the | |
| reconstructed Barcelona topology the two baselines produce identical results, | |
| because most origin–destination pairs have effectively one sensible route. A | |
| pre-event plan cannot help when the network offers no alternative — which is part | |
| of why the real event's transport interface was the thing that failed. | |
| Prediction accuracy is validated on **disjoint seeds** from training and reported | |
| in the dashboard under *Model accuracy* — including the analytic mass-balance | |
| baseline it must beat. If the trained model does not beat that baseline on | |
| held-out data, FlowTwin refuses to load it and falls back to the baseline rather | |
| than presenting an unvalidated prediction. | |
| --- | |
| ## Hugging Face integration | |
| Perception is a genuine input to the engine, not a decorative dependency: | |
| ``` | |
| camera frame ──► HF crowd model ──┐ | |
| ├──► crowd observation ──► Crowd State Engine | |
| synthetic agents ───────────────────┘ (density, risk, | |
| prediction, strategy) | |
| ``` | |
| Both observation modes converge on one schema, so nothing downstream can tell — | |
| or needs to tell — which one is feeding it. | |
| The model is resolved through a candidate chain, first one that loads wins: | |
| 1. `AbdurRahman011/csrnet-indian-metro-crowd-density` — density-map regression (specification candidate A). CSRNet's architecture is defined locally in `perception/csrnet.py` so a bare checkpoint can be loaded. | |
| 2. `AmineSam/irail-crowd-counting-yolov8n` — head detection on the RPEE-Heads dataset (specification candidate B), via `ultralytics`. | |
| 3. `hustvl/yolos-tiny`, then `facebook/detr-resnet-50` — widely mirrored COCO detectors, counting the `person` class. | |
| Override with `FLOWTWIN_HF_MODEL`. Run `scripts/fetch_hf_model.py` to download, | |
| select and verify with a real inference; it writes `models/perception_manifest.json` | |
| recording which model was chosen. | |
| **If no model loads, the endpoint reports the actual error and returns nothing. | |
| It never invents a count.** The dashboard's Perception panel shows the chain, the | |
| active model, and every load failure verbatim. | |
| --- | |
| ## Reproducibility | |
| Every run is fully determined by `(venue, scenario, seed, overrides)`. The random | |
| generator state travels with the simulation snapshot, so a counterfactual branch | |
| is exactly reproducible and two strategies are always compared from an identical | |
| starting state. The seed is displayed on the dashboard and returned by the API. | |
| ```bash | |
| POST /api/simulation/start | |
| { "venue_id": "circuit_alpha", "scenario_id": "circuit_alpha_post_race", "seed": 42193 } | |
| ``` | |
| The test suite asserts this directly: same seed reproduces identical output, | |
| different seeds diverge, snapshot/restore is exact, and two branches of one state | |
| produce identical metrics. | |
| --- | |
| ## Project layout | |
| ``` | |
| flowtwin/ | |
| ├── backend/flowtwin/ | |
| │ ├── venue/ graph + scenario schema and loaders | |
| │ ├── simulation/ agents, movement physics, the simulator | |
| │ ├── crowd/ density, flow, state engine, bottleneck detection | |
| │ ├── prediction/ features, trained model, inference | |
| │ ├── routing/ dynamic edge costs, next-hop tables | |
| │ ├── strategy/ interventions, counterfactuals, optimizer | |
| │ ├── perception/ Hugging Face crowd model + CSRNet architecture | |
| │ ├── runtime/ sessions, WebSocket broadcast, replay | |
| │ ├── benchmarks/ evaluation harness | |
| │ └── api/ REST + WebSocket | |
| ├── frontend/ Race Control dashboard (no build step) | |
| ├── data/ venues, scenarios, fallback recordings | |
| ├── scripts/ venue builder, training, benchmarks, HF fetch, UI check | |
| ├── docs/ ARCHITECTURE.md, DEMO.md | |
| └── benchmarks/ generated results | |
| ``` | |
| ## API | |
| | Endpoint | Purpose | | |
| |---|---| | |
| | `GET /api/meta` | Version, prediction accuracy, perception status, config | | |
| | `GET /api/venues` · `/api/venues/{id}` | Venue graph and provenance | | |
| | `GET /api/scenarios` | Scenario catalogue | | |
| | `POST /api/simulation/start` | Start a run | | |
| | `GET /api/simulation/{id}/state` | Current crowd state | | |
| | `POST /api/simulation/{id}/control` | play / pause / speed / step / run_to / trigger_event | | |
| | `POST /api/simulation/{id}/strategy/simulate` | Run the counterfactual sweep | | |
| | `POST /api/simulation/{id}/strategy/apply` | Apply a strategy to the live run | | |
| | `GET /api/simulation/{id}/alerts` · `/prediction` | Alerts, projections | | |
| | `POST /api/perception/analyze` | Hugging Face crowd observation | | |
| | `GET /api/benchmarks` | Measured benchmark results | | |
| | `WS /api/ws/simulation/{id}` | Live state stream | | |
| Interactive docs at `/docs`. | |
| --- | |
| ## Limitations | |
| Stated plainly, because they are the difference between a prototype and a claim: | |
| - Synthetic agents are not people. The movement model reproduces the *phenomena* that matter for this decision — speed collapse under density, throughput limits, spillback, partial compliance — not human behaviour in general. | |
| - Public historical information cannot reproduce original venue telemetry. Barcelona is a documented-condition counterfactual with labelled assumptions. | |
| - Density thresholds are context-dependent. The warning/critical values are venue configuration, presented as an operational scale, not a safety standard. | |
| - Camera-based counting undercounts dense or occluded crowds. The perception result says so alongside every count. | |
| - Real deployment would require venue-specific calibration, sensor integration and operational validation. | |
| - **FlowTwin is decision support.** It shows a recommendation, its cause, its lead time and its expected outcome. A trained human operator makes the call. It does not control gates or emergency systems and cannot guarantee that any incident is prevented. | |
| ## Licence and data | |
| Venue geometry is fictional (Circuit Alpha) or schematic (Barcelona). No personal | |
| data is collected, required or stored: the system needs position, density and | |
| flow, never identity. | |
| ## Documentation | |
| | File | What it is | | |
| |---|---| | |
| | `README.md` | This file — overview, setup, results | | |
| | `docs/ARCHITECTURE.md` | How it is built and why each decision was made | | |
| | `docs/DEMO.md` | Timed demo script, fallbacks, judge questions | | |
| | `docs/SPEC_AUDIT.md` | Every spec requirement checked, plus the ten engine defects found and fixed | | |
| | `docs/ROADMAP.md` | Known gaps and what to fix next | | |
| | `benchmarks/BENCHMARKS.md` | Generated results table | | |