工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用

必要な部品と材料

NeoPixelストリップ(Adafruit製) ×1 個別RGB LEDで代替可能
Arduino 101 ×1
SparkFun プッシュボタンスイッチ 12mm ×1
抵抗 10kΩ ×1
ジャンパーワイヤー(汎用) ×1 肩から手までLEDを配線するため長めのワイヤーが必要
9V電池(汎用) ×1
9V→バレルジャック変換コネクタ ×1
Adafruit Flora RGB Neopixel LED 4個パック ×1 NeoPixelストリップの代替として使用可能
縫い付け可能導電糸 ×1 一部配線の代替に使用可能

必要な工具と機材

使用するアプリとオンラインサービス

プロジェクト概要

Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用

私はエンジニア兼アーティストとして、科学と芸術を融合したプロジェクトを楽しんでいます。マイクロコントローラーを埋め込んだドレスデザインを制作し、電子部品の機能でデザインテーマを強化してきました。今回は、Arduino 101搭載のIntel Curie内蔵パターンマッチングエンジン(PME)と加速度センサーを活用します。Curieはニューラルネットワーク機能を持つPME向けに設計されていますが、PMEの活用例はまだ少ないのが現状です。この例がCurieのPME機能を引き出すきっかけになれば幸いです。

このドレスには私のグラフィックノベルの1ページを生地として使用しています。科学者とロボットが望遠鏡を覗いているデザインで、着用者が空中にパターンを描くと、星座の形に配置されたLED群が夜空のように光ります。

制作手順

注: 公開後に電子回路部分を改良しました。LEDの布への取り付け方と耐久性向上の改良版はステップ3以降に記載しています。

更新: 数回の終日デモイベントでの着用によりバッテリー持続時間の目安が得られました。このドレスの駆動には9V電池が約3時間ごとに交換必要です。電池が完全に消耗するわけではなく、9Vを下回るとパターンマッチングに非効率になるためです。使用済み電池は他用途に流用可能です。

ステップ1: LED配置

Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用

AdafruitのNeoPixelストリップをカットし、星座の形(オリオン座、北斗七星、白鳥座、カシオペア座)に配置してベース生地に接着または縫い付けます。個別RGB LEDの使用も可能です。

ステップ2: インターフェーシング生地の追加

Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用

上からインターフェーシング生地を重ね、星座の輪郭を縫い付けます。このステップは省略可能ですが、複数層の生地で構造を強化できると感じました。実際にはベース生地の裏にさらに厚手の生地を縫い付け、計3層構造でLEDをサンドイッチしています。

ステップ3: LEDのはんだ付け

Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用

LEDをはんだ付けします。縫い付けタイプの個別LEDなら導電糸で接続も可能です。いずれにしても根気のいる手作業です。4つの星座それぞれを独立したトレース(配線経路)に分け、Arduino 101の異なるピンに接続します。

改良版手順

最初に使用した生地が柔らかすぎ、LEDと配線が曲がってはんだ接点が破損しやすかったため、以下の改良版で全工程をやり直しました。

LEDの位置決め
Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用

フェルトは厚みがあり構造が出せ、グルーガンとの相性も良い生地です。LEDを各星座の星の位置に合わせて配置し、点灯時に星座が浮かび上がるようにします。

LEDと配線の計画
Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用

LEDストリップをフェルトの数点で接着します。ストリップ全面に接着剤を塗ると、後でヒートシュリンクチューブを通せなくなるため避けてください。撚り線を適切な長さにカットし、フェルト上の相対位置に配置します。配線をホットグルーで固定するのはまだ早いです(図ではすでに接着してしまっていますが失敗例です)。はんだ付け時に位置調整できるよう、一時的にテープで固定するのが賢明です。

全パーツを縫い付け準備
Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用

4つの星座(オリオン座、北斗七星、白鳥座、カシオペア座)をそれぞれ別パーツに準備しました。はんだ付けとヒートシュリンク処理後、すべてをフェルトにしっかり接着します。

テスト!

先に進む前に回路をテストしましょう! 各トレースでNeoPixel Strandtestを実行するだけです。

難易度を「簡単」としたのは、コードを理解すれば科学的には難しくないからですが、布上でケーブルを安定させるのに大変な手間がかかります。

Arduino IDEを最新版にし、Curie PMEライブラリを導入してください。Arduino Web Editorの使用を推奨します。ライブラリはこちらから。

ドレスの縫製

並行してドレスを縫製します。回路テスト後、LED付きベース生地をドレスの内側に縫い付けます。LEDの光がグラフィックを透過して輝きます。

Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用
Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用

Arduino 101は手に持つ形にし、3DプリントしたケースにArduino 101と電池を収納。長い配線でLEDと基板を接続し、袖の中に隠しています。

以下のコードでボードのプログラミング方法を確認できます。コード書き込み後、まずニューロンを学習させてパターンを覚えさせます。動画の0:30頃を参照してください:デモ動画

その他の写真やテックファッション/布絵デザインについては私のウェブサイトをご覧ください。

コード

PME_4LED_new.ino Arduino

前バージョンから更新されたスクリプト。学習データを保存可能。初期化ステップを搭載:起動時は赤色LED、Arduino 101を水平に持ち(USBポートが水平)、ボタンを押すと前回の学習データを使用。緑→青に変化し準備完了を示す。垂直に持って(USBポートが垂直)ボタンを押すと学習データを消去し、再学習可能。

/* パターンマッチングエンジン(CuriePME)を使用し、
 * CurieIMUからの加速度データストリームを分類する例
 * 
 * まず空中でいくつかの文字を描いてもらい(見えないホワイトボードに
 * ボードをペン代わりに書くイメージ)、そのIMUデータをPMEの学習データとする。
 * 学習完了後、文字を描き続けるとPMEがどの文字か推測する。
 * 
 * デジタルピン4にボタン接続が必要
 * https://www.arduino.cc/en/Tutorial/Button
 * 
 * 注意: 最良の結果を得るには、1-2フィート以上の大きな文字を描くこと。
 * Copyright (c) 2016 Intel Corporation. All rights reserved.
 */
#include "CurieIMU.h"
#include "CuriePME.h"
#include 
#include 
#include 

#define PINM 6  // NeoPixel接続ピン
#define PINC 3
#define PINS 9
#define PINO 5

Adafruit_NeoPixel stripM = Adafruit_NeoPixel(10, PINM, NEO_GRB + NEO_KHZ800);
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, tg = 0, tb = 0;
int r = 0, g = 0, b = 0;
int rawX = 0, rawY = 0;
float angle = 0.0;

const unsigned int trainingReps = 4;  // 文字ごとの学習回数
const unsigned char trainingStart = 'A';
const unsigned char trainingEnd = 'D';
const unsigned int buttonPin = 4;
const unsigned int sampleRateHZ = 200;
const unsigned int vectorNumBytes = 128;
const unsigned int samplesPerVector = (vectorNumBytes / 3);
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);
  pinMode(buttonPin, INPUT);
  CurieIMU.begin();
  CuriePME.begin();
  CurieIMU.setAccelerometerRate(sampleRateHZ);
  CurieIMU.setAccelerometerRange(2);
  
  if (!SerialFlash.begin(ONBOARD_FLASH_SPI_PORT, ONBOARD_FLASH_CS_PIN)) {
    Serial.println("SPIフラッシュチップにアクセスできません");
  }
  
  stripM.begin(); stripS.begin(); stripC.begin(); stripO.begin();
  stripM.show(); stripS.show(); stripC.show(); stripO.show();
  solidM(stripM.Color(255, 0, 0), 50);  // 赤:入力待機
}

// 以前の学習データをファイルから復元
void restoreNetworkKnowledge() {
  SerialFlashFile file;
  int32_t fileNeuronCount = 0;
  Intel_PMT::neuronData neuronData;
  
  file = SerialFlash.open(filename);
  CuriePME.beginRestoreMode();
  
  if (file) {
    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++;
      
      // 復元過程を表示
      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;
int lastReading = -1;
boolean lastPress = false;

void loop() {
  int orientation = -1;
  String orientationString;
  
  // 加速度読み取り
  int x = CurieIMU.readAccelerometer(X_AXIS);
  int y = CurieIMU.readAccelerometer(Y_AXIS);
  int z = CurieIMU.readAccelerometer(Z_AXIS);
  
  int absX = abs(x), absY = abs(y), absZ = abs(z);
  
  // ボードの向き判定
  if ((absZ > absX) && (absZ > absY)) {
    if (z > 0) { orientationString = "up"; orientation = 0; }
    else { orientationString = "down"; orientation = 1; }
  } else if ((absY > absX) && (absY > absZ)) {
    if (y > 0) { orientationString = "digital pins up"; orientation = 2; }
    else { orientationString = "analog pins up"; orientation = 3; }
  } else {
    if (x < 0) { orientationString = "connector up"; orientation = 4; }
    else { orientationString = "connector down"; orientation = 5; }
  }
  
  if (orientation != lastOrientation) {
    Serial.println(orientationString);
    lastOrientation = orientation;
  }
  
  int reading = digitalRead(buttonPin);
  if (lastReading != reading) {
    Serial.print("buttonPin="); Serial.println(reading);
    lastReading = reading;
  }
  
  // 長押し検出(2秒以上)
  if (reading == HIGH) {
    if (startTime == 0) startTime = millis();
    else if ((millis() - startTime) > 2000) {
      longPress = true;
      if (!lastPress) {
        Serial.println("longPress");
        solidM(stripM.Color(0, 255, 0), 50);  // 緑:長押し検出
        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);  // 青:マッチング準備完了
    blackout(5);
    
    while (true) { match(); }
  }
}

// 以下、LED制御、データ処理、学習、保存などの関数が続く...
// (全文はGitHub参照)
PME_LED Arduino

Curie PMEでLEDを制御する汎用コード。「空中で描画」PMEコードとAdafruit NeoPixelサンプルを組み合わせたもの。実際に使用したコードとは異なります(私の星座配置に特化しすぎているため)。ピン配分などをカスタマイズしてご利用ください。改良版を後日追加予定。

/* パターンマッチングエンジン(CuriePME)で
 * CurieIMUの加速度データストリームを分類する例
 * Draw in the Air例の改変版
 * https://github.com/01org/Intel-Pattern-Matching-Technology
 * 
 * デジタルピン4にボタン接続必要
 * 注意: 1-2フィート以上の大きな文字を描くこと
 * Copyright (c) 2016 Intel Corporation.
 */
#include "CurieIMU.h"
#include "CuriePME.h"
#include 

#define PIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(54, PIN, NEO_GRB + NEO_KHZ800);

// 以下、PME_4LED_new.inoと同様のロジックで
// 単一ストリップ版として簡略化されたコード
// (全文はGitHub参照)

回路図

LEDをArduino 101ピンに接続し、ボタンをピン4に接続するだけです(コード参照: Arduino Buttonチュートリアル)。回路図はAdafruitのFritzing図と類似:NeoPixel基本接続

9V電池をバレルジャックに直接接続しています。

Arduino 101で作るパターンマッチングLEDドレス:Intel Curie活用

製造プロセス

  1. 大量生産洋裁パターンの進化と製造
  2. DHT11温湿度センサーで作る温度アラート – LEDとピエゾブザーで視覚/聴覚通知
  3. Bluetooth操作のArduinoスパイボットを自作する
  4. FlickMote:ジェスチャー操作のIRリモコンでスマートホームを制御
  5. 自作Arduino TV-B-Gone:全テレビ対応の万能リモコン
  6. 12メートル先から見える大型LEDマスタークロック(アラーム機能付き)を自作しよう
  7. MIDIキーボードをArduino・LED・MuseScoreでスマートピアノチューターに変身
  8. ラズベリーパイとNode-REDでAlexaからLEDを制御する - 簡単ステップバイステップガイド
  9. ArduinoとBluetoothで作るスマート紛失防止タグ「Find Me」
  10. Arduinoプロジェクトのための最適化電源ソリューション
  11. ジャンパワイヤを排除:BreadShieldでArduinoをブレッドボードに直接接続