Dropbox

室内でPIDチューニングBF4.5版、フィルター設定

以前、書いた「室内で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チューニングについては次の記事にいたします。

ドローン搭載のRGB LEDで無限の色を得る

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 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 Pi AI Kit用に画像認識モデルを作る

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環境に導入し更なるモデル変換に備える ]</strong

以下のステップは現在(2025-07-02)、うまく動きません。dockerを利用した方法を新たに紹介しました。以下のリンクを参照してください。

Hailo AIモデル変換環境の構築:Dockerイメージを活用した導入

 

ここからが長い道のりでした。よくわからないまま試行錯誤でいくつかの手順を参考になんとか使える状態にしました。最初、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で実行させることが出来ます。これの詳細については、次の記事に書く予定です。

RunCam Thumb2を試してみた

入手してから随分と時間を経過してやっとまともなテスト飛行を行いました。実は一度飛ばしてはいるのですが、ジャイロデータを取得していなかったため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でもメモリーが足りなくなりそうでした。

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

TinySA UltraでVTXの毒電波を発見しよう

ひょんなことからTinySA Ultraという簡便なスペクトルアナライザーが6GHzまで対応しているらしいことを知りました。これならVTXの電波が見えるのではないかと思い試すことにしました。

詳細な情報はTinySA Wikiにまとまっています。

[ セルフテストとキャリブレーション ]
最初にテストと校正を行えとのことなのでやってみます。

まずは付属しているケーブルで上下のポートを接続します。画面をタップ(今時の静電式ではなく感圧式です)するとメニューが出ますのでCONFIG/SELF TESTを実行します。

続いてLEVEL CALを実行します。

こんなふうに5.34GHzを境に分かれています。CALIBRATE 100kHz to 5.340GHzを実行します。残念ながらVTXの周波数が含まれるCALIBRATE above 5.340GHzの方はTinySA単体では実行できないようです。まぁ精密な測定を行うわけではないので校正せずに進めます。

[ ENABLE ULTRA ]
6GHzまで測定するためにはCONFIG/->MORE/ENABLE ULTRAを有効にします。この時Unlock Codeを聞かれるので4321と入力します。

[ VTXの電波を見る ]
細かい設定はまだ良くわかっていませんが、とりあえずはVTXの電波は見ることが出来ました。

 下のポートに適当なアンテナを接続して周波数を設定しただけです。

トップメニューのFREQUENCYに入ります。STARTとSTOPの周波数を設定します。私はそれぞれ5.6GHzと6GHzにしてみました。

実際の電波を見たところです。F4(5800MHz)が見えています。ピークを検出して周波数を教えてくれるのが便利です。規定値ではひとつの周波数しか教えてもらえないので設定を変更します。トップメニューのMARKERに入ります。私は4 MARKERSにしてみました。

そしてE1, F1, F4の電波を同時に出してみました。

大体良い感じです。残念ながら毒電波を吐くVTXが手元にないので、悪い事例を紹介することはできませんでした。

[ 設定の保管 ]
ここまで行った設定をいつでも呼び出せるように保管しておきます。
トップメニューからPRESET/STORE/STORE 1にVTXという名前で保管しました。

これでいつでも設定が呼び出せます。また’STORE AS STARTUP’で起動時に読み込まれます。

[ SCREEN COPY ]
STORAGE/SAVE CAPTUREで画面のコピーがSDカードにBMPフォーマットで保管できます。いちいち名前を付けないと行けないのがちょっと面倒です。このブログにしようしている画面はWindowsのアプリから取り込んだものです。こちらはPNGフォーマットでした。このアプリ、すぐに接続は切れるし思うように動かないしで今ひとつですので詳しいことは省きます。

[ Water Fall表示 ]
トップメニュー/DISPLAY/WATER FALLをオンにすると時間経過とともに一定強度の信号を受信した記録が残ります。つまり少しだけ時間を遡って電波の状態を調べることが出来ます。

[ おまけ ]
HDZeroのNarrowモードが本当に17MHz帯域幅なのかどうかを調べてみました。

RotorHazardのLAPをTinyViewPlusに送るプラグイン

RotorHazardはシステムとしての作り込みがとても素晴らしくレース運営に力を発揮しますが、すでにTinyViewPlusでレースの運営が確立していることも多いと思います。そういうケースにおいてRotorHazardを補助的に使ってみようという贅沢な試みです。発案はninjaMoonLightさんです。

現在、TinyViewPlusを使用していてARマーカーを読み損なった時の対策としてゲートを通過する時にAR認識と並行して手動でLAP追加を行うという作業を行なっています。これの代わりにRotorHazardが認識したLAPをOSCを介してTinyViewに伝えようというものです。ただし予想される問題としてゲート不通過で近くを通った場合もLAP検知が発生することです。これはやはり審判員による修正が必要になります。

動作テストの様子です。

RotorHazardのプラグインとして作りました。GitHubで公開しましたので以下のリンクをご覧ください。

https://github.com/nkozawa/rh_tinyviewplus

RotorHazardのプラグインの作り方

VTX電波を利用した高機能なFPVレース計測システムであるRotorHazardのプラグインを作るべく調査中です。結局のところ公式ドキュメントを読んでも今ひとつ分かりにくいので、実際に動きそうなプラグインを読んだりRotorHazardのソースを読むのが手っ取り早いです。

タイトルには作り方などと書きましたが、とりあえず最低限の動作可能なコードを示しておくに留めます。何かしらお作法的なことに抜けがあるかもしれませんので予めご了承ください。またプラグインを公開する際にはマニュフェストも書いた方が良いようですが、それもまだ未調査です。

[ 言語と実行環境 ]
プラグインはPython3で記述します。RotorHzard自体がPythonで記述されておりPythonの仮想環境で実行されています。もしpip3で新たなモジュールの導入が必要な場合は、その仮想環境に入ってpip3の実行を行う必要があります。仮想環境に入るにはRotorHazardが導入されているユーザーのホームでsource ./.venv/bin/activateを実行します。ターミナルでsystemctl cat rotorhazardを実行すればRotorHazardがどこに導入されていてどの仮想環境が使われているかは確認できます。

[ プラグインの作成 ]

/home/pi/RotorHazard/src/server/plugins

以下にディレクトリーを作ります。私はテスト用に

mkdir rh_myTestLog

などとしました。
そしてそのディレクトリー下に

__init__.py

というpythonコードを作成します。コードの中身は以下の通りです。

# my first plugin test

import logging
logger = logging.getLogger(__name__)

from eventmanager import Evt

def myTestLog(args):
  node = args['node_index']
  logger.info("My test plugin - LAP (" + str(node) + ")")

def initialize(rhapi):
  rhapi.events.on(Evt.RACE_LAP_RECORDED, myTestLog)

initialize(rhapi)が必須の関数で、ここでは主にトリガーとなるイベントを登録します。この例ではドローンが計測点を通過してラップタイムが記録されたタイミングでmyTestLog(args)という関数が呼ばれるように定義しています。トリガーとなるイベントはeventmanager.pyで定義されていますので、このコードを参照して名前から必要なイベントを探ってみてください。

myTestLog(args)の引数argsにはイベントに対応したデータが渡されます。この例ではargs[‘node_index’]にてラップタイムが記録されたノードの番号を取得しています。そしてそれをログに書き込んでいます。
このRACE_LAP_RECORDEDにどんなデータが付いてくるかはソースコードRHRace.pyに記述されていました。

[ テスト ]
プラグインを書き込んだらsudo systemctl restart rotorhazardの実行あるいはシステムを再起動します。

RotorHazard起動したらログを確認します。正常にプラグインが組み込まれれば以下のようなログが記録されます。

2024-07-06 08:21:47.296: __main__ [INFO] Loaded plugin 'rh_myTestLog'