Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

How to work Edge AI with ML.NET .Anomaly Detection Tutorial

Scripts → Tutorial | Concept | How-to Guide | Reference


Overview

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

Tasks

Classes and displays.

Prerequisites

:

  • Complete Scripts & Data Enrichment (Tutorial)
  • Tags for sensor monitoring
    • FrameworX with Scripts module
    • ML.NET 4.0 references (included in FrameworX)
    • Basic understanding of tags and displays

    Table of Contents
    maxLevel2
    minLevel2
    indent10px
    excludeSteps
    stylenone


    Step 1: Create Monitoring Tags

    1. Navigate to Unified Namespace → Tags
    2. Create the following tags:
      • Tag.SensorValueMixerVibration (Double) - Current readingSimulated sensor value
      • Tag.AnomalyScoreAnomalies (DoubleString) - Detection scoreWill hold the anomaly list
      • Tag.IsAnomalySimulatorSeed (BooleanInteger) - Alert flag
      • Tag.Threshold (Double) - Detection threshold (default: 0.3)
      • For simulation consistency

    Step 2: Create ML Anomaly Detection

    Task

    Script Class

    1. Go Navigate to Scripts → TasksClasses
    2. Create task: AnomalyDetector
    3. Set trigger: Period = 1000ms
    4. Click "Create new script"
    5. Configure the new script:
      • Name: ML_Anomaly
      • Language: CSharp
      • Domain: Server
      • Select "Create new Code"Methods (Default)
      • ? Check "Add ML.NET namespaces" (Required for ML functionality)
    6. Click OK
    7. Replace the default code withAdd code:

    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}");
    }

    Step 3: Create Simple Simulator

                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);
        }
    }

    Step 3: Create Value Simulator

    1. Go to Scripts → Tasks
    2. Create task: SimulateVibrationCreate task: SensorSimulator
    3. Set trigger: Period = 500ms
    4. Add simulation code:

    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
    }

    Step 4: Create Monitoring Task

    1. Go to Scripts → Tasks
    2. Create task: MonitorVibration
    3. Set trigger: Period = 1000ms (1 second)
    4. Add code:

    csharp

    // Call the ML anomaly detection
    @Script.Class.ML_Anomaly.Check(@Tag.MixerVibration);

    Step 5: Create Display Components

    1. Create display with:
      • Trend chart for SensorValue
      • Indicator for IsAnomaly
      • Text display for AnomalyScore
      • Threshold adjustment slider

    Step 5: Test ML Detection

    1. Start runtime
    2. Observe sensor simulation
    3. Watch for anomaly detection
    4. Adjust threshold as needed
    5. Check audit trail for logged anomalies

    Next Steps

    • Advanced MCP Tools → Complex multi-tool scenarios
    • ML.NET Models → Regression and classification
    • Edge Computing → Deploy to field devices
    These tutorials provide simple, practical starting points for both MCP Tools and ML.NET integration, focusing on real industrial scenarios while keeping complexity minimal for learning purposes.
    1. Open Displays → Create New Display
    2. Add a TextBox for Results:
      • Add a TextBox control
      • Configure in Settings tab:
        • Label Text: Last 10 anomalies
        • Linked Value: Tag.Anomalies
        • Binding: OutputOnly
        • ? Enable Multiline
    3. Add a Refresh Button:
      • Add a Button control
      • Label: Refresh
      • In the Dynamics tab, add an Action:
        • Event: MouseLeftButtonDown
        • Action: RunExpressions
        • Result (optional): Tag.Anomalies
        • Expression: Script.Class.ML_Anomaly.GetAnomalies()

    Step 6: Test the System

    1. Start Runtime (F5)
    2. Open your display
    3. Observe the simulated vibration values
    4. Click "Refresh" button periodically
    5. The TextBox will display:
      • "No new anomalies detected" when buffer is empty
      • List of anomalies with format: 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

    How It Works

    1. SimulateVibration generates realistic sensor data with 5% anomalies
    2. MonitorVibration checks each value using ML model
    3. ML_Anomaly class uses SR-CNN algorithm to detect spikes
    4. Anomalies are buffered internally (max 10)
    5. Refresh button retrieves and clears the buffer

    Key Learning Points

    ? 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


    Next Steps

    • Connect real sensor data instead of simulator
    • Adjust SR-CNN threshold (0.15) for sensitivity
    • Crate alarms for critical anomalies
    • Store anomaly history in Historian
    • Create trending displays for pattern analysis

    This completes the ML.NET anomaly detection implementation in FrameworX!


    In this section...

    Page Tree
    root@parent