Dropbox

WTW四国のドローンレース集計システム実装例

昨年のNaka Drone Racing 2024以来育ててきたGoogle Sheetによるレース集計システムを紹介します。まだまだ未完成ですが、いくつかのレースを経て役に立つものになりつつあります。またTinyViewPlus v0.9.33 beta6の通知機能を利用しリアルタイムな更新も可能となりました。

その概要とプログラム全てを公開いたします。使い方や細かい動作までは説明しきれないので、勝手に参考にしてもらったり、使える部分は再利用をしてください。という放任主義です、悪しからず。

[ システムの概要 ]
WTW Tokushimaのドローン部長による場内PAおよび配信システムの一部であるTinyViewPlusと集計操作用のMacBook Airを連携して行います。

流行っているのでChatGPTに書いてもらった絵です。何となくな感じであまり正確にシステムを表現しているとは言えません。

[ oscrcv.py ]
oscrcv.pyスクリプトはTinyViewPlusからのOSCプロトコルによる通知で受信したデータをGoogle SheetにPOSTします。データの受信を確実にするためにoscrcv.pyはTinyViewPlusが稼働しているWindows PC上で稼働するようにしています。また予選、決勝などのレース毎(あるいはGoogle Sheetの切り替え)にoscrcv.pyの起動パラメータを指定して立ち上げ直す必要がありますので、MacBook AirからsshでTinyViewPlusのPCにログインして行なっています。

# oscrcv.py
import argparse
import math
from pythonosc import dispatcher
from pythonosc import osc_server
import json
import requests
import time
import datetime

url = ""
url1 = "https://script.google.com/macros/s/AKfycbzDg0hHdgDlE_qYEngvYBBqYDummyssJ_sA_fmstVcXgyyx-KMem32_lbSoC8gdAhgg/exec"
url2 = "https://script.google.com/macros/s/AKfycbw7_T9Z4eWVWbybGm2IhXsazDummyRVwOjrQ_OcuowXC2yV-onDk0ppOxefZdZNQHQh/exec"

sheetname = ""
sh1 = "練習"
sh2 = "予選ヒート1,2"
sh3 = "予選ヒート3,4"
sh4 = "勝ち上がり戦入力"

def timeformat(seconds):
    td = datetime.timedelta(seconds=seconds)
    minutes, seconds = divmod(td.total_seconds(), 60)
    return f"{int(minutes):02d}:{seconds:05.2f}"

def oschandle(addr, *args):
    global url
    global sheetname
    print(addr)
    print(args[0])
    print("")
    timestamp = time.strftime('%Y/%m/%d %H:%M:%S')

    args3 = args4 = ""
    if (len(args) > 3):
        if (isinstance(args[3], float)):
            args3 =  "{:05.2f}".format(args[3])
        else:
            args3 =  args[3]
    if (len(args) > 4):
        if (isinstance(args[4], float)):
            args4 =  timeformat(args[4])
        else:
            args4 =  args[4]

    data = {
        "timestamp" : timestamp,
	    "addr" : addr,
	    "arg0" : args[0],
        "arg1" : args[1] if (len(args) > 1) else "",
        "arg2" : args[2] if (len(args) > 2) else "",
        "arg3" : args3,
        "arg4" : args4,
        "sheetName" : sheetname
    }

    print(json.dumps(data))
    # json.dumpでデータをJSON形式として扱う
    r = requests.post(url, data=json.dumps(data))
    print(r)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--ip",
                        default="", help="The ip to listen on")
    parser.add_argument("--port",
                        type=int, default=4001, help="The port to listen on")
    parser.add_argument("--url",
                        default="1", help="1 = Sprint, 2 = Endurance")
    parser.add_argument("--sheet",
                        default="1", help="1 = 練習, 2 = 予選ヒート1,2, 3 = 予選ヒート3,4, 4 = 勝ち上がり戦入力")
    args = parser.parse_args()

    if (args.url == "1"):
        url = url1
    else:
        url = url2

    if (args.sheet == "1"):
        sheetname = sh1
    elif (args.sheet == "2"):
        sheetname = sh2
    elif (args.sheet == "3"):
        sheetname = sh3
    else:
        sheetname = sh4

    print("URL: ", args.url)
    print("Sheetname: ", sheetname)

    dispatcher = dispatcher.Dispatcher()
    dispatcher.map("/*", oschandle)

    server = osc_server.ThreadingOSCUDPServer(
        (args.ip, args.port), dispatcher)
    print("Serving on {}".format(server.server_address))
    server.serve_forever()

[ Google Sheetでの処理 ]
oscrcv.pyからのPOSTはGASで受信します。送られてくるデータは2種類あります、一つはLapデータでリアルタイムなレース状況の更新に使います。

もう一つはレース結果で、レースの集計に使います。

集計作業は結果入力シートにデータを集積し集計プログラムシートにあるボタンからGASを起動して順位を別のシートに記入します。詳細は書きませんが、勝ち上がり戦の組み合わせまでは自動で行います。勝ち上がり戦の集計以降は手作業で行なっています。
予選の順位は周回数が多い方を上位とします。同一周回数の場合は合計時間が短い方が上位となります。全く同じ成績であった場合の処理は入っていませんのでデータの目視確認が必要となります。

WTWShikokuDroneRaceWorksheet
このワークシートはRead Onlyで共用しています。ご自身のGoogl DriveにコピーするとGASの内容も読めるようになります。

TinyViewPlus->Python Script->Google Sheetへの連携の詳細は別記事「Tiny View Plusの外部通知機能を試す」に書いていますので参照願います。

[ ディスク共用 ]
OSCのデータによる集計に問題があった時のためにTinyViewPlusのResultフォルダーをMacBook Airからアクセスできるように共用しています。Google SheetのGASは集計データのファイルからの読み込みにも対応しています。この部分はninjaMoonLightさんが書かれたものです。

[ TinyView Plusのバイロット名 ]
集計作業とは別ですがWindows上のTinyView Plusのカメララベル(パイロット名)のセットもMacBook Airから行なっています。使用しているプログラムはGithubにて公開しています。
https://github.com/nkozawa/tvpilotnames

以上です。まだ完全自動とは言えませんが、そこそこ役に立つものになりました。また進歩があれば何かの形で公開したいと思います。

Mac Mini M4とMacBook Air M2で画像認識モデル生成能力比較

メモリーたっぷりのMac Miniと普通のAirの比較です。M2とM4で処理速度はいくらかは違いますが、それよりもメモリーサイズが重要であることを実際に確認してみた記録です。テストに使用したのはMacBook Air M2 16GBモデルとMac Mini M4 32GBモデルです。Python関連のバージョンは少し異なりますが、スクリプトは同じで画像認識モデルの作成はUltralytics YOLOv10を使いました。

画像認識モデルの生成で重要なのはVRAMサイズです。より重いモデルの生成には、より大きなVRAMが必要となります。Apple Siliconは単一のメモリーの一部をGPUが使用するVRAMとして必要に応じて割り当てるので、柔軟な対応が可能です。

テスト用に用意したのはイメージファイルを57個でラベル数は2です。これに対しYOLOv10の異なるモデルで作成を行いました。epochは20です。

YOLOv10モデル名 Mac Miniでの処理時間 MacBook Air
YOLOv10n 2分14.55秒 2分58.25秒
YOLOv10s 3分7.91秒 4分35.2秒
YOLOv10m 5分19.98秒 失敗
YOLOv10b 7分57.4秒 失敗
YOLOv10l 44分30.05秒 失敗
YOLOv10x 113分2.03秒 失敗

– M2よりM4は少し速い。
– 16GBのMacBook Airでは軽量モデルであるYOLOv10nとYOLOV10sしか生成できなかった。
– 最重量モデルYOLOv10xでは32GBのほとんどがVRAMとして使われていた。
– 実際のモデル生成ではepochs=200以上を指定するので重いモデルのYOLOv10lとYOLOv10xの生成は実用的とは言えない。

YOLOv10bまではMac Miniでモデル生成が出来るのは大きな収穫です。それより重いモデルの生成はやはりGoogle Colabを使用するしかありません。それも無料で使えるGPUではVRAMサイズが足りずYOLOv10lとYOLOv10xの生成には有料(安いです)のGPU A100などを使用します。その重いモデルもテスト的にMac Miniで生成できるのはとてもありがたいことです。

[ おまけ ]
動画ファイルに対する画像認識の速度テストも行いました。使用した動画は720×480 60FPSのものです。モデルはYOLOv10sを使用しています。自前のスクリプトで計算したところ、MacBook Airではばらつきがありますが18FPSから20FPSくらいでした。Mac Miniは少しだけ速く22FPSから23FPSでした。どちらも実時間にはだいぶ足りない状態でした。

Tiny View Plusの外部通知機能を試す

Tiny View Plus v0.9.33より外部への通知機能が搭載されました。この機能はWTW香川からのリクエストに作者の浅野さんが答えてくださったものです。本来の目的はラップ毎にLEDを点灯させるなどのレースの演出に使うことでした。

通知のされるのはレースの開始、終了、ラップなどです。詳細はドキュメントをご覧ください。
ラップの通知にはラップ数とタイムの情報も含まれているので、これを利用して集計も出来そうなのでテストをしています。

テストの様子です。右側のラップ数をグラフ的に表示するアイデアはninjaMoonLightさんから頂きました。

[ 私のテスト環境においての情報の流れ ]
– Windows上のTinyViewPlusからOSCプロトコルで通知を発信。
– macOS上のPythonで受信し、データをGoogle SheetにPOSTする。
– Google ScriptでPOSTを受けてSheetに書き込む。

ここでは、一番基本となる部分だけを紹介します。

[ Google Sheet側の設定 ]
– Googleドライブで新規のスプレッドシートを作ります。
– “拡張機能”から”Apps Script”を開きます。
– スクリプトは以下のようになります。

const spredSheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spredSheet.getSheetByName('シート1');

function doPost(e) {
  var data = JSON.parse(e.postData.getDataAsString());
  sheet.appendRow([data.timestamp, data.addr, data.arg0, data.arg1, data.arg2]);
}

– 左側のメニューの”サービス”で”Google Sheets API”を追加します。

– 上の方にある”デプロイ”から”新しいデプロイ”を実行します。種類の選択は”ウェブアプリ”にします。アクセスできるユーザーを全員にしておきます。

– デプロイ時にアクセスの確認が出ます。”アクセスの承認”を押します。
後は、いつもの手順ですが、少々ややこしいです。画面の流れだけ示しておきます。

アカウントを選択

Advanced(詳細)をクリック

Goto [プロジェクト名]をクリック

Allowをクリック

– 完了するとウェブアプリのURLが払い出されますのでコピーしておきます。URLを知る人は誰でもPOST出来るので注意が必要です。

– スクリプトを変更した時は、”デプロイを管理”からバージョンを更新します。URLは変わりません。

[ パソコン側の設定 ]
Python3が動けば、どんなパソコンでも大丈夫なはずです。
OSCのためのライブラリーが必要です。
pip install python-osc
プログラムは以下の通りです。url=のところに先にコピーしたウェブアプリのURLを入れます。

from pythonosc import dispatcher
from pythonosc import osc_server

import json
import requests
import time

url = "https://script.google.com/macros/s/xxxxx/exec"
ip = ""
port = 4001

def oschandle(addr, *args):
  print(addr)
  print(*args)
  timestamp = time.strftime('%Y/%m/%d %H:%M:%S')

  data = {
    "timestamp" : timestamp,
    "addr" : addr,
    "arg0" : args[0],
    "arg1" : args[1] if (len(args) > 1) else "",
    "arg2" : args[2] if (len(args) > 2) else ""
  }
  r = requests.post(url, data=json.dumps(data))
  print(r)

if __name__ == "__main__":

  dispatcher = dispatcher.Dispatcher()
  dispatcher.map("/*", oschandle)

  server = osc_server.ThreadingOSCUDPServer((ip, port), dispatcher)
  print("Serving on {}".format(server.server_address))
  server.serve_forever()

[ Tiny View Plusの設定 ]
最新のTiny View Plusを導入します。記事を作成時の最新版はv0.9.33-beta3です。
ドキュメントに書かれているように一度Tiny View Plusを起動後に作成されるdata/settings.xmlを編集してOSC通知の送信先IP Addressを変更します。

<oscMonitor>
    <enabled>1</enabled>
    <host>192.168.1.255</host>
    <port>4001</port>
</oscMonitor>

IP Addressは私はサブネットに対するブロードキャストにしました。受信するパソコンのIP Addressを指定すべきかも知れませんが、サブネット内の複数の機器で同時に情報を受信できることを狙ってブロードキャストIP Addressを試しています。
IP Addressの設定後にTiny View Plusを起動します。

これで準備完了です。Pythonのプログラムを起動し、Tiny View Plusでレースの開始やラップを記録し、データがGoogleシートに記録されれば成功です。ここまでくれば後は、ゴリゴリとGASを書いていけば集計作業の省力化が可能と思います。ただしOSCはUDPで送られてきますので、データが必ず受信されるとは限りませんのでデータが欠損した場合のことは考慮しておく必要があります。また、OSの実装にもよると思いますがデータの処理される順序が入れ替わることもありますので注意が必要です。

Hummingbird V3.1 RaceSpecのLED Strip設定とその他諸々

Newbeedrone Hummingbird V3.1 RaceSpecは箱だしで16g台の軽さとその重量の軽さ以上に感じられる軽快な飛びでレーサーの期待を集めています。

[ LED Strip設定 ]
マッドさんが最初の頃にFC上のLED_StripがどこであるかをXで公表されていました。その時はRGB LEDをそこに接続するだけで使えていたそうです。ところが私の購入した機体およびファームウェア(betaflight_4.5.0_HUMMINGBIRD_RS_7498f3807_KAACK_Oct252024.hex)ではLEDとしての動作をさせることとができませんでした。調べてみるとLED_Stripのリソースが割り当てられていませんでした。そこで回答です。


左の写真で黄色いリード線を接続しているところがLED_Stripになります。そしてリソース関係の設定は以下のコマンドをCLIに投入します。

resource LED_STRIP 1 B01
timer B01 AF2
dma pin B01 0

LEDの数は32灯までです。
余談ですが、HummingBird V3 FCにはFC上に4灯のLEDが載っていました。

RaceSpecではこのLEDチップが取り付けられていません。そのランドを利用して外付けのRGB LEDを取り付けることにしたのです。またメーカーとしてはLEDチップを外したのでリソースからLED_Stripを削除したのは正しいことだと言えます。

[ USBコネクター ]
USBコネクターがもげてしまったと言う話しを聞きました。


上の投稿の通りでUSBコネクターのハウジングの足がFC基板のスルーホールに刺さっているだけで半田付けされていません。半田を流し込んでみました。おそらく、これで丈夫になると思います。

[ PID ]
色々な人の意見を総合するとメーカー設定のPIDは正しく出ていない気もします。ただしBlackboxログが取れないので数値での検証は出来ません。またフレームも極端に柔らかいことも考えるとなかなか難しいです。私は別の65機で調整がうまくいったPIDを使用して様子を見るつもりです。

コザックのBFチューン講座で使うBlackBoxログ

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

Tiny Frameの種類によってPIDチューンは変わるのか?

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チューニングBF4.5版、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チューニング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が見つかります。ここを自分用に変更すれば良さそうです。