How to work Edge AI with ML.NET .Anomaly Detection Tutorial
Scripts → Tutorial | Concept | How-to Guide | Reference
This
Tutorial Teaches you to:Edge AI with ML.NET (Tutorial)tutorial demonstrates using ML.NET 4.0 for real-time anomaly detection on sensor data using FrameworX Script
TasksClasses and displays.
Table of Contents maxLevel 2 minLevel 2 indent 10px exclude Steps style none
Tag.SensorValueMixerVibration
(Double) - Current readingSimulated sensor valueTag.AnomalyScoreAnomalies
(DoubleString) - Detection scoreWill hold the anomaly listTag.IsAnomalySimulatorSeed
(BooleanInteger) - Alert flagTag.Threshold
(Double) - Detection threshold (default: 0.3)AnomalyDetector
ML_Anomaly
csharp
// Simple spike detection using ML.NET
using Microsoft.ML;
using Microsoft.ML.Data;
// Static ML context (initialized once)
if (@Tag.MLContext == null)
{
@Tag.MLContext = new MLContext(seed: 0);
@Tag.DetectionEngine = InitializeDetector();
}
// Data class for ML model
public class SensorData
{ HOW TO USE
// Create an expression or Task to run periodically with the code:
// @Script.Class.ML_Anomaly.Check(@Tag.YourSensorValue);
// To retrieve anomalies: var list = @Script.Class.ML_Anomaly.GetAnomalies();
// Check if value is anomalous
public void Check(double currentValue) {
// Initialize ML engine if needed
if (_engine == null)
Initialize();
// Create data point
var data = new SensorData {
Timestamp = DateTime.Now,
SensorValue = currentValue
};
// Get prediction from ML model
AnomalyPrediction prediction = _engine.Predict(data);
// Store anomaly if detected
if (prediction.Prediction[0] >= 1) { // >= 1 means anomaly detected
if (_anomalyBuffer.Count >= 10)
_anomalyBuffer.Dequeue();
_anomalyBuffer.Enqueue($"{DateTime.Now:yyyy-MM-dd HH:mm:ss}|{currentValue:F3}|{prediction.Prediction[1]:F2}");
}
}
// Internal buffer to store last 10 anomalies
private static Queue<string> _anomalyBuffer = new Queue<string>(10);
// Method to retrieve anomalies and clear buffer
public string GetAnomalies() {
if (_anomalyBuffer.Count == 0)
return "No new anomalies detected";
var anomalies = string.Join(Environment.NewLine, _anomalyBuffer);
_anomalyBuffer.Clear();
return anomalies;
}
//The following code is standard - no changes needed
public class SensorData {
public DateTime Timestamp { get; set; }
public floatdouble ValueSensorValue { get; set; }
}
public class AnomalyPrediction
{ {
// SR-CNN returns an array with 3 doubles:
// [0] = flag (0 or 1) - Binary indicator if anomaly detected
// [1] = score (0.0 to 1.0) - Confidence/severity of the anomaly
// [2] = baseline - Expected normal value at this point
// Note: This tutorial uses [0] for simplicity. Advanced users can leverage [1] and [2] for custom thresholds.
[VectorType(3)]
public double[] Prediction { get; set; }
}
// Initialize detector (runs once)
private ITransformer InitializeDetector()
private ITransformer _model;
private TimeSeriesPredictionEngine<SensorData, AnomalyPrediction> _engine;
private MLContext _mlContext;
List<SensorData> dataTrain;
private void Initialize() {
var_mlContext dataView = @Tag.MLContext.Data.LoadFromEnumerable(new List<SensorData>MLContext());
if (dataTrain == null) {
var pipeline DateTime now = @Tag.MLContext.TransformsDateTime.Now;
.DetectSpikeBySsa(
// Training data with clear normal outputColumnName: "Prediction",pattern (wave-like)
var trainingData = inputColumnName: "Value",new List<(int minutesAgo, double value)> {
(15, 0.20), confidence: 95,(14, 0.25), (13, 0.30), (12, 0.25), // Rising
pvalueHistoryLength: 30,
trainingWindowSize: 90,(11, 0.20), (10, 0.15), (9, 0.20), (8, 0.25), // Valley to peak
seasonalityWindowSize: 30);(7, 0.30), (6, 0.25), (5, 0.20), (4, 0.15), // Falling
return pipeline.Fit(dataView);
}
// Detection logic (runs every second)
var currentValue = (float)@Tag.SensorValue;
var data = (3, 0.20), (2, 0.25), (1, 0.30), (0, 0.25) // Rising
};
dataTrain = new List<SensorData>();
foreach (var item in trainingData) {
dataTrain.Add(new SensorData { Value = currentValue };
var prediction
Timestamp = @Tag.DetectionEngine.Transform(now.AddMinutes(-item.minutesAgo),
@Tag.MLContext.Data.LoadFromEnumerable(new[] { data }));
var result = @Tag.MLContext.Data
.CreateEnumerable<AnomalyPrediction>(prediction, false)
.First();
// Update tags with results
@Tag.AnomalyScore = result.Prediction[0]; // Spike score
@Tag.IsAnomaly = result.Prediction[0] > @Tag.Threshold;
// Log anomalies
if (@Tag.IsAnomaly)
{
@Alarm.GlobalSettings.AuditTrail.AddCustomMessage(
$"Anomaly detected: Sensor={currentValue:F2}, Score={result.Prediction[0]:F3}");
}
SensorValue = item.value
});
}
}
// SR-CNN in ML.NET - use these default values for most applications
SrCnnAnomalyEstimator pipeline = _mlContext.Transforms
.DetectAnomalyBySrCnn(
outputColumnName: nameof(AnomalyPrediction.Prediction),
inputColumnName: nameof(SensorData.SensorValue),
windowSize: 32,
backAddWindowSize: 32,
lookaheadWindowSize: 16,
averagingWindowSize: 16,
judgementWindowSize: 16,
threshold: 0.15);
IDataView dataView = _mlContext.Data.LoadFromEnumerable(dataTrain);
_model = pipeline.Fit(dataView);
_engine = _model.CreateTimeSeriesEngine<SensorData, AnomalyPrediction>(_mlContext);
// Train the model with initial data
foreach (var row in dataTrain) {
_engine.Predict(row);
}
}
SimulateVibration
Create task: SensorSimulator
csharp
// Simulate normal sensor data with occasional spikes anomalies
if (@Tag.SimulatorSeed == 0)
@Tag.SimulatorSeed = Environment.TickCount;
Random rand = new Random(@Tag.SimulatorSeed + DateTime.Now.Millisecond);
// Base vibration level with sine wave pattern
double time = DateTime.Now.Ticks / 10000000.0;
double baseValue = 500.25 + 0.05 * Math.Sin(time);
double noise = (rand.NextDouble() *- 0.5) -* 20.502;
// Inject5% anomalychance occasionally (5% chance)of anomaly
if (rand.NextDouble() < 0.05)
{
@Tag.SensorValueMixerVibration = baseValue + (rand.NextDouble() * 300.5 + 200.3); // Spike
}
else
{
@Tag.SensorValueMixerVibration = baseValue + noise; // Normal variation
}
MonitorVibration
csharp
// Call the ML anomaly detection
@Script.Class.ML_Anomaly.Check(@Tag.MixerVibration);
Last 10 anomalies
Tag.Anomalies
OutputOnly
Refresh
MouseLeftButtonDown
RunExpressions
Tag.Anomalies
Script.Class.ML_Anomaly.GetAnomalies()
timestamp|value|severity
Example output:
2025-01-15 10:23:45|0.895|0.82
2025-01-15 10:24:12|0.756|0.91
2025-01-15 10:25:33|0.923|0.75
? ML.NET Integration: Must enable ML.NET namespaces when creating script class
? Simple API: Single method call Check()
for detection
? Self-contained: No external tags needed for ML state
? Production Ready: Can replace simulator with real sensor tags
This completes the ML.NET anomaly detection implementation in FrameworX!
Page Tree | ||
---|---|---|
|