TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

在 Kubernetes Pod 中让 PX4 无人机飞行:SITL 矩形任务与源码编译中的六个坑

状态:草稿(2026-10-18),实测。 测量时间为 2026-10-17 23:55 至 2026-10-18 00:45 (KST),在私有实验室 Kubernetes 的临时 Pod 中进行。 节点为私有实验室的一台 GPU 节点(AMD Ryzen 7 系列,16 线程),但未显式使用 GPU。飞行 Pod 由模拟器容器(CPU 上限 3.5 核,内存 3 GiB)和运行 MAVSDK 的 python:3.12-slim 边车容器(上限 0.5 核、1 GiB)组成,编译 Pod 的 CPU 上限设为 4 核、内存 4 GiB。 所用版本为预制镜像 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。 飞行各进行了一次,因此表中的数值均为单次测量结果。原始数据(两个 ULog 文件、摘要 JSON、控制台日志)位于工作目录 measure/sim-px4/ 中,Pod 在测量结束后均已被删除。

目录

1. 直觉:将真正的飞行软件装进虚拟的躯壳

SITL(Software In The Loop,软件在环)是一种将无人机飞行软件 原封不动 地在计算机上运行、仅由模拟器替代传感器和电机的方案。 在 PX4 看来,这与真实飞行器无法区分,因此无需实体机即可测试任务规划、应急动作和地面站连接。 如果说第 1 部分的轮式机器人在 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 秒 Ubuntu 24.04 编译工具链(不含仿真工具) 修改并编译 PX4 代码

如果使用预制镜像通过 HEADLESS=1 在无界面环境下启动,不到 25 秒即可打印出“Startup script returned successfully”。 开发镜像的体积接近三倍,且需要手动安装 Gazebo 及仿真依赖,因此如果没有修改代码的需求,使用预制镜像更为合适。

3. 矩形任务飞行

由于模拟器容器将 MAVLink 发送到同一 Pod 的 localhost:14540 ,边车容器中的 MAVSDK 会从 udpin://0.0.0.0:14540 接收数据。 任务设定为:起飞至 10 米,按顺时针方向飞往北侧和东侧边长 40 米的 4 个矩形航点(速度 5 m/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 的小函数。)

한 변 40 m 사각형 임무, ULog 로 다시 그린 비행 위에서 본 경로(왼쪽)와 고도(오른쪽). 8배속. ▶ 누르면 재생 홈 WP1 WP2 WP3 동쪽(m) 북 0 5 10 고도(m) 임무 41.0 s 시각: 이륙 명령 뒤 0–78 s 지금: 이륙 지금: 대기(LOITER) 지금: 임무(사각형) 지금: 귀환·착륙(RTL) 지금: 대기(LOITER) 파란 선은 vehicle_local_position(EKF 추정). 점선은 계획한 사각형입니다.
图:使用 ULog 重新绘制的矩形任务飞行轨迹与高度
项目 数值
起飞指令 → 达到 9.5 m 12.9 s
任务(AUTO_MISSION) 41.0 s
返航·降落(AUTO_RTL) 20.6 s
从连接到解锁解除 79 s
最大高度 10.03 m
任务期间高度误差 平均值 / 最大值 0.048 / 0.189 m
矩形轨迹偏差(cross-track) 平均值 / 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,降落判定:真
最大横滚 / 俯仰 19.1° / 18.4°
模拟器容器资源 CPU 62.8 s / 经过时间 215 s(平均 0.29 核),memory.peak 205 MiB

轨迹偏差是指 EKF 估计的 vehicle_local_position 偏离起点与四个航点连线段的距离。 航点内切 0.6–0.8 米被认为是由于“飞越”设置和到达判定半径(NAV_ACC_RAD 10 m)导致角被稍微切小了(推测)。 该图通过标准库直接读取 ULog(28 MB)绘制,降落位置 0.055 m 以及各阶段耗时与分析摘要保持一致。

模拟器的电池被设置为在 60 秒内降至 50%,因此从 100% 降至 50%、电压从 16.2 V 降至 15.3 V。这并不代表真实的电池消耗。

4. 源码编译版:仅通过 shell 命令飞行

从开发镜像中获取 PX4-Autopilot(--depth 1,含子模块,耗时 117–121 秒,大小 1.9 GiB)并进行编译。这一次没有使用 MAVSDK,仅通过 PX4 shell 命令完成飞行。 shell 输入通过 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 后 20 秒读取到高度 10.015 米,并在 commander land 后以“Disarmed by landing”结束。无需地面站或 Python,仅凭飞行控制器即可完成测试。

5. 遇到的六个问题

  1. MAVSDK 4 的 API 更改。 最新的 mavsdk 4.0.6 改为使用原生绑定,无法调用 mavsdk.mission 。我们将其固定在了 mavsdk<4(3.17.4)。旧方式的后续包名为 mavsdk-grpc。
  2. Gazebo 依赖必须先于编译安装。 如果在没有 Gazebo 的情况下配置过一次构建目录,CMake 缓存中会残留“Gazebo simulation dependencies not found”,导致后续即使安装了 Gazebo, gz_x500 也会失败。应先安装依赖再进行编译。
  3. OpenCV。 gz-harmonic 如果仅安装,optical_flow 插件将找不到 OpenCV。必须完整安装 PX4 的 ubuntu.sh 所列出的精简仿真依赖列表(新增 211 个包,171 MB,39 秒)。
  4. 并行编译 OOM 与 PX4_MAKE_ARGS. 如果不指定并行数,ninja 会使用节点核心数 (16) + 2 个任务进行编译,导致在 4 GiB 内存上限下触发 OOMKilled。 然而,PX4 Makefile 通过终端( -j )读取ps T,因此在没有终端的 nohup 执行中, make -j2 可能无法传递。我们通过将 PX4_MAKE_ARGS=-j2 作为 make 变量传递解决了这个问题。
  5. ULog 路径。 源码编译的日志不在 build/px4_sitl_default/rootfs/log/ ,而是在 build/px4_sitl_default/rootfs/fs/log/<날짜>/*.ulg 。
  6. 守护进程模式的输出。 px4 -d如果以(守护进程)启动, px4-<모듈> 客户端命令(例如 mavlink status)的输出不会流向该命令的终端,而是进入服务器日志。结果是从 px4 日志中读取的。

顺便一提,用 pyulog 读取 ULog 时,如果对时间戳(uint64)直接做减法会导致负数溢出。必须先转换为浮点数再做减法。

地面站(QGroundControl)通常在 UDP 14550 端口等待连接。因此很容易让人产生“必须打开 SITL 容器的 14550 端口”的想法,但实测结果恰恰相反。

综上所述,地面站应当持有 14550 端口,SITL 端没有理由绑定或发布 14550 端口。有报告称在 Docker Desktop 中使用 -p 14550:14550/udp 发布容器端口时,主机的 14550 可能会被容器端占用,导致地面站绑定失败,但在当前环境(containerd)中无法复现,故未作验证(未实测)。

7. 局限性

8. 下篇预告

接下来的 第 5 篇将重新回到乒乓机器人,讨论在第 2 部分小球模型上运行的经典控制基准算法的击球成功率。

TT 寄语。 飞行控制器是真实的,而躯壳是虚拟的。因此,你可以不用担心摔坏飞机,放心地无数次测试矩形飞行。另外别忘了,地面站端口应该由地面站掌握,而不是无人机!

参考

所有链接均已于 2026-10-18 验证可正常访问。

登录后即可点赞

评论

还没有评论。

登录后即可发表评论