Main Content

Deploy Signal Classifier Using Wavelets and Deep Learning on Raspberry Pi

This example shows the workflow to classify human electrocardiogram (ECG) signals using the Continuous Wavelet Transform (CWT) and a deep convolutional neural network (CNN). This example also provides information on how to generate and deploy the code and CNN for prediction on a Raspberry Pi target (ARM®-based device).

SqueezeNet is a deep CNN originally designed to classify images in 1000 categories. In the exampleClassify Time Series Using Wavelet Analysis and Deep Learning(Wavelet Toolbox), SqueezeNet is retrained to classify ECG waveforms based on theirscalograms. A scalogram is a time-frequency representation of the signal and is the absolute value of the CWT of the signal. We reuse the retrained SqueezeNet in this example.

ECG Data Description

In this example, ECG data fromPhysioNetis used. The ECG data is obtained from three groups of people: persons with cardiac arrhythmia (ARR), persons with congestive heart failure (CHF), and persons with normal sinus rhythms (NSR). The data set includes 96 recordings from persons with ARR, 30 recordings from persons with CHF, and 36 recordings from persons with NSR. The 162 ECG recordings are from three PhysioNet databases:MIT-BIH Arrhythmia Database[2][3],MIT-BIH Normal Sinus Rhythm Database[3], andThe BIDMC Congestive Heart Failure Database[1][3]. Shortened ECG data of the above references can be downloaded from theGitHub repository.

Prerequisites

For supported versions of libraries and for information about setting up environment variables, seePrerequisites for Deep Learning with MATLAB Coder(MATLAB Coder). This example is not supported in MATLAB Online™.

Functionality of Generated Code

The core function in the generated executable,processECG, uses 65,536 samples of single-precision ECG data as input. The function:

  1. Takes the CWT of the ECG data.

  2. Obtains scalogram from wavelet coefficients.

  3. Converts the scalogram to an RGB image of dimension 227-by-227-by-3. This makes the image compatible with the SqueezeNet network architecture.

  4. Performs prediction to classify the image using SqueezeNet.

typeprocessECG
函数[YPred] = processECG(输入)% processECGfunction - converts 1D ECG to image and predicts the syndrome % of heart disease % % This function is only intended to support the example: % Signal Classification Code Generation Using Wavelets and % Deep Learning on Raspberry Pi. It may change or be removed in a % future release. % Copyright 2020 The MathWorks, Inc. % colourmap for image transformation persistent net jetdata; if(isempty(jetdata)) jetdata = colourmap(128,class(input)); end % Squeezenet trained network if(isempty(net)) net = coder.loadDeepLearningNetwork('trainedNet.mat'); end % Wavelet Transformation & Image conversion cfs = ecg_to_Image(input); image = ind2rgb(im2uint8(rescale(cfs)),single(jetdata)); image = im2uint8(imresize(image,[227,227])); % figure if isempty(coder.target) imshow(image); end % Prediction [YPred] = predict(net,image); %% ECG to image conversion function cfs = ecg_to_Image(input) %Wavelet Transformation persistent filterBank [~,siglen] = size(input); if isempty(filterBank) filterBank = cwtfilterbank('SignalLength',siglen,'VoicesPerOctave',6); end %CWT conversion cfs = abs(filterBank.wt(input)); end %% Colourmap function J = colourmap(m,class) n = ceil(m/4); u = [(1:1:n)/n ones(1,n-1) (n:-1:1)/n]'; g = ceil(n/2) - (mod(m,4)==1) + (1:length(u))'; r = g + n; b = g - n; r1 = r(r<=128); g1 = g(g<=128); b1 = b(b >0); J = zeros(m,3); J(r1,1) = u(1:length(r1)); J(g1,2) = u(1:length(g1)); J(b1,3) = u(end-length(b1)+1:end); feval = str2func(class); J = feval(J); end end

Create Code Generation Configuration Object

Create a code generation configuration object for generation of an executable program. Specify generation of C++ code.

cfg = coder.config('exe'); cfg.TargetLang ='C++';

Set Up Configuration Object for Deep Learning Code Generation

Create acoder.ARMNEONConfigobject. Specify the same version of the ARM Compute library as the one on the Raspberry Pi. Specify the architecture of the Raspberry Pi.

dlcfg = coder.DeepLearningConfig('arm-compute'); dlcfg.ArmComputeVersion ='19.05'; dlcfg.ArmArchitecture ='armv7';

Attach Deep Learning Configuration Object to Code Generation Configuration Object

Set theDeepLearningConfigobje属性的代码生成配置ct to the deep learning configuration object. Make the MATLAB Source Comments visible in the configuration object at the time of code generation.

cfg.DeepLearningConfig = dlcfg; cfg.MATLABSourceComments = 1;

Create a Connection to the Raspberry Pi

Use the MATLAB Support Package for Raspberry Pi Support Package function,raspi, to create a connection to the Raspberry Pi. In the following code, replace:

  • raspinamewith the name or IP address of your Raspberry Pi

  • usernamewith your user name

  • passwordwith your password

r = raspi('raspiname','username','password');

Configure Code Generation Hardware Parameters for Raspberry Pi

Create acoder.Hardwareobject for Raspberry Pi and attach it to the code generation configuration object.

hw = coder.hardware('Raspberry Pi'); cfg.Hardware = hw;

Specify the build folder on the Raspberry Pi.

buildDir ='~/remdirECG'; cfg.Hardware.BuildDir = buildDir;

Provide C++ Main File for Code Execution

The C++ main file reads the input ECG data, calls theprocessECGfunction to perform preprocessing and deep learning using CNN on the ECG data, and displays the classification probability.

在代码生成进行指定的主要文件guration object. To learn more about generating and customizingmain_ecg_raspi.cpp, refer toGenerating Standalone C/C++ Executables from MATLAB Code(MATLAB Coder).

cfg.CustomSource ='main_ecg_raspi.cpp';

Generate Source C++ Code Usingcodegen

Use thecodegenfunction to generate the C++ code. Whencodegenis used with the MATLAB Support Package for Raspberry Pi Hardware, the executable is built on the Raspberry Pi board.

Make sure to set the environment variablesARM_COMPUTELIBandLD_LIBRARY_PATHon the Raspberry Pi. SeePrerequisites for Deep Learning with MATLAB Coder(MATLAB Coder).

codegen-configcfgprocessECG-args{ones(1,65536,'single')}-darm_compute
Deploying code. This may take a few minutes.

Fetch Generated Executable Directory

To test the generated code on the Raspberry Pi, copy the input ECG signal to the generated code directory. You can find this directory manually or by using theraspi.utils.getRemoteBuildDirectoryAPI. This function lists the directories of the binary files that are generated by usingcodegen.

applicationDirPaths = raspi.utils.getRemoteBuildDirectory('applicationName','processECG')
applicationDirPaths=1×4 cell array{1×1 struct} {1×1 struct} {1×1 struct} {1×1 struct}

The complete path to the remote build directory is derived from the present working directory. If you do not know whichapplicationDirPathsentry contains the generated code, use the helper functionhelperFindTargetDir. Otherwise, specify the proper directory.

directoryUnknown = true;ifdirectoryUnknown targetDirPath = helperFindTargetDir(applicationDirPaths);elsetargetDirPath = applicationDirPaths{1}.directory;end

Copy Input File to Raspberry Pi

The text fileinput_ecg_raspi.csvcontains the ECG samples of a representative ARR signal. To copy the file required to run the executable program, useputFile, which is available with the MATLAB Support Package for Raspberry Pi Hardware.

r.putFile('input_ecg_raspi.csv', targetDirPath);

For a pictorial representation, the first 1000 samples can be plotted by using these steps.

input = dlmread('input_ecg_raspi.csv'); plot(input(1:1000)) title('ARR Signal')

Run Executable on Raspberry Pi

Run the executable program on the Raspberry Pi from MATLAB and direct the output back to MATLAB. The input file name is passed as the command line argument for the executable.

exeName ='processECG.elf';% executable namefileName ='input_ecg_raspi.csv';% Input ECG file that is pushed to targetcommand = ['cd 'targetDirPath';./'exeName' 'fileName]; output = system(r,command)
output = 'Predicted Values on the Target Hardware ARR CHF NSR 0.806078 0.193609 0.000313103 '

References

  1. Baim, D. S., W. S. Colucci, E. S. Monrad, H. S. Smith, R. F. Wright, A. Lanoue, D. F. Gauthier, B. J. Ransil, W. Grossman, and E. Braunwald. "Survival of patients with severe congestive heart failure treated with oral milrinone."Journal of the American College of Cardiology. Vol. 7, Number 3, 1986, pp. 661–670.

  2. Goldberger A. L., L. A. N. Amaral, L. Glass, J. M. Hausdorff, P. Ch. Ivanov, R. G. Mark, J. E. Mietus, G. B. Moody, C.-K. Peng, and H. E. Stanley. "PhysioBank, PhysioToolkit,and PhysioNet: Components of a New Research Resource for Complex Physiologic Signals." Circulation. Vol. 101, Number 23: e215–e220. [Circulation Electronic Pages;http://circ.ahajournals.org/content/101/23/e215.full]; 2000 (June 13). doi: 10.1161/01.CIR.101.23.e215.

  3. Moody, G. B., and R. G. Mark. "The impact of the MIT-BIH Arrhythmia Database."IEEE Engineering in Medicine and Biology Magazine. Vol. 20. Number 3, May-June 2001, pp. 45–50. (PMID: 11446209)

Supporting Functions

helperFindTargetDir

functiontargetDir = helperFindTargetDir(dirPaths)%% This function is only intended to support wavelet deep learning examples.% It may change or be removed in a future release.% find pwdp = pwd;ifispc% replace blank spaces with underscoresp = strrep(p,' ','_');% split path into component folderspSplit = regexp(p,filesep,'split');% Since Windows uses colons, remove any colons that occurfork=1:numel(pSplit) pSplit{k} = erase(pSplit{k},':');end% now build the path using Linux file separationpLinux ='';fork=1:numel(pSplit)-1 pLinux = [pLinux,pSplit{k},'/'];endpLinux = [pLinux,pSplit{end}];elsepLinux = p;endtargetDir ='';fork=1:numel(dirPaths) d = strfind(dirPaths{k}.directory,pLinux);if~isempty(d) targetDir = dirPaths{k}.directory;breakendendifnumel(targetDir) == 0 disp('Target directory not found.');endend