ステータス:ドラフト(2026-10-18)、実測値。 測定は2026-10-17 23:55から2026-10-18 00:45(KST)まで、ホームラボのKubernetesの一時Podで行いました。 ノードはホームラボのGPUノード1台(AMD Ryzen 7系16スレッド)ですが、GPUは使用していません。飛行Podはシミュレータコンテナ(CPU上限3.5コア・メモリ3GiB)と MAVSDKを実行する
python:3.12-slimサイドカーコンテナ(上限0.5コア・1GiB)、ビルドPodはCPU上限4コア・メモリ4GiBで起動しました。 使用したバージョンは、事前ビルド済みイメージがPX4 v1.18.0-beta1-660(61907962c9、2026-09-11)、ソースビルドがPX4-Autopilot main 93579610c6(2026-10-16)、Gazebo Sim 8.15.0(Harmonic)、MAVSDK-Python 3.17.4です。 飛行はそれぞれ1回のみであるため、表の値は1回測定した値です。生データ(ULog 2つ、サマリーJSON、コンソールログ)は作業ディレクトリmeasure/sim-px4/にあり、Podは測定後にすべて削除しました。
目次
- 1. 概要:本物のフライトソフトウェアをバーチャルな機体に載せる
- 2. イメージの選択:すぐに飛ばすか、修正して飛ばすか
- 3. 四角形ミッション飛行
- 4. ソースビルド版:シェルコマンドだけで飛ばす
- 5. 直面した6つの問題
- 6. MAVLinkは14550をリッスンしない
- 7. 制限事項
- 8. 次回予告
- 参考
1. 概要:本物のフライトソフトウェアをバーチャルな機体に載せる
SITL(Software In The Loop)は、ドローンに搭載されるフライトソフトウェアを そのまま コンピュータ上で実行し、センサーとモーターのみをシミュレータが代行する方式です。 PX4から見ると本物の機体と区別がつかないため、機体なしでミッション計画、緊急動作、地上局との接続をテストできます。 前回の車輪ロボットがROS 2上で動作したのに対し、今回はフライトコントローラー自体がテスト対象となります。
2. イメージの選択:すぐに飛ばすか、修正して飛ばすか
| イメージ | 圧縮サイズ | 取得時間 | 含まれる内容 | 用途 |
|---|---|---|---|---|
px4io/px4-sitl-gazebo:latest |
656 MiB | 32.5 s | ビルド済みPX4 1.18 + Gazebo Harmonic、起動スクリプト | すぐに飛ばして試す、ミッション・地上局のテスト |
px4io/px4-dev:v1.18.0-rc1 |
1.77 GiB | 2分 3.9 s | Ubuntu 24.04ビルドツール(シミュレーションツールは除く) | PX4コードの修正とビルド |
事前ビルド済みイメージを HEADLESS=1 でヘッドレス起動すると、25s以内に「Startup script returned successfully」が出力されました。
開発イメージはサイズが3倍近くあり、さらにGazeboとシミュレーションの依存関係を直接インストールする必要があるため、コードを修正する予定がないのであれば事前ビルド済みイメージの方が適しています。
3. 四角形ミッション飛行
シミュレータコンテナがMAVLinkを同じPod内の localhost:14540 に送信するため、サイドカーコンテナのMAVSDKが udpin://0.0.0.0:14540 で受信します。
ミッションは高度10mへの離陸、北と東へ一辺40mの四角形の4つの頂点(速度5m/s、フライバイ通過)、終了後にRTL(帰還)・着陸となります。
corners_ne = [(SIDE, 0), (SIDE, SIDE), (0, SIDE), (0, 0)]
wps = [offset(lat0, lon0, n, e) for n, e in corners_ne]
await drone.mission.set_return_to_launch_after_mission(True)
await drone.mission.upload_mission(MissionPlan([item(a, b) for a, b in wps]))
await drone.action.set_takeoff_altitude(ALT)
await drone.action.arm()
await drone.action.takeoff()
(TT Labの実験コード fly_square.py から抜粋。 item() はMAVSDK 3.xの15個の引数 MissionItem を生成する小さな関数です。)
| 項目 | 値 |
|---|---|
| 離陸指令 → 9.5m到達 | 12.9 s |
| ミッション(AUTO_MISSION) | 41.0 s |
| 帰還・着陸(AUTO_RTL) | 20.6 s |
| 接続からディスアームまで | 79 s |
| 最大高度 | 10.03 m |
| ミッション中の高度誤差 平均 / 最大 | 0.048 / 0.189 m |
| 四角形経路からの逸脱(クロストラック) 平均 / RMS / p95 / 最大 | 0.256 / 0.328 / 0.657 / 0.722 m |
| 頂点への最接近距離(WP1–4) | 0.80, 0.70, 0.62, 0.00 m |
| 着陸位置(ホームからの距離) | 0.055 m、着陸判定 True |
| 最大ロール / ピッチ | 19.1° / 18.4° |
| シミュレータコンテナのリソース | CPU 62.8 s / 経過時間 215 s(平均0.29コア)、memory.peak 205 MiB |
経路逸脱は、EKFが推定した vehicle_local_position がホームと4つの頂点を結ぶ線分からどれだけ離れているかを測定した値です。
頂点を0.6〜0.8m内側に通過した原因は、「フライバイ」設定と到着判定半径(NAV_ACC_RAD 10m)の影響により、角を少しカットして旋回したためと推測されます。
図はULog(28MB)を標準ライブラリで直接読み込んで描画しており、着陸位置0.055mや各段階の時刻が分析サマリーと一致していることが確認できました。
シミュレータのバッテリーは60sの間に50%まで低下するように設定されており、100%から50%、16.2Vから15.3Vへ低下しました。実際のバッテリー消費を意味するものではありません。
4. ソースビルド版:シェルコマンドだけで飛ばす
開発イメージからPX4-Autopilotを取得し(--depth 1、サブモジュールを含む、117〜121 s、1.9 GiB)、ビルドを行いました。今回はMAVSDKを使用せず、PX4のシェルコマンドだけで飛行させました。
シェルへの入力はFIFO経由で行いました。
param set NAV_DLL_ACT 0 # 지상국이 없어도 이륙을 거부하지 않게
param set MIS_TAKEOFF_ALT 10
commander takeoff
listener vehicle_local_position -n 1
commander land
| 項目 | 値 |
|---|---|
| 依存関係のインストール(Gazebo Harmonic + シミュレーション依存関係) | 157 s |
HEADLESS=1 make px4_sitl gz_x500 PX4_MAKE_ARGS=-j2 ビルド後の起動 |
182 s |
| アーム維持時間 | 37.4 s |
| 最大高度 | 10.04 m |
| ホバリング中の水平方向の揺れの最大値 | 0.092 m |
| 実行中のメモリ | anon 156 MiB、gz sim RSS 209 MiB、px4 RSS 21 MiB |
commander takeoff の20s後に高度10.015mを読み取り、 commander land の後に「Disarmed by landing」で終了しました。地上局もPythonもなく、フライトコントローラー単体でテストが可能です。
5. 直面した6つの問題
- MAVSDK 4のAPI変更。 最新のmavsdk 4.0.6はネイティブバインディングに変更されたため、
mavsdk.missionをインポートできませんでした。mavsdk<4(3.17.4)に固定しました。従来方式の後継パッケージ名はmavsdk-grpcです。 - Gazeboの依存関係はビルドの前に。 Gazeboなしで一度構成したビルドディレクトリには、CMakeキャッシュに「Gazebo simulation dependencies not found」が残るため、後からGazeboをインストールしても
gz_x500が失敗しました。依存関係を先にインストールしてからビルドする必要があります。 - OpenCV。
gz-harmonicのみをインストールした場合、optical_flowプラグインがOpenCVを見つけられませんでした。PX4のubuntu.shにある軽量なシミュレーション依存関係リストをそのままインストールする必要がありました(211パッケージ、171MB追加、39s)。 - 並列ビルド時のOOMと
PX4_MAKE_ARGS. 並列数を指定しない場合、ninjaがノードのコア数(16)+2個のジョブを実行するため、メモリ上限4GiBの環境でOOMKilledとなりました。 しかし、PX4のMakefileは-jを端末(ps T)から読み取るため、端末のないnohupの実行ではmake -j2が正しく伝わらない場合があります。PX4_MAKE_ARGS=-j2をmake変数として渡すことで解決しました。 - ULogのパス。 ソースビルドのログは
build/px4_sitl_default/rootfs/log/ではなくbuild/px4_sitl_default/rootfs/fs/log/<날짜>/*.ulgに保存されていました。 - デーモンモードでの出力。
px4 -d(デーモン)として起動した場合、px4-<모듈>クライアントコマンド(例:mavlink status)の出力はそのコマンドの端末ではなく、サーバーのログに出力されます。結果はpx4のログから読み取りました。
おまけとして、pyulogでULogを読み込む際、タイムスタンプ(uint64)同士を引き算すると負の値でオーバーフローします。浮動小数点数(float)に変換してから引き算する必要があります。
6. MAVLinkは14550をリッスンしない
地上局(QGroundControl)は通常UDPポート14550で待ち受けます。そのため「SITLコンテナ側の14550を開く必要がある」と考えがちですが、実測の結果は逆でした。
- 一般的なKubernetesのPodで
mavlink statusを確認すると、最初のインスタンスがUDP (18570, remote port: 14550)であり、ブロードキャストは無効、ログには「MAVLink only on localhost」が出力されます。 - コンテナ内のUDPソケットテーブル(
/proc/net/udp)には18570、14580、14280、13030などのポートはあるものの、 14550にバインドされているソケットはありませんでした。 PX4は14550をリッスンする側ではなく、14550へ 送信する 側です。 - イメージの起動スクリプトは、
host.docker.internalがIPv4として名前解決できる場合にのみ、すべてのmavlink startの行に-t <그 주소>を追加します(Docker Desktop用)。通常のPodでは名前解決できないため、何も変更されませんでした。 - PodのhostAliasesを使用してその名前が127.0.0.2に解決されるようにすると、5行すべてが
-t 127.0.0.2に変更され、同じPod内で0.0.0.0:14550を開いた受信機が30s間に送信元ポート18570のパケットを10,004個受信しました。14540側は送信元ポート14580から11,492個でした。 - イメージ設定の
EXPOSE 14540/udp 14550/udp 8888/udpは単なるメタデータであり、ポートを開くものではありません。
要約すると、地上局側が14550を保持している必要があり、SITL側で14550をバインドまたは公開する理由はありません。Docker Desktop環境において -p 14550:14550/udp でコンテナポートを公開すると、ホスト側の14550をコンテナ側が占有してしまい、地上局のバインドが失敗するという報告がありますが、この環境(containerd)では再現できなかったため確認できていません(未実測)。
7. 制限事項
- 飛行はそれぞれ1回のみです。風、センサーノイズ、GPS切断などの条件は考慮していません。
- 経路逸脱はEKFの推定位置で測定しました。第1回の教訓に従えば、シミュレータの正解位置(
vehicle_local_position_groundtruth、ULogに同時に記録される)と並べて確認すべきですが、本記事ではまだ比較していません。 - 地上局の接続(QGroundControl)とDocker Desktopのポート競合については未実測です。
8. 次回予告
次回 第5回は再び卓球ロボットに戻り、第2回のボールモデル上で実行した古典制御ベースラインの返球成功率について取り上げます。
T-ти(ティティ)の一言: フライトコントローラーは本物で、機体はバーチャルです。そのため機体を壊す心配なく、何度でも四角形を描いて飛ばすことができます。地上局のポートはドローンではなく地上局側が保持していなければならないことも忘れないでくださいね!
参考
すべてのリンクは2026-10-18時点でのアクセスと有効性を確認しています。
- PX4ドキュメント(Gazeboシミュレーション): https://docs.px4.io/main/en/sim_gazebo_gz/
- PX4-Autopilotリポジトリ: https://github.com/PX4/PX4-Autopilot、関連するIssue: https://github.com/PX4/PX4-Autopilot/issues/29292
- PX4コンテナイメージ: https://hub.docker.com/r/px4io/px4-sitl-gazebo, https://hub.docker.com/r/px4io/px4-dev
- MAVSDK-Python: https://github.com/mavlink/MAVSDK-Python
- ULogフォーマット: https://docs.px4.io/main/en/dev_log/ulog_file_format.html
- 引用されたコードおよびコマンドはTT Labの実験コードです。