独自拡張版 Scratch で Teachable Machine拡張を使った音（異なる音階）でのロボットカー操作

## はじめに

昨年12月に、[下記の Qiitaの記事（過去記事 1）](https://qiita.com/youtoy/items/ce2efadf1228241ee3e3)を書く際に使わせていただいた「 [Junya Ishihara（@ jishiha）さん](https://twitter.com/jishiha)が公開されている[独自環境の Scratch](https://stretch3.github.io/)」、
そこに「 [ブラウザ上で簡単に機械学習のモデル作成等ができる Teachable Machine](https://teachablemachine.withgoogle.com/) の拡張機の機能」が新しく追加されていました。
以下の画像の赤枠で囲んだほうの拡張機能です。
<img width="509" alt="独自環境Scratchでの独自拡張.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/60e3f9cf-c5c2-24da-dfd3-418f46b1c740.jpeg">

今回の記事の内容は、この新拡張機能を試してた過程・プログラムの内容などについてです。

Teachable Machine に関して、「声の音の学習」・「その学習モデルを使った[ロボットトイ toio ](https://toio.io/)の操作（Web Bluetooth API を使った JavaScript でのプログラミング）」というのを試したことがあり、その内容は下記の 2つの Qiitaの記事（[過去記事 2](https://qiita.com/youtoy/items/9096836e5d77274500ea) と [過去記事 3](https://qiita.com/youtoy/items/37f70bb4ce630e6cbd92) ）で書いていました。

- 過去記事 1：[【Scratch 2019】Scratch を使い音声で車を操作 ＋ 音声合成（2019/12/25）](https://qiita.com/youtoy/items/ce2efadf1228241ee3e3)
- 過去記事 2： [Teachable Machine を使った音声からの任意のキーワードの検出（ブラウザ上で機械学習）](https://qiita.com/youtoy/items/9096836e5d77274500ea)
- 過去記事 3： [toio を音で制御してみた（Audio用の Teachable Machine でベルやタンバリンの音を機械学習）](https://qiita.com/youtoy/items/37f70bb4ce630e6cbd92)

今回の記事で新拡張機能を使って試す内容は、上記の「独自拡張版 Scratch でのロボットカー操作」・「Teachable Machine を使った音の学習による学習モデルの作成」で行った内容を組み合わせたものになります。

## 本編
###【実現すること】 ハンドベルの音でロボットカー micro:Maqueen を操作
まず、今回の記事の内容で実現することを動画で紹介します。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/jishiha?ref_src=twsrc%5Etfw">@jishiha</a> さんが開発された Scratch の独自拡張版（ <a href="https://t.co/qXjoB7z01r">https://t.co/qXjoB7z01r</a> ）に<br>Teachable Machine 拡張が追加されたようなので、<br>過去に試した「Teachable Machineでの音階の学習」と「拡張版Scratchでのロボットカー Micro: Maqueenの操作」と、上記拡張を組み合わせてみました！ <a href="https://t.co/hxtZabg09P">pic.twitter.com/hxtZabg09P</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1236673850466684928?ref_src=twsrc%5Etfw">March 8, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
これを実現するために利用する要素は以下があります。

- [Teachable Machine（ブラウザ上での機械学習）](https://teachablemachine.withgoogle.com/)
- [TM2Scratch （※ 独自拡張版Scratch に組み込まれた独自拡張機能）](https://github.com/champierre/tm2scratch)
- [Scratch2Maqueen （※ 独自拡張版Scratch に組み込まれた独自拡張機能）](https://github.com/champierre/scratch2maqueen)

### Teachable Machine を使った音（音階）の機械学習
まず、Teachable Machine での機械学習を行っていきます。
Teachable Machine の使い方の手順は、「過去記事 2： [Teachable Machine を使った音声からの任意のキーワードの検出（ブラウザ上で機械学習）](https://qiita.com/youtoy/items/9096836e5d77274500ea)」で記載した通りです。

ただ、学習させた内容は異なり、過去記事 2 では自分がしゃべった声を対象としましたが、今回はハンドベルの異なる音階を学習させています。（以下に学習させたクラスを記載）。

- 過去記事 2 で学習させた内容（しゃべった内容）
    - Background Noise ⇒ 背景ノイズが入力されている（何もしゃべっていない）
    - Class2 ⇒ 「OK Google」というキーワード
    - Class3 ⇒ 「Hey Siri」というキーワード
    - Class4 ⇒ 「Alexa」というキーワード
    - Class5 ⇒ 「ねぇClova」というキーワード
- 今回学習させた内容（ハンドベルで鳴らした音）
    - Background Noise ⇒ 背景ノイズが入力されている（音を鳴らしていない）
    - ド（C4）
    - ド（C4）連打
    - レ（D4）
    - レ（D4）連打
    - ミ（E4）
    - ファ（F4）
    - ソ（G4）

なお、各音階の録音では過去記事 2 の「【2020/2/6 追記】」の部分に書いた 1回で録音可能な時間の変更（デフォルトの2秒から8秒に変更）を行っています。また、同記事内の「[作成したモデルをプログラムで利用する](https://qiita.com/youtoy/items/9096836e5d77274500ea#%E4%BD%9C%E6%88%90%E3%81%97%E3%81%9F%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%A7%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B)」の手順では、作成したモデルはダウンロードとクラウドへのアップロードのうち、クラウドへのアップロードをしています。
ここでモデルをアップロードした際に表示される URL（モデルのアップロード先の URL）はメモしておいてください。

上記のクラスの中で「ド（C4）連打」・「レ（D4）連打」は、[上で紹介した動画](https://twitter.com/youtoy/status/1236673850466684928)では明示的に使われてなかったクラスです。これは、今回使った音のモデルが、下記の動画にある toio 操作の実験をするために作ったもので、それをそのまま流用したことによる名残です。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">先日、Tsukuba Mini Maker Faire 2020 の展示作品で使って、仕組みを記事にしていた、音で <a href="https://twitter.com/hashtag/toio?src=hash&amp;ref_src=twsrc%5Etfw">#toio</a> を操作するやつ、<br>その別バージョンで同じ楽器の音階違いでも似たことが実現できるか、試した動画です。<br><br>音階違いの音の機械学習、普通にできてしまいました。<br>　<a href="https://t.co/AAJDyHgLk3">https://t.co/AAJDyHgLk3</a><a href="https://twitter.com/hashtag/toiotomo?src=hash&amp;ref_src=twsrc%5Etfw">#toiotomo</a> <a href="https://t.co/Szxa8gs0YL">pic.twitter.com/Szxa8gs0YL</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1231948828074360832?ref_src=twsrc%5Etfw">February 24, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### 独自拡張版 Scratch でのプログラム作成
Teachable Machine でのモデルの準備ができたので、次に[独自環境の Scratch](https://champierre.github.io/scratch3/)で用意されている 2つの独自拡張機能である「TM2Scratch」と「Scratch2Maqueen」を使っていきます。

#### プログラム全体
今回作ったプログラムの全体は以下となります。
この画像だと個々のブロックが見づらいかと思いますので、この後に処理のまとまりごとの画像を提示しつつ、主な処理の説明を行っていきます。

<img width="483" alt="プログラム全体.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/6e093b34-c558-e83f-eb3a-0dd4e993915a.jpeg">

全体の処理について、大まかな内容は以下となります。

- 初期化処理（TM2Scratch の設定など）
- 音を認識した際の処理
- 認識した音に応じてロボットカー（micro:Maqueen）の挙動を変える処理
- ロボットカー（micro:Maqueen）を動かす処理

ここから、上記の 4つのそれぞれについて、主な処理内容を説明していきます。

##### 初期化処理（TM2Scratch の設定など）
最初にブロックで組んだプログラムを紹介します。
<img width="460" alt="初期化処理.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/61cbbf5a-dbdd-d65b-5d62-d1b34fa84f6f.jpeg">
初期化処理の部分でポイントは1つだけ、「音声分類モデルURL」のブロックです。
ここで、Teachable Machine の Webサイト上で作成した、モデルのアップロード先を指定します。拡張機能 TM2Scratch を開いた際に、デフォルト設定としてモデルの URL が入力されていますですが、ご自身で作成したモデルの URL に置きかえてください。

その他のブロックは、変数の初期化や、この後に紹介するプログラムで 入/切 の制御をしている内容を、最初に明示的に止める処理などになります。
ここでラベル付けの時間間隔を明示的に指定しています。現状で「0.1 / 0.2 / 0.5 / 1」から選択できるようになっていますが、とりあえず今回は 0.5 を最終的に選択して試しました（最適値を探して選んだという感じではないです）。

##### 音を認識した際の処理
まずはプログラム部分を紹介します。
<img width="488" alt="音声ラベルを受け取ったときの処理.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/71d27e49-9bc9-6d65-0399-0407ec5c41c7.jpeg">
この処理は、音の認識が行われて、その音があらかじめ学習させていたものいずれかであると認識されたとき（音声ラベルのどれかを受け取れたとき）に実行されます。
この部分のおおまかな流れは以下となります。

 -ラベル付けをいったん止める
 - 受け取ったラベルの内容・確度のチェック（いったん内容と確度を変数に格納）
 - ラベルの確度が 55％ より大きい場合に、ロボットカーを動かす処理を実行
 - ラベル付けを再開させる

今回、実現したい挙動は、
「学習済みの特定の音階が認識されたとき、その内容によって何らかロボットカーを動作させて、ロボットカー動作中は音の認識はせず、その後音の認識を再開」
という内容なのですが、初期化処理で選択していたラベル付け時間の間隔 0.5秒（または、現状の設定で選択できる最大値 1秒の間隔）で認識をさせていると、ロボットカーが動作を終える前に次の認識が行われ、ロボットカー動作中に次の指示が来ることがありました。
そのため、この「音を認識した際の処理（「メッセージを送って待つ ＝ "ラベルの確度のチェックとその後の処理" を送って待つ」のブロックの内容を含む）」が完了するまでの間、ラベル付けを止める処理を入れています。

また、ロボットカーを動作させる「メッセージを送って待つ」のブロックを動作させるのは、ラベルの確度が 0.55（55％）より大きい場合、という条件をつけています。
この条件を入れている理由は、意図しないノイズの音を学習済みの音階のどれかと誤認識した場合、それを除外するためです（※学習済みのものと似た音でなければ、確度は低くなるはず）。また、確度を 80％や 90％など高い値にしていないのは、今回の仕組みを試しに動かしている中で 70％程度の値もでていたことが要因の 1つです。その際、試しに 50％は超えていつつも低めな値を設定してテストし、想定通りの動作をしていたので、この値にしました。今回の学習モデルと自分が試した環境では、軽く試してみた中でこの設定値で問題は生じてないですが、状況や要求条件によって変えるべきポイントになってくるかもしれません。

##### 認識した音に応じてロボットカーの挙動を変える処理
まずはプログラム部分を紹介します。
<img width="436" alt="ラベルの内容による振り分け.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/bcf84be5-10a3-463b-dae6-73e66166110f.jpeg">
主な処理は、事前に Teachable Machine で学習させた各ラベルと、ロボットカーの動作とを紐付けるものです（上で紹介した動画では、ラベルの内容に応じて Scratch の画面の背景を変える処理も入れていましたが、ここでは省略しています）。
今回、自分が用意した学習モデルのクラスの中で、連打と連打でないものは区別せず使おうと思ったので、ドの音からソの音までの5種類のパターンに分け（連打と連打でないものは同じと見なすように設定して）、その5パターンにロボットカーの以下の動作を割り当てています。

- 前進（ソの音）
- 2つの単色LEDを点滅させる（ファの音）
- 後退（ミの音）
- 時計回りに回転移動（レの音、連打も含む）
- 反時計回りに回転移動（ドの音、連打も含む）

ロボットカーを動かす処理は、ブロック定義を使った 2つの処理を作りました（モーターの制御と LED の制御の 2つ）。その内容は、以下のとおりです。

##### ロボットカーを動かす処理
まずはプログラム部分を紹介します。
<img width="491" alt="Maqueenを動かす.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/6e29ce3a-920a-4b87-48d9-7328f793f530.jpeg">
ブロック定義を使った 2つの処理について、それぞれ主な内容は以下のとおりです。

- LEDの制御
    - ロボットカーの前方についている 2つの単色 LED を制御
    - LED の 入/切 を5回繰り返す設定
- モーターの制御
    - 3つの引数の値で、左右のモーターを動かす速さとモーターを動かす時間を設定
    - モーターを動かす処理を使うと、明示的に止める処理をするまでモーターが動き続けるので、一定時間経過後に停止の処理を実行

なお、LEDの制御の処理の最後に、LED を切にする処理を追加で入れている部分は、このLED制御処理の 5回の繰り返しを終えた後に LEDが点灯したままだったことが何度かあったためです。ただ、それに対する対処方法がこれで良いのかは分かっていません（Scratch と micro:bit の間のリアルタイムな処理実行部分の通信部分に関わっていそうな気もしますが、原因を確かめられておらず）。

## おわりに
今回、Teachable Machine を扱う独自拡張機能「TM2Scratch」を試しました。
TM2Scratch は、今回使った音の学習モデルを扱う機能だけでなく、画像の学習モデルを扱う機能もあります（余談ですが、Teachable Machine には、さらに人のポーズを学習させる機能もあったりします）。

今回使っていない画像の学習モデルを扱う機能も、今後試せればと思っています。