Manufacturing Sensor Drift Detection
Tested against SynapCores CE v1.7.0.1-ce (the currently-shipped release on Docker Hub:
synapcores/community:v1.7.0.1-ce).
Objective
Detect when a single machine's sensor drifts away from its own historical baseline — even if its absolute reading still looks "normal" compared to the fleet. The pattern is partition-by-machine + window AVG/STDDEV.
Why this matters: every machine has its own thermal/wear signature. Plant-wide thresholds either fire constantly (sensitive machines) or miss real drift (tolerant ones). Per-machine self-baselining is what the OEM service contracts charge $100K/yr for; it's a 4-line SQL query in AIDB.
Step 1 — Schema + multi-machine telemetry
3 machines × 80 readings = 240 samples. M2 drifts hot in its last 20 readings (simulating a failing bearing).
DROP TABLE IF EXISTS factory_temp;
CREATE TABLE factory_temp (
id INTEGER PRIMARY KEY,
machine_id TEXT,
temp_c DOUBLE,
reading_seq INTEGER
);
INSERT INTO factory_temp VALUES
(1,'M1',74.48,0),
(2,'M1',73.58,1),
(3,'M1',73.16,2),
(4,'M1',73.59,3),
(5,'M1',73.08,4),
(6,'M1',70.33,5),
(7,'M1',71.64,6),
(8,'M1',73.06,7),
(9,'M1',71.45,8),
(10,'M1',68.93,9),
(11,'M1',71.22,10),
(12,'M1',70.72,11),
(13,'M1',71.07,12),
(14,'M1',72.79,13),
(15,'M1',71.75,14),
(16,'M1',70.75,15),
(17,'M1',70.34,16),
(18,'M1',71.64,17),
(19,'M1',71.77,18),
(20,'M1',71.74,19),
(21,'M1',72.23,20),
(22,'M1',73.86,21),
(23,'M1',69.93,22),
(24,'M1',71.02,23),
(25,'M1',72.82,24),
(26,'M1',72.28,25),
(27,'M1',70.01,26),
(28,'M1',71.98,27),
(29,'M1',72.69,28),
(30,'M1',73.92,29),
(31,'M1',71.13,30),
(32,'M1',72.55,31),
(33,'M1',72.27,32),
(34,'M1',71.5,33),
(35,'M1',72.95,34),
(36,'M1',71.58,35),
(37,'M1',71.48,36),
(38,'M1',72.62,37),
(39,'M1',69.74,38),
(40,'M1',72.46,39),
(41,'M1',72.28,40),
(42,'M1',73.5,41),
(43,'M1',71.05,42),
(44,'M1',72.37,43),
(45,'M1',73.27,44),
(46,'M1',73.35,45),
(47,'M1',70.84,46),
(48,'M1',72.69,47),
(49,'M1',71.54,48),
(50,'M1',71.66,49),
(51,'M1',73.04,50),
(52,'M1',69.7,51),
(53,'M1',71.94,52),
(54,'M1',74.21,53),
(55,'M1',71.68,54),
(56,'M1',71.04,55),
(57,'M1',71.94,56),
(58,'M1',72.5,57),
(59,'M1',70.87,58),
(60,'M1',72.68,59),
(61,'M1',70.63,60),
(62,'M1',72.11,61),
(63,'M1',71.41,62),
(64,'M1',71.81,63),
(65,'M1',72.24,64),
(66,'M1',73.52,65),
(67,'M1',71.27,66),
(68,'M1',72.13,67),
(69,'M1',71.44,68),
(70,'M1',75.29,69),
(71,'M1',72.59,70),
(72,'M1',70.16,71),
(73,'M1',71.23,72),
(74,'M1',72.99,73),
(75,'M1',71.57,74),
(76,'M1',70.52,75),
(77,'M1',71.79,76),
(78,'M1',71.75,77),
(79,'M1',71.81,78),
(80,'M1',71.22,79),
(81,'M2',67.81,0),
(82,'M2',69.8,1),
(83,'M2',69.66,2),
(84,'M2',68.12,3),
(85,'M2',68.0,4),
(86,'M2',67.85,5),
(87,'M2',69.34,6),
(88,'M2',68.16,7),
(89,'M2',66.75,8),
(90,'M2',68.04,9),
(91,'M2',68.09,10),
(92,'M2',67.26,11),
(93,'M2',68.4,12),
(94,'M2',68.36,13),
(95,'M2',70.03,14),
(96,'M2',68.36,15),
(97,'M2',67.74,16),
(98,'M2',67.98,17),
(99,'M2',66.79,18),
(100,'M2',68.71,19),
(101,'M2',67.98,20),
(102,'M2',68.82,21),
(103,'M2',69.31,22),
(104,'M2',70.8,23),
(105,'M2',68.9,24),
(106,'M2',69.89,25),
(107,'M2',68.29,26),
(108,'M2',67.68,27),
(109,'M2',68.98,28),
(110,'M2',67.22,29),
(111,'M2',69.27,30),
(112,'M2',68.23,31),
(113,'M2',67.48,32),
(114,'M2',69.01,33),
(115,'M2',68.32,34),
(116,'M2',69.75,35),
(117,'M2',67.42,36),
(118,'M2',69.03,37),
(119,'M2',69.22,38),
(120,'M2',67.72,39),
(121,'M2',70.38,40),
(122,'M2',69.19,41),
(123,'M2',68.25,42),
(124,'M2',68.61,43),
(125,'M2',68.56,44),
(126,'M2',66.57,45),
(127,'M2',67.25,46),
(128,'M2',66.66,47),
(129,'M2',68.22,48),
(130,'M2',70.78,49),
(131,'M2',66.94,50),
(132,'M2',70.29,51),
(133,'M2',70.03,52),
(134,'M2',68.49,53),
(135,'M2',66.78,54),
(136,'M2',69.42,55),
(137,'M2',68.23,56),
(138,'M2',67.1,57),
(139,'M2',69.48,58),
(140,'M2',69.05,59),
(141,'M2',80.4,60),
(142,'M2',80.29,61),
(143,'M2',77.88,62),
(144,'M2',82.19,63),
(145,'M2',80.26,64),
(146,'M2',79.44,65),
(147,'M2',81.7,66),
(148,'M2',81.87,67),
(149,'M2',79.64,68),
(150,'M2',79.74,69),
(151,'M2',76.18,70),
(152,'M2',80.07,71),
(153,'M2',79.47,72),
(154,'M2',79.08,73),
(155,'M2',81.97,74),
(156,'M2',81.11,75),
(157,'M2',84.07,76),
(158,'M2',81.01,77),
(159,'M2',78.61,78),
(160,'M2',80.3,79),
(161,'M3',74.05,0),
(162,'M3',73.89,1),
(163,'M3',75.01,2),
(164,'M3',72.52,3),
(165,'M3',76.6,4),
(166,'M3',75.36,5),
(167,'M3',76.63,6),
(168,'M3',71.48,7),
(169,'M3',76.43,8),
(170,'M3',75.67,9),
(171,'M3',74.37,10),
(172,'M3',73.3,11),
(173,'M3',75.78,12),
(174,'M3',75.06,13),
(175,'M3',75.6,14),
(176,'M3',74.78,15),
(177,'M3',74.61,16),
(178,'M3',73.07,17),
(179,'M3',73.58,18),
(180,'M3',75.28,19),
(181,'M3',74.52,20),
(182,'M3',74.48,21),
(183,'M3',72.16,22),
(184,'M3',73.33,23),
(185,'M3',74.76,24),
(186,'M3',74.17,25),
(187,'M3',73.48,26),
(188,'M3',76.29,27),
(189,'M3',73.67,28),
(190,'M3',76.18,29),
(191,'M3',76.41,30),
(192,'M3',73.34,31),
(193,'M3',72.82,32),
(194,'M3',72.84,33),
(195,'M3',74.61,34),
(196,'M3',73.68,35),
(197,'M3',73.19,36),
(198,'M3',75.87,37),
(199,'M3',72.16,38),
(200,'M3',74.48,39),
(201,'M3',76.1,40),
(202,'M3',73.89,41),
(203,'M3',72.58,42),
(204,'M3',73.17,43),
(205,'M3',74.69,44),
(206,'M3',73.96,45),
(207,'M3',76.23,46),
(208,'M3',74.82,47),
(209,'M3',75.26,48),
(210,'M3',72.8,49),
(211,'M3',73.29,50),
(212,'M3',74.46,51),
(213,'M3',76.07,52),
(214,'M3',74.01,53),
(215,'M3',75.96,54),
(216,'M3',74.05,55),
(217,'M3',74.73,56),
(218,'M3',73.51,57),
(219,'M3',74.1,58),
(220,'M3',73.67,59),
(221,'M3',73.59,60),
(222,'M3',75.36,61),
(223,'M3',70.44,62),
(224,'M3',73.64,63),
(225,'M3',73.81,64),
(226,'M3',73.78,65),
(227,'M3',74.8,66),
(228,'M3',72.26,67),
(229,'M3',75.55,68),
(230,'M3',71.74,69),
(231,'M3',74.48,70),
(232,'M3',72.78,71),
(233,'M3',73.99,72),
(234,'M3',73.85,73),
(235,'M3',73.33,74),
(236,'M3',75.07,75),
(237,'M3',74.16,76),
(238,'M3',73.77,77),
(239,'M3',74.45,78),
(240,'M3',75.06,79)
;
Step 2 — Per-machine baseline
SELECT machine_id, AVG(temp_c) AS mu, STDDEV(temp_c) AS sigma, COUNT(*) AS n
FROM factory_temp
GROUP BY machine_id;
-- → M1: μ=71.95, σ=1.18
-- → M2: μ=71.43, σ=5.28 ← σ is 4× M1's: drift detected
-- → M3: μ=74.23, σ=1.28
The wide σ on M2 is the smoking gun. M1 and M3 are tight; M2 is oscillating because the last 20 readings have drifted up.
Step 3 — Per-row deviation against the machine's own baseline
SELECT id, machine_id, temp_c, reading_seq,
AVG(temp_c) OVER (PARTITION BY machine_id) AS grp_mu,
STDDEV(temp_c) OVER (PARTITION BY machine_id) AS grp_sigma
FROM factory_temp
ORDER BY machine_id, reading_seq;
-- The grp_mu/grp_sigma columns per row let you compute (temp - mu)/sigma
-- downstream as the per-row Z-score.
Step 4 — Surface drifted readings
-- The "alert" view: any reading > 2.5σ above its machine's own baseline
SELECT machine_id, COUNT(*) AS drifted_readings
FROM (
SELECT machine_id, temp_c,
AVG(temp_c) OVER (PARTITION BY machine_id) AS grp_mu,
STDDEV(temp_c) OVER (PARTITION BY machine_id) AS grp_sigma
FROM factory_temp
) t
WHERE temp_c > grp_mu + 2.5 * grp_sigma
GROUP BY machine_id;
-- → M2 lights up; M1 and M3 are quiet.
Productionizing
Stream OPC-UA / MQTT telemetry into AIDB via the ingest endpoint. Run
the per-machine baseline as a 24h rolling window (so a slowly drifting
machine eventually rebaselines but a sudden drift fires immediately).
Add vibration_hz and current_amps as extra columns for tri-axial
drift detection.