#include "CurieIMU.h" #include "CuriePME.h" const unsigned int buttonPin = 4; const unsigned int trainingReps = 4; const unsigned int classification = 3; const unsigned int len = 128; void training() { unsigned int currentClassification = 0; while ( currentClassification < classification ) { currentClassification++; unsigned int currentTraining = 0; while( currentTraining < trainingReps ) { currentTraining++; // 获取传感器数据 byte data[128] = readVectorFromIMU(vector); // 使用该数据进行训练 CuriePME.learn(vector, 128, currentClassification); } } unsigned int currentTraining = 0; while (i < repeat) { byte vector[vectorNumBytes]; if (i) Serial.println("And again..."); readVectorFromIMU(vector); CuriePME.learn(vector, vectorNumBytes, letter - upperStart); Serial.println("Got it!"); delay(1000); ++i; } } /* Sample rate for accelerometer */ const unsigned int sampleRateHZ = 200; /* No. of bytes that one neuron can hold */ const unsigned int vectorNumBytes = 128; /* Number of processed samples (1 sample == accel x, y, z) * that can fit inside a neuron */ const unsigned int samplesPerVector = (vectorNumBytes / 3); const unsigned int sensorBufSize = 2048; const int IMULow = -32768; const int IMUHigh = 32767; void setup() { Serial.begin(9600); while(!Serial); pinMode(buttonPin, INPUT); CurieIMU.begin(); CuriePME.begin(); CurieIMU.setAccelerometerRate(sampleRateHZ); CurieIMU.setAccelerometerRange(2); // 进入训练状态 trainLetters(); Serial.println("Training complete. Now, draw some letters (remember to "); Serial.println("hold the button) and see if the PME can classify them."); } void loop () { // 进入识别状态 byte vector[vectorNumBytes]; unsigned int category; char letter; /* Record IMU data while button is being held, and * convert it to a suitable vector */ readVectorFromIMU(vector); /* Use the PME to classify the vector, i.e. return a category * from 1-26, representing a letter from A-Z */ category = CuriePME.classify(vector, vectorNumBytes); if (category == CuriePME.noMatch) { Serial.println("Don't recognise that one-- try again."); } else { letter = category + upperStart; Serial.println(letter); } } /* Simple "moving average" filter, removes low noise and other small * anomalies, with the effect of smoothing out the data stream. */ byte getAverageSample(byte samples[], unsigned int num, unsigned int pos, unsigned int step) { unsigned int ret; unsigned int size = step * 2; if (pos < (step * 3) || pos > (num * 3) - (step * 3)) { ret = samples[pos]; } else { ret = 0; pos -= (step * 3); for (unsigned int i = 0; i < size; ++i) { ret += samples[pos - (3 * i)]; } ret /= size; } return (byte)ret; } /* We need to compress the stream of raw accelerometer data into 128 bytes, so * it will fit into a neuron, while preserving as much of the original pattern * as possible. Assuming there will typically be 1-2 seconds worth of * accelerometer data at 200Hz, we will need to throw away over 90% of it to * meet that goal! * * This is done in 2 ways: * * 1. Each sample consists of 3 signed 16-bit values (one each for X, Y and Z). * Map each 16 bit value to a range of 0-255 and pack it into a byte, * cutting sample size in half. * * 2. Undersample. If we are sampling at 200Hz and the button is held for 1.2 * seconds, then we'll have ~240 samples. Since we know now that each * sample, once compressed, will occupy 3 of our neuron's 128 bytes * (see #1), then we know we can only fit 42 of those 240 samples into a * single neuron (128 / 3 = 42.666). So if we take (for example) every 5th * sample until we have 42, then we should cover most of the sample window * and have some semblance of the original pattern. */ void undersample(byte samples[], int numSamples, byte vector[]) { unsigned int vi = 0; unsigned int si = 0; unsigned int step = numSamples / samplesPerVector; unsigned int remainder = numSamples - (step * samplesPerVector); /* Centre sample window */ samples += (remainder / 2) * 3; for (unsigned int i = 0; i < samplesPerVector; ++i) { for (unsigned int j = 0; j < 3; ++j) { vector[vi + j] = getAverageSample(samples, numSamples, si + j, step); } si += (step * 3); vi += 3; } } void readVectorFromIMU(byte vector[]) { byte accel[sensorBufSize]; int raw[3]; unsigned int samples = 0; unsigned int i = 0; /* Wait until button is pressed */ while (digitalRead(buttonPin) == LOW); /* While button is being held... */ while (digitalRead(buttonPin) == HIGH) { if (CurieIMU.accelDataReady()) { CurieIMU.readAccelerometer(raw[0], raw[1], raw[2]); accel[i] = (byte) map(raw[0], IMULow, IMUHigh, 0, 255); accel[i + 1] = (byte) map(raw[1], IMULow, IMUHigh, 0, 255); accel[i + 2] = (byte) map(raw[2], IMULow, IMUHigh, 0, 255); i += 3; ++samples; /* If there's not enough room left in the buffers * for the next read, then we're done */ if (i + 3 > sensorBufSize) { break; } } } undersample(accel, samples, vector); } void trainLetter(char letter, unsigned int repeat) { unsigned int i = 0; while (i < repeat) { byte vector[vectorNumBytes]; if (i) Serial.println("And again..."); readVectorFromIMU(vector); CuriePME.learn(vector, vectorNumBytes, letter - upperStart); Serial.println("Got it!"); delay(1000); ++i; } } void trainLetters() { for (char i = trainingStart; i <= trainingEnd; ++i) { Serial.print("Hold down the button and draw the letter '"); Serial.print(String(i) + "' in the air. Release the button as soon "); Serial.println("as you are done."); trainLetter(i, trainingReps); Serial.println("OK, finished with this letter."); delay(2000); } }