CFD-U: crowd density in corridor bends

CFD-U (Crowd Flow Dynamics U-Net) predicts the steady-state pedestrian density map (ped/m²) of a one-way corridor bend from its geometry and inflow. It is a surrogate for JuPedSim simulations: one inference replaces a simulation run, which makes interactive exploration possible.

Files

File Content
cfdu-ensemble.onnx 3 training seeds merged in one graph, float32 (23 MB)
cfdu-ensemble-fp16.onnx the same graph with float16 weight storage, float32 compute (12 MB)
manifest.json shapes, SHA-256 hashes, parity checks

Input and output

  • Input channels: float32 [1, 5, 96, 96], a 24 m × 24 m window at 0.25 m/pixel centred on the turn. The channels are, in order:
    1. walkable: 1 inside the corridor, 0 outside
    2. wall_distance: distance to the nearest wall (m)
    3. inlet: mask of the opening where the flow enters the window
    4. exit: mask of the opening where it leaves
    5. load: specific flow (ped/(m·s)), constant over the image
  • Output seeds: float32 [3, 96, 96], one density map (ped/m²) per seed, 0 outside the corridor. Use the mean as the prediction and the standard deviation as a measure of disagreement.

Input normalisation, softplus and wall masking are inside the graph: feed raw channels and get ped/m² back. The channels have to be built exactly as aporia.rasterize builds them.

import numpy as np, onnxruntime as ort
from aporia.generators import generate
from aporia.rasterize import rasterize

params = {"theta_deg": 90, "width": 2.0, "cut": 0.5, "specific_flow": 0.6}
x = rasterize(generate("bend", params)).channels[None].astype(np.float32)
seeds = ort.InferenceSession("cfdu-ensemble.onnx").run(["seeds"], {"channels": x})[0]
density = seeds.mean(axis=0)  # (96, 96), ped/m²

Model

U-Net, 4 levels (32-64-128-256 channels), 1.93 M parameters per seed, ONNX opset 18. Three seeds (0, 1, 2) trained with identical settings; the app averages them with equal weight.

Training

  • Data: dataset v2: 1,500 simulated bends (angle, width, inner-corner cut, flow), two simulation seeds each, 1,466 accepted. Splits: 896 train / 112 validation / 111 interpolation test / 347 extrapolation test.
  • Simulator: JuPedSim 1.4.2, collision-free speed model, configuration calibrated on experiments (see docs/validation-report.md in the repo).
  • Setup: 300 epochs, AdamW (lr 2e-3, weight decay 1e-4, 5 warm-up epochs), batch 16, float16 autocast, one RTX 2060. Loss: MSE over walkable pixels; checkpoint chosen on validation RMSE.

Results

Mean of the three seeds. RMSE in ped/m² over walkable pixels. Interpolation: θ 30–120°; extrapolation: θ 120–150°, angles never seen in training.

Method RMSE (interp. / extrap.) p99 peak error Peak Fruin level correct
Continuity baseline 0.200 / 0.248 39 % / 51 % 23 % / 16 %
Nearest neighbour 0.158 / 0.307 10 % / 17 % 84 % / 73 %
CFD-U 0.008 / 0.077 1.2 % / 8.5 % 99 % / 83 %

Seed-to-seed simulation noise floor: 0.0023 (interp.) / 0.0079 (extrap.) ped/m².

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Dataset used to train pierrelouis-c/cfdu