Skip to content

Commit cbb7e32

Browse files
Adds new case study for MATLAB Agent (#16)
- Adds new case study on Industrial Cooling Fan Anomaly Detection Algorithm which has been taken from Matlab website. - Adds documentation and restructures existing examples. --------- Co-authored-by: prasadtalasila <prasadtalasila@users.noreply.github.com>
1 parent 825e2d9 commit cbb7e32

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1963
-290
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,13 @@ cython_debug/
179179
agents/matlab/client
180180
agents/matlab/logs
181181
agents/matlab/performance_log
182+
agents/matlab/matlab_agent/docs/examples/industrial-cooling-fan-anomaly-detection/Data/
182183
# Interactive simulation in progress
183184
agents/matlab/matlab_agent/src/interactive/
184185
agents/matlab/matlab_agent/docs/examples/InteractiveSimulation.m
185186
agents/matlab/matlab_agent/docs/examples/InteractiveSimulationWrapper.m
187+
# Simulink
188+
slprj/
186189
# Distribution files
187190
agents/matlab/dist/
188191
# Config files
@@ -198,4 +201,5 @@ simulation*.yaml
198201
#SIMULATION BRIDGE
199202
/logs
200203

201-
204+
.venv*
205+
certs

agents/matlab/README.md

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -349,17 +349,7 @@ Inside this folder, you'll find:
349349
- `simulation.yaml` — The simulation request payload that will be sent to the MATLAB Agent
350350
- `use_matlab_agent.py` — Python script to send the request and receive the results
351351

352-
**Steps to Run**
353-
354-
1. Open and configure `use.yaml` with the appropriate protocol settings.
355-
2. Open and configure `simulation.yaml` with the parameters of your simulation request.
356-
3. Run the script to send the request:
357-
358-
```bash
359-
python use_matlab_agent.py
360-
```
361-
362-
4. The script will send the simulation request to the MATLAB Agent and display the response received.
352+
For detailed instructions on how to configure and use the client, refer to the [Use Matlab Agent](./matlab_agent/resources/README.md) in the `agents/matlab/matlab_agent/resources/` folder.
363353

364354
## Workflow
365355

agents/matlab/matlab_agent/docs/examples/SimulationBatch1.m

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Batch Simulation
2+
3+
This example showcases a basic "Hello world" batch simulation that computes the future position of a ball moving in three-dimensional space, given an initial position and constant velocity along each axis.
4+
5+
The input parameters are:
6+
7+
- the initial position of the ball (`x_i`, `y_i`, `z_i`)
8+
- the velocity components (`v_x`, `v_y`, `v_z`)
9+
- the time duration `t` for which the ball moves
10+
11+
The simulation returns the final position (`x_f`, `y_f`, `z_f`) using basic equations of uniform linear motion.
12+
This is a simple, deterministic model used to test the simulation system with customizable input parameters.
13+
14+
## Table of Contents
15+
16+
- [Batch Simulation](#batch-simulation)
17+
- [Table of Contents](#table-of-contents)
18+
- [Usage](#usage)
19+
20+
## Usage
21+
22+
Before running the simulation, you need to configure the Matlab agent by setting the simulation folder path in the `config.yaml` file under the simulation section:
23+
24+
```yaml
25+
simulation:
26+
path: <path_to_simulation_folder>
27+
```
28+
29+
This path should point to the directory `batch-simulation` containing the simulation files
30+
31+
Once configured, you can initiate the simulation using the API as described below.
32+
33+
The simulation can be initiated via the API by submitting a YAML payload, a template of which is available in the file `api/simulation.yaml`
34+
35+
```yaml
36+
simulation:
37+
request_id: abcdef12345 # Unique identifier for the simulation request
38+
client_id: dt # ID of the client making the request (e.g., Digital Twin)
39+
simulator: matlab # Specifies MATLAB as the simulation engine
40+
type: batch # Simulation type: 'batch' means a one-time execution, not continuous/streaming
41+
file: SimulationBatch.m # Name of the MATLAB file to run for the simulation
42+
inputs:
43+
x_i: 10 # Initial x-coordinate of the ball
44+
y_i: 9 # Initial y-coordinate of the ball
45+
z_i: 0 # Initial z-coordinate of the ball
46+
v_x: 1 # Velocity along the x-axis
47+
v_y: 14 # Velocity along the y-axis
48+
v_z: 3 # Velocity along the z-axis
49+
t: 10 # Duration of motion (time)
50+
outputs:
51+
x_f: Final x position # Output field name for the final x-coordinate
52+
y_f: Final y position # Output field name for the final y-coordinate
53+
z_f: Final z position # Output field name for the final z-coordinate
54+
```
55+
56+
Use the client `use_matlab_agent.py` with the CLI option `--api-payload` to specify the path to this YAML payload file and start the client.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
simulation:
2+
request_id: abcdef12345 # Unique identifier for the simulation request
3+
client_id: dt # ID of the client making the request (e.g., Digital Twin)
4+
simulator: matlab # Specifies MATLAB as the simulation engine
5+
type: batch # Simulation type: 'batch' means a one-time execution, not continuous/streaming
6+
file: SimulationBatch.m # Name of the MATLAB file to run for the simulation
7+
inputs:
8+
x_i: 10 # Initial x-coordinate of the ball
9+
y_i: 9 # Initial y-coordinate of the ball
10+
z_i: 0 # Initial z-coordinate of the ball
11+
v_x: 1 # Velocity along the x-axis
12+
v_y: 14 # Velocity along the y-axis
13+
v_z: 3 # Velocity along the z-axis
14+
t: 10 # Duration of motion (time)
15+
outputs:
16+
x_f: Final x position # Output field name for the final x-coordinate
17+
y_f: Final y position # Output field name for the final y-coordinate
18+
z_f: Final z position # Output field name for the final z-coordinate
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
%% CoolingFanAnomalyDetectionExample
2+
% This is the original example simulation that has been copied and modified to
3+
% support batch constraints inside simulation.m. The current file serves as a reference
4+
% for the original implementation before the modifications were made to handle
5+
% batch processing constraints.
6+
7+
addpath('Data_Generator/', 'Data_Generator/VaryingConvectionLib/');
8+
mdl = "CoolingFanWithFaults";
9+
open_system(mdl)
10+
11+
generateFlag = false;
12+
if isfolder('./Data')
13+
folderContent = dir('Data/CoolingFan*.mat');
14+
if isempty(folderContent)
15+
generateFlag = true;
16+
else
17+
numSim = numel(folderContent);
18+
end
19+
else
20+
generateFlag = true;
21+
end
22+
23+
if generateFlag
24+
numSim = 5;
25+
rng("default");
26+
generateData('training', numSim);
27+
end
28+
29+
ensemble = simulationEnsembleDatastore(pwd,'simulation','Data');
30+
ensemble.SelectedVariables = ["Signals", "Anomalies"];
31+
32+
trainEnsemble = subset(ensemble, 1:numSim-3);
33+
testEnsemble = subset(ensemble, numSim-2:numSim-1);
34+
validationEnsemble = subset(ensemble, numSim);
35+
36+
tempData = trainEnsemble.read;
37+
signal_data = tempData.Signals{1};
38+
anomaly_data = tempData.Anomalies{1};
39+
40+
h = figure;
41+
tiledlayout("vertical");
42+
ax1 = nexttile; plot(signal_data.Data(:,1));
43+
ax2 = nexttile; plot(signal_data.Data(:,2));
44+
ax3 = nexttile; plot(signal_data.Data(:,3));
45+
ax4 = nexttile; plot(anomaly_data.Data(:,1), 'bx', 'MarkerIndices',find(anomaly_data.Data(:,1)>0.1), 'MarkerSize', 5); hold on;
46+
plot(anomaly_data.Data(:,2), 'ro', 'MarkerIndices',find(anomaly_data.Data(:,2)>0.1), 'MarkerSize', 5);
47+
plot(anomaly_data.Data(:,3), 'square', 'Color', 'g', 'MarkerIndices',find(anomaly_data.Data(:,3)>0.1), 'MarkerSize', 5);
48+
linkaxes([ax1 ax2 ax3 ax4],'x')
49+
ylim([0, 2]);
50+
legend("Load Anomaly", "Fan Anomaly", "Power Supply Anomaly", 'Location', 'bestoutside')
51+
set(h,'Units','normalized','Position',[0 0 1 .8]);
52+
53+
trainingData = trainEnsemble.readall;
54+
anomaly_data = vertcat(trainingData.Anomalies{:});
55+
labelArray={'Normal','Anomaly1','Anomaly2', 'Anomaly3', 'Anomaly12','Anomaly13','Anomaly23','Anomaly123'}';
56+
yCategoricalTrain=labelArray(sum(anomaly_data.Data.*2.^(size(anomaly_data.Data,2)-1:-1:0),2)+1,1);
57+
yCategoricalTrain=categorical(yCategoricalTrain);
58+
summary(yCategoricalTrain);
59+
60+
yTrainAnomaly1=logical(anomaly_data.Data(:,1));
61+
yTrainAnomaly2=logical(anomaly_data.Data(:,2));
62+
yTrainAnomaly3=logical(anomaly_data.Data(:,3));
63+
64+
windowSize=2000;
65+
sensorDataTrain = vertcat(trainingData.Signals{:});
66+
ensembleTableTrain1 = generateEnsembleTable(sensorDataTrain.Data,yTrainAnomaly1,windowSize);
67+
ensembleTableTrain2 = generateEnsembleTable(sensorDataTrain.Data,yTrainAnomaly2,windowSize);
68+
ensembleTableTrain3 = generateEnsembleTable(sensorDataTrain.Data,yTrainAnomaly3,windowSize);
69+
70+
ensembleTableTrain1_reduced = downsampleNormalData(ensembleTableTrain1);
71+
ensembleTableTrain2_reduced = downsampleNormalData(ensembleTableTrain2);
72+
ensembleTableTrain3_reduced = downsampleNormalData(ensembleTableTrain3);
73+
74+
startApp = false;
75+
if startApp
76+
diagnosticFeatureDesigner; %#ok<UNRCH>
77+
end
78+
79+
featureTableTrain1 = diagnosticFeatures(ensembleTableTrain1_reduced);
80+
featureTableTrain2 = diagnosticFeatures(ensembleTableTrain2_reduced);
81+
featureTableTrain3 = diagnosticFeatures(ensembleTableTrain3_reduced);
82+
83+
head(featureTableTrain1);
84+
85+
m.m1 = fitcsvm(featureTableTrain1, 'Anomaly', 'Standardize',true, 'KernelFunction','gaussian');
86+
m.m2 = fitcsvm(featureTableTrain2, 'Anomaly', 'Standardize',true, 'KernelFunction','gaussian');
87+
m.m3 = fitcsvm(featureTableTrain3, 'Anomaly', 'Standardize',true, 'KernelFunction','gaussian');
88+
89+
save anomalyDetectionModelSVM m
90+
91+
testData = testEnsemble.readall;
92+
anomaly_data_test = vertcat(testData.Anomalies{:});
93+
94+
yTestAnomaly1=logical(anomaly_data_test.Data(:,1));
95+
yTestAnomaly2=logical(anomaly_data_test.Data(:,2));
96+
yTestAnomaly3=logical(anomaly_data_test.Data(:,3));
97+
98+
sensorDataTest = vertcat(testData.Signals{:});
99+
ensembleTableTest1 = generateEnsembleTable(sensorDataTest.Data,yTestAnomaly1,windowSize);
100+
ensembleTableTest2 = generateEnsembleTable(sensorDataTest.Data,yTestAnomaly2,windowSize);
101+
ensembleTableTest3 = generateEnsembleTable(sensorDataTest.Data,yTestAnomaly3,windowSize);
102+
103+
featureTableTest1 = diagnosticFeatures(ensembleTableTest1);
104+
featureTableTest2 = diagnosticFeatures(ensembleTableTest2);
105+
featureTableTest3 = diagnosticFeatures(ensembleTableTest3);
106+
107+
results1 = m.m1.predict(featureTableTest1(:, 2:end));
108+
results2 = m.m2.predict(featureTableTest2(:, 2:end));
109+
results3 = m.m3.predict(featureTableTest3(:, 2:end));
110+
111+
yT = [featureTableTest1.Anomaly, featureTableTest2.Anomaly, featureTableTest3.Anomaly];
112+
yCategoricalT = labelArray(sum(yT.*2.^(size(yT,2)-1:-1:0),2)+1,1);
113+
yCategoricalT = categorical(yCategoricalT);
114+
115+
yHat = [results1, results2, results3];
116+
yCategoricalTestHat=labelArray(sum(yHat.*2.^(size(yHat,2)-1:-1:0),2)+1,1);
117+
yCategoricalTestHat=categorical(yCategoricalTestHat);
118+
119+
figure
120+
confusionchart(yCategoricalT, yCategoricalTestHat)
121+
122+
figure
123+
tiledlayout(1,3);
124+
nexttile; confusionchart(featureTableTest1.Anomaly, results1);
125+
nexttile; confusionchart(featureTableTest2.Anomaly, results2);
126+
nexttile; confusionchart(featureTableTest3.Anomaly, results3);

0 commit comments

Comments
 (0)