Extract Features and Partition Data for Transmission System Fault Detection
R2026bThis example shows how to extract features and partition data for training and testing fault detection models in an automotive transmission system. The extracted features are derived from the original signal data and reformatted into tabular data. After partitioning the tabular data into training and test sets, you must verify that the data sets conform to the requirements outlined in Define Requirements for Transmission System Fault Detection.
Note: This example requires Predictive Maintenance Toolbox™.
Extract Tabular Features
In order to create machine learning models for fault detection, the signal data must first be converted into tabular features. These features can then be provided as training data to the models.
You can begin by loading the signal data for the eight fault and failure scenarios described in Fault and Failure Scenarios. Then, use the helper function helperExtractFeatures to compute various statistics over the signals—such as mean, variance, median, root mean square (RMS), and more—along with predictive maintenance metrics. Finally, combine the extracted features for all the signals into the faultData table. To reduce the time required to extract features, the helperExtractFeatures function performs some computations in parallel when you have Parallel Computing Toolbox™.
To extract tabular features from the raw signal data, set runExtractFeatures to true in the code below. Because the feature extraction process can take some time, runExtractFeatures is set to false by default. In this case, the example uses previously saved data instead.
runExtractFeatures = false; if runExtractFeatures supportFileNames = ["faultScenario1","faultScenario2","faultScenario3","faultScenario4", ... "faultScenario5","faultScenario6","faultScenario7","faultScenario8"]; scenarioVarNames = ["dataTable_s1","dataTable_s2","dataTable_s3","dataTable_s4", ... "dataTable_s5","dataTable_s6","dataTable_s7","dataTable_s8"]; faultData = table; for k = 1:numel(supportFileNames) if ~exist(scenarioVarNames(k),"var") d = load(matlab.internal.examples.downloadSupportFile("nnet", ... "data/transmissionfaults/" + supportFileNames(k) + ".mat")); scenarioData = d.(scenarioVarNames(k)); else scenarioData = scenarioVarNames(k); end numObservations = size(scenarioData,1); chunk = helperExtractFeatures(scenarioData,numObservations); chunk.ScenarioID = repmat(k,height(chunk),1); faultData = [faultData; chunk]; end else if ~exist("faultData","var") load("TransmissionFaultData.mat","faultData"); end end
Display the first eight observations in the faultData table.
head(faultData)
SigMean SigMedian SigRMS SigVar SigPeak SigPeak2Peak SigSkewness SigKurtosis SigCrestFactor SigMAD SigRangeCumSum SigApproxEntropy SigLyapExponent PeakFreq PeakSpecKurtosis RPMMedian RPMStd RPMIQR ShaftHz FaultCode ScenarioID
___________ ___________ _______ _______ _______ ____________ ___________ ___________ ______________ _______ ______________ ________________ _______________ ________ ________________ _________ ________ _________ _______ _________ __________
-0.008488 -0.0014868 1.0048 1.0097 1.9065 3.813 0.0094102 2.264 1.8973 0.81736 1191.9 0.098646 119.65 10.417 113.06 19.881 0.006589 0.0082359 0.33135 0 1
0.016147 0.004569 0.99833 0.99643 1.9065 3.813 -0.01328 2.2809 1.9097 0.8112 911.6 0.16966 161.24 39.993 6.9331 31.847 24.029 47.417 0.53079 0 1
-0.0062246 -0.00087335 1.0012 1.0025 1.9065 3.813 0.0060058 2.2689 1.9042 0.81418 250.69 0.34513 164.76 10.893 6.3998 50.719 33.319 65.764 0.84531 0 1
0.002205 0.00017328 1.0025 1.005 1.9065 3.813 -0.0017144 2.2717 1.9018 0.81488 147.27 0.43647 175.18 67.948 9.5997 39.428 47.749 68.388 0.65713 0 1
-0.00074636 -6.2967e-05 1.0007 1.0015 1.9065 3.813 0.0016406 2.2724 1.9051 0.81341 106.54 0.42986 177.77 359.93 20.799 58.797 60.276 51.524 0.97994 0 1
-0.00043971 3.4018e-06 1.0019 1.0039 1.9065 3.813 -0.00059818 2.271 1.9028 0.81449 87.845 0.41084 179.47 298.38 25.066 20.694 107.46 127.97 0.34489 0 1
0.0011 7.4387e-05 1.0004 1.0008 1.9065 3.813 0.00079942 2.2733 1.9058 0.81319 71.399 0.40673 182.89 201.84 29.333 32.662 87.873 31.055 0.54437 0 1
0.00074992 8.9083e-07 1.0012 1.0024 1.9065 3.813 -0.00086386 2.2715 1.9042 0.81384 63.251 0.41399 187.21 1310.9 33.599 28.169 115.39 16.858 0.46948 0 1
Create Separate Target Variable for Each Fault
As described in Fault and Failure Scenarios, the transmission system has two possible target faults (sensor drift and shaft wear) and one possible failure (gear tooth failure). The FaultCode variable in faultData encodes the two fault types in the following way:
0 indicates a healthy state (with no faults).
1 indicates a sensor drift fault.
2 indicates a shaft wear fault.
3 indicates a sensor drift fault and a shaft wear fault.
Because the gear tooth failure is an environmental condition rather than a detection target, it is not encoded in FaultCode.
Use the FaultCode variable to create two binary variables, one for detecting a sensor drift fault (SensorDrift) and one for detecting a shaft wear fault (ShaftWear). Then, remove the FaultCode variable from the faultData table.
n = size(faultData,1); SensorDrift = zeros(n,1); ShaftWear = zeros(n,1); FaultCode = faultData.FaultCode; for i = 1:n switch FaultCode(i) case 1 SensorDrift(i) = 1; case 2 ShaftWear(i) = 1; case 3 SensorDrift(i) = 1; ShaftWear(i) = 1; end end faultData.FaultCode = [];
You can train a classifier to detect a fault type using the corresponding binary response variable. Separating fault detection into two binary classification problems has the following advantages:
Each classifier solves a simpler binary classification problem rather than a more complex multiclass problem, with enough training observations for both its positive and negative classes.
Each classifier is trained on only the features most relevant to its fault mode, which reduces dimensionality and improves interpretability.
Partition Data
Split the extracted features into training and test sets. Reserve 30% of the observations for testing, and use the remaining observations for training. Note that the training and test sets are stratified over the scenarios (faultData.ScenarioID). This stratified partitioning ensures that each set contains the same proportion of scenarios.
rng(1,"twister");
cv = cvpartition(faultData.ScenarioID,Holdout=0.3);
faultDataTrain = faultData(cv.training,:);
faultDataTest = faultData(cv.test,:);Split the labels for sensor drift fault detection.
SensorDriftTrain = SensorDrift(cv.training); SensorDriftTest = SensorDrift(cv.test);
Split the labels for shaft wear fault detection.
ShaftWearTrain = ShaftWear(cv.training); ShaftWearTest = ShaftWear(cv.test);
After verifying that the data sets pass all data requirements, you can use the faultDataTrain features and the SensorDriftTrain labels to train a binary classifier for detecting sensor drift. Similarly, you can use the faultDataTrain features and the ShaftWearTrain labels to train a binary classifier for detecting shaft wear. For more information on this next step, see Perform Feature Selection and Model Training for Transmission System Fault Detection.
Verify Extracted Tabular Data Requirements
Verify that the training set faultDataTrain and the test set faultDataTest meet the extracted tabular data requirements described in Extracted Tabular Data Requirements. In particular, both data sets must contain sufficient observations for each simulation scenario (that is, at least 140 observations per scenario in the training set and at least 60 observations per scenario in the test set) and must contain exactly 20 extracted features. Use the helper function helperVerifyRequirements to summarize the results in a table.
verificationTable = helperVerifyRequirements(faultDataTrain,faultDataTest)
verificationTable = 18×5 table
RequirementID Description Value Threshold Result
__________________________________ ___________________________________________________________ _____ _________ ______
"TRAINING_COMPLETENESS_SCENARIO_1" "Training observations from Scenario 1: Healthy" 280 140 "PASS"
"TRAINING_COMPLETENESS_SCENARIO_2" "Training observations from Scenario 2: Sensor Drift" 140 140 "PASS"
"TRAINING_COMPLETENESS_SCENARIO_3" "Training observations from Scenario 3: Shaft Wear" 140 140 "PASS"
"TRAINING_COMPLETENESS_SCENARIO_4" "Training observations from Scenario 4: SD + SW" 140 140 "PASS"
"TRAINING_COMPLETENESS_SCENARIO_5" "Training observations from Scenario 5: Gear Tooth Failure" 147 140 "PASS"
"TRAINING_COMPLETENESS_SCENARIO_6" "Training observations from Scenario 6: SD + GTF" 147 140 "PASS"
"TRAINING_COMPLETENESS_SCENARIO_7" "Training observations from Scenario 7: SW + GTF" 146 140 "PASS"
"TRAINING_COMPLETENESS_SCENARIO_8" "Training observations from Scenario 8: SD + SW + GTF" 147 140 "PASS"
"TEST_COMPLETENESS_SCENARIO_1" "Test observations from Scenario 1: Healthy" 120 60 "PASS"
"TEST_COMPLETENESS_SCENARIO_2" "Test observations from Scenario 2: Sensor Drift" 60 60 "PASS"
"TEST_COMPLETENESS_SCENARIO_3" "Test observations from Scenario 3: Shaft Wear" 60 60 "PASS"
"TEST_COMPLETENESS_SCENARIO_4" "Test observations from Scenario 4: SD + SW" 60 60 "PASS"
"TEST_COMPLETENESS_SCENARIO_5" "Test observations from Scenario 5: Gear Tooth Failure" 63 60 "PASS"
"TEST_COMPLETENESS_SCENARIO_6" "Test observations from Scenario 6: SD + GTF" 63 60 "PASS"
"TEST_COMPLETENESS_SCENARIO_7" "Test observations from Scenario 7: SW + GTF" 63 60 "PASS"
"TEST_COMPLETENESS_SCENARIO_8" "Test observations from Scenario 8: SD + SW + GTF" 62 60 "PASS"
Get insights using Copilot
⋮
The verification table shows that the training and test sets pass all the completeness and feature count data requirements.
Helper Functions
helperExtractFeatures
The helperExtractFeatures function derives 20 features from the vibration signal data (data), which contains the specified number of observations (n). The function returns the features as columns in a table (extractedTbl).
The function performs some computations in parallel when you have Parallel Computing Toolbox™.
function extractedTbl = helperExtractFeatures(data,n) % Remove invalid rows tf = cellfun(@(x) isempty(x),data.TachoPulses); data(tf,:) = []; n = n - sum(tf); pulsesPerRev = 1; % Set tacho pulses per revolution (PPR) if not 1 % Create list of features to compute varnames = ["SigMean","SigMedian","SigRMS","SigVar","SigPeak","SigPeak2Peak", ... "SigSkewness","SigKurtosis","SigCrestFactor","SigMAD","SigRangeCumSum", ... "SigApproxEntropy","SigLyapExponent","PeakFreq", "PeakSpecKurtosis", ... "RPMMedian","RPMStd","RPMIQR","ShaftHz", "FaultCode"]; % Create empty table extractedTbl = table(Size=[0 numel(varnames)], ... VariableTypes=[repmat("double",1,numel(varnames)-1) "double"], ... VariableNames=varnames); if ischar(data) % Return the names of the features being computed extractedTbl = varnames(:); return end parfor i = 1:n % Run computations in parallel % Extract the vibration signal for feature calculation Vibration = data.Vibration{i}; % Interpolate the vibration signal in the periodic time base suitable % for fast Fourier transform (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"); % Signal mean SigMean = mean(Vibration.Data); % Signal median SigMedian = median(Vibration.Data); % Signal root mean square (RMS) SigRMS = rms(Vibration.Data); % Signal variance SigVar = var(Vibration.Data); % Signal peak SigPeak = max(Vibration.Data); % Signal peak-to-peak SigPeak2Peak = peak2peak(Vibration.Data); % Signal skewness SigSkewness = skewness(Vibration.Data); % Signal kurtosis SigKurtosis = kurtosis(Vibration.Data); % Signal crest factor SigCrestFactor = peak2rms(Vibration.Data); % Signal median absolute deviation (MAD) SigMAD = mad(Vibration.Data); % Signal range of cumulative sum d = cumsum(Vibration.Data); SigRangeCumSum = max(d)-min(d); % Signal approximate entropy SigApproxEntropy = approximateEntropy(y.Data); % Signal Lyapunov exponent SigLyapExponent = lyapunovExponent(y.Data,1/seconds(dt)); % RPM metrics and shaft frequency from tacho pulse times tp = seconds(data.TachoPulses{i}); % Pulse times within window (in seconds) [RPMMedian,RPMStd,RPMIQR,ShaftHz] = helperRPMFromPulseTimes(tp,pulsesPerRev); % Peak frequency % Compute the FFT of the time-synchronous average (TSA) of the vibration % signal dt = seconds(dt); % Sample period in seconds vibrationTSA = tsa(y,tp); np = numel(vibrationTSA.tsa); f = fft(vibrationTSA.tsa .* hamming(np))/np; % Compute the peak frequency frTSA = f(1:floor(np/2)+1); wTSA = (0:np/2)/np*(2*pi/dt); mTSA = abs(frTSA); fHz = (0:np/2)/np*(1/dt); % Hz frequency vector for band segmentation [~,idx] = max(mTSA); PeakFreq = wTSA(idx); % Frequency with maximum spectral kurtosis [~,~,~,fc] = kurtogram(y.Data,1/dt,8); PeakSpecKurtosis = fc; % Save and return computed values sData = table(SigMean,SigMedian,SigRMS,SigVar,SigPeak, ... SigPeak2Peak,SigSkewness,SigKurtosis,SigCrestFactor, ... SigMAD,SigRangeCumSum,SigApproxEntropy,SigLyapExponent, ... PeakFreq,PeakSpecKurtosis,RPMMedian,RPMStd, ... RPMIQR,ShaftHz,data.FaultCode(i), ... VariableNames=varnames); extractedTbl = [extractedTbl; sData]; end end
helperRPMFromPulseTimes
The helperRPMFromPulseTimes function computes revolutions per minute (RPM) metrics based on the tacho pulse times within a specific window of time. In particular, the function returns the RPM median (rpmMed), standard deviation (rpmStd), and interquartile range (rpmIQR), as well as the shaft frequency (shaftHz).
function [rpmMed,rpmStd,rpmIQR,shaftHz] = helperRPMFromPulseTimes(tpSec,pulsesPerRev) tpSec = tpSec(:); if numel(tpSec) < 2 rpmMed = NaN; rpmStd = NaN; rpmIQR = NaN; shaftHz = NaN; return; end dtPulse = diff(tpSec); dtPulse(dtPulse<=0) = NaN; rpmInst = 60./(dtPulse*pulsesPerRev); rpmMed = median(rpmInst,"omitnan"); rpmStd = std(rpmInst,0,"omitnan"); rpmIQR = iqr(rpmInst); shaftHz = rpmMed/60; end
helperVerifyRequirements
The helperVerifyRequirements function determines whether the requirements described in Extracted Tabular Data Requirements are met, given the training set dataTrain and the test set dataTest. The function returns a table (verificationTable) with the results.
function verificationTable = helperVerifyRequirements(dataTrain,dataTest) minTrainObservations = 140; minTestObservations = 60; expectedFeatures = 20; scenarioNames = ["Scenario 1: Healthy","Scenario 2: Sensor Drift", ... "Scenario 3: Shaft Wear","Scenario 4: SD + SW", ... "Scenario 5: Gear Tooth Failure","Scenario 6: SD + GTF", ... "Scenario 7: SW + GTF","Scenario 8: SD + SW + GTF"]; RequirementID = strings(18,1); Description = strings(18,1); Value = zeros(18,1); Threshold = zeros(18,1); Result = strings(18,1); % Training data completeness requirements (rows 1–8) for k = 1:8 count = nnz(dataTrain.ScenarioID == k); RequirementID(k) = sprintf("TRAINING_COMPLETENESS_SCENARIO_%d",k); Description(k) = "Training observations from " + scenarioNames(k); Value(k) = count; Threshold(k) = minTrainObservations; if count >= minTrainObservations Result(k) = "PASS"; else Result(k) = "FAIL"; end end % Test data completeness requirements (rows 9–16) for k = 1:8 count = nnz(dataTest.ScenarioID == k); RequirementID(8+k) = sprintf("TEST_COMPLETENESS_SCENARIO_%d",k); Description(8+k) = "Test observations from " + scenarioNames(k); Value(8+k) = count; Threshold(8+k) = minTestObservations; if count >= minTestObservations Result(8+k) = "PASS"; else Result(8+k) = "FAIL"; end end % Training data feature count requirement (row 17) trainFeatureCount = width(dataTrain); RequirementID(17) = "TRAINING_FEATURE_COUNT"; Description(17) = "Training set contains expected number of features"; Value(17) = trainFeatureCount; Threshold(17) = expectedFeatures; if trainFeatureCount == expectedFeatures Result(17) = "PASS"; else Result(17) = "FAIL"; end % Test data feature count requirement (row 18) testFeatureCount = width(dataTest); RequirementID(18) = "TEST_FEATURE_COUNT"; Description(18) = "Test set contains expected number of features"; Value(18) = testFeatureCount; Threshold(18) = expectedFeatures; if testFeatureCount == expectedFeatures Result(18) = "PASS"; else Result(18) = "FAIL"; end verificationTable = table(RequirementID,Description,Value,Threshold,Result); end
See Also
Topics
- Design Condition Indicators at the Command Line (Predictive Maintenance Toolbox)