当前位置: 首页 > news >正文

EXTREME-PARKOUR项目学习记录

这篇文章尝试从代码实现的角度,系统梳理这个四足极限跑酷项目的整体结构、数据流动、训练过程、奖励函数与域随机化设计。与其把它理解为一个“单纯用深度图控制四足机器人”的项目,不如更准确地说:它是一套先利用仿真特权信息训练强教师策略,再逐步蒸馏到可部署视觉策略的两阶段框架。

项目的主线可以压缩成一句话:训练时,完整的753维观测会被拆成多路,分别经过scan_encoder / priv_encoder / history_encoder / depth_encoder / estimator等模块,最后被压缩到一个114维动作决策输入;部署时,则只保留可感知、可估计的那部分信息,由depth_actor输出最终的12维动作。

1. 项目总览:从 753 维训练观测到 114 维动作输入

在训练阶段,环境完整观测的拼接顺序是:

obs = proprio(53) + scan(132) + priv_explicit(9) + priv_latent_raw(29) + history(10x53=530) = 753

其中:

  • proprio(53):本体感觉,包括机身角速度、姿态相关量、yaw 误差、速度命令、关节位置、关节速度、上一时刻动作、足端接触等。
  • scan(132):机器人前方地形高度采样,也就是所谓的地形扫描特权信息。
  • priv_explicit(9):显式特权信息。当前代码中这 9 维里真正有信息的主要是前 3 维机身线速度,后 6 维基本是占位。
  • priv_latent_raw(29):隐藏参数真值,包括质量参数、摩擦系数和电机强度等域随机化变量。
  • history(10x53):最近 10 帧本体感觉历史。

虽然训练时观测是 753 维,但 actor 真正决策时并不直接使用全部原始维度,而是将不同来源的信息编码成低维表示,最终拼成:

114 = proprio(53) + terrain_latent(32) + priv_explicit(9) + latent(20)

部署阶段更进一步,不再输入 753 维完整观测,而是直接构造这 114 维输入。

2. 模块关系:每个编码器到底在做什么

整个项目最容易混淆的地方,是不同 encoder 的职责边界。实际上它们分工非常明确。

2.1scan_encoder:把地形扫描压成低维地形特征

scan_encoder接收obs_scan(132),输出scan_latent(32)
它的作用是把高维地形高度采样压缩成策略可用的低维地形表示。这个模块没有显式监督标签,而是作为 actor 的一部分,通过 PPO 端到端学习。换句话说,网络会自动找到“怎样的 32 维地形表示最有利于产生高回报动作”。

2.2priv_encoder:把隐藏参数真值编码成适应 latent

priv_encoder接收priv_latent_raw(29),输出priv_latent(20)
这里的 29 维并不是地形,而是仿真中的隐藏参数真值,包括:

  • 质量参数 4 维
  • 摩擦系数 1 维
  • 电机强度 12 维
  • 电机强度 12 维

这个模块也不是监督学习,而是把这些原始隐藏参数编码成策略可用的 20 维低维表示。它一方面通过 PPO 梯度更新,另一方面还会受到priv_reg_loss的额外约束。

2.3history_encoder:用历史本体感觉恢复隐藏信息

history_encoder接收最近10proprio(10x53),输出hist_latent(20)
它学的不是地形,也不是动作,而是试图从历史运动信息中恢复出与priv_latent对齐的低维隐藏参数表示。可以把它理解为“部署时用来代替隐藏真值的一条适应分支”。

需要特别强调的是:
history_encoder的目标不是拟合priv_latent_raw(29),而是拟合经过priv_encoder压缩后的priv_latent(20)

2.4estimator:从本体感觉预测显式特权信息

estimator接收proprio(53),输出priv_explicit_est(9)
它是标准监督学习模块,利用obs中环境直接提供的priv_explicit_gt(9)作为标签,学习从本体感觉估计显式特权量。

这里有一个实现细节非常重要:虽然写成了 9 维,但当前代码里真正有物理意义的主要是前 3 维线速度,后 6 维基本是零占位。因此从语义上看,这一路本质上更接近“线速度估计器”。

2.5depth_encoder:从深度图提取视觉地形特征与 yaw

depth_encoder接收一张58x87的深度图和一个被抹去 yaw 相关量的proprio(53),输出:

  • depth_latent(32):视觉版地形特征
  • pred_yaw(2):预测出来的 yaw 相关量

它的作用是用深度视觉去替代原本依赖扫描得到的scan_latent,同时补回被屏蔽掉的 yaw 信息。

2.6actordepth_actor:同一个主干,两种地形特征来源

actor是教师策略,depth_actor是学生策略。二者在结构上并没有本质区别,depth_actor本身就是actor的拷贝。它们的唯一区别在于输入中的 32 维地形特征来自哪里:

  • actor使用scan_encoder(obs_scan)得到的scan_latent
  • depth_actor使用depth_encoder(depth)得到的depth_latent

因此,depth_actor并不是“多了一个视觉 head 的 actor”,而是同一个 actor 主干,只是地形特征来源从扫描换成了视觉

3. 数据流动:训练与部署时信息是怎么传的

3.1 第一阶段:Teacher RL

第一阶段的核心目标,是在拥有特权信息的条件下先训练出一个强教师策略。

数据流大致如下:

  • obs_scan(132)经过scan_encoder得到scan_latent(32)
  • proprio(53)经过estimator得到priv_explicit_est(9)
  • priv_latent_raw(29)经过priv_encoder得到priv_latent(20)
  • 最近 10 帧proprio经过history_encoder得到hist_latent(20)

之后 actor 接收:

proprio(53) + scan_latent(32) + priv_explicit_est(9) + latent(20)

这里最后这个latent(20)并不是同时放入priv_latenthist_latent两份,而是同一个 20 维槽位在两者之间切换

  • 大多数时候是priv_latent
  • 做 adaptation / DAgger 更新时会切换成hist_latent

这一点很重要。它说明 actor 不是同时吃两个 20 维适应向量,而是在训练过程中逐步从“依赖真值隐变量”过渡到“依赖历史估计隐变量”。

3.2 第二阶段:Vision Distillation

第二阶段的目标,是用视觉分支取代扫描分支。

具体流程如下:

  • 输入一张58x87深度图
  • 再输入一个被 mask 掉 yaw 相关维度的proprio(53)
  • depth_encoder输出depth_latent(32)pred_yaw(2)
  • 再把pred_yaw(2)回填到被 mask 的 yaw 位置
  • 然后构造学生输入:

proprio(53) + depth_latent(32) + priv_explicit_gt(9) + hist_latent(20)

然后把它送进depth_actor,输出学生动作。

这里有两个非常关键的实现细节:

第一,第二阶段训练时,depth_actor用的是priv_explicit_gt,而不是estimator的预测值。这意味着第二阶段存在一个轻微的 train-deploy mismatch:训练时学生还在“偷看”显式特权真值,而部署时必须改成 estimator 输出。

第二,代码里虽然保留了“让depth_latent直接拟合scan_latent”的 latent 对齐接口,但在当前主流程里这一项实际上没有启用。真正生效的是:

  • depth_actor_loss = || action_teacher_mean - action_student_mean ||
  • yaw_loss = || yaw_teacher - yaw_student ||

因此第二阶段本质上做的不是显式 latent 回归,而是:

  • 用视觉特征替代扫描特征
  • 用行为蒸馏让学生模仿教师动作
  • 用 yaw 监督帮助视觉分支恢复方向相关信息

3.3 第三阶段:Deployment / JIT

部署时真正运行的是:

  • depth_encoder提供depth_latent
  • estimator提供priv_explicit_est
  • history_encoder提供hist_latent
  • depth_actor输出最终 12 维动作

所以部署态真正依赖的是:

proprio + depth_latent + priv_explicit_est + hist_latent

这一点非常能体现整个项目的设计思想:训练时充分利用仿真里的特权信息,部署时则尽可能只依赖真实可得的感知和历史状态。

4. 梯度更新:每个模块到底怎么学

4.1 第一阶段:强化学习与辅助监督共同进行

第一阶段并不是单纯的 PPO,而是“PPO 主目标 + 多个辅助学习目标”共同作用。

estimator是最标准的监督学习模块。它用proprio(53)预测priv_explicit_gt(9),损失是预测值和真值之间的均方误差。

history_encoder主要通过单独的update_dagger()更新,其目标是让hist_latent(20)拟合priv_latent(20)。这一步从损失形式上看是监督学习,但因为训练样本来自当前策略不断 rollout 出来的新状态,所以代码里采用了DAgger这个名字。这里的DAggerDataset Aggregation,中文常译为“数据聚合式模仿学习”。它和普通监督学习的区别不在于 loss,而在于训练数据不是固定离线数据,而是随着当前策略不断重新收集和聚合的。

actor / critic / scan_encoder主要通过 PPO 更新。具体包括:

  • 策略截断损失
  • 价值函数损失
  • 熵正则项

priv_encoder稍微特殊。它不直接拟合某个真值标签,而是把priv_latent_raw(29)编码成策略可用的priv_latent(20)。它一方面会受到 PPO 梯度更新,另一方面还会受到priv_reg_loss的共同塑形。

这里必须把priv_reg_loss说清楚,因为这是整个项目里很容易被理解反的地方。
priv_reg_loss发生在 PPO 的更新过程中,本质上是一个 latent 对齐正则。代码实现时,hist_latentdetach()成常量,梯度只会回到priv_latent这一支,也就是说:

  • 它不是“把 history_encoder 拉向 priv_encoder”
  • 而是“固定 hist_latent,把 priv_latent 往 hist_latent 的方向拉近”

这样做的目的是缩小训练时 privileged 分支和未来部署时 history 分支之间的表示差距,避免 actor 只适应“带真值隐藏参数”的输入分布,而在切换到历史估计 latent 时性能骤降。

4.2 第二阶段:行为蒸馏 + yaw 监督

第二阶段开始时,会先复制教师 actor,初始化出depth_actor。之后,教师 actor 在这一阶段只是作为固定老师使用:

  • 它负责产生教师动作
  • 不参与反向传播
  • 不更新参数

学生侧则由depth_actor + depth_encoder组成。训练目标有两个:

  • depth_actor_loss = || action_teacher_mean - action_student_mean ||
  • yaw_loss = || yaw_teacher - yaw_student ||

这里的depth_actor_loss比较的是 teacher 和 student 的确定性动作输出,而不是 PPO 中从高斯分布里 sample 出来的随机动作。teacher 和 student 看到的是同一时刻、同一环境状态,但两者的地形信息来源不同:

  • teacher 用真实扫描特征scan_latent
  • student 用视觉替代特征depth_latent

这就是典型的行为蒸馏。

最终第二阶段真正被联合更新的是:

  • depth_actor
  • depth_encoder

而不是教师 actor。

5. 奖励函数:这个项目到底在鼓励什么

从代码实现上看,奖励项虽然很多,但完全可以归纳成四类。

5.1 任务跟踪类奖励

这一类奖励直接决定机器人是否在“完成跑酷任务”。

最核心的两个项是:

  • tracking_goal_vel
  • tracking_yaw

tracking_goal_vel鼓励机器人沿着当前目标点方向移动。
tracking_yaw鼓励机器人让自身朝向对准目标方向。

这两项构成了最直接的任务驱动力:既要朝目标走,也要朝向目标。

5.2 机体稳定类奖励

这一类奖励负责限制身体姿态过于激烈、避免翻车。

主要包括:

  • lin_vel_z:惩罚 z 向速度过大,抑制过激跳动
  • ang_vel_xy:惩罚滚转和俯仰角速度过大
  • orientation:约束身体姿态保持合理

它们的作用是告诉机器人:可以高速跑、可以跳跃,但不能把身体搞成失控状态。

5.3 能耗与动作平滑类奖励

如果没有这类正则,策略很容易学出“能跑但很暴力”的动作模式。

主要包括:

  • torques:惩罚扭矩过大
  • delta_torques:惩罚相邻时刻扭矩变化过快
  • action_rate:惩罚动作变化过快
  • dof_acc:惩罚关节加速度过大
  • hip_posdof_error:鼓励关节不要偏离默认姿态太远

这一类奖励的本质,是让机器人不仅要跑得快,还要跑得像一个真正可控、可执行的机器人。

5.4 跑酷安全与地形交互类奖励

跑酷任务对接触安全要求更高,因此代码里还加入了几项专门和地形交互有关的惩罚。

主要包括:

  • collision:惩罚身体不该碰撞的部位发生接触
  • feet_stumble:惩罚脚撞到近似垂直障碍
  • feet_edge:惩罚脚踩在地形边缘

这些奖励的目标很明确:机器人不仅要过去,而且要“安全地过去”。

如果把整个奖励系统压缩成一句话,它鼓励的是:

  • 朝目标方向稳定前进
  • 保持合理朝向和姿态
  • 用平滑、低代价的动作完成运动
  • 避免危险碰撞和错误落脚

6. 域随机化:项目里到底随机了什么

本项目中的域随机化主要可以归纳成四类:动力学参数随机化、外部扰动随机化、执行器随机化和视觉相关随机化。除此之外,代码里还保留了一些可选的鲁棒性增强项,但当前默认配置下没有全部启用。

6.1 动力学参数随机化

这是最核心的一类随机化,主要包括:

  • 摩擦系数随机化
  • 机体质量随机化
  • 质心偏移随机化

摩擦系数随机化让不同环境实例落在不同摩擦条件下,从而提升策略对接触条件变化的适应能力。
机体质量随机化和质心偏移随机化,则用于模拟真实机器人质量建模误差、安装件变化、电池与传感器负载差异等问题。

这三项共同作用的结果,是让策略不要过拟合某一个精确动力学模型。

6.2 外部扰动随机化

项目中启用了随机推搡机制。实现上并不是施加真实的外力脉冲,而是每隔一段时间直接随机修改机器人 base 的水平速度。

虽然这种实现方式比较简化,但目的很明确:让机器人在训练中反复遭遇扰动,并学会从扰动中恢复。对跑酷任务来说,这一点尤其重要,因为真实世界中的落地误差、碰撞反弹和地形细节偏差,都可以近似理解为某种外部扰动。

6.3 执行器随机化

项目还对电机强度做了随机化,范围是[0.8, 1.2]。在控制实现里,这会影响 PD 控制中的刚度与阻尼通道,相当于让不同环境中的电机“强一点或弱一点”。

这项设计非常合理,因为现实中的执行器性能不可能永远和仿真标称值完全一致。通过训练时引入这一随机化,可以显著降低策略对理想执行器模型的依赖。

6.4 视觉相关随机化

对于视觉学生策略来说,传感器本身也不能过于理想。项目中对相机参数做了轻量级随机化,主要包括:

  • 相机俯仰角随机化
  • 水平视场角随机化
  • 可选的深度噪声

这类随机化的意义在于:现实相机的安装角度、视场和成像噪声都不可能与仿真完全一致。通过在训练中轻微扰动这些参数,可以减少视觉策略对某一套理想相机配置的过拟合。

6.5 尚未默认启用的可选随机化

除了已经启用的项,代码里还保留了:

  • 初始状态随机化
  • 动作延迟随机化
  • 观测噪声
  • 地形测量噪声

这些更像是“鲁棒性工具箱”。当前主配置没有全部打开,但从工程角度看,它们为进一步增强 sim-to-real 泛化提供了扩展空间。

7. 这个项目最值得注意的实现细节

最后总结几条最容易混淆、但也最值得读代码时记住的点。

第一,history_encoder学的不是地形,而是隐藏参数的低维适应表示。
第二,depth_actor不是一个全新网络,而是教师 actor 的结构拷贝,区别只在于地形特征来源从扫描切换成了视觉。
第三,第二阶段并没有真正启用scan_latent -> depth_latent的显式 latent 对齐损失,真正生效的是动作蒸馏和 yaw 监督。
第四,训练与部署之间存在一个小的不一致:第二阶段蒸馏时学生仍然使用priv_explicit_gt,而部署时则必须使用priv_explicit_est

8. 总结

从代码角度看,这个项目的设计并不是“直接把深度图喂进 actor”这么简单,而是一套很清晰的逐步替换流程:

  • 第一阶段先借助仿真特权信息训练强教师策略
  • 同时训练 estimator 和 history adaptation 分支
  • 第二阶段再用视觉特征去替代扫描特征,用行为蒸馏训练学生策略
  • 最终部署时只保留真实可得的信息:proprio + depth + history

这套框架最有价值的地方,在于它非常务实地利用了仿真的优势:第一阶段先利用地形特权信息、显式特权信息和隐式特权信息训练出强教师策略,同时让历史编码器学习如何用历史本体信息去逼近隐式特权表示;第二阶段再用深度图提取的视觉地形特征替代原来的地形扫描特征,并结合本体信息、显式信息和历史信息训练学生策略,最终逼近真实部署条件。
对高动态四足跑酷这种任务来说,这种“先在仿真里把问题学明白,再向现实约束收缩”的思路,往往比一开始就强行追求纯端到端部署输入,更容易做出真正可用的系统。

http://www.jsqmd.com/news/637798/

相关文章:

  • 动手学深度学习——样式迁移
  • 2026年特级四明山绿茶礼盒/四明山春茶绿茶/春季四明山绿茶/四明山绿茶早芽稳定供货厂家推荐 - 品牌宣传支持者
  • AI写的AI写小说软件
  • Z-Image-Turbo_Sugar脸部Lora部署避坑:CUDA版本冲突与xinference兼容性解决方案
  • 深度学习模型演进:6个里程碑式CNN架构
  • Guohua Diffusion 企业级应用:基于卷积神经网络的风格迁移系统
  • Agent开发中的LangChain组件:Chain与Agent的关系
  • AIAgent记忆泄漏正在 silently 拖垮你的O1推理成本——从Python GC钩子到WASM沙箱隔离的3层防御体系
  • IgH EtherCAT 从入门到精通:第 2 章 环境搭建与编译安装
  • 动手学深度学习——样式迁移代码
  • 推荐1款家庭库存管理软件,建议收藏使用!
  • 万象视界灵坛实操手册:图像预处理Pipeline(Resize/Crop/Normalize)对齐CLIP标准
  • 可靠性如何嵌入产品开发流程
  • 忍者像素绘卷开源可部署:支持国产操作系统(OpenEuler)的兼容方案
  • AIAgent目标分解到底难在哪?5大认知陷阱正在拖垮你的智能体落地进度
  • unifolm-vla的数据训练recipe统计
  • Langchain .. 学习 --- LCEL和Runnable劳
  • DAMO-YOLO TinyNAS保姆级教学:EagleEye日志分析、错误排查与常见报错解决方案
  • 仿真模拟电击穿路径的模型:自定义形状、有限元Comsol相场法及PDE模块应用
  • 新能源极耳裁切产线:西门子S7-1500 PLC与基恩士变频器EtherNet/IP协议转换应用
  • 负载箱的故障模式与工程局限:从理想模型到现实约束的技术反思
  • 协议层延迟骤增87%?揭秘AIAgent微服务间通信协议设计的4层降本增效架构实践,今天不看明天宕机
  • 以前我背的是字母,现在才像是在真正记单词
  • DeerFlow PPT自动生成:研究报告一键转换为演示文稿
  • 国企行政筹办正式会议,标准国企会议纪要撰写权威指南
  • 像素语言·维度裂变器:5分钟上手,让AI帮你一键改写平庸文案
  • Phi-4-mini-reasoning企业实操:金融风控规则推理引擎构建案例
  • AI头像生成器保姆级教程:中文描述转Midjourney V6可用Prompt全解析
  • SpringBoot 应用启动流程:从启动到 Web 容器初始化
  • 【工业级AIAgent仿真底座】:基于Docker+Kubernetes+gymnasium的可复现、可审计、可压测环境搭建全链路