
ESP32だけで、呼びかけ・意図認識・声クローン発話。おもちゃ向けAI「のんきちゃんai」を作りました。

あなたのatomS3Rのデコレーションも現地で行います。
はじめに
まずsanoTTS-JPやそれ以外のオープンソース、すばらしいコーパスを公開してくださっている作者のみなさんに感謝します。皆さんのコードやデータがなけれなのんきちゃんaiは作れませんでした。本当にありがとうございます。
のんきちゃんaiは、話しかけるとこちらの言いたいことをくみとり、好きな声でこたえてくれるAIです。それをクラウドに頼らず、手のひらサイズのマイコンESP32-S3(ATOMS3R)だけで動かしています。
この記事では、何ができて、どう動いていて、これからどうしていくかを紹介します。後半では、中で使っている技術も少し書きます。
ファームウェアと声のモデルは、GitHub の kabayan/nonkiai で公開しています。ATOMS3R とボイスベースがあれば、ご自分でビルドして書き込めます(くわしくは「オープンソース公開について」)。
なぜ、全部オフラインなのか
おもちゃは、いつでもどこでも、すぐに遊べることが大事だと考えています。ネットにつながらなければ、Wi-Fiの設定も通信の待ち時間もいりません。声が外に送られないので、子どもが使うおもちゃでも安心です。サービスの終了で動かなくなることもありません。
必要なものは5,000円以下
必要なのは、ATOMS3Rとボイスベースだけです。合わせて5,000円以下でそろいます。組み込みやすい専用ユニットも、いま企画しています。
のんきちゃんaiの遊び方
遊び方は簡単です。のんきちゃんをポンと1回たたくか、「のんきちゃん」と呼ぶと「ピポ」と鳴ります。そこから3秒のあいだに話しかけます。「お名前は?」と聞けば名乗り、「今何時?」と聞けば時間を教えてくれます。
※ 会場がにぎやかで「のんきちゃん」の呼びかけが届きにくいときは、たたいて始める方法でも遊べます。
のんきちゃんaiでできること
できることは大きく3つです。
呼びかけに反応する(ウェイクワード認識)
言われたことの意図を推定する(意図認識)+赤外線送信など
その意図に合った言葉を、好きな声でしゃべる(音声合成)
これを使うと、たとえば推しキャラのアクリルスタンドに仕込んで声かけにこたえさせたり、ぬいぐるみをおしゃべり相手にしたりできます。赤外線で動くおもちゃなら、「すすんで」「みぎ」と声で操作することもできます。

「単語や文章」ではなく「意図」を聞きとる
のんきちゃんaiの音声認識は、言葉を文字に起こすのではなく、「この人は何をしてほしいのか」という意図を推定します。「お名前は?」も「名前を教えて」も、同じ「名前を聞きたい」という意図になります。
認識できる意図は17種類です(ここに「のんきちゃん」のウェイクワードが加わります)。名前・時間・天気・あいさつ・ほめ言葉・はい/いいえといったおしゃべりの意図と、前・後ろ・右・左・止まれなどの動きの意図があります。意図ごとに返したい言葉をテキストで登録しておくと、それを音声合成でしゃべります。
意図を絞っているのは、2つの理由があります。
1つはChatGPTなどとの対話に慣れているユーザーに、ESP32単体で可能な会話では期待に応えることができないからです。
もう1つはロボットの簡単な動きを確実に操作するとき、自然文での動作は体験を損なうことがわかりました。おもちゃ向けaiでは確実な単語を発話すると確実に動くことが重要だと考えます。
さらに、用途に合わせてモードを切り替えられるようにしています。ウェイクワードを認識したあと、設定したモードによって聞きとる意図が変わります。
ぬいぐるみモード:名前・時間・天気などのおしゃべりが中心
おもちゃモード:前・後ろ・左・右のコマンドで、赤外線おもちゃを操作
10秒ほどの声から、その声でしゃべる
音声合成は、sanoTTS-JPをベースとしたツールでモデルを作成しています。このツールを使うと10秒ほどの声の録音があれば、その声で、登録した好きな文をしゃべらせられます。
このツールですが今のところ公開予定はありません。sanoTTS-JPにプルリク出そうと頑張っていたのですが、変更箇所がむちゃくちゃ多くなってマージできるレベルではなくなったことも要因の1つです。
nonkiaiで公開している2つのモデルはIrodoriTTSを使い、プロンプトから生成した2つのリファレンス音声を使って再度IrodoriTTSを使って生成した学習データを使っています。
生成はストリーミング方式で、生成速度は約0.7です。音声の長さより短い時間で作れるので、作りながら順に再生できます。マイコンの上でも、待たせずに返事ができます。
中のしくみ(少しだけ技術の話)
のんきちゃんaiの中では、「聞きとる」と「しゃべる」の2つのAIが、同じマイコンの上で動いています。どちらも、大きなAIの知識を小さなAIに写す「蒸留」と、計算を8ビットの整数で行う「INT8量子化」を使って、マイコンに収まる大きさにしています。
しゃべる:sanoTTS-jp を大きく改造して、声と音質を作りなおした
音声合成には、オープンソースの日本語音声合成 sanoTTS-jp をもとにしています。sanoTTS-jp は、大きな音声合成モデル(piper-plus、MB-iSTFT-VITS2)を「先生」にして、その出力を約56万パラメータの小さな「生徒」3つ(音の長さを決める Duration、声の特徴を作る Acoustic、波形にする iSTFT Decoder)に蒸留したものです。漢字の読みやアクセントの推定まで、マイコンの中で行えます。
ただ、そのままでは声が少し遅れて重なって聞こえる「二重感」やノイズ、アクセント違いが目立ちました。また元のリファレンス音声によって品質差や障害が個別に発生しました。そこで、次のように大きく手を入れています。
先生を、声を変えられるモデルに入れ替えた:先生のモデルを Style-Bert-VITS2 に替え、話者ごとに先生を学習できるようにしました。
少しの録音から、学習用の声を大量に作る流れを作った:10秒ほどの参照音声をもとに、声をまねる音声合成(Irodori-TTS)で約5,600文の読み上げを作ります。それを音声認識で読み直して、読み間違いや雑音のある文を選り分けます。この素材で先生を学習し、生徒に蒸留します。この一連の流れは、ブラウザから操作できる道具にまとめました。
二重感やノイズ、アクセント違いの原因を一つずつつぶした:先生に答えを作らせるときに入れていた乱数のゆらぎが、生徒の誤差より大きかったので、ゆらぎを止めて作りなおしました。生徒が使うアクセントと先生が使うアクセントの食い違い(文の約4割で食い違っていた)もそろえました。先生の学習が足りていなかった分は、学習を延ばしました。
まだかすかに残っていますが実機ではほぼ気にならないはずです。生徒の出口を広げた:波形を作る直前の部分を48次元から512次元に広げ、細かい音を出せるようにしました。モデルは元の約2.3倍(約1.5MB)になりましたが、マイコンの上で音声の長さの約0.65倍の時間で作れ、ストリーミング再生するので途切れずにしゃべれます。また文長制限はありません。
聞きくらべ:もとの声・先生・マイコンの声
実際の音を聞いてみてください。女性の声(female2)と男性の声(man2)の2つについて、もとにした録音と、同じ文を「先生」(Style-Bert-VITS2)と「生徒」(マイコンで動く sanoTTS-jp 改造版)が読んだ音声を並べました。生徒の音声は、ESP32 と同じ計算のしかたのプログラムを PC で動かして作ったものです。生徒は、先生の声を小さなモデルに写しとったものです。
もとにした録音(リファレンス音声)
この短い録音だけから、それぞれの声の先生と生徒を作りました。
同じ文を、先生と生徒が読んだ音声
文、female2の先生と生徒、man2の先生と生徒
1 永久にボス部屋を出られない気がして,ぴぇーんと涙ぐむ.
聞く 聞く 聞く 聞く
2 指をくわえてぴゅーと一声口笛を吹いた。
3 ヴェールを脱ぐように、少しずつ新しい機能を公開していきましょう。サプライズも必要ですよね!
4 はい。優先順位を上げて、早急に対応いたします。
5 平たく言えば、虎穴に入らずんば虎子を得ずってやつだ。
聞く 聞く 聞く 聞く
先生(Style-Bert-VITS2)は 44.1kHz、生徒(マイコン用)は 22.05kHz で出力しています。
生徒の音声は PC で作りました。マイコンのスピーカーで鳴らした音を録ったものではありません。
聞きとる:日本語のウェイクワードと意図認識を、合成音声で学習した
音声認識には、私が作っている stt32 を使っています。このツールは今後公開予定です。
流れは次のとおりです。
マイクの音を16kHzで3秒ぶん取り、声のある区間だけを切り出します。
音をメルスペクトログラム(耳の感じ方に近い周波数の分布)に変えます。
WakeHuBERT で、20ミリ秒ごとに「どんな音か」を表す特徴を取り出します。WakeHuBERT は、大きな音声モデル HuBERT を約96万パラメータに蒸留した公開モデルで、双方向GRU(前後の音のつながりを読む仕組み)を持っています。
小さな畳み込みネットワークの分類器が、その特徴から17の意図のどれかを判定します。分類器は10本作り、10本の答えを平均して決めます(アンサンブル)。
学習には、人の声の録音をほとんど使っていません。声をまねる音声合成で、130人ぶんの声・60以上の言い方の読み上げを約9,000件作り、そのデータに環境音の追加やピッチ変更などを行いデータを増やして学習しました。本人の声の録音は、できあがりの確かめにだけ使っています。本人の声の803件で試すと、約89%(711件)が正しく聞きとれました。
マイコンへの実装では、重み(約1.6MB)を8ビットに量子化し、計算の重いGRUの部分は2つのCPUコアで分けて計算しています。3秒の音を聞いてから答えが出るまで、遅いときでも約1.2秒です。
ウェイクワードの「のんきちゃん」は、いちばん苦労しているところです。最初は専用の判定器や、意図の1つとして足す方法を試しましたが、合成音声だけで学習したモデルは、本人の声の「のんきちゃん」にほとんど反応しませんでした。言い方を増やし、音の条件や話す速さを変えたデータで学習しなおし、録音の音質の違いを補正し、他にもいろいろ調整したところ、本人の声の40回すべてに反応し、日常の会話の音声約6時間では一度も誤って反応しない結果が出ました。これを本体に組み込み、のんきちゃんの本体でも、呼びかけで聞きとりを始められるようになりました。さぁ、会場ではどうかな??
モデルの大きさ
3つを合わせても約6MBで、マイコンのフラッシュメモリに収まっています。声を替えるときは、しゃべるモデルの約1.5MBだけを書き換えます。
聞きとる(stt32)
WakeHuBERT(約96万パラメータ)+意図の分類器10本(1本あたり約6.4万パラメータ)
約1.6MB
しゃべる(sanoTTS-jp 改造版)
Duration / Acoustic / iSTFT Decoder の3つ
約1.5MB
読みの辞書
漢字の読みとアクセント用、13万5千語
約3.0MB
おまけ
この2つ以外にもセンサーを使ってぬいぐるみを揺らせたり、寝かしたりすることで話す機能も入っています。マイク、スピーカー、ITU、赤外線の同時利用はなかなかややこしい実装になっています。AIではぬいぐるみを動かしたり、おもちゃを操作したりできないのでミートプロキシー三昧。
声の学習に使ったデータ
声のモデル(先生と生徒)を作るときは、10秒ほどの参照音声の声で、公開されている日本語の読み上げ用の文(コーパス)を読ませて学習用の素材にしています。使ったのは、自由に使えるライセンスで公開されている次のコーパスの文です。音声認識で読み直して選り分けたあとに残った文の数を、声ごとに書いています。
コーパス 、入手先、ライセンス、元の文の数
ROHAN4600 | mmorise/rohan4600 | CC0-1.0 | 4,600
あ ITA コーパス | mmorise/ita-corpus | パブリックドメイン / Unlicense | 424
AICA コーパス | reinehonoka/aica-corpus | CC0-1.0 | 500
COEIROINK コーパス | shirowanisan/coeiroink-corpus-manager | パブリックドメイン / Unlicense | 652
オープンソース公開について
のんきちゃんaiのファームウェアと声のモデルを、GitHub で公開しました。
https://github.com/kabayan/nonkiai
リポジトリを clone すると、ESP-IDF でビルドして ATOMS3R に書き込めます。ネットから取ってくるのは ESP-IDF の部品だけで、それ以外のソースと重みは全部リポジトリの中にあります。
cd firmware && idf.py set-target esp32s3
idf.py build
idf.py -p <ポート> flash入っているものは次のとおりです。
・のんきちゃんのファームウェア(本体のプログラム) | MIT |
・声のモデル female2(既定)・man2 | MIT |
・ 聞きとる部分 stt32 のプログラムと重み | Apache-2.0(第三者の表記は同梱の NOTICE) |
・漢字の読みの辞書 | NAIST-JDIC の派生(修正 BSD) |
・sanoTTS-jp・Open JTalk・x-io Fusion の写し | それぞれのライセンス(同梱)sanoTTS-JPはソースだけでモデルなどは含まれていない |
声のモデルは、PC のブラウザ(Chrome / Edge)から USB で差し替えることもできます。手順はリポジトリの `README.md` にあります。
声のモデルの参照音声と学習用の音声は、どちらも Irodori-TTS で作りました。実在の人の録音は使っていません。
「この声でしゃべらせたい」「こんな言葉にも反応させたい」といったご要望は、お気軽にご相談ください。
問い合わせ先:Xアカウント kabayan kabayan@adlibjapan.jp
ソースコード: https://github.com/kabayan/nonkiai