MechSense AI β Driving DNA
Driver behaviour analysis and component wear prediction from smartphone IMU sensors β no OBD port needed.
Model Description
Driving DNA is a suite of three models that together build a unique behavioural fingerprint ("driving DNA") for each driver using only a smartphone's accelerometer and gyroscope. The system classifies driving style, predicts component wear, and creates a 32-dimensional embedding that uniquely identifies a driver's behaviour.
Part of the MechSense AI project.
Three Models
1. DrivingStyleLSTM (lstm_style.onnx)
- Task: Classify driving style from 5-minute IMU sequences
- Input:
(1, 10, 20)β 10 consecutive 30-second feature windows, 20 features each - Output:
(1, 3)logits β [normal, drowsy, aggressive] - Architecture: 2-layer LSTM (hidden=64) β FC(32) β FC(3)
- Parameters: 57,475
- Val accuracy: 57.4% cross-driver (D6 held out from 6-driver dataset)
57.4% vs 33.3% random baseline β cross-driver generalisation is inherently limited with 6 drivers. Performance scales with more diverse training drivers.
2. DriverEncoder / Siamese Network (driver_encoder.onnx)
- Task: Embed driving sessions into a 32-dim identity space
- Input:
(1, 10, 20)β same format as LSTM - Output:
(1, 32)β L2-normalised driver embedding - Architecture: Twin LSTMs with shared weights β FC β LayerNorm β L2-norm
- Training: Contrastive loss with margin=1.0
- Separation: pos_dist=0.354, neg_dist=0.706 (2Γ separation ratio)
3. WearPredictor (wear_predictor.onnx)
- Task: Predict component wear multiplier from a single 30s driving window
- Input:
(1, 20)β 20 driving features - Output:
(1, 3)β [clutch_mult, brake_mult, tyre_mult] in range [1.0, 3.0] - Architecture: MLP β FC(20β32) β ReLU β Dropout β FC(32β16) β ReLU β FC(16β3) β Sigmoid
- Val MAE: 0.055 on [1.0, 3.0] scale
- Baseline km: Clutch 80,000 km Β· Brakes 40,000 km Β· Tyres 50,000 km
Feature Extraction (20 Features)
Input to all three models is a 20-dimensional feature vector extracted from a 300-sample (30-second at 10Hz) window of IMU data:
features = [
mean_magnitude, std_magnitude, max_magnitude, pct95_magnitude, # 0-3
mean_longitudinal, std_longitudinal, max_longitudinal, pct95_long, # 4-7
mean_lateral, std_lateral, max_lateral, # 8-10
mean_jerk, std_jerk, max_jerk, # 11-13
braking_events, acceleration_events, direction_changes, # 14-16
smoothness_magnitude, smoothness_longitudinal, std_vertical, # 17-19
]
Training Dataset
| Dataset | Drivers | Duration | Behaviours | Sensors |
|---|---|---|---|---|
| UAH-DriveSet | 6 | 500+ min | Normal, Drowsy, Aggressive | Accelerometer, Gyroscope, GPS |
- Sampling rate: 10 Hz
- Window: 300 samples (30 seconds)
- Hop: 150 samples (50% overlap)
- Total sequences: 655 (10-window sequences for LSTM)
Usage
Quick Start
import numpy as np
import onnxruntime as ort
from huggingface_hub import hf_hub_download
REPO = "YOUR_USERNAME/mechsense-driving-dna"
# Download models
lstm_path = hf_hub_download(REPO, "lstm_style.onnx")
encoder_path = hf_hub_download(REPO, "driver_encoder.onnx")
wear_path = hf_hub_download(REPO, "wear_predictor.onnx")
# Load sessions
lstm_sess = ort.InferenceSession(lstm_path, providers=["CPUExecutionProvider"])
encoder_sess = ort.InferenceSession(encoder_path, providers=["CPUExecutionProvider"])
wear_sess = ort.InferenceSession(wear_path, providers=["CPUExecutionProvider"])
STYLE_NAMES = ["normal", "drowsy", "aggressive"]
COMPONENTS = ["clutch", "brake", "tyre"]
BASELINE_KM = {"clutch": 80000, "brake": 40000, "tyre": 50000}
def analyse_session(features: np.ndarray):
"""
features: (N, 20) array of driving feature windows
N >= 10 recommended for LSTM (padded with zeros if shorter)
"""
SEQ_LEN = 10
if len(features) >= SEQ_LEN:
seq = features[-SEQ_LEN:][np.newaxis, :, :].astype(np.float32)
else:
pad = np.zeros((SEQ_LEN - len(features), 20), dtype=np.float32)
seq = np.concatenate([pad, features], axis=0)[np.newaxis]
# Driving style
logits = lstm_sess.run(None, {"input": seq})[0]
exp = np.exp(logits - logits.max(axis=-1, keepdims=True))
probs = exp / exp.sum(axis=-1, keepdims=True)
style = STYLE_NAMES[probs[0].argmax()]
# Driver embedding
emb = encoder_sess.run(None, {"input": seq})[0][0] # (32,)
emb = emb / (np.linalg.norm(emb) + 1e-8)
# Wear prediction
wear_preds = wear_sess.run(
None, {"input": features.astype(np.float32)}
)[0] # (N, 3)
mean_wear = wear_preds.mean(axis=0)
wear_result = {}
for i, comp in enumerate(COMPONENTS):
mult = float(np.clip(mean_wear[i], 1.0, 3.0))
wear_result[comp] = {
"multiplier" : round(mult, 3),
"km_remaining" : int(BASELINE_KM[comp] / mult),
}
return {
"style" : style,
"confidence": float(probs[0].max()),
"wear" : wear_result,
"embedding": emb.tolist(),
}
Files
| File | Description |
|---|---|
lstm_style.onnx |
LSTM driving style classifier |
driver_encoder.onnx |
Siamese encoder β 32-dim driver embedding |
wear_predictor.onnx |
Component wear MLP |
lstm_style_best.pt |
PyTorch LSTM checkpoint |
siamese_driver_best.pt |
PyTorch Siamese checkpoint |
wear_predictor_best.pt |
PyTorch wear predictor checkpoint |
config.json |
Feature names, class labels, baselines |
Companion Model
- Engine Whisperer: huggingface.co/YOUR_USERNAME/mechsense-engine-whisperer
- Live Demo: huggingface.co/spaces/YOUR_USERNAME/mechsense-demo
Citation
@misc{mechsense2026,
author = {Krishna Chandana Giri},
title = {MechSense AI: Smartphone-based Bearing Fault Detection and Driving Style Analysis},
year = {2026},
publisher = {Hugging Face},
url = {https://huggingface.co/YOUR_USERNAME/mechsense-driving-dna}
}
License
MIT β Training data (UAH-DriveSet) is separately licensed for academic use.
- Downloads last month
- 71