Export Network to FMU
This example shows how to export a trained network as a Functional Mock-up Unit (FMU).
An FMU is a file that contains a simulation model that adheres to the Functional Mock-up Interface (FMI) standard [1]. Software that implements the FMI standard can use FMU files for model exchange or co-simulation. MATLAB® and Simulink® support exporting trained deep neural networks as FMUs. This example shows how to take a network trained using Deep Learning Toolbox™, implemented and tested using Simulink, and export it to FMU for use with other simulation software.
Load Pretrained Network
This example uses a pretrained LSTM network to predict the remaining useful life (RUL) of an engine, measured in cycles. The LSTM network consists of an LSTM layer with 200 hidden units, followed by a fully connected layer of size 50 and a dropout layer with dropout probability 0.5.The network was trained using the Turbofan Engine Degradation Simulation Data Set as described in [2]. The training data contains simulated time series data for 100 engines. Each sequence varies in length and corresponds to a full run to failure (RTF) instance. The test data contains 100 partial sequences and corresponding values of the remaining useful life at the end of each sequence. For more information on training the network, see the example Sequence-to-Sequence Regression Using Deep Learning.
net = coder.loadDeepLearningNetwork("rulNetwork.mat");
Download and Prepare Test Data
This section summarizes the steps to download and prepare the test data that this example uses. For more information on the Turbofan Engine Degradation Simulation data set and the preprocessing steps, see Sequence-to-Sequence Regression Using Deep Learning.
Download Data Set
Create a directory to store the Turbofan Engine Degradation Simulation data set.
dataFolder = fullfile(tempdir,"turbofan"); if ~exist(dataFolder,"dir") mkdir(dataFolder); end
Download and extract the Turbofan Engine Degradation Simulation data set.
filename = matlab.internal.examples.downloadSupportFile("nnet","data/TurbofanEngineDegradationSimulationData.zip"); unzip(filename,dataFolder)
Preprocess the data using the processTurboFanData
function provided at the end of this example. The processTurboFanData
function returns:
structure
simin
containing the data values and an empty time vectorcell array
YValidate
containing target sequencesarray
sequenceLengths
containing the lengths of the sequences inYValidate
[simin,YValidate,sequenceLengths] = processTurboFanData(dataFolder);
Simulink Model for Prediction
The Simulink model for predicting the remaining useful life of a turbofan engine is shown. The model uses the Predict block from the Deep Neural Networks library that imports the trained network from the rulNetwork
MAT-file. Additionally, the Mini-batch size
parameter of the block is set to 1
.
model = "rulPredict";
open_system(model)
Run the Simulation
To validate the Simulink model.
sim(model);
The output YPred
of the Simulink model contains the predicted remaining useful life values from the network. This output is first trimmed to remove the results from the zero-padded values and then converted to a cell array.
maxSequenceLen = max(sequenceLengths); YPred_cell = squeeze(mat2cell(YPred,1,maxSequenceLen,ones(1,100))); for t = 1:numel(sequenceLengths) YPred_cell{t}(:,sequenceLengths(t) + 1:end) = []; end
Plot the predicted RUL values for four randomly-selected observations and compare them to the validation data. The supporting function rulExamplePlots
is provided at the end of this example.
observationIdx = randperm(100,4); rulExamplePlots(observationIdx,YValidate,YPred_cell);
Prepare Model for Export
To be exported as an FMU, a deep learning model in Simulink must meet the following requirements.
The network used by the Predict block must support code generation without using any third-party libraries.
The input and output signals, parameters, and their elements of the model must be
double
,int32
,boolean
, orstring
.The simulation target language must be C.
The solver type must be
fixed-step
.
For more information on exporting Simulink models to FMU and considerations for simulating in another environment, see Export Simulink Models to Functional Mock-up Units (Simulink Compiler).
Check that the network supports library-free code generation.
analyzeNetworkForCodegen(net,TargetLibrary="none")
Supported _________ none "Yes"
For a list of the networks, layers, and classes supported for code generation, see Networks and Layers Supported for Code Generation (MATLAB Coder).
The network used by the Predict block outputs single-precision numeric values. As single
is not an allowed output data type when exporting to FMU, add a Data Type Conversion block to convert the output to double
.
delete_line(model,"Predict/1","Outport/1") add_block("simulink/Commonly Used Blocks/Data Type Conversion",model+"/CastToDouble") set_param(model+"/CastToDouble",OutDataTypeStr="double") add_line(model,"Predict/1","CastToDouble/1") add_line(model,"CastToDouble/1","Outport/1")
The simulation target language defines the language of the generated code. Check that the target language is C. If the target language is not C, set it using set_param(activeConfigObj,
"SimTargetLang"
,
"C++"
)
.
get_param(model,"SimTargetLang")
ans = 'C'
By default, Simulink automatically selects a variable-step solver. Set the solver type to fixed-step
.
set_param(model,SolverType="fixed-step")
Replace the From Workspace input block and replace with an Inport block. Set Inport port dimensions to match the dimensions of the data in simin
and realign the layout of the model.
replace_block(model,"FromWorkspace","Inport","noprompt") set_param(model+"/From Workspace",Name="In1",PortDimensions="[17 303]") Simulink.BlockDiagram.arrangeSystem(model)
Export Model to FMU
Using the exportToFMU2CS
function, export the model to an FMU. The exportToFMU2CS
(Simulink Compiler) function creates an .fmu file in the current folder with the same name as the model.
exportToFMU2CS("rulPredict");
Setting System Target to FMU Co-Simulation for model 'rulPredict'. Setting Hardware Implementation > Device Type to 'MATLAB Host' for model 'rulPredict'. ### 'GenerateReport' is disabled for Co-Simulation Standalone FMU Export. ### 'GenerateComments' is disabled for Co-Simulation Standalone FMU Export. Build Summary Top model targets built: Model Action Rebuild Reason ============================================================================================ rulPredict Code generated and compiled. Code generation information file does not exist. 1 of 1 models built (0 models already up to date) Build duration: 0h 1m 57.954s ### Model was successfully exported to co-simulation standalone FMU: 'C:\TEMP\rulPredict.fmu'.
Close the model without saving.
bdclose(model)
Test FMU (Optional)
To ensure that the FMU operates correctly, you can reimport it into Simulink.
Open the original model.
open_system(model)
Replace the Predict block with an FMU block and set the FMU name parameter to the name of the FMU file.
replace_block(model,"Predict","FMU","noprompt") set_param(model+"/Predict",Name="FMU",FMUName="rulPredict.fmu")
Connect the blocks and remove unused lines.
add_line(model,"From Workspace/1","FMU/1") delete_line(find_system(model,FindAll="on",Type="line",Connected="off"))
As co-simulating with an FMU in Simulink introduces a time delay of one time step, increase the StopTime
of the model by one time step. In this example, the StopTime
is increased from 9.9
to 10
as the solver sample time is 0.1
. For more information on this delay, see Why there is a time delay of one time step while co-simulating a FMU in Simulink?.
set_param(model,StopTime="10");
Run the simulation.
sim(model);
Remove the first element of the predicted values due to the time delay introduced by the FMU block.
YPred = YPred(:,:,2:end);
Plot the predicted RUL values for four randomly-selected observations and compare them to the validation data.
YPred_cell = squeeze(mat2cell(YPred,1,maxSequenceLen,ones(1,100))); for t = 1:length(sequenceLengths) YPred_cell{t}(:,sequenceLengths(t) + 1:end) = []; end observationIdx = randperm(100,4); rulExamplePlots(observationIdx,YValidate,YPred_cell);
Supporting Functions
Process TurboFan Data
The processTurboFanData
function reads the turbofan training and testing data and returns structure simin
containing normalized validation data and an empty time vector, cell array YValidate
containing target sequences, and array sequenceLengths
containing the lengths of the sequences in YValidate
. To make the input validation data compatible with Simulink code generation, the sequence lengths for each of the independent 100 observations are zero-padded to create uniformly-sized, 17-by-303 input arrays. The padded values are then converted to a 17-by-303-by-100 numeric array. To import this data into the Simulink model, specify a structure variable containing the data values and an empty time vector. During simulation, the input for the first time step is read from the first 17-by-303 element of the array. The value for the second time step is read from the second element, and so on, for a total of 100 steps.
The processTurboFanData
function uses the functions processTurboFanDataTrain
and processTurboFanDataTest
, which are attached to this example as supporting files. Open the example as a live script to use these functions.
function [simin,YValidate,sequenceLengths] = processTurboFanData(dataFolder) % Determine the mean and variance of the training data. filenamePredictors = fullfile(dataFolder,"train_FD001.txt"); [XTrain] = processTurboFanDataTrain(filenamePredictors); m = min([XTrain{:}],[],2); M = max([XTrain{:}],[],2); idxConstant = M == m; for i = 1:numel(XTrain) XTrain{i}(idxConstant,:) = []; end mu = mean([XTrain{:}],2); sig = std([XTrain{:}],0,2); % Read the validation data and normalize. filenamePredictors = fullfile(dataFolder,"test_FD001.txt"); filenameResponses = fullfile(dataFolder,"RUL_FD001.txt"); [XValidate,YValidate] = processTurboFanDataTest(filenamePredictors,filenameResponses); thr = 150; for i = 1:numel(XValidate) XValidate{i}(idxConstant,:) = []; XValidate{i} = (XValidate{i} - mu) ./ sig; YValidate{i}(YValidate{i} > thr) = thr; end % Pad the sequences. sequenceLengths = cellfun(@length,XValidate,UniformOutput=true); maxSequenceLen = max(sequenceLengths); padFcn = @(x) [x,zeros(size(x,1),maxSequenceLen-size(x,2))]; XValidatePad = cellfun(padFcn,XValidate,UniformOutput=false); % Store the validation data and an emtpy time vector in a structure. simin.time = []; simin.signals.values = cell2mat(reshape(XValidatePad,1,1,[])); simin.signals.dimensions = size(XValidatePad{1}); end
Plot Remaining Useful Life
The rulExamplePlots
function plots RUL values, comparing target values against predictions.
function rulExamplePlots(observationIdx,YTest,YPred) N = numel(observationIdx); figure for i = 1:N subplot(N/2,2,i) plot(YTest{observationIdx(i)},'--') hold on plot(YPred{observationIdx(i)},'.-') hold off ylim([0 175]) title("Test Observation " + observationIdx(i)) xlabel("Time Step") ylabel("RUL") end legend(["Test Data" "Predicted"],Location="southeast") end
References
1. The Functional Mock-up Interface (FMI) Standard Specification, Version 3.0, https://fmi-standard.org/.
2. Saxena, Abhinav, Kai Goebel, Don Simon, and Neil Eklund. "Damage propagation modeling for aircraft engine run-to-failure simulation." In Prognostics and Health Management, 2008. PHM 2008. International Conference on, pp. 1-9. IEEE, 2008.
See Also
exportToFMU2CS
(Simulink Compiler)
Related Topics
- Export Simulink Model to Standalone FMU (Simulink Compiler)
- Import FMUs (Simulink)