Dropbox

zoomセミナーアーカイブ

Blackbox解析ツール
https://blackbox.betaflight.com/
https://github.com/bw1129/PIDtoolbox

コザックのBFチューン講座の中で紹介するBlackBoxログデータです。リンクを右クリックして保存してください。

フィルター説明用データ:
https://www.nkozawa.com/blackbox/btfl_flt1.bbl – プロペラ破損
https://www.nkozawa.com/blackbox/btfl_flt2.bbl

PDバランス説明用データ:
PIスライダー0.50/0.55 https://www.nkozawa.com/blackbox/btfl_PI050055.bbl
PIスライダー1.30/1.35 https://www.nkozawa.com/blackbox/btfl_pi130135.bbl
PIスライダー1.60/1.65 https://www.nkozawa.com/blackbox/btfl_pi160165.bbl

スライド: https://www.docswell.com/s/6654543372/KJ48JM-2024-11-25-192028/1

PIDチューンを手持ちの機体全てに行うのは面倒です。機体スペックが同じならPID自体はおそらく同じで良いでしょう。と言うのは多くの人が同意すると思います。私自身は65サイズならフレームやモーター違っても同じPIDで済ませようと思っていました。

ところが、私がBetaFPV Air FrameでカリカリにチューンしたPID設定をWTW Shikoku会長のKanrekiさんがMobula6 2024に適用したら振動が出たとの報告をもらいました。その現象はPIスライダーを小さくしたら快適になったとのことです。これは対策として正しいものと思います。

と言うことで、私もBetaFPV Airフレームで調整したPID値をMobula6 2024フレームに適用してテストをしてみることにしました(FCとモーター、ペラはAirフレームと同じ)。結論を先に言ってしまうと、私のテストでは問題なし。むしろBetaFPV Airフレームより良い結果が出ました。フレーム差に加えて個体差が何かしらあるようですが、機体の特性は似ていると思います。やや緩めのPID設定にすれば65フレーム共通で良いのではないかと言うのが現在の結論です。

このテストが面白かったので、経過を書いて行きます。

[ フィルター設定 ]
いつも通りのフィルター設定でノイズを確認してみます。

普通に飛ばせますし、モーターの加熱もありませんが、Rollのジャイロ信号において200Hzより少し上のところに輝線が認められます。これは横軸のスロットルポジションに関係なく一定の周波数で機体が振動しているものをジャイロが捉えていることを示します。また左から二つ目のグラフにも現れていることからフィルターを通過してPIDループにもこの信号が入力されている事が分かります。

このまま放置しても問題は無いかも知れませんが、対策を施してみます。

これはBlackBox ExplorerでRollのフィルター後の信号を見たところです。これで輝線の中心周波数は219Hzだと言うことが分かります。PIDToolBoxのグラフとは違い横軸が周波数であることに注意してください。

対策として追加したのはジャイロノッチフィルター1です。中心周波数は先のデータに従い219Hzにしました。カットオフ周波数は勘で215Hzとしました。これの意味は215Hzから223Hzあたりの信号を取り除くと言う事です。

見事にノイズが除去されました。

他のMobula6 2024フレームでテストは行なっていないので、これがこのフレームに共通したものなのか、この個体の組み上げ方によるものなのかは分かりません。

[ PID設定はどうだろう ]
飛ばしてみると調子は良いので、大きな問題は無いだろうと思いつつログを調べてみました。

赤字の数値を見てもらうと分かるようにFF,I,D Maxの影響を排除して取得したデータで純粋にPDバランスを見ています。Pitchがほんの少しオーバーシュートしてはいますが、かなり綺麗なグラフになりました。

さらに普通に飛ばす時の設定でも、めちゃくちゃ綺麗なグラフになりました。多分、過去設定した中で一番綺麗です。

と言うことで個体差は考慮の必要はありますが、メーカーが違っても似たような65フレームであれば共通したPID設定が使えそうです。

前回、フィルターの設定が出来ました。いよいよPIDを設定していきます。

まず最初にBetaflight ConfiguratorをExpertモードにしておきます。(上の方に小さく書かれているやつです)

[ 基準値 倍率設定(Master multiplier)の決定 ]
最初に動的ダンピング(Dynamic Damping) D Maxを0にします。この状態で倍率設定(Master Mulitplier)を1より少し小さい値から0.05ずつ上げ、実際に飛ばしてみます。このステップは全くの体感で行います。細かい調整は後から行うので心配は要りません直感を信じて進めます。
倍率設定を上げていくとだんだん反応が良くなります。それがあるところを超えるとまた反応が悪くなったり、モーター音の異常、モーターの加熱が発生します。気持ち良く飛ばせる中で一番高い数値にしておきます。

私の場合は1.25に設定しました。

[ 動的アイドル値(Dynamic Idle) ]

動画中にある表を参考に設定します。65サイズが書いてないですし、プロペラのピッチによる設定値の幅が大きいので、大体のところで100にセットしてみました。

[ PDバランス ]
これからが本番です。最初に技術的な背景を簡単に紹介します。プロポのスティックを操作すると、それがFCで解釈されてYaw/Pitch/Rollのセットポイントという値が作り出させれます。これはその操作を行った時にジャイロが作り出す値の目標となる値です。FCは各モーターを動かしジャイロの出力がセットポイントを追従する様に制御します。その追従の具合を調整することがPIDチューニングです。


「動的ダンピング D Max」, 「スティックレスポンス FF」, 「ドリフト-ふらつき I」を全て0にします。
「トラッキング P&I強度」を0.5から0.05ずつ上げながら20秒ほど飛行しログを取ります。PIDtoolboxでStep Resp Toolで波形を見ます。オーバーシュートしない程度で素早く立ち上がるPの値をRollとPitchでそれぞれ記録します。

これはP&I=1.35にした時の状態です。Rollは良い値が出たところです。Pitchはややオーバーシュートしていますので、もう少し小さな値が良さそうです。
私は、RollはP&I=1.35, P=75、PitchはP&I=1.20, P=70を採用しました。
もし、この調整でスライダーが右端まで行ってしまった場合は最初の「基準値 倍率設定」の数値が足りていないのだと思われます。

「トラッキング P&I強度」スライダーでRollのP値を75にします。これは調べた時の値ですのでP&I=1.35になります。
「ピッチトラッキング」スライダーを調整してPitchのP値を70にします。実際にはスライダー0.9でP=71としました。
これでPDバランスの調整は完了です。

[ ドリフト、ふらつき I 強度 ]
I値は共用範囲が広いのでBetaflightの規定値でも大丈夫かも知れません。大きい方がレスポンスが良くなりますが大きすぎると振動やバウンスバックが出ます。私は今のところ、なんとなくスライダーを1.0にしています。初期値は80/84/80で現在値は135/127/135とだいぶ大きくなっています。もっと小さくすべきかも知れませんが、これでしばらくテストしてみます。

[ Feed forward ]
スライダーではなく右の方にある各値はプリセット/RC_LINKで適切な値を選択することで設定されます。

私の場合はELRS 500Hz, Race, Serial RX, Single cell valueで設定しています。
FFのスライダーはとりあえず1.0にしておきました。

[ 動的ダンピング ]
強度=37,詳細=0に設定します。

[ 動的ダンピング D Max ]
動的ダンピング(D Max)が0の時は上の数値欄にあるD MaxとDerivativeに同じ数値が出ているはずです。これを記録します。「ダンピング D強度」のスライダーを下げ「動的ダンピング D Max」のスライダーを上げてD Maxを記録した値、Derivativeをそれより少し小さい値にします。私の場合は「ダンピング D強度」を0.75, 「動的ダンピング D Max」を1.0にしました。まったり飛行の場合は、もっと差を小さくした方が良いかも知れません。


最終的なPID画面はこんな感じになりました。これでしばらく飛ばしてみようと思います。

以前、書いた「室内でPIDチューニング」のBetaflight 4.5版です。前回と同じくChris Rosser師匠のYouTubeビデオに基づいた内容になっています。細かいところは除いて私が実際に試した部分だけを書いています。詳細については以下のYouTubeをご覧ください。
Betaflight 4.5 PID Tuning
Betaflight 4.5 Filter Tuning

[ PIDtoolbox ]
PIDtoolboxを使用します。ちょっと前までは無料でダウンロード出来たのですが今は月会費を支払ってダウンロードします。一年前くらいの無料時代の物でも今回のPIDチューニングには使用出来ます。
PIDtoolboxの導入方法と基本的な使用方法は以下の記事をご覧ください。
PIDToolbox v0.62
PIDToolbox v0.62を使う
Windows版もMac版も導入方法や使い方は同じです。最新のMacOSでは最初のプログラムの起動が少し面倒になりました。ダウンロードしたプログラムがうまく起動せずゴミ箱に入れますかなどと聞かれます。これを解消するにはターミナルを開いて以下のコマンドを投入しなければなりません。

xattr -c {PIDtoolboxのディレクトリー名に置き換える}/main/PIDtoolbox.app
xattr -c {PIDtoolboxのディレクトリー名に置き換える}/main/blackbox_decode

[ 今回使用した機体 ]
BetaFPV Meteor65 Frame, BetaFPV Air 5in1 FC, BetaFPV 0702-30000KV motor, HQ UltraLight Prop
Betaflight 4.5.1です。
新しいBetaFPV Air FCはBlackBox用に16MBのストレージが搭載されているのでPIDチューンがとても行いやすいです。

[ フィルター設定 ]
前提はRPMフィルターを設定済みであることです。
フィルターは可能な限り軽い方が良いです。軽いというのは設定るフィルターの数を少なく、また設定する周波数を高くすることです。生のGyroデータには沢山のノイズ(機体の本来の動きとは別のもの)が含まれます。それをフィルターで出来る限り取り除いてからPID処理に入れます。問題はフィルターにより必ず遅延が発生することです。その遅延はフィルターが一つ増えれば、その分増えます。また、低い周波数を見極めるのには高い周波数より時間がかかります。

実際のフィルター設定は簡単です。Chris Rosserさんの推奨値でたいていの場合は問題ないと思います。

画面の通りです。Betaflight規定値と違うのは
– ジャイロローパス1 = オフ
– ジャイロローパス2 = 1000Hz (もし基本設定でGyro周波数とPID周波数を同じにしている場合はこれもオフにする)
– 動的ノッチフィルター = オフ (もし機体の固有振動によるノイズ出たら改めて考える)
– D値ローパス1 = 動的/80/110/7/BIQUAD
– D値ローパス2 = オフ
これでモータの異音、加熱、機体の振動がなければ基本的には問題なしです。

RPMフィルターの高調波として3が指定されています。これはモーターの回転から引き起こされる振動とその正数倍の振動を考慮したものです。この高調波がプロペラの羽の数によって違うという予測があります。私は入れていませんが、それを考慮して以下のコマンドをCLIで入力すると良いよとChris Rosserさんは言っています。
3枚羽の場合は、set rpm_filter_weights = 100, 0, 80
2枚羽の場合は、set rpm_filter_weights = 100, 80, 0だそうです。

では、ログを取ってみます。まずはBlackboxページでデータの消去を行います。その後で普通に飛行します。スロットル位置ごとのデータが必要なのでなるべくゆっくりと幅広くスロットルを動かします。20秒ほど飛べば大丈夫です。アクロ、アングルどちらでも構いません。クラッシュ、もしくは着陸したらすぐにDisarmします。
ログファイルを取り出しPIDtoolboxに読み込みSpectral Analyzer/’Freq x Throttle’/Runでグラフが出ます。

で、実際に取ったデータです。これは実は良くない状態です。横軸がスロットルで縦軸はジャイロデータに含まれる周波数成分です。PID演算において本当に欲しい機体の動きは100Hz以下に現れます。それ以上の部分はノイズ(ほとんどは何かしらの理由による機体の振動)です。このデータではスロットル30%以上の部分に太い帯が見えます。これは明らかな異常振動です。

機体を点検したところひとつのペラが曲がっていました。

ベラを交換して再度データを取りました。

今度は大丈夫そうです。一番左側はフィルターをかける前のジャイロデータです。左下から右上に向けて斜めの太い帯が見えます。これはスロットルポジション、すなわちモーターの回転数に比例して周波数が高くなるノイズ群です。これを効果的に取り除くのがRPMフィルターです。左から二つ目のグラフがフィルターをかけた後のデータです。とてもうまくフィルターされているのが分かります。Dtermについては多くは語りませんが、まあこれで良いかなと思います。

長くなるのでPIDチューニングについては次の記事にいたします。

WTW四国ではVTXチャネルによってLEDの色を変えるというレギュレーションの普及を目指しています。VTXチャネルをsmartaudioで変更すると同時にLEDの色を変更するのは合理的ではありますが、レースによってはVTXと関係なく送信機のスイッチで色を変更することも考える必要があるかも知れません。その一つの答えは独立したマイコンチップでLEDを制御する方法でした。ここで原点に戻りBetafligh configuratorのLEDストリップ画面でAUXスイッチを使う方法を再度試してみることにします。

これから書くことは偶然発見しました。多分、公式ドキュメントを読むだけでは分からない方法です。


こんな感じでAUXに3ポジションスイッチを選択しカラーパレットのどれか、例えば14を選びます。そうするとスイッチを中央にすると14番の色になり、スイッチを倒すと14の前後、13か15番の色になります。ここまでは以前から知っていました。送信機のスイッチが足りないので、これをS1で試すことにしました。S1を左端まで回した時と中央、右端まで回した時で3ポジションスイッチと同じ動作にしようという目論見です。

当然のことながらうまく動きます。S1を回していく過程の途中でパレットの色が突然切り替わるところがあるのではないかと想像していました。場所を見極めてみようとゆっくり回すと何故か徐々に変化して行きます。どうも二つのパレットに指定した数値を徐々に変化させているようです。ということで、ここから設定手順です。

[ プロポの設定 ]
S1、S2などをAUXに割り当てます。

モデル設定のMIXESでCH11にS1を割り当てました。Betaflight Configuratorの受信機タプでAUX7がS1を回して変化することを確認しました。

[ Betaflightの設定 ]
まずはワイヤーオーダーの設定が必要です。これについては過去記事を参照願います。
そして最初のスクリーンショットの様に機能=カラー、カラー修正=AUX7にし、カラーパレットのどこか(0と15を除く)を指定します。

次にカラーパレットに割り当てる数値を変更します。カラーパレットをダブルクリックするとHSVの値を指定することが出来ます。

例えば先のステップでカラーパレット14を指定した場合、変更するのは13, 14, 15になります。HSVのうちSは0、Vは255固定です。H(色相)の値をバレット13は0、14は143、15を285にすると赤から紫までの虹のスペクトルに変化させることが出来ます。

色相を一周させる場合はカラーパレット13,14,15のHを0,180,359にします。

カラーパレットの操作は色々と面倒なのでCLIに流し込むものを用意しました。LED数は16個、AUX7を使用、カラーパレットは13,14,15で赤から紫まで虹色の変化を行うものです。

# led
led 0 0,0::CT:14
led 1 1,0::CT:14
led 2 2,0::CT:14
led 3 3,0::CT:14
led 4 4,0::CT:14
led 5 5,0::CT:14
led 6 6,0::CT:14
led 7 7,0::CT:14
led 8 8,0::CT:14
led 9 9,0::CT:14
led 10 10,0::CT:14
led 11 11,0::CT:14
led 12 12,0::CT:14
led 13 13,0::CT:14
led 14 14,0::CT:14
led 15 15,0::CT:14
led 16 0,1::CT:14

# color
color 13 0,0,255
color 14 143,0,255
color 15 285,0,255

# mode_color
mode_color 7 0 10


Raspberry Pi AI Kitをセットアップし自前のモデルを動かしてみるところまでの記録です。
基本的な設定は迷うところはありません。公式ドキュメント通りで大丈夫です。
使用したのはRaspberry Pi OS 64 bit版です。

[ 基本ソフトウェアの導入 ]

ドキュメントに従い、

sudo apt update && sudo apt full-upgrade

次にファームウェアバージョンを確認します。

sudo rpi-eeprom-update

私の場合、ファームウェアのバージョンが最新の物でしたので続くeeprom更新は行いませんでした。
続くドキュメントに従いhailo関連のソフトウェアを導入します。

sudo apt install hailo-all

導入の確認は、

hailortcli fw-control identify

というコマンドで行います。これでAIハードウェアの情報が表示されれば完成です。

[ サンプルプログラム ]

次に実際に動くサンプルプログラムを導入します。いくつかありますが、実際のアプリケーションに応用可能なものとして
Hailo RPi5 Basic Pipelinesを選びました。

git clone https://github.com/hailo-ai/hailo-rpi5-examples.git
cd hailo-rpi5-examples
./install.sh

以上のコマンドで導入は完了です。
実際に動かしてみます。

source setup_env.sh

Pythonの仮想環境に入ります。そして次のコマンドを投入します。

python basic_pipelines/detection.py

これでobject detectionのデモが動きます。デモは24FPSぐらいは出ているようです。

[ 自分で作成したモデルでのテスト ]

python basic_pipelines/detection.py --help

これでコマンドラインオプションを調べ”–input”でテストしたい動画ファイル、”–hef-path”で自作のモデルを指定すれば動きます。ただし、このままだと自分の意図するのと違うラベルが表示されてしまいます。”resource/barcode-labels.json”などを参考に自分のモデルに合ったラベルを含むjsonファイルを作成して”–labels-json”で指定すると良いです。

動画ファイルの代わりに”/dev/video0″などとするとUSBからの画像入力も扱うことが出来ます。これについては、また長い試行錯誤があります。改めて記事にするつもりです。

実際のアプリケーションを作成する場合は、何かしら検出した物体に対し自分の処理を加えたいはずです。その場合でもこのサンプルはそのまま活用できそうです。先の”detection.py”の中に”app_callback()というファンクションがあります。何かしら検出すると、これがよばれるようになっています。中身を見ていくと検出したラベルや大きさが格納されたbboxが見つかります。ここを自分用に変更すれば良さそうです。

Raspberry Pi5にM.2 Hatを介して接続するAi Kitなるものがあります。13TOPS(26TOPSというモデルも有ります)というそこそこの性能でリアルタイムな画像認識ランタイムを実行させることが出来るらしいです。ただし使いこなしには少々の慣れやらKnow-Howの蓄積が必要になります。何時ものように自分自身の覚書として理屈は抜きで上手くいった事例の一つとして書き残すことにしました。まずは独自の学習モデルをAi Kit向けに作るところからです。

Raspberry Pi AI KitはHailoという独自なソフトウェア群で稼働させなければなりません。Yolo向けに作成した学習モデルをそのまま使うことはできません。以下の手順でHailoで使えるモデルに変換しなければなりません。私が試してうまく行った方法を書き残します。

参考にした記事は以下の通りです。
1. Raspberry Pi AI Kitでカスタムモデルを使う方法
2. Tutorial of AI Kit with Raspberry Pi 5 about YOLOv8n object detection
3. hailo_dataflow_compiler_v3.29.0_user_guide.pdf
4. hailo_model_zoo_v2.13.0.pdf
3と4のpdfファイルはHAILO DEVELOPER ZONEより入手します。ユーザー登録が必要です。

[ 手順の流れ ]
– yamlに少し手を入れて従来の手順でyoloで学習を行う
– 出来上がったbest.ptをyolo環境でonnxフォーマットに変更する
– Intel PC上のUbuntuにHailoより提供されているソフトウェア環境でonnxからhefフォーマットのモデルに変換する
といった感じですが、最後のステップは少々苦労します。

[ クラス数80のモデルを作成する ]
まずは学習モデルを以下の記事にあるやり方で作成します。ただ一つだけ相違点があります。
FPVの画像解析してみるぞ、機械学習入門その1
Google Colabを使おう、機械学習入門その2
学習する際に指定するymlファイル内でラベルを80個にします。私の事例では2個のラベルしか使用していません。残りの78個はダミーになります。

# number of classes
nc: 80

# class names
names:
    0: gate
    1: goalgate
    2: dummy1
    3: dummy2
    4: dummy3
    5: dummy4

このようにしてdummyラベルを追加して合計80個にします。あとは通常通り学習を行います。なぜ80なのかはよく分かりません。

[ onnxフォーマットのモデルに変換する ]
出来上がった”best.pt”ファイルをonnxフォーマットに変換する。私はmacOSに導入したUltarytics環境で行いました。以下のコマンドを投入します。

yolo export model=./best.pt imgsz=640 format=onnx opset=11

[ Hailoソフトウェア群をUnuntu環境に導入し更なるモデル変換に備える ]
ここからが長い道のりでした。よくわからないまま試行錯誤でいくつかの手順を参考になんとか使える状態にしました。最初、Intel NUC上のUbuntu 20.04で環境を構築し、次に手順の確認のために再度WSL2(Windows11)で環境を構築しました。WSL2での作業を以下に書き残します。試行錯誤の部分も書きますので、煩雑な内容になってしまいました。

最初にWindows 11のWSL2, Ubuntu 22.04, Python 3.10.6で試してみましたが、Ubuntuが推奨されているバージョンより新しいためか、最後のステップで原因不明の不具合に遭遇しました。以下の手順はWSL2Ubuntu 20.04.6 LTS, Python 3.8.10で行ったものです。

最初に、お約束のsudo apt updateとsudo apt upgardeを実行しておきます。

< venv作成 >

sudo apt install python3.8-vnev
python3 -m venv hailo.env
source hailo.env/bin/activate

< Hailo Dataflow Compilerの導入 >
公式ドキュメントHailo Dataflow Compiler User Guideの”3.1 System Requirements”に従い以下のパッケージを導入。

sudo apt install python3.8-dev
sudo apt install python3.8-distutils
sudo apt install python3-tk
sudo apt install graphviz
sudo apt install libgraphviz-dev

Hailo Developer ZoneのSoftware Downloadsより該当するオプションを選んでDataflow Compilerをダウンロードする。

ダウンロードしたファイルをpipで導入します。

pip install hailo_dataflow_compiler-3.29.0-py3-none-linux_x86_64.whl

沢山、エラーメッセージが出ました。”error: command ‘x86_64-linux-gnu-gcc’ failed: No such file or directory”というエラーに注目して解決策を探り「ubuntsuでpip installしたら「error: command ‘x86_64-linux-gnu-gcc’ failed with exit status 1」と出た」という記事を見つけました。その対処方法に書かれている通り、

sudo apt-get install build-essential libssl-dev libffi-dev python3-dev

を実行し、再度pipでdatacompilerの導入を実行。今度は問題なく終了。
hailo -h
で導入されたことを確認します。要求項目も概ね問題がないことが分かります。

< Model Zooの導入 >
公式ドキュメント”Model Zoo User Guide”の”3.2.2 Manual Installatio”に従い導入します。

git clone https://github.com/hailo-ai/hailo_model_zoo.git
cd hailo_model_zoo; pip install -e .

hailomz -h
で導入されたことを確認します。
次にcoco datasetを導入します。手順は7.2 COC2017にあるobject detection用のものです。

python hailo_model_zoo/datasets/create_coco_tfrecord.py val2017
python hailo_model_zoo/datasets/create_coco_tfrecord.py calib2017

作業ディレクトリーに先のステップで作成したonnxフォーマットのモデルをコピーします。これを3段階で変換していきます。
まずはparseです。

hailomz parse --hw-arch hailo8l --ckpt ./best.onnx yolov10s

これでyolov10s.harファイルが作成されます。オプションで指定するyoloのモデル名は最初にyoloで使用したものに合わせます。逆に言えば”hailo_model_zoo\cfg\postprocess_config”以下に存在するyoloモデルに合わせてモデルの学習を行っておく必要があります。

次はoptimizeです。

hailomz optimize --hw-arch hailo8l --har ./yolov10s.har yolov10s

エラーが出ます。
“FileNotFoundError: Couldn’t find dataset in /home/kozawa/.hailomz/data/models_files/coco/2021-06-18/coco_calib2017.tfrecord. Please refer to docs/DATA.rst.”
どうもcoco datasetのファイルが見つからないようです。調べて見ると”~/.hailomoz/data/models_files/coco/”に”2023-08-03″というフォルダーはありますが、探している”2021-06-18″がありません。シンボリックリンクで2021-06-18が見えるようにして問題は解消しました。

cd ~/.hailomz/data/models_files/coco/
ln -s 2023-08-03 2021-06-18

再度optimizeを実行すると、またエラーです。
Post-process config file isn’t found in /home/kozawa/hailo/hailo.env/lib/python3.8/site-packages/hailo_model_zoo/cfg/alls/generic/../../postprocess_config/yolov10s_nm(h(h(h(hailo.env)
などと言ってます。また何かファイルが足りないようです。これは一番最初に提示した参考記事の1番に答えがありました。

mkdir -p hailo/lib/python3.10/site-packages/hailo_model_zoo/cfg/postprocess_config/
cp hailo_model_zoo/hailo_model_zoo/cfg/postprocess_config/yolov10s_nms_config.json hailo/lib/python3.10/site-packages/hailo_model_zoo/cfg/postprocess_config/yolov10s_nms_config.json

参考にした記事ではyolov8nのファイルになっていますが、私が使用したのはyolov10sです。

これで再度optimizeを実行して問題なく終了しました。

最後はcompileです。

hailomz compile yolov10s --hw-arch hailo8l --har ./yolov10s.har

しばらく待って、棒グラフが画面に表示され始めたら、あとは完了を待つだけです。
yolov10s.hefが出来上がれば、それをRaspberry PI AI Kitで実行させることが出来ます。これの詳細については、次の記事に書く予定です。

入手してから随分と時間を経過してやっとまともなテスト飛行を行いました。実は一度飛ばしてはいるのですが、ジャイロデータを取得していなかったためGyroflowが使えなくてビデオをお蔵入りにしていました。その辺りも踏まえて使用感を書いてみたいと思います。

まずは簡単な印象を列記しておきます。
– 映像は進化して、かなり綺麗になったっぽい。細かい比較はしていません。ざっくりとした印象です。ファイルも大きくなります。
– 電源供給する裏側の小さなコネクターが3PになったのでBECケーブルがRunCam Thumb Proと使いまわせないのが残念。
– RunCam Thumb Proより11gほど重くなり27gになった。小型ドローンにはちょっとだけ厳しい。
– 付属のマウントがGoPro互換ぽくなった。仕方がないのでRunCam Thumb Proと使いまわすためのマウントを作った。モデルなどの情報は記事の最後に書いておきます。

ともあれ、飛行テストの様子です。

設定は以下の通りです。
録画モード : 標準
ビデオクオリティー : 高
解像度 : 4K60FPS
音量 : ノイズ除去
手ぶれ補正 : ジャイロスコープデータ (これを間違うとGyroflow用のデータが得られません)
歪み補正 : オフ
YouTubeの動画はカラーはだいぶ弄っています。音はそのままです。

GyroflowのLens profileはGithubからダウンロードします。Runcam_Thumb2_16by9.jsonというものを使用しました。Motion dataは自分で指定する必要があります。MP4と同時に取得される拡張子gcsvのものです。

使用したカメラマウントはThingiverseで公開しています。
https://www.thingiverse.com/thing:6793100
販売も行っています。
https://dskozak.stores.jp/items/67188c61816e6104457a0cef

前回、手順は確認出来ました。次に探求すべきはどのモデルを使いepochsに何回指定すれば良いのかということです。まずはepcohs=200で各種モデルのテストをしてみようと思い立ちましたが、M2 MacBook AirではいくらGPU使ってもモデルによっては一晩では学習が完了しないということが分かりました。メモリーサイズの関係で生成出来ないモデルもあります。それは諦めるとしても時間がかかってしょうがない。目標が定まれば時間をかけて生成するのも悪くはないですが、試行錯誤のためには素早く終わらせたい。

高価なGPUを買いに走るわけにも行かないのでGoogle Colabを試してみることにしました。最初、無料版で試していましたが、色々と試しているとすぐに使用制限がかかって止まってしまいます。多分、何をするかが決まっていれば無料版だけでも大丈夫かも知れませんが、ストレス無くテストを行うために一番安いPay As You Goの1179円を支払って100コンピューティング単位を買いました。この単位がどれくらいのものかは分かりませんが、一連のテストで消費したのは17単位ほどでしたので、小規模なテストには十分でしょう。速度については申し分ないです。M2 MacBook Airで一晩で終わらない学習が無料で使えるGPUでも10分ほどで完了します。

以下、Colabでの実行例です。

前回、Label StudioからExportしたデータはGoogle Driveにアップロードしました。Colabノートブック内にアップロードする方法もありますが、ノートブックを接続解除(頻繁に行う可能性があります)するたびに消えてしまうので使い勝手が良くありません。

yamlファイルもGoogel Driveに作成します。

# fpvgoals dataset

# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
train: /content/drive/My Drive/fpvgates
val: /content/drive/My Drive/fpvgates

# number of classes
nc: 2

# class names
names: ['gate', 'goalgate']

Google Colabで新規ノートブックを作ります。

「ランタイム/ランタイムの変更」でT4 GPUを選択します。下のスクリーンショットは課金した後なので選択肢が多くなっています。

!pip install ultralyticsを実行しUltralyticsを導入します。

そして以下のコードを貼り付けて実行します。

import time
start = time.time()
print(f'start time: {start}')
from ultralytics import YOLO
model = YOLO('yolov9m.pt')
model.info()
model.train(data='/content/drive/My Drive/fpvgoals.yaml', epochs=200, imgsz=640)
trained = time.time()
print(f'elpased time: {trained-start}')
model.val()
end = time.time()
print(f'end time: {end - start}')

Colabの画面だとこんな感じです。

これでモデルを変えたりepochsに指定する数値を変更して試してみました。元になるデータは最初に用意した56枚の画像です。

ざっくりと目視でトラッキング画像で判断して、重いモデルを使用するほど精度は良くなりそう。またepochsは20回より200回がはるかに良いという当たり前のことが確認できました。精度(誤認識の程度)が目的に適っているかどうかは簡単なプログラムを書いて確認しました(プログラムについてはいずれ紹介します)。その結果、軽めのモデルでも十分な結果が出せそうだということも分かりました。

軽いモデルの利点は学習時間が短い、大きなメモリーを必要としないということは既に分かっていましたが、それに加えて画像認識に要する時間が短いという特性があります。これはリアルタイム処理能力を考えるうえで重要なポイントになります。

ベース・モデル 1フレームの処理時間 fps
yolov9t.pt 45ms 22.2
yolov9s.pt 45ms 22.2
yolov9m.pt 75ms 13.3
yolov9c.pt 86ms 11.6
yolov9e.pt 152ms 6.6
yolo10n.pt 42ms 23.8
yolo10s.pt 64ms 15.6
yolo10m.pt 57ms 17.5
yolo10b.pt 79ms 12.7
yolo10l.pt 90ms 11.1
yolo10x.pt 115ms 8.7

これはM2 MacBook Airでの実行例です。22FPS出ているとまずまず目的に適っている気がします。ということでyolov9s.ptを使用する事にしました。yolov10nの方が速度が出ているのですが、まあ何となく感で決めました。

epochsについては200,300と400で比較してみましたが、見た目での差はほとんど分かりませんでした。result.pngで表示されるグラフについても微妙な差でした。勘でしかありませんが当面はepoch=300で進めてみます。

あとは元となる画像を増やしていって何かしら問題点があるかどうかを試していきたいです。

入門などと書いていますが、解説的なことは一切なく、やってみたら出来た手順の紹介です。

目的はここでは明かしませんが(出来るかどうか分かっていないw)、サンプル画像などから自ずと分かってくることでしょう。

世の中にない目的物を認識するためには自分で学習させないといけません。それを教師付き学習という手法で行います。その手順を示すことが、この記事の主な目的です。

[ Python3で仮想環境を作る ]
とりあえずコマンドラインでpython3が起動することを確認してください。記憶は定かではありませんがMacOS Sonomaでも何かしら作業を行わないと動かないかも知れません。その辺りはググっていただきたいです。WindowsならばPython3の導入が必要ですが、ここでは詳細は省きます。また以下の手順はMacOSを使用しています。

後から簡単に無かったことに出来るのでpythonの仮想環境を作って試すことをお勧めします。ターミナルを開き作業用のディレクトリーにて以下の呪文を唱えれば良いです。

python3 -m venv .yolo
source ./.yolo/bin/activate

.yoloは任意の名前です。最初のピリオドは無くても良いです。

以下の作業は全てこの仮想環境で行なっています。

[ アノテーション ]
学習する元データとなるサンプル画像をなるべく多数用意します。そして一枚一枚の画像の中で目標物の範囲を指定し、それが何者であるかのラベルを付けていく作業です。根気と忍耐が試されるステージとなります。

使用するツールはいくつか存在しますが、最初に試したのはlabelImgというものでした。「YOLOv8自作データセットの学習方法(ローカルでも動かすよ)」という記事が詳しいです。手軽に試すには良いのですが、作業を中断したり、後から画像を追加したりするのが難しいので軽いテストで使用するに留まりました。

現在はLabel Studioというツールを使用しています。作業用のPCに入れてローカルでも使えますが、元々サーバーに入れて使うことを前提に考えられているものなので作業用PCとは別のUbuntuサーバーで動かしています。複数の作業者で一つのプロジェクトを完成させるような作りですので作業を途中で中断したり、データを追加するのも自由自在です。Label Studioの使用方法については「多機能アノテーションツールLabelStudioの使い方を解説」という記事が詳しいです。

とりあえず手順の確認のため50枚強の画像を準備しました。元データはFPVドローンのゴーグルで録画した動画ファイルです。動画に直接アノテーションを行う手法もあるようですが、まずは静止画で行います。動画ファイルから使えそうな画像を切り出した静止画(jpgファイル)をLabel Studioにアップロードし、一枚一枚にラベルを付けていきます。これくらいの枚数ならば大して時間はかかりません。

出来上がったらYOLO形式でExportして作業用のPCにダウンロードします。zipファイルになっているので適当なところに解凍して分かりやすいフォルダー名に変更しておきます。

[ YOLOv8 ]
いよいよ学習モデルの作成です。色々な事例を真似してみましたがUltralyticsのYOLOv8パッケージを使用する方法が簡単で確実です。ドキュメントもよく出来ています。これを利用した事例の紹介も多数ありますがサンプルコードは公式ドキュメントと同じものがほとんどです。このブログ記事も含めて、事例紹介を読むより公式ドキュメントをじっくり読むことをお勧めします。

良くわっていませんが、プログラムとしてはYOLOv8ですが学習の元となるモデルはYOLOv10まで進んでいるようです。自分の目的を達成するために、どのモデルを使い、どれくらい学習させれば良いかは試してみるしかないようです。何はともあれ動かしてみます。

[ M2 MacBook Airでの学習 ]
ultralyticsを導入します。

pip install ultralytics

で、いよいよ学習するためのスクリプトです。

import time
from ultralytics import YOLO

start = time.time()
print(f'start time: {start}')
model = YOLO('yolov9s.pt')
model.info()
model.train(data='fpv.yaml', epochs=10, imgsz=640, device='mps')
trained = time.time()
print(f'elpased time: {trained-start}')
model.val()
end = time.time()
print(f'end time: {end - start}')

yolov9s.ptというのが元になるモデルで、何かしらのラベルが付いた学習済みモデルになっています。ここにyolov9s.yamlというファイルを指定して学習させると、全くのゼロからの学習になるらしいですが、ptファイルを使った方が速く結果が出るように思えます。yolov9s.ptは比較的軽いモデルです。目的や学習環境に合わせて、ここを変えます。モデル毎の特徴についてはYOLOのドキュメントを見てください。YOLOv9YOLOv10のリンクを貼っておきます。

epochs=10というのは学習を繰り返す回数です。お試しなので少なめにしておきます。device=’mps’はAppleのM2チップのGPUを使用するためのおまじないです。device=を省略するとMacBookの場合はCPUによる稼働になります。NVIDIAのGPUが実装されている環境ではdeviceの指定がなくても自動的にGPUを使用します。

fpv.yamlというファイルでラベルを付けたデータの置き場所を指定します。内容は以下の通りです。

# fpvgoals dataset

# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
train: /Users/kozak/Downloads/fpvgates
val: /Users/kozak/Downloads/fpvgates

# number of classes
nc: 2

# class names
names: ['gate','goalgate']

trainとvalの所で指定しているのはLabel StudioからExportしたYOLO形式のzipファイルを展開したフォルダーです。ncは作成したラベルの数に合わせます。namesにはラベル名を書いておきます。

これで先のスクリプトを走らせれば自前の学習済みデータが出来上がります。結果は’runs/detect/train{n}/’ ({n}は数字と置き換えて読んでください)以下に入ります。学習結果を示す各種データがありますが、あまり良くは研究していないです。result.pngを見比べるとepochによる繰り返しが適当な回数になったかどうかが分かるような気もします。出来上がったモデルはweights/bets.ptです。これを使って画像認識を行います。

[ 動画に画像認識をかける ]
いきなりスクリプトを提示します。

import cv2
import time
from ultralytics import YOLO
model = YOLO("./runs/detect/train13/weights/best.pt")
model.to('mps')

video_path = "../video/test2.mov"
cap = cv2.VideoCapture(video_path)
t1 = time.time()
while cap.isOpened():
  success, frame = cap.read()
  if success:
    results = model.track(frame, persist=True)
    annotated_frame = results[0].plot()
    cv2.imshow("FPV Tracking", annotated_frame)
    t2 = time.time()
    print("elapsed: " + str(t2-t1))
    t1 = t2
    if cv2.waitKey(1) & 0xFF == ord("q"):
      break
  else:
    break
cap.release()
cv2.destroyAllWindows()

最初の方にあるbest.ptが自分で生成したモデルです。その直後にある’model.to(‘mps’)はApple M2チップのGPUを使用するためのおまじないです。
“../video/test2.mov”で用意した動画に対して画像認識を行います。単純なトラッキングならば動画を直接YOLOに渡してしまっても良いのですが、画像認識の結果(resultsに格納される各種データ)を後からプログラムで処理するためにOpenCV2にて画像をフレームごとに分解して解析を行うようにしています。

下の例は、もうちょっと深く学習させてからの例ですが、様子的には同じものが出てくきます。

少しテストしてみるとepochs=10では足りなくて200回か300回回した方が良いと分かります。問題は処理時間でM2 MacBook Airでは一晩走らせても終わらないということもあります。あと16GBメモリーのMBAでもメモリーが足りなくなりそうでした。

ということで、探求は次回に続きます。