NTT DATA BEST Hackathon 2026 Β· Politecnico di Torino Β· Team 24
A real-time tire-road friction identification system using an Extremum Seeking Control (ESC) gradient-based algorithm. The system continuously estimates Burckhardt friction model parameters from live wheel slip and friction measurements, enabling optimal slip tracking for maximum braking and traction force.
Try it now: sajjad-shahali.github.io/NTT_DATA_hackathon/friction-demo.html
An interactive in-browser demo β no server required. Visualises the Burckhardt friction curves, runs the surface classifier live, and lets you explore the algorithm with your own data.
Parametric ΞΌ(s) curves for all four road surfaces (dry asphalt, wet asphalt, snow, ice) with peak friction and optimal slip markers.
Classification results mapped against the Burckhardt friction curve β shows how the ESC identifier assigns each measurement batch to the correct surface type.
The core algorithm is a three-layer identifier (ESCTwoPointID) ported from MATLAB to Python. It is validated against 48 closed-loop braking simulations and exposed through an interactive web demo.
ΞΌ(s) = cβ Β· (1 β exp(βcβ Β· |s|)) β cβ Β· |s|
s_opt = ln(cβΒ·cβ / cβ) / cβ # slip at peak friction
ΞΌ_peak = ΞΌ(s_opt)
| Surface | cβ | cβ | cβ | ΞΌ_peak | s_opt |
|---|---|---|---|---|---|
| Dry asphalt | 1.2801 | 23.99 | 0.52 | ~1.17 | ~0.17 |
| Wet asphalt | 0.857 | 33.82 | 0.347 | ~0.80 | ~0.13 |
| Snow | 0.1946 | 94.13 | 0.0646 | ~0.19 | ~0.06 |
| Ice | 0.05 | 306.4 | 0.001 | ~0.05 | ~0.03 |
Key discriminant: cβ separates surfaces cleanly β 24 vs 34 vs 94 vs 306.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LAYER 1 β LUT (always active) β
β ΞΌ_max β interpolate β cβ_coarse β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β LAYER 2 β ESC-Gradient Analytic β
β g_esc + (s, ΞΌ) β solve analytically β cβ, cβ β
β Works at ANY operating point β no peak assumption β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β LAYER 3 β Brent Root-Finding β
β Two-point β refined cβ (when slip excitation > Ξ΄) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Sign convention: g_true = βg_esc Β· 2 / a_esc
NTT_DATA_hackathon/
βββ hackaton_id.py # ESCTwoPointID β Python port of MATLAB identifier
βββ predict_mu.py # Offline ΞΌ(s) prediction β NLS vs GP vs NN comparison
βββ preprocess.py # Raw CSV β prepared_friction.csv pipeline
βββ make_plots.py # Regenerate all presentation plots (01β05)
βββ load_mat_data.py # Inspect / filter / prepare friction_data_full.csv
βββ plot_data_exploration.py # EDA plots 06β10 (real simulation data)
βββ plot_burckhardt_vs_real.py # c_matrix curves vs real scatter (plots 11β12)
βββ evaluate_unlabeled.py # Surface ID from unlabeled (slip,ΞΌ) β plot 13
βββ eval_classification.py # F1 / Precision / Recall evaluation β plot 14
βββ eval_robustness.py # Monte Carlo robustness comparison across classifier variants
βββ src/
β βββ burckhardt.py # Burckhardt model math, surface presets, s_opt/ΞΌ_peak utils
β βββ data_gen.py # Synthetic (slip, ΞΌ) dataset generation
β =
βββ data/
β βββ raw/ # friction_data_full.csv (export from MATLAB)
β βββ synthetic/ # Auto-generated Burckhardt curves
βββ models/ # Saved fitted models
βββ docs/
β βββ friction-demo.html # Static interactive demo page
βββ reports/ # Team photos and generated plots
βββ deliverables/
β βββ plot.png # Surface classification plot vs Burckhardt curves
β βββ Burckhardt Friction Curves.png # Burckhardt ΞΌ(s) curves for all surfaces
βββ requirements.txt
βββ README.md
git clone https://github.com/Sajjad-Shahali/NTT_DATA_hackathon.git
cd NTT_DATA_hackathon
# Activate virtual environment (Windows/Git Bash)
source .env/Scripts/activate
# Install dependencies
pip install -r requirements.txtAn interactive landing page visualises the algorithm and exposes an in-browser classifier.
# Windows (without activating venv)
.env/Scripts/python.exe app.py
# Or with venv activated
python app.pyOpen http://localhost:5000 in the browser.
| Section | Description |
|---|---|
| Animated NLS Fitting | Watch Burckhardt curve converge live as noisy data accumulates β choose Dry / Wet / Snow / Ice |
| Burckhardt Friction Curves | All 4 surfaces plotted with peak markers + confusion matrix + F1 bars |
| Robustness Comparison | Monte Carlo F1 vs Ο_ΞΌ and Ο_s curves for 4 classifier variants |
| Noise Slider | Interactive Ο_ΞΌ sweep 0β0.10 with live F1 / Balanced Accuracy |
| Upload Your Data | Drop a CSV with slip + mu_noisy columns β classified entirely in-browser |
| Algorithm | Three-layer ESC identifier diagram + Burckhardt equation |
| Literature | 8 key references (Burckhardt 1993, Gustafsson 1997, Pacejka 2012, β¦) |
| Team | Authors + affiliations |
POST /api/classify
Body: { "slips": [...], "mus": [...] }
Returns: { "surface": "Dry asphalt", "c1": 1.280, "c2": 23.99, "c3": 0.520, "mu_peak": 1.172 }
Three improvements over the baseline were implemented and compared via Monte Carlo (100 batches Γ 8 Ο levels):
| Variant | Batch | Classify on | Improvement at Ο_ΞΌ=0.05 |
|---|---|---|---|
| Baseline | 50 | 3D NN (cβ,cβ,cβ) | β |
| Large batch Γ2 | 100 | 3D NN | +~0.25 F1 |
| cβ-only | 50 | cβ alone | +~0.15 F1 |
| Majority vote Γ3 | 50 | 3D NN, last-3 vote | +~0.10 F1 |
Run the comparison via βΆ Run Comparison button in the web demo.
import numpy as np
from hackaton_id import ESCTwoPointID
identifier = ESCTwoPointID()
params = np.array([
8, # [0] buf_size
0, # [1] (unused)
0.008, # [2] fark_threshold
0, # [3] (unused)
0.5, # [4] t_start
0.5, # [5] c1_min
2.0, # [6] c1_max
10.0, # [7] c2_min
150.0, # [8] c2_max
0.01, # [9] c3_min
1.0, # [10] c3_max
0.8, # [11] mu_buf_init
0.05, # [12] s_buf_init
1e-4, # [13] brent_tol
20, # [14] brent_iter
])
result = identifier.identify(
mu_measured=0.85, s_measured=0.12, s_probe=0.10,
c1_prev=1.28, c2_prev=23.99, c3_prev=0.52,
t=1.0, params=params, g_esc=-0.03, a_esc=0.02,
)
(c1, c2, c3, s_opt, mu_opt, valid, debug_flag,
best_fark, fark_now, best_s, best_mu,
s_at_peak, mu_opt_est) = result
if valid:
print(f"c1={c1:.3f} c2={c2:.2f} c3={c3:.4f} s_opt={s_opt:.4f}")debug_flag values: 0=Brent+grad, 1=warmup, 10=LUT+grad, 20=LUT+peak_fallback
# 1. Export from MATLAB
run('run_validation_updated.m') # 48 simulations β results
run('export_for_python.m') # β friction_data_full.csv (29 columns)
# 2. Copy to project
cp friction_data_full.csv data/raw/
# 3. Preprocess
python preprocess.py # β data/raw/prepared_friction.csv (20,616 rows)
# 4. Evaluate classifier
python eval_classification.py # F1 / Precision / Recall β plot 14
python evaluate_unlabeled.py # Unlabeled surface ID β plot 13
# 5. Train prediction models
python predict_mu.py --data data/raw/prepared_friction.csv --n-train 500
# 6. Generate all plots
python make_plots.py| Step | Filter | Rows remaining |
|---|---|---|
| Raw | β | ~765,000 |
| ABS filter | abs_active == 1 | ~479,000 |
| Confidence filter | Ξ± > 0.01 | ~340,000 |
| Stride=10 | Thin autocorrelated series | ~48,000 |
| Balance | Snowβ€12k / Dry,Wetβ€6k each | 20,616 |
Method: Burckhardt NLS fit β normalised nearest-neighbour in (cβ,cβ,cβ) space. Eval: 20% stratified hold-out of prepared_friction.csv β 82 batches of 50 pts.
| Surface | Precision | Recall | F1 | Batches |
|---|---|---|---|---|
| Dry asphalt | 1.0000 | 1.0000 | 1.0000 | 17 |
| Wet asphalt | 1.0000 | 1.0000 | 1.0000 | 17 |
| Snow | 1.0000 | 1.0000 | 1.0000 | 48 |
| Macro avg | 1.0000 | 1.0000 | 1.0000 | 82 |
Why perfect: cβ separates surfaces widely (24 vs 34 vs 94). Noise erodes performance only above Ο_ΞΌ β 0.05.
Regenerate with python make_plots.py β reports/plots/:
| File | Content |
|---|---|
01_surfaces_overview.png |
All 4 Burckhardt curves with ΞΌ_peak & s_opt |
02_model_comparison.png |
NLS vs GP vs NN per surface |
03_rmse_comparison.png |
RMSE grouped by method and surface |
04_data_efficiency.png |
RMSE vs training-set size |
05_identifier_convergence.png |
ESC identifier converging across surfaces |
06β10 |
EDA plots β slip dist, ΞΌ vs slip, alpha, scenario coverage |
11β12 |
Burckhardt curves vs real scatter |
13 |
Unlabeled surface ID evaluation |
14 |
F1 / Precision / Recall per surface |
| Issue | Location | Details |
|---|---|---|
| Brent swap bug | hackaton_id.py ~line 265 |
a_b=b_b; b_b=c_br; c_br=a_b β duplicates old b_b. Identical bug in MATLAB source; fix only when MATLAB is corrected |
| Name | Role | Programme |
|---|---|---|
| Sajjad Shahali | Machine Learning Engineer | MSc β Politecnico di Torino |
| Omer Ozkan | Data Science & Engineer | MSc β Politecnico di Torino |
| Kaan Sadik Aslan | Mechatronic Engineering | MSc β Politecnico di Torino |
| Berk Ali Demir | Electrical Computer Engineering | MSc β Politecnico di Torino |
Developed for the NTT DATA BEST Hackathon 2026 at Politecnico di Torino.

