👄

GUIのないAndroid端末を音声コマンドで制御する

に公開

ボタンが押せない状況では困ります

 首掛け型のAndroid端末である「THINKLET」には、GUIがありません。制御は装置に付いている3つのボタンか、クラウド経由でリモートコントロールになります。
 この仕様はすこし不便なので、以前にUSBに接続するだけでタッチ可能な画面を表示するデバイスを作ったりしてみました。

Raspberry Piで作るTHINKLET Viewer
https://zenn.dev/fairydevices/articles/c730e7776139bb

 ただ、作業の現場では、両手が使えないということは結構あって、クラウド経由で制御するのも装置のボタンを押すのも、Viewerを接続して画面をタッチするのも困難なことがあります。
 たとえば、厚手の手袋の装着が必須であるような現場であるとか、手が水に濡れていたり、泥で汚れていたりするような現場です。また、部品や工具の操作などで両手がふさがっていることもあるでしょう。このような状況では、せっかく作った「THINKLET Viewer」も出番がありません。

 両手が作業で塞がっていたり、ボタン操作が困難なシーンでは、装置に向かって音声コントロールで指示ができれば便利です。
 ところが、音声認識と解釈を行うのは、不可能ではないものの技術的にはなかなか難しく、さらにその機能をAndroidアプリケーションごとに個別に組み込む必要があります。また、常時入力される音声を監視していないと対応できないので、待機時の消費電力も大きくなります。内蔵バッテリで動作している機器には致命的な問題です。
 また、キーワード検出を実際に試験的に組み込んでみても、認識の頻度が結構まちまちで、話者の発音や周辺の雑音などでデモに失敗することも多いです。実用にするにはだいぶハードルが高い状態です。

音声認識マイコンを使う

 中国では、数年前から主に玩具市場向けに音声コントロール用のマイコンが発売されています。
 玩具市場向けなので、価格がわりと安く設定されいて、電気をあまり食わない仕様です。音声認識も汎用品ですので話者によらず比較的高い認識率です。
 そのようなマイコンの1つを使った製品として、UnitASRというM5Stack社の販売しているモジュールがあります。
 UnitASRは、マイクとスピーカーを内蔵していて、音声入力コマンドに対して単体で応答して、シリアルポートに受信した音声コマンドの種類を出力することができます。ただし、あらかじめ登録した単純な音声コマンドにしか対応できません。UnitASRには、プリセットでいくつかのコマンドが登録されています。

M5Stack用ASRユニット - オフライン音声認識モジュール (CI-03T) — スイッチサイエンス
https://www.switch-science.com/products/10220

 UnitASRをTHINKLETに接続してやれば、音声コマンドでの制御が簡単に実現できそうです。
 さっそくやってみました。

THINKLETを音声マイコンで制御 - YouTube
https://www.youtube.com/watch?v=DiXwrtJgXcc

※ THINKLET側の音声には「VOICEVOX:ずんだもん」を使用しています。

 「Hi,M5!」はマイコンのウェイクワードです。「start」にOKと応えているのもマイコン側。「録画を開始します」と言っているのはTHINKLET上で動作するアプリです。

 これをどのように作ったのか、簡単に解説します。

USBシリアルコンバーター

 UnitASRの出力はTTLレベルのシリアル通信になりますので、Androidで動くTHINKLETとの接続にはUSBシリアルコンバーターを使用します。
 Androidで認識できるUSBシリアルコントローラーはいくつかありますが、今回はFTDI FT232RLを使用しました。国内ではType-C接続でOTG接続ができるモジュールはほとんど売っていませんので、Aliexpressで1個82円で輸入したものを使用します。安すぎるのでFT232RLの偽物が使われている危惧がありましたが、とりあえず問題なく動作しているようです。

Flutterでアプリケーションを作る

 Androidアプリケーションは、Flutterを使って開発しました。
 Flutterにはusb_serialというシリアル通信制御用のライブラリがありますので、これを使用します。

usb_serial | Flutter package
https://pub.dev/packages/usb_serial

 適用シーンとしては、THINKLETでの録画の開始と停止を音声コマンドで行うことを想定します。
 今回は技術デモ実装なので、実際に録画をコントロールするところまでは行わず、UnitASRへの指示がTHINKLETに伝わって音声が再生できればよいでしょう。

 UnitASRからの通信はバイナリで送られてくるので、16進に変換してFlutterのプログラム側でコードに対応した音声を流します。
 usb_serialのサンプルコードでは、見つかったUSBシリアルデバイスに対して、画面上のボタンを押して接続開始するように書かれています。THINKLETではボタンを押すことができないので、見つかったデバイスに対してすぐに接続を行うように変更します。

 Androidでは、USBシリアルが接続する度に、アプリケーション内でユーザーに接続を許可するダイアログを出すようになっています。これも、GUIのないTHINKLETでは利用が困難です。

 USBシリアルデバイスへのアクセス許可ダイアログは、Androidのセキュリティ機能の一部であり、通常のアプリケーションがユーザーの明示的な許可なしにこのダイアログを完全に非表示にすることは、基本的にできません。これは、悪意のあるアプリが勝手にUSBデバイスを操作することを防ぐための重要な仕組みです。

 しかし、特定のUSBデバイスが接続されたときに、毎回ダイアログを表示させずにアプリが自動的に処理を開始するように設定することは可能です。
 そこで、インテントフィルターの設定を行って、特定のUSBデバイス(VendorIDとProductIDで識別)が接続されたときに、Android側がアプリを起動候補として認識し、ユーザーが「常にこのアプリを使用する」を選択できるように設定します。
 AndroidManifest.xmlとdevice_filter.xmlを編集して、初回のみ許可したら以降は許可がいらないように設定しました。デバイスフィルタでは、10進で値を書く必要があるので注意が必要です。

AndroidManifest.xml

        <activity ()>
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
            </intent-filter>

            <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                android:resource="@xml/device_filter" />
        </activity>

device_filter.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-device vendor-id="1027" product-id="24557" />
</resources>

 確認のダイアログはこのように変わりました。チェックを入れておくと、次回からダイアログの確認は表示されなくなり、USBシリアルを認識した場合は今回作成したアプリケーションが起動されるようになります。

 最初の一回だけの許可も許されない状況では、アプリケーションにシステム署名を行うと、この確認を回避できますが、技術デモ実装なのでそこまではやりませんでした。

 USBシリアルのUSB PIDとVIDを登録して、機器が接続されたら自動的に指定したアプリケーションが起動されるような設定を行ったので、USBシリアルが差し込まれると自動的に作成したアプリケーションが起動されます。THINKLETで使う場合には、アプリケーションの起動コントロールがいらなくなるので便利です。

 THINKLETでは、アプリケーションの起動時やフロントに回った時に何か言ってくれないと、現在の状態がわかりません。このような場合には「音声コントロールを開始します」と言わせることにしました。

M5stack Japan Tour 2025 Spring Osakaで展示します

 5/2に大阪で開催される「M5stack Japan Tour 2025 Spring Osaka」にて、この記事の内容の実機デモ展示と、解説LTを行います。お近くの方で興味があれば、ぜひのぞきに来てください。

M5stack Japan Tour 2025 Spring Osaka | Peatix
https://m5stack2025springosaka.peatix.com/

今後の拡張

 今回は、イベントに向けてあわてて実装したこともあり、実際の録画制御の処理までは行っていません。Flutterでもカメラのコントロールはライブラリを使用すると簡単に実現できます。要望がありましたら、実際の録画のコントロールに対応するような変更を行いたいところです。
 また、音声マイコン単体で音声指示でGPIO制御もできるので、新規にLEDライトを付けた基板を作成して、音声でオンオフすることもできそうです。ライト付きなら暗いところで使用するときには便利でしょう。

フェアリーデバイセズ公式

Discussion