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

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
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support