M5UnitVの開発環境をセットアップする

# はじめに
* 最近エッジAIなるものに目覚め、JetsonNanoにDeepStream SDKを入れて遊んだりしている。
* 年末にスイッチサイエンスさんのセールでM5UnitVを購入したので、ちょっといじってみる。
 * M5UnitVは、M5StickVから液晶、バッテリ、ジャイロセンサを省いた低価格版のため、M5StickVの文献が参考になる。
 * 2020年2月発売当初はあまり文献が無かったみたいだが、先人達が情報を残してくれているおかげで、今はスムーズに進められる。
 * microSDカードについては相性問題があるらしいので、ちゃんと使えるものを確保できるかがポイント。

![IMG_3303D.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/b0288d15-c1cb-2f99-05cb-980377308e56.jpeg)


# セットアップ
* 開発環境はiMac
* 基本的に、M5UnitV公式の「Quick Start」に沿ってガンガン進める。
 * https://docs.m5stack.com/en/quick_start/unitv/maixpy

ここで入手するのは、以下。
![スクリーンショット 2020-12-27 23.23.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/cc5f76b7-8012-2a87-9bd0-18dad8e7240e.png)

|  File | 説明 |
|:----|:--|
|kflash_gui_v1.5.2_macOS.dmg| ファーム書込みソフトKflash |
|M5StickV_Firmware_v5.1.2.kfpkg| M5UnitVファームウェア |
|maixpy-ide-mac-0.2.5_2.dmg| 開発環境 MaixPy IDE |

# ファームウェアのダウンロード
* 以下の公式ページのFirmwareリンクをクリックしてダウンロード。
 * https://docs.m5stack.com/en/quick_start/unitv/maixpy
* (ちなみに、MacではEasyLoaderはWindows専用なので使わない。)

![スクリーンショット 2020-12-27 23.00.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/b36f4a62-55b7-5faf-0a61-ffbecada20a5.png)


# ファーム書込みソフトKflash
* 続いて、Kflash_GUI をダウンロード。

![スクリーンショット 2020-12-27 22.58.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/9184831a-c8bc-7a7c-6090-16acf049c718.png)

* ダウンロードしたら、ファイルを開いて Macにインストール。
 * `kflash_gui.app`のアイコンをApplicationsフォルダにコピーすればOK. 

![スクリーンショット 2020-12-27 23.26.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/65bdb517-aa9c-8eb8-b1f5-bcae2dc2f142.png)

* 続いてKflashを起動。
 * 自分の場合、アイコンをダブルクリックしても起動しなかったので、`open /Applications/kflash_gui.app`で起動。
* M5UnitVをUSBケーブルで繋ぎ、先ほどダウンロードしておいたファームウェアを書き込む。
 * Select Fileで、`M5StickV_Firmware_v5.1.2.kfpkg`を指定。
 * Boardの種類は、M5StickV。
 * PortはUSBケーブルを繋いだシリアルポート。

![スクリーンショット 2020-12-27 23.34.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/e8db5296-6be4-1a7d-3f13-904427f0572e.png)

* しばらく待つと、書き込みが完了する。

![スクリーンショット 2020-12-27 23.36.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/b0b072b2-94e7-2a43-e5bd-e65db5a4d65b.png)

これでM5UNitV側の準備はOK。

# MaixPy
* 以下のサイトから最新のIDEをダウンロードする。
 * http://dl.sipeed.com/shareURL/MAIX/MaixPy/ide/v0.2.5
 * 2月に発売された以降、マイナーバージョンが上がってるので、とりあえず最新のをインストール。
* safariだと何故かダウンロードの認証ダイヤログで認証してもダウンロード開始しないので、chrome使ってダウンロードした。
 * 回線が細いのか？　100MB程度のダウンロードなのに30分以上かかった・・・。

![スクリーンショット 2020-12-27 23.02.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/b1bec786-daaa-db04-320d-e290c517f3f0.png)

* ダウンロードできたらファイルをクリック。
 * 以下の画面が立ち上がるので、アイコンをApplicationsフォルダにコピー。

![スクリーンショット 2020-12-27 23.28.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/5f70c365-2c3a-520d-50b6-171a27f2c5e5.png)


## 動作確認
* MaixPyを起動。
 * 外部から入手したアプリケーションはMacOX 上で起動していいか聞かれるので、OKにしておく。
* ボードの設定。
 * `ツール -> Select Board`で、M5StickVを選択
* M5UnitVに接続
 * 左下のチェーンのアイコンをクリックする。
 * シリアルポートを選択できるので、M5UnitVと接続されているシリアルポート名を指定。
* MaixPyのサンプルコード`helloworld_1.py`をそのまま実行。
 * 左下の再生ボタンで実行。
 * 右上のカメラ映像が表示されれば成功！
* IDE外観
 * 真ん中の領域に書いたPythonコードが左下の再生ボタンで実行され、カメラ映像が右上に表示される。
 * シリアル通信の入出力は、真ん中の下部の「シリアルターミナル」って部分をクリックすると表示される。

![スクリーンショット 2020-12-27 23.44.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/a6e5577b-9fe5-9454-2cf1-7e5ed0f85586.png)

# MaixPyのサンプルコードを見てみる。
* https://cn.maixpy.sipeed.com/en/

## その１：緑領域認識
* MaixPyページの左側メニューで、`MODULES/LIBS` --> `Machine vision` --> `image`を開く。
 * https://cn.maixpy.sipeed.com/en/libs/machine_vision/image.html
 * `1.1. Routine 1: Find green`に記載されているコードで緑領域を認識させてみる。

```Python:FindGreen
import sensor
import image
import lcd
import time
lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.run(1)
green_threshold   = (0,   80,  -70,   -10,   -0,   30)
while True:
    img=sensor.snapshot()
    blobs = img.find_blobs([green_threshold])
    if blobs:    
        for b in blobs:
            tmp=img.draw_rectangle(b[0:4]) 
            tmp=img.draw_cross(b[5], b[6]) 
            c=img.get_pixel(b[5], b[6])
    lcd.display(img)
```

おお、確かに緑色の部分を認識して、バウンディングボックスを重畳表示してくれる！

![スクリーンショット 2020-12-28 14.14.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/e28a7e34-bc46-19eb-cc83-03a8ef862a8a.png)

## その２：QR Code認識
* 続いて、`1.3. Routine 3: Scan QR code`のコードを真ん中のエディタ領域に貼り付けて実行してみる。
* そのままのコードだとclockがインポートできない、ってエラーになるので以下のように修正。
 * `import clock` --> `import time`
 * `clock = clock.clock()` --> `clock = time.clock()`


```Python:QR_Code
import sensor
import image
import lcd
#import clock
import time

#clock = clock.clock()
clock = time.clock()
lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1)
sensor.run(1)
sensor.skip_frames(30)
while True:
    clock.tick()
    img = sensor.snapshot()
    res = img.find_qrcodes()
    fps =clock.fps()
    if len(res) > 0:
        img.draw_string(2,2, res[0].payload(), color=(0,128,0), scale=2)
        print(res[0].payload())
    lcd.display(img)
```

* 認識させるためのQR Codeは以下のサイトを利用。
 * https://www.the-qrcode-generator.com

おお、ちゃんと認識されとる！！
上記ページにもあるようにQR Codeなので、URL以外に任意の文字列を認識させることができる。
これを読み取らせてコマンドとしてロボット制御してみても面白そう。

![スクリーンショット 2020-12-28 16.10.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/682bb581-1735-72b5-9552-c85ae007c6fc.png)

## その３: （応用）M5UnitVデバイス制御
* ちょっと応用して、A/Bボタン押下でLEDを光らせてみる。
* 公式ページの`UnitV_boot-v1220.py`を参考に抜き出し。
 * Aボタン押下でLEDをGreenに光らせる
 * Bボタン押下でLEDをBlueに光らせる

```Python
# --------------------------------
#  M5UnitV テストコード
#   A/Bボタン押下で LEDを光らせます
# --------------------------------
import time
from modules import ws2812
from fpioa_manager import *
from Maix import GPIO

# RGB LED設定
class_ws2812 = ws2812(8,100)
BRIGHTNESS = 0x10


# M5UnitV Aボタン設定(GPIO19)
fm.register(19, fm.fpioa.GPIO1)
but_a=GPIO(GPIO.GPIO1, GPIO.IN, GPIO.PULL_UP)

# M5UnitV Bボタン設定(GPIO18)
fm.register(18, fm.fpioa.GPIO2)
but_b = GPIO(GPIO.GPIO2, GPIO.IN, GPIO.PULL_UP)

but_a_pressed = 0
but_b_pressed = 0

# RGB LEDを光らせる
def RGB_LED(r,g,b):
    a = class_ws2812.set_led(0,(r,g,b))
    a = class_ws2812.display()
    time.sleep(0.2)
    a = class_ws2812.set_led(0,(0,0,0))
    a = class_ws2812.display()

# ボタンA/B Push検出
while(True):
    if but_a.value() == 0 and but_a_pressed == 0:
        print("A_push")
        but_a_pressed=1
        RGB_LED(0, BRIGHTNESS, 0)
    if but_a.value() == 1 and but_a_pressed == 1:
        print("A_release")
        but_a_pressed=0

    if but_b.value() == 0 and but_b_pressed == 0:
        print("B_push")
        but_b_pressed=1
        RGB_LED(0, 0, BRIGHTNESS)
    if but_b.value() == 1 and but_b_pressed == 1:
        print("B_release")
        but_b_pressed=0
```

## その４:色領域検出

![スクリーンショット 2021-01-02 21.25.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/42a787dd-9080-bdec-201e-de625a55cbdb.png)

```
# orange
target_lab_threshold = (0, 100, 4, 127, 15, 127)

# red
#target_lab_threshold = (0, 100, 31, 127, 19, 127)

# blue
#target_lab_threshold = (0, 100, 35, 127, -128, 127)
```


## その５: boot.pyの作成
* MaixPyで書いて実行したコードはM5UnitV上には残らないため、USBケーブルを抜き、別途M5UnitVに電源を供給してもブートしない。
* 任意コードでブートするようにするには、SDカードのROOTフォルダに`boot.py`と言う名前のファイルを置いておく必要がある。
 * https://cn.maixpy.sipeed.com/en/get_started/upload_script.html?h=boot.py
* また、いちいちSDカードにファイルを配置するのはめんどくさいので、MaixPy上でこの作業ができる手段が提供されている。
 * シリアル接続した状態で、`ツール` --> `Save open script to board(boot.py)`

![スクリーンショット 2020-12-30 22.57.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/3d2f4b44-f6f6-d1fd-b665-a5536b7c5fd2.png)


# SDカード相性確認
* いよいよ学習データを用意して任意の物体認識をさせる作業に入る前に、microSDカードの相性をチェックする。
* M5StickV, M5UnitVはmicroSDカードの相性がシビアだとされているので、手持ちのカードが使えなかったらこれ以上先に進められない。。。
 * （スイッチサイエンスには相性確認済みのmicroSDカード付きのM5UnitVも販売されているが、セール対象になって無かったので茨の道を選択した・・・）

何枚か持ってる手持ちのmicroSDカードを試した結果、、、、

* 以下の結果となった。
 * シリコンパワーの16GB（真ん中の白いやつ）：OK
 * 最近買ったKIOXIAの32GB（右側の青いやつ）：NG

よかった〜。
使えるのがあった。(ちょっと古めの奴がいいのかな？)

![IMG_3305D.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/f3a14f68-3cde-7747-143b-5a40b45fa349.jpeg)

* 相性チェック方法は、以下の[V-Training](https://docs.m5stack.com/#/en/related_documents/v-training)のページにある`Clock to download boot-UnitV`から以下の学習データ撮影用のpythonコード使う。
 * https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/UnitV_boot_v1220.py 
 * 実行すると、右上にカメラ映像が表示されるので、その状態でM5UnitVの`Aボタン`を押すと撮影できる。
 * SDカードが認識され、撮影画像がSDカードに書き込めると、以下のシリアルターミナルのような出力が得られる。
 * 特にシャッター音はしないので、シリアルターミナルの撮影枚数がインクリメントされるかで確認する。

![スクリーンショット 2020-12-28 23.16.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/d67addc0-d59a-52b7-cb37-f7ebbd7cf750.png)

# V-Trainingで機械学習
* UnitVで撮影した画像を学習させ、任意の物体を検出できるようにするまでの手順が以下のV-Trainingページに記載されている。
 * https://docs.m5stack.com/#/en/related_documents/v-training
* 自分もこれに沿って作業を進め６種類の物体データを用意し、指定された形式のZipファイルにしてトレーニングサービスにアップロードする所までやってみた。

![スクリーンショット 2020-12-29 11.57.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/3dd8a7c5-5e06-a864-8cef-8b9b1c51b408.png)

が、、、
2020/12/30現在、M5Stackのトレーニングサービスがストップしているらしい。。。
（2020/12/24 のタスクがずっと残ってるし・・・）

![スクリーンショット 2020-12-29 11.50.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283854/31447fd0-c11d-2679-461c-14589d4dbb0d.png)

M5Stack Communityにスレッドを立てて状況を確認してみたが、1日経っても返答なし。。。
年末なのでサーバ管理者もお休みかな？

[V-Training Service is active now??](https://community.m5stack.com/topic/2666/v-training-service-is-active-now)

残念。

と言うことで一旦、今年はここでおしまい。

----

追記(2021/1/1)
M5Stackのエンジニアが対応してくれました。検証再開です。


# V-Trainingでエラーを出しまくる。
* `UnitV_boot_v1220.py`を使ってトレーニングデータを撮影。
* Zipファイルにしてデータをアップロードすると何度もエラーで弾かれてしまった。
 * 現時点で自分は機械学習の中身は全く理解してないので、学習処理に関するエラーについては後で調べます。

## 1. Train or Valid folder not found
```
Online Training Request Failed
------------------------------
Hi!

Sorry for that, there is some error happened during the training process. 
We attached the reason below, if you have any questions, welcome to report to us through Twitter, Facebook, or Forum.
Or, check out our docs here: https://docs.m5stack.com/#/en/related_documents/v-training

USERID: XXXXXXXXX
CONTENT: Train or Valid folder not found. If you are using the M5StickV software, make sure you reach enough image counts of 35 per class.

Notice Info:
```

* 問題
 * 学習データが足りない。
 * 1クラス（分類）あたりtrainフォルダに35ファイル以上、学習データが必要だが足りない。
 * 取得したスクリプトを実行すると、31-35番目のファイルはtrainフォルダでなく、validフォルダに配置されるため、ギリギリだと足りなくなる。
* 修正方法
 * 1クラスあたり、50枚くらい撮影しておく。

## 2. Lake of Enough Valid Dataset
```
CONTENT: Lake of Enough Valid Dataset, Only 30 pictures found, but you need 35 in total.
```

* `Lake of Enough Valid Dataset`ってのは多分タイポで`Lack of Enough Valid Dataset`だろう。
* 問題
 * 50枚くらい撮影するとvalidフォルダ配下のClass分類番号フォルダに自動的に5ファイル正解データが配置されるが、それだと足りないみたい。
 * `ex. valid/1/31.jpg, 32.jpg, 33.jpg, 34.jpg, 35.jpg`
* 修正方法
 * 手動でtrainフォルダから正解ファイルをvalidフォルダに1ファイルだけコピーしてみた。
 * 6クラスに分類する構成なので、各クラス5-->6ファイルで、30-->36ファイルにした。