Arduino Classifying EMG signal by ANN - arduino
Currently I'm trying to replicate this wonderful project https://www.youtube.com/watch?v=u8pwmzUVx48, though with more minimum hardware, in which for the classification i'm using a neural network embbeded inside the arduino uno which had been trained offline in more stronger PC. The code of the classifier is here
#include <math.h>
#include <Wire.h>
double dataMagnitude[100];
double feature[7];
double feat_hid[10];
double output[4];
int classi;
const double w_hid[8][10] =
{
{18.9336670380822,11.9186055569093,-5.40114594311576,-21.1410711100689,-49.7798510124792,-14.8215222433047,-34.7308138535581,118.348601654702,-13.6275407707706,-11.6090487951753},
{-21.4994463865001,72.0672467700052,4.05328299052883,21.4875491464005,51.1604296852446,-15.8459578543758,6.30350750703905,-152.565733734759,12.8648040583067,13.8199895673734},
{8.66214515599865,-200.488705071393,-5.86973559011113,-23.4805286444925,-1.11016147795671,-3.04686605995311,-93.4143749063794,-73.8925691072615,-6.18427236042285,-10.9872535411407},
{-12.317892447947,-37.2154526807162,3.83978412266769,2.12866508710205,-11.9740446238331,10.2198116665101,41.9369083083022,63.2036147993661,1.40768362243561,15.4978881385563},
{7.58319670243546,161.895072542918,-2.3654701067923,1.91708846557259,-2.87224127392426,-16.5850302581614,45.2372430254377,34.255971511768,-2.30234070310014,-7.8952356779332},
{0.603971580989218,128.0244079393,0.628535912033608,-1.25788426737745,-0.204480047424961,-41.3372322514891,3.26860448943873,4.163893213914,0.443532051478394,-0.276136697942473},
{-3.63944154925129,18.3802540188974,0.975095285922393,-0.326187652485656,1.25521855841132,39.4877166809573,-15.3542772116519,-14.9546824078715,0.965532742621277,3.72386985716534},
{5.93321854314305,12.673690719929,-3.36580252980446,-21.2089241183081,-10.8980644878121,-7.29095431091201,-30.2240843969778,-2.48980394834853,-5.4167647620581,-5.68671825319015}
}, w_out[11][4] =
{
{1.07887052819228,-21.9615926704441,105.450147012522,-84.5674248703326},
{0.0344508533215184,0.551350792323934,-0.96380329372866,0.37800164808339},
{-99.251397621058,23.1671754381478,7.53147169676884,68.5527504861813},
{-5.0354869957171,4.36918523413481,0.408725687040089,0.257576074543764},
{-27.4478368365984,7.00659524306471,1.74370945871769,18.6975321348269},
{-0.213736004615516,-0.784795805217531,0.0732416484342244,0.925290161399087},
{8.62052547929066,-45.9408034639393,116.959261953552,-79.6389839689755},
{-8.5317661802465,45.4251911929737,-117.146523754862,80.2530987422069},
{127.053878005091,-29.4397580015468,-9.33919798608733,-88.2749220175082},
{1.11869995358251,-21.5111648695486,105.002356379838,-84.6098914639344},
{-5.81786935588552,3.78305066207264,0.11556429335063,-0.0807455995360207}
};
double S, Sig, Sigma, Sigma1, Sigma2, MAV, RMS, VAR, SDT, WL, ZC, SSC, Xn, accum, accumi;
char analogpin = 0, N=100;
void setup()
{
/* add setup code here */
Serial.begin(9600);
Wire.begin();
}
void loop()
{
do{
//data acqusition
for( int i = 0; i<100;i++)
{
Xn = (analogRead(analogpin))-510;
dataMagnitude[i]=Xn;
delayMicroseconds(830);
// Serial.println(dataMagnitude[i]);
}
S = 0;
for (size_t i = 0; i < N; i++)
{
S = S + dataMagnitude[i];
}
Sig = 0;
Sigma = 0;
Sigma1 = 0;
Sigma2 = 0;
for (size_t i = 0; i < N; i++)
{
Sig = Sig + abs(dataMagnitude[i]);
Sigma = Sigma + pow(dataMagnitude[i], 2);
Sigma1 = Sigma1 + pow((dataMagnitude[i] - S / N), 2);
}
for (size_t i = 0; i < N - 1; i++)
{
Sigma2 = Sigma2 + abs(dataMagnitude[i + 1] - dataMagnitude[i]);
}
//featureextract
MAV = (((1 / (double)N)*Sig-27.67)*2/(272.02-27.67))-1;
RMS = (sqrt((1 / (double)N)*Sigma)-34.91002721)*2/(284.1419012-34.91002721)-1;
VAR = (((1 / (double)(N))*Sigma1)-698.4139)*2/(52178.5691-698.4139)-1;
SDT = (sqrt((1 / (double)(N)) * Sigma1)-26.42752164)*2/(228.4262881-26.42752164)-1;
WL = (Sigma2-1621)*2/(11273-1621)-1;
//ZC = 0;
for (size_t i = 0; i < N - 1; i++)
{
if (dataMagnitude[i] == 0 && dataMagnitude[i] != dataMagnitude[i + 1])
{
ZC++;
}
}
ZC = (ZC-0)*2/(39-0)-1;
//SSC = 0;
for (size_t i = 0; i < N - 2; i++)
{
if (dataMagnitude[i]>dataMagnitude[i + 1] && dataMagnitude[i + 1]<dataMagnitude[i + 2])
{
SSC++;
}
else if (dataMagnitude[i]<dataMagnitude[i + 1] && dataMagnitude[i + 1]>dataMagnitude[i + 2])
{
SSC++;
}
}
SSC = (SSC-48)*2/(78-48)-1;
double feature[] = { MAV, RMS, VAR, SDT, WL, ZC, SSC };
//neural network construction
//first-hidden layer
for (int i = 0; i < 10; i++)
{
accum = w_hid[7][i];
for (int j = 0; j < 7; j++)
{
accum += (feature[j] * w_hid[j][i]);
}
feat_hid[i] = tanh(accum);
}
for (int i = 0; i < 4; i++)
{
accumi = w_out[10][i];
for (int j = 0; j < 10; j++)
{
accumi += (feat_hid[j] * w_out[j][i]);
}
output[i] = round(accumi);
}
//Classify the output
if (output[0] == 1 && output[1] < 1 && output[2] < 1 && output[3] < 1)
{
classi = 1;
}
else if (output[0] < 1 && output[1] == 1 && output[2] < 1 && output[3] < 1)
{
classi = 2;
}
else if (output[0] < 1 && output[1] < 1 && output[2] == 1 && output[3] < 1)
{
classi = 3;
}
else if (output[0] < 1 && output[1] < 1 && output[2] < 1 && output[3] == 1)
{
classi = 4;
}
else
{
classi = 0;
}
Wire.beginTransmission(5);
Wire.write(classi);
//Wire.write(int(output[2]));
Wire.endTransmission();
Serial.println("wew");
Serial.println(output[0]);
Serial.println(output[1]);
Serial.println(output[2]);
Serial.println(output[3]);
//Serial.println(classi);
//Serial.println(feature[1]);
//Serial.println(feature[2]);
//Serial.println(feature[3]);
//Serial.println(feature[4]);
//Serial.println(feature[5]);
//Serial.println(feature[6]);
for (size_t i = 0; i < 10; i++)
{
feat_hid[i] = 0;
}
for (size_t i = 0; i < 4; i++)
{
output[i] = 0;
}
}while(analogRead(analogpin)>0);
}
But the real time implementation is rather dissatisfying, in which the output always mismatched, though by offline implementation is quite better.
My assumption is that the data capturing when online is rather bad, or maybe because any other things?
Thanks for any feedback
How did you train the network offline?
If you have used Matlab to train the network (i.e. to identify the weight and bias values) and then implement the same network on Arduino.
In such case we have problems with number precision. Matlab by default works on 64 bit precision whereas Arduino works on 32 bit.
Difference in the way the numbers are represented creates the Error.
Related
Why do I get a StackOverFlow error on the first recursive call?
Below is the code I am referring to, and the first recursive call # checkDirections(grid, i - 1, j) is giving me a StackOverFlow error. I understand that this means the code is not hitting the base case, but I do not understand why. class Solution { public int orangesRotting(int[][] grid) { int rowLength = grid.length; int colLength = grid[0].length; int minMinutes = 0; for (int i = 0; i < rowLength; i++) { for (int j = 0; j < colLength; j++) { if (grid[i][j] == 2) { checkDirections(grid, i, j); } } } return minMinutes; } public void checkDirections(int[][] grid, int i, int j) { if ((i < 0 || i > grid.length || j < 0 || j > grid[0].length) || grid[i][j] == 0) { return; } else if (grid[i][j] == 1) { grid[i][j] = 2; return; } //check left checkDirections(grid, i - 1, j); //check right checkDirections(grid, i + 1, j); //check up checkDirections(grid, i, j - 1); //check down checkDirections(grid, i, j + 1); } }
How to convert Ximea xiAPI camera data into QImage?
I have data from a camera in mono 8bit. This is converted into an int vector using std::vector<int> grayVector(size); // convert / copy pointer data into vector: 8 bit if (static_cast<XI_IMG_FORMAT>(format) == XI_MONO8) { quint8* imageIterator = reinterpret_cast<quint8*> (pMemVoid); for (size_t count = 0; count < size; ++count) { grayVector[count] = static_cast<int>(*imageIterator); imageIterator++; } } Next, I need to convert this into a QImage. If I set the image format to QImage::Format_Mono the app crashes. With QImage::Format_RGB16 I get strippes, and with QImage::Format_RGB32 everything is black. I would like to know how to do this the best, efficient and correct way? // convert gray values into QImage data QImage image = QImage(static_cast<int>(sizeX), static_cat<int>(sizeY), QImage::Format_RGB16); for ( int y = 0; y < sizeY; ++y ) { int yoffset = sizeY*y; QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)) ; for ( int x = 0; x < sizeX ; ++x ) { int pos = x + yoffset; int color = grayVector[static_cast<size_t>(pos)]; *line++ = qRgb(color, color, color); } }
The conversion to int is unnecessary and you do it in a very inefficient way; all you need is to use the QImage::Format_Grayscale8 available since Qt 5.5 (mid-2015). Anyway, what you really want is a way to go from XI_IMG to QImage. The default BP_UNSAFE buffering policy should be adequate - the QImage will do a format conversion, so taking the data from XiApi's internal buffer is OK. Thus the following - all of the conversions are implemented in Qt and are quite efficient - much better than most any naive code. I didn't check whether some Xi formats may need a BGR swap. If so, then the swap can be set to true in the format selection code and the rest will happen automatically. See also: xiAPI manual. static QVector<QRgb> grayScaleColorTable() { static QVector<QRgb> table; if (table.isEmpty()) { table.resize(256); auto *data = table.data(); for (int i = 0; i < table.size(); ++i) data[i] = qRgb(i, i, i); } return table; } constexpr QImage::Format grayScaleFormat() { return (QT_VERSION >= QT_VERSION_CHECK(5,5,0)) ? QImage::Format_Grayscale8 : QImage::Format_Indexed8; } QImage convertToImage(const XI_IMG *src, QImage::Format f) { Q_ASSERT(src->fmt == XI_MONO16); Q_ASSERT((src->padding_x % 2) == 0); if (src->fmt != XI_MONO16) return {}; const quint16 *s = static_cast<const quint16*>(src->bp); const int s_pad = src->padding_x/2; if (f == QImage::Format_BGR30 || f == QImage::Format_A2BGR30_Premultiplied || f == QImage::Format_RGB30 || f == QImage::Format_A2RGB30_Premultiplied) { QImage ret{src->width, src->height, f}; Q_ASSERT((ret->bytesPerLine() % 4) == 0); const int d_pad = ret->bytesPerLine()/4 - ret->width(); quint32 *d = (quint32*)ret.bits(); if (s_pad == d_pad) { const int N = (src->width + s_pad) * src->height - s_pad; for (int i = 0; i < N; ++i) { quint32 const v = (*s++) >> (16-10); *d++ = 0xC0000000 | v << 20 | v << 10 | v; } } else { for (int j = 0; j < src->height; ++j) { for (int i = 0; i < src->width; ++i) { quint32 const v = (*s++) >> (16-10); *d++ = 0xC0000000u | v << 20 | v << 10 | v; } s += s_pad; d += d_pad; } } return ret; } QImage ret{src->width, src->height, grayScaleFormat()}; const int d_pad = ret->bytesPerLine() - ret->width(); auto *d = ret.bits(); if (s_pad == d_pad) { const int N = (src->width + s_pad) * src->height - s_pad; for (int i = 0; i < N; ++i) { *d++ = (*s++) >> 8; } else { for (int j = 0; j < src->height; ++j) { for (int i = 0; i < src->width; ++i) *d++ = (*s++) >> 8; s += s_pad; d += d_pad; } } return ret; } QImage fromXiImg(const XI_IMG *src, QImage::Format dstFormat = QImage::Format_ARGB32Premultiplied) { Q_ASSERT(src->width > 0 && src->height > 0 && src->padding_x >= 0 && src->bp_size > 0); Q_ASSERT(dstFormat != QImage::Format_Invalid); bool swap = false; int srcPixelBytes = 0; bool externalConvert = false; QImage::Format srcFormat = QImage::Format_Invalid; switch (src->fmt) { case XI_MONO8: srcPixelBytes = 1; srcFormat = grayScaleFormat(); break; case XI_MONO16: srcPixelBytes = 2; externalConvert = true; break; case XI_RGB24: srcPixelBytes = 3; srcFormat = QImage::Format_RGB888; break; case XI_RGB32: srcPixelBytes = 4; srcFormat = QImage::Format_RGB32; break; }; if (srcFormat == QImage::Format_Invalid && !externalConvert) { qWarning("Unhandled XI_IMG image format"); return {}; } Q_ASSERT(srcPixelBytes > 0 && srcPixelBytes <= 4); int bytesPerLine = src->width * srcPixelBytes + src->padding_x; if ((bytesPerLine * src->height - src->padding_x) > src->bp_size) { qWarning("Inconsistent XI_IMG data"); return {}; } QImage ret; if (!externalConvert) ret = QImage{static_cast<const uchar*>(src->bp), src->width, src->height, bytesPerLine, srcFormat}; else ret = convertToImage(src, dstFormat); if (ret.format() == QImage::Format_Indexed8) ret.setColorTable(grayScaleColorTable()); if (ret.format() != dstFormat) ret = std::move(ret).convertToFormat(dstFormat); if (swap) ret = std::move(ret).rgbSwapped(); if (!ret.isDetached()) // ensure that we don't share XI_IMG's data buffer ret.detach(); return ret; }
How to writte matrice(2d array) into listbox
I have tried following code but it writes in vertical way.How can i add 2d array into my listbox for (int i = 0; i < test.GetLength(0); i++) { for (int j = 0; j < test.GetLength(1); j++) { ListBox3.Items.Add("-" + test[i, j].ToString()); } ListBox3.Items.Add("\n"); } }
This may help you from what I understand from your query; string listboxstr = ""; for (int i = 0; i < test.GetLength(0); i++) { listboxstr = "" + test[i, 0].ToString(); for (int j = 1; j < test.GetLength(1); j++) { listboxstr +="-" + test[i, j].ToString(); } ListBox3.Items.Add(listboxstr); ListBox3.Items.Add("\n"); } }
Unhandled exception error with two dimensional array
This dynamic programming algorithm is returning unhandled exception error probably due to the two dimensional arrays that I am using for various (and very large) number of inputs. I can't seem to figure out the issue here. The complete program as follows: // A Dynamic Programming based solution for 0-1 Knapsack problem #include<stdio.h> #include<stdlib.h> #define MAX 10000 int size; int Weight; int p[MAX]; int w[MAX]; // A utility function that returns maximum of two integers int maximum(int a, int b) { return (a > b) ? a : b; } // Returns the maximum value that can be put in a knapsack of capacity W int knapSack(int W, int wt[], int val[], int n) { int i, w; int retVal; int **K; K = (int**)calloc(n+1, sizeof(int*)); for (i = 0; i < n + 1; ++i) { K[i] = (int*)calloc(W + 1, sizeof(int)); } // Build table K[][] in bottom up manner for (i = 0; i <= n; i++) { for (w = 0; w <= W; w++) { if (i == 0 || w == 0) K[i][w] = 0; else if (wt[i - 1] <= w) K[i][w] = maximum(val[i - 1] + K[i - 1][w - wt[i - 1]], K[i - 1][w]); else K[i][w] = K[i - 1][w]; } } retVal = K[n][W]; for (i = 0; i < size + 1; i++) free(K[i]); free(K); return retVal; } int random_in_range(unsigned int min, unsigned int max) { int base_random = rand(); if (RAND_MAX == base_random) return random_in_range(min, max); int range = max - min, remainder = RAND_MAX % range, bucket = RAND_MAX / range; if (base_random < RAND_MAX - remainder) { return min + base_random / bucket; } else { return random_in_range(min, max); } } int main() { srand(time(NULL)); int val = 0; int i, j; //each input set is contained in an array int batch[] = { 10, 20, 30, 40, 50, 5000, 10000 }; int sizeOfBatch = sizeof(batch) / sizeof(batch[0]); //algorithms are called per size of the input array for (i = 0; i < sizeOfBatch; i++){ printf("\n"); //dynamic array allocation (variable length to avoid stack overflow //calloc is used to avoid garbage values int *p = (int*)calloc(batch[i], sizeof(int)); int *w = (int*)calloc(batch[i], sizeof(int)); for (j = 0; j < batch[i]; j++){ p[j] = random_in_range(1, 500); w[j] = random_in_range(1, 100); } size = batch[i]; Weight = batch[i] * 25; printf("| %d ", batch[i]); printf(" %d", knapSack(Weight, w, p, size)); free(p); free(w); } _getch(); return 0; }
Change this: for (i = 0; i < size + 1; i++) free(K[i]); free(K); return K[size][Weight]; To this: int retVal; ... retVal = K[size][Weight]; for (i = 0; i < size + 1; i++) free(K[i]); free(K); return retVal;
Changing method to be completely recursive
void merge(List<E> l, int lower, int upper) { ArrayList<E> array = new ArrayList<E>(); for (int i = lower; i <= upper; i++) array.add(list.get(i)); int front= 0; int front2= (array.size() + 1) / 2; for (int i = lower; i <= upper; i++) { if (front2 >= array.size() || (first < ((array.size() + 1) / 2) && (array.get(first).compareTo(array.get(second)) <= 0))) { l.set(i, array.get(front)); front++; }// end if else { l.set(i, array.get(front2)); front2++; } } } This is my method. I want to change it to be completely recursive(I don't want to use for loops), but I simply don't see how. Is there a way to make this recursive or avoid using loops?