Main Content

Using Simulink to Generate Fault Data

This example shows how to use a Simulink® model to generate fault and healthy data. The fault and healthy data is used to develop a condition monitoring algorithm. The example uses a transmission system and models a gear tooth fault, a sensor drift fault and shaft wear fault.

Transmission System Model

The transmission casing model uses Simscape™ Driveline™ blocks to model a simple transmission system. The transmission system consists of a torque drive, drive shaft, clutch, and high and low gears connected to an output shaft.

mdl = 'pdmTransmissionCasing';
open_system(mdl)

The transmission system includes a vibration sensor that is monitoring casing vibrations. The casing model translates the shaft angular displacement to a linear displacement on the casing. The casing is modelled as a mass spring damper system and the vibration (casing acceleration) is measured from the casing.

open_system([mdl '/Casing'])

Fault Modelling

The transmission system includes fault models for vibration sensor drift, gear tooth fault, and shaft wear. The sensor drift fault is easily modeled by introducing an offset in the sensor model. The offset is controlled by the model variable SDrift, note that SDrift=0 implies no sensor fault.

open_system([mdl '/Vibration sensor with drift'])

The shaft wear fault is modeled by a variant subsystem. In this case the subsystem variants change the shaft damping but the variant subsystem could be used to completely change the shaft model implementation. The selected variant is controlled by the model variable ShaftWear, note that ShaftWear=0 implies no shaft fault.

open_system([mdl '/Shaft'])

open_system([mdl,'/Shaft/Output Shaft'])

The gear tooth fault is modelled by injecting a disturbance torque at a fixed position in the rotation of the drive shaft. The shaft position is measured in radians and when the shaft position is within a small window around 0 a disturbance force is applied to the shaft. The magnitude of the disturbance is controlled by the model variable ToothFaultGain, note that ToothFaultGain=0 implies no gear tooth fault.

Simulating Fault and Healthy Data

The transmission model is configured with variables that control the presence and severity of the three different fault types, sensor drift, gear tooth, and shaft wear. By varying the model variables, SDrift, ToothFaultGain, and ShaftWear, you can create vibration data for the different fault types. Use an array of Simulink.SimulationInput objects to define a number of different simulation scenarios. For example, choose an array of values for each of the model variables and then use the ndgrid function to create a Simulink.SimulationInput for each combination of model variable values.

toothFaultArray = -2:2/10:0; % Tooth fault gain values
sensorDriftArray = -1:0.5:1; % Sensor drift offset values
shaftWearArray = [0 -1];       % Variants available for drive shaft conditions

% Create an n-dimensional array with combinations of all values
[toothFaultValues,sensorDriftValues,shaftWearValues] = ...
    ndgrid(toothFaultArray,sensorDriftArray,shaftWearArray);

for ct = numel(toothFaultValues):-1:1
    % Create a Simulink.SimulationInput for each combination of values
    siminput = Simulink.SimulationInput(mdl);
    
    % Modify model parameters
    siminput = setVariable(siminput,'ToothFaultGain',toothFaultValues(ct));
    siminput = setVariable(siminput,'SDrift',sensorDriftValues(ct));
    siminput = setVariable(siminput,'ShaftWear',shaftWearValues(ct));
    
    % Collect the simulation input in an array
    gridSimulationInput(ct) = siminput;
end

Similarly create combinations of random values for each model variable. Make sure to include the 0 value so that there are combinations where only subsets of the three fault types are represented.

rng('default'); % Reset random seed for reproducibility
toothFaultArray = [0 -rand(1,6)];    % Tooth fault gain values
sensorDriftArray = [0 randn(1,6)/8]; % Sensor drift offset values
shaftWearArray = [0 -1];               % Variants available for drive shaft conditions

%Create an n-dimensional array with combinations of all values
[toothFaultValues,sensorDriftValues,shaftWearValues] = ...
    ndgrid(toothFaultArray,sensorDriftArray,shaftWearArray);

for ct=numel(toothFaultValues):-1:1
    % Create a Simulink.SimulationInput for each combination of values
    siminput = Simulink.SimulationInput(mdl);
    
    % Modify model parameters
    siminput = setVariable(siminput,'ToothFaultGain',toothFaultValues(ct));
    siminput = setVariable(siminput,'SDrift',sensorDriftValues(ct));
    siminput = setVariable(siminput,'ShaftWear',shaftWearValues(ct));
    
    % Collect the simulation input in an array
    randomSimulationInput(ct) = siminput;
end

With the array of Simulink.SimulationInput objects defined use the generateSimulationEnsemble function to run the simulations. The generateSimulationEnsemble function configures the model to save logged data to file, use the timetable format for signal logging and store the Simulink.SimulationInput objects in the saved file. The generateSimulationEnsemble function returns a status flag indicating whether the simulation completed successfully.

The code above created 110 simulation inputs from the gridded variable values and 98 simulation inputs from the random variable values giving 208 total simulations. Running these 208 simulations in parallel can take as much as two hours or more on a standard desktop and generates around 10GB of data. An option to only run the first 10 simulations is provided for convenience.

% Run the simulations and create an ensemble to manage the simulation results
if ~exist(fullfile(pwd,'Data'),'dir')
    mkdir(fullfile(pwd,'Data')) % Create directory to store results
end
runAll = true;
if runAll
   [ok,e] = generateSimulationEnsemble([gridSimulationInput, randomSimulationInput], ...
        fullfile(pwd,'Data'),'UseParallel', true);
else
    [ok,e] = generateSimulationEnsemble(gridSimulationInput(1:10), fullfile(pwd,'Data')); %#ok<*UNRCH>
end
[21-Jul-2022 15:36:27] Checking for availability of parallel pool...
Starting parallel pool (parpool) using the 'local' profile ...
Connected to the parallel pool (number of workers: 6).
[21-Jul-2022 15:37:34] Starting Simulink on parallel workers...
[21-Jul-2022 15:38:00] Configuring simulation cache folder on parallel workers...
[21-Jul-2022 15:38:01] Transferring base workspace variables used in the model to parallel workers...
[21-Jul-2022 15:38:01] Loading model on parallel workers...
[21-Jul-2022 15:38:20] Running simulations...
[21-Jul-2022 15:38:55] Completed 1 of 208 simulation runs
[21-Jul-2022 15:38:56] Completed 2 of 208 simulation runs
[21-Jul-2022 15:38:57] Completed 3 of 208 simulation runs
[21-Jul-2022 15:38:58] Completed 4 of 208 simulation runs
[21-Jul-2022 15:38:59] Completed 5 of 208 simulation runs
[21-Jul-2022 15:39:00] Completed 6 of 208 simulation runs
[21-Jul-2022 15:39:09] Completed 7 of 208 simulation runs
[21-Jul-2022 15:39:09] Completed 8 of 208 simulation runs
[21-Jul-2022 15:39:10] Completed 9 of 208 simulation runs
[21-Jul-2022 15:39:11] Completed 10 of 208 simulation runs
[21-Jul-2022 15:39:12] Completed 11 of 208 simulation runs
[21-Jul-2022 15:39:13] Completed 12 of 208 simulation runs
[21-Jul-2022 15:39:21] Completed 13 of 208 simulation runs
[21-Jul-2022 15:39:22] Completed 14 of 208 simulation runs
[21-Jul-2022 15:39:23] Completed 15 of 208 simulation runs
[21-Jul-2022 15:39:23] Completed 16 of 208 simulation runs
[21-Jul-2022 15:39:24] Completed 17 of 208 simulation runs
[21-Jul-2022 15:39:25] Completed 18 of 208 simulation runs
[21-Jul-2022 15:39:34] Completed 19 of 208 simulation runs
[21-Jul-2022 15:39:35] Completed 20 of 208 simulation runs
[21-Jul-2022 15:39:35] Completed 21 of 208 simulation runs
[21-Jul-2022 15:39:36] Completed 22 of 208 simulation runs
[21-Jul-2022 15:39:37] Completed 23 of 208 simulation runs
[21-Jul-2022 15:39:38] Completed 24 of 208 simulation runs
[21-Jul-2022 15:39:47] Completed 25 of 208 simulation runs
[21-Jul-2022 15:39:47] Completed 26 of 208 simulation runs
[21-Jul-2022 15:39:48] Completed 27 of 208 simulation runs
[21-Jul-2022 15:39:49] Completed 28 of 208 simulation runs
[21-Jul-2022 15:39:50] Completed 29 of 208 simulation runs
[21-Jul-2022 15:39:51] Completed 30 of 208 simulation runs
[21-Jul-2022 15:40:00] Completed 31 of 208 simulation runs
[21-Jul-2022 15:40:00] Completed 32 of 208 simulation runs
[21-Jul-2022 15:40:01] Completed 33 of 208 simulation runs
[21-Jul-2022 15:40:02] Completed 34 of 208 simulation runs
[21-Jul-2022 15:40:03] Completed 35 of 208 simulation runs
[21-Jul-2022 15:40:05] Completed 36 of 208 simulation runs
[21-Jul-2022 15:40:13] Completed 37 of 208 simulation runs
[21-Jul-2022 15:40:14] Completed 38 of 208 simulation runs
[21-Jul-2022 15:40:15] Completed 39 of 208 simulation runs
[21-Jul-2022 15:40:16] Completed 40 of 208 simulation runs
[21-Jul-2022 15:40:17] Completed 41 of 208 simulation runs
[21-Jul-2022 15:40:18] Completed 42 of 208 simulation runs
[21-Jul-2022 15:40:26] Completed 43 of 208 simulation runs
[21-Jul-2022 15:40:27] Completed 44 of 208 simulation runs
[21-Jul-2022 15:40:27] Completed 45 of 208 simulation runs
[21-Jul-2022 15:40:28] Completed 46 of 208 simulation runs
[21-Jul-2022 15:40:29] Completed 47 of 208 simulation runs
[21-Jul-2022 15:40:30] Completed 48 of 208 simulation runs
[21-Jul-2022 15:40:38] Completed 49 of 208 simulation runs
[21-Jul-2022 15:40:39] Completed 50 of 208 simulation runs
[21-Jul-2022 15:40:40] Completed 51 of 208 simulation runs
[21-Jul-2022 15:40:41] Completed 52 of 208 simulation runs
[21-Jul-2022 15:40:42] Completed 53 of 208 simulation runs
[21-Jul-2022 15:40:43] Completed 54 of 208 simulation runs
[21-Jul-2022 15:40:52] Completed 55 of 208 simulation runs
[21-Jul-2022 15:40:55] Completed 56 of 208 simulation runs
[21-Jul-2022 15:40:56] Completed 57 of 208 simulation runs
[21-Jul-2022 15:40:57] Completed 58 of 208 simulation runs
[21-Jul-2022 15:40:58] Completed 59 of 208 simulation runs
[21-Jul-2022 15:40:59] Completed 60 of 208 simulation runs
[21-Jul-2022 15:41:09] Completed 61 of 208 simulation runs
[21-Jul-2022 15:41:10] Completed 62 of 208 simulation runs
[21-Jul-2022 15:41:11] Completed 63 of 208 simulation runs
[21-Jul-2022 15:41:12] Completed 64 of 208 simulation runs
[21-Jul-2022 15:41:13] Completed 65 of 208 simulation runs
[21-Jul-2022 15:41:14] Completed 66 of 208 simulation runs
[21-Jul-2022 15:41:20] Completed 67 of 208 simulation runs
[21-Jul-2022 15:41:21] Completed 68 of 208 simulation runs
[21-Jul-2022 15:41:22] Completed 69 of 208 simulation runs
[21-Jul-2022 15:41:22] Completed 70 of 208 simulation runs
[21-Jul-2022 15:41:23] Completed 71 of 208 simulation runs
[21-Jul-2022 15:41:24] Completed 72 of 208 simulation runs
[21-Jul-2022 15:41:33] Completed 73 of 208 simulation runs
[21-Jul-2022 15:41:33] Completed 74 of 208 simulation runs
[21-Jul-2022 15:41:34] Completed 75 of 208 simulation runs
[21-Jul-2022 15:41:35] Completed 76 of 208 simulation runs
[21-Jul-2022 15:41:36] Completed 77 of 208 simulation runs
[21-Jul-2022 15:41:37] Completed 78 of 208 simulation runs
[21-Jul-2022 15:41:45] Completed 79 of 208 simulation runs
[21-Jul-2022 15:41:46] Completed 80 of 208 simulation runs
[21-Jul-2022 15:41:47] Completed 81 of 208 simulation runs
[21-Jul-2022 15:41:48] Completed 82 of 208 simulation runs
[21-Jul-2022 15:41:49] Completed 83 of 208 simulation runs
[21-Jul-2022 15:41:49] Completed 84 of 208 simulation runs
[21-Jul-2022 15:41:57] Completed 85 of 208 simulation runs
[21-Jul-2022 15:41:58] Completed 86 of 208 simulation runs
[21-Jul-2022 15:41:59] Completed 87 of 208 simulation runs
[21-Jul-2022 15:42:00] Completed 88 of 208 simulation runs
[21-Jul-2022 15:42:01] Completed 89 of 208 simulation runs
[21-Jul-2022 15:42:02] Completed 90 of 208 simulation runs
[21-Jul-2022 15:42:10] Completed 91 of 208 simulation runs
[21-Jul-2022 15:42:11] Completed 92 of 208 simulation runs
[21-Jul-2022 15:42:12] Completed 93 of 208 simulation runs
[21-Jul-2022 15:42:13] Completed 94 of 208 simulation runs
[21-Jul-2022 15:42:14] Completed 95 of 208 simulation runs
[21-Jul-2022 15:42:15] Completed 96 of 208 simulation runs
[21-Jul-2022 15:42:23] Completed 97 of 208 simulation runs
[21-Jul-2022 15:42:23] Completed 98 of 208 simulation runs
[21-Jul-2022 15:42:24] Completed 99 of 208 simulation runs
[21-Jul-2022 15:42:25] Completed 100 of 208 simulation runs
[21-Jul-2022 15:42:26] Completed 101 of 208 simulation runs
[21-Jul-2022 15:42:27] Completed 102 of 208 simulation runs
[21-Jul-2022 15:42:35] Completed 103 of 208 simulation runs
[21-Jul-2022 15:42:36] Completed 104 of 208 simulation runs
[21-Jul-2022 15:42:37] Completed 105 of 208 simulation runs
[21-Jul-2022 15:42:38] Completed 106 of 208 simulation runs
[21-Jul-2022 15:42:38] Completed 107 of 208 simulation runs
[21-Jul-2022 15:42:39] Completed 108 of 208 simulation runs
[21-Jul-2022 15:42:47] Completed 109 of 208 simulation runs
[21-Jul-2022 15:42:48] Completed 110 of 208 simulation runs
[21-Jul-2022 15:42:50] Completed 111 of 208 simulation runs
[21-Jul-2022 15:42:51] Completed 112 of 208 simulation runs
[21-Jul-2022 15:42:52] Completed 113 of 208 simulation runs
[21-Jul-2022 15:42:53] Completed 114 of 208 simulation runs
[21-Jul-2022 15:43:01] Completed 115 of 208 simulation runs
[21-Jul-2022 15:43:02] Completed 116 of 208 simulation runs
[21-Jul-2022 15:43:03] Completed 117 of 208 simulation runs
[21-Jul-2022 15:43:04] Completed 118 of 208 simulation runs
[21-Jul-2022 15:43:05] Completed 119 of 208 simulation runs
[21-Jul-2022 15:43:06] Completed 120 of 208 simulation runs
[21-Jul-2022 15:43:13] Completed 121 of 208 simulation runs
[21-Jul-2022 15:43:14] Completed 122 of 208 simulation runs
[21-Jul-2022 15:43:14] Completed 123 of 208 simulation runs
[21-Jul-2022 15:43:15] Completed 124 of 208 simulation runs
[21-Jul-2022 15:43:15] Completed 125 of 208 simulation runs
[21-Jul-2022 15:43:16] Completed 126 of 208 simulation runs
[21-Jul-2022 15:43:28] Completed 127 of 208 simulation runs
[21-Jul-2022 15:43:29] Completed 128 of 208 simulation runs
[21-Jul-2022 15:43:30] Completed 129 of 208 simulation runs
[21-Jul-2022 15:43:31] Completed 130 of 208 simulation runs
[21-Jul-2022 15:43:32] Completed 131 of 208 simulation runs
[21-Jul-2022 15:43:33] Completed 132 of 208 simulation runs
[21-Jul-2022 15:43:38] Completed 133 of 208 simulation runs
[21-Jul-2022 15:43:40] Completed 134 of 208 simulation runs
[21-Jul-2022 15:43:41] Completed 135 of 208 simulation runs
[21-Jul-2022 15:43:42] Completed 136 of 208 simulation runs
[21-Jul-2022 15:43:43] Completed 137 of 208 simulation runs
[21-Jul-2022 15:43:44] Completed 138 of 208 simulation runs
[21-Jul-2022 15:43:51] Completed 139 of 208 simulation runs
[21-Jul-2022 15:43:52] Completed 140 of 208 simulation runs
[21-Jul-2022 15:43:53] Completed 141 of 208 simulation runs
[21-Jul-2022 15:43:54] Completed 142 of 208 simulation runs
[21-Jul-2022 15:43:55] Completed 143 of 208 simulation runs
[21-Jul-2022 15:43:56] Completed 144 of 208 simulation runs
[21-Jul-2022 15:44:03] Completed 145 of 208 simulation runs
[21-Jul-2022 15:44:05] Completed 146 of 208 simulation runs
[21-Jul-2022 15:44:06] Completed 147 of 208 simulation runs
[21-Jul-2022 15:44:07] Completed 148 of 208 simulation runs
[21-Jul-2022 15:44:08] Completed 149 of 208 simulation runs
[21-Jul-2022 15:44:09] Completed 150 of 208 simulation runs
[21-Jul-2022 15:44:16] Completed 151 of 208 simulation runs
[21-Jul-2022 15:44:17] Completed 152 of 208 simulation runs
[21-Jul-2022 15:44:18] Completed 153 of 208 simulation runs
[21-Jul-2022 15:44:18] Completed 154 of 208 simulation runs
[21-Jul-2022 15:44:19] Completed 155 of 208 simulation runs
[21-Jul-2022 15:44:20] Completed 156 of 208 simulation runs
[21-Jul-2022 15:44:28] Completed 157 of 208 simulation runs
[21-Jul-2022 15:44:29] Completed 158 of 208 simulation runs
[21-Jul-2022 15:44:29] Completed 159 of 208 simulation runs
[21-Jul-2022 15:44:31] Completed 160 of 208 simulation runs
[21-Jul-2022 15:44:32] Completed 161 of 208 simulation runs
[21-Jul-2022 15:44:33] Completed 162 of 208 simulation runs
[21-Jul-2022 15:44:42] Completed 163 of 208 simulation runs
[21-Jul-2022 15:44:42] Completed 164 of 208 simulation runs
[21-Jul-2022 15:44:43] Completed 165 of 208 simulation runs
[21-Jul-2022 15:44:44] Completed 166 of 208 simulation runs
[21-Jul-2022 15:44:45] Completed 167 of 208 simulation runs
[21-Jul-2022 15:44:46] Completed 168 of 208 simulation runs
[21-Jul-2022 15:44:54] Completed 169 of 208 simulation runs
[21-Jul-2022 15:44:55] Completed 170 of 208 simulation runs
[21-Jul-2022 15:44:56] Completed 171 of 208 simulation runs
[21-Jul-2022 15:44:57] Completed 172 of 208 simulation runs
[21-Jul-2022 15:44:57] Completed 173 of 208 simulation runs
[21-Jul-2022 15:44:58] Completed 174 of 208 simulation runs
[21-Jul-2022 15:45:07] Completed 175 of 208 simulation runs
[21-Jul-2022 15:45:08] Completed 176 of 208 simulation runs
[21-Jul-2022 15:45:08] Completed 177 of 208 simulation runs
[21-Jul-2022 15:45:09] Completed 178 of 208 simulation runs
[21-Jul-2022 15:45:10] Completed 179 of 208 simulation runs
[21-Jul-2022 15:45:11] Completed 180 of 208 simulation runs
[21-Jul-2022 15:45:20] Completed 181 of 208 simulation runs
[21-Jul-2022 15:45:21] Completed 182 of 208 simulation runs
[21-Jul-2022 15:45:22] Completed 183 of 208 simulation runs
[21-Jul-2022 15:45:23] Completed 184 of 208 simulation runs
[21-Jul-2022 15:45:24] Completed 185 of 208 simulation runs
[21-Jul-2022 15:45:25] Completed 186 of 208 simulation runs
[21-Jul-2022 15:45:32] Completed 187 of 208 simulation runs
[21-Jul-2022 15:45:33] Completed 188 of 208 simulation runs
[21-Jul-2022 15:45:34] Completed 189 of 208 simulation runs
[21-Jul-2022 15:45:35] Completed 190 of 208 simulation runs
[21-Jul-2022 15:45:36] Completed 191 of 208 simulation runs
[21-Jul-2022 15:45:37] Completed 192 of 208 simulation runs
[21-Jul-2022 15:45:45] Completed 193 of 208 simulation runs
[21-Jul-2022 15:45:45] Completed 194 of 208 simulation runs
[21-Jul-2022 15:45:46] Completed 195 of 208 simulation runs
[21-Jul-2022 15:45:47] Completed 196 of 208 simulation runs
[21-Jul-2022 15:45:48] Completed 197 of 208 simulation runs
[21-Jul-2022 15:45:49] Completed 198 of 208 simulation runs
[21-Jul-2022 15:45:57] Completed 199 of 208 simulation runs
[21-Jul-2022 15:45:58] Completed 200 of 208 simulation runs
[21-Jul-2022 15:45:59] Completed 201 of 208 simulation runs
[21-Jul-2022 15:46:00] Completed 202 of 208 simulation runs
[21-Jul-2022 15:46:01] Completed 203 of 208 simulation runs
[21-Jul-2022 15:46:02] Completed 204 of 208 simulation runs
[21-Jul-2022 15:46:09] Completed 205 of 208 simulation runs
[21-Jul-2022 15:46:10] Completed 206 of 208 simulation runs
[21-Jul-2022 15:46:11] Completed 207 of 208 simulation runs
[21-Jul-2022 15:46:12] Completed 208 of 208 simulation runs
[21-Jul-2022 15:46:12] Cleaning up parallel workers...

The generateSimulationEnsemble ran and logged the simulation results. Create a simulation ensemble to process and analyze the simulation results using the simulationEnsembleDatastore command.

ens = simulationEnsembleDatastore(fullfile(pwd,'Data'));

Processing the Simulation Results

The simulationEnsembleDatastore command created an ensemble object that points to the simulation results. Use the ensemble object to prepare and analyze the data in each member of the ensemble. The ensemble object lists the data variables in the ensemble and by default all the variables are selected for reading.

ens
ens = 
  simulationEnsembleDatastore with properties:

           DataVariables: [6×1 string]
    IndependentVariables: [0×0 string]
      ConditionVariables: [0×0 string]
       SelectedVariables: [6×1 string]
                ReadSize: 1
              NumMembers: 208
          LastMemberRead: [0×0 string]
                   Files: [208×1 string]

ens.SelectedVariables
ans = 6×1 string
    "SimulationInput"
    "SimulationMetadata"
    "Tacho"
    "Vibration"
    "xFinal"
    "xout"

For analysis only read the Vibration and Tacho signals and the Simulink.SimulationInput. The Simulink.SimulationInput has the model variable values used for simulation and is used to create fault labels for the ensemble members. Use the ensemble read command to get the ensemble member data.

ens.SelectedVariables = ["Vibration" "Tacho" "SimulationInput"];
data = read(ens)
data=1×3 table
         Vibration                Tacho                  SimulationInput        
    ___________________    ___________________    ______________________________

    {40272×1 timetable}    {40272×1 timetable}    {1×1 Simulink.SimulationInput}

Extract the vibration signal from the returned data and plot it.

vibration = data.Vibration{1};
plot(vibration.Time,vibration.Data)
title('Vibration')
ylabel('Acceleration')

Figure contains an axes object. The axes object with title Vibration contains an object of type line.

The first 10 seconds of the simulation contains data where the transmission system is starting up; for analysis discard this data.

idx = vibration.Time >= seconds(10);
vibration = vibration(idx,:);
vibration.Time = vibration.Time - vibration.Time(1);

The Tacho signal contains pulses for the rotations of the drive and load shafts but analysis, and specifically time synchronous averaging, requires the times of shaft rotations. The following code discards the first 10 seconds of the Tacho data and finds the shaft rotation times in tachoPulses.

tacho = data.Tacho{1};
idx = tacho.Time >= seconds(10);
tacho = tacho(idx,:);
plot(tacho.Time,tacho.Data)
title('Tacho pulses')
legend('Drive shaft','Load shaft') % Load shaft rotates more slowly than drive shaft

Figure contains an axes object. The axes object with title Tacho pulses contains 2 objects of type line. These objects represent Drive shaft, Load shaft.

idx = diff(tacho.Data(:,2)) > 0.5;
tachoPulses = tacho.Time(find(idx)+1)-tacho.Time(1)
tachoPulses = 8×1 duration
   2.8543 sec
   6.6508 sec
   10.447 sec
   14.244 sec
    18.04 sec
   21.837 sec
   25.634 sec
    29.43 sec

The Simulink.SimulationInput.Variables property contains the values of the fault parameters used for the simulation, these values allow us to create fault labels for each ensemble member.

vars = data.SimulationInput{1}.Variables;
idx = strcmp({vars.Name},'SDrift');
if any(idx)
    sF = abs(vars(idx).Value) > 0.01; % Small drift values are not faults
else
    sF = false;
end
idx = strcmp({vars.Name},'ShaftWear');
if any(idx)
    sV = vars(idx).Value < 0;
else
    sV = false;
end
if any(idx)
    idx = strcmp({vars.Name},'ToothFaultGain');
    sT = abs(vars(idx).Value) < 0.1; % Small tooth fault values are not faults
else
    sT = false
end
faultCode = sF + 2*sV + 4*sT; % A fault code to capture different fault conditions

The processed vibration and tacho signals and the fault labels are added to the ensemble to be used later.

sdata = table({vibration},{tachoPulses},sF,sV,sT,faultCode, ...
    'VariableNames',{'Vibration','TachoPulses','SensorDrift','ShaftWear','ToothFault','FaultCode'})  
sdata=1×6 table
         Vibration          TachoPulses      SensorDrift    ShaftWear    ToothFault    FaultCode
    ___________________    ______________    ___________    _________    __________    _________

    {30106×1 timetable}    {8×1 duration}       true          false        false           1    

ens.DataVariables = [ens.DataVariables; "TachoPulses"];

The ensemble ConditionVariables property can be used to identify the variables in the ensemble that contain condition or fault label data. Set the property to contain the newly created fault labels.

ens.ConditionVariables = ["SensorDrift","ShaftWear","ToothFault","FaultCode"];

The code above was used to process one member of the ensemble. To process all the ensemble members the code above was converted to the function prepareData and using the ensemble hasdata command a loop is used to apply prepareData to all the ensemble members. The ensemble members can be processed in parallel by partitioning the ensemble and processing the ensemble partitions in parallel.

reset(ens)
runLocal = false;
if runLocal
    % Process each member in the ensemble
    while hasdata(ens)
        data = read(ens);
        addData = prepareData(data);
        writeToLastMemberRead(ens,addData)
    end
else
    % Split the ensemble into partitions and process each partition in parallel
    n = numpartitions(ens,gcp);
    parfor ct = 1:n
        subens = partition(ens,n,ct);
        while hasdata(subens)
            data = read(subens);
            addData = prepareData(data);
            writeToLastMemberRead(subens,addData)
        end
    end    
end

Plot the vibration signal of every 10th member of the ensemble using the hasdata and read commands to extract the vibration signal.

reset(ens)
ens.SelectedVariables = "Vibration";
figure, 
ct = 1;
while hasdata(ens)
    data = read(ens);
    if mod(ct,10) == 0
        vibration = data.Vibration{1};
        plot(vibration.Time,vibration.Data)
        hold on
    end
    ct = ct + 1;
end
hold off
title('Vibration signals')
ylabel('Acceleration')

Figure contains an axes object. The axes object with title Vibration signals contains 20 objects of type line.

Analyzing the Simulation Data

Now that the data has been cleaned and pre-processed the data is ready for extracting features to determine the features to use to classify the different faults types. First configure the ensemble so that it only returns the processed data.

ens.SelectedVariables = ["Vibration","TachoPulses"];

For each member in the ensemble compute a number of time and spectrum based features. These include signal statistics such as signal mean, variance, peak to peak, non-linear signal characteristics such as approximate entropy and Lyapunov exponent, and spectral features such as the peak frequency of the time synchronous average of the vibration signal, and the power of the time synchronous average envelope signal. The analyzeData function contains a full list of the extracted features. By way of example consider computing the spectrum of the time synchronous averaged vibration signal.

reset(ens)
data = read(ens)
data=1×2 table
         Vibration          TachoPulses  
    ___________________    ______________

    {30106×1 timetable}    {8×1 duration}

vibration = data.Vibration{1};

% Interpolate the Vibration signal onto periodic time base suitable for fft analysis
np = 2^floor(log(height(vibration))/log(2));
dt = vibration.Time(end)/(np-1);
tv = 0:dt:vibration.Time(end);
y = retime(vibration,tv,'linear');

% Compute the time synchronous average of the vibration signal
tp = seconds(data.TachoPulses{1});
vibrationTSA = tsa(y,tp);
figure
plot(vibrationTSA.Time,vibrationTSA.tsa)
title('Vibration time synchronous average')
ylabel('Acceleration')
% Compute the spectrum of the time synchronous average
np = numel(vibrationTSA);
f = fft(vibrationTSA.tsa.*hamming(np))/np;
frTSA = f(1:floor(np/2)+1);            % TSA frequency response
wTSA = (0:np/2)/np*(2*pi/seconds(dt)); % TSA spectrum frequencies
mTSA = abs(frTSA);                     % TSA spectrum magnitudes
figure
semilogx(wTSA,20*log10(mTSA))
title('Vibration spectrum')
xlabel('rad/s')

The frequency that corresponds to the peak magnitude could turn out to be a feature that is useful for classifying the different fault types. The code below computes the features mentioned above for all the ensemble members (running this analysis can take up to an hour on a standard desktop. Optional code to run the analysis in parallel using the ensemble partition command is provided.) The names of the features are added to the ensemble data variables property before calling writeToLastMemberRead to add the computed features to each ensemble member.

reset(ens)
ens.DataVariables = [ens.DataVariables; ...
    "SigMean";"SigMedian";"SigRMS";"SigVar";"SigPeak";"SigPeak2Peak";"SigSkewness"; ...
    "SigKurtosis";"SigCrestFactor";"SigMAD";"SigRangeCumSum";"SigCorrDimension";"SigApproxEntropy"; ...
    "SigLyapExponent";"PeakFreq";"HighFreqPower";"EnvPower";"PeakSpecKurtosis"];
if runLocal
    while hasdata(ens)
        data = read(ens);
        addData = analyzeData(data);
        writeToLastMemberRead(ens,addData);
    end
else
    % Split the ensemble into partitions and analyze each partition in parallel
    n = numpartitions(ens,gcp);
    parfor ct = 1:n
        subens = partition(ens,n,ct);
        while hasdata(subens)
            data = read(subens);
            addData = analyzeData(data);
            writeToLastMemberRead(subens,addData)
        end
    end
end

Selecting Features for Fault Classification

The features computed above are used to build a classifier to classify the different fault conditions. First configure the ensemble to read only the derived features and the fault labels.

featureVariables = analyzeData('GetFeatureNames');
ens.DataVariables = [ens.DataVariables; featureVariables];
ens.SelectedVariables = [featureVariables; ens.ConditionVariables];
reset(ens)

Gather the feature data for all the ensemble members into one table.

featureData = gather(tall(ens))

Consider the sensor drift fault. Use the fscnca command with all the features computed above as predictors and the sensor drift fault label (a true false value) as the response. The fscnca command returns weights for each feature and features with higher weights have higher importance in predicting the response. For the sensor drift fault the weights indicate that two features are significant predictors (the signal cumulative sum range and the peak frequency of the spectral kurtosis) and the remaining features have little impact.

idxResponse = strcmp(featureData.Properties.VariableNames,'SensorDrift');
idxLastFeature = find(idxResponse)-1; % Index of last feature to use as a potential predictor
featureAnalysis = fscnca(featureData{:,1:idxLastFeature},featureData.SensorDrift); 
featureAnalysis.FeatureWeights
idxSelectedFeature = featureAnalysis.FeatureWeights > 0.1;
classifySD = [featureData(:,idxSelectedFeature), featureData(:,idxResponse)]

A grouped histogram of the cumulative sum range gives us insight into why this feature is a significant predictor for the sensor drift fault.

figure
histogram(classifySD.SigRangeCumSum(classifySD.SensorDrift),'BinWidth',5e3)
xlabel('Signal cumulative sum range')
ylabel('Count')
hold on
histogram(classifySD.SigRangeCumSum(~classifySD.SensorDrift),'BinWidth',5e3)
hold off
legend('Sensor drift fault','No sensor drift fault')

The histogram plot shows that the signal cumulative sum range is a good featured for detecting sensor drift faults though an additional feature is probably needed as there may be false positives when the signal cumulative sum range is below 0.5 if just the signal cumulative sum range is used to classify sensor drift.

Consider the shaft wear fault. In this case the fscnca function indicates that there are 3 features that are significant predictors for the fault (the signal Lyapunov exponent, peak frequency, and the peak frequency in the spectral kurtosis), choose these to classify the shaft wear fault.

idxResponse = strcmp(featureData.Properties.VariableNames,'ShaftWear');
featureAnalysis = fscnca(featureData{:,1:idxLastFeature},featureData.ShaftWear);
featureAnalysis.FeatureWeights
idxSelectedFeature = featureAnalysis.FeatureWeights > 0.1;
classifySW = [featureData(:,idxSelectedFeature), featureData(:,idxResponse)]

The grouped histogram for the signal Lyapunov exponent shows why that feature alone is not a good predictor.

figure
histogram(classifySW.SigLyapExponent(classifySW.ShaftWear))
xlabel('Signal lyapunov exponent')
ylabel('Count')
hold on
histogram(classifySW.SigLyapExponent(~classifySW.ShaftWear))
hold off
legend('Shaft wear fault','No shaft wear fault')

The shaft wear feature selection indicates multiple features are needed to classify the shaft wear fault, the grouped histogram confirms this as the most significant feature (in this case the Lyapunov exponent) has a similar distribution for both faulty and fault free scenarios indicating that more features are needed to correctly classify this fault.

Finally consider the tooth fault, the fscnca function indicates that there are 3 features primary that are significant predictors for the fault (the signal cumulative sum range, the signal Lyapunov exponent and the peak frequency in the spectral kurtosis). Choosing those 3 features to classify the tooth fault results in a classifier that has poor performance. Instead, use the 6 most important features.

idxResponse = strcmp(featureData.Properties.VariableNames,'ToothFault');
featureAnalysis = fscnca(featureData{:,1:idxLastFeature},featureData.ToothFault); 
[~,idxSelectedFeature] = sort(featureAnalysis.FeatureWeights);
classifyTF = [featureData(:,idxSelectedFeature(end-5:end)), featureData(:,idxResponse)]
figure
histogram(classifyTF.SigRangeCumSum(classifyTF.ToothFault))
xlabel('Signal cumulative sum range')
ylabel('Count')
hold on
histogram(classifyTF.SigRangeCumSum(~classifyTF.ToothFault))
hold off
legend('Gear tooth fault','No gear tooth fault')

Using the above results a polynomial svm to classify gear tooth faults. Split the feature table into members that are used for training and members for testing and validation. Use the training members to create a svm classifier using the fitcsvm command.

rng('default') % For reproducibility
cvp = cvpartition(size(classifyTF,1),'KFold',5); % Randomly partition the data for training and validation 
classifierTF = fitcsvm(...
    classifyTF(cvp.training(1),1:end-1), ...
    classifyTF(cvp.training(1),end), ...
    'KernelFunction','polynomial', ...
    'PolynomialOrder',2, ...
    'KernelScale','auto', ...
    'BoxConstraint',1, ...
    'Standardize',true, ...
    'ClassNames',[false; true]);

Use the classifier to classify the test points using the predict command and check the performance of the predictions using a confusion matrix.

% Use the classifier on the test validation data to evaluate performance
actualValue = classifyTF{cvp.test(1),end};
predictedValue = predict(classifierTF, classifyTF(cvp.test(1),1:end-1));

% Check performance by computing and plotting the confusion matrix
confdata = confusionmat(actualValue,predictedValue);
h = heatmap(confdata, ...
    'YLabel', 'Actual gear tooth fault', ...
    'YDisplayLabels', {'False','True'}, ...
    'XLabel', 'Predicted gear tooth fault', ...
    'XDisplayLabels', {'False','True'}, ...
    'ColorbarVisible','off');   

The confusion matrix indicates that the classifier correctly classifies all non-fault conditions but incorrectly classifies one expected fault condition as not being a fault. Increasing the number of features used in the classifier can help improve the performance further.

Summary

This example walked through the workflow for generating fault data from Simulink, using a simulation ensemble to clean up the simulation data and extract features. The extracted features were then used to build classifiers for the different fault types.

See Also

Related Topics