什么是机器学习 机器学习领域的先驱ArthurSamuel,在其论文《Some Studies in Machine Learning Using the Game of Checkers》中,将机器学习非正式定义为:“在不直接针对问题进行编程的情况下,赋予计算机学习能力的一个研究领域。”例如要让Genuino 101判断其自身姿态是正面朝上,还是朝下时。常规做法是,计算出姿态角,并判断其是否在某一区间中;而使用机器学习,可以通过多次将Genuino 101朝上或朝下放置,并将此时传感器数据及姿态输入模式匹配引擎进行学习,此后Genuino 101即可根据新的传感器数据判断当前的姿态了。 intel Curie的模式匹配引擎(patternmatching engine),带有128个神经元,支持k近邻法(k-Nearest Neighbors)和径向基核函数(Radial Basis Function)两种匹配算法。其让Curie具有了像人一样的学习、归类能力,进而可以省去某些繁琐的编程过程。 Intel提供了CuriePME库用于驱动模式匹配引擎,其下载地址为: 或 下载安装CuriePME后,可通过示例程序了解其使用方法。如下示例程序可用于学习并识别手势动作。 [kenrobot_code]/*
* This example demonstrates using the pattern matching engine (CuriePME)
* to classify streams of accelerometer data from CurieIMU.
*
* First, the sketch will prompt you to draw some letters in the air (just
* imagine you are writing on an invisible whiteboard, using your board as the
* pen), and the IMU data from these motions is used as training data for the
* PME. Once training is finished, you can keep drawing letters and the PME
* will try to guess which letter you are drawing.
*
* This example requires a button to be connected to digital pin 4
* https://www.arduino.cc/en/Tutorial/Button
*
* NOTE: For best results, draw big letters, at least 1-2 feet tall.
*
* Copyright (c) 2016 Intel Corporation. All rights reserved.
* See license notice at end of file.
*/
#include "CurieIMU.h"
#include "CuriePME.h"
/* This controls how many times a letter must be drawn during training.
* Any higher than 4, and you may not have enough neurons for all 26 letters
* of the alphabet. Lower than 4 means less work for you to train a letter,
* but the PME may have a harder time classifying that letter. */
const unsigned int trainingReps = 4;
/* Increase this to 'A-Z' if you like-- it just takes a lot longer to train */
const unsigned char trainingStart = 'A';
const unsigned char trainingEnd = 'F';
/* The input pin used to signal when a letter is being drawn- you'll
* need to make sure a button is attached to this pin */
const unsigned int buttonPin = 4;
/* 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);
/* This value is used to convert ASCII characters A-Z
* into decimal values 1-26, and back again. */
const unsigned int upperStart = 0x40;
const unsigned int sensorBufSize = 2048;
const int IMULow = -32768;
const int IMUHigh = 32767;
void setup()
{
Serial.begin(9600);
while(!Serial);
pinMode(buttonPin, INPUT);
/* Start the IMU (Intertial Measurement Unit) */
CurieIMU.begin();
/* Start the PME (Pattern Matching Engine) */
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]);
/* Map raw values to 0-255 */
accel = (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);
}
}
[/kenrobot_code]
编译并上传以上程序到Genuino101,按串口提示,即可体验使用Genuino 101学习并识别动作。运行该示例需要在4号引脚上接一个按键模块,按下按键即会开始一次新的学习,松开按键结束该次学习。 该示例主要使用的了CuriePME中learn和classify两个函数,这也是机器学习的两个主要过程。
学习 uint16_t CuriePME.learn (uint8_t vector[], int32t vector_length, uint16_t category) |
其中参数vector要进行学习的数据,参数vector_length为数据长度,参数category该次学习对应的分类类别。 调用learn函数,即可告知CuriePME,数据vector属于类别category。 CuriePME是由128个特殊存储单元组成的神经元网络。每个存储单元可以容纳128字节的数据。每次调用learn函数,都会将输入的新数据写入网络中的一个神经元,即CuriePME在清空重置的状态下,可以进行128次学习操作,每次用于学习的数据vector长度最大为128字节。
分类 uint16_t CuriePME.classify (uint8_t vector[], int32_t vector_length) |
其中参数vector是要进行识别的数据,vector_length是要该数据的长度。调用classify函数,CuriePME即会判断数据vector属于哪一个别类,并返回别类对应的编号。
以上程序中,CurieIMU设定加速度采样频率200Hz,采样缓冲区2 kb,最多可以录制3.41秒的动作,再经过程序处理将2kb数据缩小到128 byte,进行学习和分类。如果需要录制更长时间的动作,可以将加速度采样频率降低,或扩大采样缓冲区。 由于实际用于学习和分类的特征数据只有128 byte,所以理论上越简单的动作,约少的录制时间,会得到更好的学习和识别效果。 CuriePME主要用来结合CurieIMU进行姿态、动作的学习和识别,但实际上也可以用于其他类型数据的处理,本书篇幅有限,不做过多论述。
-------------------------------------------------------------------------------------------------------------
本教程分为五部分:
1.配置IMU及获取数据 http://www.arduino.cn/thread-42850-1-1.html
2.解算AHRS姿态 http://www.arduino.cn/thread-42851-1-1.html
3.姿态数据可视化 http://www.arduino.cn/thread-42852-1-1.html
4.IMU中断检测 http://www.arduino.cn/thread-42853-1-1.html
5.神经元与机器学习 http://www.arduino.cn/thread-42854-1-1.html
|