画像認識AI付きカメラ「HuskyLens」をmicro:bitから使ってみた

スポンサーリンク

Huskylensとは

HuskyLensはニューラルネットワークプロセッサ(KPU)を搭載したAIカメラ。
クレジットカードより小型のボードたった一枚で画像認識の学習と認識の両方をこなす。
LCDもカメラも内蔵していて、足りないのは電源だけ。動かすだけなら、その電源もmicro USBでOK。
学習方法にコツがいるし、認識に向いたものと向かないものがあるものの、ダイヤルとボタンだけで学習して認識してしまう。
学習の待ち時間もほぼゼロ。小学生に使わせてみたが、すぐに学習も認識もできるようになった。
すごい時代になったものだ。

スペック

Processor: Kendryte K210
Supply Voltage: 3.3~5.0V
Current Consumption(TYP): 320mA@3.3V, 230mA@5.0V (face recognition mode; 80% backlight brightness; fill light off)
Connection Interface: UART, I2C
Display: 2.0-inch IPS screen with 320*240 resolution

リンク

製品ページ
公式のベータ版ドキュメント
Maqueen PlusとHuskyLensを使うための公式チュートリアルPDF
PDFには応用例など、上記ベータ版ドキュメントに記載されていない内容が色々記載されている。
コミュニティ
git
ファームウェア

内蔵アルゴリズム

内蔵アルゴリズムは次の6種類

  • 顔認識(Face Recognition)
  • オブジェクト追跡(Object Tracking)
  • ライン追跡(Line Tracking)
  • 色認識(Color Recognition)
  • タグ認識(Tag Recognition)
  • オブジェクト認識(Object Recognition)

ファームウェアを書き換えれば、オブジェクト分類(Object Classification)も使えるらしい。
本体のダイヤルボタンを長押しするとアルゴリズム別のオプションが表示されるけど、いまいちよくわからないオプションが多い。

顔認識(Face Recognition)

初期状態で顔の検出はしてくれる。
学習させることで特定の人の顔を覚えてくれる。
"Learn Multiple"をOnにすると、複数の顔を学習できる。
電源を切っても学習内容は保持される。

オブジェクト追跡(Object Tracking)

追跡できるのは1つのオブジェクトのみ。
明確な輪郭があるものがいいらしい。
学習時はオブジェクトが画面中央のオレンジ色の境界ボックスに含まれるようにする。オブジェクトの一部のみがボックスに含まれているのもOK。
"Learn Enable"をOnにすると、オブジェクト追跡中は学習を続けることができる。例えば手をグーにして認識し、ゆっくり手をパーに変えても追跡し続ける。すごい。学習直後は"Learn Enable"がOnになる。
オブジェクト追跡の場合は、認識中にもLearningの黄色い枠が表示されるのは、"Learn Enable"オプションの影響。
"Auto Save"をOnにすると、電源を切っても学習内容は保持される。
オブジェクト追跡にはOpenTLDが使われている

色認識(Color Recognition)

"Learn Multiple"をOnにすると、複数の色を学習できる。
電源を切っても学習内容は保持される。

オブジェクト認識(Object Recognition)

認識できるオブジェクトは固定20種類なのであまり使い道はなさそう。

タグ認識(Tag Recognition)

サポートしているのはAprilTagというタグ。PRO版ではQRコードにも対応らしいけど、持ってないので未確認。

オブジェクト分類(Object Classification)

まだ試してない。ファームウェアを書き換えが必要。詳しい内容はコミュニティ参照

HuskyLensとSuper:bit(micro:bit)との接続

HuskyLensとmicro:bitとの接続にはI2Cインタフェースで接続できる。
micro:bitから供給する電源だけでは足りないため、micro:bitの拡張ボード「Super:bit」と接続してみた。

HuskyLens側はI2C/UART兼用のインタフェースがあり、自動で切り替わる。
基板上の表記は外側から T R - +。

Label Pin Function Description 付属ケーブルを接続したときの線の色
T SDA serial clock line(*1)
R SCL serial data line(*1)
- GND negative pole of power supply(0V)
+ VCC positive pole of power supply(3.3~5.0V)

*1 公式ページの表だとPin FunctionとDescriptionの内容が矛盾している。
TがSDAでserial data line、RがSCLでserial clock lineが正しい。

Super:bit側にはI2Cインタフェースがある。
基板上の表記は内側から 3.3V SCL(P19) SDA(P20) GND。
SCL,SDAはmicro:bitのP19,P20に直接つながっている。

適合するコネクタは「PHコネクタ 4ピン」。
HuskyLens付属ケーブルではそのまま接続できない。

最初はケーブルを作ろうと思ったけど、意外とPHコネクタ 4ピンの入手が面倒そう。
途方にくれていると、Super:bitの入出力端子(GPIO)にもP19,P20があった。
GPIOの基板上の表記は GND GND SCL SDA 3.3V 3.3V。
けど、これは罠で正しくは、GND GND SDA SCL 3.3V 3.3V。(テスターでMicro:bitとの接続状況を確認)
なので、外側の2番目から黒 緑 青 赤の順になるように接続したら動いた。

f:id:sato_susumu:20200902235058j:plain

適当に作ったHuskyLends用カメラ台

f:id:sato_susumu:20200902235101j:plain

つなげた様子

f:id:sato_susumu:20200902235052j:plain

プログラムの作成

micro:bitから利用するので、MakeCode環境でプログラムを作成してみた。
MakeCodeからの使い方は公式Wiki10.5 MakeCode Introductionに書かれている。

拡張機能には https://github.com/tangjie133/pxt-huskylens を利用する。

MakeCode用のプログラム(python)

Aボタン 内蔵アルゴリズムの切り替え。
認識したら1を表示して、座標をシリアル出力。
認識しなければ、Xを表示。

def on_button_pressed_a():
    global mode
    mode += 1
    if mode == 5:
        mode = 0
    switch_algorithm()
input.on_button_pressed(Button.A, on_button_pressed_a)

def switch_algorithm():
    if mode == 0:
        huskylens.init_mode(protocolAlgorithm.ALGORITHM_FACE_RECOGNITION)
        basic.show_string("F")
    if mode == 1:
        huskylens.init_mode(protocolAlgorithm.ALGORITHM_OBJECT_TRACKING)
        basic.show_string("O")
    if mode == 2:
        huskylens.init_mode(protocolAlgorithm.ALGORITHM_LINE_TRACKING)
        basic.show_string("L")
    if mode == 3:
        huskylens.init_mode(protocolAlgorithm.ALGORITHM_COLOR_RECOGNITION)
        basic.show_string("C")
    if mode == 4:
        huskylens.init_mode(protocolAlgorithm.ALGORITHM_TAG_RECOGNITION)
        basic.show_string("T")
mode = 0
basic.show_icon(IconNames.CHESSBOARD)
huskylens.init_i2c()
mode = 3
switch_algorithm()
huskylens.init_mode(protocolAlgorithm.ALGORITHM_COLOR_RECOGNITION)

def on_forever():
    huskylens.request()
    if huskylens.isAppear_s(HUSKYLENSResultType_t.HUSKYLENS_RESULT_BLOCK):
        if huskylens.is_appear(1, HUSKYLENSResultType_t.HUSKYLENS_RESULT_BLOCK):
            basic.show_number(1)
            serial.write_value("xc", huskylens.reade_box(1, Content1.X_CENTER))
            serial.write_value("yc", huskylens.reade_box(1, Content1.Y_CENTER))
            serial.write_value("w", huskylens.reade_box(1, Content1.WIDTH))
            serial.write_value("h", huskylens.reade_box(1, Content1.HEIGHT))
    elif huskylens.isAppear_s(HUSKYLENSResultType_t.HUSKYLENS_RESULT_ARROW):
        if huskylens.is_appear(1, HUSKYLENSResultType_t.HUSKYLENS_RESULT_ARROW):
            basic.show_number(1)
            serial.write_value("xb", huskylens.reade_arrow(1, Content2.X_ORIGIN))
            serial.write_value("yb", huskylens.reade_arrow(1, Content2.Y_ORIGIN))
            serial.write_value("xe", huskylens.reade_arrow(1, Content2.X_TARGET))
            serial.write_value("ye", huskylens.reade_arrow(1, Content2.Y_TARGET))
    else:
        basic.show_icon(IconNames.NO)
    basic.pause(500)
basic.forever(on_forever)