システムアーキテクチャとハードウェア構成の選定
2026年現在の自律型UAV(無人航空機)運用において、GPSの誤差(通常2〜5m)を克服するための視覚ベース精密着陸システムは不可欠なコンポーネントです。本プロジェクトでは、エッジコンピューティングデバイスとして Jetson Nano を採用し、深度情報の取得に Intel RealSense D435i、フライトコントローラー(FC)に Pixhawk を使用する構成を構築しました。

データフローは、D435iからのRGB-Dストリームを Jetson Nano が受信し、YOLOv8 モデルでランディングパッドを検出、その中心座標を深度マップと照合して3D相対距離を算出します。最終的に pymavlink を介して LANDING_TARGET メッセージを Pixhawk に送信し、ArduPilot の自律着陸アルゴリズムを駆動させます。USB 3.0 バスの帯域幅確保と、Jetson Nano の電力モード(10Wモード)の固定が安定稼働の前提条件となります。

合成データセット生成による学習モデルの汎用性向上
実環境でのデータ収集には限界があるため、OpenCVを用いた合成データセット生成スクリプトを実装しました。ランディングパッドのPNG画像を、様々なアスファルトやコンクリートの背景画像にランダムに合成します。この際、ドローンの接近角をシミュレートするために cv2.getPerspectiveTransform を用いた透視変換を適用することが重要です。

import cv2
import numpy as np
def apply_perspective_transform(image, src_points, dst_points):
matrix = cv2.getPerspectiveTransform(src_points, dst_points)
result = cv2.warpPerspective(image, matrix, (image.shape[1], image.shape[0]))
return result
# Synthetic data generation logic for landing pad augmentation
このスクリプトにより、輝度変化、モーションブラー、および幾何学的歪みを含む1,000枚の学習データを短時間で確保しました。これにより、実機テスト時の検出失敗率が大幅に低下しました。
YOLOv8の学習とTensorRTへのエクスポートプロセス
Jetson Nano の CPU リソースは極めて限定的であるため、PyTorch モデル(.pt)をそのまま推論に使用すると FPS が 2〜5 程度まで低下し、飛行制御に致命的な遅延をもたらします。これを解決するために TensorRT への変換が必須となります。

まず、高性能なデスクトップPC(RTX 4090環境)で YOLOv8-nano モデルを学習させ、その後 Jetson Nano 上で以下のコマンドを実行してエンジンファイルを生成します。
# Exporting YOLOv8 model to TensorRT format on Jetson Nano
yolo export model=best.pt format=engine device=0 half=True
エクスポート時のログ出力例
TensorRT: starting export with TensorRT 8.2.1...
TensorRT: input "images" with shape(1, 3, 640, 640) DataType.HALF
TensorRT: output "output0" with shape(1, 84, 8400) DataType.HALF
TensorRT: export success, saved as best.engine (14.2 MB)
half=True (FP16) を指定することで、推論精度を維持しつつ、Jetson Nano 上で 35 FPS 以上のスループットを確保しました。

RealSense D435iによる深度マッピングと3D座標変換
検出されたバウンディングボックスの中心点 $(u, v)$ を、RealSense の深度フレームと照合します。単一ピクセルの深度値はノイズの影響を受けやすいため、中心点周辺 5x5 ピクセルの平均値を取得するフィルタリングを実装しています。
def get_filtered_depth(depth_frame, x, y, window_size=5):
depth_roi = depth_frame[y-window_size:y+window_size, x-window_size:x+window_size]
valid_depths = depth_roi[depth_roi > 0]
return np.mean(valid_depths) if len(valid_depths) > 0 else 0
この座標データは、カメラの取り付け角度(ピッチ角)を考慮した回転行列を適用した後、MAVLink メッセージとしてパッキングされます。

MAVLink通信によるLANDING_TARGETの送信
計算された相対座標を Pixhawk に送信するために、pymavlink を使用します。ArduPilot は LANDING_TARGET メッセージを受信すると、内部の EKF3 フィルタに統合し、着陸フェーズでの位置補正を開始します。
from pymavlink import mavutil
def send_landing_target(connection, x_rad, y_rad, distance):
connection.mav.landing_target_send(
0, 0, mavutil.mavlink.MAV_FRAME_BODY_NED,
x_rad, y_rad, distance, 0, 0
)

トラブルシューティング:推論レイテンシと通信の不安定性
1. TensorRT実行時のサーマルスロットリング
現象: 推論開始から約10分後、FPSが30から12に急落する。
原因: Jetson Nano の SoC 温度が 80°C を超え、周波数制限が発生していた。
対策: jetson_clocks コマンドを実行してファン速度を最大に固定し、物理的な大型ヒートシンクへの換装を実施。
2. RealSense USB 3.0 認識エラー
現象: RuntimeError: Frame didn't arrive within 5000 が頻発する。
原因: Jetson Nano のキャリアボードにおける USB バス供給電力の不足。
対策: D435i を外部給電式の USB 3.0 ハブ経由で接続するか、Jetson Nano への給電を DC ジャック(5V 4A)に切り替えることで解決。
3. MAVLink メッセージのパケットロス
現象: Pixhawk 側で LANDING_TARGET が断続的にしか受信されない。
原因: シリアル通信のボーレート不足(115200bps)によるバッファオーバーフロー。
対策: ボーレートを 921600bps に引き上げ、SERIAL1_PROTOCOL=2 (MAVLink 2) を明示的に設定。

システム検証と運用テストの結果
実装したシステムの検証を、高度 5m からの自動着陸シーケンスで実施しました。以下のログは、着陸直前のターゲット補正状況を示しています。
運用ログ:着陸ターゲット追従状況
[INFO] Target Detected: x=0.12m, y=-0.05m, dist=3.42m | FPS: 36.2
[INFO] Target Detected: x=0.08m, y=-0.02m, dist=2.15m | FPS: 35.8
[INFO] Target Detected: x=0.01m, y=0.01m, dist=0.85m | FPS: 36.1
[SUCCESS] Precision Landing Completed. Offset: 4.2cm

検証の結果、最終的な着陸精度は中心点から半径 8cm 以内に収まり、GPS 単独時の誤差(約 2.5m)と比較して大幅な精度向上を確認しました。また、TensorRT による高速化により、ドローンの急激な姿勢変化に対しても遅延なくターゲットを追従することが可能となりました。

結論と今後の運用上の留意点
本システムは、Jetson Nano という制約のあるリソース下で、AI推論と深度計測を同期させる実用的な解法を提供します。運用上の留意点として、RealSense の深度計測範囲(D435i の場合は約 0.3m〜10m)を考慮し、高度 10m 以上では YOLO による 2D 検出のみを行い、10m 以下で深度情報を統合するロジックの切り替えが推奨されます。

また、夜間運用においては、赤外線プロジェクターの出力を最大化するか、ランディングパッド自体にアクティブな発光体(LEDマーカー)を配置する等の物理的な対策が、検出安定性の向上に寄与します。
