うごくものをつくる

個人的な学習ノート

工作物

SiPEED Maix M1n module(Maix Nano)で顔認識&顔位置座標の取得

1. SiPEED Maix M1n moduleってなに?

SiPEEDというメーカーが出しているマイコンボードに、Maixというシリーズがある。

Kendryte社のK210というマイコンを搭載しており、このK210がエッジAIにとても適した性能なので、パソコンのような高性能マシンを使わなくてもマイコン上で顔認識等の推論が可能だ。

Maixシリーズには、ESP32も一緒に載っててWi-Fi対応になったもの、最低限の機能のみ載せた小型で安価なものなどがある。

https://maixpy.sipeed.com/maixpy/en/develop_kit_board/get_hardware.html

今日使うのは、一番安くて小さいMaix Nanoというモデル。基板はだいたいSDカードと同じくらいの面積しか無く、とてもコンパクト。M1n moduleとかAI development kitとか呼ばれたりもする。本記事では書くのが楽だからという理由でM1nという名称を使う。

(画像は秋月電子通商より)

秋月電子では1450円で売っている(2021年2月7日時点)。たったこれだけの値段で、AIを実行可能なマイコンモジュールが手に入るって嬉しいよね。しかも2枚の基板だけでなくカメラもセットでこの値段。

Sipeed M1n Module AI Development Kit based on K210(RISC-V)

2枚の基板はコネクタを刺して2枚重ねになるような感じで繋げたあと、端をねじで固定する。それから、カメラのフレキ基板をFPCコネクタに挿す。この時、FPCコネクタにはフレキ基板が裏表どっちだろうと挿さるんだけど、正しい向きで挿さないとちゃんと認識されない。向きは写真の通り。M1nのFPCコネクタが見える面と、カメラの背面が上になる向きで挿す。

USBコネクタが有る側の基板にピンヘッダをはんだ付けしたらこんな感じ。

若干シルク印刷の文字が潰れてて読みづらいかな?あとUSBコネクタのすぐ隣のピン番号を示す文字がかなり無理のある配置で印刷されている。読めないよ……。

無理にシルクを判読しなくても、データシートを見たら良いんだけどね。

※SiPEEDデータシートより

2. 開発環境(MaixPyのインストール)

Maixシリーズの開発環境はいくつかあって、ESP32が載ってるモデルだとArduino IDEで開発できるし、普通のMicroPythonを使っても良い。が、今日使うM1nに関しては、SiPEEDが出してるMaixPyを使うのがおすすめ。これを使うと、カメラが見てる映像をPCソフト上で確認することができる。Maix Dockみたいな液晶ディスプレイがついてるモデルは、わざわざパソコン上にカメラ映像を出さなくてもそっちの画面で動作確認すればいいんだけど、安さと小ささが売りのM1nは画面がついてないから、PCソフト側で確認するしか無い。

というわけで、https://dl.sipeed.com/MAIX/MaixPy/ide/から、MaixPyをダウンロード&インストールしよう。

最新バージョンのフォルダを開き(2021年2月7日時点ではv0.2.5)、maixpy-ide-windows-x.x.x.exeというexeファイルをダウンロードし、これを実行してインストールする(ファイル名最後のx.x.xは実際にはバージョンの数字が入っている)。

3. MaixPy IDEを使う

インストールできたら、MaixPy IDEを起動する。ウィンドウは、左側がコード、右側がカメラが取得した画像とそのヒストグラムを表示する領域となっている。新規ファイルを作成すると、空のファイルではなく初めは下の画像のようなhelloworld_1.pyというファイル名のサンプルコードが開かれると思う。

これは、単にカメラで画像を撮ってLCDディスプレイに表示、をずっと繰り返すというもの。念の為サンプルコードをここにも載せておく。

# Hello World Example
#
# Welcome to the MaixPy IDE!
# 1. Conenct board to computer
# 2. Select board at the top of MaixPy IDE: `tools->Select Board`
# 3. Click the connect buttion below to connect board
# 4. Click on the green run arrow button below to run the script!

import sensor, image, time, lcd

lcd.init(freq=15000000)
sensor.reset()                      # Reset and initialize the sensor. It will
                                    # run automatically, call sensor.run(0) to stop
sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE)
sensor.set_framesize(sensor.QVGA)   # Set frame size to QVGA (320x240)
sensor.skip_frames(time = 2000)     # Wait for settings take effect.
clock = time.clock()                # Create a clock object to track the FPS.

while(True):
    clock.tick()                    # Update the FPS clock.
    img = sensor.snapshot()         # Take a picture and return the image.
    lcd.display(img)                # Display on LCD
    print(clock.fps())              # Note: MaixPy's Cam runs about half as fast when connected
                                    # to the IDE. The FPS should increase once disconnected.

ちなみに、22行目はLCDにカメラで撮影した映像を表示させるという記述だが、そもそもLCDディスプレイを持っておらずパソコン画面に表示させるしか手段のないM1nには関係ない。よって、22行目をコメントアウトしても問題なく動作する。

まずはこれを実行してみよう。

M1nとパソコンをUSB type-Cのケーブルで繋いだあと、画面左下にある緑色の鎖みたいなマークを押す。すると、シリアル通信するためのCOMポートを選ぶ選択肢が現れる。私の場合はCOM1、COM5、COM6の3つが表示されていた。COM1は何も繋いでなくても常に表示されているので違うとわかるけど、他の2つはどっちが正解だ……?と迷った。とりあえず数字の小さい方(COM5)を選ぶと問題なく接続できた。

左下のさっきまで緑色の鎖だったアイコンがこんな感じで赤くなれば接続成功。この赤いマークをクリックすると、接続は解除される。

コードの実行には、赤いマークの下にある再生ボタンっぽいのを押す。すると、M1n上のカメラが起動し、撮影した内容をウィンドウ右側エリアに表示させる。試しにたまたま手元にあったお菓子のグミのパッケージを映してみた。

ちゃんと動いている。なお、動作は右下の×をクリックすると停止させることができる。

これだけでは単なる画質の悪いビデオカメラなだけなので、いよいよK210マイコンの得意分野である、AIを使った画像認識(顔認識)を動かしてみる。

4. SiPEED Maix Nanoで顔認識

顔認識をさせるためには、K210に顔認識の学習モデルを書き込まなければならない。それから必須ではないが、ついでに最新のファームウェアもダウンロードして書き込んでおこう。

https://dl.sipeed.com/MAIX/MaixPy/release/master/から、最新バージョンのページに行き、ファームウェアのbinファイルをダウンロードする。ここで、同じような名前のファイルが大量にあると思われるが、迷ったらとりあえず内容全部込みの「一番ファイルサイズが大きく、ファイル名は一番短い」binファイルを選ぼう。例えばv0.6.2の場合、maixpy_v0.6.2_27_g4d8d4fbf0.binというファイルを選ぶ。g4d8d4fbf0というよくわからない文字列のあとにさらに色々と情報が付加されたもの(maixpy_v0.6.2_27_g4d8d4fbf0_openmv_kmodel_v4_with_ide_support.binなど)は無視する。

顔認識の学習モデルデータは、https://dl.sipeed.com/MAIX/MaixPy/release/maixpy_v0.3.1/にある、face_model_at_0x300000.kfpkgというファイルをダウンロードする。最新版が出ているかもしれないのでそこは要チェック。

次に、これらのファイルをK210に書き込むためのツールをダウンロードする。

https://github.com/sipeed/kflash_gui/releasesから最新版のページに入り、自分の使っているOS用のファイルをダウンロードする。私はwindowsを使っているので、kflash_gui_vx.x.x_windows.7z をダウンロード&展開し、その中にあるkflash_gui.exeから書き込みソフトを起動できる。

使い方は簡単。

① Open Fileから、さっきダウンロードしたファームウェアbinファイルを選択する。

② Portのところで、Maix M1nのCOMポート番号を選ぶ(私の場合、COM5)。

③ Downloadボタンを押して書き込み実行する。

これだけ。

顔認識学習データも、同じようにしてOpen Fileから選んで書き込むと良い。

このとき、MaixPy IDEの方でさっき動作確認した時のままシリアルポート接続状態だと書き込み時にエラーが出る。一旦MaixPy IDEを閉じるか、接続解除しておく。

なお、最新版のファームウェアはペリフェラルに関する設定が変わったのか、カメラ映像のコントラストが低くなってしまい、ちょっと暗い環境だと全然顔認識してくれなくなってしまった。環境によるのかもしれないが、とりあえず私は最新版ではなくちょっと昔のファームウェアv0.4.0_99を使うことにした。部屋がそこそこ明るければ、最新のFWでも問題ないと思う。

顔認識モデルの書き込みができたら、下の顔認識サンプルコードをMaixPy IDEにコピペし、さっきと同様のやり方で実行する。

import sensor
import image
import lcd
import KPU as kpu

lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.run(1)
task = kpu.load(0x300000) # you need put model(face.kfpkg) in flash at address 0x300000
# task = kpu.load("/sd/face.kmodel")
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9.01025)
a = kpu.init_yolo2(task, 0.5, 0.3, 5, anchor)
while(True):
    img = sensor.snapshot()
    code = kpu.run_yolo2(task, img)
    if code:
        for i in code:
            print(i)
            a = img.draw_rectangle(i.rect())
    a = lcd.display(img)
a = kpu.deinit(task)

これを実行してみると……

カメラが人間の顔を認識すると顔を囲む四角が自動で描画されるので、ちゃんと顔認識できていることがわかる。カメラ画質のせいか、小さすぎると反応しないかも?

5. 顔の位置(座標)取得

さて、顔認識そのものはできたが、今はまだ自動で顔を囲む枠が表示されているだけだ。たとえば、顔を認識した時にその位置が右寄りだったら○○をする、左寄りなら△△をする、といった処理をしたい時はどうすればいいだろうか?

※ここから先の解説は、PythonそのものやMicroPython、Maixシリーズのドキュメントをちゃんと読んでおらず行き当りばったりの独学なので間違った理解で書かれている可能性があるので注意。

コードを読むと、どうやら顔を囲む枠を描いているのはa = img.draw_rectangle(i.rect())という部分の処理のようだ。i.rect()を引数として枠を描いているみたいだけど、このi.rect()とは何なのだろう?

print文で出力させるなどいろいろ試してみるとわかるが、このi.rect()は「x座標、y座標、枠の幅、枠の高さ」を格納した配列となっている。というか、これら4つの数値情報を基に枠を生成している、と言ったほうが順番的には正しいか。

ということは、例えば顔の左右位置(x座標)を使って何らかの処理をしたいのなら、x座標数値が入っているi.rect()配列の0番目を抜き出せばいい。

つまり、int x = int(i.rect()[0])とすると、int型変数xに顔認識した顔位置のx座標が入る。

ちなみに、ここでのx座標、y座標とは枠の左上の角を原点としたものなので、実際に判定した顔の中心位置を表す数値ではない。顔の中心を正確に知りたいのなら、x = int(i.rect()[0]) + int(i.rect()[2]/2)で、y = int(i.rect()[1]) + int(i.rect()[3]/2)とするといいんだけど、別にここまでして正確な顔の中心位置を出さなくても、取得した座標は左上の角であることを念頭に入れた上で以降の処理の条件を決めればいいだけなので、本質的には必要ない。

これをもとに、M1nにLEDを2個繋ぎ、「認識した顔がカメラ視野の右半分にあるのか左半分にあるのかを判定し、それに応じて点灯させるLEDを変える」というプログラムを作ってみた。

# 顔認識の勉強用コード
# YOLOで顔認識したあと、顔の位置を座標取得して、カメラ視野の右半分か左半分のどちらにいるのかで
# 2つのLEDのどちらかを対応して光らせる

import sensor
import image
import time
import lcd
import KPU as kpu
from Maix import GPIO, FPIOA
from fpioa_manager import fm
from board import board_info

fm.register(20, fm.fpioa.GPIO1)#20番ピンをGPIO1番に設定
fm.register(15, fm.fpioa.GPIO2)#15番ピンをGPIO2番に設定。
#GNDのすぐ隣のGPIOピンソケットが20番、15番.近くて使いやすいのでこの2本を使用


led_R = GPIO(GPIO.GPIO1, GPIO.OUT)#GPIO1を出力ピンに設定し、オブジェクト「led_R」として生成
led_L = GPIO(GPIO.GPIO2, GPIO.OUT)#GPIO2を出力ピンに設定し、オブジェクト「led_L」として生成

led_R.value(1)#led_Rを点灯
time.sleep(0.5)#0.5秒待つ。
led_R.value(0)#led_Rを消灯。(ただの動作確認)

lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.run(1)

task = kpu.load(0x300000) # you need put model(face.kfpkg) in flash at address 0x300000
# task = kpu.load("/sd/face.kmodel")
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9.01025)
a = kpu.init_yolo2(task, 0.5, 0.3, 5, anchor)
while(True):
    img = sensor.snapshot()
    code = kpu.run_yolo2(task, img)
    if code:#YOLOで顔を検知したとき
        for i in code:
            print(i)
            a = img.draw_rectangle(i.rect())
            a = img.draw_string(0, 0, str(i.rect()[0]), color=(255, 255, 0), scale=2)
            face_x = int(i.rect()[0])
            #↑数値として処理に使うため、int型で変数に代入しておく。
        a = lcd.display(img)

        if face_x >= 90:#検出した顔x座標がカメラ視野の半分より右
            led_R.value(1)#led_Rの出力をハイに
            led_L.value(0)#led_Lの出力をローに
        else:#検出した顔x座標がカメラ視野の半分より左
            led_R.value(0)
            led_L.value(1)
    else:#顔を見つけられていないとき
        led_R.value(0)
        led_L.value(0)
a = kpu.deinit(task)

これを実行した様子はこんな感じになる。

M1nモジュールと撮影用カメラを持っていて両手がふさがっているので、顔ではなくM1nの方を直接動かしているが、とにかくカメラの角度によって認識した顔が視野の左右どちら側にあるのかが変わり、それに連動して点灯するLEDも変わっている様子がわかる。(なお、M1nは白いケースに入っているがこれは自分で3Dプリンターで作ったものであり、キットには付属していない)

今回はx座標を使ったので左右の位置変化に対応するプログラムとなったが、例えば幅や高さを条件にすれば、カメラに映る顔の大きさ、すなわちカメラと顔との距離に応じた処理をさせたりすることもできる。

また、今回はわかりやすさのために単純にLEDをオン・オフするだけのプログラムにしたが、出力をサーボモーターにつないで、顔の位置に応じてサーボにおくる信号を変えてやれば、顔の移動に追従する機構なんかも作ることができる。

5. まとめ

・SiPEED MaixシリーズはAIに特化したマイコンであるK210を搭載しており、M1n (Maix Nano)はLCDディスプレイが付いていないので安い。

・M1n moduleを使った顔認識、および顔の位置座標の取得、それに応じた処理をさせることができた。

それにしても、こんなに簡単にエッジAIで顔認識できるマイコンボードがたった1450円……。

性能と利便性の高いMaixDuinoですら5000円もしないくらいの値段で買えちゃう……

Sipeed Maix duino RISC-V AI + IoT 用のESP32モジュールを搭載した Sipeed Maixduino MCU

これを使ったいろんなプロダクトが今後たくさん出てくると思う。というかもう出てる。自分も取り残されないよう、使いこなせるまでたくさん勉強しよう。

あと、やっぱり画面がないのは不便なので、次に買うならMaixduinoやMaix Dockのディスプレイとセット売りのやつにしよう。インターネット接続しなくてもいいのなら、Maix Dockを買うのが一番いいかな。

Sipeed M1ドックスーツ(M1ドック+ 2.4インチLCD + OV2640) Edge Computing用ボード1st RV64 AIボード

MaixモジュールにはM1とM1wというのがあって、M1wはWi-Fi対応だけど確かまだ技適取れてなかったはずだから、色々面倒な手続きをしたくないのであればWi-Fi非対応のM1の方を買ったほうが良い。

おわり。

返信する

メールアドレスが公開されることはありません。 が付いている欄は必須項目です