跳转至

第94章:ASAP——人形 Sim-to-Real 两阶段方法论

元信息
难度 ⭐⭐⭐⭐(Sim-to-Real 理论 + 残差模型 + 部署闭环)
预计时间 1.5 周(40-50 小时)
核心平台 Unitree G1、HumanoidVerse、IsaacGym/IsaacSim/Genesis/MuJoCo
主线 动作数据 → 跟踪策略 → 真机 rollout → delta-action → 微调 → 部署

本章定位 本章面向已经学过腿足简化模型、WBC、复合机器人动力学的读者。 写作重点不是罗列论文名,而是把数学假设、控制接口和真实系统故障连成一条可推理的链。 读完后应能解释每个控制量从哪里来、为什么这样定义、用错以后会怎样。

核心参考文献 - He et al., "ASAP: Aligning Simulation and Real-World Physics for Learning Agile Humanoid Whole-Body Skills," RSS 2025, arXiv:2502.01143 - Luo et al., "PHC: Perpetual Humanoid Control for Real-time Simulated Avatars," ICCV 2023 - Hwangbo et al., "Learning Agile and Dynamic Motor Skills for Legged Robots," Science Robotics, 2019 - Kumar et al., "RMA: Rapid Motor Adaptation for Legged Robots," RSS 2021, arXiv:2107.04034 - Cheng et al., "ExBody/ExBody2: Expressive Whole-Body Control for Humanoid Robots," 2024-2025 - He et al., "HOVER: Versatile Neural Whole-Body Controller for Humanoid Robots," 2024


94.0 前置自测

# 问题 前置知识 合格答案关键词
1 Domain Randomization 的贝叶斯解释是什么? 足式/190 对参数分布的期望回报优化
2 Teacher-Student 解决什么信息差? 足式/190、93章 训练全信息、部署稀疏信息
3 动作模仿策略为什么需要 phase? 93章 区分同姿态下不同时间目标
4 Sim2sim 与 sim2real 的区别是什么? 足式部署 可控目标域 vs 真实硬件
5 为什么接触触发会让残差学习变难? 本章 离散模式切换导致转移不光滑

本章目标

  1. 系统理解人形 sim-to-real gap 的来源:从动力学、感知、执行、延迟四个维度进行穷举式分类。
  2. 掌握 ASAP 两阶段流程:phase-conditioned tracking policy 与 delta-action residual model。
  3. 推导 delta-action 与 delta-dynamics 的差异,理解为什么修改 action 更适合 PPO 微调。
  4. 理解系统辨识与执行器网络如何缩小仿真与真实的底层差距。
  5. 掌握从训练完成到真机运行的完整部署 pipeline,能独立设计 sim2sim delta-action 实验。
  6. 理解 ASAP 与 DR、SysID、RMA、PHC/ExBody/HOVER 的关系和定量对比。

94.1 Sim-to-Real Gap 的系统分类 ⭐⭐

动机:为什么仿真训练的策略不能直接用

仿真训练得到的策略实际优化的是 \(f_{sim}\) 下的回报。真机部署面对的是 \(f_{real}\)。sim-to-real 的核心就是让策略在 \(f_{real}\) 下仍然保持稳定和任务性能。

\[ s_{t+1}^{sim}=f_{sim}(s_t,a_t),\qquad s_{t+1}^{real}=f_{real}(s_t,a_t). \]

初学者往往以为 sim-to-real gap 只是"参数不准"。实际上这个差距是多维度、多层次的,需要系统分类才能设计有针对性的解决方案。

四维度穷举分类

回顾教学规范 Rule 4.5F 的要求:对开放性问题必须做系统性分类,而非列举几个例子。sim-to-real gap 按照信号链条中的位置,可以穷举为以下四个维度:

维度一:动力学差距(Dynamics Gap)

动力学差距是 sim-to-real 中研究最多也最直觉的一类。仿真器使用简化的物理模型,与真实世界的物理过程存在结构性差异。

子类别 具体表现 影响程度 典型处理方法
质量/惯量不匹配 CAD 模型与实物差异、线缆/紧固件未建模 质量/惯量随机化
关节摩擦与阻尼 库仑摩擦、粘性摩擦、Stribeck 效应 摩擦参数辨识 + 随机化
接触模型 穿透、弹跳、摩擦方向、接触刚度 非常高 跨仿真器验证、接触参数随机化
地面特性 柔软度、不平整、摩擦系数空间分布 地形课程 + 摩擦随机化
连杆柔性 实际机器人的结构柔性未建模 中低 通常忽略或用等效刚度

反事实推理:如果我们只随机化质量和惯量,不处理接触模型差异会怎样?仿真中学到的步态可能在接触瞬间产生与真实完全不同的冲击力分布。Unitree G1 的脚踝连杆机构让接触刚度与仿真差距尤其大——ASAP 论文报告仅对踝关节 4 个自由度训练 delta-action 就能减少 52.7% 的跟踪误差,说明接触区域的动力学差距是主导因素。

维度二:感知差距(Perception Gap)

子类别 具体表现 影响程度
IMU 噪声与偏置 零偏漂移、温度相关偏置
编码器量化 角度分辨率有限
传感器时间戳不对齐 不同传感器的采样时刻不同步
视觉域差异 纹理、光照、相机内参 高(视觉策略)
数据丢包 通信丢失导致观测缺失

维度三:执行差距(Actuation Gap)

执行差距是人形机器人 sim-to-real 中最被低估的一类差距。仿真中电机可以瞬时输出任意力矩,但真实电机有带宽限制、延迟、死区、温度效应和饱和特性。

子类别 具体表现 影响程度
电机带宽 高频力矩指令被低通滤波 非常高
PD 控制器延迟 底层伺服有 1-3 个周期的延迟
力矩死区 小力矩指令无法克服静摩擦
温度退磁 持续运行导致电机出力下降 中(长时间任务)
齿轮间隙 减速器间隙导致正反转不对称 中低
连杆机构非线性 如 G1 踝关节的四连杆传动 非常高

本质洞察:执行差距的根本原因不是"参数不准",而是"模型结构不对"。仿真中的理想 PD 控制器是一个线性映射 \(\tau = K_p(q^d - q) + K_d(\dot{q}^d - \dot{q})\),但真实电机的力矩输出是一个非线性动态系统——它取决于当前转速、温度、负载历史和通信延迟。Hwangbo et al. (2019) 提出用神经网络拟合这个非线性映射,这就是"执行器网络"的思想基础。

维度四:延迟差距(Latency Gap)

子类别 具体表现 典型量级
观测延迟 传感器读数到策略输入的时间差 1-5 ms
计算延迟 策略推理时间 0.5-2 ms(GPU)/ 5-20 ms(CPU)
通信延迟 控制指令到电机驱动器的传输时间 1-5 ms
执行延迟 驱动器接收指令到力矩实际输出 5-20 ms
端到端延迟 从观测到执行的总延迟 10-50 ms
\[ a_{applied}(t) = \pi(o(t - \tau_{obs} - \tau_{comp} - \tau_{comm})) \]

其中 \(\tau_{obs}\) 是观测延迟,\(\tau_{comp}\) 是计算延迟,\(\tau_{comm}\) 是通信延迟。

跨领域类比:延迟差距可以类比为网络游戏中的 ping 值。仿真中的延迟等于零(ping=0),策略在这个"零延迟游戏"中学会了极其激进的反应动作。当它被部署到"高延迟"的真机上,之前学到的快速反应动作反而会因为信息过时而产生过冲和振荡——就像高 ping 玩家在射击游戏中总是打到敌人之前的位置一样。

四维度的相互作用

这四类差距不是独立的。执行延迟会放大动力学差距(延迟导致的过冲让接触力更大),感知噪声会让延迟补偿更困难(不知道当前真实状态就无法正确预测未来状态)。这种耦合意味着单独处理任何一个维度都不够,需要系统性的解决方案。

传统方法 主要针对的维度 优点 局限
Domain Randomization 动力学 + 部分感知 简单、覆盖广 分布难设,可能过于保守
System Identification 动力学 精确、可解释 耗时且难覆盖动态变化
执行器网络 执行 针对性强 不能解决接触结构差异
延迟随机化 延迟 策略学会鲁棒控制 不能消除延迟本身
ASAP delta-action 所有维度的残余差距 直接对齐轨迹 需要真机数据

94.2 Sim-to-Real 方法的历史演进 ⭐⭐

从 DR 1.0 到 ASAP:方法论的四代演进

理解 ASAP 的定位需要先看清整个 sim-to-real 领域的演进脉络。Guangya Shi 在 2025 年提出了 "Sim2Real 1.0 到 4.0" 的分类框架,我们在此基础上梳理:

第一代:Domain Randomization(2017-2019)

核心思想:训练时在仿真器中随机化物理参数(质量、摩擦、惯量等),让策略对参数不确定性鲁棒。

\[ \max_\theta \mathbb{E}_{\xi \sim p(\xi)} \left[ \sum_t \gamma^t r(s_t, a_t) \mid \pi_\theta, f_{sim}(\cdot; \xi) \right] \]

其中 \(\xi\) 是随机化的物理参数向量。

代表工作:OpenAI Dexterous Manipulation (2018), ETH ANYmal DR (2019)

局限:随机化范围需要人工设定。范围太窄不够鲁棒,范围太宽导致过于保守——策略学会了"最安全但最慢"的行为。此外,DR 假设真实参数在随机化分布内,这对结构性差异(如接触模型不同)不成立。

第二代:System Identification + Adaptive(2019-2022)

核心思想:先辨识真实参数,再在精确仿真中训练;或者训练在线适应模块,实时推断环境参数。

代表工作:Hwangbo et al. 执行器网络 (2019), RMA (2021), A-RMA (2023)

局限:SysID 需要大量真机实验,且难以覆盖动态变化。RMA 需要假设环境变化可参数化,对结构性差异(接触模式切换)支持有限。

第三代:Real-to-Sim + Residual(2023-2024)

核心思想:从真实数据中提取信息来改进仿真,形成 "real → sim → real" 闭环。

代表工作:TRANSIC (2024), Real-to-Sim frameworks

局限:需要较多真机交互数据,在线学习有安全风险。

第四代:ASAP 风格的离线对齐(2025-)

核心思想:少量真机 rollout → 学习仿真修正模型 → 在修正后的仿真中大量训练 → 部署。

代表工作:ASAP (2025), VideoMimic (2025)

优势:真机上只需被动收集数据(无需在线探索),安全性高;修正后的仿真可以大量平行训练,样本效率高。

代际 真机数据需求 安全性 处理结构性差异 代表工作
DR 1.0 最高 不能 OpenAI Dexterous
SysID/RMA 少量标定 有限 ANYmal, RMA
Real-to-Sim 中等 可以 TRANSIC
ASAP 少量 rollout 可以 ASAP

本质洞察:四代方法的演进方向是"从改变策略来适应仿真-真实差距"转向"改变仿真来匹配真实"。DR 让策略对差距鲁棒(改变策略),SysID 让仿真更精确(改变仿真参数),ASAP 让仿真动态更匹配真实(改变仿真行为)。最终目标是让仿真和真实在策略可见的范围内不可区分。

Domain Randomization 的设计细节

虽然 ASAP 超越了纯 DR,但 DR 仍然是阶段一训练的基础组件。一个好的 DR 配置需要系统设计:

随机化参数 分布类型 推荐范围(G1 示例) 随机化方式
连杆质量 均匀 0.8-1.2 倍标称值 每次 reset
惯量张量 均匀 0.5-1.5 倍标称值 每次 reset
地面摩擦系数 均匀 0.4-1.2 每次 reset
地面弹性 均匀 0.0-0.5 每次 reset
关节摩擦 均匀 0.0-0.5 Nm 每次 reset
PD 增益 均匀 0.8-1.2 倍标称值 每次 reset
观测噪声 高斯 \(\sigma\) 见传感器规格书 每步
观测延迟 均匀整数 0-3 步 每步
外力扰动 均匀 0-30 N,随机方向 随机时刻
地面倾斜 均匀 \(\pm 5°\) 每次 reset

陷阱警告 ⚠️

思维陷阱:认为 DR 的参数范围"越大越好"

新手想法:"把所有参数的随机化范围都调到最大,不就更鲁棒了?"

实际上:过大的随机化范围会让训练分布中包含大量不合理的物理参数组合。策略在这些极端环境中学到的行为是保守的——它会选择在所有随机化实例中都安全的动作,这往往意味着缓慢、低幅度的动作。ASAP 论文指出,过保守的策略在真机上的表现反而可能不如适度随机化的策略

正确做法:从小范围开始,逐步扩大,每次扩大后检查策略在标称参数下的性能是否显著下降。如果下降超过 15%,说明范围太大了


94.3 系统辨识与执行器网络 ⭐⭐⭐

系统辨识的基本思想

系统辨识(System Identification, SysID)的目标是从输入输出数据中估计物理系统的参数。对于腿足机器人,主要辨识目标包括连杆质量、惯量张量、关节摩擦系数和电机模型参数。

\[ \theta^* = \arg\min_\theta \sum_{t} \left\| s_{t+1}^{real} - f_{sim}(s_t^{real}, a_t^{real}; \theta) \right\|^2 \]

其中 \(\theta\) 是仿真器的物理参数向量。

这个优化问题的困难在于:(1) 参数空间维度高,(2) 仿真器的微分不一定可得,(3) 接触导致的不连续性让梯度方法不稳定。实践中通常用贝叶斯优化、CMA-ES 或手动调参。

执行器网络:用数据学习电机的非理想响应

Hwangbo et al. (2019) 在 ANYmal 四足机器人上首次证明了执行器网络(Actuator Network)的有效性。核心思想是用一个神经网络替代仿真中的理想 PD 控制器,使其输出更接近真实电机的力矩响应。

为什么不直接用物理模型?

真实电机(尤其是串联弹性执行器 SEA 或准直驱电机)的力矩输出取决于复杂的非线性因素:

  1. 电机绕组的电感动态
  2. 减速器的回程间隙和摩擦
  3. 编码器量化和控制器离散化
  4. 热效应导致的参数漂移

要精确建模这些因素需要大量领域知识和实验标定。执行器网络的洞察是:不需要理解每个物理细节,只需要从数据中学到输入(位置指令历史)到输出(实际力矩)的映射。

网络架构与推导

设关节 \(j\) 在时刻 \(t\) 的目标位置为 \(q_j^d(t)\),实际位置为 \(q_j(t)\),实际速度为 \(\dot{q}_j(t)\)。理想 PD 控制器的力矩为:

\[ \tau_j^{ideal}(t) = K_p (q_j^d(t) - q_j(t)) + K_d (0 - \dot{q}_j(t)) \]

执行器网络用一个 MLP 替代这个映射。输入不仅包含当前时刻的状态,还包含过去若干时步的历史,以捕获电机的动态特性:

\[ \tau_j^{net}(t) = \text{MLP}_\psi\left(\underbrace{q_j^d(t-H:t), q_j(t-H:t), \dot{q}_j(t-H:t)}_{\text{过去 H 步的指令、位置和速度历史}}\right) \]

其中 \(H\) 通常取 3-6 步。网络结构为 2-3 层全连接层,隐藏层维度 32-64,激活函数为 softsign 或 ELU。

训练数据采集

在真实机器人上执行预定义的激励轨迹(正弦扫频、阶跃响应、随机行走),同时记录:

记录量 来源 采样率
位置指令 \(q^d(t)\) 上层控制器输出 500-1000 Hz
实际关节角 \(q(t)\) 编码器 500-1000 Hz
实际关节角速度 \(\dot{q}(t)\) 编码器差分或速度传感器 500-1000 Hz
实际力矩 \(\tau(t)\) 力矩传感器或电流估计 500-1000 Hz

训练损失:

\[ \mathcal{L}_{act} = \sum_{t} \left\| \tau_j^{net}(t) - \tau_j^{real}(t) \right\|^2 \]

陷阱警告 ⚠️

错误做法:用仿真中的理想轨迹作为激励信号

现象:训练出的执行器网络在仿真中表现好,但没有覆盖真机可能遇到的力矩范围

根本原因:仿真轨迹的状态分布和真机部署时的状态分布不同,如果激励信号不够多样,网络在分布外的预测不可靠

正确做法:使用多种类型的激励信号——正弦扫频覆盖不同频率,阶跃响应覆盖快速变化,随机信号覆盖非结构化运动,行走数据覆盖实际工况

在 ASAP 流程中的位置

执行器网络在 ASAP 之前使用。它缩小了执行差距的"平均偏差",但不能消除所有差距(接触、延迟、温度变化仍然存在)。ASAP 的 delta-action 在此基础上进一步对齐残余差距。

import torch
import torch.nn as nn


class ActuatorNetwork(nn.Module):
    """中文注释:单关节执行器网络,输入历史位置/速度/指令,输出力矩预测。"""
    def __init__(self, history_len: int = 6, hidden_dim: int = 32):
        super().__init__()
        # 中文注释:输入维度 = 3 * history_len (指令、位置、速度各 H 步)
        input_dim = 3 * history_len
        self.net = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ELU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ELU(),
            nn.Linear(hidden_dim, 1),  # 中文注释:输出单关节力矩
        )

    def forward(self, q_des_hist, q_hist, dq_hist):
        """中文注释:拼接历史输入并预测力矩。"""
        x = torch.cat([q_des_hist, q_hist, dq_hist], dim=-1)
        return self.net(x)

94.4 Stage 1 详解:仿真中的运动策略训练 ⭐⭐⭐

阶段一目标

阶段一要解决"在仿真中把目标动作跟好"。数据可以来自 AMASS 动捕数据库,也可以来自视频重建得到的 SMPL 序列。经过重定向(retargeting)后,机器人得到每帧参考关节角、根状态、关键点或末端位姿。

回顾93章关于动作模仿的讨论:重定向不是简单的关节映射,而需要解决人体骨架与机器人骨架的运动学差异(自由度不同、连杆比例不同、关节限位不同)。

Phase-conditioned observation 的完整设计

\[ o_t=[o_{prop},\;q^{ref}_{t:t+H},\;x^{ref}_{t:t+H},\;\phi_t]. \]

\(\phi_t\) 是动作相位。同一个身体姿态可能出现在抬脚前和落脚后,没有相位策略会混淆接下来该做什么。

为什么需要相位?一个具体的例子。

考虑行走过程中双脚同时着地的瞬间。这个姿态在步态周期中出现两次:一次是右脚刚落地、准备抬左脚;一次是左脚刚落地、准备抬右脚。如果策略只看到当前关节角和参考关节角,它无法区分这两种情况——动作的"方向"完全相反。相位变量 \(\phi_t\) 告诉策略"你现在在周期的哪个位置",消除了这种歧义。

跨领域类比:相位变量类似于乐谱中的小节号。同一个和弦可能在不同小节出现,但音乐家需要知道当前在哪个小节才能决定下一个音符。没有小节号的乐谱就像没有相位的策略——看到同一个和弦不知道接下来该演奏什么。

观测块 内容 维度(G1 示例) 作用
本体感知 \(o_{prop}\) 关节角 \(q\)、关节角速度 \(\dot{q}\)、IMU 姿态/角速度、上一时步动作 \(a_{t-1}\) ~70-80 闭环稳定、状态估计
短期参考 未来 \(H\) 帧参考关节角 \(q^{ref}_{t:t+H}\) \(H \times n_{joints}\) 提前准备动作
关键点参考 未来 \(H\) 帧关键点位置 \(x^{ref}_{t:t+H}\) \(H \times n_{kp} \times 3\) 全局位置约束
相位 \(\phi_t\) 动作进度(线性或周期编码 \([\sin\phi, \cos\phi]\) 1-2 区分动作阶段
速度命令 期望根速度、朝向变化率 3-4 任务条件化

奖励函数的完整设计

\[ r=w_q \exp\!\left(-\frac{\|q-q^{ref}\|^2}{\sigma_q^2}\right) + w_v \exp\!\left(-\frac{\|\dot{q}-\dot{q}^{ref}\|^2}{\sigma_v^2}\right) + w_k \exp\!\left(-\frac{\|K(q)-K^{ref}\|^2}{\sigma_k^2}\right) + r_{stable} + r_{smooth} \]

为什么每一项都不可少?

奖励项 如果去掉会怎样 具体后果
关节跟踪 \(r_q\) 关键点到了但姿态畸形 膝盖反弯、肘部过伸
速度跟踪 \(r_v\) 位置对但运动节奏错 动作僵硬或抖动
关键点跟踪 \(r_k\) 关节角对但全局位置偏 整体平移漂移
稳定正则 \(r_{stable}\) 跟踪准但频繁摔倒 为了跟踪牺牲平衡
平滑正则 \(r_{smooth}\) 关节力矩和速度有高频跳变 真机电机无法跟随

各项的工程推荐权重和温度参数

参数 ASAP 推荐范围 物理含义
\(w_q\) 0.3-0.5 关节角跟踪的重要性
\(\sigma_q\) 0.2-0.5 rad 关节角容差(越小越严格)
\(w_v\) 0.1-0.2 速度跟踪权重
\(\sigma_v\) 1.0-2.0 rad/s 速度容差
\(w_k\) 0.2-0.4 关键点权重
\(\sigma_k\) 0.05-0.1 m 关键点位置容差

PPO 训练配置

ASAP 使用标准的 PPO(Proximal Policy Optimization)算法,在 IsaacGym 中利用 GPU 并行仿真数千个环境实例。

训练参数 推荐值 说明
并行环境数 4096-8192 GPU 内存允许即尽量多
训练步数 50M-200M 复杂动作需要更多步
Clip ratio \(\epsilon\) 0.2 PPO 标准值
GAE \(\lambda\) 0.95 偏差-方差权衡
学习率 \(3\times10^{-4}\) Adam 优化器
策略网络 MLP [512, 256, 128] 三层全连接
值函数网络 MLP [512, 256, 128] 与策略分离

陷阱警告 ⚠️

概念误区:认为阶段一的策略必须在仿真中完美跟踪才能进入阶段二

新手想法:"先把仿真跟踪训练到完美,再做 sim-to-real"

实际上:过于追求仿真中的完美跟踪会导致策略过拟合仿真器的特性。如果策略利用了 PhysX 接触模型的某些 artifact(比如微小穿透带来的额外稳定性),这些 artifact 在真机上不存在,反而会让迁移更困难

正确思维:阶段一的目标是"足够好的跟踪 + 合理的鲁棒性"。同时使用 Domain Randomization 训练,让策略不要过度依赖仿真器的具体参数

跟踪奖励的温度参数 \(\sigma\) 的物理意义

奖励中的 \(\exp(-\|e\|^2/\sigma^2)\) 形式中,温度参数 \(\sigma\) 控制的是"多大的误差算可以接受"。

  • \(\|e\| \ll \sigma\) 时,\(\exp(-\|e\|^2/\sigma^2) \approx 1\)——误差在容差范围内,满分
  • \(\|e\| = \sigma\) 时,\(\exp(-1) \approx 0.37\)——误差刚好到容差边缘,37% 的分数
  • \(\|e\| = 2\sigma\) 时,\(\exp(-4) \approx 0.018\)——误差已经很大,几乎零分

\(\sigma\) 太小会让策略过于关注精确跟踪(可能牺牲稳定性),\(\sigma\) 太大会让策略忽视跟踪质量(动作粗糙但不摔倒)。

推荐的 \(\sigma\) 设置方法:从大 \(\sigma\) 开始训练(策略先学会不摔倒),然后逐步减小 \(\sigma\)(策略在稳定的前提下提高跟踪精度)。这本身就是一种课程学习。

def tracking_reward_with_curriculum(error: float, sigma_init: float,
                                    sigma_final: float, progress: float) -> float:
    """中文注释:带课程的跟踪奖励,sigma 从大到小。"""
    import math
    sigma = sigma_init + (sigma_final - sigma_init) * progress
    return math.exp(-(error ** 2) / (sigma ** 2))

策略网络架构的选择

ASAP 使用标准的 MLP 策略网络。但对于需要处理长历史的场景,也可以考虑其他架构:

架构 优点 缺点 适用场景
MLP + 历史堆栈 简单、推理快 历史长度固定 ASAP 默认选择
LSTM/GRU 可变长历史、隐式记忆 训练更慢、调参困难 需要长期记忆
Transformer 注意力机制、灵活 计算量大、部署延迟高 多模态输入
TCN(时域卷积) 并行训练、因果结构 感受野固定 中等长度历史

ASAP 选择 MLP + 历史堆栈的原因是:(1) 踝关节的 sim-to-real gap 主要是短期动态特性,不需要长期记忆;(2) MLP 推理延迟最低(GPU 上 < 0.5 ms),适合高频控制。


94.5 延迟补偿:观测延迟建模与补偿方法 ⭐⭐⭐

延迟为什么是 sim-to-real 的关键瓶颈

在仿真中,从观测到动作执行是瞬时的:\(a_t = \pi(o_t)\)\(o_t\)\(a_t\) 在同一个物理时步内。但在真机上,端到端延迟 \(\tau_{total}\) 通常在 10-50 ms,而控制周期 \(\Delta t\) 通常在 10-20 ms。这意味着策略的动作是基于 1-3 个周期之前的观测做出的。

\[ a_{applied}(t) = \pi(o(t - \tau_{total})) \]

对于 50 Hz 控制频率,\(\Delta t = 20\) ms,如果 \(\tau_{total} = 30\) ms,则策略实际使用的观测比当前状态"旧"了 1.5 个控制周期。

延迟建模的三种方法

方法一:延迟随机化(最简单)

在训练时对观测施加随机延迟,让策略学会容忍延迟:

\[ o_t^{delayed} = o_{t - d}, \quad d \sim \text{Uniform}(0, d_{max}) \]

优点是简单,缺点是策略被迫采用保守控制策略来应对最坏情况的延迟,损失了部分性能。

方法二:历史观测堆栈(推荐)

把过去若干步的观测堆叠作为策略输入,让策略从历史中推断当前状态:

\[ o_t^{stack} = [o_{t-K}, o_{t-K+1}, \ldots, o_t] \]

其中 \(K\) 是堆叠深度。这让策略隐式地学会了状态估计和延迟补偿。ASAP 使用这种方法,\(K\) 通常取 3-6。

方法三:显式预测模型(最复杂)

训练一个状态预测网络,从延迟观测中预测当前状态:

\[ \hat{o}_t = f_{pred}(o_{t-K:t}, a_{t-K:t-1}) \]

策略使用预测状态 \(\hat{o}_t\) 而非延迟观测。优点是可以对延迟做精确补偿,缺点是预测误差会累积。

延迟补偿的完整推导

考虑一个简化的线性系统 \(s_{t+1} = As_t + Ba_t\),策略使用延迟 \(d\) 步的观测,则实际闭环为:

\[ s_{t+1} = As_t + B\pi(s_{t-d}) \]

如果 \(\pi\) 是线性策略 \(\pi(s) = -Ks\),则闭环变为:

\[ s_{t+1} = As_t - BKs_{t-d} \]

这是一个延迟差分方程,其稳定性取决于 \(A - BK\) 的特征值和延迟 \(d\)。关键结论是:

  1. 延迟越大,稳定域越小——增益 \(K\) 必须更保守
  2. 延迟不确定时——增益必须对最坏情况延迟也稳定
  3. 非线性系统中延迟效应更严重——因为线性化的有效范围被延迟缩小了

本质洞察:延迟不是简单的"信息变旧了"。对于反馈控制系统,延迟本质上是引入了额外的动态——把一个 \(n\) 阶系统变成了 \(n + d\) 阶系统。这个额外的动态让稳定性分析和控制器设计变得困难得多。这就是为什么在仿真中"零延迟完美运行"的策略,在真机上因为 30 ms 延迟就可能崩溃。

import torch


def stack_observations(obs_buffer: list, stack_depth: int = 6):
    """中文注释:将最近 stack_depth 步观测堆叠为一个长向量。"""
    # 中文注释:obs_buffer 是一个 deque,最新观测在最后
    recent = obs_buffer[-stack_depth:]
    return torch.cat(recent, dim=-1)


def simulate_delay(obs: torch.Tensor, delay_steps: int, obs_buffer: list):
    """中文注释:模拟随机延迟,返回延迟后的观测。"""
    obs_buffer.append(obs)
    if len(obs_buffer) > delay_steps:
        # 中文注释:返回 delay_steps 之前的观测
        return obs_buffer[-delay_steps - 1]
    else:
        return obs_buffer[0]

94.6 Delta-Action Residual Model 数学形式 ⭐⭐⭐⭐

动机:为什么需要在阶段一之后再做一步

即使使用了 Domain Randomization、执行器网络和延迟建模,仿真和真实之间仍然存在残余差距。这些残余差距可能来自未建模的动力学(如 G1 踝关节的四连杆机构)、接触模型的结构性差异、或者传感器的特定噪声模式。

传统方法到这里就止步了——"继续加 DR 吧"。但这种做法有根本性的局限。

DR 的理论局限

DR 的理论保证建立在一个关键假设上:真实物理参数在随机化分布内。如果真实的接触模型与仿真器使用的模型有结构性差异(比如 PhysX 用的是基于穿透深度的罚函数,而真实世界是弹塑性接触),那么无论怎样随机化参数,仿真中都不可能产生与真实一致的接触行为。

\[ \text{Gap}_{structural} = f_{real}(s, a) - \sup_{\xi \in \Xi} f_{sim}(s, a; \xi) \]

这个"结构性残差" \(\text{Gap}_{structural}\) 无法通过扩大 \(\Xi\)(随机化范围)来消除。

ASAP 提出了一个更直接的思路:如果我们有少量真机数据,能不能直接学习这个结构性残差?

残差学习的信息论视角

从信息论的角度看,ASAP 的两阶段可以理解为"先粗后精"的信息利用:

阶段 信息来源 信息量 处理方式
DR + 执行器网络 物理知识 + 少量标定数据 高先验、低数据 参数化建模
Delta-action 真机 rollout 数据 低先验、中数据 非参数化学习

DR 和执行器网络利用的是关于物理世界的先验知识(质量有范围、电机有带宽等),处理了大部分可参数化的差距。Delta-action 利用的是真机数据中关于残余差距的直接证据,处理了不可参数化的结构性差距。

核心方程与完整推导

\[ a_t^{corr}=a_t+\Delta_\phi(s_t,a_t,h_t) \]

其中 \(a_t\) 是策略输出动作,\(h_t\) 是短历史(过去 5-20 步的状态和动作),\(\Delta_\phi\) 是参数为 \(\phi\) 的残差网络。

训练目标的推导

我们希望找到一个动作修正 \(\Delta\),使得仿真器在修正后的动作下产生的下一状态尽可能接近真实的下一状态:

\[ \min_\phi\;\mathbb{E}_{(s_t^r,a_t^r,s_{t+1}^r) \sim \mathcal{D}_{real}} \left\|s_{t+1}^r-f_{sim}(s_t^r,a_t^r+\Delta_\phi(s_t^r,a_t^r,h_t))\right\|^2 \]

这个目标看起来像监督学习,但它需要调用仿真器转移函数 \(f_{sim}\)。训练时把真实状态喂进仿真器,再寻找哪个动作修正能让仿真器走到真实下一状态附近。

为什么这不是普通的监督学习?

普通监督学习的梯度可以直接通过输出到输入反传。这里的梯度链条是:

\[ \frac{\partial \mathcal{L}}{\partial \phi} = \frac{\partial \mathcal{L}}{\partial s_{t+1}^{sim}} \cdot \underbrace{\frac{\partial f_{sim}}{\partial a}}_{\text{仿真器的动作梯度}} \cdot \frac{\partial \Delta_\phi}{\partial \phi} \]

中间需要仿真器关于动作的梯度 \(\frac{\partial f_{sim}}{\partial a}\)。对于可微仿真器(如 Brax、DiffTaichi),这个梯度可以直接计算。对于不可微仿真器(如 IsaacGym/MuJoCo),需要用有限差分近似或零阶优化。

为什么修改 action 而不是 dynamics

这是 ASAP 最核心的设计决策。有两种残差策略:

Delta-dynamics:直接在仿真器输出的下一状态上加修正

\[ s_{t+1}^{corr} = f_{sim}(s_t, a_t) + \delta_{dyn}(s_t, a_t) \]

Delta-action:在动作上加修正,仍然让仿真器负责状态转移

\[ s_{t+1}^{corr} = f_{sim}(s_t, a_t + \delta_{act}(s_t, a_t, h_t)) \]
维度 Delta-action Delta-dynamics
学习对象 动作修正 \(\delta_{act} \in \mathbb{R}^{n_a}\) 状态修正 \(\delta_{dyn} \in \mathbb{R}^{n_s}\)
输出维度 动作空间维度(G1: 23) 状态空间维度(G1: ~50+)
物理一致性 仍由仿真器积分,保持物理约束 可能生成非物理状态(穿透、负能量)
过拟合风险 受动作空间限制,表达力有界 可直接改状态,表达力过强
与 PPO 结合 可作为动作 wrapper,无需改环境 需要修改环境转移函数
部署处理 微调后可丢弃或保留 通常必须保留

本质洞察:Delta-action 是一种受限残差。它不能任意捏造下一状态,只能通过"给仿真器一个不同的动作"来改变结果。这种限制反而是优势——它相当于加了一个信息瓶颈,迫使模型学习"真实硬件需要怎样不同的动作指令",而不是记忆一张状态误差表。

反事实推理:如果使用 delta-dynamics 会怎样?假设真机上脚底接触产生了仿真中不存在的 50 N 侧向摩擦力,delta-dynamics 可以直接在下一状态中加一个侧向速度修正。但这个修正可能违反能量守恒——状态突然多了动能,而没有对应的力来源。下一个时步,仿真器从这个非物理状态出发积分,可能产生发散。相比之下,delta-action 只能通过修改关节力矩来间接产生侧向运动,物理一致性由仿真器保证。

Delta-action 的表达力分析

一个自然的问题是:delta-action 的表达力足够吗?如果真实世界需要的状态修正不能通过修改动作来实现怎么办?

考虑仿真器的动力学 \(s_{t+1} = f_{sim}(s_t, a_t)\)。Delta-action 能达到的状态集合是:

\[ \mathcal{S}_{reachable}(s_t) = \{ f_{sim}(s_t, a_t + \delta) \mid \delta \in \mathcal{A} \} \]

其中 \(\mathcal{A}\) 是动作空间。如果真实的下一状态 \(s_{t+1}^{real} \notin \mathcal{S}_{reachable}(s_t)\),则 delta-action 无法精确补偿。

什么时候表达力不够?

  1. 状态维度高于动作维度:G1 有 23 个关节但 delta-action 只修正 4 个踝关节。如果差距主要在其他关节,修正踝关节无法覆盖。实践中 ASAP 发现踝关节是主要瓶颈,所以可行。
  2. 仿真器本身不可达:如果真实物理允许但仿真器禁止的状态(如某些接触穿透模式),delta-action 也无法达到。
  3. 非线性死区:如果仿真器在某些动作区域不敏感(如关节限位附近),delta-action 的修正效果会被削弱。

实践结论:对于大多数行走和全身运动任务,delta-action 的表达力足够——因为主要差距来自执行器(可以通过修改动作补偿)和接触模式(可以通过修改接触前的动作来改变接触时刻和力分布)。

ASAP 的具体实现:只修正踝关节

ASAP 论文的一个重要发现是:不需要对全部 23 个自由度训练 delta-action,只需要对踝关节的 4 个自由度训练就足够了。

G1 踝关节四连杆机构的 sim-to-real gap

Unitree G1 的踝关节使用了四连杆传动机构,而不是直驱电机。这是一个关键的工程设计决策——四连杆在紧凑空间中提供了更大的运动范围。但它给仿真带来了严重问题:

  1. 非线性传动比:四连杆的传动比 \(\eta(\theta)\) 随关节角度 \(\theta\) 非线性变化。在仿真中通常用常数传动比近似,这在大角度运动时会产生显著误差。
\[ \tau_{ankle} = \eta(\theta) \cdot \tau_{motor} \]

仿真中 \(\eta\) 是常数,真实中 \(\eta(\theta)\)\(\theta\) 的非线性函数。当 \(\theta\) 偏离标称值时,力矩传递效率可能变化 20-40%。

  1. 间隙和摩擦:四连杆的铰接点有间隙,运动方向切换时存在死区。这种非理想特性在仿真中通常被忽略。

  2. 弹性变形:高载荷下连杆会发生弹性变形,改变了实际的运动学关系。

  3. 装配误差:四连杆的运动学参数(连杆长度、铰接点位置)受装配精度影响,个体差异大。

为什么只修正 4 个自由度就够?

从数据效率的角度看:全部 23 个自由度的 delta-action 需要学习 23 维输出,以真机 10-30 分钟的数据(30k-90k 样本)训练可能不充分。而 4 维输出只需要少得多的数据。

从物理角度看:踝关节是人形机器人与地面的唯一接口。行走稳定性很大程度上取决于脚底的力分布,而脚底力分布直接由踝关节动力学决定。修正踝关节就是修正了机器人与环境交互的最关键接口。

思维陷阱 🧠

新手想法:"既然只修正踝关节就有 52.7% 的改善,那修正全部关节岂不是更好?"

实际上:更多的修正维度需要更多的真机数据来避免过拟合。ASAP 论文的消融实验表明,全 23 自由度的 delta-action 在有限数据下反而不如只修正踝关节——因为其他关节的残差很小,全维度模型在这些"不重要"的维度上引入了噪声

正确思维:先分析哪些关节的 sim-to-real gap 最大(通过 sim2sim 误差分析),只修正 gap 最大的少数关节

\[ \Delta_\phi: \mathbb{R}^{n_s + n_a + n_h} \to \mathbb{R}^{4} \quad (\text{仅踝关节}) \]
import torch


def delta_action_loss(delta_model, sim_step, batch):
    """中文注释:用真实转移数据训练动作残差模型。"""
    state = batch['state_real']        # 中文注释:真机记录的当前状态
    action = batch['action_real']      # 中文注释:策略输出的动作
    next_state_real = batch['next_state_real']  # 中文注释:真机的下一状态
    history = batch['history']         # 中文注释:过去若干步的状态-动作历史

    # 中文注释:残差模型预测动作修正量
    delta = delta_model(state, action, history)

    # 中文注释:把修正后的动作送入仿真器
    corrected_action = action.clone()
    corrected_action[:, ankle_indices] += delta  # 中文注释:仅修正踝关节

    next_state_sim = sim_step(state, corrected_action)

    # 中文注释:状态误差可按关节、根速度、接触等分块加权
    loss = (next_state_sim - next_state_real).square().mean()
    return loss


# 中文注释:G1 的踝关节索引(左右各 2 个自由度)
ankle_indices = [4, 5, 10, 11]

94.7 Stage 2 详解:真机数据微调的方法论 ⭐⭐⭐⭐

数据采集:真机 rollout 的工程实践

真机 rollout 不是越激烈越好。一开始应选择仿真策略已经较稳的动作,并设置姿态、速度和力矩安全阈值。

记录项 原因 最低要求 工程注意事项
状态 \(s_t\) 残差训练输入 时间戳对齐 用硬件时钟而非软件时钟
动作 \(a_t^{policy}\) 策略原始输出 保存未裁剪值 记录 tanh 前后的值
动作 \(a_t^{sent}\) 实际发送到电机的动作 保存裁剪后值 包含安全限幅的影响
下一状态 \(s_{t+1}\) 监督目标 同一时钟源 确保与 \(s_t\) 间隔恰好一个 \(\Delta t\)
历史 \(h_t\) 估计延迟和隐变量 至少 5-20 步 环形缓冲区实现
接触标记 分段残差 足底力传感器 阈值过滤噪声
安全标记 过滤异常数据 摔倒/急停/通信异常 异常数据不参与训练

数据量要求

ASAP 论文报告使用约 10-30 分钟的真机行走数据(50 Hz 采样 = 30,000-90,000 个转移对)就足以训练有效的 delta-action 模型。数据量少是因为只修正 4 个踝关节自由度——学习目标的维度低,需要的数据相应少。

微调时如何使用残差

训练好 \(\Delta_\phi\) 后,在仿真中运行策略时不直接执行 \(a_t\),而是执行修正后的动作:

\[ a_t^{corrected} = a_t + \Delta_\phi(s_t, a_t, h_t) \]

策略在这个被修正的仿真环境中继续用 PPO 训练。从策略的角度看,仿真器的动力学发生了改变——更接近真机。策略逐步把补偿吸收到自身参数中。

微调的数学解释

设原始策略为 \(\pi_\theta\),微调后的策略为 \(\pi_{\theta'}\)。在带残差的仿真环境中,策略的目标是:

\[ \max_{\theta'} \mathbb{E}_{\tau \sim \pi_{\theta'}, f_{sim}(\cdot, \cdot + \Delta_\phi)} \left[ \sum_t \gamma^t r(s_t, a_t) \right] \]

如果 \(\Delta_\phi\) 准确地捕获了仿真和真实的差距,那么这个目标等价于在真实环境中直接优化策略——但完全在仿真中完成,无需额外的真机交互。

为什么部署时可以丢弃残差

如果微调充分,策略的参数 \(\theta'\) 已经编码了在真实物理下需要的动作偏移。此时残差模型 \(\Delta_\phi\) 的输出接近零,可以安全丢弃。

部署链路变为:\(o_t \to \pi_{\theta'}(o_t) \to a_t \to\) 电机,不需要额外的残差推理,延迟更低。

陷阱警告 ⚠️

"可以丢弃"不是数学定理,而是经验上成立的部署策略。如果残差模型学习的是强接触模式下的瞬时补偿(例如跳跃着地时的踝关节力矩尖峰),策略可能无法完全吸收这种高频、大幅度的补偿。

判断标准:部署前做 A/B 对比——保留残差 vs 丢弃残差,在目标动作集上比较跟踪误差。如果丢弃后误差增加不超过 10%,可以安全丢弃。如果增加超过 20%,建议保留轻量残差或延长微调。

最小日志格式

from dataclasses import dataclass, field
import numpy as np


@dataclass
class RolloutRecord:
    """中文注释:用于 delta-action 训练的一步真实转移记录。"""
    time: float                          # 中文注释:硬件时钟时间戳
    state: np.ndarray = field(default_factory=lambda: np.array([]))
    action_policy: np.ndarray = field(default_factory=lambda: np.array([]))
    action_sent: np.ndarray = field(default_factory=lambda: np.array([]))
    next_state: np.ndarray = field(default_factory=lambda: np.array([]))
    contact_flag: np.ndarray = field(default_factory=lambda: np.array([]))
    safety_flag: int = 0                 # 中文注释:0=正常, 1=急停, 2=摔倒


@dataclass
class RolloutDataset:
    """中文注释:管理真实转移数据集。"""
    records: list = field(default_factory=list)

    def filter_safe(self):
        """中文注释:过滤掉安全标记异常的数据。"""
        return [r for r in self.records if r.safety_flag == 0]

    def to_training_batch(self, history_len: int = 10):
        """中文注释:把连续记录整理成带历史的训练批次。"""
        safe = self.filter_safe()
        batches = []
        for i in range(history_len, len(safe)):
            history = [safe[j].state for j in range(i - history_len, i)]
            batches.append({
                'state_real': safe[i].state,
                'action_real': safe[i].action_policy,
                'next_state_real': safe[i].next_state,
                'history': np.stack(history),
            })
        return batches

94.8 对比方法论:RMA vs Residual RL vs ASAP ⭐⭐⭐

三种 sim-to-real 适应路线的对比

从"如何利用真机信息"的角度,可以把主流 sim-to-real 方法分为三条路线:

路线一:RMA(Rapid Motor Adaptation, Kumar et al. 2021)

RMA 的核心思想是在线适应:训练一个"适应模块",从短历史观测中实时推断环境参数(如地面摩擦、负载质量),然后把推断出的参数向量作为条件输入策略。

\[ \hat{e}_t = f_{adapt}(o_{t-K:t}), \quad a_t = \pi(o_t, \hat{e}_t) \]
阶段 训练内容 数据来源
第一阶段 训练 teacher 策略,条件化在真实环境参数 \(e_t\) 仿真(\(e_t\) 可直接获取)
第二阶段 训练 student 适应模块,从观测历史预测 \(e_t\) 仿真(Teacher 的 rollout)
部署 适应模块实时推断 \(\hat{e}_t\) 真机观测

路线二:Residual RL / TRANSIC(残差强化学习)

在预训练策略的输出上叠加一个残差策略,残差策略用真机上的少量交互数据训练:

\[ a_t = \pi_{base}(o_t) + \pi_{residual}(o_t) \]

残差策略在真机上在线训练(或用人类修正数据离线训练)。

路线三:ASAP(Delta-Action + Sim 微调)

ASAP 与上述两种方法的关键区别是:它不是在部署时做在线适应,而是先收集真机数据,再用这些数据改进仿真环境,最后在改进后的仿真中继续训练。

维度 RMA Residual RL ASAP
适应时机 在线(部署时实时) 在线或离线 离线(训练完成后部署)
适应对象 推断环境参数 策略动作修正 仿真动力学修正
真机数据需求 零(纯仿真训练) 中等(在线交互) 少(10-30 分钟 rollout)
安全性 高(无需真机训练) 中(在线探索有风险) 高(真机只收集数据)
泛化能力 好(覆盖参数分布) 有限(拟合特定环境) 中等(拟合特定机器人)
复杂动作支持 中等 低(在线学习样本效率低) 高(可在仿真中大量训练)
能处理的 gap 类型 可参数化的 gap 任意 gap(但需在线数据) 任意 gap(通过动作补偿)
部署复杂度 需要运行适应模块 需要运行残差策略 可丢弃残差,部署最简

反事实推理:如果用 RMA 做人形全身动作模仿会怎样?RMA 假设环境变化可以用一个有限维的参数向量 \(e_t\) 描述。对于地面摩擦、负载等连续变化的物理量,这个假设合理。但人形的接触模式切换(站立→行走→跳跃→着地)不是连续参数变化,而是结构性的模式切换。适应模块需要在几十毫秒内从行走模式切换到着地模式,这对实时推断网络是很大的挑战。ASAP 绕开了这个问题——它不需要在线实时适应,而是离线对齐后在仿真中充分训练。

定量结果对比

ASAP 论文报告了在 Unitree G1 真机上的跟踪误差对比:

方法 关节角 MPJPE (rad) 改善比例
仅 DR(baseline) 0.185 -
DR + 执行器网络 0.142 23.2%
DR + 执行器网络 + ASAP delta-action 0.088 52.7%

这个结果说明 ASAP 在 DR + 执行器网络的基础上又减少了约 38% 的残余误差。


94.9 HumanoidVerse 与跨仿真器实验 ⭐⭐

为什么要跨仿真器

一个策略只在 IsaacGym 表现好,可能只是适应了 PhysX 的接触近似。把策略迁移到 MuJoCo、IsaacSim 或 Genesis,可以提前发现对仿真器细节的过拟合。

跨领域类比:跨仿真器验证类似于软件工程中的跨平台测试。一个只在 Windows 上测试过的程序不能保证在 Linux 上正常运行——不是因为逻辑错了,而是因为系统调用、文件路径、内存管理等底层差异。类似地,IsaacGym 和 MuJoCo 的接触求解器、时间积分方法、摩擦模型都不同,在一个仿真器中有效的策略可能依赖了另一个仿真器中不存在的特性。

源域 目标域 暴露的问题
IsaacGym (PhysX) MuJoCo 接触刚度、摩擦近似(锥 vs 金字塔)、关节阻尼差异
IsaacGym IsaacSim (Omniverse PhysX) 资产管线、传感器模拟、渲染和物理参数差异
IsaacGym Genesis 并行仿真架构差异、接触求解差异
任意仿真 G1 真机 执行器、延迟、线缆弹性、地面特性、温度

统一接口设计

跨仿真器的关键是统一观测、动作、奖励、终止条件和资产坐标系。如果每个仿真器都使用不同的命名和归一化,策略差异将无法解释。

统一项 标准化方法
动作空间 统一为关节目标位置,相同的 action_scale
观测归一化 使用训练集的运行均值和标准差
坐标系 统一为右手坐标系,z 轴向上
时间步长 统一物理时步和 decimation 比例
URDF/MJCF 从同一源文件转换

Sim2sim delta-action 实验

教学中可以用 IsaacGym 作为源域,MuJoCo 作为目标域。先把 MuJoCo rollout 当作"真实数据",训练 delta-action,再回到 IsaacGym 微调。这个实验安全、可重复,适合作为真机前的验证。

ASAP 论文的 sim2sim 实验矩阵:

源域 → 目标域 跟踪误差改善
IsaacGym → IsaacSim 41.2%
IsaacGym → Genesis 38.5%
IsaacGym → G1 真机 52.7%

HumanoidVerse 统一评测平台

HumanoidVerse 是 ASAP 团队开源的跨仿真器人形机器人训练和评测平台。它提供了统一的观测和动作接口,使得同一个训练配置可以在 IsaacGym、IsaacSim、MuJoCo 和 Genesis 中运行。

统一抽象层的设计

抽象 统一定义 各仿真器实现
reset() 重置环境,返回初始观测 各仿真器的 reset API
step(action) 执行动作,返回 (obs, reward, done, info) 各仿真器的 step API
get_dof_state() 返回关节角和角速度 按统一顺序排列
get_root_state() 返回基座位姿和速度 统一坐标系和四元数顺序
set_action_scale() 设置动作缩放因子 确保相同动作在不同仿真器中含义相同

实验矩阵设计方法

跨仿真器实验的目标不是"找到哪个仿真器最好",而是"找到策略对哪些仿真细节敏感"。设计实验矩阵时应考虑:

对比维度 实验设计 观察指标
接触模型 PhysX vs MuJoCo Elliptic Cone 着地冲击力分布
积分方法 Euler vs Semi-implicit vs RK4 能量漂移
摩擦模型 金字塔近似 vs 椭圆锥 脚底滑移率
GPU 并行 批量 vs 独立 策略动作分布差异
时间步长 1ms vs 2ms vs 5ms 接触穿透深度

思维陷阱 🧠

新手想法:"在最准确的仿真器上训练就行了"

实际上:没有一个仿真器在所有维度上都最准确。MuJoCo 的接触模型可能比 PhysX 更精确,但 PhysX 的 GPU 并行让训练快 100 倍。选择仿真器是"精度 vs 速度"的权衡,而不是"最好"的选择。ASAP 的 sim2sim 验证正是为了揭示这种权衡——如果策略在 PhysX 和 MuJoCo 中表现差异很大,说明它依赖了某个仿真器的特性,需要更多 DR 或更好的执行器模型

主流仿真器对比

仿真器 接触求解 GPU 并行 开源 主要优势 主要局限
IsaacGym (PhysX) 穿透惩罚 + 金字塔摩擦 原生支持, 4096+ 环境 部分开源 速度最快 已停止开发
Isaac Lab (PhysX 5) 改进的穿透惩罚 原生支持 开源 NVIDIA 官方维护 生态较新
MuJoCo 3.x 凸优化接触 + 椭圆锥 部分 GPU (MJX) 开源 接触精度高 并行速度较慢
Genesis 可微物理 GPU 原生 开源 可微梯度 生态较小
Brax 弹簧接触 JAX 原生 开源 完全可微 接触精度有限

选择策略: - 大规模 PPO 训练 → IsaacGym / Isaac Lab(速度第一) - 接触精度验证 → MuJoCo(精度第一) - 需要可微梯度 → Genesis / Brax(可微第一) - ASAP sim2sim → 源用 IsaacGym,目标用 MuJoCo

Sim2sim 实验的完整协议

  1. 源域训练:在 IsaacGym 中完成完整的 Stage 1 训练
  2. 源域评估:记录 100 个 episode 的跟踪误差分布作为基线
  3. 直接迁移:把完全相同的策略权重加载到目标仿真器
  4. 目标域评估:记录 100 个 episode 的跟踪误差
  5. 差异分析:按关节、时间相位、接触状态分解误差来源
  6. Delta-action 训练:用目标域 rollout 训练残差模型
  7. 微调评估:在源域+残差中微调后,再次在目标域评估
  8. 丢弃测试:丢弃残差后在目标域评估

94.10 接触触发失败案例与模式条件化 ⭐⭐⭐

为什么接触让残差难学

连续残差模型默认相近状态对应相近修正。接触触发时,状态只差几毫米,下一状态可能从空中运动变成强冲击。这会让监督目标不光滑,残差模型容易学到平均动作——既不是空中的最优,也不是接触的最优。

场景 不连续来源 表现 处理方法
脚掌刚接触地面 接触力从零突变到数百牛 下一状态误差尖峰 接触相位输入 + 力渐入
手撞到物体 外部约束突然出现 手臂反弹角度不一致 接触模式分类器
脚底滑移 静摩擦→动摩擦切换 速度突变 摩擦随机化 + 滑移检测
跳跃落地 高频力峰值 IMU 和关节速度尖峰 低通滤波 + 冲击课程

模式条件化残差

\[ \Delta(s,a,h,c)=\sum_m\mathbf{1}[c=m]\,\Delta_m(s,a,h) \]

\(c\) 可以是接触相位(空中/着地/离地)、足底力阈值、动作阶段(抬腿/摆动/落地)或分类器输出。模式条件化让每个残差子模型处理更平滑的局部转移。

反事实推理:如果不做模式条件化,用一个统一的残差模型会怎样?考虑脚掌从空中 1 mm 处到接触后 1 mm 处的转移。空中状态的下一状态是继续下落(小速度变化),接触状态的下一状态是瞬间减速加反弹。统一模型会把这两种目标平均——输出一个介于"继续下落"和"瞬间停止"之间的预测,两种情况下都不准确。

课程干预

先训练非接触或弱接触动作(站立、慢走),再逐渐加入高冲击和手部接触。每次增加难度前,应检查:

  1. Delta 量级是否合理(不应超过动作范围的 30%)
  2. 状态预测误差是否稳定下降
  3. 策略动作分布在微调前后是否平滑变化

94.11 部署流程:从训练完成到真机运行 ⭐⭐⭐

完整 pipeline 概览

阶段一完成 → sim2sim 验证 → 真机 rollout → delta-action 训练 → 带残差微调 → 丢弃/保留残差 → 部署

部署前检查清单

检查项 原因 通过标准 失败后处理
动作限幅 避免超出低层接口 所有维度在安全范围内 调整 action_scale
姿态阈值 摔倒前急停 roll/pitch 触发可靠 降低阈值
力矩估计 保护电机 峰值力矩留有 20% 裕度 降低动作幅度
延迟测量 残差依赖时间对齐 端到端延迟稳定在 \(\pm 2\) ms 内 检查通信链路
回放一致性 训练日志可复现 同一日志在仿真中重放误差 < 1% 检查单位和坐标系
观测归一化 训练和部署使用相同归一化 均值/方差与训练集一致 更新归一化参数

关键诊断曲线

部署调试应至少记录五类曲线:

  1. 状态预测误差:真实状态与仿真预测下一状态的逐维误差——用于验证 delta-action 的效果
  2. Delta 动作分布:每个维度的均值、标准差和峰值——delta 过大说明仿真与真实差距仍然很大
  3. 策略动作对比:微调前后的动作分布变化——平滑变化说明微调成功
  4. 接触相位误差:按接触状态分段统计误差——定位差距的主要来源
  5. 任务指标:MPJPE(关节角误差)、存活率、动作完成度

部署安全约束

import numpy as np


def safety_check(state: dict, limits: dict) -> tuple[bool, str]:
    """中文注释:部署安全检查,返回是否安全及原因。"""
    # 中文注释:检查基座姿态
    roll, pitch = state['base_euler'][0], state['base_euler'][1]
    if abs(roll) > limits['max_roll']:
        return False, f"roll={roll:.2f} > {limits['max_roll']}"
    if abs(pitch) > limits['max_pitch']:
        return False, f"pitch={pitch:.2f} > {limits['max_pitch']}"

    # 中文注释:检查关节位置限位
    q = state['joint_pos']
    if np.any(q < limits['q_min']) or np.any(q > limits['q_max']):
        violated = np.where((q < limits['q_min']) | (q > limits['q_max']))[0]
        return False, f"joint limit violated at {violated}"

    # 中文注释:检查关节速度
    dq = state['joint_vel']
    if np.any(np.abs(dq) > limits['max_dq']):
        return False, f"joint velocity too high: max={np.max(np.abs(dq)):.2f}"

    return True, "OK"


# 中文注释:G1 的安全限制示例
G1_SAFETY_LIMITS = {
    'max_roll': 0.8,      # rad, ~45 度
    'max_pitch': 0.6,     # rad, ~35 度
    'q_min': np.array([-1.57] * 23),  # 简化示例
    'q_max': np.array([1.57] * 23),
    'max_dq': 10.0,       # rad/s
}

94.12 完整工程 Pipeline:从零到部署 ⭐⭐⭐

系统架构总览

下面给出一个完整的 ASAP 工程流程,适用于从零开始的人形 sim-to-real 项目:

┌──────────────────────────────────────────────────────────────────┐
│                      ASAP 完整工程 Pipeline                       │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  [数据准备]                                                       │
│  AMASS/视频 → SMPL → 重定向 → 机器人参考动作序列                    │
│       ↓                                                          │
│  [Stage 1: 仿真训练]                                              │
│  IsaacGym + DR + 执行器网络 → PPO + Phase-conditioned tracking    │
│       ↓                                                          │
│  [Sim2sim 验证]                                                   │
│  IsaacGym → MuJoCo/Genesis 跨仿真器测试                           │
│       ↓                                                          │
│  [真机 Rollout]                                                   │
│  Stage 1 策略 → G1 真机 → 记录 (s, a, s') 转移数据                 │
│       ↓                                                          │
│  [Delta-action 训练]                                              │
│  转移数据 → 训练踝关节残差模型 Δ_φ                                 │
│       ↓                                                          │
│  [Stage 2: 带残差微调]                                             │
│  IsaacGym + Δ_φ → PPO 继续训练 → 策略吸收补偿                     │
│       ↓                                                          │
│  [部署决策]                                                       │
│  A/B 对比 → 丢弃残差 or 保留轻量残差                               │
│       ↓                                                          │
│  [最终部署]                                                       │
│  策略推理 → 安全限幅 → 电机指令 → 真机运行                         │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

每个阶段的关键检查点

阶段 进入条件 退出条件 典型耗时
数据准备 有动捕/视频数据 参考轨迹可在仿真中可视化 1-3 天
Stage 1 训练 参考轨迹就绪 仿真跟踪 MPJPE < 0.15 rad 2-5 天
Sim2sim 验证 Stage 1 收敛 跨仿真器性能下降 < 30% 1 天
真机 Rollout Sim2sim 通过 10-30 分钟安全数据 1-2 天
Delta-action 训练 真机数据就绪 状态预测误差下降 > 30% 0.5 天
Stage 2 微调 Delta 模型收敛 微调后仿真 MPJPE 改善 1-2 天
部署决策 微调完成 A/B 对比结论明确 0.5 天
最终部署 决策完成 真机运行稳定 1 天

版本管理与复现

sim-to-real 项目的一大困难是复现性。以下信息必须版本化:

版本化内容 存储方式 原因
参考动作数据 Git LFS 或数据服务器 训练依赖的输入
DR 配置文件 Git(YAML/JSON) 随机化参数的完整记录
训练检查点 模型服务器 + hash 对应特定训练配置
真机 rollout 日志 时间戳命名的 HDF5 Delta 训练的数据来源
Delta 模型权重 与训练检查点关联 微调依赖
部署配置 Git(包括安全限幅参数) 确保部署可复现
仿真器版本 记录具体 commit hash 不同版本可能行为不同

陷阱警告 ⚠️

编程陷阱:不记录仿真器版本

错误做法:用 pip install isaacgym 安装最新版本开始训练,不记录具体版本

现象:三个月后重跑同一个配置,结果完全不同

根本原因:IsaacGym/Isaac Lab/MuJoCo 的不同版本可能修改了默认接触参数、积分器设置或 GPU 调度方式。这些"看不见的变化"会导致相同配置产生不同训练结果

正确做法:在项目根目录维护一个 environment.yml,锁定所有依赖版本。训练日志中记录仿真器的 git commit hash

常见时间陷阱

陷阱 浪费时间 避免方法
DR 范围调了一周 5+ 天 使用论文推荐的默认范围开始
Stage 1 过度训练 3+ 天 MPJPE 不再下降就停止
真机 rollout 数据质量差 2+ 天返工 先做短时间试跑验证日志质量
坐标系不一致 1-3 天 debug 在 pipeline 第一步就统一坐标系
忘记记录 action_sent 重做 rollout 使用标准日志模板

94.13 与 PHC/ExBody/HOVER 的关系 ⭐⭐⭐

PHC(Perpetual Humanoid Control, Luo et al. ICCV 2023)

PHC 提出了 Progressive Multiplicative Control Policy (PMCP),能够不重置地持续跟踪动作序列。ASAP 的阶段一训练可以使用 PHC 的跟踪策略架构——PHC 提供了强大的动作跟踪能力,ASAP 在此基础上解决 sim-to-real 迁移。

维度 PHC ASAP
解决的问题 仿真中持续不间断的动作跟踪 跟踪策略从仿真到真机的迁移
工作域 仿真 仿真 + 真机
核心贡献 PMCP 渐进式网络扩展 Delta-action 残差模型
互补关系 提供强大的 Stage 1 策略 解决 Stage 1 策略的 sim-to-real gap

PHC 的 PMCP 机制详解

PHC 的核心创新是 Progressive Multiplicative Control Policy(PMCP)。传统方法对每个动作序列训练一个策略,PMCP 通过渐进式地增加网络容量来处理越来越难的动作。

具体过程: 1. 先训练一个小网络处理简单动作(站立、慢走) 2. 冻结已训练的网络权重,增加新的网络层 3. 新层通过乘法(而非加法)与旧层组合——\(\pi_{new}(o) = \pi_{old}(o) \odot g_{new}(o)\) 4. 只训练新层的参数,处理更难的动作(跑步、跳跃) 5. 重复 2-4,渐进地覆盖所有动作

乘法组合的优势\(g_{new}(o)\) 初始化接近 1,意味着新层初始不改变旧层的输出。这比加法组合 \(\pi_{old}(o) + g_{new}(o)\) 更稳定——加法组合的 \(g_{new}\) 初始化为 0 时虽然也不改变输出,但训练过程中 \(g_{new}\) 的变化会与 \(\pi_{old}\) 叠加产生大的动作变化。

ExBody/ExBody2(Cheng et al. 2024-2025)

ExBody 系列专注于全身表现力控制——不仅跟踪关节角,还要表现出自然的人类动作风格。与 ASAP 的关系是正交的:ExBody 改进了仿真中的训练目标和网络架构,ASAP 改进了仿真到真机的迁移。

HOVER(He et al. 2024)

HOVER 提出了统一的人形全身控制器,能在同一个策略中处理多种运动模式(行走、跑步、跳跃、翻转)。ASAP 可以作为 HOVER 的下游——用 HOVER 的多技能策略作为 Stage 1,然后用 ASAP 迁移到真机。

方法关系图谱

                    训练质量提升
         ┌─────────────────────────────┐
         │                             │
         ▼                             │
   [PHC]──────→ Stage 1 策略 ──→ [ASAP] ──→ 真机部署
     │              ↑                           │
     │         [ExBody]                         │
     │         提升动作自然度                     │
     │              ↑                           ▼
     │         [HOVER]                    力敏感任务
     │         统一多技能               ┌──→ [FALCON] ─→ 双策略
     │              ↑                  │  → [SoFTA] ──→ 频率解耦
     │         [BFM-Zero]             │
     │         零样本泛化              遥操作数据
     │                               ┌──→ [HOMIE]
     └─────────────────────────────────┘

关键区分: - PHC/ExBody/HOVER 解决的是"在仿真中如何训练更好的策略" - ASAP 解决的是"如何把仿真策略迁移到真机" - FALCON/SoFTA 解决的是"如何处理力敏感任务" - HOMIE 解决的是"如何获取高质量训练数据" - BFM-Zero 解决的是"如何减少手工奖励设计"

这些方法可以组合使用,但每个解决的是 pipeline 中不同的瓶颈。选择组合时,先确定你的瓶颈在哪里——是仿真中策略不够好?是 sim-to-real gap?是力控任务需求?还是缺乏训练数据?


94.14 奖励工程的常见陷阱 ⭐⭐⭐

奖励塑造不当导致的失败模式

ASAP 的 Stage 1 训练依赖精心设计的奖励函数。奖励设计不当会导致策略学到错误的行为——这就是"reward hacking"。

陷阱 表现 原因 修复
抖动 关节高频振荡 平滑惩罚不足,策略发现抖动可以增加速度跟踪奖励 增大 \(r_{smooth}\) 权重
趴地 策略让机器人趴下不动 摔倒惩罚太大,策略发现趴着比站着安全 增加站立姿态奖励
脚底打滑 策略不抬脚只滑动 滑动比抬脚更容易获得速度跟踪奖励 增加空中脚步奖励
姿态畸形 跟踪对了但姿态不自然 只有关节角奖励没有关键点/姿态奖励 增加关键点和姿态正则
能耗爆炸 力矩很大但动作正确 无能耗惩罚,策略用暴力方式完成任务 增加力矩平方和惩罚

陷阱警告 ⚠️

编程陷阱:用 \(\exp(-e^2/\sigma^2)\)\(\sigma\) 设得太大

现象:训练看起来在收敛(总奖励在增加),但动作质量没有提高

根本原因:当 \(\sigma\) 远大于实际误差时,\(\exp(-e^2/\sigma^2) \approx 1 - e^2/\sigma^2\),奖励对误差变化不敏感。策略缺乏改善跟踪的梯度信号

正确做法\(\sigma\) 应设在"初始策略的平均误差"附近——既不太小(初始就拿零分,没有改善方向),也不太大(已经接近满分,没有改善空间)


常见故障与排查 ⭐⭐

🔧 故障排查手册

症状 可能原因 排查步骤 修复方向
delta 训练 loss 低但微调无效 状态权重不合理或只拟合易预测维度 分块查看根速度、关节、接触误差 重设 loss 权重,增加接触附近数据
delta 输出很大(>30% 动作范围) 真实数据分布离仿真太远 画每维 delta 直方图 先做 SysID/DR 或降低动作难度
微调后仿真好真机差 策略吸收了仿真残差但未覆盖真实变化 比较微调前后动作分布 增加真实 rollout 多样性
接触瞬间误差尖峰 连续残差无法处理模式切换 按接触相位切分误差 加入接触条件化模型
部署丢弃残差后退化 补偿没有被策略完全吸收 保留残差做 A/B 对比 延长微调或在线保留轻量残差
sim2sim 结果不稳定 源目标仿真器接口不一致 检查动作缩放和坐标系 统一资产、单位和归一化
真机 rollout 数据无法重放 时间戳或低层裁剪未记录 检查 action_sent 与 action_policy 保存裁剪后动作和同步时钟
微调后策略抖动 残差引入了高频修正 画残差的频谱 对残差输出加低通滤波
奖励不涨但策略在动 奖励项互相抵消 分项打印每个奖励分量 重新平衡权重
训练中成功率突然下降 课程推进太快 记录课程参数和成功率曲线 设置成功率门槛才推进课程

调试工具箱

ASAP 的调试需要一系列可视化和分析工具。以下是推荐的最小工具箱:

工具 用途 实现
奖励分项记录器 打印每个奖励分量的时间序列 TensorBoard / WandB
Delta 直方图 展示每个关节的残差分布 matplotlib histogram
状态预测误差热力图 按关节和时间展示误差 seaborn heatmap
接触相位标注器 在轨迹上标注接触时刻 足底力阈值检测
动作分布对比 比较微调前后的动作分布 KDE 密度图
频谱分析器 分析动作和状态的频率成分 scipy.signal.welch
回放可视化器 在仿真中可视化真机日志 MuJoCo viewer
import numpy as np


def diagnose_delta_action(delta_model, real_data, sim_step):
    """中文注释:delta-action 模型的诊断函数。"""
    results = {
        'delta_per_joint': [],    # 中文注释:每个关节的 delta 统计
        'prediction_error': [],    # 中文注释:状态预测误差
        'contact_phase_error': {   # 中文注释:按接触相位的误差
            'flight': [], 'stance': [], 'transition': []
        }
    }

    for batch in real_data:
        delta = delta_model(batch['state'], batch['action'], batch['history'])
        corrected = batch['action'].clone()
        corrected[:, [4, 5, 10, 11]] += delta  # 中文注释:踝关节修正

        pred_next = sim_step(batch['state'], corrected)
        error = (pred_next - batch['next_state']).abs()

        results['delta_per_joint'].append(delta.detach().cpu().numpy())
        results['prediction_error'].append(error.detach().cpu().numpy())

        # 中文注释:按接触相位分类
        contact = batch['contact_flag']
        for i, phase in enumerate(['flight', 'stance', 'transition']):
            mask = (contact == i)
            if mask.any():
                results['contact_phase_error'][phase].append(
                    error[mask].mean().item()
                )

    return results

真机部署的典型失败案例

下面列出几个真实工程中常见的部署失败案例,帮助读者建立"失败预期":

案例一:坐标系错误

症状:策略在仿真中完美行走,真机上一启动就翻倒。

原因:仿真中 IMU 坐标系用的是 NWU(北西上),真机的 IMU 驱动返回的是 NED(北东下)。Pitch 角的符号反了,策略以为在前倾而实际在后倾,做出了完全相反的补偿。

教训:在 pipeline 的第一步就统一坐标系,并用静态测试验证(把机器人手动倾斜,检查观测值的符号和量级)。

案例二:动作裁剪不一致

症状:策略输出的动作在仿真中有效,真机上关节不动或动作很小。

原因:仿真中的 action_scale 是 0.5 rad,但真机的低层驱动器有一个额外的裁剪——关节速度不超过 5 rad/s。在仿真中策略可以每步变化 0.5 rad(等效 25 rad/s @ 50Hz),但在真机上被裁剪到 0.1 rad/step。

教训:在训练中引入与真机一致的动作和速度裁剪。

案例三:时间戳不对齐

症状:Delta-action 训练的 loss 很低,但用于微调后策略反而变差。

原因:真机记录的 \(s_t\)\(s_{t+1}\) 之间的实际时间间隔不是 \(\Delta t\),而是 \(\Delta t \pm 3\) ms(因为操作系统调度抖动)。状态-动作对的对应关系有误,delta 模型学到了错误的补偿。

教训:使用硬件时钟(如 MCU 上的定时器中断)保证采样间隔精确,或在日志中记录精确时间戳并在训练时按实际间隔选取。


练习 ⭐⭐

练习 94.1 Sim-to-Real Gap 分类(基础) ⭐⭐

选择一个你熟悉的机器人平台(四足、人形或机械臂),为它列出至少 12 个具体的 sim-to-real gap 来源,按动力学/感知/执行/延迟四个维度分类。对每个 gap 给出:(a) 定性描述,(b) 估计量级,(c) 建议的处理方法。

练习 94.2 执行器网络实现 ⭐⭐⭐

在 PyTorch 中实现一个单关节执行器网络。使用正弦扫频信号作为激励,生成带噪声的"真实"力矩数据(在理想 PD 力矩上加非线性饱和和延迟),训练网络并评估预测精度。具体要求:

  1. 设计至少 3 种不同频率的激励信号
  2. 对比网络预测和理想 PD 模型的力矩误差
  3. 分析网络在训练数据分布外的泛化能力

练习 94.3 Sim2sim delta-action ⭐⭐⭐⭐

把 MuJoCo rollout 当目标域,IsaacGym(或另一个仿真器)当源域,实现完整的 delta-action 训练和微调闭环:

  1. 在源域训练一个简单的行走策略
  2. 在目标域部署并记录 rollout 数据
  3. 训练 delta-action 模型
  4. 在源域+残差中微调策略
  5. 评估微调后在目标域的跟踪误差改善

练习 94.4 延迟补偿对比实验 ⭐⭐⭐

在同一个行走策略上对比三种延迟处理方法的效果:(a) 无延迟处理,(b) 延迟随机化训练,(c) 观测堆栈。在仿真中引入 20ms、40ms、60ms 的延迟,画出跟踪误差随延迟的变化曲线。

练习 94.5 接触模式分析 ⭐⭐⭐⭐

按足底接触相位(空中/着地/稳定/离地)统计 delta-action 的预测误差。分析哪种接触模式的残余差距最大,并讨论模式条件化残差是否能改善。

练习 94.6 部署链路设计 ⭐⭐

画出从传感器读数到电机指令的完整数据流图,标注每个环节的延迟。设计一个日志系统,能够记录部署时的所有关键信号并支持离线回放。

练习 94.7 综合项目(跨章)⭐⭐⭐⭐

结合93章的动作模仿和本章的 ASAP 方法,设计一个完整的人形全身技能学习和部署方案。要求覆盖:数据获取(AMASS/视频)→ 重定向 → Stage 1 训练 → sim2sim 验证 → 真机 rollout → delta-action → 微调 → 部署。写出每个环节的关键决策和可能的失败模式。


公式速查

编号 公式 含义 使用位置
E1 \(s_{t+1}=f(s_t,a_t)\) 转移动力学 gap 定义
E2 \(a^{corr}=a+\Delta_\phi(s,a,h)\) 动作残差 ASAP 核心
E3 \(\min_\phi\|s_{t+1}^r-f_{sim}(s_t^r,a_t^r+\Delta_\phi)\|^2\) 残差训练目标 阶段二
E4 \(o=[o_{prop},q^{ref}_{t:t+H},\phi]\) 相位条件观测 跟踪策略
E5 \(r=w_q e^{-\|q-q^r\|^2/\sigma^2}+r_{stable}\) 跟踪奖励 阶段一
E6 \(\Delta(s,a,h,c)=\sum_m 1[c=m]\Delta_m(s,a,h)\) 模式条件化残差 接触扩展
E7 \(\tau^{net}=\text{MLP}(q^d_{t-H:t}, q_{t-H:t}, \dot{q}_{t-H:t})\) 执行器网络 系统辨识
E8 \(a_{applied}(t)=\pi(o(t-\tau_{total}))\) 延迟下的策略执行 延迟建模

综合项目:ASAP 风格 sim2sim 对齐

  • 阶段 1:在源仿真器(IsaacGym)训练 phase-conditioned G1 动作跟踪策略。
  • 阶段 2:把同一策略部署到目标仿真器(MuJoCo)并记录转移数据。
  • 阶段 3:训练 delta-action 模型,使源仿真器在修正动作下逼近目标域下一状态。
  • 阶段 4:把 delta-action 注入源仿真器继续微调策略。
  • 阶段 5:分别评估原策略、带残差策略、微调后丢弃残差策略在目标域的跟踪误差。
  • 阶段 6:按接触相位统计误差,判断是否需要模式条件化。
  • 阶段 7:画出所有诊断曲线(误差、delta 分布、动作对比、接触分段误差、任务指标)。

本章小结

知识点 核心内容 与其他知识的关系
Sim-to-real gap 四维分类 动力学/感知/执行/延迟 为所有 sim-to-real 方法提供分析框架
执行器网络 用 MLP 拟合电机非线性响应 缩小执行差距,为 ASAP 提供更好的起点
Phase-conditioned tracking 相位消除动作歧义 93章动作模仿的工程实现
Delta-action 残差 通过动作修正对齐仿真和真实 ASAP 的核心贡献
微调与丢弃 策略吸收补偿后可丢弃残差 简化部署链路
跨仿真器验证 提前暴露对特定仿真器的过拟合 sim2sim 是 sim2real 的低风险预演
接触模式条件化 按接触状态分段训练残差 处理接触不连续性
延迟补偿 历史堆栈或显式预测 减少延迟维度的 gap

巩固卡片

卡片 01:Sim-to-real gap 四维分类

  • 问题:gap 来自哪里?
  • 直觉:仿真和真实在动力学、感知、执行和延迟四个维度上都存在差距,这些差距相互耦合。
  • 数学抓手\(f_{sim}\ne f_{real}\)
  • 工程接口:Domain Randomization、SysID、执行器网络、延迟随机化
  • 易错判断:不能只看仿真奖励来评估 sim-to-real gap,必须在真机或跨仿真器上验证。
  • 自测动作:为目标机器人列出至少 12 个 gap 来源并分类。

卡片 02:Delta-action 残差

  • 问题:为什么修动作而不修状态?
  • 直觉:修动作让仿真器继续负责积分,保持物理一致性。修状态可能生成非物理状态。
  • 数学抓手\(a^{corr}=a+\Delta_\phi(s,a,h)\)
  • 工程接口:残差模型训练 + 带残差微调
  • 易错判断:delta 输出太大(>30% 动作范围)说明源域仿真与真实差距仍然很大,应先做 SysID/DR。
  • 自测动作:画 delta 输出的每维直方图。

卡片 03:Phase-conditioned 跟踪

  • 问题:为什么动作跟踪要相位?
  • 直觉:同一个身体姿态可能出现在步态周期的不同位置,相位告诉策略"现在在哪个位置"。
  • 数学抓手\(\phi_t\)
  • 工程接口:观测向量中包含 \([\sin\phi, \cos\phi]\)
  • 易错判断:无相位策略在对称姿态处会随机选择动作方向,导致跟踪失败。
  • 自测动作:移除相位做对照实验。

卡片 04:执行器网络

  • 问题:为什么不用理想 PD 模型?
  • 直觉:真实电机的力矩输出是非线性动态系统,理想 PD 是线性静态模型。
  • 数学抓手\(\tau^{net}=\text{MLP}(q^d_{-H:0}, q_{-H:0}, \dot{q}_{-H:0})\)
  • 工程接口:真机数据采集 → MLP 训练 → 替换仿真中的 PD 模型
  • 易错判断:激励信号不够多样会导致网络在分布外预测不可靠。
  • 自测动作:对比执行器网络和理想 PD 在不同频率激励下的力矩预测误差。

卡片 05:接触模式条件化

  • 问题:为何连续残差在接触切换处失败?
  • 直觉:接触触发让转移函数不连续,连续网络学到的是平均值,两种模式下都不准。
  • 数学抓手\(\Delta(s,a,h,c)=\sum_m 1[c=m]\Delta_m(s,a,h)\)
  • 工程接口:足底力传感器提供接触标记 \(c\)
  • 易错判断:不做模式分段,统一模型在接触切换点误差会急剧增大。
  • 自测动作:按接触相位统计预测误差并画图。

卡片 06:部署丢弃决策

  • 问题:什么时候能丢弃残差?
  • 直觉:如果微调充分,策略参数已经编码了补偿;如果高冲击动作的补偿幅度大,策略可能吸收不完。
  • 数学抓手:丢弃后误差增加 < 10% → 可丢弃
  • 工程接口:保留/丢弃 A/B 对比测试
  • 易错判断:高冲击接触场景(跳跃、快速转向)最容易在丢弃后退化。
  • 自测动作:做 A/B 对比,分别在慢走和快跑动作上评估。

延伸阅读

资料 难度 内容
He et al., ASAP (arXiv:2502.01143, RSS 2025) ⭐⭐⭐⭐ 本章核心论文,delta-action 方法
Hwangbo et al., Science Robotics 2019 ⭐⭐⭐ 执行器网络的原始论文
Kumar et al., RMA (arXiv:2107.04034, RSS 2021) ⭐⭐⭐ 在线适应方法,与 ASAP 互补
Luo et al., PHC (ICCV 2023) ⭐⭐⭐⭐ 持续动作跟踪,Stage 1 的强基线
He et al., HOVER (2024) ⭐⭐⭐ 统一人形控制器,多技能策略
Cheng et al., ExBody2 (2025) ⭐⭐⭐ 全身表现力控制
LeCAR-Lab/ASAP GitHub 仓库 ⭐⭐ 代码实现和配置参考
Shi, "Sim2Real 1.0 to 4.0" 知乎技术分享 ⭐⭐ Sim-to-real 技术演进的中文综述
He, "面向人形机器人的 Visual Sim-to-Real 框架" 知乎 ⭐⭐⭐ VIRAL 框架的中文解读
He, "Learning Humanoid Control from Simulation to Real to Simulation" CMU MSR Thesis, 2025 ⭐⭐⭐⭐ ASAP 第一作者的硕士论文,包含完整技术细节
TRANSIC: Sim-to-Real Policy Transfer (NeurIPS 2024) ⭐⭐⭐ 人类修正数据的残差策略学习
Walk These Ways (Margolis et al. 2023) ⭐⭐⭐ 四足 sim-to-real 的完整工程流程参考