Tag Archives: Milk-V

Milk-V Duo256で ねこカメラを作る。~2~

前回、Duo256を使って庭にやってくる猫を自動認識し、レリーズ端子に接続した外部デジカメで撮影する事が出来ました。
でも下の写真の様にDuo256と一眼レフを並べるという、面倒な方法になっています。

でもね・・・

わざわざ一眼レフと接続しなくても、Duo256ひとつで撮影まで済ませたいですよね。
Duo256の開発環境はTDL-SDKというライブラリ群を使って行います。これを使ってカメラで撮影するとVIDEO_FRAME_Sという構造体に画像データが入るっぽいので、ここから画像を取り出してファイルに残せば済みそうですが、ドキュメントを見たり色々試したりしても、データを取り出す方法が見つかりません。
またDuo256にはOpenCV mobileというライブラリ群もリリースされていて、こちらを使って撮影する事もできるのですが、今度はYOLOに渡す為に画像をVIDEO_FRAME_Sに入れる方法が分かりません。
ならばこれらのライブラリ使わずにやろうとすると、今度はハードウェアにアクセスする方法が情報不足となるのです。
という状態なので、どなたか分かる方、教えてください。

で、また苦し紛れの対策その2

OpenCV mobileで静止画を撮影するサンプルプログラムと、静止画をYOLOで判定するサンプルプログラムが公開されているので、これらを改造して一旦画像ファイルに書き出したファイルをYOLOで判定する方法でやってみます。
でも毎回SDカードに書くとすぐにFLASHメモリがヤラれそうですよね。
ならばRAMディスクなら問題ないだろな・・・と思ってdfコマンドを実行したところ、/tmpはtmpfsデバイス(≒RAMディスク)をマウントしていました。
という事は/tmpの下に画像ファイルを置けばFDカードに悪影響は無いでしょう。

ではまずyolov5のサンプルプログラムを元に、猫、犬、カップ(デバッグ用)の検出有無を返すプログラムをnekoyoloという名前で作成しました。

次にopencv-mobileサンプルプログラムを元にしてnekoshotというプログラムを作成しました。こちらは静止画を撮影して/tmp下にjpegファイルを作成した後、上のnekoyoloを実行して戻り値が「検出あり」ならその画像を改めてSDカードに書き出す・・という動作を繰り返します。なおSDカードに書き出すファイル名は重複しない様に連番を付けておきます。

図にすると以下の感じ・・・


nekoyoloプロセスはその都度生成しては消えるので、起動のオーバーヘッドが加わりますが、今のところ2秒周期程度で繰り返しているのでまあ良しとします。

動かしてみる。

ではまた窓ガラスに貼り付けて動作させてみます。
前回の様に横に一眼レフを置く必要が無いので設置は簡単です。

Duo256とPCの間はUSB-NCMで接続しているので、動作しながらも下図の様にWinSCPで様子を見る事ができます。
下図の5つの画像ファイルの内、最初の3つは例によって動作確認用のカップの画像で、最後の2つには猫が写った様です。
なおDuo256内にはバッテリ駆動の時計は無いのでファイルの更新時刻はメチャクチャです。(USB-NCMではなくEthernet接続にすればNTPで時刻を合わせられる気がする)

早速ファイルを開いてみると・・・お、いるいる。

そしてもう一枚。

前回撮れた2枚の写真とは違う猫に見えますね。
もう暫く動作させてみましょう・・・

そして後日もう一枚。走ってる走ってる。

上のと同じ猫かもしれませんがハッキリ見えません。

・・・という事でDuo256単体で猫検出&撮影ができる様になりました。

こうなるとPCとは接続せずにモバイルバッテリーから電源を入れて放置するだけで撮影したいです。これには電源を入れたら勝手にnekoshotが起動する必要があります。
普通のLinuxだと/etc/rc3.dの下に設定ファイルを置いたりしますが、Duo256の場合/mnt/system/auto.shが起動時に実行される様なので下記の様に記載しました。
なおnekosyotとnekoyoloの実行ファイルは/root//workの下に置いています。
またLD_LIBRARY_PATHを設定しておかないとyoloがライブラリを見つけられませんでした。

プログラム

今回のプログラムです。
/data/nekoshot.tar.gz
/data/nekoyolo.tar.gz

コンパイル済バイナリも含んでいますが、再コンパイルする場合は前回のライブラリに加えてopencv mobileライブラリが必要です。インストール方法は本家の下記ページに記載があります。
https://milkv.io/docs/duo/resources/opencv-mobile

ライブラリの配置は下記前提にしています(これと違う場合はMakefileを要修正)。

~/milkv
  ├duo-examples
  ├cvitek-tdl-sdk-sg200x
  ├opencv-mobile-4.10.0-milkv-duo
  ├host-tools
  └myproj
   ├nekoshot
   ├ ├Makefile
   ├ └nekoshot.cpp
   └nekoyolo
    ├Makefile
    └nekoyolo.cpp

Milk-V Duo256で ねこカメラを作る。

前回に続きMilk-V Duo256をいじくっている話の続きです。

Duo256にはAIを処理する為のTPU(Tensor Processing Unit)が載っています。
前回書いた様に、トラ技2024年12月号にはこれを使ったサンプルの実行例が2件載っていたので、これに従い下記の動作を確認する事が出来ました。

  • 顔検出
    カメラの動作テストに含まれている。PC側でVLCを実行して動画をモニターでき、人間の顔を検出すると枠で囲んで表示する。
  • YOLOv5
    写真ファイルに対して物体検出&分類を実行し結果をテキストで表示する。

動画でYOLOv5を動かしてみる。

で、この先です。
YOLOを動画でも動かせるのかなーと思って色々調べたところ、開発ツールのcvitek-tdl-sdk-sg200xの下、sample/cvi_tdl ディレクトリにsample_vi_od.cというソースコードが付属していて、これをコンパイルすれば実行できそうな感じです。
それにはPC上でクロスコンパイルする必要があるのでWSL(Windows Subsystem for Linux)上で、下記ページに従い実行しました。

https://milkv.io/docs/duo/application-development/tdl-sdk/tdl-sdk-introduction

そしてコンパイルしたファイルをscpでDuo256に転送し、実行権も付けておきます。

このプログラムを実行するにはモデル名とモデルパスの2つの引数が必要です。
モデルパスは学習済AIモデルへのファイルパスで、モデル名はAIモデルに応じた動作をプログラムに伝える為の名前です。なのですが実際に何を指定したらいいのか、ズバッと書いた説明が見つかりません。
そこで引数無しで実行した時に表示されるUsageにモデル名一覧が書いてあったので、この中から”yolov3″で試してみます。そして学習済みモデルはhttps://github.com/milkv-duo/tdl-models/tree/milkv/cv181xにあったのをダウンロードしてきました。これを~/tdl-models/cv181x下に置いてこの中にあるyolov3.cvimodelを指定してみます。

こんな感じで・・・
./sample_vi_od yolov3 /root/tdl_models/cv181x/yolov3.cvimodel

しかしOut of memoryとか出てきて動作しません。

そこでモデル名に指定した引数がどう扱われるのかソースをみていくと、get_od_model_info()という関数に渡していました。更にこの関数を辿っていくとUsageには含まれていなかったYOLOv5も動作する様な記述があります。
という事で以下のコマンドラインでそれらしき動作を開始しました。
(コマンドが長ったらしくなるのが嫌なのでAIモデルは同じディレクトリにコピー済です)

./sample_vi_od yolo5 yolo5_s.cvimodel

でも下記の様にyolo5_mの方が認識結果が良い様です。

./sample_vi_od yolo5 yolo5_m.cvimodel

VLCでモニターすると・・

おお!それっぽく検出していますね。でも検出した物が何なのかを表示していないです。

コマンド画面上でも下記の様に何個の物体を見つけたかという数字は出ていますが、これが何なのかは表示していません。

ちょっといじくる。

という事で更にソースコード(sample_vi_od.c)を見てみます。
どうやらこのプログラムの中ではビデオデータを受け取るために二つのスレッドを生成し、一方はコマンド画面への結果表示、もう一方は検出した物体を囲む枠と文字情報を生成して映像に重ねた上でRTSPに流す(これをVLCで受けて表示している)仕組みになっている様です。

そして映像に枠を書く方の関数run_venc()の中を見ていくと、68行あたりに下の記述がありました。

sprintf(name, "%s: %.2f", stObjMeta.info[oid].name, stObjMeta.info[oid].bbox.score)

この行ではstObjMeta.info[oid].nameに物体名が入っている事を期待してそうですが表示されていません。
そこでstObjMeta.info[oid].classsというメンバー変数を表示させてみるとクラス番号が入っていました。YOLOのクラス番号は下表の様に80種の物体と番号が対応していて、クラス番号が15なら猫、16なら犬となります。

ならばこの番号を元に名前に変換してやれば良さそうです。
そこで提供された開発環境内のヘッダファイルにこの表に相当するコードがあるかと探しましたが見当たりません。
仕方ないので新たにyolov5class.hという名前のファイルを作ってこの中に記述し、これをソースにインクルードした上で先ほどの行を下の様に変更してみます。

sprintf(name, "%s: %.2f", yolov5class[stObjMeta.info[oid].classes], stObjMeta.info[oid].bbox.score);

すると・・・

いいんじゃないでしょうか。

そしてテキスト表示スレッド内のrun_tdl_thread()にも変更を加え、何を見つけたのを表示させました。

ねこカメラを作りたい。

という事でここからが本題です。我が家の庭はよく猫が通っていくので、猫を検出したら写真を残す「ねこカメラ」を作ってみたいと思います。

上記の通りYOLOv5は猫を見つけると15番が返ってきます。その時に画像を保存すれば出来上がり・・・と簡単に考えていたのですが画像をファイルに落とす方法が判らずハマりました。VIDEO_FRAME_Sという構造体に画像データが入っていそうなのですが取り出せないのです。

この状態だと進まないので、とりあえずは苦し紛れの策で対処したいと思います。
上記のテキスト表示スレッドで猫を示す15番を検出するとGPIO(G15)にパルスを一発出して、これを外部デジカメのレリーズ端子に入れて撮影するのです。
パルスを一発出した後の5秒間は出力無しにしています。またパルスと一緒にLEDも点灯して動作が分かる様にしました。

使ったカメラはちょっと古いCanonのデジタル一眼レフです。このカメラのレリーズ端子は2.5mmの3極ミニプラグで、シャッター、フォーカス、GNDの3端子があり、シャッター端子とGND端子を短絡するとシャッターが切られる仕組みです。

ではこんな回路で・・・

フォーカス回路も含めていますが自動でピントを合わせると時間がかかるので、マニュアルでピントを合わせておく事にします。 Duo256のGPIO端子の内、G15がシャッター、G14がフォーカスで、念のためにフォトカプラを経由にしました。

なお2.5mmのミニプラグが無かったので3.5mmへの変換ケーブルを急遽Amazonで購入。

そしてユニバーサル基板に搭載してハードは完成。

カメラは箱に入れ、内側をスプレー缶で黒に塗りました。Duo256本体は箱の裏側にテープで貼り付けています。

しかしこんなラズピコサイズのボード1枚で物体検出だの分類だのが出来てしまうというの、オドロキですね。。。

動作実験

2つのカメラを庭に向けて設置し、画角がほぼ一致する様に調整します。
そしてデジタル一眼レフはマニュアルでフォーカスを合わせておきました。

なおテスト用としてカップにも反応する様にしておいたので試してみます。
ちゃんとデジカメのシャッターが下りて撮影されましたね。

あとは猫が通るまで放置します。。。

結果

夕方の終了直前、お隣との塀の上を2匹で歩いている姿が撮れていました。
お隣が写っているままだとマズいのでぼかしを入れています。

翌日はカメラを下に向けて隣家が写らない角度に。。。
今回も一枚撮れていましたがマニュアルフォーカスをミスってピンボケになりました。

という事で・・・

最低限の状態で動いたという感じです。
でもやっぱりカメラ2台体制ではなくDuo256だけで完結したいですよね。。。
もうちょっとドキュメントを読んでみます。

参考

今回のプログラムはこれ→/data/nekocam.tar.gz

再コンパイルする場合は以下をインストールしておく必要があります。

これらのファイルが以下の配置にある前提でMakefileを作成しているので、異なる場合は修正が必要です。

~/milkv
  ├duo-examples
  ├cvitek-tdl-sdk-sg200x
  ├host-tools
  └myproj
   ├Makefile
   ├yolov5class.h
   └nekocam.c

Milk-V Duo256

昨年末のトラ技(2024年12月号)に載っていたMilk-V Duo256というマイコンボードが面白そうだったので衝動買いしました。動かしてみてハマった箇所もあったので覚え書き。

まずこんな箱に入ってきました。

本体。基板の色はランダムらしいですが届いたのはごく普通の基板色。
またピンヘッダが付属していそうな雰囲気ですが付いてませんでした。

こちらが開発ボード。本体基板を載せるとイーサーネット接続ができます。

そしてカメラ。フレキシブルケーブルが2本付属しているのは安心ですね。

動かしてみる。

トラ技の記事通りMicro-SDカードにシステムを書き込みました。
記事で分かりにくい部分は本家の説明ページを見ると良いです。

電源を入れる前にPCと接続しますが、基板に載っているUSB端子は良くあるUSB-Serial変換による接続ではありません。ここもトラ技記事に従って開発基板のシリアル端子に外付けUSB-Serial回路を接続しました。
写真のFRISKケースが外付けUSB-Serial回路で、中にFT-232RLが入っています。

※なお最新システムでは初期状態でUSB-NCMという仕組みが有効になっており、USBだけでも接続できる様になっています。下に記載の「ハマったところ」参照。

立ち上げると数秒でrootのプロンプトが出ました。

その後トラ技記事通りYOLO5の実行は問題なし。
そしてカメラとイーサネットケーブルを接続して顔認識を実行しようとした際、一瞬「camera-test.sh」がどこにあるんだろうと思いましたがパスが通してあったので、このまま実行すればOKでした。VLCで接続すると映像が表示され、顔を認識すると回りに四角枠が現れます。

ハマったところ

USB-NCM

最新システムだと初期状態でUSB-NCMが有効になっているので、PCとはUSBケーブル経由でTCP/IP接続できます。。。なのですが、なかなかうまくいかずハマりました。

まず開発ボードのUSB端子は電源入力だけなので本体ボード側のUSB端子にケーブルを挿す必要があります。
そしてWindowsのデバイスマネージャを見ながら本体にケーブルを挿しても「不明なUSBデバイス(デバイス記述子要求の失敗)」となって正しく認識されません。

分かったことは本体基板を開発ボードに載せているとごく稀にしか接続が成功せず、開発ボードは使わず本体基板だけの状態で接続すると成功する様になりました。

(この症状、ウチだけでしょうか?)


結局色々とイジる間はイーサーネットで接続するよりもUSBの方が便利なので開発基板無しで使っています。TCP/IPで接続するため、電源もSSHもVLCもUSBケーブル一本で済むのです。

rootパーティション拡張

32GBのSDカードを挿しているのにデフォルトではルートパーティションは752MBしか確保されておらず、残りの殆どが未使用状態になっていて、このままイジっていると直ぐに満杯になってしまいました。そこで本家のセットアップ説明に従いパーティションを拡張しました。
なお本家ページの説明ではパーティション番号3がルートパーティションでしたが、ウチのは2だったので読み替えて設定しました。
※説明通りにやれば既存のファイルは消えずにパーティションを拡張できています(でも責任は持てないです)。

という事で・・・

色々とイジくっています。