SwarmCore 集群控制框架(swarm_api)使用说明与教程
适用对象:需要在 SwarmCore 上开发单机/集群算法的用户 前置条件:已完成 SwarmCore 快速入门教程,能跑通单机 demo 对应版本:swarm_api 0.1.0(2026-07-27) 配套文档:swarm_api API 接口说明(快速查阅用,本文是理解与教学用)
目录
- 框架是什么、解决什么问题
- 架构与设计原理
- 坐标系与航向约定
- 编译与环境
- 快速上手
- 阻塞语义、超时与并行模型
- 错误处理完整体系
- 进阶用法
- 安全机制与失效行为
- 性能特征与调参建议
- 基于框架开发自己的算法包
- 从仿真迁移到真机
- 当前限制与边界
- 常见问题 FAQ
1. 框架是什么、解决什么问题
1.1 没有框架时要面对什么
直接操作 PX4 的 ROS 2 Offboard 接口写控制程序,需要处理一大堆与算法无关的工程细节:
| 工程细节 | 不处理的后果 |
|---|---|
fmu/in 话题必须 reliable QoS,fmu/out 是 best_effort | 指令被静默丢弃,飞机毫无反应且无任何报错 |
| PX4 内部用 NED 坐标系,ROS 习惯用 ENU | 飞机往错误方向飞、高度正负写反 |
| Offboard 模式要求 ≥2Hz 持续发送设定点 | 断流 0.5 秒飞控触发失联保护,动作中断 |
| 切 Offboard 前必须先有一段设定点流 | 模式切换被拒绝,飞机不起飞 |
| 切模式/解锁命令可能被丢掉 | 需要循环重发直到状态确认 |
| EKF 高度原点有偏差(尤其室内/仿真) | 写死绝对高度导致起飞高度不对 |
一个单机 demo 里这些样板代码近 200 行。要控制 N 架飞机,还得处理多机并行:每架飞机的状态订阅、指令发送、超时监控都要同时进行——复制 N 份单机代码既无法维护,也无法做到"同时起飞"这种基本集群动作。
1.2 框架给出的答案
swarm_api 把以上全部封装成库。你写算法时只看到动作,看不到通信:
from swarm_api import Swarm
swarm = Swarm(num_drones=3) # 自动发现在线飞机
swarm.takeoff(1.5) # 三机同时起飞
swarm.goto_all([(0,2,1.5), (2,2,1.5), (4,2,1.5)]) # 各机飞各自目标,同时到达
swarm.land() # 三机同时降落
swarm.shutdown()
控制 1 架、3 架还是 10 架,代码量基本不变——这就是框架存在的意义:让算法开发者把时间花在算法上。
1.3 框架不做什么
明确边界,避免误用:
- 不做底层控制:姿态环、位置环都在 PX4 飞控里,框架只发设定点。这也意味着框架的输出频率(20Hz)不决定飞行品质。
- 不做轨迹规划/避障:
goto是"直线飞过去",路径上有障碍物它不管。规划算法是你(或后续规划包)的事。 - 不做全局坐标管理:仿真中每架飞机有自己的本地坐标系原点(见第 3 节),跨机全局规划需要真机 UWB 环境或自行做坐标对齐。
2. 架构与设计原理
2.1 分层结构
┌─────────────────────────────────────────────────┐
│ 你的算法 / 策略插件(Strategy 子类) │
├─────────────────────────────────────────────────┤
│ Swarm 多机并行层 │
│ · 自动发现在线飞机(扫描 ROS 话题) │
│ · 每机一个任务线程,动作真正并行 │
│ · 单机异常隔离 + SwarmError 汇总 │
│ · 编队辅助(line/column/triangle/grid) │
├─────────────────────────────────────────────────┤
│ Drone 单机原语层 │
│ · takeoff / goto / set_velocity / hover / land │
│ · 阻塞式语义 + 超时保护 │
│ · 实时状态属性(pos/yaw/armed/offboard) │
├─────────────────────────────────────────────────┤
│ 通信层(Drone 内部) │
│ · QoS 配置、ENU↔NED 转换 │
│ · 后台 20Hz 设定点流线程 │
│ · 切模式/解锁命令自动重发 │
├─────────────────────────────────────────────────┤
│ PX4 飞控(Offboard 接口)——仿真与真机完全同一套 │
└─────────────────────────────────────────────────┘
2.2 线程模型
理解线程模型对写出正确的算法很重要:
每架 Drone 有 2 个常驻后台线程(Drone() 构造时启动,shutdown() 时停止):
- 订阅线程:ROS executor 线程,持续接收飞控上报的位置与状态,更新
pos/yaw/armed/offboard属性。你读这些属性永远是最新值,不需要自己 spin。 - 设定点流线程:20Hz 循环。
takeoff()之后开始工作,把"当前目标"(位置点或速度)持续发给飞控。这是 Offboard 模式的生命线——你调用goto只是改了一个变量,真正维持飞行的是这个线程。
Swarm 的并行:调用 swarm.takeoff() 这类多机动作时,Swarm 为每架飞机临时创建一个任务线程,同时执行对应的单机阻塞原语,全部结束后才返回。因此:
swarm.takeoff(1.5)的总耗时 ≈ 最慢那架飞机的起飞耗时,而不是三架相加;- 多机动作是"同发同收"的屏障(barrier)语义:全部到位才继续往下走。
线程安全性:框架内部的共享状态(当前目标、模式标志)都是简单标量/列表的原子读写,你的算法线程直接调动作函数是安全的。但不要从多个线程同时调用同一架飞机的 goto——后调用会覆盖先调用的目标点,行为符合直觉但不保证你想要的时序。
2.3 Offboard 状态机与框架动作的关系
PX4 侧的关键状态转换,以及框架如何驱动它:
takeoff() land()
[待机/上锁] ──────────────────────→ [Offboard 飞行中] ──────────→ [自动降落] → [上锁]
↑ │ │ │
│ │ 1. 预发设定点流 1s │ │ goto():改目标点
│ │ 2. 循环发切模式+解锁 │ │ set_velocity():改速度目标
│ │ 命令直到生效 │ │ hover():目标点=当前位置
│ │ │ │
│ └──── 任意环节失败 ──────┴───┴──→ 抛 DroneError
│
└──── 飞控 failsafe(失联/姿态异常/围栏越界)随时可强制接管,
框架检测到 Offboard 丢失后立即报错(不等超时)
要点:
- 解锁≠起飞。
takeoff()内部完成了"预发流 → 切 Offboard → 解锁 → 爬升到目标高度"四步,对用户是一步。 - 降落由 PX4 执行。
land()先发NAV_LAND命令并停止设定点流(不停流飞控会拒绝降落命令),之后飞控自动降落、触地自动上锁,框架等到上锁才返回。 - failsafe 优先级高于一切。飞控触发失联/姿态/围栏保护时会强制退出 Offboard,此时框架的指令不再被听从——这是设计如此,安全永远优先于任务。
2.4 关键实现机制(使用框架不必读,改框架前必读)
| 机制 | 实现 | 为什么 |
|---|---|---|
| 设定点流 | 独立线程 20Hz 发布 OffboardControlMode + TrajectorySetpoint,未用字段填 NaN | PX4 要求 ≥2Hz 且未用通道必须 NaN,否则行为未定义 |
| 坐标转换 | 只有 enu_to_ned / yaw_enu_to_ned 两个函数,只在发布/订阅两处调用 | 转换散在代码各处是坐标系 bug 的万恶之源 |
| 高度基准 | takeoff(alt) 的目标高度 = 起飞前实测高度 + alt | EKF 高度原点有偏差,写死绝对海拔会出错 |
| 命令重发 | 切模式/解锁命令每 0.5s 重发,直到状态话题确认生效 | 单条命令可能丢失,"发了等确认"才是可靠做法 |
| Offboard 丢失检测 | goto 循环里监控 offboard 属性,丢失超过 2s 立即抛错 | 飞控 failsafe 接管后再等下去毫无意义 |
| 降落前断流 | land() 第一步停止设定点流 | 持续的位置设定点会让 PX4 保持 Offboard 并拒绝 NAV_LAND(实测踩过的坑) |
| 异常隔离 | Swarm 任务线程捕获单机异常 → 该机 hover → 汇总抛 SwarmError | 一架飞机出问题不该拖垮整个集群 |
3. 坐标系与航向约定
3.1 框架层:统一 ENU
框架所有输入输出(位置、速度、航向)都是 ENU(REP-103,ROS 标准):
北 y
↑
│ x = 东(East)
│ y = 北(North)
└──────→ 东 x z = 上(Up),海拔越高值越大
- 位置
(x, y, z):单位米。z向上为正,起飞 1.5m 就是z=1.5。 - 速度
(vx, vy, vz):单位 m/s。vy=0.5表示以 0.5 m/s 向北飞。 - 航向
yaw:单位弧度,0 = 朝东,逆时针为正。朝北 = π/2,朝西 = π,朝南 = -π/2。 - 航向角速度
yaw_rate:rad/s,逆时针为正。
3.2 底层:PX4 是 NED
PX4 飞控内部使用 NED(x=北,y=东,z=下),航向 0=北、顺时针为正。框架只在 drone.py 顶部的两个函数里做转换,一处转换、全局生效:
def enu_to_ned(x, y, z): return y, x, -z # 换轴即可
def yaw_enu_to_ned(yaw): return math.pi/2 - yaw # 两个约定相差 90° 且方向相反
如果你需要直接读 PX4 原始话题(绕过框架),务必记得这个差异。
3.3 本地坐标系 vs 全局坐标系(重要)
仿真环境:每架 PX4 实例有自己的本地坐标系,原点在各自出生点,互不相同。所以 swarm.goto_all((0, 2, 1.5)) 让三架飞机各自向"自己的北"飞 2m,互不冲突——demo 正是利用这一点。
真机 UWB 环境:所有飞机接入同一套 UWB 定位后,本地坐标系对齐到统一的全局坐标系(uwb_map)。此时 goto_all 传同一个点意味着真的飞向同一个物理点——会撞机!编排航线时各机目标点必须分开。
写算法时建议养成习惯:集群动作的目标点永远按机分别给出(goto_all([(x1,y1,z1), (x2,y2,z2), ...])),这样代码在两种环境下行为一致、都安全。
4. 编译与环境
框架位于 swarm_ws/src/swarm_api/,一键安装脚本 install.sh 已会自动编译。手动编译:
cd ~/0c00_ws/swarm_ws
source /opt/ros/humble/setup.bash
colcon build --packages-select swarm_api
每次使用前加载环境(每个新终端都要):
source /opt/ros/humble/setup.bash
source ~/0c00_ws/swarm_ws/install/setup.bash
依赖只有 rclpy 和 px4_msgs,纯 Python 实现,无 C++ 编译、无第三方 pip 包。
5. 快速上手
5.1 单机(Drone)
# 终端 1:单机仿真
~/0c00_ws/swarm_ws/src/bringup/scripts/start_swarm_sim.sh 1 1
# 终端 2
source /opt/ros/humble/setup.bash && source ~/0c00_ws/swarm_ws/install/setup.bash
python3 ~/0c00_ws/swarm_ws/src/bringup/scripts/demo_single_drone.py
demo_single_drone.py 核心逻辑(起飞 → 顺时针画 2m 正方形 → 回原点 → 降落):
from swarm_api import Drone
drone = Drone("uav_1") # 连接一架飞机
drone.takeoff(1.5) # 起飞到 1.5m
drone.goto(0, 2, 1.5) # 向北 2m(阻塞:飞到才返回)
drone.goto(2, 2, 1.5) # 向东 2m
drone.goto(2, 0, 1.5) # 向南 2m
drone.goto(0, 0, 1.5) # 向西 2m 回原点
drone.land() # 降落,自动上锁后返回
drone.shutdown() # 释放资源
5.2 集群(Swarm)
# 终端 1:三机仿真
~/0c00_ws/swarm_ws/src/bringup/scripts/start_swarm_sim.sh 3 1
# 终端 2
python3 ~/0c00_ws/swarm_ws/src/bringup/scripts/demo_swarm_square.py
demo_swarm_square.py 核心逻辑(三机同时画正方形):
from swarm_api import Swarm
swarm = Swarm(num_drones=3) # 自动发现 uav_1..uav_3
swarm.takeoff(1.5) # 全群同时起飞
for x, y in [(0,2), (2,2), (2,0), (0,0)]:
swarm.goto_all((x, y, 1.5)) # 全群同时飞向下一个角点
swarm.land()
swarm.shutdown()
注意两个 demo 的结构几乎一样——从单机到集群的学习成本只有一次思维转换:Drone 的单数动作换成 Swarm 的复数动作。
6. 阻塞语义、超时与并行模型
6.1 哪些动作阻塞、哪些不阻塞
| 动作 | 阻塞? | 返回时机 |
|---|---|---|
takeoff | 阻塞 | 飞机到达目标高度并悬停 |
goto / goto_all / goto_formation | 阻塞 | 到达判定满足(见下) |
land | 阻塞 | 触地并自动上锁 |
set_velocity / set_velocity_all | 不阻塞 | 立即返回,飞机持续按该速度飞 |
hover / emergency_stop | 不阻塞 | 立即返回(目标点已设为当前位置) |
6.2 到达判定与 tol
goto 的到达判定:水平误差与垂直误差同时小于 tol(默认 0.3m):
d_xy = math.hypot(pos_x - target_x, pos_y - target_y) < tol
d_z = abs(pos_z - target_z) < tol
- 室内/UWB 精度好:可以用
tol=0.15飞得更精确; - GPS 仿真/定位噪声大:
tol太小会导致飞机在目标附近反复微调、很久才判定到达,建议保持默认或放宽到 0.5; - 状态轮询周期 0.1s,不要把
tol设得比定位噪声还小。
6.3 timeout 语义
所有阻塞动作都有 timeout(默认 60s),超时抛 DroneError。注意:
- 超时不等于失败坠机——飞机仍在原目标点悬停(设定点流还在工作),你可以捕获异常后继续下达新指令;
- 长距离飞行要自己估算时间:
goto的飞行速度由 PX4 参数(默认约 1~2 m/s)决定,飞 20m 至少给 30s; takeoff的 timeout 覆盖"切模式+解锁+爬升"全过程,仿真刚启动 EKF 未收敛时可能需要更长。
6.4 Swarm 的屏障语义与部分失败
Swarm 的多机动作是屏障(barrier):所有飞机的任务线程都结束(成功或失败)后才返回。
- 全部成功 → 正常返回;
- 部分失败 → 失败机自动悬停,其余机已完成动作,最后抛
SwarmError(携带每机的错误明细)。
这意味着"三机编队变换"中若有一架超时,另外两架已经飞到新位置了——你的异常处理代码要意识到这一点,必要时在 except SwarmError 里让全群 hover() 再决定下一步。
7. 错误处理完整体系
7.1 异常类型
from swarm_api import DroneError, SwarmError
DroneError:单机操作失败。消息格式"uav_1: 具体原因"。SwarmError:多机并行执行的汇总错误。e.errors是{命名空间: 异常}字典,可逐机检查。
7.2 失败场景与框架行为对照表
| 场景 | 框架行为 | 飞机实际状态 |
|---|---|---|
| 启动时无飞控数据 | takeoff 10s 后抛 DroneError(等待超时) | 地面待机 |
| EKF 未收敛 / preflight 不过 | takeoff 循环重发命令直到 timeout 抛错 | 地面待机 |
goto 未在 timeout 内到达 | 抛 DroneError | 仍在目标点悬停,可继续下达指令 |
| 飞行中飞控 failsafe 接管 | goto 2s 内检测 Offboard 丢失,抛错 | 飞控按 failsafe 逻辑行动(悬停/返航/降落) |
未 takeoff 就 goto/set_velocity | 立即抛 DroneError | 地面待机 |
land 超时未上锁 | 抛 DroneError | 可能仍在降落中,检查飞控状态 |
| 多机动作中某机失败 | 该机自动 hover,其余继续,最后抛 SwarmError | 失败机悬停,其余机正常 |
| 程序崩溃 / Ctrl+C | 设定点流停止 → 飞控 Offboard 失联保护接管 | 自动降落(PX4 默认行为) |
7.3 推荐的错误处理写法
from swarm_api import Swarm, SwarmError
swarm = Swarm(num_drones=3)
try:
swarm.takeoff(1.5)
swarm.goto_all([(0,2,1.5), (2,2,1.5), (4,2,1.5)])
except SwarmError as e:
for ns, err in e.errors.items():
print(f"{ns} 失败: {err}")
swarm.hover() # 先稳住全群
# ... 决策:重试 / 放弃任务 ...
finally:
swarm.land() # 无论如何安全降落
swarm.shutdown()
如果不想自己写 try/finally,直接用 Strategy 基类(见 8.3),异常处理已托管。
8. 进阶用法
8.1 速度控制(非阻塞,适合连续轨迹)
set_velocity 设定速度后立即返回,飞机持续按该速度飞,直到下一个指令。适合圆轨迹、跟踪、人工摇杆这类连续控制场景:
import math, time
from swarm_api import Swarm
swarm = Swarm(num_drones=3)
swarm.takeoff(1.5)
t0 = time.time()
while time.time() - t0 < 10: # 飞 10 秒圆
t = time.time() - t0
swarm.set_velocity_all((-math.sin(t), math.cos(t), 0.0)) # 切向速度
time.sleep(0.1)
swarm.hover() # 切回位置模式悬停
swarm.land()
swarm.shutdown()
要点:
- 速度指令周期建议 10~20Hz(
sleep(0.05~0.1)),框架流线程会按最新值持续发出; - 从速度模式切回位置模式只需调
hover()或goto(),框架自动处理OffboardControlMode标志位切换; yaw_rate参数可以让飞机边飞边转头(如搜索时扫描)。
8.2 集群动作 + 单机微调混用
Swarm 提供三种单机访问方式,可随时对个别飞机单独下指令:
swarm = Swarm(num_drones=3)
swarm.takeoff(1.5)
swarm["uav_2"].goto(1, 1, 2.5) # 按命名空间取:只让 uav_2 爬升
swarm[0].set_velocity(0, 0.3, 0) # 按下标取:uav_1 低速向北
swarm.drones[2].hover() # 按列表取
swarm.goto_all((0, 3, 1.5)) # 全群再一起动(注意此时各机高度不同)
swarm.land()
swarm.shutdown()
8.3 用 Strategy 编写可复用的集群算法(推荐)
把算法写成策略插件,起飞、异常处理、降落、收尾全部托管——这也是平台内置基线算法(编队、覆盖、搜索、任务分配)的组织方式,便于你的算法与基线在相同环境、相同指标下公平对比:
from swarm_api import Strategy
class TriangleDemo(Strategy):
name = "triangle_demo"
def setup(self, swarm):
# 可选:算法前的准备(订阅传感器、加载地图、打印参数)
print(f"将对 {len(swarm)} 架飞机执行三角编队")
def run(self, swarm):
# 必须实现:算法主体
swarm.takeoff(1.5)
swarm.goto_formation("triangle", spacing=2.0, z=1.5)
def teardown(self, swarm):
# 可选:降落后的清理(保存数据、生成报告)
print("数据已保存")
if __name__ == "__main__":
TriangleDemo().main(num_drones=3)
main() 托管的完整生命周期:
发现飞机 → setup() → run() →(异常则全群悬停)→ 自动降落 → teardown() → 释放资源
- 运行中 Ctrl+C 会安全触发降落;
run()里抛任何异常都会被捕获、打印堆栈、全群悬停后降落——写算法时不用为安全收尾分心。
8.4 长任务与监控循环
阻塞动作之间可以随时读状态做监控或决策:
swarm.takeoff(1.5)
for d in swarm.drones:
print(f"{d.ns}: 高度 {d.pos[2]:.2f} m, 航向 {d.yaw:.2f} rad, "
f"armed={d.armed}, offboard={d.offboard}")
9. 安全机制与失效行为
框架内置五道防线(全部自动生效,无需配置):
| # | 机制 | 保护对象 |
|---|---|---|
| 1 | 后台 20Hz 设定点流 | 杜绝"断流导致 Offboard 退出"这类低级事故 |
| 2 | Offboard 丢失 2s 快速报错 | 飞控 failsafe 接管后,算法立即知情而不是傻等 |
| 3 | 单机异常隔离 + 自动悬停 | 故障机不拖垮全群 |
| 4 | PX4 失联保护 | 程序崩溃/Ctrl+C 后飞控自动接管降落(最后防线) |
| 5 | PX4 电子围栏 | 仿真默认 10m×6m 围栏(启动脚本 GF_ACT 环境变量可配),越界自动返航 |
以及两条使用纪律(框架管不了,靠流程):
- 真机之前必须先过仿真回归——这是平台的强制流程,没有例外;
- 真机实验时人手握遥控器待命——框架再可靠也只是软件,PX4 的遥控器接管优先级最高,用它。
10. 性能特征与调参建议
| 项目 | 数值 | 说明 |
|---|---|---|
| 设定点流频率 | 20Hz / 机 | PX4 要求 ≥2Hz,20Hz 是经验舒适值 |
| 状态轮询周期 | 0.1s | goto 到达判定的检查间隔 |
| 命令重发间隔 | 0.5s | 切模式/解锁/降落命令 |
| 控制链路延迟 | 典型 20~50ms | 指令到飞控生效(与产品指标一致) |
| 每机线程开销 | 2 常驻 + 1 临时(动作期间) | 10 机约 30 线程,CPython 无压力 |
| 实测规模 | 3 机(仿真) | 产品承诺 3~4 机,6 机演示 |
调参建议:
- 机数 > 6:先测通信链路的并发上限(WiFi 验证链路有实测报告),框架本身不是瓶颈;
tol不要小于定位噪声:仿真 GPS 约 0.10.3m,UWB 典型 0.10.3m(以实测报告为准);- 不要追求过快的 goto 接力:每个
goto到达后飞控需要时间稳定,密集小步点不如用set_velocity走连续轨迹。
11. 基于框架开发自己的算法包
推荐的工程结构(以 ROS 2 Python 包形式,与 swarm_api 并列):
swarm_ws/src/
├── swarm_api/ # 框架(不要改,改了全平台受影响)
└── my_algorithm/ # 你的算法包
├── package.xml # exec_depend: swarm_api, rclpy
├── setup.py
└── my_algorithm/
├── __init__.py
├── my_strategy.py # Strategy 子类:算法主体
└── my_planner.py # 纯计算逻辑(不碰 ROS,方便单元测试)
开发规范:
- 算法逻辑与框架调用分离:
my_planner.py只算目标点(输入状态、输出坐标),不 import rclpy——这样可以脱离仿真做单元测试; - 不要继承/修改 Drone 的内部:需要新原语时,在你的包里组合现有原语(如"飞矩形扫描"= 循环 goto);确实需要框架级新能力,提需求给平台维护者;
- 坐标一律 ENU:你的包对外接口也用 ENU,全平台统一;
- 版本绑定:算法包、参数文件、固件版本三者绑定归档,真机实验可追溯到精确版本组合。
12. 从仿真迁移到真机
框架层零代码改动。迁移检查清单:
| 项目 | 仿真 | 真机 | 要做的 |
|---|---|---|---|
| 命名空间 | uav_N,自动发现 | 自动发现(同一规则) | 无 |
| 坐标系 | 每机各自本地系 | UWB 统一全局系 | 检查 goto_all 目标点是否会撞机 |
| 高度基准 | GPS(已降噪) | 光流/UWB | 无(takeoff 用相对高度,天然免疫) |
| 起飞高度 | 1.5m 任意 | 受场地净高限制 | 确认围栏与净高 |
| 通信 | 本地回环 | ESP32 WiFi 透传 | 测并发延迟与丢包 |
| 失效行为 | 围栏 10m×6m | 按 SOP 配置 | 布场后核对 GF 参数 |
| 安全 | 无所谓 | 防护网+遥控器待命 | 完成安全培训(验收前置条件) |
13. 当前限制与边界
使用前先了解这些,避免踩坑:
- 无避障:
goto走直线,不管路径上有什么; - 无轨迹插值:目标是阶跃的,飞控自身的位置环会平滑它,但不要指望精确的时间同步轨迹(多机"同时转弯"是近似的同时);
- 无全局坐标变换层:仿真中跨机的全局规划需要自己处理各机原点偏移(真机 UWB 无此问题);
- 速度模式无位置保护:
set_velocity期间框架不做位置监控,飞出围栏由 PX4 围栏兜底; - Python GIL:每机状态回调与流线程共享 GIL,20 机以上规模未验证(产品也不承诺这个规模);
- 不订阅的话题拿不到:框架目前只订阅位置和状态。电量、GPS 质量等需要时请在你的算法包里自行订阅(
/uav_N/fmu/out/battery_status等)。
14. 常见问题 FAQ
Q1:Swarm(num_drones=3) 报"只发现 0 架飞机"?
仿真没启动或没启动完。先运行 start_swarm_sim.sh 3 1,等 30~40 秒再运行脚本。用 ros2 topic list | grep vehicle_local_position 确认飞机在线。
Q2:takeoff 超时,报"进入 Offboard/解锁超时"?
飞控 preflight 检查未通过。仿真刚启动时 EKF 需要 10~20 秒收敛,等一会再跑;真机检查定位源、罗盘与起飞前检查项(QGC 里能看到具体哪项不过)。
Q3:飞机往反方向飞 / 高度反了?
确认传的是 ENU(x=东,y=北,z=上)。框架不接受 NED。想复习转换看 swarm_api/drone.py 顶部的两个函数,全部转换只在那里发生。
Q4:goto 总在目标附近晃很久才判定到达?
tol 相对定位噪声太小了。放宽 tol(如 0.5),或接受它——飞机到位精度没变,只是判定晚了。
Q5:多机动作抛 SwarmError,剩下的飞机怎么办?
它们已完成该动作并在原地悬停,处于安全状态。捕获异常后调 swarm.hover() 稳住,再决定重试或降落(见 7.3 的写法)。
Q6:能在 set_velocity 期间读位置做闭环吗?
可以,这正是设计用法:while 循环里读 drone.pos、算速度、调 set_velocity,周期 0.05~0.1s。注意做位置保护(框架不管,见第 13 节第 4 条)。
Q7:最大能控几架? 框架本身无硬上限,瓶颈在 WiFi 链路和地面站性能。仿真实测 3 机;产品承诺 3~4 机、6 机演示。
Q8:和 PX4 官方 uXRCE-DDS 例程什么关系?
框架底层就是官方 Offboard 接口(trajectory_setpoint + offboard_control_mode + vehicle_command),把工程细节产品化了。想深入底层,swarm_api/drone.py 全文约 250 行,注释完整,是最好的学习材料。
Q9:为什么 land() 要先停设定点流?
PX4 在 Offboard 模式下持续收到位置设定点时,会认为外部计算机仍在主动控制,从而拒绝 NAV_LAND。这是实测发现的飞控行为,框架已在内部处理,了解即可。
Q10:真机上 takeoff(1.5) 和仿真行为完全一致吗?
一致。高度基准都是"当前实测高度 + 1.5m",不依赖绝对海拔,对 GPS/光流/UWB 任何高度源都成立。
文档版本:v2.0(2026-07-27),对应 swarm_api 0.1.0。框架 API 变更时本文档同步更新。