"""
读取 BP_P51 关键点,并演示位姿控制接口。
关键点顺序: C, N, T, WL, WR, CP
C = 机体中心
N = 机头
T = 机尾
WL = 左翼尖
WR = 右翼尖
CP = 座舱
用法 (需 AirSim Play):
python generate_fixed_wing_points.py
python generate_fixed_wing_points.py --draw
python generate_fixed_wing_points.py --set-pose 0 0 -2 5 0 30 --draw
python generate_fixed_wing_points.py --set-position 10 0 -2 --set-attitude 5 0 30
python generate_fixed_wing_points.py --draw --cam-pitch 50 --cam-yaw 30
"""
import argparse
import json
import os
import sys
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
_PARENT_DIR = os.path.dirname(_SCRIPT_DIR)
if _PARENT_DIR not in sys.path:
sys.path.insert(0, _PARENT_DIR)
from draw_fixed_wing_points import capture_and_draw
from fixed_wing_api import FixedWingInterface
def export_points_txt(fw: FixedWingInterface, txt_path: str):
points = fw.get_key_points()
lines = ["# label x y z (NED world, meter)"]
for label, point in points.items():
x, y, z = point["position_ned_m"]
lines.append(f"{label} {x:.6f} {y:.6f} {z:.6f}")
with open(txt_path, "w", encoding="utf-8") as file:
file.write("\n".join(lines) + "\n")
def apply_pose_from_args(fw: FixedWingInterface, args) -> None:
if args.set_pose is not None:
x, y, z, pitch, roll, yaw = args.set_pose
print(
f"\n=== 设置位姿: pos=({x}, {y}, {z}) m, "
f"attitude=(pitch={pitch}, roll={roll}, yaw={yaw}) deg ==="
)
fw.set_pose(x=x, y=y, z=z, pitch_deg=pitch, roll_deg=roll, yaw_deg=yaw)
fw.print_status()
return
if args.set_position is None and args.set_attitude is None:
return
pos_kwargs = {}
if args.set_position is not None:
x, y, z = args.set_position
pos_kwargs = {"x": x, "y": y, "z": z}
print(f"\n=== 设置位置: ({x}, {y}, {z}) m ===")
att_kwargs = {}
if args.set_attitude is not None:
pitch, roll, yaw = args.set_attitude
att_kwargs = {"pitch_deg": pitch, "roll_deg": roll, "yaw_deg": yaw}
print(f"=== 设置姿态: pitch={pitch}, roll={roll}, yaw={yaw} deg ===")
fw.set_pose(**pos_kwargs, **att_kwargs)
fw.print_status()
def main():
parser = argparse.ArgumentParser(description="BP_P51 关键点读取与位姿控制")
parser.add_argument(
"--set-pose",
nargs=6,
type=float,
metavar=("X", "Y", "Z", "PITCH", "ROLL", "YAW"),
help="同时设置位置(NED,m)和姿态(度),例如: --set-pose 0 0 -2 5 0 30",
)
parser.add_argument(
"--set-position",
nargs=3,
type=float,
metavar=("X", "Y", "Z"),
help="只设置位置 (NED,m),可与 --set-attitude 组合",
)
parser.add_argument(
"--set-attitude",
nargs=3,
type=float,
metavar=("PITCH", "ROLL", "YAW"),
help="只设置姿态 (度),可与 --set-position 组合",
)
parser.add_argument(
"--export-txt",
action="store_true",
help="导出关键点到 fixed_wing_points_ned.txt",
)
parser.add_argument(
"--draw",
action="store_true",
help="将关键点投影绘制到图像并保存 fixed_wing_org/show.jpg",
)
parser.add_argument("--cam-pitch", type=float, default=0.0, help="相机俯仰角 (度)")
parser.add_argument("--cam-yaw", type=float, default=0.0, help="相机偏航角 (度)")
parser.add_argument("--cam-roll", type=float, default=0.0, help="相机横滚角 (度)")
parser.add_argument(
"--show-imshow",
action="store_true",
help="弹窗显示原图与标注图",
)
args = parser.parse_args()
fw = FixedWingInterface()
fw.connect()
print("=== 读取 BP_P51 位姿与关键点 ===")
fw.print_status()
apply_pose_from_args(fw, args)
if args.export_txt:
txt_path = os.path.join(_SCRIPT_DIR, "fixed_wing_points_ned.txt")
export_points_txt(fw, txt_path)
print(f"\n已导出: {txt_path}")
if args.draw:
draw_result = capture_and_draw(
fw,
output_dir=_SCRIPT_DIR,
pitch_deg=args.cam_pitch,
yaw_deg=args.cam_yaw,
roll_deg=args.cam_roll,
show_imshow=args.show_imshow,
imshow_wait_ms=0,
)
snapshot_draw = {
"camera": {
"pitch_deg": args.cam_pitch,
"yaw_deg": args.cam_yaw,
"roll_deg": args.cam_roll,
},
"pixels": {
label: (None if uv is None else [uv[0], uv[1]])
for label, uv in draw_result["pixels"].items()
},
"images": {
"org": draw_result["org_path"],
"show": draw_result["show_path"],
"pixels_txt": draw_result["txt_path"],
},
}
else:
snapshot_draw = None
json_path = os.path.join(_SCRIPT_DIR, "fixed_wing_points_snapshot.json")
snapshot = {
"pose": {
k: v for k, v in fw.get_pose().items() if k != "raw_pose"
},
"key_points": fw.get_key_points(),
"draw": snapshot_draw,
}
with open(json_path, "w", encoding="utf-8") as file:
json.dump(snapshot, file, indent=2, ensure_ascii=False)
print(f"已保存快照: {json_path}")
if __name__ == "__main__":
main()