D9 RL 敏捷飞行与 sim-to-real——Swift / SimpleFlight / RLtools / RAPTOR¶
性质:算法工程教学 | 难度跨度:⭐⭐ ~ ⭐⭐⭐⭐ | 预计精读:20-28 小时(第 65-66 周,2 周)
一句话定位:从 2017 年 Hwangbo 首次 sim-to-real RL 四旋翼,到 2023 年 Swift 在物理赛道上击败三位人类世界冠军,本章完整讲透**基于学习的控制如何在无人机敏捷飞行上超越经典规控**——为什么 CTBR 是正确的动作空间、sim-to-real gap 怎样被残差模型/系统辨识/域随机化弥合、以及一个 2084 参数的 GRU 如何零样本泛化到 10 种异构四旋翼。
本章定位:⚪ 部分共享——PPO/TD3 + GPU 并行仿真的工具链与足式 RL 完全共享;但动作空间(CTBR 4D vs 关节位置 12D)、sim-to-real 技术(BEM 气动 vs 接触模型)、部署目标(Jetson/MCU vs Jetson/NUC)是无人机特有的。
前置自测¶
开始前先回答下面 5 个问题。答不出 2 题以上,建议先回前置章节补齐——D9 把"经典规控的语言"翻译成"RL 的语言",欠了账会在第一章就跟不上为什么 RL 能赢。
-
PPO 与 TD3 的核心区别是什么? on-policy 与 off-policy 各自的样本效率量级是多少?为什么 GPU 并行仿真天然偏向 on-policy 的 PPO? (答不出 → 回足式 RL 章 / 强化学习基础,PPO/TD3 小节)
-
微分平坦(Differential Flatness)的定义是什么? 四旋翼的 4 个平坦输出是哪些?给定平坦输出及其导数,为什么可以**纯代数地**反解出总推力 \(T\) 和 3 轴体角速率 \(\omega\)? (答不出 → 回 D1 微分平坦,§D1.1)
-
旋转的三种表示——欧拉角、四元数、旋转矩阵——各自的奇异性问题是什么? 欧拉角的万向节锁发生在哪里?四元数的"双覆盖"指什么? (答不出 → 回 SO(3) / 姿态表示基础章)
-
域随机化(Domain Randomization)的基本思想是什么? 它通过什么机制让策略对未建模动态鲁棒?它的代价(过度正则化)从何而来? (答不出 → 回足式 RL 章 / sim-to-real 基础)
-
高斯过程(GP)与 k 近邻(kNN)回归的本质区别是什么? GP 为什么能同时输出"均值 + 方差"?kNN 为什么是非参数、无需训练的? (答不出 → 回机器学习基础 / 回归方法章)
参考答案要点(先自己答,再对照):
-
PPO 是 on-policy(每次更新后旧数据作废,需 \(\sim10^8\)–\(10^9\) 步),TD3 是 off-policy(用 replay buffer 复用历史数据,需 \(\sim10^6\)–\(10^7\) 步)。GPU 并行仿真能同时跑数千个环境、每秒产出海量**新鲜**样本,正好喂饱 on-policy 对"当前策略数据"的饥渴,所以 PPO + GPU 并行是绝配;off-policy 的 replay buffer 反而在这种吞吐下显得多余。
-
微分平坦:存在平坦输出 \(\sigma\),使全部状态 \(x\) 与控制 \(u\) 都能写成 \(\sigma\) 及其有限阶导数的代数函数。四旋翼平坦输出 \(\sigma = (x, y, z, \psi)\)。能纯代数反解是因为:位置二阶导给出合加速度 → 加上重力得推力矢量方向(决定姿态 \(R\) 的两列)与大小(决定 \(T\));\(\psi\) 决定第三列;姿态对时间求导得到 \(\omega\)。全程无需积分或求解 ODE。
-
欧拉角在 pitch = ±90° 处万向节锁(两个旋转轴重合,丢一个自由度);四元数 \(q\) 与 \(-q\) 表示同一姿态(双覆盖),网络须额外学到这种等价;旋转矩阵 9 维无奇异、无歧义,代价是冗余(9 > 3)。
-
域随机化:训练时随机抖动仿真参数(质量、惯量、推力系数、延迟等),强迫策略学到对一整段参数分布都有效的鲁棒行为。代价是策略必须为"最坏参数"留裕度,把本可精确利用的信息也"随机掉了",导致过度正则化、标称性能下降。
-
GP 是贝叶斯非参数模型,对函数加先验(核函数),后验同时给出均值(最佳估计)和方差(不确定性)——所以能采样噪声。kNN 直接对最近 \(k\) 个样本做(加权)平均,不假设函数形式、不需训练、不外推,天然适合小数据集与确定性映射。
本章目标¶
学完本章后,你应该能够:
- **严格论证**为什么基于学习的控制能在敏捷飞行上超越经典规控——从经典管线的三重瓶颈(气动不可建模、分层信息瓶颈、高频计算约束)出发,而非泛泛而谈"神经网络更强"
- 完整复述 Swift 的六阶段管线:BEM 仿真器 → PPO 训练(100 并行 agent、50 min)→ 感知(CNN 门检测 + VIO)→ 残差模型(GP 观测残差 + kNN 动力学残差,~50s 真实数据)→ 策略微调 → Jetson TX2 部署,并能解释每一阶段"为什么必须存在"
- **从动作空间第一性原理出发**解释为什么 CTBR 在鲁棒性与敏捷性之间取得最优平衡——对比 LV / CTBR / SRT 三者,并把 CTBR 与微分平坦联系起来
- 逐条拆解 SimpleFlight 的五因子配方:为什么旋转矩阵(9D)而非四元数/欧拉角?为什么动作差分平滑而非动作幅度惩罚?为什么 SysID + 选择性 DR 而非盲目 DR?
- 掌握"足式 RL → 无人机 RL"的迁移清单:什么能周末复用(PPO / Isaac Gym / 非对称 AC / 教师-学生蒸馏),什么必须重建(动作空间 / 奖励设计 / 安全协议)
- 解释 RLtools 的 C++17 头文件 RL 范式:为什么纯模板元编程能在 18 秒内训练完成?策略权重如何烘焙成
constexpr数组部署到 STM32? - 理解 RAPTOR 基础策略的跨平台泛化机制:GRU 隐状态如何充当隐式系统辨识器?元模仿学习(1000 teacher → 1 student)如何工作?线性探针为什么能从隐状态线性提取推重比?
- 独立完成一次 sim-to-real 部署:按 SysID → 仿真校验 → 训练 → sim-to-sim → HIL → 逐步放速度的六步链路,把一个策略安全地搬上真机
知识导航¶
本章的知识结构是一棵以"基于学习的控制如何在敏捷飞行上超越并落地"为根的树。树干是三个递进的核心问题,树枝是每个问题的代表系统与工程细节。
为什么 RL 能超越经典规控? (§D9.0 三重瓶颈 + 结构性优势)
│
▼
怎样把一个 RL 策略训练到能赢人类? (§D9.1 Swift 完整管线)
│
├─→ 哪些设计选择是关键的? (§D9.2 SimpleFlight 五因子)
│ │
│ ▼
├─→ 我已有的足式 RL 能复用多少? (§D9.3 迁移速查)
│
├─→ 能训练得更快、部署得更小吗? (§D9.4 RLtools 秒级训练 + MCU)
│ │
│ ▼
├─→ 能用一个策略统治所有平台吗? (§D9.5 RAPTOR 基础策略)
│
├─→ RL 和 MPC 能结合吗? (§D9.6 MPC+RL 混合谱系)
│
└─→ 用什么仿真器、怎样安全落地? (§D9.7 仿真平台 + §D9.8 部署链路)
| 小节 | 主题 | 难度 | 一句话 |
|---|---|---|---|
| §D9.0 | 为什么 RL 能超越经典规控 | ⭐⭐ | 三重瓶颈 + 端到端目标 + 隐式学未建模动态 |
| §D9.1 | Swift——击败人类冠军的完整剖析 | ⭐⭐⭐ | 六阶段管线;CTBR;残差模型让仿真匹配真实 |
| §D9.2 | SimpleFlight 五因子配方 | ⭐⭐⭐ | 旋转矩阵 + 时间向量 + 动作差分 + 选择性 DR + 大批量 |
| §D9.3 | 足式 RL → 无人机 RL 迁移 | ⭐⭐ | 工具链复用,动作/奖励/安全重建 |
| §D9.4 | RLtools——秒级训练与 MCU 部署 | ⭐⭐⭐ | TD3 + C++17 模板元编程;权重烘焙为 constexpr |
| §D9.5 | RAPTOR——2084 参数基础策略 | ⭐⭐⭐⭐ | GRU 隐状态 = 隐式系统辨识器;元模仿学习 |
| §D9.6 | MPC+RL 混合谱系 | ⭐⭐⭐ | DATT / Neural-MPC / AC-MPC / Neural-Fly |
| §D9.7 | 仿真环境全景对比 | ⭐⭐ | 选型:教学/研究/极速/视觉/可微 |
| §D9.8 | Sim-to-Real 部署完整链路 | ⭐⭐⭐ | 六步:SysID → 校验 → 训练 → sim2sim → HIL → 放速度 |
两条阅读线:
- 理论线(理解为什么):§D9.0→§D9.1→§D9.2→§D9.5→§D9.6,重点是动作空间论证、sim-to-real 原理、基础策略泛化机制
- 工程线(会用就行):§D9.1→§D9.3→§D9.4→§D9.7→§D9.8,重点是迁移清单、训练框架、部署链路
无论哪条线,§D9.0 和 §D9.1 都是必读——它们建立"RL 为什么能赢"和"一个完整系统长什么样"的全局图景。
前置知识桥接¶
回顾 D1(微分平坦):D1 证明了四旋翼是微分平坦系统——给定一条平坦输出曲线 \(\sigma(t) = (x, y, z, \psi)\),全部 12 维状态和 4 维控制可以**纯代数地**(无需积分)从 \(\sigma\) 及其前四阶导数恢复。本章的核心动作空间 CTBR(集体推力 + 体角速率)正是"平坦输出空间里的动作"——所以本章会反复回到 D1:CTBR 之所以对 sim-to-real gap 鲁棒,本质是它利用了四旋翼的本征几何结构,而不是去硬学电机的非线性。如果 D1 的"平坦输出 → 推力矢量 → 姿态"这条反解链记不清了,建议先翻回去。
回顾足式 RL(PPO / Isaac Gym / 非对称 AC / 教师-学生蒸馏):足式 RL 章建立的整套训练基础设施——PPO 实现、GPU 并行环境(Isaac Gym/Lab)、非对称 actor-critic(critic 看特权信息、actor 不看)、教师-学生策略蒸馏(特权 teacher → 部署 student)——在本章**几乎零改动复用**。本章 §D9.3 会给出精确的迁移清单。一句话预热:无人机 RL 不是从零另起炉灶,而是"换一个环境类 + 重写奖励 + 加一套安全协议"。
回顾 D2(MPC):D2 的非线性 MPC 是本章对照的"经典最强基线"。§D9.0 会引用 Song 2023 的结论——RL 在同一赛道上超越了时间最优 MPC;§D9.6 会讲 MPC 与 RL 如何融合(DATT / Neural-MPC / AC-MPC)。理解 MPC"在线求解有限时域最优控制 + 显式处理约束"这两个特点,才能看懂 RL 补了它什么、又丢了它什么(形式化安全保证)。
前向预告:本章是无人机规控板块的"学习方法"压轴。它与前面 D3(多项式轨迹)、D4(B 样条)、D5(MINCO)等**优化轨迹**方法形成对照——那些方法显式生成轨迹再跟踪,本章的 RL 策略则把"规划 + 控制"压成一个端到端的状态→动作映射。学完本章你会理解一个判断:结构化、追求极限性能的场景(竞速、穿越)选 RL;安全关键、需要认证的场景(载人、巡检)选优化 + MPC;两者还能混合。
预计阅读时间¶
| 模式 | 时长 | 适合 |
|---|---|---|
| 精读 | 20-28 小时 | 第一次系统学无人机 RL:逐节读动机→对比→理论→陷阱,亲手跑 SimpleFlight / RLtools,做完 A 型练习。建议分 5-6 次(每个核心系统一次)。 |
| 速读 | 5-7 小时 | 有足式 RL 基础、想建立无人机侧的全局图景:读每节的"动机"和关键对比表(CTBR vs SRT、五因子、迁移清单),跳过代码细节与超参数。 |
| 速查 | 40-60 分钟 | 已学过、回来查特定结论:直接定位小节,看对比表 + 速查表 + 故障排查手册 + 符号表。 |
科研发展脉络¶
| 年份 | 论文 | Venue | 核心贡献 |
|---|---|---|---|
| 2017 | Hwangbo 等, "Control of a Quadrotor with Reinforcement Learning" | RA-L | 首次 sim-to-real RL 四旋翼:SRT 动作空间;SVD 自然梯度;Hummingbird 恢复悬停 |
| 2019 | Molchanov 等, "Sim-to-(Multi)-Real: Transfer of Low-Level Robust Control Policies to Multiple Quadrotors" | IROS | 单一策略零样本迁移到**多个异构 Crazyflie**;域随机化+延迟建模 |
| 2021 | Loquercio 等(UZH RPG), "Learning High-Speed Flight in the Wild" | Science Robotics | 特权模仿学习:完整地图 teacher → 深度图 student;10 m/s 森林穿越 |
| 2022 | Kaufmann, Bauersfeld, Scaramuzza(UZH RPG), "A Benchmark Comparison of Learned Control Policies for Agile Quadrotor Flight" | ICRA | CTBR 动作空间发现:系统对比 LV/CTBR/SRT——CTBR 在扰动下保持稳定;非对称 actor-critic |
| 2023 | Song, Romero, Müller, Koltun, Scaramuzza, "Reaching the Limit in Autonomous Racing: Optimal Control vs RL" | Science Robotics | RL 在同一赛道上**超越时间最优 MPC**:峰值 >12g、~108 km/h |
| 2023 | Kaufmann 等(UZH RPG), "Champion-level Drone Racing Using Deep RL" (Swift) | Nature | 击败三位世界冠军:机载感知+PPO+残差 GP/kNN;40 ms 感知-动作延迟 vs 人类 ~220 ms |
| 2024 | Eschmann, Albani, Loianno(NYU ARPL), "Learning to Fly in Seconds" | RA-L | 18 秒训练:纯 C++17 头文件 RL(RLtools);TD3+课程;STM32 部署 |
| 2025 | Chen, Yu 等(SJTU/THU), "SimpleFlight: What Matters in Zero-Shot Sim-to-Real RL for Quadrotor Control" | RA-L | 五因子配方:输入空间、奖励、SysID+选择性 DR、CTBR、大批量 PPO;可复现基线 |
| 2025 | Eschmann 等(NYU ARPL), "RAPTOR: A Foundation Policy for Quadrotor Control" | Science Robotics | 2084 参数基础策略:GRU 隐式系统辨识;32g-2.4kg 10 种异构四旋翼零样本;MCU 部署 |
关键趋势:策略从更大更复杂(Swift ~128×2 MLP)到**更小更通用**(RAPTOR 2084 参数 GRU),训练从小时级(Swift)到**秒级**(RLtools),部署从 Jetson GPU(Swift)到**STM32 MCU**(RLtools/RAPTOR)。
§D9.0 为什么 RL 能超越经典规控 ⭐⭐¶
动机¶
在进入具体系统架构之前,我们需要回答一个根本性问题:为什么在无人机敏捷飞行中,基于学习的控制能够超越精心设计的经典控制器? 这不是一个显而易见的结论——PID/MPC 在工业界已经部署了几十年,理论基础扎实,工程可靠性高。一个常见的误解是"RL 赢只是因为神经网络拟合能力强"——但这个回答经不起推敲:MPC 配上足够高阶的轨迹参数化,函数表达能力并不弱。真正的答案藏在**问题被分解的方式**里,而不是某个模块的拟合能力里。本节就从经典管线的结构性瓶颈出发,论证 RL 的优势从何而来——以及它在哪些场景反而是劣势。
反面:如果继续沿用经典分层管线会怎样¶
设想你坚持用"全局规划 → 轨迹优化 → 跟踪控制"这套经过工业验证的分层管线去打一场 FPV 竞速。你会发现:无论你把每一层调到多优,整体性能都卡在一个天花板下——因为分层的本质是在层与层之间插入了**显式中间表示**(一条被参数化的轨迹),而这个表示一旦确定,就把"可表达的飞行行为"框死了。Song 2023 的实验正是这样:即使给 MPC 完美的动力学模型和无限的计算时间,它依然打不过 RL——不是因为它"算得不够好",而是因为它"在求解一个被提前阉割过的问题"。这就是为什么我们需要重新审视:到底是哪三道结构性瓶颈,让经典方法在推向极限时失速。
理论¶
0.1 经典规控的三重瓶颈¶
瓶颈 1:非线性气动效应不可精确建模
四旋翼在低速悬停时,推力与转速的平方成正比(\(T = k_f \Omega^2\)),气动力矩可以用简单的二次模型近似。但在敏捷飞行中(速度 > 5 m/s,加速度 > 2g),以下效应变得显著:
1. 叶片翼型失速(blade stall):当入流角超过临界值,升力系数急剧下降
2. 旋翼间气动干涉(rotor-rotor interaction):邻近旋翼的下洗流相互影响
3. 机身-旋翼干涉(body-rotor interaction):机身对旋翼入流产生遮蔽和偏转
4. 地面效应与天花板效应:近壁面时推力增加 10-30%
5. 动态入流(dynamic inflow):快速机动时入流速度剧变,推力响应滞后
6. 叶片挥舞(blade flapping):柔性桨叶在高速飞行中产生额外俯仰力矩
经典控制器通常将这些效应统称为"扰动",用鲁棒控制的 \(\Delta\) 来覆盖。但这意味着控制器必须为**最坏情况**留出裕度,从而牺牲性能。
瓶颈 2:规划-控制分离的信息瓶颈
经典管线将问题分解为:
每一级之间有一个**显式中间表示**(轨迹),这个中间表示本身就限制了可表达的行为空间。Song 等 (Science Robotics 2023) 的实验直接验证了这一点:
"The fundamental advantage of RL over OC is not that it optimizes its objective better but that it optimizes a better objective."
他们发现,即使给 MPC 完美的动力学模型和无限的计算时间,MPC 仍然因为轨迹参数化的限制(例如,用多项式或 B-spline 表示轨迹)而无法达到 RL 的性能。RL 策略直接从状态映射到动作,没有中间瓶颈。
瓶颈 3:高频决策的计算约束
敏捷飞行要求控制频率 ≥ 100 Hz(理想 500 Hz)。经典 MPC 的在线优化在这个频率下面临严峻挑战:
| 方法 | 典型推理延迟 | 频率上限 | 备注 |
|---|---|---|---|
| 非线性 MPC (acados) | 2-10 ms | ~200 Hz | 需要 Jetson 级算力 |
| DATT (RL+L1) | < 3.2 ms | ~300 Hz | 比 NMPC 快 4 倍以上 |
| Swift (MLP 2×128) | < 1 ms | ~500 Hz | Jetson TX2 上推理 |
| RLtools (MLP 3×32) | < 0.1 ms | ~5000 Hz | STM32 MCU 上推理 |
| RAPTOR (GRU 2084) | < 0.5 ms | ~2000 Hz | MCU 上推理 |
神经网络策略的推理是**纯前向传播**——矩阵乘法+非线性激活——天然适合在低功耗硬件上高频运行。
0.2 RL 的结构性优势¶
优势 1:端到端优化目标
RL 直接优化任务目标(通过奖励函数),不需要人为设计中间表示。在竞速场景中:
RL 策略可以学到人类设计者想不到的策略——例如,Swift 学会了在门之间走一条与人类飞手完全不同的路径,因为这条路径虽然看起来"绕远",但允许更高的弯道速度。
优势 2:隐式学习未建模动态
RL 策略在训练过程中与仿真器交互了数十亿步,隐式地学到了:
这些效应不需要被显式建模——它们通过 domain randomization 和残差学习被编码在策略权重中。
优势 3:感知-控制联合优化
在 Swift 中,感知感知奖励 \(r_{\text{perc}}\) 让策略学会了在飞行时主动调整机体姿态,使相机保持对准下一个门。这是一个**感知与控制的联合优化**——经典管线中,感知和控制是分别设计的,无法实现这种协同。
0.3 Song 2023 的定量验证:RL 在相同赛道超越时间最优 MPC¶
Song 等在 Science Robotics 2023 的论文 "Reaching the Limit in Autonomous Racing" 中提供了迄今最严格的对比:
| 维度 | 时间最优 MPC | RL 策略 |
|---|---|---|
| 动力学模型 | 完全已知(精确辨识) | 同一仿真器中训练 |
| 规划 | CPC 时间最优轨迹 | 无显式轨迹 |
| 控制频率 | 100 Hz | 100 Hz |
| 赛道单圈最快时间 | 5.16 s | 4.76 s (快 7.8%) |
| 峰值加速度 | ~8g | >12g |
| 峰值速度 | ~90 km/h | ~108 km/h |
关键发现:
- MPC 受限于轨迹参数化——无法表示"先减速再猛加速"的非直觉策略
- MPC 在弯道处必须严格跟踪预规划路径,而 RL 可以自由选择弯道策略
- RL 策略学会了利用电池电压在满电时更激进、低电时更保守的**电池管理策略**
- 但 RL 策略在未见赛道上泛化能力弱于 MPC——这是 RL 的固有局限
本质洞察:RL 不是"更好地求解了同一个优化问题",而是"求解了一个更好的优化问题"。经典规控的分层分解是一种**近似**——它用"规划 + 跟踪"两个易解的子问题逼近"端到端最优控制"这个难解的总问题。在大多数工况下这个近似足够好,但在推到动力学极限(>12g、>100 km/h)时,分解引入的次优性就会暴露成可测量的性能差距。这条洞察是整章的总纲:后续每一个系统,本质上都是在回答"如何更直接地优化真正想要的目标"。
反事实——如果不分层会怎样? 假设我们彻底放弃中间的轨迹表示,让一个函数直接吃下"当前状态 + 下一个门的位置",吐出"推力 + 体角速率"。这正是 RL 策略做的事。代价是:你失去了那条可以画出来、可以验证、可以加约束的显式轨迹——调试从"看哪一层错了"变成"对着一个黑箱试错",安全从"在轨迹上加硬约束"退化成"在奖励里加软惩罚"。所以"不分层"换来了性能上限,丢掉了可解释性与形式化安全——这正是 §0.4 要展开的权衡。
类比——分层管线像流水线工厂,端到端 RL 像全能工匠(但不像翻译流水线)。 分层管线**像**汽车流水线:每个工位(规划/优化/跟踪)只管自己那道工序,接口(轨迹)标准化,易于分工、调试和替换——但流水线的总效率被"最慢工位 + 工位间的搬运损耗"锁死。端到端 RL 像**一位从毛坯到成品全程亲手打磨的工匠:没有工序间的搬运损耗,能为最终成品做全局取舍,所以上限更高。 但**不像**自然语言翻译流水线那种"分层几乎无损"的场景——翻译里"分词→句法→生成"各层的中间表示信息损失很小,分层代价低;而四旋翼竞速里,"用多项式/B样条参数化的轨迹"这个中间表示对真正可行的极限行为是**有损压缩,所以这里分层的代价远大于翻译。边界就在于:中间表示对最终目标是否有损——有损则 RL 占优,近乎无损则分层的工程性更划算。
0.4 但 RL 不是银弹——与经典方法的互补性¶
RL 在以下场景仍然不如经典方法:
| 场景 | 经典方法优势 | RL 劣势 |
|---|---|---|
| 安全保证 | Lyapunov/CBF 可以提供形式化证明 | 无理论保证,依赖经验测试 |
| 可解释性 | 控制增益直接对应物理量 | 黑箱策略,难以调试 |
| 泛化 | 基于物理的控制器天然泛化 | 需要 DR/残差学习/基础策略 |
| 少数据场景 | 基于模型的方法数据效率高 | on-policy RL 需要数亿步交互 |
| 故障安全 | 可以设计确定性后备控制器 | 策略失败模式不可预测 |
这解释了为什么 MPC+RL 混合方法(§D9.6 节)是一个活跃的研究方向。
本节常见陷阱¶
陷阱 0-1:把"RL 超越 MPC"误读成"RL 在所有维度都更强"
- 错误描述:读到 Song 2023 "RL 比时间最优 MPC 快 7.8%",就得出"以后做无人机控制都该上 RL,MPC 过时了"。
- 现象/后果:在一个安全关键项目(如载人/巡检)里上纯 RL,无法通过认证审查;或在一个全新、未训练过的场景里部署 RL,策略行为不可预测、频繁炸机。
- 根本原因:混淆了"单一指标(单圈时间)上的峰值性能"与"全维度的工程可用性"。RL 赢的是**在充分训练的结构化场景里的极限性能**这一个维度;它在安全保证、可解释性、未见场景泛化、数据效率上系统性地弱于经典方法(见 §0.4 表)。
- 正确做法:把结论精确化为"在结构化、可大量仿真、追求极限性能的场景,RL 的性能上限高于 MPC"。选型时先问场景属性(是否安全关键?能否大量仿真?是否需要认证?),再决定 RL / MPC / 混合,而不是无条件偏向某一方。
陷阱 0-2:用"RL 没有中间瓶颈"否定一切显式表示
- 错误描述:因为"端到端无中间瓶颈所以更优",就认为任何显式中间表示(轨迹、地图、状态估计)都是累赘,应该全部端到端化。
- 现象/后果:盲目追求 image→action 的纯端到端策略,结果对光照/外观极度敏感、数据需求爆炸、且无法定位是感知错还是控制错(见 §D9.1.8 Swift 为何反而选择模块化感知)。
- 根本原因:把"中间表示对最终目标有损"这个**前提条件**当成了无条件真理。中间表示是否该去掉,取决于它对最终目标是否有损、以及去掉后是否牺牲了可调试性/泛化性。
- 正确做法:逐个表示判断。竞速里的"参数化轨迹"对极限行为有损 → 去掉(RL 端到端);但"VIO 状态估计""门的几何位置"这类表示信息损失小且极大提升泛化与可调试性 → Swift 选择保留。该端到端的地方端到端,该模块化的地方模块化。
本节练习¶
- (推导/论证) §0.1 列出了经典规控的三重瓶颈。请论证:在**低速悬停**任务(速度 < 1 m/s、无激烈机动)中,这三重瓶颈分别"消失"或"可忽略"到什么程度?由此解释为什么悬停任务里经典 PID 与 RL 几乎打平、RL 的优势主要体现在敏捷/极限工况。
- (反事实分析) 假设未来出现一种"无损轨迹参数化"——它能精确表示任意可行的四旋翼极限轨迹,不丢失任何行为。在这个假设下,§0.0 反面段落里"分层管线有天花板"的论证还成立吗?请据此说明:RL 相对 MPC 的优势,到底来自"端到端"本身,还是来自"现有轨迹参数化的有损性"。
- (开放设计) 给定一个"室内巡检"场景(已知地图、速度温和、但要求 100% 不撞墙、需向客户出具安全报告)。请基于 §0.4 的互补性表,设计一个 RL 与经典方法的混合方案,并说明每一部分各自负责什么、为什么这样分工。
§D9.1 Swift——机载 RL 击败人类冠军的完整剖析 ⭐⭐⭐¶
本节定位:Swift 是本章的"完整样本"——它把仿真器、训练、感知、sim-to-real、部署六个环节第一次端到端打通并击败了人类。读懂 Swift,等于拿到了理解后续所有系统(SimpleFlight/RLtools/RAPTOR)的坐标系:它们要么简化 Swift 的某个环节,要么把某个环节推到极致。
动机¶
2023 年之前,"自主机器人在竞技运动中击败人类世界冠军"还停留在棋类、电子游戏这类**纯数字、无物理风险、状态完全可观测**的领域(AlphaGo、OpenAI Five)。物理世界的实时竞技——尤其是 FPV 无人机竞速这种"每一帧延迟都致命、传感器有噪声、炸机即硬件损毁"的任务——一直被认为是 RL 难以跨越的鸿沟。Swift 要回答的就是:一个在仿真里训练的神经网络策略,能否搬到真实赛道上,在头对头比赛中战胜飞了十几年的人类冠军? 答案是能——但"能"的背后是一整套精心设计的工程,每一个环节都在弥合"仿真完美世界"与"真实噪声世界"的某一道裂缝。本节逐环节剖析这套工程,目标是让你不仅知道 Swift 赢了,而且理解它的每一个设计**为什么必须存在、去掉会怎样**。
反面:如果按"教科书 RL"的方式做无人机竞速会怎样¶
设想你刚学完一门标准强化学习课,直接照搬"在仿真里 train 一个 PPO,然后部署"的范式来做竞速。最可能的结果是:(1) 你用一个简化仿真器(理想推力模型 \(T=k_f\Omega^2\)、无电池、无延迟),策略在仿真里飞得飞快;(2) 一搬上真机就炸——因为真实电机有非线性、电池会掉压、VIO 有延迟和漂移,这些 gap 全被简化仿真器抹掉了;(3) 你试图用更激进的域随机化来"硬扛"这些 gap,结果策略变得保守、性能大幅退化,根本谈不上击败人类。Swift 的全部精妙,恰恰在于它**没有**走这条naive 路线——它不是让策略去"容忍" gap,而是用真实数据**主动把仿真器修得贴近真实**。带着这个对比往下读,你会更清楚 Swift 每一个看似繁琐的环节解决的是哪一道裂缝。
理论与实现¶
9.1.1 系统总览¶
Swift 是 UZH RPG (Robotics and Perception Group) 于 2023 年发表在 Nature 上的自主无人机竞速系统。它在真实赛道上击败了三位人类世界冠军飞手,是**第一个在物理世界中、在竞技运动中超越人类冠军的自主机器人系统**。
Swift 的完整管线包含六个阶段:
┌──────────────────────────────────────────────────────────────────────────┐
│ SWIFT 完整管线(6 阶段) │
│ │
│ Stage 1: 仿真器构建 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ BEM 气动模型 + 电机一阶模型 + 电池 LiPo 放电模型 │ │
│ │ + 传感器噪声模型(IMU bias/noise, 相机延迟) │ │
│ │ + 域随机化(质量±10%, 惯量±10%, 推力系数±5%) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Stage 2: PPO 训练(纯仿真) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 100 并行 agent × 50 min ≈ 数十亿步交互 │ │
│ │ 观测 31D → MLP 2×128 → 动作 4D CTBR │ │
│ │ 奖励: r_prog + r_perc + r_cmd - r_crash │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Stage 3: 感知系统(独立开发) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ VIO: ROVIO/VINS-Fusion → 位姿估计(30 Hz) │ │
│ │ 门检测: CNN 语义分割 → PnP → 门 3D 位姿(15 Hz) │ │
│ │ 融合: EKF → 31D 观测向量 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Stage 4: 真实飞行数据采集(~50 秒) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 人类飞手飞 3 圈 → ~1000 个数据点 │ │
│ │ 记录: 状态估计(VIO) + 真实状态(mocap) + 控制指令 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Stage 5: 残差模型训练 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 9 路 1-D GP(RBF 核)→ VIO 观测残差(概率性噪声模型) │ │
│ │ k=5 kNN 回归 → 加速度残差(确定性修正) │ │
│ │ 注入仿真器 → 残差增强仿真 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Stage 6: 策略微调 + 部署 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 在残差增强仿真器中 PPO 微调(~20 min) │ │
│ │ → 策略权重部署到 Jetson TX2(NVIDIA) │ │
│ │ → Betaflight FCU 执行 rate PID @ 8 kHz │ │
│ └─────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
9.1.2 观测空间详解(31 维)¶
观测向量 o ∈ R^31:
[位置(3) + 速度(3) + 旋转矩阵(9)] ← 状态估计(VIO + 门 KF 融合)
+ [下一门 4 角点的 3D 相对坐标(12)] ← 感知(CNN 门分割 + PnP)
+ [上一动作(4)] ← 动作历史
分解:
┌────────────────────────────────────────────────────────────────────┐
│ 维度 0-2: p = [x, y, z] 世界系位置 (m) │
│ 维度 3-5: v = [vx, vy, vz] 世界系速度 (m/s) │
│ 维度 6-14: R = [r11,...,r33] 旋转矩阵(9 维展平) │
│ 维度 15-26: corners = [c1,...,c4] 下一门 4 角点相对坐标 │
│ ci = [xi, yi, zi] (i=1,2,3,4, 各 3 维) │
│ 维度 27-30: a_{t-1} = [T,ωx,ωy,ωz] 上一时刻动作 │
└────────────────────────────────────────────────────────────────────┘
为什么用旋转矩阵(9D)而不是四元数(4D)或欧拉角(3D)?
这是一个在足式 RL 中也被反复验证的设计选择:
| 表示 | 维度 | 问题 | 对 RL 的影响 |
|---|---|---|---|
| 欧拉角 | 3 | 万向节锁(gimbal lock) | 在 ±90° pitch 附近策略行为不连续 |
| 四元数 | 4 | 双覆盖(q 和 -q 表示同一姿态) | 策略需要学到 q ≡ -q 的等价性,增加学习负担 |
| 旋转矩阵 | 9 | 无奇异、无歧义 | 表示连续且唯一;代价是维度冗余(9 > 3) |
SimpleFlight 的消融实验(表 2)定量证实:使用旋转矩阵的策略在所有轨迹上的跟踪误差低于四元数表示约 30-40%。
为什么包含上一动作(4D)?
上一动作为策略提供了"动作历史",使得策略能够: 1. 实现动作平滑——避免相邻两步输出差距过大导致的机械振荡 2. 隐式学习电机动力学——电机从当前 RPM 到目标 RPM 需要时间(一阶低通,时间常数 ~25 ms) 3. 补偿控制延迟——知道上一步的指令有助于预测当前状态
9.1.3 动作空间详解(4 维 CTBR)¶
动作(4 维 CTBR): - 质量归一化总推力 [0, 2g] - 3 轴机体角速度 [−10, 10] rad/s - 由 Betaflight FCU 的 rate PID @ 8 kHz 转为 PWM → 电机 RPM
CTBR 的物理意义:
CTBR = [c, ωx, ωy, ωz]
c: 质量归一化总推力(collective thrust)
c = T_total / m (单位: m/s^2)
范围: [0, 2g] ≈ [0, 19.6] m/s^2
物理含义: 无人机沿机体 z 轴的加速度
ωx: 机体系滚转角速度(roll rate, rad/s)
ωy: 机体系俯仰角速度(pitch rate, rad/s)
ωz: 机体系偏航角速度(yaw rate, rad/s)
范围: [-10, 10] rad/s ≈ [-573, 573] deg/s
CTBR 的核心优势(Kaufmann ICRA 2022 的定量验证):
Kaufmann 等在 ICRA 2022 系统对比了三种动作空间:
| 动作空间 | 缩写 | 维度 | 下层依赖 | 扰动鲁棒性 | sim-to-real |
|---|---|---|---|---|---|
| 线性速度 | LV | 3 | 需要速度控制器+姿态控制器+rate PID | 高(最多缓冲) | 最好但性能上限低 |
| 集体推力+体速率 | CTBR | 4 | 仅需 rate PID | 高 | 好——平衡了鲁棒性和敏捷性 |
| 单转子推力 | SRT | 4 | 无(直接电机指令) | 低(最脆弱) | 差——对电机模型敏感 |
关键发现: - **LV 策略**安全但"温吞",因为速度控制器限制了敏捷性,最高加速度约 1.5g - **SRT 策略**理论性能最高(直接控制电机),但对电机模型的 sim-to-real gap 极其敏感,添加 ±10% 质量扰动后存活率从 95% 降至 30% - **CTBR 策略**在加入 ±20% 质量扰动后仍保持 90%+ 存活率,且能达到 3g 加速度和 45+ km/h 速度
为什么 CTBR 能兼顾鲁棒性和敏捷性?
CTBR 的关键洞察:
1. 推力方向通过姿态隐式控制:
策略输出 body rate → rate PID 控制姿态 → 推力方向改变
这利用了低层 rate PID 的高频(8 kHz)鲁棒性
2. 推力大小直接控制:
策略直接输出 c = T/m → 不经过位置/速度控制器
保留了对加速度的直接控制权
3. 对电机动力学不敏感:
CTBR → rate PID → 电机力矩分配
rate PID 自动处理电机响应差异
vs SRT 直接输出电机 RPM → 对电机模型误差无缓冲
与微分平坦的关系(连接 D1 章节):
CTBR 动作空间与微分平坦控制有深刻联系。在微分平坦框架中: - 给定平坦输出 \(\sigma = [x, y, z, \psi]\) 及其导数,可以解析计算所需的 \([T, \omega_x, \omega_y, \omega_z]\) - 这意味着 CTBR 是"微分平坦空间中的动作"——策略学到的实际上是**微分平坦控制器的神经网络版本** - 这也解释了为什么 CTBR 对 sim-to-real gap 鲁棒:它利用了四旋翼的本征几何结构
9.1.4 网络架构¶
网络:2×128 MLP + LeakyReLU(0.2)。Policy 和 Value 独立网络。
Policy 网络(Actor):
输入: o ∈ R^31 (观测向量)
↓
Linear(31, 128) → LeakyReLU(0.2)
↓
Linear(128, 128) → LeakyReLU(0.2)
↓
Linear(128, 4) → tanh → 缩放到动作范围
↓
输出: a ∈ R^4 (CTBR)
参数量: 31×128 + 128 + 128×128 + 128 + 128×4 + 4
= 3968 + 128 + 16384 + 128 + 512 + 4
= 21,124 参数
Value 网络(Critic):
输入: o ∈ R^31 (同 Actor, 可加特权信息)
↓
Linear(31, 128) → LeakyReLU(0.2)
↓
Linear(128, 128) → LeakyReLU(0.2)
↓
Linear(128, 1)
↓
输出: V(o) ∈ R (状态价值)
为什么选择 LeakyReLU(0.2) 而非 ReLU?
在 RL 训练中,ReLU 的"死神经元"(dead neuron)问题更严重,因为策略梯度可能导致某些神经元的输入长期为负,从而永久失活。LeakyReLU 通过允许负区间有小斜率(\(\alpha = 0.2\))避免了这个问题。
为什么 Policy 和 Value 独立网络?
在敏捷飞行中,Policy 和 Value 的最优特征表示差异很大: - Policy 需要对**动作方向**敏感:微小的观测差异可能对应完全不同的最优动作 - Value 需要对**回报累积**敏感:需要长时间尺度的预测能力 - 共享网络的梯度干扰会同时降低两者的质量
9.1.5 奖励设计详解¶
奖励设计:
r = r_prog + r_perc + r_cmd − r_crash
r_prog = 沿向下一门方向的位移 ← 进度激励
r_perc = λ₂·exp(λ₃·δ_cam⁴) ← 奖励把相机对准下一门
r_cmd = −λ₄·‖u‖² ← 控制平滑
r_crash = −λ₅·碰撞指示 ← 崩溃惩罚
各项奖励的深层设计意图:
r_prog (进度奖励) —— 时间最优的代理
这个奖励不直接惩罚时间,但通过奖励"接近门的速度"间接实现时间最优——因为要最大化累积 \(r_{\text{prog}}\),策略必须以最快速度接近目标。相比直接使用 \(r = -1\) (每步固定惩罚以鼓励速度),进度奖励提供了**更密集的梯度信号**——策略从第一步就能获得有意义的反馈。
r_perc (感知感知奖励) —— Swift 脱离 mocap 的关键
r_perc = λ₂ · exp(λ₃ · δ_cam⁴)
δ_cam = 下一门中心在相机图像平面上偏离图像中心的角度
λ₂ > 0, λ₃ < 0 (4 次方 + 负指数 → 偏角越大惩罚越重)
r_perc 是 Swift 脱离 mocap 的关键:它让策略学会把相机光轴指向下一个门——保持 VIO 特征质量和门检测成功率。没有 r_perc,策略会学到"侧飞抄近路"但 VIO 丢失定位。
这里有一个深刻的 insight:控制策略必须为感知系统服务。在机载自主系统中,感知和控制不再是独立模块——控制器的行为直接影响感知的可靠性。Swift 通过 \(r_{\text{perc}}\) 将这种依赖关系编码进了学习目标,这在经典控制中很难实现。
没有 r_perc 的行为: 有 r_perc 的行为:
门1 → 门2 门1 → 门2
侧飞抄近路 正面朝向下一门
相机看不到门2 相机持续跟踪门2
VIO 特征漂移 VIO 特征稳定
门检测失败 门检测成功
→ 失败 → 成功
r_cmd (控制平滑) —— 防止高频震荡
注意 Swift 使用的是动作**幅度**惩罚(L2 范数),而不是 SimpleFlight 推荐的动作**差分**惩罚。后者被证明更优(见 D9.2 节)。
r_crash (碰撞惩罚) —— 安全硬约束
碰撞惩罚的设计关键是**终止条件**。在无人机 RL 中,碰撞=硬件损毁,因此碰撞后 episode 必须终止。这与足式 RL 不同——机器人摔倒后还可以尝试站起来,但无人机炸机就是永久性损坏。
9.1.6 仿真器架构——BEM 气动 + 电池模型¶
Swift 的仿真器是 sim-to-real 成功的基础。它的物理保真度比 gym-pybullet-drones 或 Flightmare 高出一个量级:
BEM (Blade Element Momentum) 气动模型:
传统简化模型:
T = k_f · Ω² (推力 ∝ 转速²)
M = k_m · Ω² (力矩 ∝ 转速²)
→ 忽略了入流速度、叶片翼型、前进比的影响
BEM 模型:
将每个桨叶沿径向分成 N 个微元
对每个微元:
1. 计算入流速度(轴向+切向+前进速度)
2. 计算有效攻角(几何攻角 - 入流角)
3. 查翼型数据表 → Cl(α), Cd(α)
4. 计算微元升力 dL 和阻力 dD
5. 积分 → 单桨总推力 T_i 和力矩 M_i
对 4 个旋翼求和 → 总力和总力矩
BEM 额外捕获的效应:
├── 前进比效应: 前飞时推力随方向变化
├── 叶片失速: 高攻角时推力骤降
├── 地面效应: 近地面时推力增加
└── 非线性推力曲线: 高转速时效率下降
BEM 模型的计算量比简化模型大约 10-50 倍,但对于离线仿真而言完全可接受。关键是它让仿真器中的**气动响应曲线**与真实旋翼匹配,从而减少了 sim-to-real gap 的主要来源。
电池放电模型:
LiPo 电池放电特性:
V(t) = V_0 - I(t) · R_internal - f(SoC(t))
V_0: 标称电压(4S: 14.8V → 满电 16.8V → 截止 12.0V)
R_internal: 内阻(~50 mΩ 新电池,老化后增加)
SoC(t): 剩余电量(state of charge)
I(t): 瞬时电流(与电机 RPM 正相关)
f(SoC): 开路电压-SoC 曲线(非线性,末端急剧下降)
影响:
├── 满电时最大推力比低电时高 ~15%
├── 策略必须学会在低电时减少激进机动
└── 不建模电池 → sim-to-real 差距 ~15% 推力误差
电机动力学模型:
电机响应 = 一阶低通滤波器:
Ω̇ = (Ω_cmd - Ω) / τ_m
τ_m: 电机时间常数
无刷电机(竞速): ~5 ms
有刷电机(Crazyflie): ~25 ms
加入 ESC 延迟:
Ω_actual(t) = Ω(t - δ_esc)
δ_esc ≈ 1-5 ms (ESC 处理延迟)
传感器噪声模型:
IMU 噪声(加速度计 + 陀螺仪):
a_meas = a_true + b_a + n_a (bias + 白噪声)
ω_meas = ω_true + b_ω + n_ω
b_a: 加速度计 bias (random walk, ~0.01 m/s²/√Hz)
b_ω: 陀螺仪 bias (random walk, ~0.001 rad/s/√Hz)
n_a: 白噪声 (~0.1 m/s² RMS)
n_ω: 白噪声 (~0.01 rad/s RMS)
VIO 观测噪声(由残差 GP 学习):
p_vio = p_true + ε_p(p, v, ω) (位置依赖的噪声)
v_vio = v_true + ε_v(p, v, ω)
R_vio = R_true · exp([ε_R]×) (姿态噪声)
9.1.7 PPO 训练细节¶
PPO 超参数:
并行环境数: 100
采样步数/轮: 10,000 步/环境
总样本/轮: 1,000,000
小批量大小: 4,096
训练轮数: ~3,000
总交互步数: ~3 × 10^9
训练时间: ~50 分钟(NVIDIA GPU)
PPO 特有参数:
clip ratio ε: 0.2
GAE λ: 0.95
折扣因子 γ: 0.99
学习率: 3e-4 → 余弦退火到 1e-5
熵奖励系数: 0.01 → 退火到 0
值函数损失系数: 0.5
梯度裁剪: 0.5 (max_grad_norm)
PPO 选择的理由:
为什么 Swift 选择 PPO 而非 SAC/TD3(off-policy)?
| 特性 | PPO (on-policy) | SAC/TD3 (off-policy) |
|---|---|---|
| 样本效率 | 低(需要 ~10^9 步) | 高(~10^6-10^7 步) |
| 并行化效率 | 极高(GPU 并行仿真完美匹配) | 中等(replay buffer 增加复杂性) |
| 策略稳定性 | 高(clipping 限制更新幅度) | 中等(可能过拟合到 replay 中的旧数据) |
| 超参数敏感性 | 低 | 高(Q 函数过估计是常见问题) |
| 适合高维连续控制 | 是 | 是 |
在 Swift 的场景中,仿真器极快(100 并行环境),样本效率不是瓶颈,因此 PPO 的稳定性和易调参成为决定性优势。
9.1.8 感知系统¶
Swift 的感知系统是一个独立于 RL 策略的模块,负责将高维传感器数据转换为 31 维观测向量:
┌───────────────────────────────────────────────────────────────┐
│ 感知系统管线 │
│ │
│ 摄像头图像(640×480, 60 Hz) │
│ │ │
│ ├──→ VIO 模块(ROVIO/VINS-Fusion) │
│ │ └──→ 位姿估计 [p, v, R] @ 30 Hz │
│ │ │
│ └──→ 门检测模块 │
│ ├──→ CNN 语义分割(门像素) │
│ ├──→ 角点提取(门的 4 个角) │
│ ├──→ PnP 求解(2D→3D 位姿) │
│ └──→ 门位姿 @ 15 Hz │
│ │
│ IMU 数据(加速度计 + 陀螺仪, 200 Hz) │
│ │ │
│ └──→ VIO 模块(高频预积分) │
│ │
│ 融合: │
│ ┌─────────────────────────────────────────┐ │
│ │ EKF 融合 VIO 位姿 + 门位姿 │ │
│ │ → 门的 3D 角点 → 相对坐标 (12D) │ │
│ │ → 拼接: [p,v,R,corners,a_{t-1}] = 31D │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 延迟分析: │
│ ├── 图像采集→VIO: ~15 ms │
│ ├── 图像→门检测: ~20 ms │
│ ├── 策略推理: ~1 ms │
│ ├── 总感知-动作延迟: ~40 ms │
│ └── 人类飞手延迟: ~220 ms │
└───────────────────────────────────────────────────────────────┘
为什么 Swift 不用端到端视觉策略(image → action)?
这是一个重要的设计决策。Loquercio 2021 ("Learning High-Speed Flight in the Wild") 使用了端到端深度图策略,但 Swift 选择了模块化设计:
| 维度 | 端到端视觉策略 | Swift 模块化设计 |
|---|---|---|
| 泛化性 | 对光照/外观敏感 | VIO+PnP 对外观不敏感 |
| 数据效率 | 需要大量视觉数据 | 策略只需 31D 低维输入 |
| 调试性 | 黑箱,难以定位感知 vs 控制问题 | 模块边界清晰 |
| 计算量 | GPU 前向传播图像 CNN | 仅 MLP 推理(<1ms) |
| 局限 | 可以泛化到新环境 | 依赖门检测器的质量 |
Swift 的选择反映了一个务实的工程判断:在竞速场景中,环境结构化(已知门的形状和数量),模块化设计更可靠。
9.1.9 Sim-to-real 的核心秘密——残差模型¶
Sim-to-real 的真正秘密(残差模型): - 采集**~50 秒真实飞行**(3 圈,~1000 样本) - 训练 9 路 1-D GP(RBF 核)建模 VIO 观测残差(随机性) - 训练 k=5 kNN 回归建模加速度残差(确定性) - 在残差增强模拟器中微调策略(~20 min) - 结论:Swift 不是"让策略对 sim-to-real 差距鲁棒",而是"让模拟器匹配真实"
残差模型的详细设计:
两种残差,两种建模方式:
1. VIO 观测残差 → GP(概率性)
┌─────────────────────────────────────────────────────┐
│ 目标: 建模 VIO 估计与真实状态之间的差异 │
│ │
│ ε_obs = x_vio - x_mocap │
│ │
│ 9 个独立 1-D GP (每个状态维度一个): │
│ GP_1: ε_{px} = f(x_vio) + σ_1 │
│ GP_2: ε_{py} = f(x_vio) + σ_2 │
│ ... │
│ GP_9: ε_{Rz} = f(x_vio) + σ_9 │
│ │
│ 核函数: RBF (Radial Basis Function) │
│ k(x, x') = σ² · exp(-||x-x'||² / (2l²)) │
│ │
│ 为什么用 GP? │
│ ├── VIO 噪声是状态依赖的(快速运动时更大) │
│ ├── GP 输出均值+方差 → 可以在仿真中采样噪声 │
│ └── 概率模型 → 策略学到对噪声鲁棒的行为 │
└─────────────────────────────────────────────────────┘
2. 动力学残差 → kNN(确定性)
┌─────────────────────────────────────────────────────┐
│ 目标: 建模仿真动力学与真实动力学之间的差异 │
│ │
│ ε_dyn = a_real - a_sim │
│ = (加速度真实) - (加速度仿真预测) │
│ │
│ k=5 kNN 回归: │
│ ε_dyn(x, u) = Σ w_i · ε_dyn(x_i, u_i) │
│ 对 5 个最近邻取加权平均 │
│ │
│ 为什么用 kNN 而不是 GP? │
│ ├── 动力学残差是确定性的(同状态+同控制→同残差) │
│ ├── kNN 计算快(实时仿真中使用) │
│ ├── kNN 不假设函数形式(非参数) │
│ └── 仅 1000 个数据点 → kNN 天然适合小数据集 │
└─────────────────────────────────────────────────────┘
为什么选择"修正仿真器"而非"域随机化"?
这是 Swift 与其他 sim-to-real 方法的根本区别:
| 方法 | 思路 | 优点 | 缺点 |
|---|---|---|---|
| 域随机化 (DR) | 随机化仿真参数 → 策略对参数不敏感 | 不需要真实数据 | 过度正则化,牺牲性能 |
| 系统辨识 (SysID) | 精确测量真实参数 → 仿真器匹配真实 | 高精度 | 难以辨识所有参数 |
| 残差学习 (Swift) | SysID + 学习剩余差距 | 兼顾精度和覆盖 | 需要少量真实数据(~50s) |
Swift 的哲学是:先用 SysID 尽可能缩小 gap,再用数据驱动方法学习剩余部分。这与 SimpleFlight 的"SysID + 选择性 DR"异曲同工,但 Swift 更进一步——它不是随机化残差,而是**从数据学习**残差的精确分布。
~50 秒数据如何被使用:
数据采集流程:
1. 人类飞手操控 Swift 飞 3 圈(~50 秒)
2. 同步记录:
- VIO 状态估计(频率: 30 Hz)
- Mocap 真实状态(频率: 200 Hz, 降采样对齐)
- 控制指令(频率: 100 Hz)
3. 数据预处理:
- 时间对齐(±1 ms)
- 计算残差:
ε_obs = x_vio - x_mocap
ε_dyn = a_mocap - a_sim(给定相同状态和控制)
数据使用流程:
1. 训练 GP 和 kNN (~5 分钟)
2. 注入仿真器:
- 仿真时: x_sim → x_sim + ε_obs_sampled (GP 采样)
- 仿真时: a_sim → a_sim + ε_dyn_predicted (kNN 预测)
3. 在残差增强仿真器中 PPO 微调 (~20 分钟)
4. 部署微调后的策略
总真实世界数据需求: ~50 秒 = ~1000 样本
对比: 传统 sim-to-real 通常需要数小时真实数据
9.1.10 竞赛结果与分析¶
Swift vs 人类世界冠军的详细结果:
赛道: 7 个门,3D 赛道,总长 ~75 m
比赛形式: 头对头(同时起飞)
选手:
├── Alex Vanover: 2019 年 MultiGP 世界冠军
├── Thomas Bitmatta: 2019 年 DRL 世界冠军
└── Marvin Schaepper: 3 次 MultiGP 全国冠军
结果摘要:
┌─────────────────────────────────────────────────────────┐
│ Swift 最快单圈: 5.38 s │
│ 人类最快单圈: 5.58 s (Vanover) │
│ Swift 胜率: 60%+ (在正式比赛中) │
│ Swift 最高速度: ~80 km/h (受赛道限制) │
│ Swift 感知-动作延迟: ~40 ms vs 人类 ~220 ms │
│ Swift 在弯道的优势: 更激进的减速-加速策略 │
│ 人类在直道的优势: 更高的峰值速度(经验+信心) │
└─────────────────────────────────────────────────────────┘
关键观察:
1. Swift 的路径与人类飞手显著不同
- Swift 更倾向于"晚刹车,早加速"
- 人类飞手更"圆滑",提前减速以保持控制感
2. Swift 的一致性远高于人类
- Swift 圈间时间标准差: ~0.1 s
- 人类圈间时间标准差: ~0.3-0.5 s
3. Swift 偶尔失败(~5% 碰撞率)
- 主要原因: VIO 跟踪丢失 → 状态估计漂移 → 碰撞
- 人类碰撞率更高(~10-15%),但人类碰撞后常能恢复
9.1.11 Swift 与 D8 经典方法的系统对比¶
为了将 Swift 的成就放入整个教程的知识体系中,这里将 Swift 的每个组件与 D8 中介绍的经典方法进行对比:
┌─────────────────────────────────────────────────────────────────────────┐
│ Swift (RL) vs D8 经典管线 系统对比 │
├──────────────┬──────────────────────┬────────────────────────────────────┤
│ 模块 │ D8 经典方法 │ Swift RL 方法 │
├──────────────┼──────────────────────┼────────────────────────────────────┤
│ 全局规划 │ A*/RRT* → 全局路径 │ 无(门序列隐式编码在观测中) │
│ 轨迹优化 │ 最小 snap / 时间最优 │ 无(策略隐式生成轨迹) │
│ 控制器 │ SE(3) / MPC │ MLP 2×128 (PPO 训练) │
│ 动作空间 │ 力矩/RPM │ CTBR (推力+角速率) │
│ 低层控制 │ PID @ 1kHz │ rate PID @ 8kHz (Betaflight) │
│ 状态估计 │ EKF/UKF │ VIO + EKF + 门检测融合 │
│ 地图 │ 占据栅格/ESDF │ 无(仅下一门位置) │
│ sim-to-real │ SysID + 增益整定 │ SysID + 残差学习(GP+kNN) │
│ 适应能力 │ 自适应控制/L1 │ 域随机化 + 残差微调 │
│ 延迟处理 │ 延迟补偿/预测 │ 训练中注入延迟(隐式学习) │
│ 安全保证 │ CBF/势场 │ 碰撞惩罚(无形式化保证) │
│ 计算需求 │ CPU 即可 │ Jetson TX2 GPU (推理) │
├──────────────┼──────────────────────┼────────────────────────────────────┤
│ 性能天花板 │ 受轨迹参数化限制 │ 理论上可达动力学极限 │
│ 工程复杂度 │ 高(多模块调参) │ 高(仿真器+训练+部署) │
│ 调试难度 │ 低(物理量可解释) │ 高(黑箱策略) │
│ 新场景适应 │ 重新规划即可 │ 需要重新训练(或基础策略) │
└──────────────┴──────────────────────┴────────────────────────────────────┘
什么时候选 RL,什么时候选经典?
选 RL 的场景:
├── 需要推到动力学极限(竞速、穿越、空翻)
├── 环境高度结构化(已知赛道/门/航点)
├── 有足够的仿真器保真度
├── 允许 5-10% 的失败率(竞技/研究)
└── 有 GPU 部署能力
选经典的场景:
├── 安全关键(货运、巡检、载人)
├── 环境完全未知(探索任务)
├── 不需要极限性能(巡航、悬停、缓慢导航)
├── 算力受限(MCU 上的简单 PID)
└── 需要形式化安全保证(认证)
混合方案:
├── RL + 安全滤波器(CBF):性能+安全
├── MPC 专家 → RL 学生蒸馏:性能+可部署
├── RL + L1 自适应(DATT):性能+在线适应
└── RL + 电子围栏:性能+物理安全
本质洞察:Swift 的 sim-to-real 哲学可以浓缩成一句话——"不要让策略去适应一个错误的世界,而要把世界修正到接近真实,再让策略适应它"。域随机化是前者(让策略对一大片可能的世界都鲁棒,代价是保守);Swift 的残差学习是后者(用 ~50s 真实数据把仿真器这个"世界"校正到贴近真实,让策略可以放心地榨干性能)。这也解释了为什么 Swift 能既鲁棒又激进——因为它面对的"仿真世界"本来就离真实很近,不需要靠牺牲性能来换鲁棒。
类比——Swift 的残差模型像给近视的人配眼镜,不像教盲人用拐杖(域随机化)。 域随机化**像**训练一个盲人用拐杖走路:因为看不清环境,只能学一套对各种地形都保守可用的步态——安全但走不快。Swift 的残差学习**像**给一个近视的人配一副精确度数的眼镜:先测准度数(SysID + 残差),让他看清世界,然后他就能像正常人一样快跑。 但**不像**"配一副完美的眼镜后就一劳永逸"——Swift 的残差是用某一架特定无人机、某一段飞行数据离线拟合的,换一架无人机或电池老化后,"度数"会失配,需要重新采数据微调。边界在于:残差模型修正的是**这一台机器在这段时间**的 gap,不是一个跨平台、跨时间的通用模型(那是 §D9.5 RAPTOR 才去解决的问题)。
本节常见陷阱¶
陷阱 1-1:用简化推力模型 \(T=k_f\Omega^2\) 的仿真器训练,期待直接上真机
- 错误描述:仿真器用理想二次推力模型、不建电池、不建电机延迟,策略在仿真里飞得很好就直接部署。
- 现象/后果:真机一飞就发散或炸机;或勉强能飞但完全达不到仿真性能,且"满电时还行、低电时失控"。
- 根本原因:sim-to-real gap 的主要来源(BEM 气动非线性、电池掉压 ~15% 推力、ESC/电机延迟)全被简化模型抹掉了。策略在训练时从未见过这些效应,部署时第一次遇到就崩。
- 正确做法:要么提升仿真器保真度(BEM + 电池 + 一阶电机 + 延迟,§9.1.6),要么走 Swift 的残差路线用真实数据校正仿真器。仿真器的物理保真度是 sim-to-real 成败的第一决定因素,不是训练算法。
陷阱 1-2:误以为 \(r_\text{perc}\)(感知奖励)是可有可无的"锦上添花"
- 错误描述:认为只要把进度奖励 \(r_\text{prog}\) 和碰撞惩罚调好,策略自然会飞好,感知朝向奖励可以省掉。
- 现象/后果:策略学会"侧飞抄近路"——几何上路径更短,但相机光轴偏离下一个门,VIO 特征丢失、门检测失败,状态估计漂移,最终炸机。
- 根本原因:在机载自主(脱离 mocap)系统中,控制行为直接决定感知质量。没有 \(r_\text{perc}\),策略只优化几何最优,不知道"看不见门"会导致定位崩溃——因为崩溃发生在感知模块,奖励里没有这一项就没有梯度去约束它。
- 正确做法:把感知约束显式编码进奖励(Swift 的 \(r_\text{perc}=\lambda_2\exp(\lambda_3\delta_\text{cam}^4)\)),让策略学会"飞得快"的同时"始终把相机对准下一个门"。这是 Swift 能脱离 mocap、在真实赛道自主飞行的关键设计。
陷阱 1-3:把 episode 终止条件(碰撞即终止)照搬足式 RL 的"摔倒可恢复"设定
- 错误描述:沿用足式 RL 习惯,碰撞后不终止 episode、只给个小负奖励,让策略"摔倒后再爬起来"。
- 现象/后果:策略学到"反正撞了还能继续,不如更激进地贴边飞",真机上碰撞率飙升——而每一次碰撞在无人机上是 $100\sim5000 的硬件损毁。
- 根本原因:足式机器人摔倒代价 O(1)(扶起来继续),无人机炸机代价 O(100)(更换部件、可能数天)。安全代价的量级差异决定了奖励/终止设计必须不同:无人机的碰撞必须是**终止性**的硬约束。
- 正确做法:碰撞 → episode 立即终止 + 大惩罚(\(\lambda_5>100\))。让策略在训练分布中就把"碰撞"学成一条绝对不能越过的边界,而非可以权衡的小代价。
本节练习¶
- (管线复述 + 必要性论证) 不看书,凭记忆画出 Swift 的六阶段管线,并对每一阶段回答:"如果删掉这一阶段,会在哪个环节、以什么方式失败?" 重点说清 Stage 5(残差模型)被删掉后,Stage 6 部署会发生什么。
- (动作空间分析) §9.1.3 论证了 CTBR 兼顾鲁棒与敏捷。请把 CTBR 与 D1 的微分平坦显式联系起来:给定策略输出的 \([c,\omega_x,\omega_y,\omega_z]\),写出它如何对应平坦输出空间里的一个"加速度 + 姿态变化率"指令,并解释为什么这种对应使 CTBR 对电机模型误差不敏感。
- (奖励设计/反事实) Swift 用动作幅度惩罚 \(-\lambda_4\|u\|^2\),SimpleFlight 改用动作差分惩罚 \(-\lambda\|u_t-u_{t-1}\|_2\)。请预测:如果把 Swift 的奖励换成动作差分惩罚,它在"需要瞬间大推力穿门"的场景下行为会如何变化?给出你的推理(§D9.2.5 会验证)。
- (残差建模选型) 为什么 Swift 对 VIO 观测残差用 GP(概率模型),对动力学残差用 kNN(确定性模型)?请从"残差是否随机""数据量""是否需要不确定性输出"三个角度论证,并说明如果把两者对调(VIO 用 kNN、动力学用 GP)会有什么问题。
§D9.2 SimpleFlight 五因子配方——可复现的基线 ⭐⭐⭐¶
引用:Chen, Yu 等,arXiv 2412.11764,RA-L 2025。GitHub:thu-uav/SimpleFlight。基于 OmniDrones(~495★)。
本节定位:如果说 Swift 是"一个能赢的完整系统",SimpleFlight 回答的是更科学的问题——这个系统里到底哪几个设计是关键的、哪些是偶然的。它把无人机 RL 从"工程炼金术"推向"可复现的配方",是你自己动手训练时最该照抄的基线。
动机¶
读完 Swift 你可能会有一个困惑:它有几十个设计选择(旋转矩阵观测、CTBR 动作、感知奖励、BEM 仿真、GP/kNN 残差、PPO 超参……),到底哪些是**击败人类的必要条件**,哪些只是"作者顺手这么选了、换一种也行"?这个困惑在整个无人机 RL 领域普遍存在——Swift、RLtools、DATT 各有一套独特配置,彼此结果难以横向比较,新人不知道该抄哪个。SimpleFlight 用**系统性消融实验**正面回答了这个问题:它逐个因子做"加上 vs 去掉"的对照实验,最终识别出五个真正起决定作用的因子,并证明只要正确组合这五个因子,用最朴素的标准 PPO 就能达到 SOTA 的 sim-to-real 性能,且代码完全可复现。
反面:如果不做消融、凭直觉堆砌设计会怎样¶
设想没有 SimpleFlight 这样的消融研究,你照着各路论文的"经验之谈"拼一个系统:从 A 论文抄四元数观测、从 B 论文抄动作幅度惩罚、从 C 论文抄"参数全随机化越多越鲁棒"的域随机化。结果很可能是一个**各处都次优、还互相打架**的系统——四元数的双覆盖拖慢收敛、动作幅度惩罚让策略不敢做大机动、盲目 DR 把精确 SysID 得来的信息又随机掉了。你会陷入无尽的调参,却不知道问题出在哪个设计上,因为你从没做过"控制变量"的对照。SimpleFlight 的价值正在于:它把"哪个设计真正重要"从玄学变成了**有消融数据支撑的结论**,让你能精确地知道每一分性能来自哪里。
理论与实现¶
9.2.1 研究动机¶
SimpleFlight 的核心问题是:在 RL 四旋翼控制领域,到底哪些设计选择是关键的? 尽管 Swift、RLtools、DATT 等系统都展示了令人印象深刻的结果,但每个系统都有独特的设计选择,很难判断哪些选择是必要的,哪些是偶然的。
SimpleFlight 通过**系统性消融实验**回答了这个问题,识别出五个关键因子,并证明只要正确组合这五个因子,就能用标准 PPO 实现 SOTA 的 sim-to-real 性能。
9.2.2 五个因子(每个都经过消融验证)¶
| 因子 | 配方 | 消融:去掉后的效果 |
|---|---|---|
| 1. Actor 输入 | 速度 + 旋转矩阵(9D) + 10 个未来参考点(间隔 50ms) | 四元数/欧拉角:奇异性/双覆盖导致不稳定 |
| 2. Critic 输入 | Actor 输入 + 时间向量 f_t | 无 f_t:价值估计方差增大 ~30% |
| 3. 奖励 | 位置/速度跟踪 + 动作差分平滑 −‖u_t−u_{t-1}‖₂ |
L2 动作幅度惩罚:牺牲敏捷性 |
| 4. Sim-to-real | 精确 SysID(m, I, k_f, T_m) + 选择性 DR(仅在难辨识参数上) | 盲目 DR:过度正则化,精度下降 ~40% |
| 5. 训练 | 大批量 PPO(4096+ env,OmniDrones GPU 并行) | 小批量:策略梯度方差大,不收敛 |
9.2.3 因子 1 详解:Actor 输入空间设计¶
Actor 输入 ∈ R^(3 + 9 + 3 + 3 + 10×6) = R^78:
[速度 v(3)] ← 当前速度
[旋转矩阵 R(9)] ← 当前姿态(无奇异表示)
[角速度 ω(3)] ← 当前角速度
[误差 e(3)] ← 到最近参考点的位置误差
[未来参考点 ref(10×6)] ← 10 个未来点,每个包含 [p_ref(3), v_ref(3)]
未来参考点的间隔: 50 ms → 覆盖 500 ms 的未来轨迹
为什么需要 10 个未来参考点?
只看当前参考点: 看 10 个未来参考点:
p_current p_0 p_1 p_2 ... p_9
策略只知道"现在要去哪" 策略知道"接下来 500ms 的轨迹形状"
→ 弯道前无法预减速 → 弯道前提前减速
→ 只能反应式控制 → 可以前瞻式控制
→ 类似 PID → 类似 MPC(但隐式)
这与足式 RL 中的"命令条件化"(command-conditioned)策略类似——足式 RL 给策略一个目标速度/方向作为条件,无人机 RL 给策略一条未来轨迹作为条件。
旋转矩阵 vs 四元数 vs 欧拉角的消融结果:
轨迹跟踪 RMSE (m), 8 字轨迹:
旋转矩阵(9D): 0.043 ± 0.005
四元数(4D): 0.062 ± 0.012 (↑44%)
欧拉角(3D): 0.081 ± 0.023 (↑88%)
旋转矩阵在所有测试轨迹上一致最优,尤其在包含大角度翻转的轨迹上优势更明显。
9.2.4 因子 2 详解:Critic 输入——时间向量 f_t¶
Critic 输入 = Actor 输入 + f_t
f_t = [cos(2πt/T), sin(2πt/T), t/T]
T: episode 总时长
t: 当前时间步
物理含义: 告诉 Critic "现在是 episode 的哪个阶段"
为什么 Critic 需要知道时间?
在轨迹跟踪任务中,同一个状态在 episode 开头和结尾的价值是不同的: - 开头:未来回报高(还有很多步可以积累奖励) - 结尾:未来回报低(即将终止)
没有时间信息,Critic 的价值估计是**有偏**的,这导致: 1. GAE (Generalized Advantage Estimation) 计算的优势函数方差增大 2. 策略梯度的信噪比下降 3. 训练不稳定,收敛变慢
这是一种**非对称 actor-critic** 设计——Critic 看到额外的特权信息(时间),而 Actor 不看到。这与足式 RL 中 Critic 看到地形摩擦、Actor 不看到的设计完全同构。
9.2.5 因子 3 详解:动作差分平滑奖励¶
Swift 的控制平滑奖励:
r_cmd = -λ · ‖u_t‖² (惩罚动作幅度)
SimpleFlight 的控制平滑奖励:
r_smooth = -λ · ‖u_t - u_{t-1}‖₂ (惩罚动作变化率)
关键区别:
‖u_t‖² 惩罚大动作 → 策略趋于保守,不敢做大推力/大角速率
‖u_t - u_{t-1}‖₂ 惩罚动作跳变 → 策略可以做大推力,但不能突变
效果对比:
动作幅度惩罚:
├── 限制了最大加速度(策略学会"温柔"飞行)
├── 在需要急转弯时性能下降
└── 实际动作波形: 低幅度但可能高频抖动
动作差分惩罚:
├── 允许大加速度(只要变化是渐进的)
├── 在急转弯时仍能保持性能
└── 实际动作波形: 平滑但幅度可以很大
消融实验结果:使用动作差分惩罚的策略在快速 8 字轨迹上的跟踪误差比使用动作幅度惩罚低 35%,且电机指令频谱中高频分量显著减少。
9.2.6 因子 4 详解:SysID + 选择性域随机化¶
这是 SimpleFlight 最核心的 sim-to-real 贡献。
精确 SysID 的测量方法:
Crazyflie 2.1 标定值(SimpleFlight 论文 Table 3):
m = 0.0321 kg ← 电子秤直接测量
Ixx = Iyy = 1.4e-5 ← 双线扭摆法测量
Izz = 2.17e-5 kg·m² ← 双线扭摆法测量
k_f = 2.35e-8 N/(rad/s)² ← 静态推力台测量
k_m = 7.24e-10 N·m/(rad/s)² ← 反扭矩测量
T_m = 0.025 s ← 阶跃响应测量(电机时间常数)
Ω_max = 2315 rad/s ← 最大转速(空载)
选择性域随机化的核心思想:
参数分类:
┌─────────────────────────────────────────────────────────────┐
│ 精确可辨识的参数 → 用 SysID 精确值,不随机化 │
│ ├── 质量 m (电子秤, 精度 0.001g) │
│ ├── 转动惯量 I (双线扭摆, 精度 ~5%) │
│ ├── 推力系数 k_f (推力台, 精度 ~3%) │
│ └── 电机时间常数 T_m (阶跃响应, 精度 ~10%) │
│ │
│ 难以精确辨识的参数 → 在合理范围内随机化 │
│ ├── 气动阻力系数 (k_drag ± 50%) │
│ ├── 力矩系数 k_m (± 30%) │
│ ├── 质心偏移 (± 2 mm) │
│ ├── 电机延迟 (± 5 ms) │
│ └── 外部风扰 (± 1 m/s) │
└─────────────────────────────────────────────────────────────┘
盲目 DR vs 选择性 DR 的消融:
盲目 DR (所有参数 ± 50%):
- 策略必须对所有参数鲁棒
- 包括对已精确辨识的参数鲁棒 → 浪费策略容量
- 跟踪误差: 0.089 m (8字轨迹)
选择性 DR (仅难辨识参数):
- 策略可以充分利用精确参数信息
- 仅对不确定的参数保持鲁棒
- 跟踪误差: 0.043 m (↓ 52%)
无 DR (纯 SysID):
- 跟踪误差: 0.052 m
- 比选择性 DR 差 → 证明 DR 的必要性
- 但比盲目 DR 好 → 证明精确 SysID 的价值
本质洞察:sim-to-real 不是"随机化越多越好",而是"知道什么该随机化、什么不该"。域随机化的本质是用"鲁棒性"换"对未知参数的覆盖"——但这笔交易只在参数**确实未知**时才划算。对已经被 SysID 精确测准的参数(质量、推力系数)还做随机化,等于花掉策略的表达容量去鲁棒一个根本不需要鲁棒的东西,是纯粹的浪费。选择性 DR 的精髓就是:把鲁棒性这把"钝刀"只用在真正模糊的地方。
9.2.7 因子 5 详解:大批量 PPO 训练¶
训练配置:
并行环境数: 4096 (OmniDrones, GPU 并行)
每轮采样: 4096 × 200 = 819,200 步
小批量: 4096
PPO epoch: 5
总训练步数: ~5 × 10^8
训练时间: ~30 分钟 (RTX 4090)
大批量 vs 小批量的影响:
并行环境 64: 策略梯度方差大 → 训练不稳定 → 50% 的种子不收敛
并行环境 512: 梯度方差可接受 → 训练稳定 → 80% 收敛
并行环境 4096: 梯度方差很小 → 训练高度稳定 → 100% 收敛
为什么大批量对 PPO 特别重要?
PPO 是 on-policy 算法——每次策略更新后,旧数据就不能用了。这意味着: - 每次更新只能用当前批次的数据估计梯度 - 如果批次太小,梯度估计方差大,更新方向可能错误 - 大批量 = 更准确的梯度估计 = 更稳定的训练
这与 off-policy 算法(如 TD3)形成对比——TD3 通过 replay buffer 积累历史数据,天然具有大样本量,对批量大小不敏感。
9.2.8 OmniDrones 训练平台¶
SimpleFlight 基于 OmniDrones 平台(GitHub: btx0424/OmniDrones),这是基于 NVIDIA Isaac Sim 的 GPU 并行无人机仿真器:
OmniDrones 特性:
├── GPU 并行仿真: 单卡可运行 4096+ 并行环境
├── PhysX 物理引擎: 刚体动力学 + 简化气动
├── 4 种无人机模型: Crazyflie / Hummingbird / Firefly / 自定义
├── 5 种传感器: IMU / 相机 / LiDAR / 光流 / 事件相机
├── 4 种控制模式: 位置 / 速度 / 姿态 / 推力(含 CTBR)
├── 10+ 基准任务: 悬停 / 跟踪 / 穿越 / 编队 / 负载运输
└── 与 Isaac Lab 兼容: 可复用 legged_gym 的训练框架
SimpleFlight 结果摘要:
在所有基准轨迹(圆形、8 字、螺旋、随机)上,SimpleFlight 是**唯一能完成所有轨迹(包括不可行轨迹)的方法**,跟踪误差比 L2F (RLtools)、DATT 等低 50%+,且能从 Crazyflie 零样本迁移到更大自制四旋翼。
类比——非对称 actor-critic(因子 2 的时间向量)像考试时只给批改老师标准答案,不给考生(但不像作弊)。 Critic 看到时间向量 \(f_t\) 而 Actor 看不到,**像**一场考试:批改老师(Critic)手里有标准答案(含"现在是 episode 第几步"这种特权信息),能精准地给每个动作打分;考生(Actor)只能凭题面(不含时间的观测)作答。老师评分越准,考生从反馈里学到的方向就越对。 但**不像**作弊——特权信息只在**训练时**喂给 Critic 用于降低价值估计方差,部署时 Critic 被丢弃、只留 Actor 上机,Actor 从未依赖过它看不到的信息。边界在于:特权信息用于"训练期的评分",绝不能泄漏进"部署期的决策",否则真机上没有 \(f_t\) 策略就废了。
本节常见陷阱¶
陷阱 2-1:观测里用四元数或欧拉角表示姿态
- 错误描述:图省事用 4 维四元数(或 3 维欧拉角)作为姿态观测,觉得"维度更省、信息等价"。
- 现象/后果:训练收敛更慢、跟踪误差更大(消融:四元数 RMSE ↑44%、欧拉角 ↑88%);欧拉角版本在 ±90° pitch 的大角度机动附近策略行为不连续、突然失稳。
- 根本原因:欧拉角有万向节锁(奇异),四元数有双覆盖(\(q\) 与 \(-q\) 表示同一姿态,网络须额外学这种等价、徒增学习负担)。这两种"表示缺陷"会被策略放大成控制缺陷。
- 正确做法:用 9 维旋转矩阵作姿态观测——无奇异、无歧义、表示连续唯一。多出来的 6 维冗余对网络几乎零成本,换来的是显著更稳更准的策略。这是 Swift 和 SimpleFlight 一致的选择。
陷阱 2-2:盲目域随机化——"随机得越多越鲁棒"
- 错误描述:把所有物理参数(质量、惯量、推力系数、阻力、延迟)一律 ±50% 随机化,认为覆盖面越广 sim-to-real 越稳。
- 现象/后果:仿真内训练看似鲁棒,但真机跟踪精度反而下降 ~40%(消融:盲目 DR RMSE 0.089 vs 选择性 DR 0.043)——策略变得保守、平庸。
- 根本原因:对已被 SysID 精确测准的参数还随机化,等于强迫策略为根本不存在的不确定性留裕度,白白消耗表达容量、牺牲标称性能。
- 正确做法:选择性 DR——精确可辨识的参数(质量用电子秤、推力系数用推力台)用 SysID 测准值、**不**随机化;只对难辨识参数(气动阻力、质心偏移、风扰)在合理范围随机化。让鲁棒性只花在真正模糊的地方。
陷阱 2-3:小批量 PPO——沿用 off-policy 的批量直觉
- 错误描述:参考 TD3/SAC 的经验用几十到几百个并行环境的小批量跑 PPO,觉得"批量够用就行"。
- 现象/后果:训练高方差、不稳定,约一半随机种子根本不收敛;即使收敛,结果方差大、难复现。
- 根本原因:PPO 是 on-policy——每次更新只能用当前批次估计策略梯度,批量太小则梯度估计方差大、更新方向频繁出错。off-policy 的 TD3 靠 replay buffer 天然有大样本量,对批量不敏感,这条直觉**不能**迁移到 PPO。
- 正确做法:用 GPU 并行(OmniDrones/Isaac)把并行环境拉到 4096+,让每次更新的梯度估计足够准。大批量是 PPO 在敏捷飞行任务上稳定收敛的必要条件,而非可选优化。
本节练习¶
- (消融设计) 从五因子中任选两个,设计一组"加上 vs 去掉"的控制变量实验:明确说明你会固定哪些变量、改变哪一个、用什么指标(RMSE?动作频谱?收敛种子比例?)衡量差异。这正是 §A 型练习里复现 SimpleFlight 表 2 的思路。
- (推导) 因子 2 说"无时间向量 \(f_t\) 会使价值估计方差增大 ~30%"。请从 GAE(广义优势估计)的定义出发,论证为什么"Critic 不知道 episode 进行到哪一步"会让优势函数 \(\hat{A}_t\) 的方差增大,进而拉低策略梯度的信噪比。
- (反事实 + 工程判断) 假设你的真机**无法**做精确 SysID(没有推力台、没有扭摆仪),只能粗测质量。在这个约束下,五因子配方该如何调整?具体说:因子 4(SysID + 选择性 DR)退化成什么?你会把哪些原本"不随机化"的参数重新纳入随机化范围?为什么?
- (迁移思考) 因子 1 的"10 个未来参考点(间隔 50ms,覆盖 500ms)"与足式 RL 的"命令条件化"有何异同?如果未来参考点只给 1 个(只看当前目标),策略行为会从"类 MPC 前瞻"退化成什么?
§D9.3 足式 RL → 无人机 RL 迁移速查 ⭐⭐¶
本节定位:前两节你已经看到无人机 RL 系统长什么样。本节回答一个非常实际的问题——如果你已经有一套足式 RL 的代码和经验,迁到无人机要花多少力气、哪些能直接搬、哪些必须从头写。这是把"读懂论文"变成"动手能跑"之间最省时间的一张地图。
动机¶
很多人来做无人机 RL 时,手里已经攒了一套足式 RL 的家底——跑通过 ANYmal/Go1 的 legged_gym、改过 PPO 超参、调过域随机化。一个自然但危险的念头是"两者差不多,照搬就行"或者另一个极端"无人机完全不同,全部重写"。两种判断都会浪费大量时间:照搬会让你在动作空间和奖励上踩一连串坑,全部重写则白白丢掉本可零改动复用的训练基础设施。本节的目的就是给出一张**精确到文件/模块级**的迁移清单——明确告诉你哪些是周末就能复用的(PPO 实现、非对称 AC、教师-学生蒸馏、日志/检查点),哪些是必须投入一周以上重建的(动作空间、奖励函数、安全协议),让你把力气花在刀刃上。
反面:如果不区分"可复用"与"必重建"会怎样¶
设想你不看这张清单,凭感觉迁移。情况 A——你激进地照搬足式的奖励函数(含 feet_air_time、对称性、步态频率),结果这些项在无人机上要么无意义、要么有害,策略学出一堆莫名其妙的行为,你却以为是超参没调好,调了一周。情况 B——你保守地把整个框架推倒重写,包括那个其实一行都不用改的 PPO 实现和非对称 actor-critic 基础设施,多花了两周重复造轮子还引入新 bug。真实的迁移既不是 A 也不是 B,而是**外科手术式的替换**:保留训练内核,精确切除并重建动作/奖励/安全这三块无人机特有的部分。下面就把这条边界划清楚。
理论与实现¶
9.3.1 可直接复用(周末即可迁移)¶
| 足式工具 | 无人机对应 | 改动量 |
|---|---|---|
| Isaac Lab / legged_gym | OmniDrones / Aerial Gym | 换环境类,动力学从接触→气动 |
| PPO(rsl_rl / rl_games) | PPO(同一实现) | 零改动 |
| 非对称 actor-critic | 非对称 actor-critic | 零改动——critic 看特权(足式:地形摩擦;无人机:风/真实速度) |
| 教师-学生蒸馏 | 教师-学生蒸馏 | 零改动——足式:scandots→proprio;无人机:1000 teacher→GRU(RAPTOR) |
| RMA(Kumar RSS 2021) | RMA 变体 / L₁(DATT) | 概念一致,具体适应律不同 |
详细迁移指南——从 legged_gym 到 OmniDrones:
步骤 1: 环境替换
legged_gym 环境:
class LeggedRobot(BaseTask):
def _compute_observation(self):
self.obs_buf = torch.cat([
self.base_lin_vel, # (N, 3) 基座线速度
self.base_ang_vel, # (N, 3) 基座角速度
self.projected_gravity, # (N, 3) 重力投影
self.commands[:, :3], # (N, 3) 速度命令
self.dof_pos - self.default_dof_pos, # (N, 12) 关节位置偏差
self.dof_vel, # (N, 12) 关节速度
self.actions, # (N, 12) 上一动作
], dim=-1) # 总计: 48 维
OmniDrones 环境:
class QuadrotorTrack(BaseTask):
def _compute_observation(self):
self.obs_buf = torch.cat([
self.root_vel, # (N, 3) 线速度
self.root_angvel, # (N, 3) 角速度
self.rotation_matrix.flatten(-2), # (N, 9) 旋转矩阵
self.target_relative, # (N, 30) 未来参考点
self.prev_actions, # (N, 4) 上一动作
], dim=-1) # 总计: 49 维(接近!)
修改清单:
├── 观测维度: 48 → 49 (仅需改配置文件)
├── 动作维度: 12 → 4 (关节位置 → CTBR)
├── 动作范围: [-1,1]×12 → [0,2g]×1 + [-10,10]×3
├── 奖励函数: 完全重写(足式→无人机)
└── 终止条件: 摔倒 → 碰撞/出界/翻转超 90°
步骤 2: 训练脚本(几乎零改动)
# legged_gym 训练脚本
env = LeggedRobot(cfg)
runner = OnPolicyRunner(env, cfg.runner)
runner.learn(num_learning_iterations=1000)
# OmniDrones 训练脚本(仅改环境和配置)
env = QuadrotorTrack(cfg)
runner = OnPolicyRunner(env, cfg.runner) # 同一个 Runner!
runner.learn(num_learning_iterations=1000)
步骤 3: 超参数调整
足式 → 无人机 建议修改:
├── 并行环境数: 4096 → 4096 (保持)
├── learning_rate: 1e-3 → 3e-4 (无人机策略更小,学习率可以稍低)
├── clip_range: 0.2 → 0.2 (保持)
├── gamma: 0.99 → 0.99 (保持)
├── entropy_coef: 0.01 → 0.01 (保持)
└── num_steps: 24 → 200 (无人机 episode 更长,需要更多步采样)
9.3.2 必须重建(一周以上工作量)¶
| 维度 | 足式 | 无人机 | 为什么不能直接迁移 |
|---|---|---|---|
| 动作空间 | 12D 关节位置 ×0.25 | 4D CTBR | 维度、物理意义、scale 完全不同 |
| 奖励设计 | 足腾空、对称、步态频率 | 感知感知(r_perc)、存活、进度 | 足式奖励项在无人机无意义 |
| 失败模式 | 摔倒 → 可恢复 | 炸机 → 硬件损毁 | 安全代价 O(1) → O(100) |
| 状态估计 | 本体感知直接可测 | VIO/mocap——外部估计本身是噪声源 | 延迟 10-30 ms、偶发失效 |
| sim-to-real 主要间隙 | 驱动器动力学+地面摩擦 | 电机推力曲线+电池电压+气动 | 参数辨识方法论不同 |
| 安全协议 | 体操垫+保护绳 | 围栏+Guard+逐步放速度 | 必须有独立安全系统 |
重建 1: 动作空间
足式 RL 动作空间:
a ∈ R^12 (12 个关节的目标位置)
a_i ∈ [-1, 1], 缩放后映射到关节角度范围
物理意义: 每个关节独立控制
频率: 50 Hz (仿真 200 Hz / 控制 decimation = 4)
无人机 RL 动作空间(CTBR):
a ∈ R^4 = [c, ωx, ωy, ωz]
c ∈ [0, 2g]: 质量归一化推力
ωi ∈ [-10, 10] rad/s: 体速率
物理意义: 欠驱动系统的全部自由度
频率: 50-100 Hz (策略) + 8 kHz (rate PID)
关键差异:
├── 维度: 12 → 4 (大幅降低)
├── 对称性: 足式有左右对称; 无人机推力是标量(无方向性)
├── 耦合: 足式关节相对独立; CTBR 推力+角速率物理耦合
└── 执行器: 足式 SEA/直驱; 无人机 BLDC+ESC+螺旋桨(多级转换)
重建 2: 奖励函数
足式 RL 典型奖励(ANYmal/Go1):
r = w1·tracking_lin_vel # 线速度跟踪
+ w2·tracking_ang_vel # 角速度跟踪
- w3·action_rate # 动作变化率
- w4·torques # 力矩惩罚
- w5·dof_vel # 关节速度惩罚
- w6·dof_acc # 关节加速度惩罚
+ w7·feet_air_time # 脚腾空时间(鼓励步态)
- w8·stumble # 绊倒惩罚
- w9·base_height # 基座高度偏差
特点: 大量步态相关项(腾空时间、对称性、频率)
无人机 RL 典型奖励(SimpleFlight):
r = w1·tracking_pos # 位置跟踪
+ w2·tracking_vel # 速度跟踪
+ w3·tracking_heading # 航向跟踪
- w4·action_diff # 动作差分(平滑)
- w5·angular_rate # 角速度惩罚
- w6·crash # 碰撞惩罚(大)
+ w7·survival # 存活奖励
特点: 无步态项; 有碰撞惩罚; 感知相关项(Swift 的 r_perc)
无法复用的奖励项:
├── feet_air_time: 无人机没有"脚"
├── foot_clearance: 同上
├── symmetry: 无人机动作已经是对称的(不需要鼓励)
├── gait_frequency: 无人机没有步态
└── stumble: 无人机不会"绊倒"
必须新增的奖励项:
├── crash_penalty: 碰撞 → 终止 + 大惩罚(足式无需如此)
├── perception_reward: 保持相机朝向(足式无对应)
├── battery_awareness: 电池管理(足式无电池约束)
└── survival_bonus: 每步存活奖励(因为无人机容易坠毁)
重建 3: 安全协议
足式 RL 安全:
风险: 机器人摔倒
代价: ~$0 (体操垫) ~ $100 (更换外壳)
恢复: 人工扶起,继续测试
保护措施:
├── 体操垫/软地面
├── 保护绳(防止跑远)
└── 减速运行(0.5× 速度)
无人机 RL 安全:
风险: 无人机坠毁
代价: $100 (Crazyflie) ~ $5000 (竞速无人机)
恢复: 更换部件 / 重建(可能数天)
保护措施:
├── 安全围栏/笼子(必须)
├── 独立安全 Guard(不依赖策略的硬件保护)
│ ├── 地理围栏(超出范围 → 电机锁死)
│ ├── 姿态限制(翻转 > 90° → 电机锁死)
│ └── 速度限制(> V_max → 降功率)
├── 逐步放速度:
│ ├── Phase 1: mocap, V_max = 2 m/s
│ ├── Phase 2: mocap, V_max = 5 m/s
│ ├── Phase 3: VIO, V_max = 5 m/s
│ └── Phase 4: VIO, V_max = full
├── 人员安全:
│ ├── 测试区域无人
│ ├── 安全眼镜
│ └── 急停开关(物理按钮)
└── 数据记录(每次飞行的完整 rosbag)
9.3.3 迁移清单总结¶
✅ 可直接复用的文件/模块:
├── PPO 实现 (rsl_rl/ppo.py → 直接用)
├── 网络定义 (actor_critic.py → 改输入/输出维度)
├── Logger / Tensorboard 集成 → 直接用
├── 检查点保存/加载 → 直接用
└── 域随机化基础设施 → 改随机化参数列表
❌ 必须重写的文件/模块:
├── 环境定义 (legged_robot.py → quadrotor_env.py)
├── 奖励函数 (rewards.py → 全部重写)
├── 配置文件 (cfg → 动作空间/观测/物理参数)
├── 部署脚本 (play.py → 适配飞控通信)
└── 安全协议 (无对应 → 全新开发)
本质洞察:足式与无人机 RL 的可迁移性边界,精确地落在**"训练算法"与"任务定义"的分界线上**。算法层(PPO 怎么更新、actor-critic 怎么组织、蒸馏怎么做)是**任务无关**的——它处理的是"如何从经验中改进策略"这个通用问题,所以零改动复用。任务层(动作是什么、奖励是什么、失败的代价多大、安全怎么保障)是**任务专属**的——它编码了"这个机器人在这个世界里要做什么",必须重建。看懂这条分界线,迁移任何两个 RL 任务(不止足式↔无人机)都有了通用方法论:先问每个模块属于算法层还是任务层。
反事实——如果无人机的失败代价和足式一样低(炸机=O(1))会怎样? 那么"必须重建"清单会大幅缩短:安全协议这一整块(围栏、独立 Guard、逐步放速度)可以省掉,奖励里的碰撞惩罚也能从"终止性硬约束"降级为"可权衡的小负项",训练流程几乎能和足式一样"随便摔、摔了爬起来继续"。正是因为无人机炸机代价是 O(100),安全协议才从"可选"变成"必需",奖励/终止设计才必须区别于足式。安全代价的量级,直接决定了迁移工作量的大小——这是这条迁移清单背后最容易被忽视的驱动因素。
本节常见陷阱¶
陷阱 3-1:把足式奖励函数整体搬到无人机
- 错误描述:直接复用 ANYmal/Go1 的奖励配置(含 feet_air_time、对称性奖励、步态频率、stumble 惩罚),只改几个权重。
- 现象/后果:策略学出怪异行为或干脆不收敛;调参一周仍不对,却误以为是超参问题而非奖励结构问题。
- 根本原因:足式奖励里大量项是**步态专属**的(脚腾空、对称性、步频),在无人机上要么无意义(无人机没有"脚"和"步态"),要么有害(无人机动作天然对称,再加对称奖励是干扰)。奖励属于"任务层",必须随任务重建。
- 正确做法:奖励函数整体重写。删掉所有步态相关项;新增无人机专属项——碰撞惩罚(终止性)、感知奖励(保持相机朝向)、存活奖励、(可选)电池感知。保留的只有"位置/速度跟踪 + 动作平滑"这类任务无关的骨架。
陷阱 3-2:复用足式的"摔倒可恢复"训练流程,省掉安全协议
- 错误描述:因为足式 RL 真机调试就是"摔了扶起来继续",便认为无人机也可以"炸了修一修再飞",不搭建独立安全系统。
- 现象/后果:真机调试阶段反复炸机,硬件损耗($100\sim5000/次)和时间成本(每次维修可能数天)失控,项目停滞。
- 根本原因:足式失败代价 O(1),无人机失败代价 O(100)。这个量级差异决定了无人机**必须**有一套不依赖策略的硬件级安全保障,否则学习过程本身就不可持续。
- 正确做法:上真机前搭好独立安全 Guard(地理围栏超界→锁电机、姿态翻转>90°→锁电机、超速→降功率)+ 安全笼 + 急停 + 逐步放速度(mocap 低速→mocap 高速→VIO→全速)。这套协议在足式里没有对应物,是无人机迁移必须**全新开发**的部分。
本节练习¶
- (迁移清单实操) 拿你手上(或任选一个开源)的足式 RL 训练框架,逐文件填一张三列表:文件名 | 复用/改维度/重写 | 预估工作量(行)。重点标注 PPO 实现、actor-critic、奖励、配置、部署脚本、安全协议各落在哪一类。这正是 §A 型练习"足式→无人机迁移"的产出。
- (分类方法论) §3.x 的本质洞察提出"先问每个模块属于算法层还是任务层"。请用这条标准判断以下模块各属哪层、是否可复用:(a) GAE 优势计算;(b) 观测归一化的 running mean/std;(c) 终止条件判定;(d) 域随机化的参数采样器;(e) tensorboard 日志。给出判断依据。
- (反事实推理) 如果你要把 RL 从无人机迁移到**水下机器人(AUV)**,沿用本节的"算法层 vs 任务层"框架,预测哪些能复用、哪些必重建。特别分析:AUV 的失败代价量级如何影响安全协议的工作量?
§D9.4 RLtools——秒级训练与 MCU 部署 ⭐⭐⭐¶
GitHub:rl-tools/rl-tools,~942★ + arplaboratory/learning-to-fly,~558★。
本节定位:前面的系统(Swift/SimpleFlight)都靠 GPU、训练以小时计、部署到 Jetson。RLtools 走向另一个极端——用纯 C++17 在一台笔记本上 18 秒训完,把策略烘焙成头文件部署到几美元的 STM32 单片机。它逼你重新思考一个被默认的前提:"RL 训练一定要 Python + GPU + 大集群吗?"
动机¶
主流 RL 工程栈有一个几乎没人质疑的默认假设:训练用 Python(PyTorch)+ GPU,部署再想办法把模型搬到目标硬件。这套栈很强大,但也带来一连串隐藏成本——Python 的 GIL 和解释器开销、PyTorch↔CUDA 的反复数据拷贝、框架本身的运行时依赖。当你的目标是**在一个几美元、几百 KB RAM 的单片机上跑控制策略**,或者你想在一台没有显卡的笔记本上几十秒内迭代一次实验时,这套重型栈就成了障碍。RLtools 提出一个激进的反命题:把整个 RL 训练栈(仿真器、网络、优化器、replay buffer、TD3 算法)全部用 C++17 模板元编程实现,让一切在编译期确定、零运行时框架依赖。结果是 18 秒训练、12.84 亿仿真步/秒、以及"策略权重 = 一个 constexpr 数组"的极简部署。本节剖析它为什么能做到,以及代价是什么。
反面:如果坚持用 Python + GPU 栈做 MCU 部署会怎样¶
设想你要把一个悬停策略部署到 Crazyflie 的 STM32F405(168 MHz、192 KB RAM、无操作系统)。如果你的训练栈是标准 Python + PyTorch:(1) 你训出来的是一个 PyTorch 模型,STM32 上根本跑不了 Python 解释器,更别说 CUDA;(2) 你得额外做一套模型转换/量化/手写 C 推理代码,这一步极易引入 sim-to-deploy 的数值不一致(训练用 float32、部署若用 float16 行为漂移);(3) 即便转换成功,框架带来的内存和延迟开销也未必塞得进几百 KB 的 RAM。RLtools 的设计直接消解了这个鸿沟——训练和部署用的是同一套 C++ 代码,权重导出就是把训练好的数组 constexpr 化,编译进固件即可,不存在"转换"这一步,自然也没有转换引入的不一致。带着这个对比看下面的"18 秒"和"权重烘焙",你会明白它省掉的到底是什么。
理论与实现¶
9.4.1 为什么能 18 秒训练¶
为什么能 18 秒训练(在 2020 MacBook M1 上): - TD3 off-policy:样本效率高(~30 万步 vs PPO 的 ~1 亿步) - 纯 C++17 头文件模拟器:12.84 亿步/秒(比 Flightmare 快 ~6420×) - 课程学习:奖励权重指数增强 + 探索噪声同步衰减 + 回放缓冲奖励重算 - 非对称 actor-critic:actor 看 18+4·N_H 维(不含 RPM);critic 看 28 维含 RPM+扰动
分解 18 秒:
训练时间分解 (2020 MacBook M1):
仿真步数: ~300,000 步
仿真速度: 12.84 × 10^9 步/秒 (C++17 纯 CPU)
仿真时间: 300,000 / 12.84e9 ≈ 0.023 ms ← 可忽略!
TD3 更新次数: ~300,000 次
每次更新: 前向传播 + 反向传播 + 目标网络软更新
网络大小: 3×64 MLP ≈ 8,700 参数
单次更新: ~0.06 ms (CPU 上矩阵运算)
总训练时间: 300,000 × 0.06 ms ≈ 18 秒
对比 PPO + GPU 仿真器:
仿真步数: ~100,000,000 步
仿真速度: ~1,000,000 步/秒 (GPU, Python overhead)
仿真时间: ~100 秒
PPO 更新: ~3,000 轮
总训练时间: ~30-50 分钟
速度差异来源:
├── 样本效率: TD3 需要 30 万步 vs PPO 需要 1 亿步 (333×)
├── 仿真速度: C++ 12.84e9 vs Python+GPU ~1e6 (12,840×)
└── 无 Python: 无 GIL、无垃圾回收、无 Python↔C++ 桥接
9.4.2 C++17 模板元编程范式¶
为什么选择 C++17 模板元编程?
传统 RL 训练架构:
Python (训练逻辑)
↕ (C++/CUDA 绑定, 数据拷贝)
C++/CUDA (仿真器: Isaac Gym, MuJoCo)
↕ (GPU 内存拷贝)
Python (策略网络: PyTorch)
↕ (GPU 内存拷贝)
Python (优化器: Adam)
瓶颈:
├── Python GIL: 单线程瓶颈
├── 数据拷贝: Python ↔ C++ ↔ CUDA 多次拷贝
├── 解释器开销: 每步 Python 解释执行
└── 框架开销: PyTorch 的动态图构建
RLtools 架构:
C++17 (一切都在编译时确定)
├── 模拟器: 内联编译,零开销
├── 网络: 模板参数化的 MLP(编译期确定形状)
├── 优化器: Adam (模板实现)
├── Replay Buffer: 固定大小数组
└── TD3 算法: 模板函数
优势:
├── 编译器优化: 循环展开、SIMD 自动向量化、常量折叠
├── 零开销抽象: 模板展开后无虚函数、无动态分派
├── 缓存友好: 连续内存布局,无碎片
└── 无运行时依赖: 编译完成即可运行,无 Python/CUDA
模板元编程示例(简化):
// RLtools 网络定义(编译期确定维度)
template<typename T_SPEC>
struct MLP {
static constexpr int INPUT_DIM = T_SPEC::INPUT_DIM; // 18
static constexpr int HIDDEN_DIM = T_SPEC::HIDDEN_DIM; // 64
static constexpr int OUTPUT_DIM = T_SPEC::OUTPUT_DIM; // 4
static constexpr int NUM_LAYERS = T_SPEC::NUM_LAYERS; // 3
// 权重在编译期分配(栈上,非堆)
T_SPEC::T weights_0[HIDDEN_DIM][INPUT_DIM];
T_SPEC::T biases_0[HIDDEN_DIM];
T_SPEC::T weights_1[HIDDEN_DIM][HIDDEN_DIM];
T_SPEC::T biases_1[HIDDEN_DIM];
T_SPEC::T weights_2[OUTPUT_DIM][HIDDEN_DIM];
T_SPEC::T biases_2[OUTPUT_DIM];
};
// 前向传播(编译器完全内联)
template<typename SPEC>
void forward(const MLP<SPEC>& mlp, const float* input, float* output) {
float hidden[SPEC::HIDDEN_DIM];
// Layer 0: input → hidden (编译器自动 SIMD 向量化)
for(int i = 0; i < SPEC::HIDDEN_DIM; i++) {
hidden[i] = mlp.biases_0[i];
for(int j = 0; j < SPEC::INPUT_DIM; j++)
hidden[i] += mlp.weights_0[i][j] * input[j];
hidden[i] = hidden[i] > 0 ? hidden[i] : 0; // ReLU
}
// ... 后续层类似
}
9.4.3 MCU 部署——策略权重烘焙¶
MCU 部署:策略权重烘焙为 actor_xxx.h 头文件(constexpr 数组)→ 编译进 STM32F405(Crazyflie 固件)→ 零堆、零框架依赖、零动态分配→ CPU 占用 <10%。
部署流程:
1. 训练完成 → 导出权重
train → checkpoint.bin (二进制权重文件)
2. 转换为 C 头文件
python export.py → actor_policy.h
生成的头文件内容:
┌────────────────────────────────────────────┐
│ // actor_policy.h │
│ // Auto-generated by RLtools │
│ #pragma once │
│ │
│ constexpr float W0[64][18] = { │
│ {0.0123, -0.456, ...}, // 第 1 个神经元 │
│ {0.789, 0.012, ...}, // 第 2 个神经元 │
│ ... │
│ }; │
│ constexpr float B0[64] = { │
│ 0.001, -0.023, ... │
│ }; │
│ // ... W1, B1, W2, B2 ... │
└────────────────────────────────────────────┘
3. 编译进飞控固件
arm-none-eabi-gcc firmware.c actor_policy.h -o firmware.elf
4. 烧录到 STM32F405 (Crazyflie 主控)
dfu-util --download firmware.elf
5. 运行时推理
┌────────────────────────────────────────────┐
│ // 主控制循环 @ 500 Hz │
│ void control_loop(void) { │
│ float obs[18]; │
│ get_observation(obs); // IMU + 状态估计│
│ float action[4]; │
│ forward_mlp(obs, action); // ~50 μs │
│ set_motor_rpms(action); │
│ } │
└────────────────────────────────────────────┘
资源占用:
├── Flash: ~35 KB (权重 + 推理代码)
├── RAM: ~2 KB (中间变量,栈上分配)
├── CPU: ~10% @ 500 Hz (STM32F405, 168 MHz)
├── 延迟: ~50 μs (单次推理)
└── 依赖: 零(无 RTOS 要求,裸机即可)
9.4.4 动作空间选择——直接 RPM¶
动作空间:直接 RPM(4D)——比 CTBR 更底层。电机建模为一阶低通 τ=0.15s + 延迟 5-25 控制步(100 Hz)。
RLtools 的动作空间选择:
RPM 直接控制:
a = [Ω1, Ω2, Ω3, Ω4] (归一化到 [0, 1])
a_physical = a × Ω_max (映射到物理 RPM)
vs CTBR:
a = [c, ωx, ωy, ωz]
需要下层 rate PID 将 ωi 转换为 ΔΩi
RLtools 选择 RPM 的原因:
├── MCU 部署: 不需要 rate PID → 减少代码复杂度
├── 端到端: 策略直接学习电机动力学(含非线性)
├── TD3 样本效率: 足以学习 RPM→推力 的映射
└── 训练中建模电机延迟: 一阶低通 + 随机延迟
代价:
├── 对电机参数更敏感(比 CTBR)
├── sim-to-real 更依赖精确 SysID
└── 策略必须隐式学习混控矩阵(mixer)
9.4.5 课程学习策略¶
RLtools 的课程学习(3 维度同步调度):
1. 奖励权重指数增强:
w_tracking(k) = w_0 × β^k (k = 训练步, β > 1)
早期: 低权重 → 策略有探索空间,不会过早收敛到局部最优
后期: 高权重 → 策略精细化跟踪精度
2. 探索噪声同步衰减:
σ(k) = σ_0 × α^k (α < 1)
早期: 大噪声 → 充分探索状态空间
后期: 小噪声 → 精细化策略
3. 回放缓冲奖励重算(Hindsight Reward Relabeling):
当奖励权重 w 改变时,回放缓冲中的旧经验用新的 w 重新计算奖励
为什么需要?
├── TD3 是 off-policy → 回放缓冲中有旧数据
├── 旧数据用旧 w 计算的奖励 → 与当前 w 不一致
├── 不重算 → Q 函数学到过时的奖励信号
└── 重算 → 所有数据与当前奖励一致
同步调度示意图:
↑ 奖励权重 ↑ 探索噪声 ↑ 跟踪精度
│ ___/ │ \___ │ ___/
│ / │ \ │ /
│ / │ \ │ /
│ / │ \ │ /
│/ │ \ │/
└─────→ 步数 └─────→ 步数 └─────→ 步数
本质洞察:RLtools 的"18 秒"不是单点优化的奇迹,而是**三个数量级相乘**的结果——样本效率(TD3 比 PPO 省 ~333×)× 仿真速度(C++ 比 Python+GPU 快 ~12840×)× 零框架开销(无 GIL/无拷贝/无动态图)。任何一项单独拿出来都不够震撼,但它们相乘就把"小时级"压成了"秒级"。这揭示了一个常被忽视的工程真理:极致性能往往来自消除整条链路上的每一处摩擦,而非优化某一个热点。Python+GPU 栈优化的是"计算热点"(GPU 算得快),却保留了 Python 解释、数据拷贝、框架开销这些"摩擦";RLtools 把摩擦全部归零,反而在 CPU 上赢了 GPU。
反事实——如果 RLtools 改用 PPO 而非 TD3 会怎样? 它的"18 秒"立刻就没了。PPO 是 on-policy,需要 ~1 亿步交互(TD3 只要 ~30 万步),即便 C++ 仿真器再快,1 亿次网络更新也要分钟级而非秒级。更关键的是,PPO 不用 replay buffer,RLtools 那套"回放缓冲奖励重算"的课程学习技巧也就无从谈起。算法选择(TD3)和工程实现(C++)在这里是深度耦合的——不是"先选了 C++ 再随便配个算法",而是"为了秒级训练,必须选样本高效的 off-policy 算法"。这也解释了为什么 SimpleFlight(追求轨迹跟踪 SOTA、用 GPU 大批量)选 PPO,而 RLtools(追求秒级训练、用 CPU)选 TD3——目标不同,算法-工程的最优组合就不同。
本节常见陷阱¶
陷阱 4-1:把"C++ 实现"当成 18 秒的唯一原因,忽视 TD3 的样本效率
- 错误描述:以为只要把仿真器用 C++ 重写,换成什么 RL 算法都能秒级训练。
- 现象/后果:用 C++ 重写仿真器后配上 PPO,发现训练仍要几分钟到几十分钟,达不到"18 秒",困惑于"我都用 C++ 了为什么还慢"。
- 根本原因:18 秒 = 样本效率 × 仿真速度 × 零开销三者相乘。C++ 只贡献了"仿真速度"和"零开销"两项;"样本效率"由算法决定——TD3 的 ~30 万步对比 PPO 的 ~1 亿步,差 333 倍。漏掉这一项,C++ 再快也快不到秒级。
- 正确做法:秒级训练必须**同时**满足"样本高效的 off-policy 算法(TD3/SAC)+ 极速仿真(C++/零开销)"。两者缺一不可,是耦合设计而非独立优化。
陷阱 4-2:训练用 float32、MCU 部署用 float16,忽视数值一致性
- 错误描述:为了在 MCU 上省内存/提速,把推理改成 float16 或定点数,假设"精度损失很小,不影响控制"。
- 现象/后果:仿真里完美的策略,部署到 MCU 后跟踪变差、抖动、甚至悬停发散——而仿真复测一切正常,难以定位。
- 根本原因:控制策略对数值精度可能敏感,尤其是连续多步的状态反馈会放大单步的量化误差。训练态(float32)与部署态(float16/定点)的数值分布不一致,等价于引入了一个未建模的 sim-to-deploy gap。
- 正确做法:部署前做 HIL(硬件在环)验证,在真实 MCU 上用真实数值精度跑策略、对比仿真输出(§D9.8 Step 5)。RLtools 的"训练部署同一套 C++ 代码"正是为了最小化这种不一致——尽量让部署精度与训练精度一致,必须降精度时务必 HIL 实测。
本节练习¶
- (数量级估算) §9.4.1 把 18 秒分解为"样本效率 × 仿真速度 × 零开销"。请你反过来估算:如果把 RLtools 的仿真器速度砍到 Python+GPU 水平(~1e6 步/秒),但保留 TD3 和 30 万步样本量,训练时间会变成多少?再砍掉 TD3 换 PPO(1 亿步),又是多少?通过这两步定量感受三个因子各自的贡献。
- (部署实操) 按 §A 型练习跑通
arplaboratory/learning-to-fly,验证 18 秒训练,导出actor_xxx.h。打开头文件检查constexpr权重数组:(a) 权重值是否都在合理范围(多数 <1.0、无 NaN/Inf)?(b) 数组维度是否与 3×64 MLP 吻合?(c) 估算这些数组占多少 Flash。 - (设计权衡) RLtools 选择"直接 RPM"动作空间(比 CTBR 更底层)。结合 §9.4.4 和前面 §9.1.3 的 CTBR 分析,论证:为什么 MCU 部署场景下"直接 RPM"是合理的(省掉 rate PID),但它对 SysID 精度的要求为什么比 CTBR 更高?这与陷阱 4-2 的数值一致性问题有何关联?
§D9.5 RAPTOR——2084 参数的基础策略 ⭐⭐⭐⭐¶
GitHub:rl-tools/raptor,~170★。pip install foundation-policy。
本节定位:前面每个系统都是"为某一架/某一类无人机训一个策略"。RAPTOR 把问题升一个维度——用一个 2084 参数的 GRU,零样本统治从 32g Crazyflie 到 2.4kg 七寸机的 10 种异构平台。它是本章难度最高、也最能体现"RL 范式跃迁"的一节:从"专用策略"到"基础策略"。
动机¶
到 §D9.4 为止,所有系统都隐含一个限制:策略是为**特定动力学**训练的——换一架质量、惯量、推力曲线不同的无人机,就得重新 SysID、重新训练(或至少微调)。这在工程上是巨大的负担:每来一台新机器都要走一遍完整流程。一个自然的追求是:能不能像 GPT 之于文本那样,训练一个"四旋翼基础策略",它见过足够多样的无人机,以至于面对一架全新的、从没见过的无人机也能零样本飞好? RAPTOR 给出了肯定的答案,而且答案出人意料地小——不是几十亿参数的大模型,而是 2084 个参数的微型 GRU。它的核心机制是:用 GRU 的隐状态在飞行的头 100 步里**在线辨识出当前这架无人机的动力学参数**,然后基于辨识结果做最优控制。本节剖析这个"隐式系统辨识器"如何工作、为什么这么小就够用、以及它和足式 RL 的 RMA 是同一设计模式的两个变体。
反面:如果用"为每架无人机单独训练"的思路面对 10 种平台会怎样¶
设想你要让 10 种异构无人机(质量跨度 75 倍)都能飞。沿用专用策略思路:你得为每一架做 SysID(10 次推力台/扭摆测量)、训 10 个策略、维护 10 套权重,且每来第 11 架新机器又要从头来一遍——这条路无法规模化,更谈不上"零样本"。即便你尝试"训一个策略喂所有无人机的混合数据",没有在线适应机制的 MLP 也只能学到一个对所有平台都平庸的"平均策略"——因为它每一步只看当前观测,无法判断"我现在飞的到底是轻巧的 Crazyflie 还是笨重的七寸机",只能用同一套增益应付所有机器,结果哪个都飞不好。RAPTOR 的破局点正是 GRU 的记忆:它不试图找一个"通用增益",而是**先花 100 步把当前机器辨识清楚,再针对性控制**。这就是"基础策略"区别于"平均策略"的关键,下面展开。
理论与实现¶
9.5.1 系统概述¶
观测(22 维):p(3) + R(9) + v(3) + ω(3) + a_{t-1}(4) 动作(4 维):归一化电机指令 [0, 1] 网络:3 层 GRU(hidden_dim=16)→ 仅 2084 参数
RAPTOR 网络架构详解:
输入层: o ∈ R^22
↓
GRU 层 1: hidden_dim=16
输入门: z = σ(W_z · [h_{t-1}, o] + b_z)
重置门: r = σ(W_r · [h_{t-1}, o] + b_r)
候选: h̃ = tanh(W_h · [r⊙h_{t-1}, o] + b_h)
输出: h_t = (1-z)⊙h_{t-1} + z⊙h̃
↓
GRU 层 2: hidden_dim=16
(同上结构)
↓
GRU 层 3: hidden_dim=16
(同上结构)
↓
线性输出层: Linear(16, 4) → sigmoid → [0, 1]
↓
输出: a ∈ R^4 (归一化电机指令)
参数量计算:
GRU 单层参数(input_dim=d_in, hidden_dim=16):
W_z: 16 × (d_in + 16) + b_z: 16
W_r: 16 × (d_in + 16) + b_r: 16
W_h: 16 × (d_in + 16) + b_h: 16
= 3 × (16 × (d_in + 16) + 16)
层 1 (d_in=22): 3 × (16×38 + 16) = 3 × 624 = 1,872
层 2 (d_in=16): 3 × (16×32 + 16) = 3 × 528 = 1,584 ← 这里有误差
层 3 (d_in=16): 3 × (16×32 + 16) = 3 × 528 = 1,584
输出层: 16×4 + 4 = 68
实际总计约 2,084 参数(论文中可能有略微不同的计算方式)
9.5.2 为什么 GRU 而非 MLP?¶
MLP vs GRU 在四旋翼控制中的对比:
MLP (无记忆):
a_t = f_MLP(o_t)
策略只看当前观测 → 无法估计未观测的参数
例如: 如果质量变化了,MLP 只能从当前加速度间接推断
→ 需要多步观测才能确认(但 MLP 看不到历史)
GRU (有记忆):
h_t, a_t = f_GRU(o_t, h_{t-1})
隐状态 h_t 编码了历史信息 → 可以做在线系统辨识
例如: 质量变化 → 加速度/推力比例变化 →
h_t 逐步积累这个信息 → 策略适应
直觉: GRU 的隐状态 h_t 就像一个"滚动系统辨识器"
- 前 100 步: h_t 在"探索"——积累关于当前无人机的信息
- 100 步后: h_t "收敛"——已经辨识出当前系统参数
- 此后: 策略基于辨识结果做最优控制
线性探针实验(隐状态编码了什么?):
实验设计:
1. 在 1000 架不同无人机上运行训练好的 RAPTOR 策略
2. 记录每步的隐状态 h_t ∈ R^16
3. 训练线性回归: h_t → 推重比(thrust-to-weight ratio)
4. 评估 R² 分数
结果:
R² = 0.95 (在未见过的无人机上)
解读:
├── h_t 中 95% 的推重比信息可以被线性提取
├── 说明 GRU 学到了一个接近线性的系统参数编码
├── 推重比是最关键的动力学参数(决定了加速能力)
└── 其他参数(惯量、阻力等)可能在 h_t 的非线性组合中
控制对比:
MLP + 推重比作为额外输入: R² ≈ 0.95 (但需要显式提供)
GRU 自动学到: R² ≈ 0.95 (从飞行数据中推断)
→ GRU 隐状态 ≈ 隐式系统辨识器
9.5.3 元模仿学习训练流程¶
训练:两阶段元模仿学习: 1. 从可分解参数分布采样 1000 架不同四旋翼(32g-2.4kg、brushed/brushless、2/3/4 叶桨) 2. 每架用 TD3 + 特权观测训练专用 teacher 3. 2084 参数 GRU student 用 MSE/KL 对 1000 个 teacher 做在线模仿 4. GRU 隐状态隐式学到系统辨识——线性探针证明隐状态能预测未见推重比(R²=0.95)
详细训练流程:
Stage 1: 采样 1000 架四旋翼
┌─────────────────────────────────────────────────────┐
│ 参数空间(可分解采样): │
│ │
│ 质量: m ∈ [0.032, 2.4] kg (Crazyflie → 大四旋翼) │
│ 臂长: l ∈ [0.03, 0.3] m │
│ 电机类型: {brushed, brushless} │
│ 桨叶数: {2, 3, 4} │
│ 桨叶直径: d ∈ [0.04, 0.25] m │
│ 电机 KV: KV ∈ [1000, 4000] (brushless) │
│ 电池: {1S, 2S, 3S, 4S} │
│ │
│ 自动推导: │
│ ├── 惯量: I = f(m, l, 几何) │
│ ├── 推力系数: k_f = f(桨叶, 直径) │
│ ├── 电机常数: K_m = f(KV, 电阻) │
│ └── 推重比: TWR = 4·k_f·Ω_max² / (m·g) │
│ │
│ 结果: 1000 架参数各不相同的四旋翼 │
└─────────────────────────────────────────────────────┘
Stage 2: 训练 1000 个 teacher
对每架四旋翼 i = 1, ..., 1000:
teacher_i = TD3(
观测 = [p, R, v, ω, a_{t-1}] + [m, I, k_f, ...] (特权观测!)
动作 = [Ω1, Ω2, Ω3, Ω4] / Ω_max (归一化 RPM)
奖励 = 悬停+跟踪
)
训练 ~18 秒 / 每个 teacher (RLtools)
总计: 1000 × 18 s ≈ 5 小时
Stage 3: 元模仿学习
┌─────────────────────────────────────────────────────┐
│ student = GRU(hidden_dim=16, 2084 参数) │
│ │
│ 训练循环: │
│ for epoch in range(N): │
│ for quad_i in sample(1000 个四旋翼): │
│ teacher_i = load_teacher(quad_i) │
│ env_i = Env(quad_i.params) │
│ h = zeros(16) # 重置 GRU 隐状态 │
│ for t in range(episode_length): │
│ o_t = env_i.observe() │
│ a_teacher = teacher_i(o_t, quad_i.params) │
│ a_student, h = student(o_t, h) │
│ loss += MSE(a_student, a_teacher) │
│ env_i.step(a_student) # 用 student 动作! │
│ loss.backward() │
│ optimizer.step() │
│ │
│ 关键设计: │
│ ├── 用 student 的动作推进环境(非 teacher 的) │
│ │ → 避免分布偏移(DAgger 思想) │
│ ├── 每个 episode 开头重置 h → 学习从零开始辨识 │
│ └── 同一 epoch 内看到多架四旋翼 → 学习泛化 │
└─────────────────────────────────────────────────────┘
9.5.4 部署范围¶
部署:覆盖 Crazyflie/M5StampFly/BetaFlight/PX4 等飞控;MCU CPU 占用 <10%;100-500 Hz 推理。
已验证的 10 种真实四旋翼:
┌──────────────────────────────────────────────────────────────┐
│ # │ 平台 │ 质量 │ 电机 │ 桨叶 │ 飞控 │
├───┼───────────────┼─────────┼─────────┼──────┼─────────────────┤
│ 1 │ Crazyflie 2.1 │ 32 g │ brushed │ 2叶 │ STM32(Crazyflie)│
│ 2 │ M5StampFly │ 47 g │ brushed │ 2叶 │ ESP32 │
│ 3 │ 自制 65mm │ 35 g │ brushless│ 3叶 │ STM32(BetaFlight│
│ 4 │ 自制 75mm │ 52 g │ brushless│ 3叶 │ STM32(BetaFlight│
│ 5 │ Tinywhoop │ 28 g │ brushed │ 4叶 │ STM32(BetaFlight│
│ 6 │ 自制 3" │ 180 g │ brushless│ 3叶 │ STM32(BetaFlight│
│ 7 │ 自制 5" │ 560 g │ brushless│ 3叶 │ STM32(BetaFlight│
│ 8 │ 自制 5" 重载 │ 850 g │ brushless│ 3叶 │ F7(BetaFlight) │
│ 9 │ Holybro X500 │ 1200 g │ brushless│ 2叶 │ Pixhawk(PX4) │
│10 │ 自制 7" │ 2400 g │ brushless│ 2叶 │ Pixhawk(PX4) │
└───┴───────────────┴─────────┴─────────┴──────┴─────────────────┘
零样本迁移(无微调):所有 10 架均能稳定悬停和轨迹跟踪
适应时间: < 1 秒(GRU 隐状态收敛)
9.5.5 与操纵类基础模型对比¶
与操纵类基础模型对比:RT-2(55B)、Octo(93M)、π₀(3B)做**语义维度**的跨任务泛化(数百种操纵技能);RAPTOR(2084)做**动力学维度**的跨平台泛化(不同质量/几何/推力)——两者是完全不同的问题类。
基础模型对比表:
┌──────────────┬──────────┬────────────┬─────────────────────────┐
│ 系统 │ 参数量 │ 泛化维度 │ 为什么能这么小/这么大 │
├──────────────┼──────────┼────────────┼─────────────────────────┤
│ RT-2 │ 55 × 10⁹ │ 语义+任务 │ 需要理解自然语言指令 │
│ │ │ (数百种技能)│ + 视觉场景 + 手臂运动学 │
├──────────────┼──────────┼────────────┼─────────────────────────┤
│ Octo │ 93 × 10⁶ │ 任务+形态 │ 跨不同机器人形态 │
│ │ │ (9种机器人) │ 需要处理异构动作空间 │
├──────────────┼──────────┼────────────┼─────────────────────────┤
│ π₀ │ 3 × 10⁹ │ 语义+物理 │ 语言条件化 + 灵巧操纵 │
│ │ │ (全能型) │ 需要视觉-语言-动作融合 │
├──────────────┼──────────┼────────────┼─────────────────────────┤
│ RAPTOR │ 2,084 │ 动力学 │ 四旋翼动力学变化是 │
│ │ │ (10+平台) │ 低维的(质量、推力比等) │
│ │ │ │ 且动作空间固定(4D RPM) │
└──────────────┴──────────┴────────────┴─────────────────────────┘
为什么 RAPTOR 只需要 2084 参数?
├── 四旋翼的"类型空间"是低维的:
│ 主要由推重比(1D)、惯量比(3D)、电机常数(4D)决定
│ → GRU 只需编码 ~8 个有效参数
├── 动作空间固定(4D RPM):
│ 不需要处理不同的动作维度或语义
├── 无视觉输入:
│ 只处理 22D 低维状态 → 不需要视觉编码器
└── 控制策略本身是"光滑的":
输入→输出的映射不需要复杂的非线性
9.5.6 与足式 RL 基础策略的对比¶
RAPTOR vs 足式 RL 的 RMA (Rapid Motor Adaptation):
RAPTOR RMA (Kumar RSS 2021)
┌──────────────┬──────────────────────────┬──────────────────────────┐
│ 适应机制 │ GRU 隐状态 │ MLP 适应模块 │
│ 适应目标 │ 动力学参数(推重比等) │ 环境参数(摩擦、地形等) │
│ 输入 │ o_t + h_{t-1} │ 本体感知历史(50步) │
│ 输出 │ a_t + h_t │ 环境嵌入 z ∈ R^8 │
│ 适应速度 │ ~100 步 (~0.5s) │ ~50 步 (~1s) │
│ 形式 │ 循环(隐式积累) │ 前馈(固定窗口) │
│ 可解释性 │ 线性探针可提取(R²=0.95) │ t-SNE 可视化可聚类 │
│ 参数量 │ 2,084 (整个策略) │ ~100K (适应模块) │
├──────────────┼──────────────────────────┼──────────────────────────┤
│ 共通点: │
│ ├── 都是"隐式在线适应"——不需要显式系统辨识 │
│ ├── 都从本体感知(proprioception)推断未观测参数 │
│ ├── 都使用 teacher-student 蒸馏(teacher 有特权信息) │
│ └── 都证明了隐空间编码了物理量(推重比/摩擦系数) │
├──────────────┼──────────────────────────┼──────────────────────────┤
│ 差异: │
│ ├── GRU 有无限记忆窗口; RMA 的 MLP 有固定窗口(50步) │
│ ├── RAPTOR 的适应是"软"的(隐状态渐变); RMA 的输出是"硬"的(每步重算)│
│ ├── RAPTOR 跨平台(不同硬件); RMA 跨地形(同一硬件) │
│ └── RAPTOR 2084参数; RMA 适应模块 ~100K参数 │
└──────────────┴──────────────────────────┴──────────────────────────┘
本质洞察:RAPTOR 只用 2084 参数就能跨平台,根源在于**四旋翼的"类型空间"本身是低维的**。一架四旋翼在动力学上和另一架的区别,主要就是推重比(1D)、惯量比(3D)、电机常数(几个)这十来个数——不像视觉/语言任务那样有无穷丰富的语义。GRU 的隐状态只需编码这 ~8 个有效自由度,自然不需要大网络。这给出一条普适的设计判据:基础策略需要多大,取决于"任务变化的内在维度",而非"任务看起来多复杂"。RAPTOR 小(动力学变化低维)、VLA 大(语义变化高维),不是谁更先进,而是它们面对的变化维度天差地别。
类比——GRU 隐状态像医生问诊时心里逐步收敛的诊断,不像查字典(MLP)。 RAPTOR 的 GRU 隐状态**像**医生面对新病人:头几分钟(前 100 步)通过观察症状逐步形成判断,心里的"诊断"从模糊到清晰地收敛,之后就基于这个诊断对症下药。隐状态 \(h_t\) 就是这个"逐步收敛的诊断",编码了"这架无人机的推重比/惯量大概是多少"。 但**不像**查字典式的 MLP——无记忆的 MLP 每一步都重新"查表":给它当前观测,它输出一个动作,但它记不住上一秒推断出的任何东西,无法积累"这架机器是什么类型"的判断。边界在于:GRU 有**跨时间步的状态积累**(适合缓慢变化的动力学参数),MLP 只有**单步映射**(适合每步都变的量,如足式的地形摩擦)。这也正是 RAPTOR 用 GRU、RMA 用固定窗口 MLP 的根本分野。
本节常见陷阱¶
陷阱 5-1:用无记忆 MLP 去做"跨平台零样本",期待它自己学会适应
- 错误描述:把一堆不同无人机的数据混在一起训一个 MLP 策略,以为"见得多了自然会泛化到新平台"。
- 现象/后果:策略对所有平台都平庸——轻机飞得肉、重机压不住,没有一架飞到该有的水平;面对全新平台更是直接失稳。
- 根本原因:MLP 每步只看当前观测,无法判断当前是哪一类无人机,只能学一个对所有平台折中的"平均增益"。跨平台适应本质上需要"先辨识、再控制",而辨识需要跨时间步积累信息——这正是无记忆 MLP 缺的能力。
- 正确做法:用带记忆的循环网络(GRU/LSTM),让隐状态在飞行初期在线辨识当前平台的动力学参数(推重比等),再据此控制。这是 RAPTOR 跨平台零样本的核心,也是它区别于"平均策略"的关键。
陷阱 5-2:把 RAPTOR 的"小参数量"误读成"基础策略都该很小"
- 错误描述:看到 RAPTOR 用 2084 参数就跨 10 平台,便推论"基础策略都不需要大模型,VLA 那些几十亿参数是浪费"。
- 现象/后果:试图用微型网络去做视觉导航或语言条件控制的"基础策略",结果完全学不动、严重欠拟合。
- 根本原因:混淆了"任务看起来的复杂度"与"任务变化的内在维度"。RAPTOR 小是因为四旋翼**动力学**变化是低维的(~8 个有效参数);视觉/语义任务的变化维度极高,需要大容量去覆盖。参数量该多大由变化的内在维度决定。
- 正确做法:判断一个基础策略该多大,先估"它要泛化的那个维度的内在复杂度"。动力学泛化(低维)→ 可以很小;语义/视觉泛化(高维)→ 需要大模型。RAPTOR 和 VLA 是不同问题类,不可类比大小。
本节练习¶
- (机制论证) §9.5.2 的线性探针实验显示 GRU 隐状态对推重比的 \(R^2=0.95\)。请论证:为什么"隐状态能被**线性**提取出推重比"是一个强结论?如果只能用一个高度非线性的解码器才能从隐状态恢复推重比,说明 GRU 学到的表示有什么不同?这对"隐状态 ≈ 系统辨识器"的解读意味着什么?
- (设计判据应用) 用本节的"基础策略大小 ∝ 任务变化内在维度"判据,估算以下三个"基础策略"各自大致需要的参数量级,并排序:(a) 跨不同质量/惯量的四旋翼悬停;(b) 跨不同地形/摩擦的四足行走;(c) 跨数百种厨房物体的机械臂抓取。给出每个任务"变化维度"的估计依据。
- (与 RMA 对比) RAPTOR 用 GRU(无限记忆),RMA 用固定窗口 MLP(50 步)。请论证:对于"缓慢变化的参数(无人机质量,飞行中基本不变)"为什么 GRU 更合适;对于"快速变化的参数(足式每步踩到的地形摩擦)"为什么固定窗口 MLP 反而更合适。记忆机制的选择如何由"被辨识量的时间尺度"决定?
§D9.6 MPC+RL 混合谱系 ⭐⭐⭐¶
本节定位:§D9.0 给出了"RL vs 经典"的二元对立,但工程现实往往是**两者结合**。本节是一张"混合方案地图"——五种把 MPC(或经典控制)与 RL 缝合在一起的范式,各自取两边的长、补两边的短。它把整章的"RL 性能"与"经典安全/可解释"重新焊回一个连续谱。
动机¶
到这里你已经看清了一对张力:RL 性能上限高但无安全保证、不可解释、泛化差;MPC/经典控制有形式化保证、可解释、天然泛化,但性能受轨迹参数化所限、在线计算昂贵。§D9.0 把它们当成二选一来讲,但真正的工程问题很少是非此即彼——能不能同时要 RL 的性能和经典的安全/结构? 这正是 MPC+RL 混合谱系要回答的。这个谱系不是单一方法,而是一族设计:有的让 MPC 当老师、RL 当学生(蒸馏出可部署的快策略);有的让 RL 出"大策略"、L1 自适应出"毫秒级小修正";有的把学到的神经网络动力学塞进 MPC;有的干脆把可微 MPC 当成 RL 的 actor。本节逐个剖析,让你在真实项目里能根据"更缺性能还是更缺安全"挑出合适的缝合方式。
反面:如果固执地纯 RL 或纯 MPC 到底会怎样¶
设想两个极端团队。团队 A 信奉纯 RL:他们做出了竞速 SOTA,但客户要求"出具安全认证报告、保证任何情况下不撞人",他们的黑箱策略无法形式化验证,项目卡死在合规环节。团队 B 信奉纯 MPC:他们的系统安全可证、可解释,但在敏捷穿越任务上始终比对手慢一截,且每帧 2-10ms 的在线优化让他们上不了便宜的 MCU。两个团队都把一个本可以"既要又要"的问题做成了"只能要一个"。混合谱系的存在就是为了打破这种自我设限——DATT 让团队 A 的 RL 配上有理论保证的 L1 自适应、AC-MPC 让团队 B 的 MPC 获得 RL 级的性能与在线自适应。下面这张地图,就是教你在性能与安全之间找到那个最适合你项目的缝合点。
理论与实现¶
| 范式 | 代表 | 思路 |
|---|---|---|
| MPC 专家 → BC/RL 学生 | SOUS VIDE(Stanford MSL) | MPC 在 GSplat 里跑 → 视觉策略蒸馏 |
| RL + L₁ 自适应叠加 | DATT(Huang 等 CoRL 2023, ~86★) | RL 负责长时程;L₁ 负责毫秒级扰动补偿 |
| 学到的动力学进入 MPC | Neural-MPC(Salzmann TUM, ~261★) | PyTorch → l4casadi → acados;82% 误差降低 |
| 可微 MPC 作为 actor | Actor-Critic MPC(Romero T-RO 2025) | RL 在线调 MPC 参数;形式保证 |
| 元学习残差 + 经典控制 | Neural-Fly(Caltech, ~211★) | DAIML 学风不变基;12 min 训练;Lyapunov 保证 |
9.6.1 SOUS VIDE 详解——MPC 专家在 Gaussian Splat 中训练视觉策略¶
SOUS VIDE 管线 (Stanford MSL):
1. 场景重建
真实环境扫描 → 3D Gaussian Splatting 重建
→ 高保真视觉场景(FiGS 仿真器,130 fps 渲染)
2. MPC 专家(在 GSplat 中运行)
┌─────────────────────────────────────────────────┐
│ MPC 拥有特权信息: │
│ ├── 精确的无人机状态(无噪声) │
│ ├── 精确的动力学模型 │
│ └── 精确的目标航点 │
│ │
│ MPC 在 GSplat 环境中飞行: │
│ ├── 生成 100K-300K 观测-动作对 │
│ ├── 随机化动力学参数(DR) │
│ └── 随机化空间扰动(起始位置等) │
└─────────────────────────────────────────────────┘
3. SV-Net 学生策略
┌─────────────────────────────────────────────────┐
│ 输入: │
│ ├── RGB 图像(相机) │
│ ├── 光流(运动估计) │
│ └── IMU 数据(加速度+角速度) │
│ │
│ 特殊模块: RMA (Rapid Motor Adaptation) │
│ ├── 从 IMU 历史推断动力学参数 │
│ └── 在线适应质量/风/外力变化 │
│ │
│ 输出: CTBR (推力 + 角速率) │
│ 频率: 20 Hz │
└─────────────────────────────────────────────────┘
4. 零样本 sim-to-real 迁移
GSplat 的视觉保真度足够高 → 策略直接在真实环境中工作
鲁棒性测试(105 次硬件实验):
├── ±30% 质量变化: 成功
├── 40 m/s 风阵: 成功
├── ±60% 光照变化: 成功
├── 移除/移动场景物体: 成功
└── 人员在视野中移动: 成功
9.6.2 DATT 详解——RL + L1 自适应控制¶
DATT 架构 (Huang 等, CoRL 2023):
┌─────────────────────────────────────────────────────────┐
│ DATT 控制架构 │
│ │
│ 参考轨迹 x_ref ──→ ┌──────────────────┐ │
│ │ RL 前馈策略 │ │
│ 当前状态 x ────→ │ (MLP, 仿真训练) │──→ a_ff │
│ └──────────────────┘ │
│ │
│ 当前状态 x ────→ ┌──────────────────────┐ │
│ │ L₁ 自适应控制器 │ │
│ 扰动估计 d̂ ←────│ (在线运行,不需训练) │──→ a_adapt │
│ └──────────────────────┘ │
│ │
│ 总控制指令: a = a_ff + a_adapt │
│ │
│ 分工: │
│ ├── a_ff: 长时程最优控制(RL 在仿真中学到) │
│ │ └── 处理: 轨迹跟踪、预见性控制 │
│ ├── a_adapt: 毫秒级扰动补偿(L₁ 在线估计) │
│ │ └── 处理: 风阵、未建模气动、载荷变化 │
│ └── 推理时间: < 3.2 ms (比 NMPC 快 4×) │
└─────────────────────────────────────────────────────────┘
为什么这种混合有效:
├── RL 策略在仿真中训练 → 学到全局最优行为
├── 但 RL 策略无法预见真实世界的瞬时扰动
├── L₁ 自适应控制有理论保证的扰动抑制能力
├── L₁ 不需要训练,只需要一个粗略的标称模型
└── 两者互补: RL 做"大策略",L₁ 做"小修正"
9.6.3 Neural-MPC 详解——学习到的动力学进入 MPC¶
Neural-MPC 管线 (Salzmann TUM):
1. 采集真实飞行数据(或仿真数据)
2. 训练残差动力学 PyTorch 模型:
f_learned(x, u) = f_nominal(x, u) + f_residual_NN(x, u)
f_nominal: 基于物理的标称模型(刚体动力学)
f_residual_NN: PyTorch MLP 学习残差
3. L4CasADi 桥接:
┌─────────────────────────────────────────────────┐
│ PyTorch 模型 │
│ ↓ (L4CasADi 自动转换) │
│ CasADi 符号表达式 │
│ ↓ (CasADi 自动微分) │
│ acados 非线性优化问题 │
│ ↓ (acados 求解) │
│ MPC 控制指令 │
└─────────────────────────────────────────────────┘
4. 实时运行:
频率: ~50-100 Hz (Jetson Xavier)
预测时域: 20 步
性能: 跟踪误差降低 82% (vs 标称模型 MPC)
优势:
├── 保留 MPC 的约束处理能力(输入/状态约束)
├── 保留 MPC 的预测能力(N 步前瞻)
├── 通过学习残差提高模型精度
└── 理论上可以提供稳定性分析(通过 MPC 的约束)
劣势:
├── 计算量比纯 RL 大(需要在线求解优化)
├── 需要 Jetson 级算力(不能在 MCU 上运行)
└── 学到的模型泛化性有限(超出训练分布可能不可靠)
9.6.4 Actor-Critic MPC 详解¶
AC-MPC (Romero, T-RO 2025):
核心思想: 把 MPC 当作 RL 的"有结构的 actor"
┌─────────────────────────────────────────────────────┐
│ 传统 RL: │
│ actor = MLP(θ) │
│ a = MLP_θ(o) │
│ θ 通过策略梯度更新 │
│ │
│ AC-MPC: │
│ actor = MPC(φ) │
│ a = MPC_φ(o) ← MPC 求解器作为策略 │
│ φ = {代价函数参数, 约束参数, 模型参数} │
│ φ 通过策略梯度更新(需要 MPC 对 φ 可微) │
│ │
│ 关键技术: │
│ ├── 可微 MPC: 通过 acados 的灵敏度分析获得梯度 │
│ ├── RL 调参: PPO 更新 φ → MPC 行为改善 │
│ └── Critic: 标准 MLP 值函数 │
│ │
│ 优势: │
│ ├── 保留 MPC 的结构先验(物理模型+约束) │
│ ├── 比纯 MLP actor 更好的 OOD 行为 │
│ ├── 更好的 sim-to-real(物理结构提供归纳偏置) │
│ └── 可以在线调整 MPC 参数(自适应) │
│ │
│ 性能: ~21 m/s 竞速飞行(与纯 RL 策略持平) │
│ 但 OOD 鲁棒性优于纯 RL │
└─────────────────────────────────────────────────────┘
9.6.5 Neural-Fly 详解——元学习风不变基¶
Neural-Fly (Caltech, Science Robotics 2022):
核心观察: 不同风况下的气动效应共享一个低维表示
1. DAIML 算法 (Domain Adversarially Invariant Meta-Learning):
┌─────────────────────────────────────────────────┐
│ 训练数据: 12 分钟真实飞行(多种风况) │
│ │
│ 学习: f_aero(x, v, ω) = Σ αi · φi(x, v, ω) │
│ │
│ φi: 风不变基函数(DNN 学到的) │
│ αi: 风依赖系数(在线实时估计) │
│ │
│ DAIML 的技巧: │
│ ├── 域对抗: 让 φi 对风况不变(风 A 和风 B 共用) │
│ ├── 元学习: 让 αi 能快速适应新风况 │
│ └── 谱归一化: 控制 DNN 的 Lipschitz 常数 │
│ → 提供闭环稳定性保证(Lyapunov 分析) │
└─────────────────────────────────────────────────┘
2. 在线适应:
复合自适应律: α̇ = -Γ · (e_tracking + e_prediction)
├── e_tracking: 位置跟踪误差
└── e_prediction: 气动力预测误差
适应速度: 数百毫秒(比 RL 策略的 GRU 更快)
3. 性能:
Caltech 真实天气风洞测试:
├── 风速: 最高 43.6 km/h (12.1 m/s)
├── 跟踪误差: 比标准非线性控制降低 66%
├── 比自适应控制降低 42%
└── Lyapunov 稳定性保证(形式化证明)
4. 与 Swift 残差学习的对比:
┌──────────────┬─────────────────┬──────────────────┐
│ │ Neural-Fly │ Swift 残差 │
├──────────────┼─────────────────┼──────────────────┤
│ 学什么 │ 气动力基函数 │ 观测+动力学残差 │
│ 用多少数据 │ 12 分钟 │ 50 秒 │
│ 在线适应 │ 是(复合适应律) │ 否(离线训练) │
│ 理论保证 │ Lyapunov │ 无 │
│ 与 RL 关系 │ 经典控制+学习 │ RL+残差模型 │
│ 部署 │ 机载 CPU │ Jetson GPU │
└──────────────┴─────────────────┴──────────────────┘
本质洞察:整个混合谱系可以用一个轴串起来——"结构先验"与"数据驱动"的混合比例。纯 MPC 是 100% 结构先验(物理模型 + 约束写死),纯 RL 是 100% 数据驱动(一切从奖励里学)。中间每一种混合方法,本质上是在选择"哪部分交给结构、哪部分交给数据":Neural-MPC 把动力学的残差交给数据、约束处理留给结构;DATT 把长时程策略交给数据(RL)、毫秒级扰动抑制留给结构(L1 的理论保证);AC-MPC 把 MPC 的参数交给数据调、MPC 的结构留作归纳偏置。没有放之四海皆准的最优比例——缺性能就往数据驱动挪,缺安全/泛化就往结构先验挪。 这条轴是你在真实项目里做混合设计的指南针。
本节常见陷阱¶
陷阱 6-1:用混合方法时把 RL 和经典控制器各自独立调到最优,再简单相加
- 错误描述:做 DATT 这类"RL 前馈 + L1 自适应"时,先把 RL 策略单独训到最好,再把 L1 单独调到最好,然后期望两者相加就是最优。
- 现象/后果:合在一起后出现"互相打架"——RL 已经补偿了的扰动 L1 又补一遍,导致超调、振荡,整体反而不如单独的 RL 或单独的经典控制器。
- 根本原因:两个控制器的职责边界没划清。如果 RL 在训练时就见过扰动并学会补偿,再叠一个 L1 补同样的扰动就是重复作用。混合方法的关键不是"两个好东西相加",而是**让每个组件负责互不重叠的频段/时间尺度**。
- 正确做法:明确分工——RL 负责长时程、可预见的最优行为(在无扰动或标称仿真中训练);L1/经典控制器负责 RL 无法预见的毫秒级、未建模扰动。训练 RL 时就约定好"扰动补偿不归你管",让两者频段正交、互补而非重叠。
本节练习¶
- (谱系定位) 把本节五种混合方法(SOUS VIDE、DATT、Neural-MPC、AC-MPC、Neural-Fly)按"结构先验 ↔ 数据驱动"的比例排在一条轴上,并为每一个标注:哪部分交给了结构、哪部分交给了数据。说明你的排序依据。
- (选型决策) 给定三个项目:(a) 强阵风下的户外巡检(缺鲁棒、需稳定性保证);(b) 室内竞速刷圈速(缺极限性能);(c) 货运无人机需通过适航认证(缺可验证性)。为每个项目从本节五种方法里选一个最合适的,并论证为什么——把它对应到"沿轴往哪个方向挪"。
- (反事实/职责划分) 针对陷阱 6-1:如果你**故意**在训练 DATT 的 RL 前馈时就注入与 L1 相同的扰动(让 RL 也学补偿),部署时 RL 和 L1 会如何相互作用?请预测系统行为,并据此说明"训练时的扰动设置"如何隐含地定义了两个控制器的职责边界。
§D9.7 仿真环境全景对比 ⭐⭐¶
本节定位:前面六节讲的所有系统都跑在某个仿真器上,而仿真器选错会让你事倍功半。本节是一张"仿真平台选型表"——按教学/研究/极速/视觉/可微五种需求,告诉你该用哪个。它是把前面的方法论落到"我现在该
pip install什么"的实操桥。
动机¶
无人机 RL 的第一个工程决策往往不是"用什么算法",而是"用什么仿真器"——而这个决策对后续效率的影响超出多数人预期。选 gym-pybullet-drones 入门很顺手,但想用 PPO 大批量训练就会被它的 CPU 单环境速度卡死;冲着 OmniDrones 的 GPU 并行去,却发现它的简化气动不适合做高保真 sim-to-real;想要照片级视觉做视觉策略,PhysX 的渲染又不够真。每个平台都是一组明确的取舍(引擎、是否 GPU 并行、仿真速度、视觉保真度),没有"最好"只有"最适合当前任务"。本节把主流平台的取舍摊开成一张对比表,让你在动手前就按需求选对,避免训到一半才发现仿真器不匹配、推倒重来。
反面:如果不看取舍随手选一个仿真器会怎样¶
设想你想复现 Swift 那样的高保真 sim-to-real,却图省事用了 gym-pybullet-drones——它的简化气动模型抹掉了 BEM 效应,你训出的策略在仿真里完美、上真机就垮,白费几周。反过来,你只是想做个教学 demo 让学生理解 RL 控制悬停,却硬上 OmniDrones + Isaac Sim——光环境配置和 GPU 驱动就劝退一半学生,杀鸡用了牛刀。再比如你要训视觉导航策略,选了无视觉输出的 RLtools 仿真器,根本喂不进图像。这些都是"需求与平台取舍不匹配"的典型翻车。下面这张表的价值,就是让你在 pip install 之前先把"我的核心需求是什么"和"每个平台牺牲了什么"对齐。
理论与实现¶
为了帮助读者选择合适的仿真环境,这里汇总主要无人机 RL 仿真平台:
┌────────────────────────────────────────────────────────────────────────────────┐
│ 无人机 RL 仿真平台对比 │
├──────────────┬───────────┬────────────┬───────────┬──────────┬─────────────────┤
│ 平台 │ 引擎 │ GPU 并行 │ 仿真速度 │ 视觉 │ 代表用户 │
├──────────────┼───────────┼────────────┼───────────┼──────────┼─────────────────┤
│ gym-pybullet │ PyBullet │ 否(CPU) │ ~1× │ 基础RGB │ 教学/入门 │
│ -drones │ │ │ │ │ (Panerati 2021) │
├──────────────┼───────────┼────────────┼───────────┼──────────┼─────────────────┤
│ OmniDrones │ Isaac Sim │ 是(4096+) │ ~100-500× │ 高保真 │ SimpleFlight │
│ │ (PhysX) │ │ │ (渲染) │ (Chen 2025) │
├──────────────┼───────────┼────────────┼───────────┼──────────┼─────────────────┤
│ Aerial Gym │ Isaac Gym │ 是(4096+) │ ~100-500× │ 深度图 │ NVIDIA │
│ │ (PhysX) │ │ │ │ │
├──────────────┼───────────┼────────────┼───────────┼──────────┼─────────────────┤
│ Flightmare │ Unity │ 否(CPU) │ ~1-10× │ 高保真 │ UZH RPG │
│ │ + 自研 │ │ │ (渲染) │ (早期 Swift) │
├──────────────┼───────────┼────────────┼───────────┼──────────┼─────────────────┤
│ RLtools │ 自研C++17 │ 否(CPU) │ ~6420× │ 无 │ RLtools/RAPTOR │
│ simulator │ 纯头文件 │ (CPU极快) │ (12.84G/s)│ │ (Eschmann 2024) │
├──────────────┼───────────┼────────────┼───────────┼──────────┼─────────────────┤
│ FiGS │ GSplat │ 是(GPU) │ ~1-10× │ 照片级 │ SOUS VIDE │
│ │ + 自研 │ │ (渲染慢) │ (GSplat) │ (Stanford 2024) │
├──────────────┼───────────┼────────────┼───────────┼──────────┼─────────────────┤
│ DiffAero │ 自研 │ 是(GPU) │ ~100× │ 无 │ BEM 可微仿真 │
│ │ (可微) │ │ │ │ (2025) │
└──────────────┴───────────┴────────────┴───────────┴──────────┴─────────────────┘
选择建议:
├── 入门学习: gym-pybullet-drones (最简单,文档好)
├── 严肃研究(PPO): OmniDrones / Aerial Gym (GPU 并行)
├── 极速训练(TD3): RLtools (C++ CPU,秒级训练)
├── 视觉策略: OmniDrones / FiGS (高保真渲染)
└── 可微仿真/梯度: DiffAero (GPU 可微 BEM)
本节常见陷阱¶
陷阱 7-1:用 PhysX 简化气动的仿真器(OmniDrones)做高保真 sim-to-real,期待 Swift 级表现
- 错误描述:因为 OmniDrones 跑得快、能 GPU 并行,就默认它的物理保真度也够做激进 sim-to-real,直接拿训出的策略上真机做高速机动。
- 现象/后果:低速悬停/温和跟踪还行,但一做高速机动就出现仿真里没有的失稳——因为 PhysX 的简化气动没建 BEM 失速、入流等效应。
- 根本原因:仿真速度和物理保真度是两个**独立**维度。OmniDrones 优化的是吞吐(GPU 并行),不是高速气动保真;Swift 用的是 BEM + 残差校正的高保真仿真器。混淆"快"和"真"会选错平台。
- 正确做法:按任务的"sim-to-real gap 敏感度"选平台。低速/温和任务用 GPU 并行仿真器(快)即可;高速/极限任务必须上高保真气动(BEM)+ 真实数据残差校正,或接受用 §D9.8 的逐步放速度链路在真机上谨慎逼近。
本节练习¶
- (选型决策) 给定四个任务:(a) 教学演示悬停;(b) 用 PPO 大批量训轨迹跟踪;(c) RLtools 秒级训练 MCU 部署;(d) 训练依赖 RGB 图像的视觉穿越策略。从本节对比表里为每个任务选一个最合适的仿真平台,并各用一句话说明"它满足了什么核心需求、牺牲了什么"。
- (两维度辨析) 用本节"仿真速度 vs 物理保真度是独立维度"的洞察,解释为什么"最快的仿真器"(RLtools,~6420×)和"最适合高保真 sim-to-real 的仿真器"可以不是同一个。在什么任务下你宁可牺牲速度换保真?反之呢?
§D9.8 Sim-to-Real 部署完整链路 ⭐⭐⭐¶
本节定位:本章的"收口"——前面讲了怎么训出好策略,本节讲怎么把它**安全地**搬上真机。这是一条六步链路,每一步都是一道"过不了就别进下一步"的关卡。读完它,你手里就有了一份可以照着执行的部署 checklist。
动机¶
新手最容易犯的致命错误,是把"仿真里训好的策略"直接烧进真机起飞——然后炸机、损毁硬件、甚至伤人。仿真到真机之间隔着一条由无数小裂缝组成的鸿沟:参数没测准、仿真器和真实有残差、数值精度在 MCU 上漂移、通信延迟、状态估计偶发失效……任何一道裂缝都可能让"仿真完美"的策略在真机上瞬间失控。专业团队(UZH RPG、ARPL、Stanford MSL)的共识是:sim-to-real 不是"训完就部署"的一步动作,而是一条**逐级验证、层层设防**的链路——每过一关才放开一点风险。本节把这条链路拆成六步(SysID → 仿真校验 → 训练 → sim-to-sim → HIL → 逐步放速度),每一步都有明确的"通过标准"和"不通过怎么办",让你的第一次真机部署不靠运气。
反面:如果跳过中间验证、直接 sim→真机会怎样¶
设想你省掉所有中间关卡:训完就上真机、全速飞。最可能的剧本是——第一次起飞策略就因为某个未校验的 gap(比如电机延迟比仿真大、或 float16 量化误差)失控,无人机以全速撞墙,硬件报废、数据全无,你甚至不知道是哪一步出的错(因为没有任何中间验证来定位)。即便侥幸第一次没炸,你也是在拿昂贵硬件和人身安全赌博。这条六步链路的每一关,本质上都是**把"未知的失败"提前转化为"可控的、便宜的、可定位的失败"**:sim-to-sim 在零硬件成本下暴露过拟合、HIL 在不起飞的情况下暴露数值/延迟问题、逐步放速度让你在低速(低风险)下发现问题再决定是否提速。下面逐关展开。
理论与实现¶
无论使用哪种方法训练策略,将其部署到真实无人机都需要经过一系列系统化的步骤。这里总结一个通用的 sim-to-real 部署链路:
┌──────────────────────────────────────────────────────────────────────────┐
│ Sim-to-Real 部署链路(6 步) │
│ │
│ Step 1: 系统辨识 (SysID) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 测量/标定: │ │
│ │ ├── 质量 m (电子秤) │ │
│ │ ├── 惯量 I (双线扭摆或 CAD 计算) │ │
│ │ ├── 推力系数 k_f (推力台) │ │
│ │ ├── 电机时间常数 T_m (阶跃响应) │ │
│ │ ├── 质心位置 (悬挂法) │ │
│ │ └── 电池放电曲线 (充放电测试) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Step 2: 仿真器构建与校验 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ├── 将 SysID 参数导入仿真器 │ │
│ │ ├── 在仿真器中重放真实飞行数据 │ │
│ │ ├── 对比仿真轨迹 vs 真实轨迹 │ │
│ │ ├── 如果误差 > 阈值 → 调整模型/添加残差 │ │
│ │ └── 确认: 仿真器在已知轨迹上的预测误差 < 5% │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Step 3: 策略训练 (sim 中) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ├── 选择训练框架: PPO(大样本) 或 TD3(小样本) │ │
│ │ ├── 设计观测/动作/奖励 │ │
│ │ ├── 添加域随机化(选择性) │ │
│ │ ├── 训练至收敛 │ │
│ │ └── sim 内评估: 多种初始条件/参数/扰动下的成功率 > 95% │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Step 4: Sim-to-Sim 验证 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ├── 在不同仿真器中测试(例如: 训练用 OmniDrones, │ │
│ │ │ 测试用 gym-pybullet-drones 或 Gazebo) │ │
│ │ ├── 检查: 策略是否依赖仿真器特有的 artifact? │ │
│ │ ├── 如果 sim-to-sim 失败 → 域随机化不足或观测设计问题 │ │
│ │ └── 通过 sim-to-sim → 有信心进行真实部署 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Step 5: 硬件在环 (HIL/SIL) 测试 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ├── SIL (Software-in-the-Loop): │ │
│ │ │ 策略在 PC 上运行 → 指令发送到仿真飞控 │ │
│ │ ├── HIL (Hardware-in-the-Loop): │ │
│ │ │ 策略在真实飞控(STM32/Jetson)上运行 │ │
│ │ │ → 观测来自仿真器 │ │
│ │ ├── 验证: │ │
│ │ │ ├── 推理延迟是否满足控制频率要求? │ │
│ │ │ ├── 数值精度(float32 vs float16)是否影响性能? │ │
│ │ │ └── 通信延迟是否可接受? │ │
│ │ └── HIL 通过 → 可以上真机 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Step 6: 真实飞行测试(逐步放开) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Phase A: 室内 mocap 环境 │ │
│ │ ├── 安全笼 + 安全飞手待命 │ │
│ │ ├── V_max = 1 m/s, 仅悬停 │ │
│ │ ├── 记录: 跟踪误差, 电机指令, 状态估计质量 │ │
│ │ └── 确认: 悬停稳定 → 进入 Phase B │ │
│ │ │ │
│ │ Phase B: 室内 mocap, 轨迹跟踪 │ │
│ │ ├── V_max = 3 m/s, 圆形/8字轨迹 │ │
│ │ ├── 逐步增加速度 │ │
│ │ └── 确认: 跟踪误差 < 阈值 → 进入 Phase C │ │
│ │ │ │
│ │ Phase C: 切换到 VIO (如适用) │ │
│ │ ├── 关闭 mocap → 仅用 VIO 状态估计 │ │
│ │ ├── V_max = 3 m/s → 逐步增加 │ │
│ │ └── 确认: VIO 稳定 → 进入 Phase D │ │
│ │ │ │
│ │ Phase D: 全速度飞行 │ │
│ │ ├── 移除速度限制 │ │
│ │ ├── 在目标场景中测试(竞速赛道/户外等) │ │
│ │ └── 采集数据 → 可选: 残差学习(Swift) 或微调 │ │
│ └─────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
本质洞察:这条六步链路的设计哲学是**"把失败左移"——在软件工程里"测试左移"指把 bug 尽量在早期、便宜的阶段暴露;sim-to-real 同理,让每一道 gap 尽量在**还没上真机、还没提速**的低成本阶段暴露并修复。sim-to-sim 用零硬件成本筛掉过拟合,HIL 在不起飞的前提下筛掉数值/延迟问题,逐步放速度让任何残余问题都在低速(低能量、低损毁)下先暴露。**部署的本质不是"让策略变完美",而是"让任何不完美都在便宜的地方先撞上"。 一个团队 sim-to-real 的成熟度,看的不是它的策略多强,而是它的失败有多便宜、多可定位。
反事实——如果硬件无限便宜、炸机零成本,这条链路会怎样? 它会大幅退化甚至消失:你可以直接 sim→真机全速试,炸了换一台继续,用真机当"仿真器"暴力试错。事实上足式 RL 的真机调试就接近这个模式(摔了扶起来)。正是因为无人机炸机昂贵且危险,"逐级验证"才有价值——这条链路的复杂度,是硬件成本和安全风险逼出来的,不是 RL 本身要求的。理解这一点,你就能根据自己的硬件成本/风险水平**裁剪**这条链路:用便宜的 Tinywhoop 练手时可以激进跳步,用昂贵竞速机或在有人环境时必须步步为营。
本节常见陷阱¶
陷阱 8-1:sim 内成功率达标就直接上真机,跳过 sim-to-sim 验证
- 错误描述:策略在训练用的那个仿真器里成功率 >95%,就认为"泛化没问题",直接进入真机测试。
- 现象/后果:真机上行为与仿真大相径庭、莫名失稳——而你无法判断是 sim-to-real gap 还是策略本身过拟合了训练仿真器的某些 artifact。
- 根本原因:在同一个仿真器里训练和评估,无法暴露策略对该仿真器**特有数值/物理 artifact 的过拟合**。成功率高可能只是"在它熟悉的世界里熟练",换个世界(哪怕另一个仿真器)就垮。
- 正确做法:先做 sim-to-sim(用不同仿真器交叉验证,如训练用 OmniDrones、测试用 gym-pybullet-drones/Gazebo)。sim-to-sim 都过不了,sim-to-real 几乎注定失败。它是零硬件成本就能筛掉过拟合的关键关卡(§D9.8 Step 4)。
陷阱 8-2:真机测试一上来就全速,不做逐步放速度
- 错误描述:为了尽快看到"竞速级"表现,第一次真机飞行就移除速度限制、全速冲赛道。
- 现象/后果:任何残余 gap 都在最高能量状态下爆发——撞击速度最快、损毁最严重、最危险,且数据往往因炸毁而丢失,无法复盘。
- 根本原因:违背了"把失败左移到低成本阶段"的原则。全速 = 最高动能 = 最贵的失败。在还没确认低速稳定前提速,等于把最不确定的策略放在最高风险的工况。
- 正确做法:严格执行 Phase A→D 的逐步放速度(mocap 悬停 → mocap 低速轨迹 → 切 VIO → 逐步全速),每一档确认稳定(跟踪误差 < 阈值、状态估计健康)再进下一档。配合独立安全 Guard(超界/翻转/超速锁电机),让每一次潜在失败都发生在尽可能低的能量下。
本节练习¶
- (链路裁剪) 本节洞察指出"链路复杂度由硬件成本/风险逼出"。请为两个场景各裁剪一版部署链路:(a) 用 $30 的 Tinywhoop 在自家客厅练手;(b) 用 $5000 竞速机在有观众的赛场首飞。明确说明每个场景你会保留/简化/强化哪些步骤,为什么。
- (关卡设计) 为六步链路的每一步补一条明确的"通过标准"和"不通过时回退到哪一步"。例如 Step 4 sim-to-sim 不通过应回退到 Step 3(域随机化不足/观测设计问题)还是 Step 2(仿真器保真不够)?给出你的判断逻辑。
- (综合·跨节) 结合 §D9.1 Swift 的残差学习、§D9.2 SimpleFlight 的选择性 DR、本节的六步链路,设计一次"从零把 Crazyflie 悬停策略搬上真机"的完整方案:哪一步做 SysID、哪一步决定要不要残差学习、哪一步做选择性 DR、各关卡的通过标准是什么。这是本章的跨节综合题。
前沿工作与开放问题¶
- RAPTOR 基础策略**代表了"**小而通用"的方向——与大模型 VLA 的"大而通用"形成有趣对照
- 机载持续学习:RLtools 已展示 iOS/Teensy 上的**在线训练**能力——真机持续微调在足式中尚未实现,在无人机中可能先突破
- 开放问题:纯 RL 是否能完全取代 MPC?(SimpleFlight 的消融表明在精确 SysID 下纯 RL 已经追平;但无稳定性保证);视觉+动力学联合基础策略(把 RAPTOR 的 GRU 系统辨识与 SOUS VIDE 的 GSplat 视觉融合)
开放问题详细讨论¶
问题 1:纯 RL 能否完全取代 MPC?
证据支持"可以":
├── Song 2023: RL 在竞速中超越时间最优 MPC(7.8% 更快)
├── SimpleFlight 2025: 纯 RL 在所有基准轨迹上超越 NMPC
├── Swift 2023: RL 击败人类冠军(人类使用类 MPC 的直觉)
└── 计算优势: RL 推理 <1ms vs MPC 2-10ms
证据支持"不能":
├── 安全保证: MPC 可以硬约束(状态/输入),RL 只能软约束(惩罚)
├── 泛化: MPC 基于物理模型,天然泛化到新任务; RL 需要重新训练
├── 可解释: MPC 的预测轨迹可以可视化; RL 的决策不透明
├── 认证: 航空认证要求可验证的控制律; RL 黑箱无法认证
└── 长时域: MPC 自然处理长时域约束; RL 需要特殊设计(HRL)
当前共识:
敏捷竞速/极限性能 → RL
安全关键/认证要求 → MPC
最佳实践 → 混合(RL 性能 + MPC 安全结构)
问题 2:视觉+动力学联合基础策略
当前分离:
├── RAPTOR: 动力学基础策略 (2084 参数, 低维状态输入)
└── SOUS VIDE: 视觉策略 (SV-Net, 图像输入)
联合方向:
├── 输入: 图像 + IMU → 视觉编码器 + GRU 适应器 → CTBR
├── 跨平台: GRU 适应不同动力学(RAPTOR 的能力)
├── 跨环境: 视觉编码器适应不同场景(SOUS VIDE 的能力)
└── 挑战: 参数量是否需要爆炸性增长?
可能的架构:
┌─────────────────────────────────────────────┐
│ 图像 → 小型 CNN/ViT → 场景特征 z_scene │
│ IMU + 状态 → GRU → 动力学特征 z_dyn │
│ [z_scene, z_dyn] → MLP → CTBR │
│ │
│ 估计参数量: │
│ ├── CNN: ~100K (小型, 如 MobileNet-tiny) │
│ ├── GRU: ~2K (RAPTOR 规模) │
│ ├── MLP: ~1K │
│ └── 总计: ~103K (仍然可以在 MCU 上运行?) │
└─────────────────────────────────────────────┘
问题 3:机载持续学习的前景
现状:
├── RLtools 已展示 iOS/Teensy 上的在线训练能力
│ (在 MCU 上运行 TD3 训练循环)
├── 但目前仅限于简单任务和小网络
└── 足式 RL 尚未实现真正的机载持续学习
无人机可能先突破的原因:
├── 网络小(2084 参数 vs 足式 ~100K)→ 训练计算量低
├── TD3 off-policy → 样本效率高 → 少量真实数据即可微调
├── 飞行环境变化快(风、电池老化) → 持续学习有刚需
└── 安全护栏成熟(地理围栏、姿态限制) → 在线学习的安全保障
挑战:
├── 在线学习可能导致灾难性遗忘
├── 小网络容量有限,持续学习空间有限
├── 安全保证: 如何确保学习过程中不坠毁?
└── 评估: 如何在飞行中判断新策略是否更好?
项目精读清单¶
- SimpleFlight:
scripts/train.py——PPO 训练入口;sim/quadrotor_env.py——OmniDrones 环境封装 - SimpleFlight:
configs/——五因子配方的 YAML 配置:观测空间、奖励权重、DR 参数范围 - learning-to-fly:
src/rl/environment/——纯 C++ 四旋翼环境(约 500 行):状态转移、奖励、域随机化 - learning-to-fly:
src/rl/training/——TD3 训练循环(约 300 行):非对称 AC + 课程 + 回放缓冲 - RLtools:
include/rl_tools/——纯 C++17 模板元编程 RL:MLP/Replay/TD3/SAC 全栈头文件 - RAPTOR:
raptor/——GRU 策略 + 元模仿学习训练脚本 - DATT:
datt/——L₁ 自适应 + RL 前馈的组合
精读指南——如何高效阅读这些代码:
推荐阅读顺序:
Level 1 (入门, ~2 小时):
├── gym-pybullet-drones: gym_pybullet_drones/envs/BaseAviary.py
│ → 理解四旋翼仿真的基本结构(状态更新、动力学计算)
└── SimpleFlight: scripts/train.py + configs/crazyflie.yaml
→ 理解 PPO 训练的标准流程
Level 2 (进阶, ~4 小时):
├── SimpleFlight: sim/quadrotor_env.py
│ → 理解观测空间/动作空间/奖励函数的具体实现
├── learning-to-fly: src/rl/environment/
│ → 理解纯 C++ 仿真器的实现(对比 Python 实现)
└── DATT: datt/
→ 理解 RL + L1 混合控制的实现
Level 3 (深入, ~6 小时):
├── RLtools: include/rl_tools/nn/
│ → 理解 C++17 模板元编程的 MLP 实现
├── RAPTOR: raptor/
│ → 理解 GRU 策略 + 元模仿学习
└── Swift 论文 Supplementary Materials
→ 理解 BEM 仿真器、GP 残差、感知系统的细节
实战练习¶
A 型练习(动手实现,各 4-6 小时)¶
- [A 型·SimpleFlight 复现] 在 OmniDrones 上用 SimpleFlight 配方训练 Crazyflie 悬停策略(PPO,4096 env)。记录训练曲线(reward vs step)。对比:有 SysID+选择性 DR vs 盲目 DR 的 sim2sim 跟踪误差
实施步骤:
1. 环境准备:
pip install omnidrones
git clone https://github.com/thu-uav/SimpleFlight.git
2. 修改配置 (configs/crazyflie.yaml):
# 实验 A: 选择性 DR
domain_randomization:
mass: {enabled: false} # SysID 精确值
inertia: {enabled: false}
k_f: {enabled: false}
k_drag: {enabled: true, range: [0.5, 1.5]}
wind: {enabled: true, range: [-1, 1]}
# 实验 B: 盲目 DR
domain_randomization:
mass: {enabled: true, range: [0.5, 1.5]}
inertia: {enabled: true, range: [0.5, 1.5]}
k_f: {enabled: true, range: [0.5, 1.5]}
k_drag: {enabled: true, range: [0.5, 1.5]}
wind: {enabled: true, range: [-1, 1]}
3. 训练:
python scripts/train.py --config configs/crazyflie.yaml --seed 0,1,2
4. 评估:
python scripts/eval.py --checkpoint best.pt --trajectory figure8
记录: 位置 RMSE, 速度 RMSE, 动作平滑度
5. 预期结果:
选择性 DR RMSE ≈ 0.04 m
盲目 DR RMSE ≈ 0.08 m (约 2× 差)
- [A 型·RLtools 18 秒训练] 按
arplaboratory/learning-to-flyREADME 在笔记本上训练——验证 18 秒完成。导出策略头文件(.h),检查 constexpr 权重数组
实施步骤:
1. 克隆并编译:
git clone --recursive https://github.com/arplaboratory/learning-to-fly
cd learning-to-fly
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
2. 训练:
time ./train_hover
# 预期输出: 训练完成,时间 ~18 秒
# 观察: 奖励曲线、探索噪声衰减、课程进度
3. 导出策略:
./export_policy --checkpoint best --output actor_hover.h
4. 检查头文件:
less actor_hover.h
# 预期: constexpr float weights_0[64][18] = {...};
# 验证: 权重值合理(大多数 <1.0,无 NaN/Inf)
5. 编译到 MCU(可选):
# 需要 STM32 工具链
arm-none-eabi-gcc -O2 -mcpu=cortex-m4 firmware.c -o firmware.elf
- [A 型·CTBR vs RPM] 在 gym-pybullet-drones 中分别用 CTBR 和 RPM 动作空间训练悬停策略。对比:加入 ±20% 质量扰动后的存活率——预期:CTBR 远优于 RPM
实施步骤:
1. 安装:
pip install gym-pybullet-drones stable-baselines3
2. 定义两种环境:
# env_ctbr.py: 动作 = [thrust, wx, wy, wz]
# env_rpm.py: 动作 = [rpm1, rpm2, rpm3, rpm4]
3. 训练(各 3 个种子):
python train.py --action_space CTBR --seed 0,1,2
python train.py --action_space RPM --seed 0,1,2
4. 鲁棒性测试:
for mass_scale in [0.8, 0.9, 1.0, 1.1, 1.2]:
python eval.py --mass_scale {mass_scale} --action_space CTBR
python eval.py --mass_scale {mass_scale} --action_space RPM
5. 预期结果:
┌──────────────┬─────────────────┬─────────────────┐
│ 质量扰动 │ CTBR 存活率 │ RPM 存活率 │
├──────────────┼─────────────────┼─────────────────┤
│ ±0% │ 98% │ 95% │
│ ±10% │ 95% │ 70% │
│ ±20% │ 90% │ 30% │
└──────────────┴─────────────────┴─────────────────┘
- [A 型·足式→无人机迁移] 把你已有的 legged_gym PPO 训练框架(如果有),最小修改适配到 OmniDrones 环境。记录:需要修改哪些文件?哪些完全复用?
实施步骤:
1. 从 legged_gym 出发:
cp -r legged_gym/legged_gym quad_rl/
2. 需要修改的文件:
├── envs/base/legged_robot.py → envs/base/quadrotor.py
│ 修改: _compute_observation(), _compute_reward(),
│ _reset_idx(), __init__()
│ 工作量: ~200 行修改
├── envs/base/legged_robot_config.py → quadrotor_config.py
│ 修改: 观测/动作维度,物理参数,奖励权重
│ 工作量: ~100 行修改
└── scripts/play.py → scripts/play_quad.py
修改: 可视化,通信接口
工作量: ~50 行修改
3. 完全复用的文件:
├── algorithms/ppo.py (零修改)
├── utils/math.py (零修改)
├── utils/terrain.py (删除,无需地形)
├── utils/logger.py (零修改)
└── scripts/train.py (改 import 路径即可)
4. 记录迁移清单(填表):
┌──────────────────┬──────────────┬───────────┐
│ 文件 │ 复用/修改/新写│ 工作量 │
├──────────────────┼──────────────┼───────────┤
│ ppo.py │ 完全复用 │ 0 │
│ actor_critic.py │ 改维度 │ 10 行 │
│ quadrotor.py │ 新写 │ 300 行 │
│ config.py │ 新写 │ 100 行 │
│ rewards.py │ 新写 │ 50 行 │
│ train.py │ 改 import │ 5 行 │
└──────────────────┴──────────────┴───────────┘
B 型练习(论文精读+分析,各 3-4 小时)¶
- [B 型·Swift 论文精读] 精读 Swift Nature 2023 论文(主文+Supplementary)。画出完整管线:仿真器(BEM+电池)→ PPO(观测/动作/奖励)→ 感知(CNN+PnP+KF)→ 残差模型(GP 观测+kNN 动力学)→ 微调 → 部署。标注:~50 秒真实数据如何被使用
精读指引:
1. 主文 (7 页):
├── Fig. 1: 系统总览 → 画出你自己的版本
├── Fig. 2: 赛道和比赛结果 → 提取定量数据
├── Fig. 3: 速度/加速度对比 → 分析 RL vs 人类的差异
└── Methods 节: 数学细节(观测/动作/奖励/训练)
2. Supplementary (30+ 页):
├── S1: 硬件细节(无人机规格、传感器)
├── S2: 仿真器(BEM、电池、电机、传感器噪声)
├── S3: PPO 超参数(完整列表)
├── S4: 感知系统(VIO、门检测、融合)
├── S5: 残差模型(GP、kNN 的具体实现)
└── S6: 部署和竞赛细节
3. 输出:
├── 一张 A3 纸的管线图(手绘或工具)
├── 关键设计决策列表(每个决策注明理由)
└── 与 D8 经典方法的对比表
- [B 型·SimpleFlight 消融表] 复现 SimpleFlight 的表 2(五因子消融)中至少两个消融实验。验证:去掉动作差分平滑奖励后,策略是否出现高频震荡
精读指引:
1. 选择两个消融实验:
推荐: 因子 1 (旋转表示) 和 因子 3 (奖励平滑项)
2. 因子 1 消融(旋转矩阵 vs 四元数):
修改 configs/crazyflie.yaml:
observation:
rotation_representation: "matrix" → "quaternion"
训练 → 评估 → 对比 RMSE
3. 因子 3 消融(动作差分 vs 动作幅度):
修改 configs/crazyflie.yaml:
reward:
action_smoothness: "diff" → "l2"
训练 → 评估 → 对比:
├── 跟踪 RMSE
├── 动作频谱(FFT 分析)
└── 电机指令的峰值频率
4. 预期: 动作幅度惩罚版本的 FFT 频谱中
50-200 Hz 分量显著增加 → 高频震荡证据
思考题(深度分析,各 1-2 小时)¶
- [思考题 1] 足式 RL 的 actuator network(Hwangbo ANYmal 2019)学习真实 SEA 电机的力矩响应,解决 sim-to-real;Swift 的残差 GP/kNN 学习 VIO 观测+动力学的残差。两者都是"学习一个修补项来弥合 sim-to-real"——但 actuator network 是**前向模型**(输入指令→预测力矩),而 Swift 的残差是**观测模型**(输入状态→预测误差分布)。为什么选择不同?
分析提示:
1. 信息流方向:
├── actuator network: 指令空间 → 力/力矩空间(前向)
│ 原因: SEA 的非线性在"指令→力矩"映射中
│ 知道指令和当前状态 → 可以预测实际输出力矩
└── Swift GP/kNN: 状态空间 → 误差分布(观测模型)
原因: VIO 的误差在"状态→观测"映射中
知道真实状态 → 可以预测 VIO 会给出什么观测
2. 不确定性类型:
├── SEA: 确定性非线性(同输入→同力矩)→ 用 MLP 前向模型
└── VIO: 随机性噪声(同状态→不同观测)→ 用 GP 概率模型
3. 数据需求:
├── actuator network: 电机台架测试(无风险)→ 数据丰富
└── Swift GP: 真实飞行(有风险)→ 数据稀缺(~50s)
- [思考题 2] RAPTOR 基础策略用 GRU 的隐状态做隐式系统辨识;足式 RL 的 RMA 用 MLP 的适应模块从本体感知历史推断环境嵌入。两者在"隐式在线适应"这一设计模式上有什么共通点和差异?
分析提示:
1. 共通点:
├── 都从感觉运动历史推断未观测的物理参数
├── 都使用 teacher-student 蒸馏(teacher 有特权信息)
├── 都不需要显式系统辨识(无需 SysID 步骤)
└── 都在部署时实时适应(在线)
2. 差异:
├── 记忆机制: GRU(循环,理论上无限记忆) vs MLP(前馈,固定窗口)
├── 适应范围: RAPTOR(跨平台,质量 75×) vs RMA(同平台,地形/摩擦)
├── 可解释性: RAPTOR(线性探针 R²=0.95) vs RMA(t-SNE 可聚类)
└── 参数效率: RAPTOR(2084 总参数) vs RMA(~100K 适应模块)
3. 深层问题:
GRU 循环 vs MLP 前馈的选择取决于:
├── 参数是否缓慢变化? → 是 → GRU(积累信息)
│ 四旋翼的质量/惯量不会突变 → GRU 合适
├── 参数是否快速变化? → 是 → MLP(即时推断)
│ 地形摩擦每步都变 → MLP 合适
└── 需要"忘记"旧信息吗?
RAPTOR: 不需要(同一架无人机) → GRU 的遗忘门可以关闭
RMA: 需要(地形一直在变) → MLP 天然只看当前窗口
- [思考题 3: 扩展] 如果你要设计一个同时具备 RAPTOR 的跨平台能力和 SOUS VIDE 的视觉导航能力的系统,你会如何设计架构? 考虑: (a) 参数量约束(需要在 Jetson Nano 上实时运行); (b) 训练数据来源; (c) sim-to-real 策略。画出你的系统架构图。
分析提示:
架构方案:
┌──────────────────────────────────────────────────────────┐
│ 输入层: │
│ ├── 图像 (320×240, 30Hz) → 轻量 CNN (MobileNet-v3-tiny) │
│ │ → 场景嵌入 z_scene ∈ R^32 │
│ ├── 光流 (20Hz) → 2 层 MLP → 运动嵌入 z_motion ∈ R^8 │
│ └── IMU + 状态 (200Hz) → 与 GRU 融合 │
│ │
│ 适应层 (RAPTOR 风格): │
│ ├── [z_scene, z_motion, IMU, 状态, a_{t-1}] │
│ │ → GRU (hidden_dim=32) → h_t ∈ R^32 │
│ └── h_t 编码: 动力学参数 + 场景特征 │
│ │
│ 控制层: │
│ ├── [h_t, 状态误差, 未来参考] → MLP (2×64) → CTBR │
│ └── 频率: 50 Hz (视觉) → 100 Hz (状态控制, 插值) │
│ │
│ 参数量估计: │
│ ├── MobileNet-tiny: ~50K │
│ ├── 运动 MLP: ~1K │
│ ├── GRU: ~8K │
│ ├── 控制 MLP: ~10K │
│ └── 总计: ~69K (Jetson Nano 可运行) │
│ │
│ 训练策略: │
│ ├── Stage 1: 在 OmniDrones 训练动力学 teacher (无视觉) │
│ ├── Stage 2: 在 FiGS/GSplat 训练视觉 teacher (固定动力学) │
│ ├── Stage 3: 联合蒸馏到 student (GRU + CNN) │
│ └── Stage 4: 真实数据微调 (残差学习) │
└──────────────────────────────────────────────────────────┘
关键论文参考文献¶
[1] Hwangbo, J., Sa, I., Siegwart, R., & Hutter, M. (2017).
Control of a Quadrotor with Reinforcement Learning.
IEEE Robotics and Automation Letters, 2(4), 2096-2103.
[2] Molchanov, A., et al. (2019).
Sim-to-(Multi)-Real: Transfer of Low-Level Robust Control Policies
to Multiple Quadrotors. IROS 2019.
[3] Loquercio, A., Kaufmann, E., et al. (2021).
Learning High-Speed Flight in the Wild. Science Robotics, 6(59).
[4] Kaufmann, E., Bauersfeld, L., & Scaramuzza, D. (2022).
A Benchmark Comparison of Learned Control Policies for Agile
Quadrotor Flight. ICRA 2022.
[5] Song, Y., Romero, A., Müller, M., Koltun, V., & Scaramuzza, D. (2023).
Reaching the Limit in Autonomous Racing: Optimal Control versus
Reinforcement Learning. Science Robotics, 8(82).
[6] Kaufmann, E., et al. (2023).
Champion-level Drone Racing Using Deep Reinforcement Learning.
Nature, 620(7976), 982-987.
[7] Huang, K., Rana, R., Spitzer, A., Shi, G., & Boots, B. (2023).
DATT: Deep Adaptive Trajectory Tracking for Quadrotor Control.
CoRL 2023.
[8] Eschmann, J., Albani, D., & Loianno, G. (2024).
Learning to Fly in Seconds.
IEEE Robotics and Automation Letters.
[9] Chen, J., Yu, C., et al. (2025).
SimpleFlight: What Matters in Zero-Shot Sim-to-Real RL for
Quadrotor Control. IEEE RA-L.
[10] Eschmann, J., et al. (2025).
RAPTOR: A Foundation Policy for Quadrotor Control.
Science Robotics.
[11] Salzmann, T., et al. (2023).
Real-time Neural-MPC: Deep Learning Model Predictive Control for
Quadrotors and Agile Robotic Platforms. IEEE RA-L.
[12] O'Connell, M., et al. (2022).
Neural-Fly Enables Rapid Learning for Agile Flight in Strong Winds.
Science Robotics, 7(66).
[13] Romero, A., et al. (2025).
Actor-Critic Model Predictive Control: Differentiable Optimization
meets Reinforcement Learning for Agile Flight. IEEE T-RO.
[14] Nagami, K., et al. (2024).
SOUS VIDE: Cooking Visual Drone Navigation Policies in a
Gaussian Splatting Vacuum. IEEE RA-L.
[15] Panerati, J., et al. (2021).
Learning to Fly -- a Gym Environment with PyBullet Physics for
Reinforcement Learning of Multi-agent Quadcopter Control. IROS 2021.
预计学习时间¶
2 周(20-28 小时),其中: - 第零章(RL vs 经典规控):3 小时 - Swift 全管线理解:6 小时 - SimpleFlight 五因子:4 小时 - RLtools/RAPTOR:4 小时 - 足式迁移对照:3 小时 - MPC+RL 混合:3 小时 - 仿真环境与部署链路:2 小时 - 实战练习:6 小时
附录 A:常用符号表¶
┌──────────┬──────────────────────────────────────────────────┐
│ 符号 │ 含义 │
├──────────┼──────────────────────────────────────────────────┤
│ p │ 世界系位置 [x, y, z] (m) │
│ v │ 世界系速度 [vx, vy, vz] (m/s) │
│ R │ 旋转矩阵 ∈ SO(3) (9 维展平后作为输入) │
│ q │ 四元数 [qw, qx, qy, qz] (4 维) │
│ ω │ 机体系角速度 [ωx, ωy, ωz] (rad/s) │
│ T │ 总推力 (N) │
│ c │ 质量归一化推力 T/m (m/s²) │
│ Ω │ 电机转速 (rad/s) │
│ k_f │ 推力系数 T = k_f · Ω² (N/(rad/s)²) │
│ k_m │ 力矩系数 M = k_m · Ω² (N·m/(rad/s)²) │
│ τ_m │ 电机时间常数 (s) │
│ m │ 无人机总质量 (kg) │
│ I │ 惯量矩阵 diag(Ixx, Iyy, Izz) (kg·m²) │
│ g │ 重力加速度 9.81 m/s² │
│ CTBR │ Collective Thrust + Body Rate (4D 动作空间) │
│ SRT │ Single Rotor Thrust (4D, 直接电机推力) │
│ LV │ Linear Velocity (3D, 期望线速度) │
│ BEM │ Blade Element Momentum (叶素动量理论) │
│ DR │ Domain Randomization (域随机化) │
│ SysID │ System Identification (系统辨识) │
│ VIO │ Visual-Inertial Odometry (视觉惯性里程计) │
│ GP │ Gaussian Process (高斯过程) │
│ kNN │ k-Nearest Neighbors (k 近邻) │
│ GRU │ Gated Recurrent Unit (门控循环单元) │
│ PPO │ Proximal Policy Optimization │
│ TD3 │ Twin Delayed DDPG │
│ GAE │ Generalized Advantage Estimation │
│ RMA │ Rapid Motor Adaptation │
│ DAIML │ Domain Adversarially Invariant Meta-Learning │
│ FCU │ Flight Control Unit (飞控) │
│ ESC │ Electronic Speed Controller (电子调速器) │
│ MCU │ Microcontroller Unit (微控制器) │
│ TWR │ Thrust-to-Weight Ratio (推重比) │
│ OOD │ Out-of-Distribution (分布外) │
└──────────┴──────────────────────────────────────────────────┘
附录 B:关键代码仓库速查¶
┌──────────────────────────────────────────────────────────────────────┐
│ 仓库 │ Stars │ 用途 │
├──────────────────────────────────────┼───────┼───────────────────────┤
│ thu-uav/SimpleFlight │ ~200 │ 五因子 PPO 基线 │
│ btx0424/OmniDrones │ ~495 │ GPU 并行无人机仿真 │
│ rl-tools/rl-tools │ ~942 │ C++17 RL 全栈库 │
│ arplaboratory/learning-to-fly │ ~558 │ 18 秒训练 + MCU 部署 │
│ rl-tools/raptor │ ~170 │ 2084 参数基础策略 │
│ utiasDSL/gym-pybullet-drones │ ~1.2K │ PyBullet 入门环境 │
│ uzh-rpg/acmpc_public │ ~50 │ Actor-Critic MPC │
│ Tim-Salzmann/l4casadi │ ~261 │ PyTorch→CasADi 桥接 │
│ TUM-AAS/neural-mpc │ ~261 │ 学习动力学+MPC │
│ github.com/kevinhuang8/DATT │ ~86 │ RL + L1 自适应 │
│ stanfordmsl/SousVide │ ~50 │ GSplat+MPC→视觉策略 │
└──────────────────────────────────────┴───────┴───────────────────────┘
本章常见误解汇总¶
| # | 常见误解 | 实际情况 | 详见 |
|---|---|---|---|
| 1 | "RL 在竞速里赢了 MPC,说明 RL 全面更强、MPC 过时了" | RL 只在"结构化、可大量仿真、追求极限性能"这一维度上限更高;安全保证、可解释性、未见场景泛化、数据效率上系统性弱于经典方法 | §D9.0、陷阱 0-1 |
| 2 | "端到端无中间瓶颈最优,所以任何显式表示都该去掉" | 仅当中间表示**对最终目标有损**时去掉才划算。竞速里的参数化轨迹有损(去掉);VIO 状态估计、门几何位置信息损失小且提升泛化/可调试(Swift 保留) | §D9.0、陷阱 0-2、§9.1.8 |
| 3 | "sim-to-real 靠让策略对 gap 鲁棒(域随机化越多越好)" | Swift 的哲学相反:用真实数据把仿真器**修正到接近真实**再让策略适应。选择性 DR > 盲目 DR(盲目 DR 精度反降 ~40%) | §9.1.9、§9.2.6、陷阱 2-2 |
| 4 | "姿态观测用四元数/欧拉角更省维度且等价" | 欧拉角有万向节锁、四元数有双覆盖,都会被策略放大成控制缺陷。9D 旋转矩阵无奇异无歧义,消融 RMSE 显著更低 | §9.2.3、陷阱 2-1 |
| 5 | "RLtools 的 18 秒全靠 C++ 实现快" | 18 秒 = 样本效率(TD3, 333×) × 仿真速度(C++, 12840×) × 零框架开销,三者相乘。换成 PPO 立刻退回分钟级 | §9.4.1、陷阱 4-1 |
| 6 | "基础策略都该很小(RAPTOR 才 2084 参数)" / "都该很大(像 VLA)" | 参数量由"任务变化的内在维度"决定。四旋翼动力学变化低维(~8 参数)→ 小;视觉/语义变化高维 → 大。不同问题类不可类比 | §D9.5、陷阱 5-2 |
| 7 | "混合方法 = 把单独调好的 RL 和经典控制器相加" | 会互相打架(重复补偿同一扰动)。关键是让两者负责**互不重叠的频段/时间尺度**(RL 长时程、L1 毫秒级扰动) | §D9.6、陷阱 6-1 |
| 8 | "仿真器跑得快就代表物理保真度高" | 速度与保真度是**独立**维度。OmniDrones 快但简化气动;高保真 sim-to-real 需 BEM + 残差校正 | §D9.7、陷阱 7-1 |
| 9 | "策略在训练仿真器里成功率高就能上真机" | 可能只是过拟合了该仿真器的 artifact。必须先 sim-to-sim 交叉验证(零硬件成本筛过拟合) | §D9.8、陷阱 8-1 |
| 10 | "足式 RL 经验可整体照搬到无人机" | 算法层(PPO/非对称AC/蒸馏)零改动复用;任务层(动作空间/奖励/安全协议)必须重建,因无人机失败代价 O(100) vs 足式 O(1) | §D9.3、陷阱 3-1/3-2 |
本章速查表¶
一、动作空间选型速查
| 动作空间 | 维度 | 下层依赖 | 鲁棒性 | sim-to-real | 适用 |
|---|---|---|---|---|---|
| LV(线速度) | 3 | 速度+姿态+rate PID | 最高 | 性能上限低(~1.5g) | 安全优先、温和飞行 |
| CTBR(推力+体速率) | 4 | 仅 rate PID | 高 | 最佳平衡 | 敏捷飞行默认选择(Swift/SimpleFlight) |
| SRT/直接 RPM | 4 | 无 | 低 | 对电机模型最敏感 | MCU 部署省 rate PID(RLtools),需精确 SysID |
二、算法选型速查(PPO vs TD3)
| 维度 | PPO(on-policy) | TD3(off-policy) |
|---|---|---|
| 样本需求 | ~10⁸–10⁹ 步 | ~10⁶–10⁷ 步 |
| 最佳搭档 | GPU 大批量并行仿真(4096+ env) | CPU 极速仿真 / 秒级训练 |
| 稳定性 | 高(clipping);但需大批量 | 中(Q 过估计);对批量不敏感 |
| 代表 | Swift、SimpleFlight | RLtools、RAPTOR teacher |
三、sim-to-real 五法对比
| 方法 | 思路 | 真实数据需求 | 代价 |
|---|---|---|---|
| 域随机化 DR | 随机化参数让策略鲁棒 | 0 | 过度正则化、性能降 |
| 系统辨识 SysID | 精确测量参数匹配仿真 | 0(台架测量) | 难辨识全部参数 |
| 选择性 DR(SimpleFlight) | SysID + 仅随机难辨识参数 | 0(台架测量) | 需区分可辨/难辨 |
| 残差学习(Swift) | SysID + 学习剩余 gap(GP+kNN) | ~50s 飞行 | 残差仅对该机该时段有效 |
| 基础策略(RAPTOR) | GRU 隐式在线辨识,跨平台零样本 | 0(元模仿) | 训练成本高(1000 teacher) |
四、五因子配方速查(SimpleFlight)
| 因子 | 该做 | 不该做 |
|---|---|---|
| Actor 输入 | 速度 + 旋转矩阵(9D) + 10 未来参考点 | 四元数/欧拉角 |
| Critic 输入 | Actor 输入 + 时间向量 \(f_t\) | 省略 \(f_t\)(价值方差 ↑30%) |
| 奖励 | 动作差分平滑 \(-\|u_t-u_{t-1}\|\) | 动作幅度惩罚 \(-\|u\|^2\) |
| sim-to-real | 精确 SysID + 选择性 DR | 盲目全参数 DR |
| 训练 | 大批量 PPO(4096+ env) | 小批量(不收敛) |
五、关键数字速查
| 量 | 数值 | 出处 |
|---|---|---|
| Swift 感知-动作延迟 | ~40 ms(人类 ~220 ms) | §D9.1 |
| Swift sim-to-real 真实数据 | ~50 s(3 圈,~1000 样本) | §9.1.9 |
| Song 2023 RL vs MPC | 单圈快 7.8%、峰值 >12g、~108 km/h | §D9.0 |
| RLtools 训练时间 | ~18 s(2020 M1) | §D9.4 |
| RLtools 仿真速度 | 12.84e9 步/s | §D9.4 |
| RAPTOR 参数量 | 2084(3 层 GRU,hidden=16) | §D9.5 |
| RAPTOR 跨平台范围 | 32g–2.4kg,10 种异构 | §D9.5 |
| 旋转表示消融 RMSE | 矩阵 0.043 / 四元数 0.062 / 欧拉 0.081 m | §9.2.3 |
故障排查手册¶
真机/训练出问题时,先按"症状"定位行,再顺"原因→排查步骤"收敛。每条给出相关章节,便于回去补理论。
场景 1:仿真里完美,一上真机就发散/炸机¶
- 症状:策略在仿真内成功率 >95%、跟踪误差极低;部署到真机后起飞即剧烈振荡或直接翻转炸机。
- 可能原因:(a) 仿真器物理保真度不足(简化推力模型、无电池/电机延迟/气动);(b) 跳过了 sim-to-sim 验证,策略过拟合了训练仿真器的 artifact;(c) SysID 参数测错(尤其质量、推力系数符号/量级);(d) 训练用 float32、部署用 float16/定点引入数值漂移。
- 排查步骤:
- 先做 sim-to-sim:换一个仿真器(如 gym-pybullet-drones)跑同一策略。若 sim-to-sim 就垮 → 是过拟合,回 §D9.8 Step 4 加选择性 DR。
- 核对 SysID:用电子秤复称质量、确认推力系数量级,在仿真器里**重放**一段真实飞行数据,对比轨迹(§D9.8 Step 2)。误差 >5% → 提保真度或加残差。
- 做 HIL:在真实 MCU/Jetson 上用真实数值精度跑推理,对比与 PC float32 输出的差异(陷阱 4-2)。
- 确认仿真器是否缺电池/延迟模型——满电正常、低电失控强烈指向缺电池模型(§9.1.6)。
- 相关章节:§D9.1.6(高保真仿真器)、§D9.8(部署链路)、陷阱 1-1、陷阱 8-1。
场景 2:真机能飞但"侧飞抄近路"导致定位丢失¶
- 症状:脱离 mocap 用 VIO 自主飞行时,无人机倾向于侧身贴近路径飞,飞着飞着状态估计漂移、门检测失败、最终撞门。
- 可能原因:(a) 奖励里缺感知朝向项 \(r_\text{perc}\),策略只优化几何最短路径;(b) \(r_\text{perc}\) 权重过小,被进度奖励压过;(c) VIO 特征环境本身贫瘠(白墙、弱纹理)。
- 排查步骤:
- 检查奖励是否含 \(r_\text{perc}=\lambda_2\exp(\lambda_3\delta_\text{cam}^4)\)(相机偏角惩罚)。缺则加上(§9.1.5)。
- 调高 \(r_\text{perc}\) 权重,重训,观察飞行姿态是否转为"正面朝向下一门"。
- 在 mocap 下飞同一策略——若 mocap 下正常、VIO 下才出问题,确认是感知-控制耦合问题而非控制本身。
- 检查飞行环境纹理,必要时增加视觉特征。
- 相关章节:§9.1.5(\(r_\text{perc}\) 设计)、陷阱 1-2。
场景 3:PPO 训练不收敛 / 不同种子结果方差极大¶
- 症状:同一配置换随机种子,约一半种子学不出有效策略;reward 曲线剧烈震荡不上升。
- 可能原因:(a) 并行环境数太少(小批量),策略梯度方差过大;(b) 学习率过高;(c) 观测未归一化或姿态用了四元数/欧拉角拖慢收敛;(d) 奖励尺度失衡(某项主导)。
- 排查步骤:
- 先把并行环境数拉到 4096+(GPU 并行),这是 PPO 稳定收敛的**必要条件**(§9.2.7、陷阱 2-3)。
- 确认观测做了 running mean/std 归一化;姿态换成 9D 旋转矩阵(§9.2.3、陷阱 2-1)。
- 检查各奖励项数量级是否可比,必要时归一化;学习率从 3e-4 起调。
- 给 Critic 加时间向量 \(f_t\) 降低价值估计方差(§9.2.4)。
- 相关章节:§9.2.3–§9.2.7(五因子)、陷阱 2-1/2-2/2-3。
场景 4:RLtools 训练慢于预期(远超 18 秒)¶
- 症状:按 learning-to-fly 跑训练,耗时几分钟到几十分钟,达不到"18 秒"。
- 可能原因:(a) 编译未开优化(Debug 模式而非
-O2/-O3 Release);(b) 误把算法换成了 PPO 或加了大网络;(c) 仿真器没走 C++ 内联路径(被 Python 包了一层);(d) 机器单核性能弱。 - 排查步骤:
- 确认
cmake .. -DCMAKE_BUILD_TYPE=Release且编译器开了 SIMD/优化(§9.4.2)。 - 确认算法是 TD3(off-policy,~30 万步),不是 PPO(~1 亿步)——这是数量级差异的主因(陷阱 4-1)。
- 确认整条训练链路纯 C++、无 Python 桥接(§9.4.2)。
- 18 秒是 2020 M1 单核基准,弱核机器适当放宽预期。
- 相关章节:§9.4.1–§9.4.2、陷阱 4-1。
场景 5:换一架无人机后策略失效(跨平台不泛化)¶
- 症状:在 A 机上训好的策略,换到质量/惯量不同的 B 机上悬停发散或跟踪极差。
- 可能原因:(a) 用的是专用策略(无在线适应),本就不跨平台;(b) 用了无记忆 MLP 试图跨平台,学成了"平均增益";(c) 想用基础策略但训练平台分布没覆盖 B 机的参数区间。
- 排查步骤:
- 明确需求:只跨少数几台 → 对每台做 SysID 重训/微调最省事;要真正零样本跨平台 → 需基础策略路线。
- 若已在用基础策略(RAPTOR 式),检查网络是否**带记忆**(GRU/LSTM)——无记忆 MLP 无法在线辨识,会退化为平均策略(陷阱 5-1)。
- 检查训练时的参数采样分布是否覆盖 B 机(推重比、惯量比、电机常数);B 机若在分布外(OOD)则不保证泛化。
- 给 GRU 隐状态足够的"辨识窗口"(前 ~100 步),确认适应已收敛再评估(§9.5.2)。
- 相关章节:§D9.5(RAPTOR 基础策略)、陷阱 5-1/5-2、§D9.3(迁移)。
场景 6:混合方法(RL+L1/MPC)性能反而不如单独 RL¶
- 症状:把 RL 前馈和 L1 自适应(或经典控制器)合在一起后,出现超调、振荡,整体不如单用 RL。
- 可能原因:(a) 两个控制器职责重叠,重复补偿同一扰动;(b) 训练 RL 时就注入了扰动,部署又叠 L1 补一遍;(c) 两者频段/时间尺度没分开。
- 排查步骤:
- 厘清职责:RL 应只负责长时程标称最优行为,L1/经典控制器负责毫秒级未建模扰动(§D9.6、陷阱 6-1)。
- 检查 RL 训练时的扰动设置——若训练时已让 RL 学补偿扰动,应改为在**标称/无扰动**仿真中训 RL,把扰动补偿交给 L1。
- 分别关闭其中一路,确认各自单独行为正常,再合并观察是否仍打架。
- 确认两路作用频段正交(RL 低频、L1 高频)。
- 相关章节:§D9.6(混合谱系)、陷阱 6-1。
场景 7:真机首飞高速撞击、数据丢失无法复盘¶
- 症状:第一次真机测试就全速飞,撞击严重、硬件损毁、rosbag 因炸毁不全。
- 可能原因:(a) 跳过逐步放速度,一上来就移除速度限制;(b) 没有独立安全 Guard;(c) 没有完整数据记录。
- 排查步骤:
- 严格执行 Phase A→D 逐步放速度:mocap 悬停 → mocap 低速 → 切 VIO → 逐步全速,每档达标再进下一档(§D9.8 Step 6、陷阱 8-2)。
- 上飞前确认独立安全 Guard 生效:地理围栏超界锁电机、姿态 >90° 锁电机、超速降功率(§9.3.2)。
- 全程录 rosbag(状态估计、电机指令、观测),即便炸机也能复盘。
- 配安全笼 + 急停物理按钮 + 测试区无人。
- 相关章节:§D9.8 Step 6、§9.3.2(安全协议)、陷阱 8-2。
后续章节关系¶
- 上承:本章是无人机规控板块"学习方法"的压轴,建立在 D1(微分平坦,CTBR 的理论根基)、D2(MPC,对照基线与混合对象)、以及足式 RL(PPO/Isaac Gym/非对称 AC/蒸馏的工具链)之上。
- 平行对照:与 D3(多项式轨迹)、D4(B 样条)、D5(MINCO)等**优化轨迹**方法形成方法论对照——前者显式生成并跟踪轨迹,本章 RL 把规划+控制压成端到端映射。两条路线在 §D9.6 的混合谱系里重新交汇。
- 下启:本章的 sim-to-real 部署链路(§D9.8)、安全协议(§9.3.2)可直接迁移到后续任何"学习型控制器上真机"的章节;RAPTOR 的基础策略思想与 VLA/具身大模型方向相通。