Spaces:
Running on Zero
Running on Zero
File size: 1,857 Bytes
f0fae3f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | """
anomaly_model.py
-----------------
Isolation Forest based anomaly detector for conveyor / crane motor sensor
streams (motor temperature, vibration, current draw, belt speed). This
powers the "Predictive Maintenance" tab -- flags abnormal equipment
behaviour before it causes an unplanned stoppage, which is exactly the kind
of workload Daifuku's intralogistics platforms (e.g. AS/RS, sorters, AGVs)
generate continuously in production.
"""
from dataclasses import dataclass
import joblib
import numpy as np
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler
FEATURES = ["motor_temp_c", "vibration_mm_s", "current_amps", "belt_speed_mps"]
@dataclass
class AnomalyResult:
is_anomaly: bool
anomaly_score: float # higher = more anomalous, roughly in [0, 1]
raw_score: float
def build_model(contamination: float = 0.1, seed: int = 42) -> IsolationForest:
return IsolationForest(
n_estimators=200,
contamination=contamination,
random_state=seed,
)
def score_reading(model: IsolationForest, scaler: StandardScaler, reading: dict) -> AnomalyResult:
x = np.array([[reading[f] for f in FEATURES]])
x_scaled = scaler.transform(x)
raw = model.decision_function(x_scaled)[0] # higher = more normal
pred = model.predict(x_scaled)[0] # 1 = normal, -1 = anomaly
# squash raw decision_function (~[-0.5, 0.5]) into a 0-1 "anomaly score"
anomaly_score = float(np.clip(0.5 - raw, 0, 1))
return AnomalyResult(is_anomaly=(pred == -1), anomaly_score=anomaly_score, raw_score=float(raw))
def save_artifacts(model, scaler, model_path: str, scaler_path: str):
joblib.dump(model, model_path)
joblib.dump(scaler, scaler_path)
def load_artifacts(model_path: str, scaler_path: str):
return joblib.load(model_path), joblib.load(scaler_path)
|