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

TinyML on ESP32-S3: キーワードスポッティング(KWS)で音声制御デバイスを作る

部品と材料

部品数量備考
ESP32-S3-DevKitC-1 (N8R8: 8MB Flash, 8MB PSRAM)1AIアクセラレータ内蔵、PSRAM必須
INMP441 I2S MEMSマイクロフォン1ボトムポート、高SNR、I2S出力
MAX98357A I2S Class-Dアンプ1音声フィードバック用スピーカー駆動
スピーカー(4Ω 3W程度)1応答音声再生用
RGB LED (WS2812B)1状態表示(待機/検知中/実行中/エラー)
タクトスイッチ2ウェイクワード登録、工場リセット
LiPoバッテリ 3.7V 500mAh + TP40561ポータブル運用用(オプション)
ブレッドボード/ユニバーサル基板 + 配線1式

開発環境・フレームワーク

プロジェクト概要

ESP32-S3のベクトル命令拡張(ESP-NN)とPSRAMを活用し、完全オフライン・低レイテンシ・低消費電力なキーワードスポッティング(KWS:Wake Word Detection)システムを構築します。「Hey ESP」「ライトオン」「エアコン26度」等の任意ウェイクワードをローカルで検出し、後続の音声コマンド認識(ASR)やデバイス制御をトリガーします。クラウド不要、プライバシー完全確保、応答時間 < 200ms を実現します。

なぜESP32-S3 + TinyMLなのか

  • TFLMデリゲート:ESP-NNをTFLMバックエンドとして登録、自動最適化
  • 要素メリット
    Xtensa LX7 デュアルコア 240MHzDSP命令 + ベクトル命令(SIMD)で行列演算高速化
    8MB PSRAMモデル重み・中間バッファ・オーディオバッファを大容量確保
    ESP-NNライブラリConv2D, DepthwiseConv, FullyConnected, LSTM/GRU等を最適化
    I2S周辺機構マイク入力・スピーカー出力をDMAでゼロコピー転送
    ESP-SRフレームワークAFE(Audio Front-End: AEC/NS/AGC/BF)、KWS、ASR、TTSを統合提供

    システムアーキテクチャ

    
    +------------------+     I2S      +------------------+     RAM      +------------------+
    |  INMP441 Mic     | ──────────▶  |  ESP32-S3        | ◀──────────▶ |  PSRAM (8MB)     |
    |  (PDM→I2S)       |   Audio In   |  (DMA + I2S)     |   Audio Ring |  Model Weights   |
    +------------------+              |                  |   Buffer     |  Feature Buffers |
                                     |  +------------+  |              |  Tensor Arena    |
                                     |  | AFE (DSP)  |  |              +------------------+
                                     |  | - AEC      |  |                     ▲
                                     |  | - NS       |  |                     │
                                     |  | - AGC      |  |              +------+------+
                                     |  | - BF       |  |              │             │
                                     |  +----+-------+  |              ▼             ▼
                                     |       │         |       +------------------+  +------------------+
                                     |       ▼         |       |  KWS Model       |  |  ASR Model       |
                                     |  Feature Extract│       |  (DS-CNN /       |  |  (Whisper tiny / |
                                     |  (MFCC/LogMel)  │       |   CRNN /        |  |   Conformer)     |
                                     |       │         |       |   MatchboxNet)   |  |                  |
                                     |       ▼         |       +--------+---------+  +--------+---------+
                                     |  TFLM Inference │                │                    │
                                     |  (ESP-NN accel) │                ▼                    ▼
                                     |       │         |       +------------------+  +------------------+
                                     |       ▼         |       |  Command Parser  |  |  Action Executor |
                                     |  Post-process   |       |  (Intent/Slot)   |  |  (GPIO/MQTT/    |
                                     |  (Score/Thresh) │       +--------+---------+  |   Matter/HTTP)   |
                                     |       │         |                │            +------------------+
                                     |       ▼         |                ▼
                                     |  Decision Logic │       +------------------+
                                     |  (Wake/Command) │       |  TTS / Feedback  |
                                     +------------------+       |  (MAX98357A)     |
                                                                +------------------+
    

    ステップバイステップ実装ガイド

    Step 1: 開発環境セットアップ

    
    # ESP-IDF v5.1 インストール(Windows/Mac/Linux共通)
    # VS Code + ESP-IDF Extension が最も簡単
    # またはコマンドライン:
    git clone --recursive https://github.com/espressif/esp-idf.git -b v5.1 --depth 1
    cd esp-idf
    ./install.sh esp32s3
    . ./export.sh
    
    # ESP-SR 取得(コンポーネントとして追加)
    cd ~/esp
    git clone --recursive https://github.com/espressif/esp-sr.git -b v1.4 --depth 1
    # プロジェクトの CMakeLists.txt で extra_component_dirs に追加
    

    Step 2: オーディオハードウェア配線

    信号INMP441MAX98357AESP32-S3 GPIO
    I2S CLK (BCLK)SCKBCLKGPIO 12
    I2S WS (LRCLK)WSLRCGPIO 11
    I2S Data InSD--GPIO 10
    I2S Data Out--DINGPIO 9
    VCC3.3V3.3V3.3V
    GNDGNDGNDGND
    L/R SelectGND (Left)----
    GAIN--GND (12dB)--
    SD (Shutdown)--3.3V (Active High)GPIO 13

    注意: I2SピンはGPIOマトリクス経由で任意割当可能だが、ハイパフォーマンス用に上記推奨。PDMマイク(SPH0645等)使用時はPDM-to-I2S変換ハードウェア(ESP32-S3内蔵PDM周辺機能)使用可能。

    Step 3: ESP-SRプロジェクトテンプレートから開始

    
    # ESP-SRの例題をベースにプロジェクト作成
    cd ~/esp/esp-sr/examples/kws
    idf.py set-target esp32s3
    idf.py menuconfig
    # 主な設定:
    # - Component config → ESP Speech Recognition → Model Configuration
    #   * KWS Model: "ds_cnn" (推奨: 高速・軽量) または "crnn" (高精度)
    #   * Enable Multi-net: Y (複数ウェイクワード対応)
    # - Audio Front-End Configuration
    #   * AEC Enable: Y (スピーカー出力ありなら必須)
    #   * NS Enable: Y (ノイズ抑制)
    #   * AGC Enable: Y (自動利得制御)
    #   * BF Enable: N (単マイクなら不要)
    # - Partition Table: "custom" (モデルサイズに合わせ 4MB+ アプリパーティション)
    # - PSRAM: Enabled, 8MB
    idf.py build flash monitor
    

    Step 4: カスタムウェイクワードモデル作成(Edge Impulse活用)

    標準モデル("Hi Lexin", "Hi ESP"等)以外の独自ワードを使う場合:

    1. データ収集:Edge Impulse Studioでプロジェクト作成 → 「Audio」ブロック → 録音またはアップロード(ターゲットワード 50-100サンプル、背景音 200-500サンプル、その他ワード 100サンプル)
    2. 前処理:サンプリング 16kHz、フレーム長 1024ms、スペクトログラム(MFCC 13係数 または Log-Mel 40バンド)
    3. モデル設計:DS-CNN(Depthwise Separable CNN、~40KB)、MatchboxNet(~100KB)、CRNN(~200KB)から選択。ESP32-S3ならDS-CNNが最適バランス。
    4. 訓練・量子化:Post-training quantization (INT8) 有効化、代表データセットでキャリブレーション
    5. エクスポート:.tflite (INT8量子化済み) ダウンロード
    6. ESP-SR統合:model/ ディレクトリに配置、model_config.h でモデル名・入力形状・量子化パラメータ指定、idf.py build でファームウェアに組み込み

    Step 5: アプリケーションロジック実装

    
    // main/app_task.cpp - KWS検知後のアクション制御
    #include "esp_sr.h"
    #include "esp_mn_models.h"
    #include "driver/gpio.h"
    #include "driver/led_strip.h"
    #include "mqtt_client.h"  // または Matter/HTTP クライアント
    
    static const char* TAG = "KWS_APP";
    
    // コマンド定義
    typedef enum {
        CMD_NONE = 0,
        CMD_LIGHT_ON,
        CMD_LIGHT_OFF,
        CMD_AC_SET_TEMP,
        CMD_FAN_ON,
        CMD_FAN_OFF,
        CMD_QUERY_TEMP,
    } cmd_id_t;
    
    // コマンド文字列マッピング(多言語対応可)
    static const char* cmd_strings[] = {
        [CMD_LIGHT_ON] = "ライトオン",
        [CMD_LIGHT_OFF] = "ライトオフ",
        [CMD_AC_SET_TEMP] = "エアコン温度",
        [CMD_FAN_ON] = "扇風機オン",
        [CMD_FAN_OFF] = "扇風機オフ",
        [CMD_QUERY_TEMP] = "温度教えて",
    };
    
    // アクション実行関数
    static void execute_command(cmd_id_t cmd, int arg) {
        switch (cmd) {
            case CMD_LIGHT_ON:
                gpio_set_level(GPIO_LIGHT_RELAY, 1);
                play_tts("ライトをオンにしました");
                break;
            case CMD_LIGHT_OFF:
                gpio_set_level(GPIO_LIGHT_RELAY, 0);
                play_tts("ライトをオフにしました");
                break;
            case CMD_AC_SET_TEMP:
                // IRリモコン送信またはMQTT公開
                char topic[64], payload[32];
                snprintf(topic, sizeof(topic), "home/ac/%d/set", arg);
                snprintf(payload, sizeof(payload), "{\"temp\":%d}", arg);
                mqtt_publish(topic, payload);
                play_tts_printf("エアコンを%d度に設定しました", arg);
                break;
            // ... 他コマンド
            default:
                play_tts("コマンドが理解できませんでした");
        }
    }
    
    // KWSコールバック(ESP-SRから呼ばれる)
    static void kws_callback(esp_mn_state_t state, void* arg) {
        if (state == ESP_MN_STATE_DETECTED) {
            esp_mn_results_t* result = (esp_mn_results_t*)arg;
            int cmd_id = result->phrase_id;  // モデル内インデックス
            float score = result->score;
            
            ESP_LOGI(TAG, "KWS Detected: phrase_id=%d, score=%.2f", cmd_id, score);
            
            // 閾値チェック(モデルごとに調整)
            if (score < 0.85f) {
                ESP_LOGW(TAG, "Score below threshold, ignoring");
                return;
            }
            
            // LEDフィードバック:検知→青点滅
            led_strip_set_pixel(led_strip, 0, 0, 0, 255);
            led_strip_refresh(led_strip);
            
            // コマンド実行
            if (cmd_id >= CMD_LIGHT_ON && cmd_id <= CMD_QUERY_TEMP) {
                execute_command((cmd_id_t)cmd_id, 0);  // 引数付きは別途パース
            } else {
                // ASR(音声認識)モードへ遷移して詳細コマンド待受
                esp_sr_start_asr();
            }
        } else if (state == ESP_MN_STATE_TIMEOUT) {
            ESP_LOGI(TAG, "KWS timeout, back to listening");
            led_strip_set_pixel(led_strip, 0, 0, 255, 0);  // 待機:緑
            led_strip_refresh(led_strip);
        }
    }
    
    void app_main() {
        // ... ハードウェア初期化(GPIO, I2S, LED Strip, Wi-Fi, MQTT等) ...
        
        // ESP-SR初期化
        esp_sr_config_t sr_config = {
            .afe_config = &afe_config_default,
            .model_config = &model_config_kws,  // カスタムモデル指定
            .kws_callback = kws_callback,
            .asr_callback = asr_callback,       // 詳細コマンド認識用
            .tts_callback = tts_callback,
        };
        esp_sr_init(&sr_config);
        
        // KWS開始(常時待受)
        esp_sr_start_kws();
        led_strip_set_pixel(led_strip, 0, 0, 255, 0);  // 待機:緑
        led_strip_refresh(led_strip);
        
        // メインループ(他タスク監視、OTA、ヘルスチェック等)
        while (1) {
            vTaskDelay(pdMS_TO_TICKS(1000));
            // ヒープ/スタック監視、Wi-Fi再接続、MQTT keepalive等
        }
    }
    

    Step 6: 音声フィードバック(TTS)統合

    ESP-SRには軽量TTS(中国語/英語中心)が含まれるが、日本語対応には別途軽量エンジン(ESP32_TTS等)またはプリ録音WAVファイル再生(SDカード/フラッシュからI2S DMA転送)が現実的。

    性能・リソース使用量(実測値・目安)

    項目DS-CNN (KWS)CRNN (KWS)MatchboxNet (KWS)Whisper Tiny (ASR)
    モデルサイズ (INT8)~42 KB~180 KB~110 KB~15 MB (INT8) / 39 MB (FP16)
    PSRAM使用量~200 KB~500 KB~400 KB~30 MB (推論時ピーク)
    推論レイテンシ~12 ms~35 ms~28 ms~1.5-3 秒
    RAM使用量 (スタック込)~80 KB~150 KB~120 KB~2 MB
    精度 (自社データ)92% / 3% FAR96% / 1% FAR95% / 2% FARWER ~15% (日本語)
    常時稼働電流~18 mA~22 mA~20 mA~45 mA (ASR起動時のみ)

    ※ESP32-S3 @ 240MHz, PSRAM 80MHz, TFLM + ESP-NN delegate, I2S 16kHz 16bit mono

    実運用での工夫・ベストプラクティス

    トラブルシューティング

    症状原因・対処
    全く検知しないマイク配線確認(I2Sピン/クロック/フォーマット)、ゲイン設定(AFEのAGCターゲットレベル)、モデル入力形状(サンプルレート/フレーム長/特徴量次元)一致確認
    誤検知頻発閾値上方調整、AEC/NS強化、背景音データ追加訓練、最小継続フレーム数設定
    応答が遅いPSRAMクロック確認(80MHz)、ESP-NNデリゲート有効確認(ログで "ESP-NN delegate created")、Tensor Arenaサイズ十分確保
    メモリ不足でクラッシュPartition Tableでappパーティション拡大(4MB→8MB)、PSRAM確保、モデルサイズ削減(プルーニング/蒸留)
    音声出力にノイズI2Sクロック設定(MCLK=256fs)、MAX98357AのSDピン制御、電源ノイズ(アナログ/デジタルGND分離)

    発展アイデア

    まとめ

    ESP32-S3は、TinyML推論に特化したハードウェア(ベクトル命令、大容量PSRAM、高速I2S/DMA)と、成熟したソフトウェアスタック(ESP-SR, TFLM, ESP-NN)が揃っており、「クラウド不要の音声AIエッジデバイス」を現実的なコスト・電力・開発労力で実現できる数少ないプラットフォームです。本プロジェクトで示したKWS+二段階ASR構成は、スマートホームコントローラ、産業用音声操作パネル、ウェアラブルアシスタント、ロボット音声インターフェースなど、幅広い応用の基盤となります。独自ウェイクワードの作成から量子化・デプロイまで、Edge Impulse等のノーコードMLツールとESP-IDFのシームレスな統合により、ML非専門家でも実装可能になっています。これからの「音声ファースト」なIoTデバイス開発のスタートラインとして、最適なリファレンスになるでしょう。

    製造プロセス

    1. エレクトリックギター:歴史、設計、現代の革新
    2. スプーンフォーク(スポーク):進化、材料、現代製造の全貌
    3. チタン:主要特性、用途、産業利用
    4. バイオセラミックス:整形外科・歯科用途の先進材料
    5. カスタネット:歴史、職人技、演奏
    6. 接着剤の歴史、製造、未来:古代アデシブから現代イノベーションへ
    7. 糸:種類、歴史、製造、品質管理
    8. アセチレン:生産、用途、取り扱いの包括的概要
    9. アスベスト包括概観:古代利用から現代健康リスクまで
    10. サイコロの歴史・設計・製造:古代ツールから現代ゲームまで
    11. 錫:青銅器時代から現代産業まで - 用途、生産、未来