426 Views
January 04, 26
スライド概要
この教材では、箱庭ドローンシミュレータとPythonプログラムを連携させてドローンを制御する仕組みを説明します。事前にシミュレータ、コア、Python 3.12、lmdisk、箱庭PDUライブラリをインストールし、サンプルコードを基に自分のプログラムを作成します。提供される基本7つのAPI(離陸、位置取得、目的地移動、荷物運搬、カメラ撮影、LiDARによる障害物検出、着陸)を使って、ミッションの実装方法や座標系(ENU と左手系)の違いも解説します。これにより、教育現場や学習者が実際にドローン操作を体験できるようになります。
おすすめタグ:ドローンシミュレーション,Python,API,Unity,教育
組込みエンジニアを30年以上やってます。 最近は、ドローンシミュレータの開発を行っており、箱庭ドローンシミュレータの開発をしています。
箱庭ドローンシミュレータ PYTHON API教育編 Pythonプログラムを使ったドローン制御を学ぶ エデュケーションWG
目次 1. 箱庭ドローンシミュレータ Pythonプログラム動作仕組み 2. 箱庭ドローンシミュレータ Python APIの解説 3. 箱庭ドローンシミュレータ Python APIを使った演習 このカリキュラム で学ぶこと 2
箱庭ドローンシミュレータ PYTHONプログラム動作仕組み 箱庭ドローンシミュレータとPythonプログラム制御の動作の 仕組みを学ぶ 3
箱庭ドローンシミュレータのPython動作 • 箱庭コアをハブとして、箱庭ドローンシミュレータと箱庭ドローンAPI制御のア セットを連携動作させることでドローンを操縦します。 PDU通信を使って 連携 Pythonを使った APIを使って ドローンを動かす Unityを使った ドローン操縦 の視覚化 4
箱庭ドローンシミュレータ PYTHON APIの説明 箱庭ドローンシミュレータを実際に動作させるPython API 群について学ぶ 5
箱庭ドローンシミュレータ Python API群 • 箱庭ドローンシミュレータ Python API以下の基本7個があります。 • ドローンを離陸させる • ドローンの現在位置を取得する • ドローンを目的地に移動させる • ドローンで荷物を運ぶ • ドローン前方カメラで撮影する • ドローンのLiDARで障害物を検出する • ドローンを着陸させる 6
箱庭ドローンシミュレータ Python API群 箱庭ドローンを操作するAPIセットは基本7個あります。 現在位置を 取得する 着陸する 離陸する カメラ撮影する 障害物を検出する 目的地へ 移動する 荷物を運ぶ 7
箱庭ドローンシミュレータ Python API群 Pythonアプリ ドローンを操縦 様々なミッションを コントロール Python APIライブラリ 箱庭ドローンシミュレータ 箱庭ドローンを 操作するため のAPIセット Unityの3Dモデル で作成された ドローン 今回はここ を学ぶ 8
箱庭ドローンシミュレータ Python API群
• 事前準備
• 箱庭ドローンシミュレータのインストール
• 箱庭コアのインストール
• Python(Version 3.12)のインストール
• lmdiskのインストール
• 箱庭PDUライブラリのインストール
• Python APIのサンプルプログラム
import sys
# 箱庭ドローンシミュレータ用のライブラリをインポート
import hakoniwa_pdu.apps.drone.hakosim as hakosim
def main():
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <config_path>")
return 1
# 箱庭ドローンシミュレータを利用するためのオブジェクト取得
# custom.jsonが必要になるので引数で渡す
# connect to the HakoSim simulator
client = hakosim.MultirotorClient(sys.argv[1], "Drone")
# オブジェクトの初期化
client.confirmConnection()
client.enableApiControl(True)
client.armDisarm(True)
#ここに自分のプログラムを書きます
• サンプルプログラムをベースに自分の
プログラムを作成していきます。
return 0
if __name__ == "__main__":
sys.exit(main())
9
箱庭ドローンシミュレータ PYTHON APIの説明 箱庭ドローンシミュレータのPython APIの詳細説明 ここ重要 10
API1:離陸する • API名:takeoff • 引数で指定した高さ(単位:メートル)まで浮上します。 離陸する # 3m浮上 client.takeoff(3) 11
API2:現在位置を取得する • API名:simGetVehiclePose • ドローンの現在位置を姿勢を取得できます。 現在位置を 取得する # ドローンの現在位置と姿勢を取得 pose = client.simGetVehiclePose() # 現在位置をデバッグ出力 print(f"POS : {pose.position.x_val} {pose.position.y_val} {pose.position.z_val}") # 姿勢情報は、クォータニオンであるため、オイラー角(ラジアン)に変換します roll, pitch, yaw = hakosim.hakosim_types.Quaternionr.quaternion_to_euler(pose.orientation) # オイラー角を度に変換して、デバッグ出力 print(f"ANGLE: {math.degrees(roll)} {math.degrees(pitch)} {math.degrees(yaw)}") 12
API3:目的地に移動する • API名:moveToPosition • 目的地へ移動させます。 目的地へ 移動する # ドローを目的地へ移動させます # X軸の位置:10m # Y軸の位置:0m # 高度:3m # 機首の方向:0度 client.moveToPosition(x=10, y=0, z=3, speed=3, yaw_deg=0) • 補足: • speedは最大で3m/sec程度で移動します。 • 現時点では、目標速度での移動は未サポートです。 13
API4:荷物を運ぶ • API名:grab_baggage • スイッチングマグネットホルダーを使って、荷物の吸引およ びその解除を操作します # 荷物配置場所(baggage_pos)へ移動します client.moveToPosition(baggage_pos['x'], baggage_pos['y'], 3, 0, -90) client.moveToPosition(baggage_pos['x'], baggage_pos['y'], 3, 5) client.moveToPosition(baggage_pos['x'], baggage_pos['y'], 3, 5, 0) # 荷物を吸引できる位置まで高度まを下げます client.moveToPosition(baggage_pos['x'], baggage_pos['y'], 0.7, 0.01, 0) # 荷物を吸引し、高度を上げます client.grab_baggage(True) client.moveToPosition(baggage_pos['x'], baggage_pos['y'], 3, 0.01) # 荷物を下ろす場所(transfer_pos)へ移動します client.moveToPosition(transfer_pos['x'], transfer_pos['y'], 3, 0.1) client.moveToPosition(transfer_pos['x'], transfer_pos['y'], transfer_pos['z'], 0.01) # 荷物を下ろします client.grab_baggage(False) 荷物を運ぶ • 補足: • 荷物を吸引する条件は、ド ローン直下1m以下に存在 するすべての荷物が対象とな ります 14
API5:カメラ撮影する(その1) • API名:simGetImage • カメラ設置位置(デフォルト:前方)からカメラ撮影します。 # 引数は必ず以下値としてください # id = “0” # image_type = hakosim.ImageType.Scene png_image = client.simGetImage("0", hakosim.ImageType.Scene) カメラ撮影する # ファイル名をscene.pngとしてファイル保存します。ファイルは、Pythonプログラム実行場所に出力されます。 if png_image: with open("scene.png", "wb") as f: f.write(png_image) • 補足: • カメラは複数配置できませんので、常にidには”0”を指定してください。 • image_typeは、ImageType.sceneのみサポートしています。 15
API5:カメラ撮影する(その2) • API名:simSetCameraOrientation • カメラのピッチ角を変更します。 # 引数は必ず以下値としてください # id = “0” client.simSetCameraOrientation("0", degree=-90) png_image = client.simGetImage("0", hakosim.ImageType.Scene) カメラ撮影する # ファイル名をscene.pngとしてファイル保存します。ファイルは、Pythonプログラム実行場所に出力されます。 if png_image: with open("scene.png", "wb") as f: f.write(png_image) • 補足:degreeは下向きがマイナスです。上向きはプラスです。 • 前方真正面は0度、下向きの最大角度は-90度、上向きは+15度です。 16
API6:障害物を検出する
• API名:getLidarData
• レーザ照射し、障害物の位置を取得します。本APIは、AirSimのLiDAR
のAPI仕様に準拠していますので、詳細はこちらを参照ください。
# LiDARデータを取得します
lidarData = client.getLidarData()
if (len(lidarData.point_cloud) < 3):
print("¥tNo points received from Lidar data")
else:
print(f"len: {len(lidarData.point_cloud)}")
# 障害物情報はポイントクラウドというデータで返されますので、parse_lidarData()で整形します。
points = parse_lidarData(lidarData)
# 障害物の情報をデバッグ出力します。pointsに位置情報が入っていますので、このデータを走査すれば障害物の位置を特定できます
print("¥tReading: time_stamp: %d number_of_points: %d" % (lidarData.time_stamp, len(points)))
# LiDARの取り付け位置と姿勢情報も参照できます
print("¥t¥tlidar position: %s" % (pprint.pformat(lidarData.pose.position)))
print("¥t¥tlidar orientation: %s" % (pprint.pformat(lidarData.pose.orientation)))
障害物を検出する
def parse_lidarData(data):
# reshape array of floats to array of [X,Y,Z]
points = numpy.array(data.point_cloud, dtype=numpy.dtype('f4’))
points = numpy.reshape(points, (int(points.shape[0]/3), 3))
return points
17
API7:着陸する • API名:land 着陸する • ドローンを着陸させます # 着陸 client.land() 18
箱庭ドローンシミュレータ PYTHON API操縦 箱庭ドローンシミュレータPython APIを利用するにあたって の前提知識 ここかなり重要!! 19
座標系:箱庭ドローン Python API X • ENU座標系です(右図) • X:前方方向(E(East):前がプラス) • Y:横方向(N(North):左がプラス) • Z:上方向(U(Up):上がプラス) Y 参考:ROSの座標系と同じです。 BlanderやCAD系では3D空間をXY平面、Zを高さ と捉えることが多い。 → 物理計算のし易さを優先していると言われている。 → OpenGLなど学術的な部分では「数式の美しさと 物理法則の整合性」を優先したと言われている。 Z 右手系:親指上、右回り 親指 :Z 人差し指 :X 中指 :Y 20
座標系:Unity Z • 左手座標系です(右図) • X:横方向(右がプラス) • Y:上方向(上がプラス) • Z:前方向(前がプラス) 2D グラフィック(Xが左右、Yが上下)に奥行き(Z軸) を付け足したとされている。 → ゲーム画面の直観的に分り易いとされている。 → MicrosoftのDirect Xが左手系座標を採用して いるためとも言われている。 X Y 左手系:親指上、左回り 親指 :Y 人差し指 :Z 中指 :X 21
座標系まとめ • ENUは右手系 • 親指が上(Z)、人差し指が前(X)、中指が左(Y) • Unityは左手系 • 親指が上(Y)、人差し指が前(Z)、中指が左(X) 座標系 前後 左右 上下 ENU +X +Y +Z Unity +Z +X +Y 実際に左手を使って 体感してみよう! 22
座標系の課題 • 箱庭ドローンシミュレータでは、ENU座標を採用しています。Unityでのビジュ アライズの座標は異なる座標を使っています。 • Python API moveToPosition(x, y, z, speed, yaw_deg)はENU座標系が前 提となっています。 • 課題1 • Unity画面で見た場合、ドローンを「右方向へ1m進む」はENU座標をどう指定したらよ いでしょうか? • 課題2 • ENU座標系でx=1,y=0.5を指定した場合にUnity画面でドローンはどう動くでしょう か? 23
箱庭ドローンシミュレータ PYTHON API演習 ここからは、実際にプログラミングしながら、ドローンの動きを理 解していくよ!! ドローン動かすよ 24
演習1-1 • Unity上のドローンを1m離陸させて、3秒ホバリングして、着陸させましょう。 離陸 ホバリング (3秒) 着陸 25
演習1-2 • Unity上のドローンを1m離陸させて、3秒ホバリングして、位置情報を表示し てから、着陸させましょう。 位置情報表示 離陸 ホバリング (3秒) 着陸 26
演習1-3 • Unity上のドローンを1m離陸させて、1m前進して、3秒ホバリングして、着 陸させましょう。 離陸 1m前進 ホバリング (3秒) 着陸 27
演習2 • Unity上のドローンで以下の部分を対応しましょう。 • 離陸 1m • カメラでの撮影 • カメラ向きの変更して撮影 • LiDARデータの取得 • 着陸 カメラ撮影 着陸 離陸 LiDARデータ取得 28
箱庭ドローンシミュレータ PYTHON APIリファレンスマニュアル 参考にしてね! 29
Python APIリファレンスマニュアル メソッド 概要 主な引数 __init__(config_path) 設定ファイルパスを指定してクライアントを初期化 config_path (str): PDU設定ファイルのパス confirmConnection() シミュレーション環境との接続確認 なし enableApiControl(v, vehicle_name=None) API制御の有効/無効を設定 armDisarm(v, vehicle_name=None) ドローンのアーム/ディスアーム simGetVehiclePose(vehicle_name=None) ENU座標系での位置・姿勢取得 simGetVehiclePoseUnityFrame(vehicle_name=No Unity座標系での位置・姿勢取得 ne) takeoff(height, vehicle_name=None) 指定高度まで離陸 moveToPosition(x, y, z, speed, yaw_deg=None, timeout_sec=-1, vehicle_name=None) ENU座標系で指定位置へ移動 v (bool): True=有効 vehicle_name (str, 任意) v (bool): True=アーム vehicle_name (str, 任意) vehicle_name (str, 任意) vehicle_name (str, 任意) height (float) x,y,z (float), speed (float) 30
Python APIリファレンスマニュアル メソッド land(vehicle_name=None) grab_baggage(grab, timeout_sec=-1, vehicle_name=None) 概要 着陸 主な引数 vehicle_name (str, 任意) 荷物のつかみ/離し grab (bool) simGetImage(id, image_type, vehicle_name=None) カメラ画像取得 id (int), image_type (str) simSetCameraOrientation(id, degree, vehicle_name=None) カメラ向き設定 id (int), degree (float) getLidarData(vehicle_name=None) LiDARデータ取得 vehicle_name (str, 任意) getGameJoystickData(vehicle_name=None) ゲームパッドデータ取得 vehicle_name (str, 任意) putGameJoystickData(data, vehicle_name=None) ゲームパッドデータ送信 data 31