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 IDE
プロジェクト概要

私はエンジニア兼アーティストとして、科学と芸術を融合したプロジェクトを楽しんでいます。マイクロコントローラーを埋め込んだドレスデザインを制作し、電子部品の機能でデザインテーマを強化してきました。今回は、Arduino 101搭載のIntel Curie内蔵パターンマッチングエンジン(PME)と加速度センサーを活用します。Curieはニューラルネットワーク機能を持つPME向けに設計されていますが、PMEの活用例はまだ少ないのが現状です。この例がCurieのPME機能を引き出すきっかけになれば幸いです。
このドレスには私のグラフィックノベルの1ページを生地として使用しています。科学者とロボットが望遠鏡を覗いているデザインで、着用者が空中にパターンを描くと、星座の形に配置されたLED群が夜空のように光ります。
制作手順
注: 公開後に電子回路部分を改良しました。LEDの布への取り付け方と耐久性向上の改良版はステップ3以降に記載しています。
更新: 数回の終日デモイベントでの着用によりバッテリー持続時間の目安が得られました。このドレスの駆動には9V電池が約3時間ごとに交換必要です。電池が完全に消耗するわけではなく、9Vを下回るとパターンマッチングに非効率になるためです。使用済み電池は他用途に流用可能です。
ステップ1: LED配置

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

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

LEDをはんだ付けします。縫い付けタイプの個別LEDなら導電糸で接続も可能です。いずれにしても根気のいる手作業です。4つの星座それぞれを独立したトレース(配線経路)に分け、Arduino 101の異なるピンに接続します。
改良版手順
最初に使用した生地が柔らかすぎ、LEDと配線が曲がってはんだ接点が破損しやすかったため、以下の改良版で全工程をやり直しました。
LEDの位置決め

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

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

4つの星座(オリオン座、北斗七星、白鳥座、カシオペア座)をそれぞれ別パーツに準備しました。はんだ付けとヒートシュリンク処理後、すべてをフェルトにしっかり接着します。
テスト!
先に進む前に回路をテストしましょう! 各トレースでNeoPixel Strandtestを実行するだけです。
難易度を「簡単」としたのは、コードを理解すれば科学的には難しくないからですが、布上でケーブルを安定させるのに大変な手間がかかります。
Arduino IDEを最新版にし、Curie PMEライブラリを導入してください。Arduino Web Editorの使用を推奨します。ライブラリはこちらから。
ドレスの縫製
並行してドレスを縫製します。回路テスト後、LED付きベース生地をドレスの内側に縫い付けます。LEDの光がグラフィックを透過して輝きます。


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電池をバレルジャックに直接接続しています。

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