Le jour où l’insert indexable a décidé de mourir à 6h du matin
C’est un lundi matin, 6h15. La ligne de tournage est à l’arrêt. L’insert en carbure a lâché en plein milieu d’un lot de 200 pièces acier inox. Temps d’arrêt non planifié : 3h42 entre le diagnostic, l’appel de l’outilleur, le retrait de la pièce endommagée et le redémarrage.
Le responsable maintenance sort son tableur Excel. Il regarde la colonne “durée de vie théorique” : 240 passages. L’insert a cédé à 187. La cause ? “Matière plus dure que prévu cette semaine”. Le remède ? “On va changer plus tôt maintenant”.
Ce scénario se répète sur la plupart des lignes d’usinage en France. La gestion des outillages repose sur des règles empiriques transmises de chef d’équipe en chef d’équipe, avec une Weibull plantée dans un manuel constructeur qui date de 2017. Résultat : soit on change trop tôt (sur-consommation), soit trop tard (casse, rebuts, arrêts).
Il existe une meilleure approche. Elle combine l’analyse de survie classique — qui reste pertinente comme base — avec XGBoost et ses capacités à ingérer des features contextuelles que Weibull ne peut pas voir. Cet article décrit les deux méthodes, leur articulation, et le code Python pour passer en production.
Weibull classique : ce qui marche, et la limite réelle
Les deux paramètres qui résument tout
La distribution de Weibull est élégante. Elle décrit la durée de vie d’un composant avec deux paramètres :
- β (bêta, paramètre de forme) : caractérise le type d’usure
- β < 1 : défaillances juvéniles (mortalité infantile, pièces défectueuses dès le départ)
- β = 1 : défaillances aléatoires (distribution exponentielle, taux de hasard constant)
- β > 1 : usure par vieillissement (le plus courant en outillage, β ≈ 2 à 4 pour les inserts carbure)
- η (êta, paramètre d’échelle) : durée caractéristique, le temps au bout duquel 63,2% des composants ont défailli
La fonction de survie Weibull est :
S(t) = exp(-(t/η)^β)
Le taux de hasard (hazard rate) :
h(t) = (β/η) × (t/η)^(β-1)
Pour β > 1, h(t) croît avec le temps. L’outil s’use progressivement : plus il est vieux, plus il a de chances de céder prochainement. C’est précisément le comportement qu’on observe sur les inserts, les forets HSS, les fraises en bout.
Ajuster les paramètres sur données terrain
En pratique, on collecte les durées de vie observées (en cycles, en minutes de coupe ou en longueur usinée) et on ajuste β et η par maximum de vraisemblance. Avec Python :
from reliability.Fitters import Fit_Weibull_2P
import numpy as np
# Durées observées (cycles)
durees_vie = [187, 203, 215, 198, 241, 176, 229, 210, 195, 220,
185, 237, 202, 191, 248, 218, 207, 193, 225, 211]
# Certaines pièces retirées avant défaillance (données censurées)
censured = [0]*len(durees_vie) # 0 = défaillance observée
fit = Fit_Weibull_2P(failures=durees_vie, show_probability_plot=True)
print(f"β (forme) = {fit.beta:.3f}") # ex: 3.24
print(f"η (échelle) = {fit.alpha:.1f}") # ex: 218.7 cycles
print(f"Fiabilité à 180 cycles : {fit.distribution.SF(180):.1%}")
Quand Weibull suffit… et quand elle coince
Weibull fonctionne bien dans un contexte homogène : même matière, même paramètres de coupe, même machine, même opérateur. Si les conditions sont stables, le modèle de survie est fiable.
Le problème industriel réel, c’est que les conditions ne sont jamais homogènes. La matière fluctue d’un lot à l’autre, la vitesse de coupe change selon la pièce programmée, la machine vibre différemment selon l’usure des broches. Weibull traite toutes ces pièces comme identiques — elle ne voit pas les covariables.
Concrètement :
- Un insert usine de l’acier 304L pendant 3 semaines, puis passe à du 316L (plus dur, +15% d’effort de coupe). Weibull n’est pas recalée — elle continue à prédire 218 cycles alors que la réalité descend à 160.
- Un opérateur dégrade les paramètres de coupe (vitesse avance augmentée) pour tenir le cadence. Weibull ne le sait pas.
- La machine vibre plus depuis 3 mois, indice de santé de la broche en dérive. Weibull ne l’intègre pas.
C’est là qu’entrent Cox et XGBoost.
Cox PH, AFT, XGBoost survie : la comparaison franche
Avant de présenter XGBoost, il faut comprendre le spectre des options.
Modèle de Cox (Proportional Hazard)
Le modèle de Cox semi-paramétrique est le standard en analyse de survie covariable. Il modélise le taux de hasard comme :
h(t | X) = h₀(t) × exp(β₁X₁ + β₂X₂ + ... + βₙXₙ)
où h₀(t) est le taux de base (non paramétrique) et les Xᵢ sont les features (conditions de coupe, matière, etc.).
Avantage : interprétable, gère les données censurées, éprouvé dans la littérature. Limite : suppose la proportionnalité des hasards (l’effet d’une feature est constant dans le temps). En usinage, cette hypothèse est souvent violée — l’effet de la dureté matière n’est pas proportionnel à l’usure progressive.
Modèle AFT (Accelerated Failure Time)
Le modèle AFT prend le problème différemment. Plutôt que de modifier le taux de hasard, il accélère ou ralentit le temps lui-même :
log(T) = α + β₁X₁ + β₂X₂ + ε
Si une matière plus dure double le rythme d’usure, le modèle AFT le capte comme un facteur d’accélération de 2. Intuitivement, c’est plus naturel pour le mécanicien.
XGBoost avec AFT (objective="survival:aft") implémente exactement ce paradigme, mais avec la flexibilité du gradient boosting pour capturer les interactions non-linéaires.
| Modèle | Paramétrique | Gère censure | Interactions non-linéaires | Interprétabilité |
|---|---|---|---|---|
| Weibull 2P | Oui | Oui (MLE) | Non | Excellente |
| Cox PH | Semi | Oui | Non | Bonne (coef) |
| AFT paramétrique | Oui | Oui | Non | Bonne |
| XGBoost AFT | Non | Oui | Oui | Moyenne (SHAP) |
| XGBSEKaplanTree | Non | Oui | Oui | Faible |
Le pipeline données : de l’historique machine au seuil de remplacement
Le diagramme suivant montre le pipeline complet, de la collecte brute à la décision de remplacement :
flowchart TD
A[Capteurs machine\nbroche / vibration / temp] --> C[Feature Engineering]
B[Historique outil\ncycles / matière / coupe] --> C
D[Gamme programme CN\nvitesse avance / profondeur] --> C
C --> E[Features outil\ncycles_cumulés\nmatière_dureté_HB\nvf_mm_min\nindice_santé_broche\nforce_coupe_estimée]
E --> F{Données labellisées\ndéfaillance = 1\nremplacement planifié = censuré}
F --> G[XGBoost AFT\nobjective: survival:aft\nmax_depth: 4\nlearning_rate: 0.05]
F --> H[Weibull baseline\nβ η ajustés\nsur historique homogène]
G --> I[Courbe de survie personnalisée\npar insert + contexte]
H --> J[Courbe de survie\nglobale population]
I --> K{Probabilité survie\n< seuil 20% ?}
J --> K
K -->|Oui| L[Alerte remplacement\nMES / ERP]
K -->|Non| M[Continuer\nréévaluer prochain cycle]
style G fill:#ff6b35,color:#fff
style L fill:#e63946,color:#fff
style K fill:#457b9d,color:#fff
Features : ce qu’on met dans le modèle
La qualité du modèle dépend directement des features. Voici le catalogue minimal pour un modèle de survie outillage opérationnel :
Features outil (état courant)
cycles_cumules: nombre de passages depuis le dernier remplacementlongueur_coupee_mm: longueur totale usinée (plus précis que les cycles pour matières variées)vbmax_estime: usure en dépouille estimée (si capteur de force disponible)
Features coupe (conditions actuelles)
vitesse_coupe_m_min: Vc — impact direct sur thermique carbureavance_mm_tour: f — charge mécanique par arêteprofondeur_passe_mm: ap — section copeauduree_coupe_min: temps effectif de contact arête/matière
Features matière (lot en cours)
dureté_HB: dureté Brinell du lot (si disponible GPAO/ERP)grade_nuance: catégorie matière encodée (304L, 316L, C45, etc.)lot_fournisseur_hash: identifiant lot (pour capturer la variabilité inter-lots)
Features machine (état broche)
indice_sante_broche: score 0–1 issu de l’analyse vibratoiretemperature_broche_celsius: proxy de l’état thermiqueheures_depuis_maintenance: âge depuis dernier entretien broche
Gestion des données censurées
Le piège principal des données d’outillage : la plupart des retraits sont planifiés (changement préventif), pas des défaillances observées. Ces observations sont censurées à droite : l’outil aurait pu durer plus longtemps, mais on ne sait pas combien.
XGBoost avec objective="survival:aft" gère nativement la censure via l’intervalle [t_lower, t_upper] :
- Défaillance observée :
t_lower = t_upper = t_défaillance - Censure à droite :
t_lower = t_dernier_cycle, t_upper = +inf
Le code : modèle de survie XGBoost complet
import numpy as np
import pandas as pd
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
import matplotlib.pyplot as plt
# ─── 1. Chargement et préparation des données ───────────────────────────────
df = pd.read_parquet("outillage_historique.parquet")
# Encodage matière
le = LabelEncoder()
df["grade_encode"] = le.fit_transform(df["grade_nuance"])
# Features
FEATURES = [
"cycles_cumules",
"longueur_coupee_mm",
"vitesse_coupe_m_min",
"avance_mm_tour",
"profondeur_passe_mm",
"dureté_HB",
"grade_encode",
"indice_sante_broche",
"heures_depuis_maintenance",
]
X = df[FEATURES].values
# Labels pour AFT : intervalles [y_lower, y_upper]
# Pour les défaillances : y_lower = y_upper = durée observée
# Pour les censurées : y_lower = durée_observée, y_upper = +inf
y_lower = df["cycles_cumules_arret"].values.astype(float)
y_upper = np.where(
df["defaillance_observee"] == 1,
y_lower, # défaillance : borne haute = borne basse
np.inf # censuré : borne haute infinie
)
# ─── 2. Split train/test ─────────────────────────────────────────────────────
X_train, X_test, yl_train, yl_test, yu_train, yu_test = train_test_split(
X, y_lower, y_upper, test_size=0.2, random_state=42
)
# Construction des DMatrix avec labels intervalle
dtrain = xgb.DMatrix(X_train)
dtrain.set_float_info("label_lower_bound", yl_train)
dtrain.set_float_info("label_upper_bound", yu_train)
dtest = xgb.DMatrix(X_test)
dtest.set_float_info("label_lower_bound", yl_test)
dtest.set_float_info("label_upper_bound", yu_test)
# ─── 3. Entraînement ─────────────────────────────────────────────────────────
params = {
"objective": "survival:aft",
"eval_metric": "aft-nloglik",
"aft_loss_distribution": "normal", # log-normal pour durées de vie
"aft_loss_distribution_scale": 1.20,
"tree_method": "hist",
"max_depth": 4,
"learning_rate": 0.05,
"n_estimators": 300,
"subsample": 0.8,
"colsample_bytree": 0.8,
"min_child_weight": 5, # évite l'overfitting sur petits groupes
"verbosity": 1,
}
evals = [(dtrain, "train"), (dtest, "eval")]
model = xgb.train(
params,
dtrain,
num_boost_round=params.pop("n_estimators"),
evals=evals,
early_stopping_rounds=30,
verbose_eval=50,
)
# ─── 4. Prédiction et courbe de survie ───────────────────────────────────────
# Le modèle prédit log(T) — on exponentie pour obtenir T médian
log_t_pred = model.predict(dtest)
t_median_pred = np.exp(log_t_pred)
# Calcul de la fonction de survie avec distribution log-normale
from scipy import stats
def survival_curve(log_t_pred, sigma=1.20, t_range=None):
"""Calcule S(t) pour une observation donnée (AFT log-normal)."""
if t_range is None:
t_range = np.linspace(1, 400, 400)
mu = log_t_pred # paramètre de localisation = prédiction modèle
sf = stats.norm.sf(np.log(t_range), loc=mu, scale=sigma)
return t_range, sf
# Exemple : courbe pour un insert en conditions normales vs dégradées
# Insert A : acier 304L, Vc=220, f=0.15, ap=1.5, HB=160, broche saine
# Insert B : acier 316L, Vc=250, f=0.20, ap=2.0, HB=200, broche dégradée
log_t_A = model.predict(xgb.DMatrix(np.array([[50, 2800, 220, 0.15, 1.5, 160, 0, 0.92, 120]])))
log_t_B = model.predict(xgb.DMatrix(np.array([[50, 2800, 250, 0.20, 2.0, 200, 1, 0.61, 480]])))
t, sf_A = survival_curve(log_t_A[0])
_, sf_B = survival_curve(log_t_B[0])
# ─── 5. Définir le seuil de remplacement ─────────────────────────────────────
SEUIL_SURVIE = 0.20 # remplacer quand probabilité survie < 20%
cycles_remplacement_A = t[np.argmax(sf_A < SEUIL_SURVIE)]
cycles_remplacement_B = t[np.argmax(sf_B < SEUIL_SURVIE)]
print(f"Insert A — remplacement recommandé à : {cycles_remplacement_A:.0f} cycles")
print(f"Insert B — remplacement recommandé à : {cycles_remplacement_B:.0f} cycles")
# Insert A → 214 cycles | Insert B → 148 cycles
# ─── 6. Visualisation ────────────────────────────────────────────────────────
plt.figure(figsize=(10, 6))
plt.plot(t, sf_A, color="#2196F3", lw=2, label="Insert A (conditions normales)")
plt.plot(t, sf_B, color="#FF5722", lw=2, label="Insert B (conditions dégradées)")
plt.axhline(SEUIL_SURVIE, color="gray", linestyle="--", alpha=0.7, label=f"Seuil {SEUIL_SURVIE:.0%}")
plt.axvline(cycles_remplacement_A, color="#2196F3", linestyle=":", alpha=0.8)
plt.axvline(cycles_remplacement_B, color="#FF5722", linestyle=":", alpha=0.8)
plt.xlabel("Cycles cumulés")
plt.ylabel("Probabilité de survie S(t)")
plt.title("Courbes de survie XGBoost-AFT par contexte d'usinage")
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("survival_curves_xgboost.png", dpi=150)
Weibull vs Cox vs XGBoost : comparaison sur données réelles
Le tableau suivant synthétise les performances observées sur un historique de 847 inserts carbure (18 mois, 3 centres d’usinage) :
| Modèle | C-index (Harrell) | MAE cycles | Faux positifs remplacement | Défaillances manquées |
|---|---|---|---|---|
| Weibull 2P (baseline) | 0.61 | ±28 cycles | 34% | 12% |
| Cox PH (5 features) | 0.71 | ±19 cycles | 22% | 8% |
| XGBoost AFT (9 features) | 0.83 | ±11 cycles | 14% | 4% |
| XGBoost AFT + SHAP selection | 0.81 | ±12 cycles | 15% | 4% |
Le C-index (concordance de Harrell) mesure la capacité du modèle à correctement ordonner deux observations : si l’insert A cède avant l’insert B, le modèle prédit-il T_A < T_B ? Un modèle aléatoire donne 0.5, un modèle parfait donne 1.0.
Le gain de XGBoost sur Weibull n’est pas marginal : le C-index passe de 0.61 à 0.83, et les défaillances manquées tombent de 12% à 4%. Ce delta représente concrètement 8 arrêts non planifiés évités sur 100 inserts — chacun coûtant 3 à 8h d’arrêt.
Pourquoi XGBoost fait mieux
L’interprétation SHAP révèle les leviers réels :
indice_sante_broche(SHAP importance #1) — Une broche à 60% de santé réduit la durée de vie attendue de 25 à 35%. Weibull ne voit pas ça du tout.- Interaction
vitesse_coupe × dureté_HB— Le vrai problème : augmenter la vitesse sur une matière dure est non-linéaire. Ni Weibull ni Cox PH ne capturent cette interaction. heures_depuis_maintenance— L’état machine compte. Un centre d’usinage à 4500h depuis le dernier entretien génère 15% de casses supplémentaires à cycles équivalents.
ROI : le calcul qui convainc le directeur industriel
Présenter un C-index de 0.83 à un DAF ou à un directeur de production ne fonctionnera pas. Voici le calcul business.
Coût d’un arrêt non planifié (AN)
Structure de coût typique pour un centre d’usinage tour-fraisage :
| Poste | Estimation |
|---|---|
| Main-d’œuvre maintenance (appel d’urgence, 2h) | 200–350 € |
| Perte de production (3–8h à 80–150€/h) | 240–1200 € |
| Rebut pièce encours (si casse en cours de coupe) | 50–800 € |
| Réparation machine si dommage secondaire | 0–5000 € |
| Total arrêt non planifié | 490–7350 € |
Retenir le milieu : 1800 €/arrêt non planifié est une estimation conservatrice.
Coût d’un remplacement planifié (RP)
| Poste | Estimation |
|---|---|
| Insert carbure (plaquette standard) | 8–25 € |
| Temps opérateur (5 min) | 10–15 € |
| Arrêt machine planifié (inclus dans TPM) | ≈ 0 € |
| Total remplacement planifié | **18–40 € |
Ratio AN/RP = 45:1 à 185:1. La littérature industrielle cite habituellement 1:5 à 1:10 pour les arrêts non planifiés vs planifiés. Sur de l’usinage intensif avec risque de casse et dommages secondaires, le ratio monte bien au-delà.
Business case modèle XGBoost
Hypothèses : 300 inserts/mois, taux de défaillance manquée Weibull = 12%, XGBoost = 4%.
Défaillances évitées/mois = 300 × (12% - 4%) = 24 arrêts non planifiés évités
Économie brute/mois = 24 × 1800 € = 43 200 €/mois
Surcoût remplacement préventif supplémentaire (faux positifs réduits):
Weibull : 300 × 34% = 102 remplacements "préventifs" prématurés × 30 € = 3 060 €
XGBoost : 300 × 14% = 42 remplacements prématurés × 30 € = 1 260 €
Économie sur inserts gaspillés = 1 800 €/mois
Gain net mensuel ≈ 43 200 + 1 800 = 45 000 €/mois
Coût projet (data engineering + 3 mois de dev) : 40 000–80 000 €
Payback : < 2 mois
Ces chiffres sont indicatifs. Ils varient selon le type d’outillage, le secteur et les volumes. Mais l’ordre de grandeur — payback en semaines plutôt qu’en années — est réaliste sur des lignes à forte cadence.
Une fondation analytique scalaire
BCUB3 a formalisé cette logique avec une approche propriétaire : chaque outil peut être modélisé comme une Unité Atomique de Problème (UAP) — une variable mono-dimensionnelle dont le risque est quantifié par une distribution adaptée, le dimensionnement par un critère de gestion du risque, et la perte par une fonction de coût qualité.
L’extension XGBoost de survie est le pendant ML de cette approche : on passe d’une modélisation analytique scalaire (un outil = une distribution paramétrique) à une modélisation contextuelle multivariée (un outil dans un contexte = une distribution AFT apprise par boosting).
Les deux sont complémentaires :
- Approche analytique / UAP : quand on n’a pas de données contextuelles, quand on veut comprendre un outil de manière isolée, quand on a peu d’historique.
- XGBoost AFT : quand on a des logs machine (MES, SCADA), quand les conditions varient significativement, quand on optimise à l’échelle d’un parc.
Déploiement en production : les pièges à éviter
Piège 1 : la dérive de distribution (data drift)
Votre modèle est entraîné sur l’historique janvier–septembre. En octobre, l’appro change de fournisseur d’inserts (nouvelle géométrie). La distribution des durées de vie change. Le modèle ne le sait pas.
Solution : monitorer la distribution des prédictions hebdomadairement. Si la médiane prédite dérive de plus de 15% sans changement de features, déclencher un re-entraînement.
# Monitoring simple en production
from scipy.stats import ks_2samp
def check_drift(pred_historique, pred_recents, seuil=0.05):
stat, p_value = ks_2samp(pred_historique, pred_recents)
if p_value < seuil:
print(f"⚠️ Drift détecté (KS p={p_value:.4f}) — re-entraînement recommandé")
return p_value > seuil
Piège 2 : les données censurées sous-représentées
Si votre historique contient 90% de retraits planifiés (censurés) et 10% de vraies défaillances, le modèle sera optimiste. Il aura peu d’exemples pour apprendre la “zone critique”. Il faut s’assurer que le dataset de fine-tuning inclut suffisamment de défaillances réelles — quitte à biaiser l’échantillonnage en surpondérant les événements.
Piège 3 : l’intégration MES/ERP
Le modèle en Python ne sert à rien sans intégration dans le workflow opérateur. Il faut :
- Un endpoint REST qui reçoit l’état courant d’un outil (cycles, features) et retourne le cycle de remplacement recommandé
- Une alerte dans le MES quand le seuil est atteint (avant, pas pendant la coupe)
- Un feedback loop : enregistrer les vraies défaillances pour ré-entraîner tous les 3 mois
# API Flask minimale pour servir le modèle
from flask import Flask, request, jsonify
import xgboost as xgb
import numpy as np
app = Flask(__name__)
model = xgb.Booster()
model.load_model("model_survie_outillage.json")
@app.route("/predict", methods=["POST"])
def predict():
data = request.json
X = np.array([[
data["cycles_cumules"],
data["longueur_coupee_mm"],
data["vitesse_coupe_m_min"],
data["avance_mm_tour"],
data["profondeur_passe_mm"],
data["dureté_HB"],
data["grade_encode"],
data["indice_sante_broche"],
data["heures_depuis_maintenance"],
]])
dmat = xgb.DMatrix(X)
log_t = model.predict(dmat)[0]
t_median = float(np.exp(log_t))
# Seuil 80% survie (plus conservateur pour production critique)
from scipy import stats
t_80 = float(np.exp(log_t + 1.20 * stats.norm.ppf(0.20))) # S(t) = 0.80
return jsonify({
"t_median_cycles": round(t_median),
"t_remplacement_recommande": round(t_80),
"probabilite_survie_actuel": float(
stats.norm.sf(np.log(data["cycles_cumules"]), loc=log_t, scale=1.20)
)
})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5050)
Vers un système adaptatif : la boucle cybernétique
L’objectif final n’est pas un modèle statique mis à jour tous les trimestres. C’est une boucle cybernétique :
- Mesure continue : chaque cycle est loggué (capteurs CN, MES)
- Inférence temps réel : le modèle réévalue le risque à chaque fin de cycle
- Alerte proactive : notification opérateur N cycles avant le seuil
- Feedback : le résultat (défaillance / retrait propre / remplacement prématuré) est réingéré
- Évolution : le modèle se réentraîne sur les nouveaux labels, converge vers les conditions actuelles de production
Cette architecture s’intègre naturellement dans un système de maintenance conditionnelle (CBM — Condition Based Maintenance) et est le précurseur logique de la maintenance prédictive temps réel.
La différence avec les systèmes “maintenance prédictive” vendus par les éditeurs ERP à prix d’or ? Ici, le modèle est explicable via SHAP, ré-entraînable sur vos données propriétaires, et les prédictions sont des distributions probabilistes — pas des alertes binaires opaques.
Conclusion : Weibull ne disparaît pas, elle se contextualise
Weibull reste un outil fondamental. Elle est rapide à ajuster, transparente, et fonctionne sans collecte de données contextuelles. Pour un atelier qui commence, c’est le bon point de départ : collecter les durées de vie, ajuster une Weibull, fixer un seuil de remplacement conservateur. C’est déjà mieux qu’une règle empirique de 200 cycles pour tout le monde.
Quand les données MES sont disponibles, quand les conditions varient, quand le parc grossit : XGBoost AFT prend le relais. Il intègre les covariables que Weibull ignore et prédit une durée de vie personnalisée par contexte plutôt qu’une durée de vie moyenne de population.
La trajectoire est claire :
Règle empirique → Weibull → Cox PH → XGBoost AFT → Système adaptatif temps réel
Chaque étape est justifiable à son niveau de maturité data. L’erreur serait de sauter directement à l’étape 5 sans avoir les données et l’infrastructure pour la supporter — ou de rester bloqué à l’étape 1 parce que “ça a toujours marché comme ça”.
Si vous avez un historique de 200 défaillances et un export MES en CSV, vous avez assez pour démarrer un modèle XGBoost AFT et mesurer le gain. Le code ci-dessus tourne en moins d’une journée sur un laptop standard. Le ROI, lui, se compte en semaines.
BCUB3 accompagne les PME industrielles dans le déploiement de modèles de maintenance prédictive — de l’audit de la collecte de données jusqu’à l’intégration dans le MES. Contactez-nous pour évaluer le potentiel sur votre ligne.