aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHongmei Gou2019-05-09 09:24:07 -0500
committerHongmei Gou2019-05-09 09:24:07 -0500
commitb1f2a6fc7f38db24b0daa6d972c796a2536e050a (patch)
treee7a40dc0ff53d9ae174f1f2206e8f64b47dd3b3b
parentd562ac54800e5bb13fe73636f34b283442faacc0 (diff)
parent8c25ca56383df4a3b71b8f34fde1ffbe1212f11b (diff)
downloadpdm-anomaly-detection-b1f2a6fc7f38db24b0daa6d972c796a2536e050a.tar.gz
pdm-anomaly-detection-b1f2a6fc7f38db24b0daa6d972c796a2536e050a.tar.xz
pdm-anomaly-detection-b1f2a6fc7f38db24b0daa6d972c796a2536e050a.zip
Merge pull request #2 in PROCESSOR-SDK/pdm-anomaly-detection from pdm-demo-bringup to master
* commit '8c25ca56383df4a3b71b8f34fde1ffbe1212f11b': Add throttling when there is no incoming sensor data Qt GUI: add setup image and block diagram along with a button to hide/display them Add Qt GUI to display sensor data and anomaly detection results
-rw-r--r--datasource.cpp80
-rw-r--r--datasource.h67
-rw-r--r--logs/normal100-anomaly150-normal100.logbin0 -> 9093122 bytes
-rw-r--r--logs/normal270-anomaly170-normal270.logbin0 -> 8601602 bytes
-rw-r--r--main.cpp60
-rwxr-xr-xmk.sh1
-rw-r--r--motor-pdm.cpp99
-rw-r--r--oscilloscope.pro28
-rw-r--r--psensors.cpp12
-rw-r--r--qml/oscilloscope/ControlPanel.qml17
-rw-r--r--qml/oscilloscope/MultiButton.qml40
-rw-r--r--qml/oscilloscope/PdmView.qml74
-rw-r--r--qml/oscilloscope/ScopeView.qml81
-rw-r--r--qml/oscilloscope/anomaly-detection.pngbin0 -> 23510 bytes
-rw-r--r--qml/oscilloscope/main.qml142
-rw-r--r--qml/oscilloscope/motor-drive.pngbin0 -> 231705 bytes
-rw-r--r--qml/oscilloscope/system-model.pngbin0 -> 61284 bytes
-rw-r--r--qml/oscilloscope/ti-logo.pngbin0 -> 19964 bytes
-rw-r--r--readme.txt21
-rw-r--r--resources.qrc13
-rwxr-xr-xrun.sh1
21 files changed, 693 insertions, 43 deletions
diff --git a/datasource.cpp b/datasource.cpp
new file mode 100644
index 0000000..8376f05
--- /dev/null
+++ b/datasource.cpp
@@ -0,0 +1,80 @@
1#include <iostream>
2#include "datasource.h"
3#include <QtCharts/QXYSeries>
4#include <QtCharts/QAreaSeries>
5#include <QtQuick/QQuickView>
6#include <QtQuick/QQuickItem>
7#include <QtCore/QDebug>
8
9#include <QtCore/QtMath>
10
11QT_CHARTS_USE_NAMESPACE
12
13Q_DECLARE_METATYPE(QAbstractSeries *)
14Q_DECLARE_METATYPE(QAbstractAxis *)
15
16DataSource::DataSource(QQuickView *appViewer, QObject *parent) :
17 QObject(parent),
18 m_appViewer(appViewer)
19{
20 qRegisterMetaType<QAbstractSeries*>();
21 qRegisterMetaType<QAbstractAxis*>();
22
23 for (int i=0; i<DISPLAY_VECTOR; i++)
24 {
25 generateData(i, 0, 256);
26 }
27
28 setPdmStage("Waiting for sensor data......");
29 setAnomalyDetection("");
30}
31
32void DataSource::update(int sel_waveform, QAbstractSeries *series)
33{
34 if (series && (m_last_read > 0)) {
35 QXYSeries *xySeries = static_cast<QXYSeries *>(series);
36 QVector<QPointF> points = m_data[sel_waveform];
37 // Use replace instead of clear + append, it's optimized for performance
38 qreal x(0);
39 qreal y(0);
40 for (int j = 0; j < m_sampleCount; j ++)
41 {
42 y = m_samplesArray[DISPLAY_VECTOR*j + sel_waveform];
43 x = j;
44 if (sel_waveform==4) {
45 if (y > 100) {y = y - 100; setPdmStage("Calibration......");}
46 if (y < -50) {y = y + 100; setPdmStage("Looking for anomaly......");}
47 }
48 points.replace(j, QPointF(x, y));
49 if ((sel_waveform==5) && (y>0)) setAnomalyDetection("Anomaly detected!!!");
50 if ((sel_waveform==5) && (y<=0)) setAnomalyDetection("");
51 }
52 xySeries->replace(points);
53 }
54}
55
56int motor_pdm_get_data(int samples_count, double *samples_array);
57void DataSource::getdata(void)
58{
59 m_last_read = motor_pdm_get_data(m_sampleCount, m_samplesArray);
60}
61
62void DataSource::generateData(int sel_waveform, int type, int colCount)
63{
64 // Remove previous data
65 m_data[sel_waveform].clear();
66
67 std::cout << std::endl << "__generating (" << sel_waveform << ":" << colCount << " type:" << type << ")____" << std::endl;
68
69 m_sampleCount = colCount;
70 // Append the new data depending on the type
71 QVector<QPointF> points;
72 points.reserve(colCount);
73 m_overlapWindow = type;
74 for (int j(0); j < colCount; j++) {
75 qreal x(0);
76 qreal y(0);
77 points.append(QPointF(x, y));
78 }
79 m_data[sel_waveform] = points;
80}
diff --git a/datasource.h b/datasource.h
new file mode 100644
index 0000000..ae88321
--- /dev/null
+++ b/datasource.h
@@ -0,0 +1,67 @@
1#ifndef DATASOURCE_H
2#define DATASOURCE_H
3
4#include <QtCore/QObject>
5#include <QtCharts/QAbstractSeries>
6
7#define DISPLAY_VECTOR 6
8
9class QQuickView;
10
11QT_CHARTS_USE_NAMESPACE
12
13class DataSource : public QObject
14{
15 Q_OBJECT
16 Q_PROPERTY(QString pdmStage READ getPdmStage WRITE setPdmStage NOTIFY pdmStageChanged)
17 Q_PROPERTY(QString anomalyDetection READ getAnomalyDetection WRITE setAnomalyDetection NOTIFY anomalyDetectionChanged)
18
19public:
20 explicit DataSource(QQuickView *appViewer, QObject *parent = 0);
21
22 QString getPdmStage()
23 {
24 return pdmStage;
25 }
26
27 void setPdmStage(const QString &text)
28 {
29 if (text == pdmStage) return;
30 pdmStage = text;
31 emit pdmStageChanged();
32 }
33
34 QString getAnomalyDetection()
35 {
36 return anomalyDetection;
37 }
38
39 void setAnomalyDetection(const QString &text)
40 {
41 if (text == anomalyDetection) return;
42 anomalyDetection = text;
43 emit anomalyDetectionChanged();
44 }
45
46Q_SIGNALS:
47 void pdmStageChanged();
48 void anomalyDetectionChanged();
49
50public slots:
51 void generateData(int sel_waveform, int type, int colCount);
52 void update(int sel_waveform, QAbstractSeries *series);
53 void getdata(void);
54
55private:
56 QQuickView *m_appViewer;
57 QVector<QPointF> m_data[DISPLAY_VECTOR];
58
59 int m_sampleCount, m_last_read;
60 int m_overlapWindow;
61
62 QString pdmStage, anomalyDetection;
63
64 double m_samplesArray[4096*DISPLAY_VECTOR]; // Keep in sync with max sample count selection in qml file
65};
66
67#endif // DATASOURCE_H
diff --git a/logs/normal100-anomaly150-normal100.log b/logs/normal100-anomaly150-normal100.log
new file mode 100644
index 0000000..47973b2
--- /dev/null
+++ b/logs/normal100-anomaly150-normal100.log
Binary files differ
diff --git a/logs/normal270-anomaly170-normal270.log b/logs/normal270-anomaly170-normal270.log
new file mode 100644
index 0000000..a110ad7
--- /dev/null
+++ b/logs/normal270-anomaly170-normal270.log
Binary files differ
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..2988119
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,60 @@
1#include <QtWidgets/QApplication>
2#include <QtQml/QQmlContext>
3#include <QtQuick/QQuickView>
4#include <QtQml/QQmlEngine>
5#include <QtCore/QDir>
6#include "datasource.h"
7#include <iostream> // std::cout
8#include <thread> // std::thread
9
10int motor_pdm_process(void);
11
12double mu1, mu2, sig1, sig2;
13double relative_threshold = 1.5;
14int main(int argc, char *argv[])
15{
16
17 //Get normalization parameters and relative threshold
18 if(argc < 5) {
19 printf("\nUsage:%s mu1 mu2 sig1 sig2 elative-threshold\n", argv[0]);
20 printf("\nTo set the relative threshold value, use an addtional positive value >= 1\n");
21 return -1;
22 }
23 mu1 = atof(argv[1]);
24 mu2 = atof(argv[2]);
25 sig1 = atof(argv[3]);
26 sig2 = atof(argv[4]);
27
28 if(argc > 5)
29 relative_threshold = atof(argv[5]);
30
31 // Qt Charts uses Qt Graphics View Framework for drawing, therefore QApplication must be used.
32 QApplication app(argc, argv);
33
34 QQuickView viewer;
35
36 // The following are needed to make examples run without having to install the module
37 // in desktop environments.
38#ifdef Q_OS_WIN
39 QString extraImportPath(QStringLiteral("%1/../../../../%2"));
40#else
41 QString extraImportPath(QStringLiteral("%1/../../../%2"));
42#endif
43 viewer.engine()->addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(),
44 QString::fromLatin1("qml")));
45 QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close);
46
47 viewer.setTitle(QStringLiteral("Deep Learning based Predictive Maintenance Demo on Sitara Edge Devices"));
48
49 std::thread motor_pdm_process_thread (motor_pdm_process);
50
51 DataSource dataSource(&viewer);
52 viewer.rootContext()->setContextProperty("dataSource", &dataSource);
53
54 viewer.setSource(QUrl("qrc:/qml/oscilloscope/main.qml"));
55 viewer.setResizeMode(QQuickView::SizeRootObjectToView);
56 viewer.setColor(QColor("#f5f5dc"));
57 viewer.show();
58
59 return app.exec();
60}
diff --git a/mk.sh b/mk.sh
deleted file mode 100755
index 79d6365..0000000
--- a/mk.sh
+++ /dev/null
@@ -1 +0,0 @@
1g++ --std=c++11 -Wall -O3 motor-pdm.cpp psensors.cpp lstm_infer.cpp onnx_model.cpp config.cpp -lpthread -o motpdm
diff --git a/motor-pdm.cpp b/motor-pdm.cpp
index 5b30193..9e60df5 100644
--- a/motor-pdm.cpp
+++ b/motor-pdm.cpp
@@ -26,21 +26,19 @@
26// delay line to hold input samples 26// delay line to hold input samples
27double insampPH1[ BUFFER_LEN ]; 27double insampPH1[ BUFFER_LEN ];
28double insampPH2[ BUFFER_LEN ]; 28double insampPH2[ BUFFER_LEN ];
29double insampPH3[ BUFFER_LEN ];
29// LP filter coefficients 30// LP filter coefficients
30#define DRATIO 200 31#define DRATIO 200
31double coeffs[ FIRFLT_LEN ]; 32double coeffs[ FIRFLT_LEN ];
32 33
33// Relative prediction error threshold
34#define RELATIVE_THRESHOLD 2.0
35
36// Each sample tick is 20ms long 34// Each sample tick is 20ms long
37#define START_FIND_THRESHOLD 150 35#define START_FIND_THRESHOLD 150
38#define STOP_FIND_THRESHOLD 1500 36#define STOP_FIND_THRESHOLD 1500
39#define HANGOVER_TIME 100 37#define HANGOVER_TIME 20
40 38
41#define max(a,b) (a > b ? a : b) 39#define max(a,b) (a > b ? a : b)
42 40
43int uart_get_data(int sample_count, int op_mode, float *samples_array); 41int uart_get_data(int sample_count, float *samples_array);
44void uart_stream_parser(int mode_op); 42void uart_stream_parser(int mode_op);
45void lstmSetup(void); 43void lstmSetup(void);
46void runLstm(double lstm_in1, double lstm_in2, double *lstm_out1, double *lstm_out2); 44void runLstm(double lstm_in1, double lstm_in2, double *lstm_out1, double *lstm_out2);
@@ -80,24 +78,54 @@ void firFloat( double *coeffs, double *insamp, int filterLength,
80 // shift input samples back in time for next time 78 // shift input samples back in time for next time
81 memmove( &insamp[0], &insamp[length], (filterLength - 1) * sizeof(double) ); 79 memmove( &insamp[0], &insamp[length], (filterLength - 1) * sizeof(double) );
82} 80}
83////////////////////////////////////////////////////////////// 81
84// Test program 82#define DISPLAY_VECTOR 6
85////////////////////////////////////////////////////////////// 83#define DISPLAY_MAX_BUFFER (DISPLAY_VECTOR*64*4096)
86// number of samples to read per loop 84static int display_rd_data_cnt = 0;
87int main( int argc, char *argv[]) 85static int display_wr_data_cnt = 0;
86static double data_ready_to_display[DISPLAY_MAX_BUFFER];
87
88int display_wr_idx = 0;
89
90int motor_pdm_get_data(int samples_count, double *samples_array)
91{
92 int rd_idx;
93 if(display_wr_data_cnt >= (display_rd_data_cnt + samples_count*DISPLAY_VECTOR))
94 {
95 for (int i = 0; i < samples_count; i ++)
96 {
97 rd_idx = display_rd_data_cnt % DISPLAY_MAX_BUFFER;
98 for (int j=0; j<DISPLAY_VECTOR; j++)
99 {
100 samples_array[DISPLAY_VECTOR*i + j] = data_ready_to_display[rd_idx + j];
101 }
102 display_rd_data_cnt += DISPLAY_VECTOR;
103 }
104
105 display_rd_data_cnt -= (127 * samples_count*DISPLAY_VECTOR) / 128;
106 return samples_count;
107 } else return 0;
108}
109
110extern double mu1, mu2, sig1, sig2;
111extern double relative_threshold;
112int motor_pdm_process(void)
88{ 113{
89 int size; 114 int size;
90 double floatInput1[SAMPLES]; 115 double floatInput1[SAMPLES];
91 double floatInput2[SAMPLES]; 116 double floatInput2[SAMPLES];
117 double floatInput3[SAMPLES];
92 double floatOutput1[SAMPLES/DRATIO]; 118 double floatOutput1[SAMPLES/DRATIO];
93 double floatOutput2[SAMPLES/DRATIO]; 119 double floatOutput2[SAMPLES/DRATIO];
120 double floatOutput3[SAMPLES/DRATIO];
94 double predict_threshold_err = 0.0; 121 double predict_threshold_err = 0.0;
95 double predict1 = 0.0; 122 double predict1 = 0.0;
96 double predict2 = 0.0; 123 double predict2 = 0.0;
97 double mu1, mu2, sig1, sig2;
98 FILE *coeff_fid; 124 FILE *coeff_fid;
99 int samples_counter = 0; 125 int samples_counter = 0;
100 int restart_hangover_timer = 0; 126 int restart_hangover_timer = 0;
127 int ad_detected = 0;
128 int pdmState = 0;
101 float samples_array[3*SAMPLES]; 129 float samples_array[3*SAMPLES];
102 130
103 //Pick up decimating FIR filter coefficients 131 //Pick up decimating FIR filter coefficients
@@ -109,16 +137,6 @@ int main( int argc, char *argv[])
109 fread(coeffs, sizeof(double), FIRFLT_LEN, coeff_fid ); 137 fread(coeffs, sizeof(double), FIRFLT_LEN, coeff_fid );
110 fclose( coeff_fid ); 138 fclose( coeff_fid );
111 139
112 //Get normalization parameters
113 if(argc < 5) {
114 printf("\nUsage:%s mu1 mu2 sig1 sig2\n", argv[0]);
115 return -1;
116 }
117 mu1 = atof(argv[1]);
118 mu2 = atof(argv[2]);
119 sig1 = atof(argv[3]);
120 sig2 = atof(argv[4]);
121
122 lstmSetup(); 140 lstmSetup();
123 // initialize the filter delay lines 141 // initialize the filter delay lines
124 firFloatInit(insampPH1, BUFFER_LEN); 142 firFloatInit(insampPH1, BUFFER_LEN);
@@ -130,43 +148,67 @@ int main( int argc, char *argv[])
130 while(1) 148 while(1)
131 { 149 {
132 // read samples from UART pipe 150 // read samples from UART pipe
133 size = uart_get_data(SAMPLES, 0, samples_array); 151 size = uart_get_data(SAMPLES, samples_array);
134 if(size == SAMPLES) 152 if(size == SAMPLES)
135 { // It means, at least this many SAMPLES are available 153 { // It means, at least this many SAMPLES are available
136 for(int i = 0; i < SAMPLES; i ++) { 154 for(int i = 0; i < SAMPLES; i ++) {
137 floatInput1[i] = (double)samples_array[3*i + 0]; 155 floatInput1[i] = (double)samples_array[3*i + 0];
138 floatInput2[i] = (double)samples_array[3*i + 1]; 156 floatInput2[i] = (double)samples_array[3*i + 1];
139 // position is samples_array[3*i + 2] 157 floatInput3[i] = (double)samples_array[3*i + 2];
140 } 158 }
141 // perform the filtering 159 // perform the filtering
142 // One decimated sample generated 160 // One decimated sample generated
143 firFloat( coeffs, insampPH1, FIRFLT_LEN, floatInput1, SAMPLES, floatOutput1 ); 161 firFloat( coeffs, insampPH1, FIRFLT_LEN, floatInput1, SAMPLES, floatOutput1 );
144 firFloat( coeffs, insampPH2, FIRFLT_LEN, floatInput2, SAMPLES, floatOutput2 ); 162 firFloat( coeffs, insampPH2, FIRFLT_LEN, floatInput2, SAMPLES, floatOutput2 );
163 firFloat( coeffs, insampPH3, FIRFLT_LEN, floatInput3, SAMPLES, floatOutput3 );
164
165 // Writting too fast, do write side throttling
166 if(display_wr_data_cnt > (display_rd_data_cnt + DISPLAY_MAX_BUFFER - 16))
167 std::this_thread::sleep_for(std::chrono::milliseconds(100));
168
169 for (int i = 0; i < SAMPLES/DRATIO; i ++)
170 {
171 display_wr_idx = display_wr_data_cnt % DISPLAY_MAX_BUFFER;
172 data_ready_to_display[display_wr_idx+0] = floatOutput1[i];
173 data_ready_to_display[display_wr_idx+1] = floatOutput2[i];
174 data_ready_to_display[display_wr_idx+2] = floatOutput3[i];
175 display_wr_data_cnt += 3;
176 }
177
145 //Calculate prediction error (using previously predicted samples) 178 //Calculate prediction error (using previously predicted samples)
146 double predict_error = (floatOutput1[0] - predict1) * (floatOutput1[0] - predict1); 179 double predict_error = (floatOutput1[0] - predict1) * (floatOutput1[0] - predict1);
147 predict_error += (floatOutput2[0] - predict2) * (floatOutput2[0] - predict2); 180 predict_error += (floatOutput2[0] - predict2) * (floatOutput2[0] - predict2);
181
182 ad_detected = 0;
183
148 if(samples_counter > START_FIND_THRESHOLD) 184 if(samples_counter > START_FIND_THRESHOLD)
149 { 185 {
150 if(samples_counter < STOP_FIND_THRESHOLD) 186 if(samples_counter < STOP_FIND_THRESHOLD)
151 { // Find error threshold 187 { // Find error threshold
188 pdmState = 100;
152 predict_threshold_err = max(predict_error, predict_threshold_err); 189 predict_threshold_err = max(predict_error, predict_threshold_err);
153 std::cout << "Find error threshold:" << samples_counter << std::endl;
154 restart_hangover_timer = 0; 190 restart_hangover_timer = 0;
155 } else { 191 } else {
156 std::cout << "Look for anomaly:" << samples_counter << " err:" << predict_error << " thre:" << (RELATIVE_THRESHOLD * predict_threshold_err); 192 pdmState = -100;
157 // Anomaly detection mode! 193 // Anomaly detection mode!
158 // ...implemented hangover 194 // ...implemented hangover
159 if(predict_error > RELATIVE_THRESHOLD * predict_threshold_err) 195 if(predict_error > relative_threshold * predict_threshold_err)
160 { 196 {
161 restart_hangover_timer = samples_counter; 197 restart_hangover_timer = samples_counter;
162 } 198 }
163 std::cout << std::endl;
164 if((samples_counter - restart_hangover_timer) < HANGOVER_TIME) 199 if((samples_counter - restart_hangover_timer) < HANGOVER_TIME)
165 { 200 {
166 std::cout << " [AD] "; 201 ad_detected = 1;
167 } 202 }
168 } 203 }
169 } 204 }
205
206 display_wr_idx = display_wr_data_cnt % DISPLAY_MAX_BUFFER;
207 data_ready_to_display[display_wr_idx+0] = predict_error;
208 data_ready_to_display[display_wr_idx+1] = predict_threshold_err*relative_threshold + pdmState;
209 data_ready_to_display[display_wr_idx+2] = ad_detected * predict_threshold_err *relative_threshold * 1.25;
210 display_wr_data_cnt += 3;
211
170 //Normalize 212 //Normalize
171 double lstm_out1, lstm_in1 = (floatOutput1[0] - mu1) / sig1; 213 double lstm_out1, lstm_in1 = (floatOutput1[0] - mu1) / sig1;
172 double lstm_out2, lstm_in2 = (floatOutput2[0] - mu2) / sig2; 214 double lstm_out2, lstm_in2 = (floatOutput2[0] - mu2) / sig2;
@@ -179,7 +221,6 @@ int main( int argc, char *argv[])
179 samples_counter ++; // Increamented at decimated rate!!! 221 samples_counter ++; // Increamented at decimated rate!!!
180 } else { 222 } else {
181 std::this_thread::sleep_for(std::chrono::milliseconds(100)); 223 std::this_thread::sleep_for(std::chrono::milliseconds(100));
182 std::cout << "sleep" << std::endl;
183 } 224 }
184 } 225 }
185 226
diff --git a/oscilloscope.pro b/oscilloscope.pro
new file mode 100644
index 0000000..effdd51
--- /dev/null
+++ b/oscilloscope.pro
@@ -0,0 +1,28 @@
1QT += charts qml quick
2
3DEFINES += QRANDOMGENERATOR_MISSING
4
5HEADERS += \
6 datasource.h\
7 config.h \
8 LSTM_model.h \
9 onnx_model.h
10
11SOURCES += \
12 main.cpp \
13 datasource.cpp \
14 psensors.cpp \
15 motor-pdm.cpp \
16 lstm_infer.cpp \
17 onnx_model.cpp \
18 config.cpp \
19
20RESOURCES += \
21 resources.qrc
22
23DISTFILES += \
24 qml/oscilloscope/*
25
26target.path = $$[QT_INSTALL_EXAMPLES]/charts/oscilloscope
27INSTALLS += target
28TARGET = RnnPdmAnomalyDetection
diff --git a/psensors.cpp b/psensors.cpp
index 8b333c9..004dfdb 100644
--- a/psensors.cpp
+++ b/psensors.cpp
@@ -18,14 +18,13 @@ static int rd_data_cnt = 0;
18static int wr_data_cnt = 0; 18static int wr_data_cnt = 0;
19static float data_ready_to_use[MAX_BUFFER]; 19static float data_ready_to_use[MAX_BUFFER];
20 20
21int uart_get_data(int samples_count, int overlap_mode, float *samples_array) 21int uart_get_data(int samples_count, float *samples_array)
22{ 22{
23 int rd_idx; 23 int rd_idx;
24#ifdef VERBOSE 24#ifdef VERBOSE
25 printf ("\nwr=%d rd=%d cnt=%d ", wr_data_cnt, rd_data_cnt, samples_count); 25 printf ("\nwr=%d rd=%d cnt=%d ", wr_data_cnt, rd_data_cnt, samples_count);
26 printf ("\noverlap_mode=%d ", overlap_mode);
27#endif 26#endif
28 if(wr_data_cnt >= (rd_data_cnt + samples_count)) 27 if(wr_data_cnt >= (rd_data_cnt + samples_count*3))
29 { 28 {
30 for (int i = 0; i < samples_count; i ++) 29 for (int i = 0; i < samples_count; i ++)
31 { 30 {
@@ -124,6 +123,13 @@ const char * myfifo = "/tmp/myfifo";
124 phase1 = (float)int_phase1 / 32767.0; 123 phase1 = (float)int_phase1 / 32767.0;
125 phase2 = (float)int_phase2 / 32767.0; 124 phase2 = (float)int_phase2 / 32767.0;
126 position = (float)val_position / 33554432.0f; 125 position = (float)val_position / 33554432.0f;
126
127 /* throttling when there is no incoming sensor data */
128 if(phase1 == 0) {
129 std::this_thread::sleep_for(std::chrono::milliseconds(20));
130 continue;
131 }
132
127 if(log_filename) fprintf(fout, "%8.5f %8.5f %8.5f\n", phase1, phase2, position); 133 if(log_filename) fprintf(fout, "%8.5f %8.5f %8.5f\n", phase1, phase2, position);
128#ifdef VERBOSE 134#ifdef VERBOSE
129 printf("... %8.5f %8.5f %8.5f", phase1, phase2, position); 135 printf("... %8.5f %8.5f %8.5f", phase1, phase2, position);
diff --git a/qml/oscilloscope/ControlPanel.qml b/qml/oscilloscope/ControlPanel.qml
new file mode 100644
index 0000000..c9554de
--- /dev/null
+++ b/qml/oscilloscope/ControlPanel.qml
@@ -0,0 +1,17 @@
1import QtQuick 2.1
2import QtQuick.Layouts 1.0
3
4ColumnLayout {
5 property alias imageDisplayButton: imageDisplayButton
6 spacing: 8
7 Layout.fillHeight: true
8 signal imageDisplayChanged(bool enabled)
9
10 MultiButton {
11 id: imageDisplayButton
12 text: " Setup Image and Diagram"
13 items: ["Show", "Hide"]
14 currentSelection: 0
15 onSelectionChanged: imageDisplayChanged(currentSelection == 1);
16 }
17}
diff --git a/qml/oscilloscope/MultiButton.qml b/qml/oscilloscope/MultiButton.qml
new file mode 100644
index 0000000..b06f319
--- /dev/null
+++ b/qml/oscilloscope/MultiButton.qml
@@ -0,0 +1,40 @@
1import QtQuick 2.0
2import QtQuick.Controls 1.0
3import QtQuick.Controls.Styles 1.0
4
5Item {
6 id: button
7
8 property string text: "Option: "
9 property variant items: ["first"]
10 property int currentSelection: 0
11 signal selectionChanged(variant selection)
12
13 signal clicked
14
15 implicitWidth: buttonText.implicitWidth + 5
16 implicitHeight: buttonText.implicitHeight + 10
17
18 Button {
19 id: buttonText
20 width: parent.width
21 height: parent.height
22
23 style: ButtonStyle {
24 label: Component {
25 Text {
26 text: button.items[currentSelection] + button.text
27 clip: true
28 wrapMode: Text.WordWrap
29 verticalAlignment: Text.AlignVCenter
30 horizontalAlignment: Text.AlignHCenter
31 anchors.fill: parent
32 }
33 }
34 }
35 onClicked: {
36 currentSelection = (currentSelection + 1) % items.length;
37 selectionChanged(button.items[currentSelection]);
38 }
39 }
40}
diff --git a/qml/oscilloscope/PdmView.qml b/qml/oscilloscope/PdmView.qml
new file mode 100644
index 0000000..dfbe9ec
--- /dev/null
+++ b/qml/oscilloscope/PdmView.qml
@@ -0,0 +1,74 @@
1import QtQuick 2.0
2import QtCharts 2.1
3
4ChartView {
5 id: chartViewPdm
6 animationOptions: ChartView.NoAnimation
7 theme: ChartView.ChartThemeBlueCerulean
8 property bool openGL: true
9 property bool openGLSupported: true
10
11 legend.font.pointSize: 16
12
13 ValueAxis {
14 id: axisY1
15 min: 0
16 max: 0.003
17 }
18
19 ValueAxis {
20 id: axisX
21 min: 0
22 max: 256
23 }
24
25 LineSeries {
26 id: lineSeries1Pdm
27 name: "Prediction Error"
28 width: 3
29 color: "sandybrown"
30 axisX: axisX
31 axisY: axisY1
32 useOpenGL: chartViewPdm.openGL
33 }
34 LineSeries {
35 id: lineSeries2Pdm
36 name: "Threshold"
37 width: 3
38 color: "#1e90ff"
39 axisX: axisX
40 axisY: axisY1
41 useOpenGL: chartViewPdm.openGL
42 }
43 LineSeries {
44 id: lineSeries3Pdm
45 name: "Anomaly"
46 width: 3
47 color: "#ff0000"
48 axisX: axisX
49 axisY: axisY1
50 useOpenGL: chartViewPdm.openGL
51 }
52
53 Timer {
54 id: refreshTimer
55 interval: 1 / 30 * 1000 // 30 Hz
56 running: true
57 repeat: true
58 onTriggered: {
59 dataSource.update(3, chartViewPdm.series(0));
60 dataSource.update(4, chartViewPdm.series(1));
61 dataSource.update(5, chartViewPdm.series(2));
62 }
63 }
64
65 function createAxis(min, max) {
66 // The following creates a ValueAxis object that can be then set as a x or y axis for a series
67 return Qt.createQmlObject("import QtQuick 2.0; import QtCharts 2.0; ValueAxis { min: "
68 + min + "; max: " + max + " }", chartViewPdm);
69 }
70
71 function changeRefreshRate(rate) {
72 refreshTimer.interval = 1 / Number(rate) * 1000;
73 }
74}
diff --git a/qml/oscilloscope/ScopeView.qml b/qml/oscilloscope/ScopeView.qml
new file mode 100644
index 0000000..72191d0
--- /dev/null
+++ b/qml/oscilloscope/ScopeView.qml
@@ -0,0 +1,81 @@
1import QtQuick 2.0
2import QtCharts 2.1
3
4ChartView {
5 id: chartView
6 animationOptions: ChartView.NoAnimation
7 theme: ChartView.ChartThemeBlueCerulean
8 property bool openGL: true
9 property bool openGLSupported: true
10
11 legend.font.pointSize: 16
12
13 ValueAxis {
14 id: axisY1
15 min: -0.12
16 max: 0.12
17 }
18
19 ValueAxis {
20 id: axisY3
21 min: 0
22 max: 1.1
23 }
24
25 ValueAxis {
26 id: axisX
27 min: 0
28 max: 256
29 }
30
31 LineSeries {
32 id: lineSeries1
33 name: "Phase Current 1"
34 color: "darkturquoise"
35 width: 3
36 axisX: axisX
37 axisY: axisY1
38 useOpenGL: chartView.openGL
39 }
40 LineSeries {
41 id: lineSeries2
42 name: "Phase Current 2"
43 color: "forestgreen"
44 width: 3
45 axisX: axisX
46 axisYRight: axisY1
47 useOpenGL: chartView.openGL
48 }
49 LineSeries {
50 id: lineSeries3
51 name: "Position"
52 width: 3
53 color: "orange"
54 axisX: axisX
55 axisYRight: axisY3
56 useOpenGL: chartView.openGL
57 }
58
59 Timer {
60 id: refreshTimer
61 interval: 1 / 30 * 1000 // 30 Hz
62 running: true
63 repeat: true
64 onTriggered: {
65 dataSource.getdata();
66 dataSource.update(0, chartView.series(0));
67 dataSource.update(1, chartView.series(1));
68 dataSource.update(2, chartView.series(2));
69 }
70 }
71
72 function createAxis(min, max) {
73 // The following creates a ValueAxis object that can be then set as a x or y axis for a series
74 return Qt.createQmlObject("import QtQuick 2.0; import QtCharts 2.0; ValueAxis { min: "
75 + min + "; max: " + max + " }", chartView);
76 }
77
78 function changeRefreshRate(rate) {
79 refreshTimer.interval = 1 / Number(rate) * 1000;
80 }
81}
diff --git a/qml/oscilloscope/anomaly-detection.png b/qml/oscilloscope/anomaly-detection.png
new file mode 100644
index 0000000..9428300
--- /dev/null
+++ b/qml/oscilloscope/anomaly-detection.png
Binary files differ
diff --git a/qml/oscilloscope/main.qml b/qml/oscilloscope/main.qml
new file mode 100644
index 0000000..d5972c7
--- /dev/null
+++ b/qml/oscilloscope/main.qml
@@ -0,0 +1,142 @@
1import QtQuick 2.0
2
3Item {
4 id: main
5 width: 1920
6 height: 1080
7
8 Text {
9 anchors.top: parent.top
10 anchors.topMargin: 50
11 anchors.left: parent.left
12 anchors.leftMargin: 50
13 text: "Deep Learning based Predictive Maintenance (PdM) on Sitara Edge Devices"
14 font.family: "Helvetica"
15 font.pointSize: 36
16 font.bold: true
17 color: "red"
18 }
19
20 Image {
21 source: "./ti-logo.png"
22 scale: 0.35
23 anchors.bottom: parent.bottom
24 anchors.bottomMargin: 35
25 anchors.right: parent.right
26 anchors.rightMargin: -80
27 }
28
29 ControlPanel {
30 id: controlPanel
31 anchors.bottom: parent.bottom
32 anchors.bottomMargin: 75
33 anchors.left: parent.left
34 anchors.leftMargin: 60
35 onImageDisplayChanged: {
36 picmotordrive.visible = (!picmotordrive.visible);
37 picanomalydetection.visible = (!picanomalydetection.visible);
38 picsystemmodel.visible = (!picsystemmodel.visible);
39 }
40 }
41
42 ScopeView {
43 id: scopeView
44 anchors.top: parent.top
45 anchors.topMargin: 200
46 anchors.bottom: parent.bottom
47 anchors.bottomMargin: 100
48 anchors.left: parent.left
49 anchors.leftMargin: 50
50 anchors.right: parent.right
51 anchors.rightMargin: 950
52 }
53
54 Image {
55 id: picmotordrive
56 source: "./motor-drive.png"
57 scale: 0.83
58 visible: false
59 anchors.top: scopeView.top
60 anchors.topMargin: 80
61 anchors.right: scopeView.right
62 anchors.rightMargin: 200
63 }
64
65
66 PdmView {
67 id: pdmView
68 anchors.top: parent.top
69 anchors.topMargin: 200
70 anchors.bottom: parent.bottom
71 anchors.bottomMargin: 100
72 anchors.left: scopeView.right
73 anchors.right: parent.right
74 anchors.rightMargin: 50
75 }
76
77 Image {
78 id: picanomalydetection
79 source: "./anomaly-detection.png"
80 scale: 0.85
81 visible: false
82 anchors.top: pdmView.top
83 anchors.topMargin: 70
84 anchors.right: pdmView.right
85 anchors.rightMargin: 370
86 }
87
88 Image {
89 id: picsystemmodel
90 source: "./system-model.png"
91 scale: 0.8
92 visible: false
93 anchors.top: pdmView.top
94 anchors.topMargin:160
95 anchors.right: pdmView.right
96 anchors.rightMargin: 15
97 }
98
99 Text {
100 text: "Real-time Sensor Input"
101 font.family: "Helvetica"
102 font.pointSize: 22
103 color: "red"
104 anchors.top: parent.top
105 anchors.topMargin: 150
106 anchors.left: parent.left
107 anchors.leftMargin: 280
108 }
109
110 Text {
111 text: "Real-time Anomaly Detection"
112 font.family: "Helvetica"
113 font.pointSize: 22
114 color: "red"
115 anchors.top: parent.top
116 anchors.topMargin: 150
117 anchors.right: parent.right
118 anchors.rightMargin: 280
119 }
120
121 Text {
122 text: dataSource.pdmStage
123 font.family: "Helvetica"
124 font.pointSize: 22
125 color: "green"
126 anchors.top: parent.top
127 anchors.topMargin: 650
128 anchors.right: parent.right
129 anchors.rightMargin: 460
130 }
131
132 Text {
133 text: dataSource.anomalyDetection
134 font.family: "Helvetica"
135 font.pointSize: 24
136 color: "red"
137 anchors.top: parent.top
138 anchors.topMargin: 650
139 anchors.right: parent.right
140 anchors.rightMargin: 125
141 }
142}
diff --git a/qml/oscilloscope/motor-drive.png b/qml/oscilloscope/motor-drive.png
new file mode 100644
index 0000000..13f6393
--- /dev/null
+++ b/qml/oscilloscope/motor-drive.png
Binary files differ
diff --git a/qml/oscilloscope/system-model.png b/qml/oscilloscope/system-model.png
new file mode 100644
index 0000000..983827f
--- /dev/null
+++ b/qml/oscilloscope/system-model.png
Binary files differ
diff --git a/qml/oscilloscope/ti-logo.png b/qml/oscilloscope/ti-logo.png
new file mode 100644
index 0000000..9763773
--- /dev/null
+++ b/qml/oscilloscope/ti-logo.png
Binary files differ
diff --git a/readme.txt b/readme.txt
index 162f32e..74f751c 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,17 +1,20 @@
1============================== 1==============================
2Native compilation on target 2Cross compilation on Ubunut
3============================== 3==============================
4Copy the folder pdm-anomaly-detection to target, and then run "./mk.sh" 4With Qt build environment and qtcharts available, run "qmake", and then "make".
5After the compilation is completed successfully, motpdm binary will be created.
6 5
7 6
8============================== 7==============================
9Run motpdm on target 8Run RnnPdmAnomalyDetection on target
10============================== 9==============================
11Terminal 1: ./run.sh > log.txt 10Stop Matrix GUI if needed: /etc/init.d/matrix-gui stop
12Terminal 2: cat ./logs/normal45-270-v100-with-friction2-iter10-15.log > /tmp/myfifo
13 11
14After the cat command in Terminal 2 is done, stop the process (CTRL-C) in Terminal 1. 12Terminal 1: ./RnnPdmAnomalyDetection -0.0001841035315 0.004191935886 0.01918238488 0.01933241767 1.5
13This will open a Qt GUI for the demo.
15 14
16Then, check reports in log.txt, with time units being in 20ms ticks (decimated sample 15Terminal 2: direct sensor data input to /tmp/myfifo, e.g.,
17rate of 50Hz). AD (Anomaly detection) is reported along with the sample counter. 16 cat ./logs/normal45-270-v100-with-friction2-iter10-15.log > /tmp/myfifo
17 cat ./logs/normal100-anomaly150-normal100.log > /tmp/myfifo
18 cat ./logs/normal270-anomaly170-normal270.log > /tmp/myfifo
19
20View the sensor data and anomaly detection results from the Qt GUI.
diff --git a/resources.qrc b/resources.qrc
new file mode 100644
index 0000000..4a7ebaf
--- /dev/null
+++ b/resources.qrc
@@ -0,0 +1,13 @@
1<RCC>
2 <qresource prefix="/">
3 <file>qml/oscilloscope/main.qml</file>
4 <file>qml/oscilloscope/ControlPanel.qml</file>
5 <file>qml/oscilloscope/ScopeView.qml</file>
6 <file>qml/oscilloscope/PdmView.qml</file>
7 <file>qml/oscilloscope/MultiButton.qml</file>
8 <file>qml/oscilloscope/ti-logo.png</file>
9 <file>qml/oscilloscope/motor-drive.png</file>
10 <file>qml/oscilloscope/anomaly-detection.png</file>
11 <file>qml/oscilloscope/system-model.png</file>
12 </qresource>
13</RCC>
diff --git a/run.sh b/run.sh
deleted file mode 100755
index f040753..0000000
--- a/run.sh
+++ /dev/null
@@ -1 +0,0 @@
1./motpdm -0.0001841035315 0.004191935886 0.01918238488 0.01933241767