跳转至

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 能赢。

  1. PPO 与 TD3 的核心区别是什么? on-policy 与 off-policy 各自的样本效率量级是多少?为什么 GPU 并行仿真天然偏向 on-policy 的 PPO? (答不出 → 回足式 RL 章 / 强化学习基础,PPO/TD3 小节)

  2. 微分平坦(Differential Flatness)的定义是什么? 四旋翼的 4 个平坦输出是哪些?给定平坦输出及其导数,为什么可以**纯代数地**反解出总推力 \(T\) 和 3 轴体角速率 \(\omega\)? (答不出 → 回 D1 微分平坦,§D1.1)

  3. 旋转的三种表示——欧拉角、四元数、旋转矩阵——各自的奇异性问题是什么? 欧拉角的万向节锁发生在哪里?四元数的"双覆盖"指什么? (答不出 → 回 SO(3) / 姿态表示基础章)

  4. 域随机化(Domain Randomization)的基本思想是什么? 它通过什么机制让策略对未建模动态鲁棒?它的代价(过度正则化)从何而来? (答不出 → 回足式 RL 章 / sim-to-real 基础)

  5. 高斯过程(GP)与 k 近邻(kNN)回归的本质区别是什么? GP 为什么能同时输出"均值 + 方差"?kNN 为什么是非参数、无需训练的? (答不出 → 回机器学习基础 / 回归方法章)

参考答案要点(先自己答,再对照):

  1. 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 反而在这种吞吐下显得多余。

  2. 微分平坦:存在平坦输出 \(\sigma\),使全部状态 \(x\) 与控制 \(u\) 都能写成 \(\sigma\) 及其有限阶导数的代数函数。四旋翼平坦输出 \(\sigma = (x, y, z, \psi)\)。能纯代数反解是因为:位置二阶导给出合加速度 → 加上重力得推力矢量方向(决定姿态 \(R\) 的两列)与大小(决定 \(T\));\(\psi\) 决定第三列;姿态对时间求导得到 \(\omega\)。全程无需积分或求解 ODE。

  3. 欧拉角在 pitch = ±90° 处万向节锁(两个旋转轴重合,丢一个自由度);四元数 \(q\)\(-q\) 表示同一姿态(双覆盖),网络须额外学到这种等价;旋转矩阵 9 维无奇异、无歧义,代价是冗余(9 > 3)。

  4. 域随机化:训练时随机抖动仿真参数(质量、惯量、推力系数、延迟等),强迫策略学到对一整段参数分布都有效的鲁棒行为。代价是策略必须为"最坏参数"留裕度,把本可精确利用的信息也"随机掉了",导致过度正则化、标称性能下降。

  5. GP 是贝叶斯非参数模型,对函数加先验(核函数),后验同时给出均值(最佳估计)和方差(不确定性)——所以能采样噪声。kNN 直接对最近 \(k\) 个样本做(加权)平均,不假设函数形式、不需训练、不外推,天然适合小数据集与确定性映射。


本章目标

学完本章后,你应该能够:

  1. **严格论证**为什么基于学习的控制能在敏捷飞行上超越经典规控——从经典管线的三重瓶颈(气动不可建模、分层信息瓶颈、高频计算约束)出发,而非泛泛而谈"神经网络更强"
  2. 完整复述 Swift 的六阶段管线:BEM 仿真器 → PPO 训练(100 并行 agent、50 min)→ 感知(CNN 门检测 + VIO)→ 残差模型(GP 观测残差 + kNN 动力学残差,~50s 真实数据)→ 策略微调 → Jetson TX2 部署,并能解释每一阶段"为什么必须存在"
  3. **从动作空间第一性原理出发**解释为什么 CTBR 在鲁棒性与敏捷性之间取得最优平衡——对比 LV / CTBR / SRT 三者,并把 CTBR 与微分平坦联系起来
  4. 逐条拆解 SimpleFlight 的五因子配方:为什么旋转矩阵(9D)而非四元数/欧拉角?为什么动作差分平滑而非动作幅度惩罚?为什么 SysID + 选择性 DR 而非盲目 DR?
  5. 掌握"足式 RL → 无人机 RL"的迁移清单:什么能周末复用(PPO / Isaac Gym / 非对称 AC / 教师-学生蒸馏),什么必须重建(动作空间 / 奖励设计 / 安全协议)
  6. 解释 RLtools 的 C++17 头文件 RL 范式:为什么纯模板元编程能在 18 秒内训练完成?策略权重如何烘焙成 constexpr 数组部署到 STM32?
  7. 理解 RAPTOR 基础策略的跨平台泛化机制:GRU 隐状态如何充当隐式系统辨识器?元模仿学习(1000 teacher → 1 student)如何工作?线性探针为什么能从隐状态线性提取推重比?
  8. 独立完成一次 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:规划-控制分离的信息瓶颈

经典管线将问题分解为:

全局规划 → 轨迹优化 → 跟踪控制
   (A*)       (MPC)      (PID/SE(3))

每一级之间有一个**显式中间表示**(轨迹),这个中间表示本身就限制了可表达的行为空间。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 直接优化任务目标(通过奖励函数),不需要人为设计中间表示。在竞速场景中:

经典:minimize ‖x(t) - x_ref(t)‖  (跟踪预规划轨迹)
RL:  maximize Σ r_t              (直接优化通过门的速度)

RL 策略可以学到人类设计者想不到的策略——例如,Swift 学会了在门之间走一条与人类飞手完全不同的路径,因为这条路径虽然看起来"绕远",但允许更高的弯道速度。

优势 2:隐式学习未建模动态

RL 策略在训练过程中与仿真器交互了数十亿步,隐式地学到了:

- 电机的非线性推力响应(不是理想的 T = k_f·Ω²)
- 电池电压下降导致的推力衰减
- ESC 的 PWM-to-RPM 延迟
- 螺旋桨的陀螺效应
- 高速飞行时的阻力增加

这些效应不需要被显式建模——它们通过 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

关键发现:

  1. MPC 受限于轨迹参数化——无法表示"先减速再猛加速"的非直觉策略
  2. MPC 在弯道处必须严格跟踪预规划路径,而 RL 可以自由选择弯道策略
  3. RL 策略学会了利用电池电压在满电时更激进、低电时更保守的**电池管理策略**
  4. 但 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 选择保留。该端到端的地方端到端,该模块化的地方模块化。

本节练习

  1. (推导/论证) §0.1 列出了经典规控的三重瓶颈。请论证:在**低速悬停**任务(速度 < 1 m/s、无激烈机动)中,这三重瓶颈分别"消失"或"可忽略"到什么程度?由此解释为什么悬停任务里经典 PID 与 RL 几乎打平、RL 的优势主要体现在敏捷/极限工况。
  2. (反事实分析) 假设未来出现一种"无损轨迹参数化"——它能精确表示任意可行的四旋翼极限轨迹,不丢失任何行为。在这个假设下,§0.0 反面段落里"分层管线有天花板"的论证还成立吗?请据此说明:RL 相对 MPC 的优势,到底来自"端到端"本身,还是来自"现有轨迹参数化的有损性"。
  3. (开放设计) 给定一个"室内巡检"场景(已知地图、速度温和、但要求 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_prog = (d_{t-1} - d_t) / dt

d_t = 到下一门的距离
dt  = 控制步长

物理含义: 沿"向门方向"的速度分量

这个奖励不直接惩罚时间,但通过奖励"接近门的速度"间接实现时间最优——因为要最大化累积 \(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 (控制平滑) —— 防止高频震荡

r_cmd = -λ₄ · ‖u‖²

u = [c, ωx, ωy, ωz]  (当前动作向量)

注意 Swift 使用的是动作**幅度**惩罚(L2 范数),而不是 SimpleFlight 推荐的动作**差分**惩罚。后者被证明更优(见 D9.2 节)。

r_crash (碰撞惩罚) —— 安全硬约束

r_crash = λ₅ · 1_{collision}   (碰撞指示函数)

λ₅ 是一个大常数(通常 > 100)
碰撞后 episode 立即终止

碰撞惩罚的设计关键是**终止条件**。在无人机 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\))。让策略在训练分布中就把"碰撞"学成一条绝对不能越过的边界,而非可以权衡的小代价。

本节练习

  1. (管线复述 + 必要性论证) 不看书,凭记忆画出 Swift 的六阶段管线,并对每一阶段回答:"如果删掉这一阶段,会在哪个环节、以什么方式失败?" 重点说清 Stage 5(残差模型)被删掉后,Stage 6 部署会发生什么。
  2. (动作空间分析) §9.1.3 论证了 CTBR 兼顾鲁棒与敏捷。请把 CTBR 与 D1 的微分平坦显式联系起来:给定策略输出的 \([c,\omega_x,\omega_y,\omega_z]\),写出它如何对应平坦输出空间里的一个"加速度 + 姿态变化率"指令,并解释为什么这种对应使 CTBR 对电机模型误差不敏感。
  3. (奖励设计/反事实) Swift 用动作幅度惩罚 \(-\lambda_4\|u\|^2\),SimpleFlight 改用动作差分惩罚 \(-\lambda\|u_t-u_{t-1}\|_2\)。请预测:如果把 Swift 的奖励换成动作差分惩罚,它在"需要瞬间大推力穿门"的场景下行为会如何变化?给出你的推理(§D9.2.5 会验证)。
  4. (残差建模选型) 为什么 Swift 对 VIO 观测残差用 GP(概率模型),对动力学残差用 kNN(确定性模型)?请从"残差是否随机""数据量""是否需要不确定性输出"三个角度论证,并说明如果把两者对调(VIO 用 kNN、动力学用 GP)会有什么问题。

§D9.2 SimpleFlight 五因子配方——可复现的基线 ⭐⭐⭐

引用:Chen, Yu 等,arXiv 2412.11764,RA-L 2025。GitHubthu-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 在敏捷飞行任务上稳定收敛的必要条件,而非可选优化。

本节练习

  1. (消融设计) 从五因子中任选两个,设计一组"加上 vs 去掉"的控制变量实验:明确说明你会固定哪些变量、改变哪一个、用什么指标(RMSE?动作频谱?收敛种子比例?)衡量差异。这正是 §A 型练习里复现 SimpleFlight 表 2 的思路。
  2. (推导) 因子 2 说"无时间向量 \(f_t\) 会使价值估计方差增大 ~30%"。请从 GAE(广义优势估计)的定义出发,论证为什么"Critic 不知道 episode 进行到哪一步"会让优势函数 \(\hat{A}_t\) 的方差增大,进而拉低策略梯度的信噪比。
  3. (反事实 + 工程判断) 假设你的真机**无法**做精确 SysID(没有推力台、没有扭摆仪),只能粗测质量。在这个约束下,五因子配方该如何调整?具体说:因子 4(SysID + 选择性 DR)退化成什么?你会把哪些原本"不随机化"的参数重新纳入随机化范围?为什么?
  4. (迁移思考) 因子 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→全速)。这套协议在足式里没有对应物,是无人机迁移必须**全新开发**的部分。

本节练习

  1. (迁移清单实操) 拿你手上(或任选一个开源)的足式 RL 训练框架,逐文件填一张三列表:文件名 | 复用/改维度/重写 | 预估工作量(行)。重点标注 PPO 实现、actor-critic、奖励、配置、部署脚本、安全协议各落在哪一类。这正是 §A 型练习"足式→无人机迁移"的产出。
  2. (分类方法论) §3.x 的本质洞察提出"先问每个模块属于算法层还是任务层"。请用这条标准判断以下模块各属哪层、是否可复用:(a) GAE 优势计算;(b) 观测归一化的 running mean/std;(c) 终止条件判定;(d) 域随机化的参数采样器;(e) tensorboard 日志。给出判断依据。
  3. (反事实推理) 如果你要把 RL 从无人机迁移到**水下机器人(AUV)**,沿用本节的"算法层 vs 任务层"框架,预测哪些能复用、哪些必重建。特别分析:AUV 的失败代价量级如何影响安全协议的工作量?

§D9.4 RLtools——秒级训练与 MCU 部署 ⭐⭐⭐

GitHubrl-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 实测。

本节练习

  1. (数量级估算) §9.4.1 把 18 秒分解为"样本效率 × 仿真速度 × 零开销"。请你反过来估算:如果把 RLtools 的仿真器速度砍到 Python+GPU 水平(~1e6 步/秒),但保留 TD3 和 30 万步样本量,训练时间会变成多少?再砍掉 TD3 换 PPO(1 亿步),又是多少?通过这两步定量感受三个因子各自的贡献。
  2. (部署实操) 按 §A 型练习跑通 arplaboratory/learning-to-fly,验证 18 秒训练,导出 actor_xxx.h。打开头文件检查 constexpr 权重数组:(a) 权重值是否都在合理范围(多数 <1.0、无 NaN/Inf)?(b) 数组维度是否与 3×64 MLP 吻合?(c) 估算这些数组占多少 Flash。
  3. (设计权衡) RLtools 选择"直接 RPM"动作空间(比 CTBR 更底层)。结合 §9.4.4 和前面 §9.1.3 的 CTBR 分析,论证:为什么 MCU 部署场景下"直接 RPM"是合理的(省掉 rate PID),但它对 SysID 精度的要求为什么比 CTBR 更高?这与陷阱 4-2 的数值一致性问题有何关联?

§D9.5 RAPTOR——2084 参数的基础策略 ⭐⭐⭐⭐

GitHubrl-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 是不同问题类,不可类比大小。

本节练习

  1. (机制论证) §9.5.2 的线性探针实验显示 GRU 隐状态对推重比的 \(R^2=0.95\)。请论证:为什么"隐状态能被**线性**提取出推重比"是一个强结论?如果只能用一个高度非线性的解码器才能从隐状态恢复推重比,说明 GRU 学到的表示有什么不同?这对"隐状态 ≈ 系统辨识器"的解读意味着什么?
  2. (设计判据应用) 用本节的"基础策略大小 ∝ 任务变化内在维度"判据,估算以下三个"基础策略"各自大致需要的参数量级,并排序:(a) 跨不同质量/惯量的四旋翼悬停;(b) 跨不同地形/摩擦的四足行走;(c) 跨数百种厨房物体的机械臂抓取。给出每个任务"变化维度"的估计依据。
  3. (与 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 时就约定好"扰动补偿不归你管",让两者频段正交、互补而非重叠。

本节练习

  1. (谱系定位) 把本节五种混合方法(SOUS VIDE、DATT、Neural-MPC、AC-MPC、Neural-Fly)按"结构先验 ↔ 数据驱动"的比例排在一条轴上,并为每一个标注:哪部分交给了结构、哪部分交给了数据。说明你的排序依据。
  2. (选型决策) 给定三个项目:(a) 强阵风下的户外巡检(缺鲁棒、需稳定性保证);(b) 室内竞速刷圈速(缺极限性能);(c) 货运无人机需通过适航认证(缺可验证性)。为每个项目从本节五种方法里选一个最合适的,并论证为什么——把它对应到"沿轴往哪个方向挪"。
  3. (反事实/职责划分) 针对陷阱 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 的逐步放速度链路在真机上谨慎逼近。

本节练习

  1. (选型决策) 给定四个任务:(a) 教学演示悬停;(b) 用 PPO 大批量训轨迹跟踪;(c) RLtools 秒级训练 MCU 部署;(d) 训练依赖 RGB 图像的视觉穿越策略。从本节对比表里为每个任务选一个最合适的仿真平台,并各用一句话说明"它满足了什么核心需求、牺牲了什么"。
  2. (两维度辨析) 用本节"仿真速度 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(超界/翻转/超速锁电机),让每一次潜在失败都发生在尽可能低的能量下。

本节练习

  1. (链路裁剪) 本节洞察指出"链路复杂度由硬件成本/风险逼出"。请为两个场景各裁剪一版部署链路:(a) 用 $30 的 Tinywhoop 在自家客厅练手;(b) 用 $5000 竞速机在有观众的赛场首飞。明确说明每个场景你会保留/简化/强化哪些步骤,为什么。
  2. (关卡设计) 为六步链路的每一步补一条明确的"通过标准"和"不通过时回退到哪一步"。例如 Step 4 sim-to-sim 不通过应回退到 Step 3(域随机化不足/观测设计问题)还是 Step 2(仿真器保真不够)?给出你的判断逻辑。
  3. (综合·跨节) 结合 §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-fly README 在笔记本上训练——验证 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/具身大模型方向相通。