Arduino Nano 33 BLE Senseで作るTinyMLジェスチャー認識デバイス
部品と用品
| Arduino Nano 33 BLE Sense | 内蔵IMU(LSM9DS1)、マイク、温湿度・気圧・光・近接・色センサー搭載 | 1 |
| micro USBケーブル | 1 | |
| PC(Arduino IDE / Arduino Cloud) | 1 |
このプロジェクトについて
Arduino Nano 33 BLE Senseに搭載された9軸IMU(加速度・ジャイロ・磁力)を使って、機械学習(TinyML)によるジェスチャー認識をエッジデバイス単体で実行するプロジェクトです。クラウド不要、オフラインで動作し、認識結果をBLEでスマホアプリやPCに通知できます。
TinyMLとは
TinyML(Tiny Machine Learning)は、マイクロコントローラー級のリソース制約デバイス上で機械学習モデルを推論する技術です。Arduino Nano 33 BLE Sense(Cortex-M4F 64MHz、1MB Flash、256KB RAM)でも、適切に量子化されたモデルならリアルタイム推論が可能です。
ワークフロー
- データ収集: ボード上のIMUから加速度・ジャイロデータを採取し、各ジェスチャーのサンプルを蓄積
- モデル訓練: Edge ImpulseやTensorFlow Lite for Microcontrollersでモデル作成・量子化
- デプロイ: 生成されたC++ライブラリをArduinoスケッチに組み込み
- 推論・通知: 実行中にジェスチャー検知したらBLEで通知
ステップ1:データ収集スケッチ
#include <Arduino_LSM9DS1.h>
const int SAMPLE_RATE = 100; // Hz
const int SAMPLES_PER_GESTURE = 100; // 1秒分
const float ACCEL_THRESHOLD = 2.0; // 着手検知閾値 (G)
enum Gesture { NONE, SHAKE, SWIPE_LEFT, SWIPE_RIGHT, CIRCLE };
Gesture currentGesture = NONE;
int sampleCount = 0;
float samples[100][6]; // ax, ay, az, gx, gy, gz
void setup() {
Serial.begin(115200);
while (!Serial);
if (!IMU.begin()) {
Serial.println("IMU init failed!");
while (1);
}
Serial.println("Gesture Data Collector");
Serial.println("Send gesture name (shake/swipe_left/swipe_right/circle) to start recording");
}
void loop() {
// シリアルコマンドでジェスチャー指定
if (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
cmd.trim();
if (cmd == "shake") currentGesture = SHAKE;
else if (cmd == "swipe_left") currentGesture = SWIPE_LEFT;
else if (cmd == "swipe_right") currentGesture = SWIPE_RIGHT;
else if (cmd == "circle") currentGesture = CIRCLE;
else currentGesture = NONE;
sampleCount = 0;
Serial.println("Recording: " + cmd);
}
// 加速度閾値超過で自動記録開始
if (currentGesture != NONE && sampleCount < SAMPLES_PER_GESTURE) {
float ax, ay, az, gx, gy, gz;
if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()) {
IMU.readAcceleration(ax, ay, az);
IMU.readGyroscope(gx, gy, gz);
float accelMag = sqrt(ax*ax + ay*ay + az*az);
if (accelMag > ACCEL_THRESHOLD || sampleCount > 0) {
samples[sampleCount][0] = ax;
samples[sampleCount][1] = ay;
samples[sampleCount][2] = az;
samples[sampleCount][3] = gx;
samples[sampleCount][4] = gy;
samples[sampleCount][5] = gz;
sampleCount++;
}
}
}
// 記録完了 → CSV出力
if (currentGesture != NONE && sampleCount >= SAMPLES_PER_GESTURE) {
Serial.println("GESTURE_DATA_START");
for (int i = 0; i < SAMPLES_PER_GESTURE; i++) {
Serial.print(currentGesture); Serial.print(",");
for (int j = 0; j < 6; j++) {
Serial.print(samples[i][j], 4);
if (j < 5) Serial.print(",");
}
Serial.println();
}
Serial.println("GESTURE_DATA_END");
currentGesture = NONE;
}
}
ステップ2:Edge Impulseでモデル作成
- Edge Impulse でプロジェクト作成
- Data acquisitionタブでCSVアップロード(上記スケッチの出力をファイル保存)
- Impulse design: Time series data → Window size 1000ms, Window increase 100ms
- Processing block: Spectral Analysis / Flatten
- Learning block: Classifier (Neural Network, 20 epochs, learning rate 0.0005)
- Generate features → Train model → Test accuracy確認(目標90%以上)
- Deployment → Arduino library →
Buildで .zip ダウンロード
ステップ3:推論スケッチ(Edge Impulseライブラリ使用)
#include <Arduino_LSM9DS1.h>
#include <edge-impulse-inferencing.h>
#define CONFIDENCE_THRESHOLD 0.8
void setup() {
Serial.begin(115200);
while (!Serial);
if (!IMU.begin()) {
Serial.println("IMU init failed!");
while (1);
}
Serial.println("TinyML Gesture Recognition Ready");
ei_printf("Inferencing settings:\n");
ei_printf("\tInterval: %.2f ms\n", (float)EI_CLASSIFIER_INTERVAL_MS);
ei_printf("\tFrame size: %d\n", EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);
ei_printf("\tSample length: %d ms\n", EI_CLASSIFIER_RAW_SAMPLE_COUNT / 100);
}
void loop() {
// 推論用バッファ確保
float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = {0};
// データ収集(ノンブロッキング)
static int bufIndex = 0;
if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()) {
float ax, ay, az, gx, gy, gz;
IMU.readAcceleration(ax, ay, az);
IMU.readGyroscope(gx, gy, gz);
buffer[bufIndex++] = ax;
buffer[bufIndex++] = ay;
buffer[bufIndex++] = az;
buffer[bufIndex++] = gx;
buffer[bufIndex++] = gy;
buffer[bufIndex++] = gz;
}
// フレーム完成で推論実行
if (bufIndex >= EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
bufIndex = 0;
signal_t signal;
signal.total_length = EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE;
signal.get_data = [](size_t offset, size_t length, float *out_ptr) {
memcpy(out_ptr, buffer + offset, length * sizeof(float));
return 0;
};
ei_impulse_result_t result = {0};
EI_IMPULSE_ERROR res = run_classifier(&signal, &result, false);
if (res != EI_IMPULSE_OK) {
Serial.println("Inference failed");
return;
}
// 結果表示
Serial.print("Predictions: ");
for (uint16_t i = 0; i < EI_CLASSIFIER_LABEL_COUNT; i++) {
Serial.print(result.classification[i].label);
Serial.print(": ");
Serial.print(result.classification[i].value, 4);
Serial.print(" ");
// 閾値超過でジェスチャー検知
if (result.classification[i].value > CONFIDENCE_THRESHOLD) {
Serial.print(" *** DETECTED: ");
Serial.println(result.classification[i].label);
// ここでBLE通知やアクション実行
}
}
Serial.println();
}
delay(10); // 100Hzサンプリング
}
ステップ4:BLE通知(オプション)
#include <ArduinoBLE.h>
BLEService gestureService("19B10000-E8F2-537E-4F6C-D104768A1214");
BLEStringCharacteristic gestureChar("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLENotify, 20);
void setupBLE() {
if (!BLE.begin()) return;
BLE.setLocalName("GestureRecognizer");
BLE.setAdvertisedService(gestureService);
gestureService.addCharacteristic(gestureChar);
BLE.addService(gestureService);
gestureChar.writeValue("ready");
BLE.advertise();
}
// 推論ループ内で検知時に:
// gestureChar.writeValue(detectedGesture);
精度向上のコツ
- 各ジェスチャー最低50サンプル以上、多様な速度・角度で収録
- 背景ノイズ(何もしない状態)も「unknown」クラスとして学習
- データ拡張:ノイズ付加、時間スケーリング、軸回転
- モデル量子化(int8)で推論高速化・メモリ削減
- 推論間隔を調整(連続検知防止のためクールダウン設定)
応用例
- スマートライト制御(シェイクでオン/オフ、スワイプで調光)
- プレゼンテーションリモコン(ジェスチャーでページ送り)
- リハビリ支援(決められた動作のカウント・フォームチェック)
- IoTデバイスの直感的インターフェース
製造プロセス
- プリント基板:設計、製造、未来トレンド
- ロウソククレヨンで3Dプリント部品を着色:新しい後処理技法
- 3Dプリント義肢:切断患者のアクセスと負担を変革
- Google地形データから高品質な3Dプリント地形図を作成する方法
- 品質管理の向上:3Dプリント工具が製造精度を高める
- ケブラーCFFで機能的な3Dプリント自転車パニアアダプターを設計
- ゴーカートの再発明:3Dプリントカーボンファイバーパーツで比類なきパフォーマンスを実現
- 人間中心のサプライチェーン変革:効率とレジリエンスを両立
- スマート旋盤:精密加工のための先進タッチスクリーン制御
- 静電容量式加速度センサーの仕組み:質量バネ系から信号調整まで
- 電気機械的に結合した導体を用いた高度ユニバーサル触覚センシング