Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen

Komponenten und Materialien

Komponente Menge Hinweise
NeoPixel-Streifen 1 Kann durch einzelne RGB-LEDs ersetzt werden.
Arduino 101 1
SparkFun Drucktaster 12mm 1
Widerstand 10k Ohm 1
Jumper-Kabel (generisch) 1 Sie benötigen möglicherweise lange Kabel, um die LEDs von der Schulter zur Hand zu führen.
9V-Batterie (generisch) 1
9V auf Hohlstecker-Adapter 1
Adafruit Flora RGB NeoPixel LEDs - Packung mit 4 1 Kann den NeoPixel-Streifen ersetzen.
Nähbares leitfähiges Garn 1 Option zum Ersetzen einiger Kabel.

Benötigte Werkzeuge und Maschinen

Apps und Online-Dienste

Über dieses Projekt

Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen
Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen

Ich bin Ingenieurin und Künstlerin und mag Projekte, die Wissenschaft und Kunst verbinden. Ich entwerfe Kleider mit eingebetteten Mikrocontrollern, um bestimmte Funktionen elektronischer Komponenten zur Verbesserung des Design-Themas zu nutzen. Für dieses Projekt möchte ich die Mustererkennungs-Engine (PME) und den Beschleunigungssensor im Intel Curie auf dem Arduino 101 nutzen. Curie ist für PME-Anwendungen konzipiert, da es neuronale Netzwerk-Fähigkeiten besitzt, aber es gab bisher wenige PME-Beispiele. Ich hoffe, dieses Beispiel inspiriert Sie, Curies PME-Fähigkeiten zu nutzen.

Das Kleid verwendet eine Seite meines Graphic Novels als Stoff. Eine Wissenschaftlerin und ihr Roboter blicken in ein Teleskop. Während die Trägerin ein Muster in die Luft zeichnet, leuchten LEDs, die in Form von Sternbildern angeordnet sind, im Nachthimmel auf dem Stoff auf.

Anleitung

Hinweis: Nach Veröffentlichung dieses Projekts habe ich den elektronischen Schaltkreis überarbeitet. Eine verbesserte Methode zur Befestigung der LEDs am Stoff und zur Erhöhung der Haltbarkeit finden Sie nach Schritt 3.

Update: Das Tragen dieses Kleides bei mehreren ganztägigen Demo-Veranstaltungen ergab eine Schätzung der Batterielebensdauer. Zum Betreiben des Kleides muss die 9V-Batterie etwa alle 3 Stunden gewechselt werden. Die Batterie wird nicht entladen, sondern fällt unter 9V, was die Mustererkennung ineffizient macht. Sie können die Batterie für andere Zwecke weiterverwenden.

Schritt 1

Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen

Ich habe einen NeoPixel-Streifen von Adafruit verwendet, ihn in Stücke geschnitten und in Form von Sternbildern angeordnet. Sie können auch andere LED-Typen wie einzelne RGB-LEDs verwenden. Kleben oder nähen Sie sie auf den Grundstoff.

Schritt 2

Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen

Legen Sie einen Zwischstoff oben drauf und zeichnen Sie die Sternbilder nach. Dieser Schritt ist optional, aber ich fand es hilfreich, mehrere Stoffschichten für mehr Stabilität zu haben. Ich habe tatsächlich einen weiteren dicken Stoff auf die Rückseite des Grundstoffs genäht. Insgesamt also drei Schichten als Basis, die die LEDs einschließen.

Schritt 3

Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen

Verlöten Sie die LEDs. Wenn Sie nähbare Einzel-LEDs verwenden, können Sie auch leitfähiges Garn zum Verbinden nutzen. In jedem Fall ist es viel Handarbeit und erfordert Geduld. Da ich vier Sternbilder (Orion, Großer Wagen, Schwan und Kassiopeia) habe, habe ich sie in vier Spuren aufgeteilt. Jede wird mit einem anderen Arduino 101 Pin verbunden.

Verbesserte Schritte

Der Stoff, den ich für die obigen Schritte verwendet habe, war zu weich, was das Biegen der LEDs und Kabel erleichterte und die Lötstellen brechen ließ. Ich habe den gesamten Prozess mit der folgenden verbesserten Methode wiederholt.

LEDs positionieren

Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen

Ich habe einen NeoPixel-Streifen von Adafruit verwendet, ihn in Stücke geschnitten und in Form von Sternbildern angeordnet. Sie können auch andere LED-Typen wie einzelne RGB-LEDs verwenden. Kleben oder nähen Sie sie auf den Grundstoff.

Filz ist ein schöner, dicker Stoff, der Struktur gibt und gut mit Heißkleber funktioniert. Ordnen Sie die LEDs in ihren jeweiligen Positionen an, damit sie beim Leuchten die Sterne in den Sternbildern widerspiegeln.

LEDs und Kabel planen

Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen

Kleben Sie die LED-Streifen an einigen Punkten auf den Filz. Kleben Sie nicht den gesamten Streifen fest, da Sie etwas Spielraum benötigen, um Schrumpfschläuche herumzuschieben, wie in der Abbildung gezeigt. Schneiden Sie Litzenkabel auf die richtigen Längen und legen Sie sie auf ihre relativen Positionen auf demselben Filzstück. Ich empfehle, die Kabel noch nicht festzukleben. Meine Abbildung ist diesbezüglich ein Fehler. Besser ist es, die Kabel mit temporärem Klebeband in Position zu halten, damit beim Verlöten der Kabel an die LEDs die Kabelpositionen angepasst werden können.

Alle Teile nähenfertig machen

Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen

Die Abbildung zeigt, dass ich vier Sternbilder (Orion, Großer Wagen, Schwan und Kassiopeia) auf vier separate Stücke vorbereitet habe. Nach dem Verlöten und Anbringen von Schrumpfschlauch um die Lötstellen können Sie alles fest auf den Filz kleben.

Testen!

Testen Sie Ihren Schaltkreis, bevor Sie fortfahren! Führen Sie einfach einen NeoPixel Strandtest für jede Spur durch.

Ich habe den Schwierigkeitsgrad als „Einfach“ eingestuft, da es wissenschaftlich nicht schwer ist, sobald man den Code versteht, aber es erfordert viel Arbeit, die Kabel auf dem Stoff zu stabilisieren.

Stellen Sie sicher, dass Ihre Arduino IDE die neueste Version ist und die Curie PME-Bibliothek enthält. Ich empfehle die Verwendung des Arduino Web Editors. Laden Sie die Bibliothek hier herunter.

Das Kleid nähen

Parallel (im übertragenen Sinne) nähen Sie das Kleid. Nachdem Sie den Schaltkreis getestet haben, nähen Sie die Grundstoffe mit den LEDs auf die Innenseite des Kleides. Die LEDs werden durch die Grafik scheinen.

Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen
Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen
Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen
Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen

Wie Sie sehen können, befindet sich der Arduino 101 in meiner Hand. Ich habe ein 3D-gedrucktes Gehäuse für den Arduino 101 und die Batterie erstellt. Lange Kabel verbinden die LEDs und das Board und sind im Ärmel versteckt.

Der folgende Code gibt Auskunft darüber, wie das Board programmiert wird. Nachdem Sie den Code geflasht haben, trainieren Sie zuerst die Neuronen, damit sie lernen, welche Muster existieren. Sehen Sie sich das Video bei ~0:30 an:

Für weitere Fotos und andere Tech-Fashion/Graphic-Novel-Designs besuchen Sie meine Website :)

Code

PME_4LED_new.ino Arduino

Dies ist ein aktualisiertes Skript vom vorherigen. Es speichert die Trainingsdaten. Derzeit ist es mit einem Initialisierungsschritt eingerichtet. Die erste LED-Serie zeigt Rot, wenn die Stromversorgung eingeschaltet ist. Den Arduino 101 flach halten mit dem USB-Port in horizontaler Richtung, während der Knopf gedrückt wird, ermöglicht die Nutzung der vorherigen Trainingsdaten. Diese LEDs wechseln dann zu Grün und dann zu Blau beim Loslassen des Knopfs, was anzeigt, dass das System bereit ist. Wird der Arduino 101 mit dem USB-Port in vertikaler Richtung gehalten, während der Knopf gedrückt wird, werden die vorherigen Trainingsdaten gelöscht. Das System kann beim Loslassen des Knopfs neu trainiert werden.

/* 
 * Dieses Beispiel demonstriert die Verwendung der Pattern Matching Engine (CuriePME) 
 * zur Klassifizierung von Beschleunigungsdatenströmen vom CurieIMU.
 * 
 * Zuerst fordert der Sketch Sie auf, einige Buchstaben in die Luft zu zeichnen 
 * (stellen Sie sich vor, Sie schreiben an eine unsichtbare Whiteboard-Tafel, 
 * wobei Ihr Board der Stift ist), und die IMU-Daten dieser Bewegungen 
 * werden als Trainingsdaten für die PME verwendet. Nach Abschluss des Trainings 
 * können Sie weiter Buchstaben zeichnen und die PME wird versuchen zu erraten, 
 * welchen Buchstaben Sie zeichnen.
 * 
 * Dieses Beispiel erfordert einen an digital Pin 4 angeschlossenen Taster
 * https://www.arduino.cc/en/Tutorial/Button
 * 
 * HINWEIS: Für beste Ergebnisse zeichnen Sie große Buchstaben, mindestens 1-2 Fuß hoch.
 * 
 * Copyright (c) 2016 Intel Corporation. Alle Rechte vorbehalten.
 * Siehe Lizenzhinweis am Ende der Datei.
 */
#include "CurieIMU.h"
#include "CuriePME.h"
#include 
#include 
#include 

#define PINM 6  // An welchem Pin sind die NeoPixels angeschlossen?
#define PINC 3
#define PINS 9
#define PINO 5

Adafruit_NeoPixel stripM = Adafruit_NeoPixel(10, PINM, NEO_GRB + NEO_KHZ800); // Der Streifen ist 10 Pixel lang. Anpassbar.
Adafruit_NeoPixel stripS = Adafruit_NeoPixel(10, PINS, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel stripC = Adafruit_NeoPixel(10, PINC, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel stripO = Adafruit_NeoPixel(10, PINO, NEO_GRB + NEO_KHZ800);

int tr = 0; // Variablen für "color-target" und "color-current" zum Glätten...
int tg = 0;
int tb = 0;
int r = 0;
int g = 0;
int b = 0;
int rawX = 0; ///// Werte vom Curie-Beschleunigungssensor
int rawY = 0;
//int rawZ = 0;
float angle = 0.0;

/* Steuert, wie oft ein Buchstabe während des Trainings gezeichnet werden muss.
 * Höher als 4, und Sie haben möglicherweise nicht genug Neuronen für alle 26 
 * Buchstaben des Alphabets. Niedriger als 4 bedeutet weniger Arbeit beim 
 * Trainieren eines Buchstabens, aber die PME könnte Schwierigkeiten haben, 
 * diesen Buchstaben zu klassifizieren.
 */
const unsigned int trainingReps = 4;

/* Erhöhen Sie auf 'A-Z', falls gewünscht -- dauert nur länger zum Trainieren */
const unsigned char trainingStart = 'A';
const unsigned char trainingEnd = 'D';

/* Der Eingangspin, der signalisiert, wann ein Buchstabe gezeichnet wird - 
 * Sie müssen sicherstellen, dass ein Taster an diesem Pin angeschlossen ist
 */
const unsigned int buttonPin = 4;

/* Abtastrate für Beschleunigungssensor */
const unsigned int sampleRateHZ = 200;

/* Anzahl Bytes, die ein Neuron speichern kann */
const unsigned int vectorNumBytes = 128;

/* Anzahl verarbeiteter Samples (1 Sample == accel x, y, z)
 * die in ein Neuron passen
 */
const unsigned int samplesPerVector = (vectorNumBytes / 3);

/* Dieser Wert wird verwendet, um ASCII-Zeichen A-Z
 * in Dezimalwerte 1-26 umzuwandeln und zurück.
 */
const unsigned int upperStart = 0x40;
const unsigned int sensorBufSize = 2048;
const int IMULow = -32768;
const int IMUHigh = 32767;
const char *filename = "NeurDataDress.dat";

void setup() {
  Serial.begin(9600);
  //while(!Serial);
  pinMode(buttonPin, INPUT);
  
  /* IMU (Inertial Measurement Unit) starten */
  CurieIMU.begin();
  
  /* PME (Pattern Matching Engine) starten */
  CuriePME.begin();
  CurieIMU.setAccelerometerRate(sampleRateHZ);
  CurieIMU.setAccelerometerRange(2);
  
  /* SPI Flash Chip initialisieren */
  if (!SerialFlash.begin(ONBOARD_FLASH_SPI_PORT, ONBOARD_FLASH_CS_PIN)) {
    Serial.println("Unable to access SPI Flash chip");
  }
  
  stripM.begin(); // NeoPixel-Streifen initialisieren
  stripS.begin();
  stripC.begin();
  stripO.begin();
  stripM.show(); // Alle Pixel auf 'aus' initialisieren
  stripS.show();
  stripC.show();
  stripO.show();
  solidM(stripM.Color(255, 0, 0), 50); // Rot für Eingabebereitschaft
}

/* Diese Funktion liest die Datei, die vom vorherigen Beispiel gespeichert wurde.
 * Die Datei enthält alle gelernten Daten, die zuvor gespeichert wurden.
 * Sobald das Netzwerk wiederhergestellt ist, kann es Muster wieder klassifizieren,
 * ohne neu trainiert werden zu müssen.
 */
void restoreNetworkKnowledge(void) {
  SerialFlashFile file;
  int32_t fileNeuronCount = 0;
  Intel_PMT::neuronData neuronData;
  
  // Datei öffnen und Testdaten schreiben
  file = SerialFlash.open(filename);
  CuriePME.beginRestoreMode();
  
  if (file) {
    // Über das Netzwerk iterieren und Daten speichern
    while(1) {
      Serial.print("Reading Neuron: ");
      uint16_t neuronFields[4];
      file.read((void*) neuronFields, 8);
      file.read((void*) neuronData.vector, 128);
      
      neuronData.context = neuronFields[0];
      neuronData.influence = neuronFields[1];
      neuronData.minInfluence = neuronFields[2];
      neuronData.category = neuronFields[3];
      
      if (neuronFields[0] == 0 || neuronFields[0] > 127) break;
      fileNeuronCount++;
      
      // Dieser Teil gibt jeden Neuron beim Wiederherstellen aus,
      // damit Sie sehen können, was passiert.
      Serial.print(fileNeuronCount);
      Serial.print("\n");
      Serial.print(neuronFields[0]);
      Serial.print("\t");
      Serial.print(neuronFields[1]);
      Serial.print("\t");
      Serial.print(neuronFields[2]);
      Serial.print("\t");
      Serial.print(neuronFields[3]);
      Serial.print("\t");
      Serial.print(neuronData.vector[0]);
      Serial.print("\t");
      Serial.print(neuronData.vector[1]);
      Serial.print("\t");
      Serial.print(neuronData.vector[2]);
      Serial.print("\n");
      
      CuriePME.iterateNeuronsToRestore(neuronData);
    }
  }
  CuriePME.endRestoreMode();
  Serial.print("Knowledge Set Restored. \n");
}

boolean longPress = false;
int startTime = 0;
int lastOrientation = -1; // vorherige Orientierung (zum Vergleich)
int lastReading = -1;
boolean lastPress = false;

void loop() {
  int orientation = -1; // Orientierung des Boards
  String orientationString; // String für Beschreibung der Orientierung
  
  // Orientierungen des Boards:
  // 0: flach, Prozessor nach oben
  // 1: flach, Prozessor nach unten
  // 2: Querformat, analoge Pins unten
  // 3: Querformat, analoge Pins oben
  // 4: Hochformat, USB-Anschluss oben
  // 5: Hochformat, USB-Anschluss unten
  
  // Beschleunigungssensor lesen:
  int x = CurieIMU.readAccelerometer(X_AXIS);
  int y = CurieIMU.readAccelerometer(Y_AXIS);
  int z = CurieIMU.readAccelerometer(Z_AXIS);
  
  // Absolute Werte berechnen, um den größten zu bestimmen
  int absX = abs(x);
  int absY = abs(y);
  int absZ = abs(z);
  
  if ((absZ > absX) && (absZ > absY)) {
    // Orientierung basierend auf Z
    if (z > 0) {
      orientationString = "up";
      orientation = 0;
    } else {
      orientationString = "down";
      orientation = 1;
    }
  } else if ((absY > absX) && (absY > absZ)) {
    // Orientierung basierend auf Y
    if (y > 0) {
      orientationString = "digitale Pins oben";
      orientation = 2;
    } else {
      orientationString = "analoge Pins oben";
      orientation = 3;
    }
  } else {
    // Orientierung basierend auf X
    if (x < 0) {
      orientationString = "Anschluss oben";
      orientation = 4;
    } else {
      orientationString = "Anschluss unten";
      orientation = 5;
    }
  }
  
  // Wenn sich die Orientierung geändert hat, Beschreibung ausgeben:
  if (orientation != lastOrientation) {
    Serial.println(orientationString);
    lastOrientation = orientation;
  }
  
  int reading = digitalRead(buttonPin);
  if (lastReading != reading) {
    Serial.print("buttonPin=");
    Serial.println(reading);
    lastReading = reading;
  }
  
  if (reading == HIGH) {
    if (startTime == 0) {
      startTime = millis();
    } else if ((millis() - startTime) > 2000) {
      longPress = true;
      if (!lastPress) {
        Serial.println("longPress");
        // Grün für langes Drücken
        solidM(stripM.Color(0, 255, 0), 50); // Grün
        lastPress = true;
      }
    }
  }
  
  if (reading == LOW && longPress) {
    blackout(5);
    Serial.print("orientation=");
    Serial.print(orientation);
    Serial.print(" SerialFlash.exists(filename)=");
    Serial.println(SerialFlash.exists(filename));
    
    if (orientation != 4 && SerialFlash.exists(filename)) {
      restoreNetworkKnowledge();
      Serial.print("Training restored.");
    } else {
      trainLetters();
      blackout(5);
      Serial.print("Training complete.");
    }
    
    Serial.println(" Now, draw some letters (remember to ");
    Serial.println("hold the button) and see if the PME can classify them.");
    solidM(stripM.Color(0, 0, 255), 500); // Blau für Matching-Bereitschaft
    blackout(5); // Ausschalten für Matching
    
    while (true) {
      match();
    }
  }
}

void solidM(uint32_t c, uint8_t wait) {
  for (uint16_t i = 0; i < stripM.numPixels(); i++) {
    stripM.setPixelColor(i, c); // Jeden Pixel ausschalten
  }
  stripM.show();
  delay(wait);
}

void blackout(uint8_t wait) {
  for (uint16_t i = 0; i < stripM.numPixels(); i++) {
    stripM.setPixelColor(i, 0); // Jeden Pixel ausschalten
  }
  for (uint16_t i = 0; i < stripS.numPixels(); i++) {
    stripS.setPixelColor(i, 0);
  }
  for (uint16_t i = 0; i < stripC.numPixels(); i++) {
    stripC.setPixelColor(i, 0);
  }
  for (uint16_t i = 0; i < stripO.numPixels(); i++) {
    stripO.setPixelColor(i, 0);
  }
  stripM.show();
  stripS.show();
  stripC.show();
  stripO.show();
  delay(wait);
}

void match() {
  byte vector[vectorNumBytes];
  unsigned int category;
  char letter;
  char pattern;
  
  /* IMU-Daten aufzeichnen, während Taster gedrückt wird,
   * und in einen geeigneten Vektor umwandeln
   */
  readVectorFromIMU(vector);
  
  /* PME verwenden, um den Vektor zu klassifizieren, d.h. eine Kategorie
   * von 1-26 zurückgeben, die einen Buchstaben von A-Z repräsentiert
   */
  category = CuriePME.classify(vector, vectorNumBytes);
  
  if (category == CuriePME.noMatch) {
    Serial.println("Don't recognise that one-- try again.");
    theaterChaseM(stripM.Color(127, 127, 127), 50);
    theaterChaseS(stripS.Color(127, 127, 127), 50);
    theaterChaseC(stripC.Color(127, 127, 127), 50);
    theaterChaseO(stripO.Color(127, 127, 127), 50); // Weiß
    stripM.show();
    stripS.show();
    stripC.show();
    stripO.show();
    blackout(1);
  } else {
    letter = category + upperStart;
    pattern = letter;
    
    if (pattern == 'A') {
      colorWipeM(stripM.Color(0, 255, 0), 50); // Grün
      theaterChaseM(stripM.Color(127, 127, 127), 50); // Weiß
      stripM.show();
    } else if (pattern == 'B') {
      colorWipeS(stripS.Color(255, 0, 0), 50); // Rot
      theaterChaseS(stripS.Color(127, 127, 127), 50); // Weiß
      stripS.show();
    } else if (pattern == 'C') {
      colorWipeC(stripC.Color(0, 0, 255), 50); // Blau
      theaterChaseC(stripC.Color(127, 127, 127), 50); // Weiß
      stripC.show();
    } else if (pattern == 'D') {
      colorWipeO(stripO.Color(255, 0, 255), 50); // Magenta
      theaterChaseO(stripO.Color(127, 127, 127), 50); // Weiß
      stripO.show();
    }
    Serial.println(letter);
  }
}

/* Einfacher "gleitender Durchschnitt"-Filter, entfernt geringes Rauschen und 
 * andere kleine Anomalien, mit dem Effekt, den Datenstrom zu glätten.
 */
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;
}

/* Wir müssen den Strom roher Beschleunigungsdaten in 128 Bytes komprimieren,
 * damit er in ein Neuron passt, während wir so viel wie möglich vom 
 * ursprünglichen Muster beibehalten. Angenommen, es gibt typischerweise 
 * 1-2 Sekunden Beschleunigungsdaten bei 200Hz, müssen wir über 90% 
 * davon wegwerfen, um dieses Ziel zu erreichen!
 * 
 * Dies geschieht auf 2 Arten:
 * 
 * 1. Jedes Sample besteht aus 3 vorzeichenbehafteten 16-Bit-Werten 
 *    (je einer für X, Y und Z). Jeden 16-Bit-Wert auf einen Bereich von 
 *    0-255 abbilden und in ein Byte packen, wodurch die Sample-Größe 
 *    halbiert wird.
 * 
 * 2. Unterabtasten. Wenn wir bei 200Hz abtasten und der Taster 1,2 
 *    Sekunden gedrückt wird, haben wir ~240 Samples. Da wir wissen, 
 *    dass jedes Sample, einmal komprimiert, 3 der 128 Bytes unseres 
 *    Neurons belegt (siehe #1), passen nur 42 dieser 240 Samples in 
 *    ein einzelnes Neuron (128 / 3 = 42,666). Wenn wir also (zum Beispiel) 
 *    jeden 5. Sample nehmen, bis wir 42 haben, sollten wir den größten 
 *    Teil des Sample-Fensters abdecken und eine Annäherung an das 
 *    ursprüngliche Muster haben.
 */
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);
  
  /* Sample-Fenster zentrieren */
  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;
  
  /* Warten, bis Taster gedrückt wird */
  while (digitalRead(buttonPin) == LOW);
  
  /* Solange Taster gedrückt wird... */
  while (digitalRead(buttonPin) == HIGH) {
    if (CurieIMU.dataReady()) {
      CurieIMU.readAccelerometer(raw[0], raw[1], raw[2]);
      
      /* Rohwerte auf 0-255 abbilden */
      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;
      
      /* Wenn nicht genug Platz in den Puffern für nächsten 
       * Lesevorgang ist, sind wir fertig
       */
      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 (letter == 'A') {
      colorWipeM(stripM.Color(0, 255, 0), 50); // Grün
      theaterChaseM(stripM.Color(127, 127, 127), 50); // Weiß
      stripM.show();
    } else if (letter == 'B') {
      colorWipeS(stripS.Color(255, 0, 0), 50); // Rot
      theaterChaseS(stripS.Color(127, 127, 127), 50); // Weiß
      stripS.show();
    } else if (letter == 'C') {
      colorWipeC(stripC.Color(0, 0, 255), 50); // Blau
      theaterChaseC(stripC.Color(127, 127, 127), 50); // Weiß
      stripC.show();
    } else if (letter == 'D') {
      colorWipeO(stripO.Color(255, 0, 255), 50); // Magenta
      theaterChaseO(stripO.Color(127, 127, 127), 50); // Weiß
      stripO.show();
    }
    
    if (i) Serial.println("And again...");
    readVectorFromIMU(vector);
    CuriePME.learn(vector, vectorNumBytes, letter - upperStart);
    Serial.println("Got it!");
    delay(1000);
    ++i;
  }
}

bool create_if_not_exists(const char *filename, uint32_t fileSize) {
  if (!SerialFlash.exists(filename)) {
    Serial.println("Creating file " + String(filename));
    return SerialFlash.createErasable(filename, fileSize);
  }
  Serial.println("File " + String(filename) + " already exists");
  return true;
}

void saveNetworkKnowledge(void) {
  SerialFlashFile file;
  Intel_PMT::neuronData neuronData;
  uint32_t fileSize = 128 * sizeof(neuronData);
  
  Serial.print("File Size to save is = ");
  Serial.print(fileSize);
  Serial.print("\n");
  
  create_if_not_exists(filename, fileSize);
  
  // Datei öffnen und Testdaten schreiben
  file = SerialFlash.open(filename);
  file.erase();
  CuriePME.beginSaveMode();
  
  if (file) {
    // Über das Netzwerk iterieren und Daten speichern
    while (uint16_t nCount = CuriePME.iterateNeuronsToSave(neuronData)) {
      if (nCount == 0x7FFF) break;
      
      Serial.print("Saving Neuron: ");
      Serial.print(nCount);
      Serial.print("\n");
      
      uint16_t neuronFields[4];
      neuronFields[0] = neuronData.context;
      neuronFields[1] = neuronData.influence;
      neuronFields[2] = neuronData.minInfluence;
      neuronFields[3] = neuronData.category;
      
      file.write((void*) neuronFields, 8);
      file.write((void*) neuronData.vector, 128);
    }
  }
  CuriePME.endSaveMode();
  Serial.print("Knowledge Set Saved. \n");
}

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);
  }
  
  // SPI Flash Chip initialisieren
  if (!SerialFlash.begin(ONBOARD_FLASH_SPI_PORT, ONBOARD_FLASH_CS_PIN)) {
    Serial.println("Unable to access SPI Flash chip");
  }
  saveNetworkKnowledge();
}

/* Spezielle Lichtfunktionen aus Adafruit Strandtest Beispielcode
 * Theater Chase Lichter aus Adafruit Strandtest Beispielcode. 
 * Nimmt den aktuellen RGB-Wert und macht einen "Theater Chase" Effekt damit.
 */
/* Theatre-style crawling lights. */
void theaterChasePIN(uint8_t p, uint32_t c, uint8_t wait) {
  Adafruit_NeoPixel strip = Adafruit_NeoPixel(10, p, NEO_GRB + NEO_KHZ800);
  for (int j = 0; j < 10; j++) { // 10 Zyklen des Verfolgens
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, c); // Jeden dritten Pixel einschalten
      }
      strip.show();
      delay(wait);
      for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, 0); // Jeden dritten Pixel ausschalten
      }
    }
  }
}

/* Punkte nacheinander mit einer Farbe füllen */
void colorWipePIN(uint8_t p, uint32_t c, uint8_t wait) {
  Adafruit_NeoPixel strip = Adafruit_NeoPixel(10, p, NEO_GRB + NEO_KHZ800);
  for (uint16_t i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

void theaterChaseM(uint32_t c, uint8_t wait) {
  for (int j = 0; j < 10; j++) {
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < stripM.numPixels(); i = i + 3) {
        stripM.setPixelColor(i + q, c);
      }
      stripM.show();
      delay(wait);
      for (uint16_t i = 0; i < stripM.numPixels(); i = i + 3) {
        stripM.setPixelColor(i + q, 0);
      }
    }
  }
}

void theaterChaseS(uint32_t c, uint8_t wait) {
  for (int j = 0; j < 10; j++) {
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < stripS.numPixels(); i = i + 3) {
        stripS.setPixelColor(i + q, c);
      }
      stripS.show();
      delay(wait);
      for (uint16_t i = 0; i < stripS.numPixels(); i = i + 3) {
        stripS.setPixelColor(i + q, 0);
      }
    }
  }
}

void theaterChaseC(uint32_t c, uint8_t wait) {
  for (int j = 0; j < 10; j++) {
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < stripC.numPixels(); i = i + 3) {
        stripC.setPixelColor(i + q, c);
      }
      stripC.show();
      delay(wait);
      for (uint16_t i = 0; i < stripC.numPixels(); i = i + 3) {
        stripC.setPixelColor(i + q, 0);
      }
    }
  }
}

void theaterChaseO(uint32_t c, uint8_t wait) {
  for (int j = 0; j < 10; j++) {
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < stripO.numPixels(); i = i + 3) {
        stripO.setPixelColor(i + q, c);
      }
      stripO.show();
      delay(wait);
      for (uint16_t i = 0; i < stripO.numPixels(); i = i + 3) {
        stripO.setPixelColor(i + q, 0);
      }
    }
  }
}

void colorWipeM(uint32_t c, uint8_t wait) {
  for (uint16_t i = 0; i < stripM.numPixels(); i++) {
    stripM.setPixelColor(i, c);
    stripM.show();
    delay(wait);
  }
}

void colorWipeS(uint32_t c, uint8_t wait) {
  for (uint16_t i = 0; i < stripS.numPixels(); i++) {
    stripS.setPixelColor(i, c);
    stripS.show();
    delay(wait);
  }
}

void colorWipeC(uint32_t c, uint8_t wait) {
  for (uint16_t i = 0; i < stripC.numPixels(); i++) {
    stripC.setPixelColor(i, c);
    stripC.show();
    delay(wait);
  }
}

void colorWipeO(uint32_t c, uint8_t wait) {
  for (uint16_t i = 0; i < stripO.numPixels(); i++) {
    stripO.setPixelColor(i, c);
    stripO.show();
    delay(wait);
  }
}

/*
 * Diese Bibliothek ist freie Software; Sie können sie weiterverbreiten und/oder
 * unter den Bedingungen der GNU Lesser General Public License modifizieren,
 * wie von der Free Software Foundation veröffentlicht; entweder Version 2.1
 * der Lizenz oder (nach Ihrer Wahl) jede spätere Version.
 * 
 * Diese Bibliothek wird in der Hoffnung verteilt, dass sie nützlich sein wird,
 * aber OHNE JEDE GEWÄHRLEISTUNG; ohne sogar die implizite Gewährleistung der
 * MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. Siehe die GNU
 * Lesser General Public License für weitere Details.
 * 
 * Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit
 * dieser Bibliothek erhalten haben; wenn nicht, schreiben Sie an die Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
PME_LED Arduino

Dies verwendet die Curie PME zur Steuerung von LEDs. Es ist im Grunde eine Kombination aus dem "Draw in the Air" PME-Code und dem Adafruit NeoPixel Beispielcode. Dieser Code ist nicht genau das, was ich verwendet habe (was etwas komplexer ist), da ich bezweifle, dass Sie exakt die gleiche Sternbild-Anordnung erstellen. Vielmehr ist dies ein generischer Code, den Sie an Ihre Bedürfnisse anpassen können, z.B. können Sie ändern, wie die Pins für verschiedene LED-Streifen verteilt werden. Ich werde ihn möglicherweise später mit einem verbesserten Code aktualisieren.

/* 
 * Dieses Beispiel demonstriert die Verwendung der Pattern Matching Engine (CuriePME) 
 * zur Klassifizierung von Beschleunigungsdatenströmen vom CurieIMU. Der Code ist 
 * eine Modifikation des "Draw in the Air" Beispiels:
 * https://github.com/01org/Intel-Pattern-Matching-Technology
 * 
 * Zuerst fordert der Sketch Sie auf, Muster in die Luft zu zeichnen 
 * (stellen Sie sich vor, Sie schreiben an eine unsichtbare Whiteboard-Tafel, 
 * wobei Ihr Board der Stift ist), und die IMU-Daten dieser Bewegungen 
 * werden als Trainingsdaten für die PME verwendet. Nach Abschluss des Trainings 
 * können Sie weiter Buchstaben zeichnen und die PME wird versuchen zu erraten, 
 * welchen Buchstaben Sie zeichnen.
 * 
 * Dieses Beispiel erfordert einen an digital Pin 4 angeschlossenen Taster
 * https://www.arduino.cc/en/Tutorial/Button
 * 
 * HINWEIS: Für beste Ergebnisse zeichnen Sie große Buchstaben, mindestens 1-2 Fuß hoch.
 * 
 * Copyright (c) 2016 Intel Corporation. Alle Rechte vorbehalten.
 * Siehe Lizenzhinweis am Ende der Datei.
 */
#include "CurieIMU.h"
#include "CuriePME.h"
#include 

#define PIN 6  // An welchem Pin sind die NeoPixels angeschlossen?
Adafruit_NeoPixel strip = Adafruit_NeoPixel(54, PIN, NEO_GRB + NEO_KHZ800); // Der Streifen ist 54 Pixel lang. Anpassbar.

int tr = 0; // Variablen für "color-target" und "color-current" zum Glätten...
int tg = 0;
int tb = 0;
int r = 0;
int g = 0;
int b = 0;
int rawX = 0; ///// Werte vom Curie-Beschleunigungssensor
int rawY = 0;
//int rawZ = 0;
float angle = 0.0;

/* Steuert, wie oft ein Buchstabe während des Trainings gezeichnet werden muss.
 * Höher als 4, und Sie haben möglicherweise nicht genug Neuronen für alle 26 
 * Buchstaben des Alphabets. Niedriger als 4 bedeutet weniger Arbeit beim 
 * Trainieren eines Buchstabens, aber die PME könnte Schwierigkeiten haben, 
 * diesen Buchstaben zu klassifizieren.
 */
const unsigned int trainingReps = 4;

/* Erhöhen Sie auf 'A-Z', falls gewünscht -- dauert nur länger zum Trainieren */
const unsigned char trainingStart = 'A';
const unsigned char trainingEnd = 'D';

/* Der Eingangspin, der signalisiert, wann ein Buchstabe gezeichnet wird - 
 * Sie müssen sicherstellen, dass ein Taster an diesem Pin angeschlossen ist
 */
const unsigned int buttonPin = 4;

/* Abtastrate für Beschleunigungssensor */
const unsigned int sampleRateHZ = 200;

/* Anzahl Bytes, die ein Neuron speichern kann */
const unsigned int vectorNumBytes = 128;

/* Anzahl verarbeiteter Samples (1 Sample == accel x, y, z)
 * die in ein Neuron passen
 */
const unsigned int samplesPerVector = (vectorNumBytes / 3);

/* Dieser Wert wird verwendet, um ASCII-Zeichen A-Z
 * in Dezimalwerte 1-26 umzuwandeln und zurück.
 */
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);
  
  /* IMU (Inertial Measurement Unit) starten */
  CurieIMU.begin();
  
  /* PME (Pattern Matching Engine) starten */
  CuriePME.begin();
  CurieIMU.setAccelerometerRate(sampleRateHZ);
  CurieIMU.setAccelerometerRange(2);
  
  trainLetters();
  
  strip.begin(); // NeoPixel-Streifen initialisieren
  strip.show(); // Alle Pixel auf 'aus' initialisieren
}

void loop() {
  Serial.println("Training complete. Now, draw some letters (remember to ");
  Serial.println("hold the button) and see if the PME can classify them.");
  
  byte vector[vectorNumBytes];
  unsigned int category;
  char letter;
  char pattern;
  
  /* IMU-Daten aufzeichnen, während Taster gedrückt wird,
   * und in einen geeigneten Vektor umwandeln
   */
  readVectorFromIMU(vector);
  
  /* PME verwenden, um den Vektor zu klassifizieren, d.h. eine Kategorie
   * von 1-26 zurückgeben, die einen Buchstaben von A-Z repräsentiert
   */
  category = CuriePME.classify(vector, vectorNumBytes);
  
  if (category == CuriePME.noMatch) {
    Serial.println("Don't recognise that one-- try again.");
    theaterChase(strip.Color(127, 127, 127), 50); // Weiß
    strip.show();
  } else {
    letter = category + upperStart;
    pattern = letter;
    
    if (pattern == 'A') {
      colorWipe(strip.Color(0, 255, 0), 50); // Grün
      theaterChase(strip.Color(127, 127, 127), 50); // Weiß
      strip.show();
    } else if (pattern == 'B') {
      colorWipe(strip.Color(255, 0, 0), 50); // Rot
      theaterChase(strip.Color(127, 127, 127), 50); // Weiß
      strip.show();
    } else if (pattern == 'C') {
      colorWipe(strip.Color(0, 0, 255), 50); // Blau
      theaterChase(strip.Color(127, 127, 127), 50); // Weiß
      strip.show();
    } else if (pattern == 'D') {
      colorWipe(strip.Color(255, 0, 255), 50); // Magenta
      theaterChase(strip.Color(127, 127, 127), 50); // Weiß
      strip.show();
    }
    Serial.println(letter);
  }
}

/* Einfacher "gleitender Durchschnitt"-Filter... */
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;
}

/* Komprimierung der rohen Beschleunigungsdaten... */
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);
  
  /* Sample-Fenster zentrieren */
  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;
  
  /* Warten, bis Taster gedrückt wird */
  while (digitalRead(buttonPin) == LOW);
  
  /* Solange Taster gedrückt wird... */
  while (digitalRead(buttonPin) == HIGH) {
    if (CurieIMU.dataReady()) {
      CurieIMU.readAccelerometer(raw[0], raw[1], raw[2]);
      
      /* Rohwerte auf 0-255 abbilden */
      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;
      
      /* Wenn nicht genug Platz in den Puffern für nächsten 
       * Lesevorgang ist, sind wir fertig
       */
      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);
  }
}

/////////////// Spezielle Lichtfunktionen aus Adafruit Strandtest Beispielcode
// Rainbow! Hinweis - diese Funktion blockiert neue Positionseingaben, bis sie fertig ist.
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;
  for (j = 0; j < 256 * 5; j++) { // 5 Zyklen aller Farben am Rad
    for (i = 0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Wert 0 bis 255 eingeben, um einen Farbwert zu erhalten. Wird für Regenbogeneffekt oben verwendet.
// Die Farben sind ein Übergang r - g - b - zurück zu r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if (WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

// Theater Chase Lichter aus Adafruit Strandtest Beispielcode.
// Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j = 0; j < 10; j++) { // 10 Zyklen des Verfolgens
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, c); // Jeden dritten Pixel einschalten
      }
      strip.show();
      delay(wait);
      for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, 0); // Jeden dritten Pixel ausschalten
      }
    }
  }
}

// Punkte nacheinander mit einer Farbe füllen
void colorWipe(uint32_t c, uint8_t wait) {
  for (uint16_t i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

// Theatre-style crawling lights mit Regenbogeneffekt
void theaterChaseRainbow(uint8_t wait) {
  for (int j = 0; j < 256; j++) { // Alle 256 Farben im Rad durchlaufen
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, Wheel((i + j) % 255)); // Jeden dritten Pixel einschalten
      }
      strip.show();
      delay(wait);
      for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, 0); // Jeden dritten Pixel ausschalten
      }
    }
  }
}

/*
 * GNU Lesser General Public License...
 */

Schaltpläne

Es geht lediglich darum, die LEDs mit den Arduino 101 Pins und einen Taster an Pin 4 anzuschließen (wie im Code beschrieben: Arduino Button Tutorial). Der Schaltplan ähnelt diesem Fritzing-Diagramm von Adafruit: Adafruit NeoPixel Uberguide.

Ich habe die 9V-Batterie direkt in die Hohlsteckerbuchse gesteckt.

Arduino 101: Mustererkennendes LED-Kleid mit Intel Curie bauen

Herstellungsprozess

  1. Die Entwicklung und Produktion massengefertigter Kleidungsschnitte
  2. Jumper Wires eliminieren: Wie BreadShield Arduino direkt mit Breadboards verbindet
  3. DHT11 Temperatur- & Feuchtesensor mit LED-Anzeige & Piezo-Summer
  4. Bluetooth-gesteuerter Arduino-Spybot bauen – Überwachungsroboter mit IP-Kamera
  5. FlickMote: Gestensteuerung per Ultraschall & IR für Smart Home (Arduino 101)
  6. DIY Arduino TV-B-Gone: Universelle TV-Aus-Fernbedienung
  7. Einen eigenen LED-Master-Clock mit Alarm bauen – aus 12 Metern Entfernung ablesbar
  8. MIDI-Keyboard zum smarten Klavierlehrer umbauen – Arduino, LEDs & MuseScore
  9. LEDs mit Alexa über Raspberry Pi steuern – Einfache Schritt-für-Schritt-Anleitung
  10. Find Me: Smarter Gegenstands-Finder mit Arduino und Bluetooth