面试经验

阅读口径:蓝色内容是面试表达建议,不等同于论文原文;具体实现以对应论文和官方代码为准。公式按当前 DDPM / PPO 语境理解,换用其他采样器或实现时不要直接套用。
用法:把面试里遇到的问题丢给我,我们重点挑出「没答上来的」,补上正确答案并总结薄弱点 · 更新方式:直接编辑本 HTML,或转给 Reasonix 更新

📊 概览

技术

7. GMR:从人体目标到机器人动作,约束从哪里来、怎么设计、如何判定

🟠 待补充 · 原理需脱稿复述

一句话总纲

GMR 的本质不是“把人体关节角复制给机器人”,而是:先把人体动作转换成机器人可实现的任务空间目标,再在机器人自身的配置空间里求一个满足关节、闭环和接触几何约束的姿态。每一帧都在解一个局部运动学问题;首尾过渡和足底清理是在此基础上增加的时序后处理。

完整流程(记住这条主线即可)

  1. 输入解析:读取 SMPL-X、BVH/Xsens、FBX、GVHMR 等人体动作,统一成逐帧人体关键点。
  2. 标定与配置:确定人体语义点和机器人 body 的映射,准备区域尺度、局部旋转偏移、地面和脚底 marker。
  3. 源动作预处理:统一 yaw/heading,必要时做膝盖弯曲和高度修正,避免坐标系或初始姿态直接把 IK 带偏。
  4. 逐帧 GMR IK:以人体关键点为目标,结合机器人默认站姿、关节范围和模型约束,求每帧机器人 qpos。
  5. 首尾过渡:从可控站姿平滑接入原动作;识别支撑脚,规划摆脚和 capture,并在结尾回到可接入状态。
  6. 可选接触清理:只在中段存在明显脚滑时锁定接触脚,保护已生成的首尾过渡,不默认二次求解。
  7. 验证与输出:检查关节限位、闭环、足底高度、支撑脚漂移、速度/加速度和 metadata,再输出动作与诊断结果。

人体动作 → 标定配置 → 坐标/姿态预处理 → 全身 IK → 首尾过渡 → 可选锁脚清理 → 验证输出

相对原始 GMR:我做了哪些改进,大概怎么做

原始 GMR 的核心是“人体关键点目标 + 单帧全身 IK”,重点解决人体姿态到机器人姿态的几何跟踪。我的改动没有替换这个核心求解器,而是补齐了换机器人、换数据源和输出动作可接入时缺失的环节。

改进方向解决的问题大概做法
配置与标定自动化原来每个机器人/数据源都要手写映射、scale 和 offset,容易左右反、坐标系不一致。从自然站立帧和 MJCF 结构提取语义参考;在 root-relative、归一化坐标中匹配人体点与 robot body;按身体区域估计 scale,生成映射、rot_offset、任务权重和接触配置。
朝向、初始姿态与源预处理源数据朝向不同、第一帧姿态不合理时,IK 会从错误状态大幅修正,导致启动抖动。把全局 yaw 和局部 rot_offset 分开;支持手动/自动/reference yaw;从默认站姿 JSON、MuJoCo keyframe 或显式 qpos 初始化;必要时预先修正人体膝盖和整体高度。
首尾接入过渡逐帧 IK 的第一帧/最后一帧不一定是稳定站姿,动作片段不能直接部署或拼接。从默认站姿出发,依据起止帧的脚接触状态安排双支撑、摆脚、落脚和 capture 阶段;每个采样点用 QP 在足底、闭环、关节限位约束下投影到可行配置;结束时用时间反向规划回站姿。
接触清理与滑脚修复原动作中可能出现“脚已接触但 XY 持续漂移”,直接低通平滑会破坏接触和过渡边界。仅在中段检测连续接触片段;为接触脚设固定位置目标(median 或边界锚点),再用局部 IK 修正;冻结站立、过渡和动作边界,避免后处理破坏上游结果。
诊断与可复现输出以前主要靠视频判断好坏,出问题难以判断是映射、接触、限位还是接缝造成的。在 PKL metadata 中记录配置、yaw、默认姿态、原动作区间、接触段、滑移、几何检查和 temporal warning;用关节限位、闭环、穿地、支撑漂移和接缝指标做结构化验收。

面试概括:我保留原始 GMR 的全身 IK 核心,把它从“单帧几何重定向”扩展成“配置生成、坐标统一、接触感知过渡、局部滑脚修复和可解释诊断”的动作生成流水线。

完整原理:四个空间要分清

  1. 人体观测空间:来自 SMPL-X、BVH、Xsens、FBX 等数据源的关键点位置/方向,记为 x_h(t)。
  2. 任务空间:选定人体语义部位与机器人 body 的对应关系,形成脚、手、头、骨盆等目标位姿 y*(t)。
  3. 机器人配置空间:机器人自由度向量 q,包括 root、hinge、ball、slide 等关节。正运动学把它映射到任务空间:y = f(q)。
  4. 可行域:由关节范围、闭环 equality、足底与地面关系、接触锁定等条件共同定义。求解不是任意追踪,而是在可行域附近找最接近目标的 q。
\(q^* = \arg\min_q \; \|f(q)-y^*\|_W^2 + \lambda\,\|q\ominus q_{prev}\|^2,\quad q\in\mathcal{F}\)

这里的 q ⊖ q_prev 是配置流形上的相对位移,不是简单逐元素相减;它能正确处理 free joint 的平移/旋转和角度环绕。

1)原始 GMR 的 IK 约束是怎么来的?

约束来源有两类:机器人模型自带的硬约束,以及动作语义带来的任务约束。

约束/目标来源数学形式判定条件
末端位置/姿态人体关键点映射与 IK 配置e = y* - f(q),用 Jacobian 线性化加权位置/旋转误差下降到设定范围
关节限位MJCF 的 jnt_rangeq_min ≤ q ≤ q_max不得越界;增量也受上下界限制
闭环/equalityMuJoCo equality/connect 等模型约束J_eq Δq ≈ -r_eq残差不超过 endpoint-derived tolerance
足底不穿地脚底 contact marker/sole geometry 与 ground heightz_sole ≥ z_ground - ε所有采样点都不能超过允许穿透量
支撑脚锁定接触阶段的运动学假设J_foot Δq = e_foot支撑脚位置/姿态误差足够小,漂移不超阈值
平滑/连续上一帧或邻域姿态惩罚 ||Δq||² 或邻帧差异速度、加速度、接缝误差不超验收阈值

关键点是:目标不是约束,约束是可行性边界。例如“手到目标位置”通常是带权软任务;“关节不能越界、闭环不能断、脚不能穿地”才是硬边界或高优先级约束。

2)为什么需要 Jacobian?局部 IK 的推导

对当前配置 q 做一阶展开:

\(f(q+\Delta q) \approx f(q)+J(q)\Delta q\)

所以任务误差 e = y* - f(q) 会被近似为 JΔq ≈ e。位置 Jacobian 描述“哪个关节动一点,末端位置怎么变”;角速度 Jacobian 描述“哪个关节动一点,末端朝向怎么变”。Mink/MuJoCo 负责计算这些局部导数,QP 负责在多个目标互相冲突时选一个折中增量。

典型的二次目标是:

\(\min_{\Delta q}\; \frac12\Delta q^T H\Delta q + g^T\Delta q\)

其中 H 来自各任务 Jacobian 的 JᵀWJ 累加,g 来自 -JᵀWe。阻尼项让接近奇异位形时仍然可解,平滑项让结果不要在相邻帧间跳动。

3)接触判定:为什么是“高度 AND 速度”?

\(contact_s(t) = [h_s(t) \le h_{ground}+h_{th}] \land [\|v_s(t)\| \le v_{th}]\)
  • 高度条件回答“脚是否在地面附近”;只看高度会把快速掠过地面的摆脚误认为接触。
  • 速度条件回答“脚是否相对稳定”;只看速度会把悬空停住的脚误认为支撑。
  • sole points而不是单个 ankle 点:脚底有面积,取 heel/center/toe 等 marker 的最低世界 Z,避免脚踝高度不能代表鞋底高度。
  • 速度计算用脚底点的 Jacobian 乘关节速度,包含脚部旋转造成的点速度,不只是 body origin 的速度。

代码中的判定通过条件是:每只脚的最低 sole 点不高于 ground + contact_height,且脚底点速度范数不高于 contact_speed。这仍是几何启发式接触检测,不是力传感器或动力学接触判定。

4)首尾过渡中的约束设计:先决定接触状态,再决定谁能动

  1. 读取起始帧和目标帧的脚接触状态 c0/c1。
  2. 找出脚位置/朝向变化超过阈值,或接触状态发生变化的脚,作为 moving foot。
  3. 规定一只脚摆动时另一只脚必须处于有效支撑状态;如果两脚都没有支撑,直接失败,因为当前规划器不是动力学腾空规划器。
  4. 在 weight transfer 阶段逐渐转移目标,在 swing 阶段给摆脚加 clearance,在 capture 阶段把落脚姿态接回原动作。

因此“约束怎么来的”不是拍脑袋:它来自双足运动的接触拓扑。脚一旦被视为支撑点,就必须限制其相对地面的位置/姿态;另一只脚才被允许沿摆脚轨迹运动。

5)QP 投影到底约束了什么?

  • 支撑脚等式:锁定脚时把位置和旋转误差写成等式,要求 J_foot Δq = e_foot。
  • 足底不等式:对每个 sole point 写成 -J_z Δq ≤ z - ground + floor_allow,从一阶近似上阻止下一步穿地。
  • 闭环边界:读取 MuJoCo 当前 equality 残差,并限制线性化后的残差不能超过允许范围;同时用残差平方加权,让优化主动修复闭环。
  • 关节增量边界:根据当前 q 到 q_min/q_max 的距离限制 Δq,而不是等求解后越界再裁剪。
  • CoM 引导:在摆脚期间用支撑脚附近的水平目标给 subtree CoM 一个软引导;它是软项,不覆盖 root 坐标,也不等价于动力学稳定性。

硬约束和软目标冲突时,优先级是“先可行,再跟踪”:宁可牺牲一点人体末端误差,也不应该让闭环断开、脚穿地或关节越界。

6)最终判定条件:哪些是硬失败,哪些只是 warning?

硬失败

边界帧超关节限位;找不到起止支撑脚;QP 无可行解;非有限值;足底穿地超过容差;闭环残差超过阈值;支撑脚漂移或旋转误差超过阈值;最终速度/加速度超过配置上限。

诊断 warning

动作本身速度很快、某段加速度尖峰、cleanup 后仍有较小滑移。它们需要记录和人工/控制器复核,但不应因为动作语义本来就快而被一刀切拒绝。

过渡阶段的典型验收包括:最大支撑脚漂移、最大支撑脚旋转误差、最大足底穿透、最大 equality 残差、最大关节速度和最大关节加速度。cleanup 阶段则重点看接触段 XY 滑移是否下降,同时冻结已生成的首尾过渡区,避免“修中间、坏边界”。

7)标定和尺度为什么也是技术原理,而不只是配置文件?

人体和机器人不是同构骨架,不能把同名关节直接复制。标定先在 root-relative、统一朝向和身高归一化后比较语义点,再综合拓扑深度、语义名称和几何距离匹配 body;尺度按躯干、上臂、前臂、大腿、小腿等区域估计,并对左右对称部位做一致化。全局 yaw 描述动作整体朝向,局部 rot_offset 描述两个 body 局部坐标系差异,二者分离后配置才能跨动作复用。

8)面试时最准确的边界表述

当前系统是“配置驱动的运动学重定向与接触感知后处理”。它能保证或检查几何层面的关节范围、闭环、足底不穿地、支撑脚锁定和时序连续性;但它没有接触力、摩擦锥、关节力矩或全身动力学可行性保证。因此“脚不穿地”不能说成“机器人一定能稳定走”。

对应工程模块包括:general_motion_retargeting/motion_retarget.py、scripts/calibrate_new_robot/、scripts/optimize_retargeted_data/contact_transition.py 和 contact_cleanup_motion.py。

📝 面试问题与答案

技术

1. 手眼标定(Hand-Eye Calibration)

🔴 没答上 · 🟡 已理清(待脱稿复述)

先分清「标定」到底在标什么(三件事,别混)

  • ① 相机内参标定(只标相机本身):标的是内参矩阵 M1 = [fx, fy, cx, cy] + 畸变系数(径向 k1/k2/k3、切向 p1/p2)。
    解决的问题:像素坐标 (u,v) ↔ 相机坐标系下的 3D 点 的对应关系(透视投影)。
    特点:只跟相机硬件有关,出厂后固定不变(焦距、镜头不动就终身有效),与机械臂无关,不需要机器人参与。工具:cv::calibrateCamera / MATLAB Camera Calibrator,张正友棋盘格法。
  • ② 相机外参标定(一般语境):标的是相机坐标系 → 某个「世界坐标系」的变换 (R, t)。
    ⚠️ 关键:这里的「世界坐标系」是相对概念——用棋盘格标定时,世界坐标系就是棋盘格自身的坐标系,所以每换一个拍摄位姿,外参就不同(这也是「标定板每摆一次就产生一组外参」的原因)。
    表达式:\(P_{\mathrm{cam}} = R\cdot P_{\mathrm{world}} + t\),即 M2。内参 + 外参合起来才构成投影矩阵 M,才能把世界点投到像素上。
  • ③ 手眼标定(机器人视觉专有):标的是相机坐标系 ↔ 机械臂坐标系之间那个固定不变的刚体变换 X ∈ SE(3),数学模型统一为 \(AX = XB\)。
    特点:与「世界坐标系」无关——它不涉及标定板在哪,只求相机和机器人之间那个装上去就定了的位姿。除非相机支架松动 / 换了 TCP,否则终身不变。
  • 一句话关系:手眼标定是外参标定在「机器人视觉」场景下的特例——把外参里的「世界坐标系」替换成机械臂基坐标系,并要求那个变换是不随机械臂运动而变的常量。

「外参 = 相机相对 base」——对,但要看你问的是哪种手眼配置

  • Eye-to-Hand(眼在手外,相机固定):✅ 外参直接就是相机 → 基座,即 base_H_cam。标准做法:末端带标定板摆多个位姿、相机固定拍,用 \({}^{\mathrm{base}}H_{\mathrm{cam}} = {}^{\mathrm{base}}H_{\mathrm{tool}} \cdot {}^{\mathrm{tool}}H_{\mathrm{obj}} \cdot {}^{\mathrm{obj}}H_{\mathrm{cam}}\) 解出。
    —— 你的场景(眼不动手动)就属于这一类,所以你说的「相机相对 base 的外参」完全正确。
  • Eye-in-Hand(眼在手上,相机随臂动):⚠️ 手眼标定直接解出的不变量是「相机 → 末端」,而不是「相机 → 基座」。因为相机跟着臂动,相机相对基座的位姿每时每刻都在变,它不是一个常量、不能直接标。
    要拿到「相机 → 基座」需要再乘一条链:\({}^{\mathrm{base}}H_{\mathrm{cam}} = {}^{\mathrm{base}}H_{\mathrm{tool}} \cdot {}^{\mathrm{tool}}H_{\mathrm{cam}}\),其中 base_H_tool 由正运动学实时给出。
    所以严格讲:手眼标定的产物是「相机 ↔ 末端」(EiH)或「相机 ↔ 基座」(EtH);「相机 → 基座」在 EiH 下是「标定结果 + 正运动学」的复合量,不是标定直出。
  • 口试时的稳妥说法:「外参是相机相对参考坐标系(通常是基座或世界系)的变换;在手眼标定里,EiH 标的是相机到末端,EtH 标的是相机到基座——最终都是为了把相机系下的点位换算到基座系下给机械臂用。」

为什么必须「先内参、后手眼」(顺序有依赖)

  • 手眼标定里,B 矩阵(相机侧两次运动的相对位姿)由 PnP 解算,而 PnP 必须用内参 + 畸变系数。
  • 内参不准 ⟹ 外参(手眼结果)直接无效,而且这种误差会「混进去」很难事后发现。
  • 所以标准流程是:先标内参(棋盘格多姿态)→ 再标手眼 → 最后验证。

一句话定义

求相机与机械臂之间那个固定不变的外参矩阵,让「相机看到的点」能换算成「机械臂基座系里的点」。抽象地说就是解一个 AX = XB 的矩阵方程。

先分清配置(我被问的是「眼不动手动」)

  • Eye-in-Hand 眼在手上:相机装在末端、随臂动,标定时臂带着相机从多个位姿去看固定的标定板。
  • Eye-to-Hand 眼在手外:相机固定在工作环境里,标定时末端带着标定板移动、相机固定观察。
  • 我的场景是「眼不动手动」——相机固定、臂动,所以它属于 Eye-to-Hand 这一类,求的是相机系 → 机械臂基座系的变换。

核心方程 AX = XB(两种配置数学形式统一)

  • A:机械臂末端两次位姿之间的相对运动(由正运动学 / 关节编码器读出,即 inv(H1)·H2)。
  • B:相机视角下两次之间的相对运动(由拍摄标定板、PnP 解出的外参算出,即 T21·inv(T22))。
  • X:待求的手眼变换 —— EiH 下是「相机 → 末端」,EtH 下是「相机 → 基座」。
  • 注意:这里每张照片的「外参」都是相机相对该时刻标定板的。正因为标定板相对某一侧是固定的,两次相减才能把「会动的量」消掉,只剩下那个不变的 X。

外参矩阵拆成两件事(我自己总结的表述,面试时可以直接这么说)

  • R,算「坐标系定义差距」:相机定义的 X/Y/Z 和基座定义的 X/Y/Z 指向不一样 → 用旋转矩阵 R 描述「该怎么转方向」。靠多角度拍标定板解算(本质是 PnP / 标定板位姿估计)。
  • t,算「位置差距」:即使轴方向对齐了,两个原点还是不重合 → 用平移向量 t 描述「该往哪走多远」。
  • 完整外参 = R(方向)+ t(位置),也就是 \(P_{\mathrm{base}} = R \cdot P_{\mathrm{camera}} + t\)。

坐标系链路(把「内参 / 外参 / 手眼」串成一条链,最好记)

像素 (u,v) ⟶内参⟶ 相机系 3D ⟶外参 / 手眼⟶ 世界系 or 基座系

  • 内参管链条前半段(像素 ↔ 相机系);外参管后半段(相机系 ↔ 世界系 / 基座系)。
  • 所以「只知道内参」只能做畸变校正、图像测量;要控制机械臂去抓,必须有外参 / 手眼结果,把像素一路换算到基座系。
  • 这也解释了为什么只标内参不需要机器人,而手眼标定必须有机器人参与。

「让末端去指标定板角点」到底在干什么

  • 这不是在解 R,而是在解 t(或验证精度)。算法只能从多个姿态解出相对的方向关系,但算不出「相机原点落在基座系的哪个位置」。
  • 做法:拿同一个物理点(标定板角点),分别用两个坐标系去描述它——
    • P_camera:相机识别该角点,得到它在相机系下的 3D 坐标;
    • P_base:末端针尖触碰该角点,读机器人控制器里末端在基座系下的坐标;
    代入 \(t = P_{\mathrm{base}} - R \cdot P_{\mathrm{camera}}\),就反推出原点偏移。
  • 前提(易翻车):末端针尖的 TCP 必须先标定准。TCP 有偏差,触碰得到的 P_base 就是歪的,t 会带一个固定偏差,抓取时表现为固定的位置偏移。

P_camera 的 Zc(深度)从哪来 —— 单目 vs 深度相机

  • 有深度传感器(结构光 / 双目):直接查深度图得到该像素的 Zc,再用针孔模型反算: \(Xc = (u - cx)\cdot Zc / fx\),\(Yc = (v - cy)\cdot Zc / fy\)。
  • 只有单目:没有深度图,靠一块已知物理尺寸的标定板,用 PnP(Perspective-n-Point) 解出标定板位姿,其中就包含 Zc。

PnP(四点透视)原理 —— 小孔成像 + 相似三角形

  • 已知几个角点在标定板上的真实相对位置(如边长 W 的方格),又知道它们在图像里的像素坐标 → 反推标定板相对相机的位姿(6 自由度:3 旋转 + 3 平移)。
  • 「歪了多少」→ 反映旋转;「整体缩小了多少」→ 反映距离。
  • 为什么至少 4 个点:求 6 个自由度需要 ≥ 4 组不共线的 2D–3D 点对(P3P 有 4 解歧义,需要第 4 点消歧)。
  • 深度公式(要能推):\(W / Zc = w / f\) ⟹ \(Zc = (W \cdot f) / w\)。 W=格子的真实宽度(如 10mm)、w=它在像平面上的成像宽度、f=焦距;两边都是「底 ÷ 高」,共用同一个顶角(光心),所以比例必须一致。
    数值例:\(Zc = 10 \times 8 / 3 \approx 26.7mm\)。(工程实现里 f、w 直接用像素为单位算,省去 mm 换算。)
  • 单目的死穴 —— 尺度不可观测:单张图里没有绝对尺度。同样一张照片,把标定板当成放大 2 倍、同时距离也放大 2 倍,成像完全一样。所以必须有已知尺寸的靶标,尺寸输错 Zc 就等比例地错。

实践要点 / 踩坑

  • 采集:机械臂走多个位姿,每个位姿拍一张标定板(棋盘格 / ArUco)。
  • 位姿要 ≥ 3 个非平行的旋转轴、多采几组,否则方程退化、t 尤其不可辨识。
  • 精度相关:机器人绝对精度、相机预热 30 分钟以上减少温漂、采集时降速 + 加等待时间避免抖动。
  • 误差来源:相机内参误差、角点检测误差、机器人运动学误差、末端 TCP 标定误差。
  • 验证:看标定报告的残差;或在仿真里对比「虚拟 TCP 点」与「真实标定板中心」是否重合,一般要求 < 3mm(高精度 < 2mm)。
  • 经典解法(备着):Tsai-Lenz、Park-Martin、Daniilidis(对偶四元数)可同时解 R 和 t;工程上直接用 cv::calibrateHandEye。

🎯 面试加分句(背下来)

「单目 PnP 算深度本质是个比例问题:只给一张图、没有已知尺寸的靶标,深度是病态解,无法唯一确定。」

来源:本人面试复盘 + DeepSeek 追问记录(对话原文)

技术

2. Diffusion / Diffusion Policy:加噪与去噪

🔴 没答上 · 🟡 已理清(待脱稿复述)

一句话框架(先给这句,再展开)

扩散模型 = 前向一步加噪 + 反向多步去噪;两种参数化的差别不在去噪流程,而在网络学什么、Loss 对谁算。

前向加噪:训练时「一步到位」

  • 递推形式(每一步加一点):\(x_t = \sqrt{1-\beta_t}\cdot x_{t-1} + \sqrt{\beta_t}\cdot \epsilon_{t-1}\),ε ~ N(0, I)。
  • 高斯可叠加(独立高斯之和仍是高斯,均值方差可相加)⟹ 展开后所有中间噪声合并成一个总噪声: \(x_t = \sqrt{\bar{\alpha}_t}\cdot x_0 + \sqrt{1-\bar{\alpha}_t}\cdot \epsilon\),\(\alpha_t = 1-\beta_t\),\(\bar{\alpha}_t = \prod_{i\leq t} \alpha_i\)。
  • 所以训练时不必真的循环 t 步加噪,任取一个 t 一步采样即可 —— 这是训练效率的关键。
  • 直观:连加 50 次小噪声 ≡ 一次性加一个大小合适的大噪声。

反向去噪:推理时「多步迭代」

  • 从 x_T ~ N(0,I) 出发,t = T … 1,每一步调用网络一次,逐步逼近 x_0。
  • 理论保证:前向每步都是高斯、步长足够小 ⟹ 反向每步也近似高斯: \(p(x_{t-1}|x_t) = \mathcal{N}(x_{t-1}; \mu_\theta(x_t,t), \Sigma_\theta(x_t,t))\)。
  • 为什么必须多步:一步从纯噪声生成干净动作太难;多步是把一个难问题拆成 T 个简单子问题,每步只消除一点不确定性。
  • 推理时要额外加一点随机扰动 σ_t z 保多样性(DDIM 可以去掉它换取确定性加速)。
  • 加速:DPM-Solver 等高阶求解器把 100 步压到 5~10 步;DP 还会「生成 Ta 步、只执行前几步,再用新观测重规划」——代价是牺牲对突发情况的响应速度。
  • 一句话:训练加噪一步到位,推理去噪步步为营。

去噪单步公式(DDPM 标准式,要能背)

\(x_{t-1} = (1/\sqrt{\alpha_t}) \cdot [ x_t - ((1-\alpha_t)/\sqrt{1-\bar{\alpha}_t}) \cdot \epsilon_\theta(x_t, t) ] + \sigma_t z\)

  • 系数 1/√α_t:缩放,保持方差稳定。
  • 核心项 ((1−α_t)/√(1−ᾱ_t))·ε_θ:估计 x_t 里混了多少噪声,直接减掉。(注意 \(1-\alpha_t = \beta_t\),两种写法是同一个量。)
  • σ_t z:人为加的随机扰动,保证多样性、数值稳定。
  • 面试话术(原对话给的比喻很好,保留):「这个公式本质是个误差校正器:先用网络预测当前样本里的噪声成分并剔除,得到干净主体的估计;再加一点点随机波动作为下一步的起点,循环往复逼近真实数据分布。」

两种参数化:真正的区别在哪

  • 加噪对象完全相同:都是对干净动作 x_0 加标准高斯噪声,输入都是同一个 x_t。
  • 唯一区别是网络的输出目标:
    • ε-prediction:输出 ε_θ,Loss = ‖ε_θ − ε‖²(噪声空间)。
    • x0-prediction(EDM / DP 常用):输出 x_θ,Loss = ‖x_θ − x_0‖²(动作空间)。
  • 数学上二者可互相换算,等价;但「在有限数据 + 有限步数的黑盒优化下」,Loss 所在的物理空间决定了收敛路径。
  • 为什么 x0 更好:Loss 直接对应「关节角偏了多少」,与最终任务目标对齐;而噪声空间里 ε 差一点、映射回动作可能被系数放大,网络「不知道这个后果」。

⚠️ 必须澄清的两个说法(原对话里讲错了,面试千万别说)

  • ❌「预测动作时,重新加噪到 x_{t−1}」,即 \(x_{t-1} = \sqrt{\bar{\alpha}_{t-1}}\cdot x_\theta + \sqrt{1-\bar{\alpha}_{t-1}}\cdot \epsilon\)。
    ✅ 实际是:先由 x_θ 反推噪声 \(\epsilon = (x_t - \sqrt{\bar{\alpha}_t}\cdot x_\theta)/\sqrt{1-\bar{\alpha}_t}\),再代入上面那个 standard 去噪式。把两步展开后 x_t 的系数是 (1 − (1−α_t)/(1−ᾱ_t))/√α_t ≈ 0.86(我数值验算过),并不等于 1,所以不是「重新加噪」。
    一句话修正:x0 参数化是「幂等」的 —— 预测出干净动作后,用同一个式子算一步,得到的是下一步的输入 x_{t−1}。
  • ❌「训练 x0 时反推噪声那一步不参与梯度回传」。反推式里含 x_θ,梯度当然会经由它回传到网络。
    ✅ 真正的区别只是 Loss 定义在哪个空间(‖x_θ−x_0‖² vs ‖ε_θ−ε‖²),两者的梯度都是对同一批参数求的。别把「梯度不回传」当成论据,会被追问穿。

一句话回答「从 x100 到 x99 具体怎么做」

  • ε 参数化:\(\epsilon_\theta = net(x_t, t)\) → 直接代入去噪式减掉。
  • x0 参数化:\(x_\theta = net(x_t, t)\) → 反推 ε → 代入同一个去噪式。
  • 两者最终算出的 x_{t−1} 在 sigma=0 时完全一致;差别只在网络学的东西和 Loss 在哪算。

面试话术:被问「去噪过程是什么样的」怎么答

  • 定性:「是一个从纯高斯噪声逐步恢复目标数据的迭代过程,本质是反向概率采样。」
  • 流程:「从 x_T 出发,每步把 x_t 和时间 t 喂给网络,网络给出当前估计(噪声或干净动作),代入公式退一步得到 x_{t−1},循环 T 步。」
  • 升华:「必须多步,是因为一步太难;多步把一个难问题拆成上百个简单子问题,每步只消除一点不确定性——这是它区别于 GAN / VAE 的核心。」

📚 完整原理(防止遗忘,按顺序读一遍就能重建整套推导)

A. 完整符号表

  • x_0 干净数据(这里是动作序列);x_t 第 t 步的带噪数据;ε ~ N(0, I) 标准高斯噪声。
  • β_t 第 t 步的噪声方差(前向方差表,通常线性从 1e−4 到 0.02);\(\alpha_t = 1 - \beta_t\);\(\bar{\alpha}_t = \prod_{i=1..t} \alpha_i\)(累积衰减)。
  • ε_θ(x_t, t) 网络预测的噪声;x_θ(x_t, t) 网络预测的干净数据;T 总步数(DDPM 常取 1000,DP 常取 100)。

B. 前向过程(加噪)—— 推导链条

  • 单步:\(q(x_t | x_{t-1}) = \mathcal{N}( x_t; \sqrt{1-\beta_t}\cdot x_{t-1}, \beta_t\cdot I )\),等价写法 \(x_t = \sqrt{\alpha_t}\cdot x_{t-1} + \sqrt{\beta_t}\cdot \epsilon\)。
  • 关键技巧(重参数化):这一步把「采样一个分布」变成「确定性函数 + 一个标准高斯」,因此可微、可反传。
  • 递推展开:因为独立高斯之和仍是高斯(均值方差可直接相加),把 t 步递归展开后所有中间噪声合并成一个总噪声:
    \(x_t = \sqrt{\bar{\alpha}_t}\cdot x_0 + \sqrt{1-\bar{\alpha}_t}\cdot \epsilon\),ε ~ N(0, I)。
  • 由此得到训练可一步采样:任取 t,直接算 x_t,不必循环 t 次 —— 这是扩散模型训练能跑得动的根本原因。
  • 极限行为:t → T 时 ᾱ_t → 0,故 x_T → N(0, I),即前向最终把数据「洗成」纯噪声。这为反向提供了起点。

C. 反向过程(去噪)—— 推导链条

  • 为什么可解:当 β_t 足够小时,反向转移 q(x_{t−1}|x_t) 也近似高斯(前向是高斯马尔可夫链)。所以可以参数化为
    \(p_\theta(x_{t-1}|x_t) = \mathcal{N}( x_{t-1}; \mu_\theta(x_t,t), \Sigma_\theta(x_t,t) )\)。
  • 后验方差(有闭式解,不用学):
    \(\sigma_t^2 = \beta_t \cdot (1-\bar{\alpha}_{t-1}) / (1-\bar{\alpha}_t)\)。
  • 后验均值(把 x_0 用 \(x_0 = (x_t - \sqrt{1-\bar{\alpha}_t}\cdot \epsilon)/\sqrt{\bar{\alpha}_t}\) 代入贝叶斯公式得到):
    \(\mu_t = (1/\sqrt{\alpha_t})\cdot[ x_t - (\beta_t / \sqrt{1-\bar{\alpha}_t})\cdot \epsilon ]\)。
  • 于是单步采样公式:\(x_{t-1} = \mu_t + \sigma_t\cdot z\),z ~ N(0,I)(t=1 时 z=0,不加随机项)。展开就是前面那条 DDPM 标准去噪式。
  • 采样循环:x_T ~ N(0,I) → 对 t = T, T−1, …, 1 反复「网络预测 → 代数 → 加扰动」→ 输出 x_0。

D. 损失函数怎么来的(面试最常被追的部分)

  • 出发点:最大化数据对数似然 log p_θ(x_0)。直接算不可能,所以用变分下界(ELBO):
    \(\log p_\theta(x_0) \geq \mathbb{E}_q[\log p_\theta(x_0\mid x_1)] - D_{\mathrm{KL}}(q(x_T\mid x_0) \Vert p(x_T)) - \sum_{t>1} D_{\mathrm{KL}}(q(x_{t-1}\mid x_t,x_0) \Vert p_\theta(x_{t-1}\mid x_t))\)。
  • 逐项含义:第一项是重建项;第二项是「终点是否等于纯噪声」(前向设计已保证 ≈ 0);第三项是核心 —— 让学出来的反向分布去拟合可计算的真后验 q(x_{t−1}|x_t,x_0)。
  • 化简:两个高斯之间的 KL,代入两侧均值后,只剩一项与 ε 有关的差:
    \(L_t = \mathbb{E}[ (\beta_t^2 / (2\sigma_t^2\cdot \alpha_t\cdot(1-\bar{\alpha}_t))) \cdot \|\epsilon - \epsilon_\theta(x_t,t)\|^2 ]\)。
  • 论文的进一步简化:把前面那一坨系数直接丢掉(作者实验发现丢掉更好),得到
    \(L_{\mathrm{simple}} = \mathbb{E}_{t,x_0,\epsilon}[ \| \epsilon - \epsilon_\theta(\sqrt{\bar{\alpha}_t}\cdot x_0 + \sqrt{1-\bar{\alpha}_t}\cdot \epsilon, t) \|^2 ]\)。
    这就是大家背的那条「预测噪声的 MSE」——它的来源是 ELBO,不是凭空猜的。
  • t 的采样:训练时 t 均匀采样 U{1..T}(也可以按重要性加权)。t 大时任务接近「猜纯噪声」,Loss 天然较大;t 小时接近重建,Loss 小。

E. 三种参数化(同一个目标的三种坐标)

  • ε-prediction:学 ε_θ,Loss = ‖ε − ε_θ‖²。(DDPM 原始做法)
  • x0-prediction:学 x_θ,Loss = ‖x_0 − x_θ‖²。由 \(\epsilon = (x_t - \sqrt{\bar{\alpha}_t}\cdot x_0)/\sqrt{1-\bar{\alpha}_t}\) 与 ε 版互相等价转换。
  • v-prediction:学 \(v = \sqrt{\bar{\alpha}_t}\cdot \epsilon - \sqrt{1-\bar{\alpha}_t}\cdot x_0\),是前两者的「角平分线」方向,在信噪比极端的 t(很大或很小)时数值更稳。
  • 三者在数学上等价,差别在于不同 t 下的梯度尺度/数值稳定性,所以实际效果不同。

F. 采样加速(为什么能少于 T 步)

  • DDPM 采样必须走完 T 步,太慢(T=1000)。
  • DDIM:把过程改写成非马尔可夫、确定性版本(σ=0),允许跳步采样(如 50 步、20 步),质量损失很小。
  • DPM-Solver / 高阶求解器:把反向过程看成 ODE 求解,用高阶方法进一步压到 5~10 步。Diffusion Policy 就用这类方案满足实时控制。
  • 另一个提速手段是减少 T(DP 常用 T=100)配合「重规划」——生成 Ta 步只执行前几步。

G. 条件生成(Diffusion Policy 为什么能当策略)

  • 把观测 o_t 作为条件喂给网络:ε_θ(x_t, t | o_t)。这就是条件扩散,等价于对 p(x_0 | o_t) 建模。
  • 因此它建模的是动作的完整多模态分布,而不是均值——这是它胜过普通 BC 回归的根本原因(避免「多个可行动作被平均成一个错误动作」)。
  • Classifier Guidance / Classifier-Free Guidance:前者用可微代价函数 G(τ) 的梯度修正去噪方向(BeyondMimic 就用这个);后者训练时随机丢条件,推理时用 \(\epsilon = \epsilon_{uncond} + w\cdot(\epsilon_{cond} - \epsilon_{uncond})\) 外推(w>1 强化条件)。
  • 顺带:L_\mathrm{simple} 那条 Loss 对条件扩散不用改,只是网络多接收一个条件输入。

H. 一句话总览(背这条就够串起来)

「前向用一个固定的高斯马尔可夫链把数据洗成噪声(闭式可一步采样);反向用一个学出来的高斯链把噪声恢复成数据(多步迭代);训练用 ELBO 化简出的去噪 MSE;推理用加速采样器(DDIM / DPM-Solver)压步数;把观测当条件就是 Diffusion Policy。」

I. 易忘的关键结论速查

  • 训练加噪一步到位(闭式),推理去噪必须多步(无闭式)。
  • 后验方差有闭式解不用学,只有均值需要网络给。
  • DDPM 的网络在学 score function ∇_{x_t} log p(x_t)(差一个常数倍),所以扩散 ≈ score matching ≈ 反向 SDE —— 三条路殊途同归。
  • ⚠️ x0-prediction 的精确等价式是 \(x_{t-1} = \sqrt{\bar{\alpha}_{t-1}}\cdot x_\theta + \sqrt{1-\bar{\alpha}_{t-1}-\sigma_t^2}\cdot \epsilon_{est} + \sigma_t z\),其中 \(\epsilon_{est} = (x_t - \sqrt{\bar{\alpha}_t}\cdot x_\theta)/\sqrt{1-\bar{\alpha}_t}\)、\(\sigma_t^2 = \beta_t(1-\bar{\alpha}_{t-1})/(1-\bar{\alpha}_t)\)。那个 σ_t² 要写对:漏掉它在数值上偏差很小(T=1000 时 σ_t 最大才 0.01 量级),但结构上必须对应,写论文/面试推导时别漏。
  • 公式里 \(1-\alpha_t = \beta_t\),两种写法是同一个量,别以为是两套公式。

来源:本人面试复盘 + DeepSeek 追问记录(对话原文)。上表两处修正 + 上述完整推导(B–I 节)为我依据 DDPM 原始论文公式与数值验证整理。

技术

3. PPO:loss 设计、反向传播、历史 MLP 的 log prob 梯度截断

🔴 没答上 · 🟡 已理清(待脱稿复述)

一句话框架

PPO = 在提升策略的同时限制每次更新的幅度;灵魂是 clipped surrogate loss,靠 clip 把 ratio 限制在 [1−ε, 1+ε],使同一批数据能安全地复用几个 epoch。

Loss 三部分(论文是最大化,PyTorch 实现取负号最小化)

  • 策略项(CLIP,灵魂):\(L^{\mathrm{CLIP}} = \mathbb{E}[ \min( r_t\cdot\hat{A}_t, \mathrm{clip}(r_t, 1-\epsilon, 1+\epsilon)\cdot\hat{A}_t ) ]\),其中 \(r_t = \pi_\theta(a_t|s_t) / \pi_{\theta_{old}}(a_t|s_t)\)。
  • 价值项:\(L^{\mathrm{VF}} = \mathrm{MSE}(V_\phi(s_t), \hat{R}_t)\),让 critic 预测的状态价值逼近回报。
  • 熵项:\(S = H(\pi_\theta)\),鼓励探索、防止过早收敛到局部最优。
  • 合计:\(total_{\mathrm{loss}} = -L^{\mathrm{CLIP}} + c1\cdot L^{\mathrm{VF}} - c2\cdot S\)(最小化写法)。

clip 的「防守」逻辑(用 A 的正负分情况说)

  • \hat{A} > 0(好动作):想提高它的概率,但 r 最多涨到 1+ε——防止过度奖励把策略带崩。
  • \hat{A} < 0(坏动作):想压低它的概率,但 r 最低只降到 1−ε——防止因一次失误就彻底抛弃该动作。
  • 这些项的梯度在超出裁剪区间时为 0,这才是 clip 真正「限步」的机制(不是把 ratio 数值硬改掉)。

反向传播:更新哪些参数

  • Actor(π_θ):更新它自己的全部权重和偏置;梯度从 loss 经 r_t(θ) 一直回传到输入层。
  • Critic(V_φ):由 L^{\mathrm{VF}} 更新,是纯回归,不受 clip 影响,梯度独立于 actor。
  • 共享特征层(若 actor/critic 共用底层 MLP):同时接收两个 loss 的梯度,叠加后更新。
  • 熵项只作用于 actor,critic 不参与。
  • 执行顺序:前向 → 算 total_\mathrm{loss} → backward()(梯度同时流向 actor 和 critic)→ optimizer.step() → 全部 epoch 结束后才 θ_old ← θ。

🎯 核心问题:历史 MLP 估计的 log prob,梯度为什么要截断

  • 分母 π_θold 是采样那批数据时那条旧网络(behavior policy)的概率,实现上就是 \(ratio = (logp_{\mathrm{new}} - logp_{\mathrm{old}}).\exp()\),其中 logp_old 是用 torch.no_grad() 存下来的。
  • 原因一(数学前提):重要性采样要求分母是固定的行为策略。对 θ_old 求梯度就破坏了 IS 的前提,引入偏差。
  • 原因二(它就是常数):θ_old 是采样那一刻冻结的快照,本就与该 batch 的梯度无关;每轮更新后至少在这一批数据的生命周期内它都不变。
  • 原因三(让 clip 失效):若让分母也参与梯度,等价于同时「移动分母」,ratio 会被无意义地拉向 1,clip 的约束意义就没了 → 更新方向错、训练不稳。
  • 一句话:分子是「现在的我」,分母必须是「采样时的我」并且被冻结。

同类必须 detach / 当常数的量(一张清单,面试可主动补)

  • logp_old(ratio 的分母)—— 上面详述。
  • \hat{A}(优势,含 GAE 结果)—— 只是 Loss 里的固定权重系数,不产生梯度;否则 critic 会从 advantage 回传。
  • 归一化后的 \hat{A}_\mathrm{norm} —— 纯数据预处理,仅对当前 batch 做 (A−mean)/std,不产生梯度,且不改变正负号与相对排序。
  • value target \hat{R}_t / bootstrap 的 V(s_{t+1}) —— 当常数,不然 critic 会「自己追自己」。
  • RLHF 里 KL 项的参考模型输出 —— 同理。

追问连带(原对话里都问到了,值得记住)

  • 归一化谁:只归一化 actor 用的优势;critic 的回报 \hat{R} 和 V(s) 绝对不归一化(要拟合真实奖励尺度,归一化会破坏物理意义)。归一化是线性的,不改变正负号和排序。
  • 熵在 loss 里的符号:熵本身 S ≥ 0 永远是正数;最小化写法里是负项 −c2·S,下降 loss 就必须增大熵。(c2 通常 0.01~0.001。)
  • 熵不会无限增大:离散动作有硬上限 S_\mathrm{max} = log N(均匀分布时取到);连续动作理论无上限但被网络 σ 限幅 + CLIP 项任务压力压住,最终与 CLIP 项形成动态对抗平衡。
  • actor loss 该收敛到 0 还是负数:都没有固定目标值,只该看「loss 是否停止下降」+「奖励曲线是否上升并稳定」;loss 随环境奖励尺度变化,正负都正常。
  • On-policy / Off-policy:PPO 本质是 On-policy(采样完更新几次就丢弃这批数据),但靠 importance ratio + clip 在稳定性和样本效率间取平衡;off-policy 代表是 DQN / DDPG / SAC。
  • 优势 vs 奖励:奖励是「原材料」(绝对尺度),优势是「减去基线后的净利润」(相对排名);PPO 用优势而非奖励,才能剥离「环境本来就给的分数」。

来源:本人面试复盘 + DeepSeek 追问记录(对话原文)。

技术

4. BeyondMimic 里的 VAE

🔴 没答上 · 🟡 已理清(待脱稿复述)

先放回整个框架里(不然说不清它为什么存在)

  • 第一阶段(RL 运动跟踪):为每个参考动作训一个专家策略,输出 PD 目标(关节目标位置),即动作空间是「关节目标」。
  • 第二阶段(蒸馏 + 引导扩散):把这些专家策略「蒸馏」成一个统一的条件扩散模型(基于 Diffuse-CLoC 的状态-动作共扩散),测试时用可微代价函数做 classifier guidance,零样本解决训练里没见过的新任务。
  • VAE 就在第二阶段:它是这个蒸馏管线里的一个组件,不是用来生成动作的,而是把「高维、不规则的参考运动分量」压成平滑、低维、结构化的 latent,让扩散模型在这个 latent 上建模。

它为什么需要这一层(三个理由,按重要性)

  • 动作空间不规则、维度高:人形机器人几十个自由度,参考运动是长序列。直接对原始高维轨迹做扩散 → 难学、样本效率低。
  • 需要「平滑、结构化」的 latent 流形:VAE 的 KL 正则把 latent 压成近似高斯、且连续无空洞。这样扩散模型面对的是一个规整的分布,才学得动(和 Latent Diffusion 的思路同源)。
  • 为了后面的 guidance 能用:classifier guidance 要在状态空间里定义代价函数 G(τ)(如避障用 SDF、导航用「到目标点距离」),并对轨迹求梯度。latent 空间结构好,梯度引导才稳定有效。

⭐ VAE 的网络结构(你问的重点)

  • 类型:条件 VAE(CVAE)——即编码器和解码器都额外接收条件输入(这里是当前观测 / 机器人状态的历史),不只是「输入 → latent → 输出」的自编码器。条件的作用是让同一个运动片段在不同状态下有不同编码,避免把运动学歧义压进 latent。
  • 编码器 Encoder q(z | x, c):输入「运动片段(要编码的量)+ 条件 c」,通常是一个 MLP(或对时序用 1D 卷积 / Transformer)。输出两组东西:均值 μ 和对数方差 log σ²(对角高斯),latent 维度 d_z 远小于输入维度 —— 这就是「压缩」。
  • 采样(重参数化技巧):\(z = \mu + \sigma \odot \epsilon\),ε ~ N(0, I)。这一步是 VAE 能反向传播的关键——把随机性挪到 ε 上,使 z 对 μ, σ 可微。
  • 解码器 Decoder p(x | z, c):输入 latent z + 同样的条件 c,输出重建的运动 / 动作。通常也是 MLP(对称结构)。
  • 损失:\(L_{\mathrm{VAE}} = \mathrm{MSE}(\hat{x}, x) + \beta\cdot D_{\mathrm{KL}}( q(z|x,c) \| \mathcal{N}(0,I) )\)
    • 第一项:重建项,保证压进去的信息能还原;
    • 第二项:KL 正则,把 latent 逼近标准正态 —— 这正是「平滑、连续、无空洞」的来源;
    • β 权衡二者(β>1 就是 β-VAE,逼得更紧、更强调压缩)。
  • KL 的作用(一句话):没有它会「作弊」——把不同动作映射到相距极远的点,latent 空间断裂,中间采样出的是无意义状态;有了它,latent 变成连续可插值的坐标系,扩散才能在上面「走」。
  • 训练方式:DAgger(关键工程细节):不是只在专家数据上训,而是让策略跑、把跑偏时的状态也标上正确动作,不断把「策略实际会访问到的状态分布」喂进来。目的:避免 VAE 只在专家轨迹上重建得好,一旦策略有偏差就崩(distribution shift)。

VAE 和 Diffusion 各自管什么(容易混,分清)

  • VAE 管「表示」:把高维动作 / 运动压成低维平滑 latent,提供可编解码的接口。
  • Diffusion 管「分布」:在 latent 上学 p(z)(严谨说是学 score),负责生成多模态、自然的运动分布。
  • Guidance 管「任务」:测试时用代价函数梯度把生成往任务目标推。
  • 所以是三层分工:表示 → 分布 → 任务,各解决一类问题,这也是它能「一套模型零样本做多任务」的原因。

论文里明确的一句(面试可以引)

  • 论文明确说:选扩散而不用 VAE / AMP,是因为 VAE 类方法虽然能生成技能,但缺少测试时可灵活引导的机制;扩散模型学的是数据分布的梯度场(score function)而不是分布本身,所以能任意可微目标做在线优化。
    —— 这句话直接把「为什么既有 VAE 又要 diffusion」讲圆了:VAE 负责表示压缩,diffusion 负责可引导的分布建模。

⑦ 易忘速查

  • VAE 在第二阶段,是条件 VAE,压的是「参考运动分量」。
  • 编码器输出 μ, log σ²;采样用重参数化 \(z = \mu + \sigma\epsilon\)(这是可反传的关键)。
  • 损失 = 重建 MSE + β·KL;KL 负责 latent 平滑连续。
  • 用 DAgger 训,覆盖策略实际状态分布。
  • 分工:VAE 表示 → diffusion 分布 → guidance 任务。
  • 选 diffusion 而非纯 VAE 的理由:VAE 没法在测试时灵活引导。

来源:我依据 BeyondMimic 论文(arXiv:2508.08241)与它所基于的 Diffuse-CLoC(arXiv:2503.11801)、PDP 整理。⚠️ 诚实标注:论文正文/补充材料给出了「条件 VAE + DAgger + 压缩参考运动为 latent z」这一层事实;编码器/解码器具体是几层 MLP、latent 维度多少,属于实现细节,面试按上面「条件 VAE + 重参数化 + 重建/ KL + DAgger」的结构答已足够,被追问具体维度时如实说「以官方代码为准」。

技术

5. 步态奖励:gait_phase 是什么、怎么和奖励挂钩

🔴 没答上 · 🟡 已理清(待脱稿复述)

一句话定义

gait_phase(步态相位)是一个自己走的内部时钟:一个在 [0, 1) 里循环的值,用来告诉每条腿「你现在处在步态周期的哪一段」——应该支撑还是摆动。它不由策略输出,而是按时间推进,作用是把「期望步态」变成可打分的标签。

① 时钟怎么走(相位生成)

  • 需要三个配置:gait_cycle(一个完整步态周期的时长,如 0.85s)、air_ratio(摆动相占比,如 0.38 → 38% 腾空、62% 支撑)、phase_offset(每条腿的初始相位偏移)。
  • 归一化时间:t = episode_time / gait_cycle,然后
    gait_phase[leg] = (t + phase_offset[leg]) mod 1,得到 [0, 1) 的周期量。
  • 左右腿相位差 = 0.5:这是交替步态的关键——一条腿在摆动相中间时,另一条正好在支撑相中间。
    (Trot 对角步态则是:前左 & 后右 同相,前右 & 后左 同相,两组成 0.5 相位差。)
  • phase_offset 通常每个 episode 随机化,避免策略只会从固定相位起步。

② 相位怎么翻译成「该干什么」—— 时钟信号

  • 有个 gait_clock(phase, air_ratio, delta_t) 函数,把标量相位翻成两个软掩码(0~1 连续,不是 0/1 硬开关):
    • I_swing:摆动相期间为 1(该抬脚);
    • I_stance:支撑相期间为 1(该踩地)。
  • 区域划分:phase ∈ [0, air_ratio) 是摆动相,[air_ratio, 1) 是支撑相。
  • ⚠️ 边界必须平滑过渡(用 delta_t 在相变点附近做线性/余弦插值)。原因两层:① 硬切换会让奖励在相变瞬间跳变,策略学不稳;② 平滑后可微,对连续控制更友好。

③ 相位怎么和奖励建立联系(核心,面试就问这个)

  • 通用公式:\(r_{\mathrm{phase}} = I_{\mathrm{stance}}(\phi) \cdot R_{\mathrm{stance}} + I_{\mathrm{swing}}(\phi) \cdot R_{\mathrm{swing}}\)
    即:把「同一时刻希望脚做两件互相矛盾的事」按相位权重分开打分。
  • 最经典的一对(GRF + 足速):
    • 支撑相:希望脚踩实(地反力大)→ \(r = I_{\mathrm{stance}} \cdot F_{\mathrm{normalized}}\)
    • 摆动相:希望脚快速离地、别蹭地(足速够大)→ \(r = I_{\mathrm{swing}} \cdot v_{\mathrm{foot}}\)
    这就把「同一个物理量、在不同相位要有相反要求」这件事用一个掩码优雅解决了 —— 这是整个概念最值钱的地方。
  • 为什么必须用相位:「脚该不该受力」不由状态决定,而由步态阶段决定。没有相位,奖励函数无从区分「现在是该踩还是该抬」。
  • 具体奖励项怎么接:
    • 足滑惩罚:乘 I_stance(只在支撑相罚滑);
    • 抬脚高度:乘 I_swing(只在摆动相要求高度);
    • 接触模式匹配:期望接触 c_\mathrm{des} = I_stance,实际接触 c_actual,奖励 1 − |c_\mathrm{des} − c_actual|;
    • 足端受力 / 速度:用上面的 GRF + 足速组合。

④ 相位要不要给策略看?(观测设计,加分点)

  • 要。否则策略不知道当前处于哪个相位,就无法配合相位奖励。
  • 两种常见编码:
    • sin/cos 编码:[sin(2πφ), cos(2πφ)]——避免 0/1 边界处数值跳变(直接给 φ 会在 0.99→0.01 处突变),且圆对称。这是 periodic reward 论文的推荐做法。
    • 直接给 φ,外加 air_ratio、相位偏移等信息(walk-these-ways 风格)。

⑤ 两种路线的取舍(能对比就显深度)

  • 相位建模路线(walk-these-ways / periodic reward):显式指定步态,可控、可切换(改 air_ratio 就能变 trot / walk / pace),代价是要调参、受设计者先验限制。
  • 足部空中时间路线(legged_gym):只奖励「抬脚够久」,步态自行涌现(通常 trot),简单通用,但难精细控制、难做多步态。
  • 一句话:要「可控步态」用相位,要「省事通用」用 airtime。

⑥ 和 BeyondMimic 的关系(连到你的项目)

  • BeyondMimic 走的是另一条路:用统一的运动跟踪奖励(task reward 跟踪参考动作 + 三项正则)替代手工步态奖励,所以它不需要显式 gait_phase——步态直接来自参考动作。
  • 被追问时可主动说:「步态相位属于手工奖励塑形时代的方法;BeyondMimic 这类工作用参考运动跟踪把步态问题绕开了。两者是不同范式的取舍:相位可控但需设计,跟踪自然但依赖高质量参考动作。」

⑦ 易忘速查

  • φ 是内部时钟,不是策略输出;\(phase = (t + offset) \bmod 1\)。
  • 左右腿差 0.5 → 交替步态。
  • \(r = I_{\mathrm{stance}}\cdot R_{\mathrm{stance}} + I_{\mathrm{swing}}\cdot R_{\mathrm{swing}}\)——掩码把「矛盾的相位要求」拆开。
  • 相变处必须平滑(delta_t),否则奖励跳变。
  • 相位要作为观测给策略,推荐 sin/cos 编码。

⑧ ⚠️ 反面对照:相位编进观测的代价 —— 观测泄漏

  • 风险:φ 是强周期、高信息量的特征,网络会用它预测腿部/机身状态;在共享观测 + 单一网络下,无关子系统(如机械臂)也会读到它,并用它做非期望的预补偿(我的项目就出现了「机械臂跟着腿的节拍周期摆动」)。
  • 关键认知:奖励里不写 ≠ 行为不会被塑造。策略会榨干观测里的一切可用信号,这是典型的 reward hacking / 观测泄漏。
  • 要害不是「编码得明不明显」,而是「它是否可被利用」——换个隐晦编码只是把问题藏起来,通路还在。

⑨ 三档解法(按「治本程度」排序,重要)

做法作用层面性质代价
不引入相位(步态靠 airtime 类奖励涌现)观测源头根治(通路不存在)步态可控性下降,难以指定 trot/walk/pace
结构隔离(相位只喂腿那一路)网络结构 / 观测根治(切断通路)需分路网络 / 多头结构,实现更复杂
奖励压制(惩罚周期性、加平滑项、按任务加权)奖励权重缓解(降低收益,通路仍在)可能换路径复发;可能误伤正常臂部运动
  • 取舍前提:需要指定步态(切换 trot/walk/pace)→ 相位几乎必需,那就走结构隔离;只需要稳定行走、不关心具体步态 → 去相位(airtime 路线)最干净。
  • 一句话:保留相位换来步态可控性,去掉相位换来干净与稳定。
行为

6. 你在项目中遇到过什么问题,怎么解决的?

🟢 素材 2 个 · 素材一完整(相位问题已修复 + 补充 PD 饱和 + torque_limits 奖励修复) · 素材二部分解决

📌 素材一:Go2 + ARX X5 —— 相位变量泄漏导致机械臂出现周期性摆动

S · 背景

四足(Unitree Go2)+ 机械臂(ARX X5)的 loco-manipulation 任务。训练目标是边移动、机械臂末端边跟踪目标——也就是「走路」和「末端追踪」两件事要同时做好。

T · 问题(核心,说具体)

  • 奖励函数没办法显式区分「行走该做的事」和「机械臂该做的事」:我们引入了步态相位,把 sin(2πφ)、cos(2πφ) 编进观测给策略,用于驱动腿部步态。
  • 但机械臂的追踪奖励里并没有任何项和相位 φ 相关——按理说机械臂不该在意 φ。
  • 观察到的现象:机械臂末端确实在周期性地前后 / 远近摆动,节奏和腿的步态周期一致,像在跟着腿的节奏走。

根因分析(这段最能体现思考深度,面试重点讲)

  • 相位信息在观测里泄漏给了整个策略网络。策略是单一网络、共享参数,φ 一旦作为输入进入观测,机械臂那一路的动作头也能「看到」它。
  • 即使奖励里不出现 φ,从策略的视角看,φ 是一个能帮助预测价值的有效特征:
    • φ 能指示腿部当前处于支撑相还是摆动相,从而预判腿的运动趋势、机身姿态变化与 IMU 读数;
    • 即使末端的追踪误差定义在机身系(我和 LeggedManip 一样用 base link 相对坐标),机身姿态的 pitch/roll 变化仍会与末端相对位置产生耦合误差;
    • 于是「用 φ 去预判、提前补偿」在短期确实能降低追踪误差 → 获得正优势;
    • 但网络学到的补偿方式是让末端自身也做周期运动(用机械臂去「抵消」这个耦合),而不是用更平滑的闭环修正。
  • 本质是:奖励没有显式约束 ≠ 行为不会被塑造。策略会利用观测里的一切可用信号去最大化奖励,包括我们不希望它用的那个。这是典型的 reward hacking / 观测泄漏,也有点像「耦合了两个不同时间尺度任务后,慢任务被快任务的节拍带跑」。
  • 关键认知:cos/sin 相位是「连续、强周期、信息量高」的特征,对网络来说是极好用的预测变量,所以特别容易被滥用到无关的子系统上。
  • ⚠️ 注意区分两路泄漏(面试若被追问,这个区分很显水平):
    • 一路:机身绝对位姿——如果误差定义在世界系,机身起伏会直接进入误差。这一路靠把误差改到机身系就能消掉(LeggedManip 的做法,我也是这么做的)。
    • 另一路:相位 φ 对腿部状态的预测能力——只要 φ 在观测里,这一路依然存在。改坐标系消不掉它。
    我遇到的是第二路,所以「我也用了机身系」并不矛盾。

A1 · 尝试一:分层控制 / 解耦(把机械臂当外部扰动)+ 域随机化

  • 思路:干脆不让腿和臂在同一个策略里纠缠——训练时把机械臂看作施加在躯干上的外部扰动,只训一个稳健的行走策略;对机械臂的末端位置和速度做域随机化(随机给它载荷 / 位姿扰动),让腿学会「背着不确定的臂也能稳住」。
  • 部署时的接法:用逆运动学(IK)根据期望末端位置解算机械臂关节角,直接发给臂的控制器;腿和臂各管各的。
  • 为什么能解掉这个问题:机械臂的动作不再由共享策略网络输出,等于物理上切断了 φ 到机械臂的梯度通路——相位泄漏的载体(共享参数)没了。
  • 代价 / 取舍:臂的运动不再参与整体优化,失去了「臂与腿协调发力、用臂摆来辅助平衡」的上限;且域随机化要到「足够宽但不能把腿训保守」的平衡点。属于用最优性换稳定性与可控性。

A2 · 尝试二:在 command 里加模式位(0/1 区分 locomotion / mani)+ 奖励门控

  • 思路:显式告诉策略「现在是哪种模式」,并按模式切换奖励组成。
    • 在 command 里增加一个 0/1 的模式位:\(0 = locomotion\)、\(1 = manipulation\)。
    • 模式位 = mani 时:把步态奖励降到 0,只保留速度追踪相关奖励。
    • 同时,该模式下的速度指令采样全部置 0(即「不要求走」)。
  • 为什么能压住周期摆动:步态奖励是驱动腿部按相位规律摆动的主要奖励来源,把它权重置 0 后,策略失去「按 φ 组织运动」的激励;再加上速度指令为 0,腿被要求停住,躯干不再产生周期性起伏——于是机械臂也失去了「用周期运动去补偿躯干」的动机。等于把泄漏借以存在的土壤(步态激励 + 周期扰动)一起移除了。
  • 效果与性质:✅ 周期摆动基本解决了;但性质是 「回避」而非「根治」——它没碰泄漏通路,只是让泄漏暂时没有用武之地,切回 locomotion 模式就会复发(详见下方「治本程度」小节)。
  • 但问题在于⚠️:这相当于把「边走边追」拆成了两个独立任务(走的时候不追、追的时候不走),严格意义上不符合原本的训练目标——我们的目标恰恰是「同时移动 + 追踪」。所以它是「能跑通、但违背任务定义」的解法。
  • 它和 A1 的差别(面试可以对比着说):
    • A1 是结构上切断(分层、别让同一网络同时输出腿和臂);
    • A2 是行为上切分(用模式位在时间上把两个任务错开)。
    • 两者都压掉了现象,但都回避了「真正同时」这个核心难点——A1 回避了臂腿联合优化,A2 回避了同时执行。这正是我后来继续找第三种方案的原因。

A3 · 尝试三:用指数函数做奖励融合(把 0/1 硬切换软化成连续插值)

  • 思路:不再用 A2 那种「模式位硬切换」(要么走、要么追),而是让两个任务按速度指令的强度自动分配权重——速度越大越偏 locomotion,速度越小越偏 manipulation,中间连续过渡。
  • 做法:把奖励划分为三大类,总奖励写成加权和:
    \(r_{\mathrm{total}} = r_{\mathrm{basic}} + r_{\mathrm{loco}} \cdot (1 - e) + r_{\mathrm{mani}} \cdot e\)
    • r_basic:两类任务都要的基础项(姿态、平滑、能量、约束等),不参与加权。
    • r_loco:行走相关(速度追踪、步态相位奖励等)。
    • r_mani:操作相关(末端追踪等)。
  • 权重函数:\(e = \exp(-|x|)\),其中 x = 速度指令向量的模长(并做归一化,使最大值为 1)。
    • \(x = 0\)(不走)→ \(e = 1\) → mani 权重 1、loco 权重 0(全部精力放在末端追踪)。
    • \(x = 1\)(全速)→ e ≈ 0.368 → mani 权重 0.368、loco 权重 0.632。
  • 设计意图:r_mani 即使在全速行走时也至少保留 0.3 以上(实测 ≈ 0.63)的权重——保证「边走边追」在高速下依然被优化,而不是被 locomotion 完全压掉。这正是对 A2「拆成两个任务」的修正。
  • 归一化的重要性:必须先归一化 x 到 [0,1],否则 exp(−|x|) 的衰减速度会随速度单位变化,权重不可控。归一化让权重函数有了确定的值域:e ∈ (0.368, 1]。

A3 权重曲线(心算/背这个表,面试被追问能立刻答)

  • \(x = 0.0\) → e = 1.000 → mani 1.000 / loco 0.000
  • \(x = 0.2\) → e = 0.819 → mani 0.819 / loco 0.181
  • \(x = 0.5\) → e = 0.607 → mani 0.607 / loco 0.393
  • \(x = 0.7\) → e = 0.497 → mani 0.497 / loco 0.503
  • \(x = 1.0\) → e = 0.368 → mani 0.368 / loco 0.632
  • 特点:单调平滑、无跳变(对比 A2 的 0/1 硬切),且两头都留了余量——mani 再快也不会掉到 0.3 以下,loco 在静止时也不占权重。
  • 一句话解释权重方向:速度指令越小(越接近「原地」),越该专注操作,所以 e 越大、r_mani 权重越高;速度越大,越需要腿出力,loco 权重相应升高。

⚠️ A3 的性质:缓解,不是根治

  • A3 调的是奖励权重,而泄漏发生在观测 → 动作的通路上,两者不在同一层。
  • φ 依然在观测里、机械臂那一路依然能读到它;A3 只是降低了利用它的收益,并没有切断利用它的能力。
  • 所以:换场景就可能复发(速度指令量化变粗、e 曲线调偏、任务难度变化,都会让 φ 的利用价值回升)。
  • 一句话:A3 是「降低泄漏收益」,不是「消除泄漏通路」。

为什么 A3 比 A2 好(核心对比,面试重点)

  • A2 是离散的:模式位是 0/1,两个任务的奖励只能「二选一」,本质是时间上把任务错开——所以违背了「同时」。
  • A3 是连续的:权重随速度指令平滑变化,任何速度下两个奖励都在起作用,只是相对权重不同。这保住了「边走边追」是一个任务,而不是两个。
  • 另一个好处:不需要额外的模式位——用已有的速度指令就推出了任务意图,不增加观测维度,也就少了一个可能被泄漏利用的显式信号(呼应根因里说的「观测泄漏」)。
  • 而且它是可微、连续的,训练过程中权重不会在某个阈值突然跳变,策略学习更稳。

💡 三个尝试的完整对比表(面试时摆出来很有说服力)

  • A1 分层 + 域随机化:结构切断(臂用 IK、不进网络)|效果:能跑,但失去臂腿联合优化|代价:牺牲协调性上限
  • A2 模式位 + 奖励门控:时间上错开(mani 下不走)|效果:周期摆动消除|代价:背离「同时移动+追踪」的任务定义
  • A3 指数奖励融合:按速度指令连续加权(r_basic + r_loco·(1−e) + r_mani·e)|效果:既压住周期摆动,又保持「同时」|代价:需要调 e 的形状 / 归一化,但保住了任务定义 ✅
  • 演进逻辑:A1 用结构换稳定 → A2 用任务定义换现象消除 → A3 找到了「不改任务定义也能解决」的第三条路。这就是完整的 debug 故事。

⭐ 关键认知:三个方案「治本程度」不同(这段最能体现深度)

  • 判断一个方案好不好,要问两层:①现象压住了吗?②泄漏通路断了吗?——三个方案都做到了①,但都没做到②。
方案动的是哪一层性质泄漏通路
A1 分层 + IK系统结构(臂不进共享网络)根治(对相位泄漏而言)切断 ✅
A2 模式位 + 步态奖励置 0任务时序回避(暂时移开土壤)原样存在 ⚠️
A3 指数加权奖励权重缓解(降低利用收益)原样存在 ⚠️
(未采用) 结构隔离 / 去相位观测·网络结构根治切断 / 不存在 ✅
  • 为什么 A2 只是「回避」而非「缓解」:它根本没碰泄漏通路,只是让泄漏暂时没有用武之地——步态奖励归零 ⟹ 「按 φ 组织运动」没激励;速度指令归零 ⟹ 腿停住、没有周期扰动可补偿。一旦切回 locomotion 模式,泄漏条件原样回来。
  • 为什么 A3 只是「缓解」而非「根治」:A3 调的是奖励权重,而泄漏发生在观测→动作通路上,两者不在同一层。φ 依然在观测里、机械臂依然能读到,A3 只是降低了利用它的收益,没有切断利用它的能力。换场景(速度量化变粗 / e 曲线调偏 / 任务难度变化)就可能复发。
  • 一个有意思的对比:A1 其实对相位泄漏是真根治的——臂的动作不再由共享网络输出,φ→臂的梯度通路物理上断了。但代价是牺牲了臂腿联合优化。
  • 所以三者各解决了一半:
    • A1:根治了泄漏,但没治好任务(放弃联合优化)
    • A2:回避了现象,但改了任务定义(拆成两个任务)
    • A3:保住了任务定义,但只缓解了泄漏
  • 这就是我继续找下去的原因:既根治泄漏、又保住任务定义的路——答案在观测/结构层,即 「不引入相位」或「结构隔离」(见下方 LeggedManip 对比)。

🎯 面试话术:主动承认方案边界(比讲成「我最终解决了」更打动人)

「A3 解决了当时的现象,但我很清楚它属于缓解:我调的是奖励权重,而泄漏发生在观测通路上,通路没断。真正治本要么不引入相位,要么结构隔离。当时选 A3 是因为它改动最小、不牺牲步态可控性——是工程上的权衡,不是原理上的根治。」

R · 结果

  • 三个方案都能压住机械臂的周期性摆动;最终采用 A3(指数奖励融合),因为只有它在不改变「同时移动 + 追踪」任务定义的前提下解决了问题。
  • (待补:末端追踪误差 / 成功率的具体数字对比,有就补,没有就用定性描述。)
  • 学到的一句话:解决工程问题时要问自己「我是真的解决了,还是把问题绕开了」——A1 绕开了联合优化、A2 绕开了「同时」,只有 A3 正面解决。

🎯 面试时的收尾金句(这段很值钱,建议背)

  • 「这件事让我意识到:奖励函数不写、不代表行为不会被塑造。策略会榨干观测里的每一点信息。相位这种强周期、信息量高的特征,特别容易泄漏到不该用它的子任务上。」
  • 「所以要么从结构上切断(分层 / 分网络),要么从观测上屏蔽(不让无关子系统看到 φ),要么在奖励上约束(惩罚非期望的周期分量)。我现在更倾向于先看信息通路、再调奖励——因为观测泄漏靠调奖励权重是压不干净的。」

可能的追问 & 准备

  • Q:为什么不直接在奖励里加一项惩罚机械臂的周期性?
    A:可以,但属于「堵」——周期信号已经在观测里,网络总有别的路径利用它,且惩罚项可能伤到正常的臂部运动。根治要看信息通路。
  • Q:A2 不是解决问题了吗,为什么还继续找第三方案?
    A:因为它把「同时移动 + 追踪」拆成了两个任务,周期摆动是没了,但任务定义被改掉了——我们本来要的就是边移动边追踪。这说明「压掉现象」和「达成目标」是两回事,判断一个方案好不好,要看它有没有在不改变任务定义的前提下解决问题。
  • Q:为什么 φ 要用 sin/cos 编码?
    A:因为直接给 [0,1) 的标量在 0.99→0.01 处会跳变;sin/cos 是连续、圆对称的编码。代价就是它太「好用」,容易被滥用到别处。
  • Q:分层后臂和腿的协调性变差了吗?
    A:会损失一部分联合优化的收益,这是明确的取舍;换来的是训练稳定、可解释、臂用 IK 可控。

⚠️ 重要修正:我的追踪目标也是「相对机身 base link」的(根因要重新定位)

  • 事实澄清:我和 LeggedManip_Lab 一样,末端追踪指令是相对机身 link0/base 的偏移(随机采样后保持一段时间),误差也在机身系下计算。
    ⟹ 所以我之前「坐标系不同导致周期摆动」的说法不成立,必须重新归因。
  • 那差异到底在哪?——在相位观测:
    • LeggedManip_Lab:没有 gait_phase,步态靠 airtime 奖励涌现 ⟹ 观测里没有周期信号。
    • 我的方案:gait_phase 的 sin/cos 编码进了观测(供所有子模块共享)⟹ 观测里存在一个强周期、高信息量的特征。
  • 重新推导根因(这才是准确版):既然误差是相对机身系的,机身绝对起伏本该被消掉。但相位 φ 在观测里,它仍然给网络提供了额外信息:
    • φ 能预测腿部当前/即将的构型与接触状态(支撑相 vs 摆动相),进而预测机身相对运动与 IMU 读数;
    • 即使误差定义在机身系,机身姿态的 pitch/roll 变化仍会让末端相对机身的实际位置与期望产生耦合误差;
    • 网络用 φ 去预补偿这部分耦合,而它选择的补偿方式表现为周期性的前后/远近摆动。
  • 一句话结论:问题不是「参照系选错了」,而是「我往观测里塞了一个不需要的周期变量,策略就一定会用它」。坐标系解耦只能消掉「机身绝对位姿」这一路泄漏,消不掉「相位预测腿部状态」这一路。这就是我和 LeggedManip 的真正差异。
  • 由此带来的方案启示:真正对标的做法是——要么别把 φ 编进观测(步态改用 airtime 类奖励),要么从结构上隔离(让机械臂那一路看不到 φ)。这正好回扣第 5 题讲的「两条路线」。

仓库:zzzJie-Robot/LeggedManip_Lab(Isaac Lab + RSL-RL PPO,Go2 + ARX5/Piper 等 7 平台)。⚠️ 它的末端追踪也在机身系(和我一样),所以真正的差异不是坐标系,而是它压根没有相位观测。

  • 相同点(对齐事实,避免误判)
    代码 position_command_b_error_exp 里,末端误差同样是先把末端位置减去机身位置、再用机身四元数逆变换转到 link0 系,再与指令比较。
    ⟹ 它的追踪目标也是「相对机身的偏移」,和我一样。所以「坐标系」不是我和它的差异点。
  • 核心手法①:WBC 用「混合坐标系」指令(比我的更精细,可借鉴)(position_command_error_exp + docs/WBC_MIXED_FRAME_CN.md)
    • XY 用 link0 系:免受行走/转向影响,「向前 0.5m」永远是机身正前方;策略只需学与机身的相对关系。
    • Z 用世界系:地面/桌面高度是绝对参照,「离地 0.6m」直接可写,策略不必推算机身起伏。
    • 姿态也相对 link0,且由「link0 → 目标位置」的方向向量自动解算 pitch/yaw(保证末端朝向目标点),避免不可达姿态。
    ⟹ 关键点:它没有把所有分量都放进机身系,而是 XY 相对机身、Z 绝对世界。我的 Z 向是否也这么处理,值得回头核对。
  • 核心手法②:奖励是「相加」而非「加权切换」
    看 RewardsCfg:end_effector_position_tracking_exp(+3.0)和 track_lin_vel_xy_exp(+3.0)、track_ang_vel_z_exp(+1.5)同时以固定权重存在,全程一起优化。
    ⟹ 没有模式位、没有 e 权重曲线、没有把 loco 置 0——两个任务从一开始就是一个联合目标。
  • 核心手法③:用「足部时序」而非「相位观测」约束步态(真正的差异所在)
    它没有 gait_phase / sin-cos 相位观测。步态是靠一组基于接触传感器的奖励塑造的:feet_air_time(奖励抬脚够久)、feet_long_air_penalty(惩罚单脚悬空过久)、air_time_variance_penalty(惩罚各脚腾空/触地时间的方差 → 逼出对称步态)、feet_slide(抑制打滑)。
    ⟹ 这正是第 5 题讲的两条路线里的「airtime 路线」:不显式给相位,步态自行涌现。观测里根本没有周期变量,所以「相位泄漏」这条路压根不存在。这才是我和它的本质差别。
  • 它是怎么做到「边倾斜身体边够」的:因为 XY 指令相对 link0,若策略主动倾斜/放低机身,末端相对机身的可达范围反而更大——奖励自然会鼓励这种 WBC 协调行为,是「相对坐标系」带来的额外收益。

🎯 对比:它的方案 vs 我的 A1/A2/A3(面试高价值素材)

  • 它的解法 ≈ 「从观测源头就不引入相位」:不切除通信、不错开时间、不调权重,而是根本不给策略相位这个变量。
  • 为什么这样就没事:我的周期摆动来自「φ 能预测腿部/机身状态 → 网络用它预补偿」这一路泄漏。φ 不在观测里,这一路就断了——不是被压制,而是不存在。
  • 代价 / 局限:
    • 没有相位 ⟹ 步态可控性下降(不能像 air_ratio 那样指定 trot/walk/pace,只能靠 airtime 奖励涌现出近似对称步态);
    • 相对机身的 XY 指令意味着末端在世界系中的绝对精度依赖状态估计,上层仍需把世界目标换算到机身系。
  • 一句话总结这个对比:「我在奖励层面想办法(门控 / 加权),本质是承认相位会干扰、再去压制它;LeggedManip 是从观测源头就不给相位,让干扰无法发生。两类方法没有绝对优劣:保留相位换来步态可控性,去掉相位换来干净与稳定。」

📌 如果我重做一次会怎么选(体现反思深度)

  • 优先试坐标系解耦:把末端追踪误差改到机身系计算——改动小、根因明确,且不牺牲「同时移动 + 追踪」的任务定义。
  • 次选去相位化:用 airtime + 方差惩罚塑造步态,从源头消除相位泄漏,代价是步态可控性下降。
  • 我的 A3(指数加权)作为保底:在必须保留相位观测、又必须同时优化两任务时,它仍是一个通用且平滑的方案。

📌 素材一补充:机械臂追踪失败的另一个真正原因——PD 增益过高导致力矩饱和(已修复)

上面 A1/A2/A3 解决的是「观测耦合 / 奖励设计」层面的问题。后来在对比 LeggedManip_Lab 项目时发现:除了相位泄漏,PD 控制器本身也一直在削峰——这才是末端到不了目标位置的物理层原因。两个问题叠加,所以当时怎么调奖励都很难收敛。

问题诊断:Kp × action_scale × max_error 超过 URDF 力矩限制

  • 理论最大力矩 = Kp × action_scale × max_error,本项目 action_scale = 0.25、最大位置误差按 1.0 rad 算。
  • 本项目的 PD 增益 + URDF 限制(红色为超限):
    • zarx_j1:Kp=60 → 理论 15.0 Nm,URDF 20.0 Nm ✅
    • zarx_j2:Kp=100 → 理论 25.0 Nm,URDF 20.0 Nm 🔴 削峰 25%
    • zarx_j3:Kp=100 → 理论 25.0 Nm,URDF 15.0 Nm 🔴 削峰 67%
    • zarx_j4:Kp=40 → 理论 10.0 Nm,URDF 7.0 Nm 🔴 削峰 43%
    • zarx_j5:Kp=20 → 5.0 Nm,URDF 5.0 Nm ⚠️ 临界
    • zarx_j6:Kp=5 → 1.2 Nm,URDF 5.0 Nm ✅
  • 而 LeggedManip_Lab 同一台机械臂的 Kp(j2=50, j3=80, j4=30)全部在 URDF 限制之内,没有任何削峰。

为什么会导致追踪失败(问题链条)

  • 大臂/肘关节(j2、j3)严重饱和 → 末端在空间中的大范围运动物理上达不到;
  • 腕部基座(j4)中度饱和 → 即使大臂到位,也无法精确调整姿态;
  • 网络收到的反馈是「不管怎么动作,末端都差一截」→ PPO 把这些位置判为不可达 → 学习停滞。
  • 训练过程中没有任何显式错误信号告诉你「是控制器饱和了」,特别容易被误判成奖励问题或网络容量问题。

修复方案:降低机械臂 Kp,确保 Kp × 0.25 留有 ≥15% 余量(已合入 go2arx_2_config.py)

  • zarx_j1:Kp 60→50,理论 12.5 Nm < 20 Nm ✅ 余量 +37.5%
  • zarx_j2:Kp 100→50,理论 12.5 Nm < 20 Nm ✅ 消除 25% 饱和
  • zarx_j3:Kp 100→50,理论 12.5 Nm < 15 Nm ✅ 消除 67% 饱和
  • zarx_j4:Kp 40→20,理论 5.0 Nm < 7 Nm ✅ 消除 43% 饱和
  • zarx_j5:Kp 20→15,理论 3.75 Nm < 5 Nm ✅ 余量 +25%
  • zarx_j6:Kp 5→15,理论 3.75 Nm < 5 Nm ✅ 余量 +25%
  • 同时按 Kd/Kp ≈ 0.04~0.10 重新配阻尼(j1: 2.5, j2/j3/j4: 2.0, j5/j6: 1.5),减少震荡。

教训 / 调试顺序

  • 机械臂训练失败,先排查底层控制器(PD 增益、力矩限制、动力学参数)→ 再查奖励设计 → 最后查网络结构 / 观测耦合。我之前反过来了,在观测耦合上花了很多功夫。
  • 经验法则:设计 PD 时一定要留 Kp × action_scale < 0.85 × URDF_limit 的余量,给瞬态响应和重力补偿留空间;不然网络再怎么学都到不了目标。
  • 修复后预期:rew_ee_tracking 开始收敛,rew_torque_limits 惩罚下降,Tensorboard 中机械臂力矩曲线不再贴边削峰。

📌 同一个坑的另一层:torque_limits 奖励函数对机械臂惩罚过严(已修复)

修完 PD 后发现追踪还是不理想。再深挖发现:底层力矩能输出了,但奖励函数又在惩罚它——这是同一症状(机械臂"动不了")的另一个根因,发生在奖励层。

问题:软限制 + 高次幂对机械臂正常使用区间惩罚爆炸

  • 原 _reward_torque_limits 用 soft_torque_limit=0.8, power=8:力矩使用率超过 80% 时,8 次方让惩罚从 0.1 暴涨到 1.0+。
  • 但机械臂正常工作恰恰需要 60–80% 力矩(克服重力 + 惯性);狗腿只需要 30–50%。
  • 结果:机械臂每一步都在被大额惩罚(6 个关节 60–80% 使用率下,总惩罚 ≈ 1.5–3.0,权重 −5e-2 后约 −0.075 ~ −0.15 每步),网络学到「力矩越小越好 → 不动」,与追踪奖励直接对冲。
  • 对比 LeggedManip_Lab:它完全没有 soft_torque_limit 软限制惩罚,只靠 URDF 硬限制裁剪兜底。

修复:腿/臂分离,软限制和幂次各自匹配子系统的实际工作范围

  • 狗腿:保持 soft=0.8, ^8(30–50% 使用率不会触发爆炸,严格约束保护电机);
  • 机械臂:改 soft=0.9, ^4(放宽阈值、降低指数)。
  • 效果(以 j2 为例):80% 使用率时,单关节惩罚从 1.0000 降到 0.1267(↓87%);90% 时从 2.5658 降到 0.3086(↓88%)。机械臂部分总惩罚从 ≈−0.15 降到 ≈−0.03。
  • 已应用到 go2arx.py / go2arx_2.py / go2arx_4.py(go2arx_1 / go2arx_3 已删除)。

这一层给我的额外教训

  • 不同子系统的"正常工作区间"不一样,不能用同一套软限制曲线约束。要先画各子系统的力矩使用率直方图,在它们的实际工作区间之外才设惩罚,不然就是"惩罚正常工作"。
  • 指数惩罚(^4 / ^8)是把双刃剑:在低使用率区间几乎为 0(友好),但越过阈值后陡峭上升(爆炸),必须确保实际工作范围离阈值有距离。
  • 奖励层修复的隐蔽性更高:不像 PD 饱和可以看力矩曲线,软限制惩罚要专门打 rew_torque_limits 的逐关节分解才能发现。
  • 加上前面 PD 那一层,机械臂追踪失败其实是「物理层」+「奖励层」两层叠加,且都被「狗腿和机械臂是不同负载」这个事实坑了——A1/A2/A3 处理的是观测层,这一层又是另两条独立的根因。

📌 素材二:真机部署(Jetson)—— 多通讯链路时间不同步导致策略输出异常(部分解决)

S · 背景

同一套策略,仿真里一切正常(观测直接从仿真引擎取,所有量同帧、零延迟);部署到真机后需要在 Jetson 上把多路异构传感器拼成 PPO 的观测向量。真机各模块走不同的通讯链路:

  • 机械臂:USB-CAN
  • 机器狗:网口(以太网)
  • 相机:USB
  • 还有 IMU,输出的是四元数(需要变换到策略期望的坐标系/朝向表示)

T · 问题(说具体)

  • 各模块更新频率不一致,通讯链路时延也不同,导致「拼观测」时拿到的数据不属于同一时刻。
  • 关键量化:即便用了异步结构,构造一次 obs 的延迟也在 ~30ms 左右——而策略控制周期是 50Hz(20ms/周期)。30ms 已经超过一个控制周期,意味着策略是在「用过期的观测算当前的动作」。
  • ⭐ 关键定位:延迟来自「观测构造」,不是「策略推理」
    我在终端逐段打印各阶段耗时实测确认:策略网络推理本身只要 ~10ms(在预算内),而观测构造那一段就要 ~30ms。
    ⟹ 这就把根因钉死在系统集成层(多路传感器读取 + 变换 + 拼接),而不是模型算力不够。
    ⟹ 也直接解释了「为什么换到高性能笔记本就好了」:省下来的是 I/O 与调度开销,不是推理时间。
  • 后果:策略输出异常——机械臂开始乱晃(典型的反馈延迟 → 相位滞后 → 震荡)。
  • 重要对照:在笔记本等高性能电脑上就不会出现 ⟹ 说明计算资源/调度开销是放大因素,不是唯一的根因,但直接决定了 30ms 这个延迟量级。

根因分析(面试重点)

  • 本质是「观测与控制不同步」:仿真里 obs(t) 和 a(t) 严格同帧;真机上 obs 是几路异步数据「凑」出来的,天然带混合时间戳 + 平均延迟 τ。
  • 为什么延迟会让机械臂乱晃:控制回路里插入纯延迟 τ,会引入相位滞后 −ωτ,降低相位裕度。对机械臂这种高带宽、低惯量的末端控制,τ 稍大就会在某个频率上失稳 → 周期性抖动/乱晃。这正是「我的任务比普通行走更敏感」的原因——腿的平衡回路带宽低,臂的位置回路带宽高。
  • 为什么 Jetson 更严重:算力弱 + 调度抖动大 ⟹ 单次 obs 构造耗时长、方差大,等效把 τ 和 τ 的抖动都放大了。笔记本上 τ 小到落在裕度内,所以「看起来没事」。
  • 为什么「异步 + 线程锁」只能缓解:锁能避免数据竞争(读到半新半旧的状态),但解决不了「数据本身就是旧的」——锁保证一致性,不保证时效性。

A · 已尝试的方案(及为什么还不够)

  • ① 传感器时间戳对齐 / 缓冲(按固定时间步重建 obs):方向正确——先解决「同一时刻」的问题。但对齐只能保证「取自同一时刻」,若那一时刻本身落后当前 30ms,延迟依然存在(对齐消的是抖动,不是延迟)。
  • ② 仿真里模拟延迟做鲁棒性训练(delay randomization):把 τ 注入训练,让策略学会容忍。有效但受限于「训练时延迟与实际分布是否匹配」——真机 τ 会随负载漂移,训练里注入的固定延迟覆盖不全;且延迟太大时策略只能变得保守,性能换稳定。
  • ③ 异步线程 + 线程锁:解决的是数据竞争,让观测是「一致」的,但不改善时效性。
  • 结果:都没能彻底解决(与用户自述一致)。

💡 还没试 / 值得尝试的方向(面试可讲,展示思路)

  • 用 IMU 做状态外推(predict then correct):IMU 频率最高(百 Hz~kHz),用它把慢链路的数据外推到当前时刻,构造「预测版 obs」。这是最贴合根因的做法——把延迟从 τ 压到「IMU 周期」。
  • 硬件/链路层降延迟:CAN 波特率提升、批量读、传感器驱动优先级、CPU 绑核 + 实时调度(SCHED_FIFO)、减少拷贝。对 Jetson 尤其有效——直接压低 τ 本身。
  • 降低策略对高频观测的依赖:如动作分块(action chunk)——一次生成多步、以较低频率下发,减轻「每周期都要新鲜 obs」的压力。
  • 把延迟建模进训练:除了固定延迟,还可做延迟抖动随机化;或训练时直接喂「过期观测」(history + timestamp 一起输入,让策略自己学会补偿)。
  • 分离控制回路:臂的位置环用本地高频控制器(如关节级 PD / IK 高频闭环),策略只以低频下发达标——把策略从高频回路里移出去(呼应素材一的 A1「分层」思路)。

R · 结果(如实说)

  • 尚未完全解决。当前状态:有缓解措施,但在 Jetson 上仍会出现机械臂乱晃。
  • (待补:目前缓解到什么程度 / 有没有量化的延迟、抖动幅度数据。)

🎯 面试话术(能诚实说「没完全解决」反而加分)

  • 「这个问题我暂时没有完全解决,但我把它定位清楚了:仿真里 obs 和 action 同帧,真机上 obs 是几路异步数据凑出来的,平均延迟 30ms、已经超过一个 50Hz 控制周期。它本质上是在控制回路里插了一段纯延迟,降低了相位裕度——而机械臂回路带宽高,所以先在这里失稳乱晃。」
  • 「定位方法是在终端逐段打耗时:策略推理只要 ~10ms,30ms 全出在观测构造上。所以问题在系统集成,不在模型——这也解释了为什么换笔记本就好了。」
  • 「我已经试了①时间戳对齐、②仿真注入延迟做鲁棒训练、③异步+线程锁。后来想清楚了:锁和对齐解决的是「一致性」和「抖动」,解决不了「时效性」——数据本身是旧的问题没变。下一步我会优先用 IMU 高频外推把观测拉到当前时刻,同时在链路层压延迟。」
  • 收获:「仿真里我以为 sim2real 的难点是动力学差异,实际上系统集成的时间同步和延迟同样是 hard 问题——而且它不在策略本身,靠调网络调不出来。」

可能的追问 & 准备

  • Q:为什么笔记本上没问题?
    A:算力强 ⟹ 单次 obs 构造快、调度抖动小,τ 落在相位裕度内;Jetson 上 τ 更大且方差更大,就越过了裕度。注意瓶颈是观测构造(I/O + 变换 + 拼接),不是推理——实测推理只要 ~10ms。
  • Q:你怎么确定瓶颈在观测构造而不是推理?
    A:在终端逐段打耗时:推理 ~10ms(在预算内),观测构造 ~30ms。这也解释了笔记本上好的原因——省的是 I/O 与调度开销。
  • Q:为什么用异步反而更糟?
    A:异步提升吞吐、避免阻塞,但让「各数据的采集时刻更分散」;不加对齐时 obs 是「拼凑」的,加了锁只保证一致不保证新鲜。
  • Q:为什么不直接降低控制频率来掩盖?
    A:降频确实能让 τ/周期 变小,但机械臂需要较高带宽才能跟踪,降频会直接牺牲跟踪性能——是用性能换稳定。

🧪 新增技术实战

技术

8. 只改 GMR,怎么修人体重定向中的自穿模?

🟠 待补充

30 秒回答

我从源头和求解器两层修:先精调身体区域的非均匀缩放,减少人体与机器人比例不同带来的任务空间错位;再在 IK 目标中加入自碰撞惩罚和时序平滑,让解远离碰撞区域且相邻帧关节角连续。它能显著缓解穿模,但 GMR 仍是运动学重定向,不能把“无穿模”说成物理可执行。

穿模根源与三个改法

  • 根源:尺度/映射错位会让手臂进入躯干或双腿互相穿透;逐帧局部 IK 没有显式碰撞或时序约束,下一帧仍可能跳到碰撞解。
  • 区域化 scale:在已有上/下半身 scale 外,对臂长、肩宽、腿长等区域独立标定,是最低成本的源头修正。
  • 自碰撞软约束:对关键 body/几何对加距离 barrier,例如 \(L_{\mathrm{collision}} = \sum_{(i,j)} \max(0, d_{\mathrm{safe}} - d_{ij})^2\),只计算可能接触的对。
  • 时序正则/滑动窗口:加入 \(L_{\mathrm{smooth}} = \lambda\lVert q_t - q_{t-1}\rVert^2\);它抑制关节突变,但不能替代碰撞约束。

面试边界

公开评测中的自碰撞率不能当成所有实现的固定结论,具体取决于数据、模型几何、碰撞定义和后处理。我的方案是几何层缓解,最终仍需用碰撞率、最小 link 距离和接触/稳定性指标验收;严格物理可行性要引入接触、动力学或后续策略精修。

技术

9. Isaac Gym:导入新机器人,以及如何建模深度相机

🟠 待补充

30 秒回答

导入链路是准备资产 → 定义配置与任务 → 每个并行环境创建 Actor → 接入 reset 和 PPO 循环。资产只加载一次,Actor 才是每个 environment 中的实例。URDF/MJCF 负责本体和可选的挂载 link;相机传感器需要在 Isaac Gym 里用 API 创建、设置参数并绑到对应刚体,深度图通过 GPU tensor 直接进入策略。

机器人与相机清单

  • 资产:检查 root、关节 axis/range、惯性、碰撞体和 mesh 路径。MJCF 对 actuator 与自碰撞配置通常更直接;任务配置还要明确 action/observation 维度、默认姿态、PD 增益、奖励和终止条件。
  • 并行环境:load_asset 一次,create_actor 每个环境一次,并隔离 collision group,避免环境之间互撞。
  • 相机:用 CameraProperties 配置宽高、FOV、near/far plane 和 enable_tensors;推荐 attach_camera_to_body 配合局部 Transform 安装到 body。
  • 读深度:遵守 render_all_camera_sensors → start_access_image_tensors → 读取 → end_access_image_tensors;headless 需启用 camera sensors 和 graphics device。相机渲染常是瓶颈,优先降分辨率、降低采样频率。
技术

10. MotionBricks 和 SOMA Retargeter:谁负责重定向,谁负责过渡?

🟠 待补充

30 秒回答

SOMA Retargeter 更接近把人类动捕逐帧转换为机器人关节轨迹的运动学工具;上一帧 warm start 能减少跳变,但不等于生成语义化过渡。MotionBricks 属于动作生成/控制模型,若提供当前状态和目标条件,它更适合生成起始姿态到目标片段、风格或速度切换的连续过渡。两者可上下游组合:SOMA 生产机器人动作数据,生成模型学习数据后负责运行时过渡与控制。

面试时别混淆

  • 逐帧 IK 输出连续,通常来自 warm start、平滑或后处理,不能自动创造动作语义。
  • 生成模型能过渡,也不等于无条件保证可执行;仍需看训练分布、状态输入、接触约束和下游控制器。
  • 只有 SOMA/GMR 轨迹时,站姿接入应单独规划:当前站姿 → 可行支撑状态 → 目标序列,并约束足底、关节限位和速度。
项目

11. BeyondMimic Fight Tracking:为什么做 LKE Reset,而不是继续加观测或 reward?

🟠 LKE 有收益,full-speed curriculum 仍在验证

30 秒回答

我先把 Fight tracking 失败拆成 observation、reward 和 reset distribution 三层。增加 temporal/future/body-error 观测后,核心失败段没有稳定消失;CoM 和角动量 reward 已实现,但尚未显示和 LKE 同等明确的收益。于是我改 reset:从参考动作筛选低关节速度、至少一只脚低速的 Anchor 作为相对可恢复的起点,再根据 Anchor 的失败历史自适应采样。它只改变训练状态分布,不改 Actor、Critic、PPO、动作空间或原 tracking reward。

LKE Reset Lite

  • 离线 Anchor:用 \(E(t) = \sum_j |\dot{q}^{\mathrm{ref}}_j(t)|\) 作为 joint-velocity activity proxy,选局部低谷、低于分位阈值、间隔足够且至少一只脚低速的帧。
  • 在线反馈:记录 attempts/failures,按失败率提高困难 Anchor 概率,并保留均匀探索底座和单 Anchor 概率上限,避免采样塌缩。
  • 已记录结果:局部降速 Fight 实验中,episode length 在 iteration 1096 首次超过 200,峰值 277.9。这支持早期训练效率改善,不等同于 full-speed 原动作已解决。

准确边界

脚低速只是 contact-like heuristic,不是接触力判定;E(t) 也不是真实动能。若 future_difficulty 尚未进入采样概率,就不能声称完整 difficulty curriculum 已验证。局部降速改变时间尺度,应作为控制难度诊断;BM-Dyn-Lite 是已实现、正在做公平对照的 dynamics-aware reward 方案。

完整设计细节:LKE-Reset Lite 到底怎样筛 Anchor

我的目标不是“只从简单帧训练”,而是先排除明显不适合作为 reset 起点的高速或腾空阶段,再让策略通过失败反馈逐渐覆盖难点。这里的 LKE 是低关节运动活跃度,不是严格物理学上的低动能。

  1. 准备参考量:对每条 motion 的每一帧读取参考关节位置和速度 \(q^{\mathrm{ref}}(t), \dot q^{\mathrm{ref}}(t)\),同时读取左右脚的线速度。
  2. 计算 activity proxy:定义 \(E(t) = \sum_j |\dot q^{\mathrm{ref}}_j(t)|\)。它计算便宜、稳定,能反映一帧附近是否处于快速关节运动;但不包含质量矩阵、惯量和姿态,因此不能称为真实 kinetic energy。严格动能应为 \(T(q,\dot q) = \tfrac12 \dot q^T M(q)\dot q\)。
  3. 局部低谷:候选帧必须满足 \(E(t) \le E(t-1)\) 且 \(E(t) \le E(t+1)\)。这会挑出动作转换、短暂停顿、落地后的相对平静阶段,而不是随机截取高速轨迹。
  4. 全局强度阈值:只保留低于该 motion 中位数的候选,即当前配置 lke_energy_quantile = 0.50,要求 \(E(t) \le \operatorname{median}(E)\)。这样会过滤“局部最小但整体仍很快”的帧。
  5. 时间去重:候选按 activity 从低到高保留,并要求相邻 Anchor 至少相隔 lke_min_anchor_gap_frames = 25 帧。一个长时间静止段不能因为逐帧相近就占满采样池。
  6. 支撑启发式过滤:要求至少一只脚满足 foot_speed ≤ 1.0,即 lke_foot_speed_threshold = 1.0 与 lke_min_support_feet = 1。它是“可能有支撑”的低成本证据,不使用真实接触力;同时记录左/右/双脚低速模式,便于后续诊断。
  7. fallback:若一条 motion 没有候选,退化为该 motion 中 \(E(t)\) 最小的一帧,保证所有动作都仍可被 reset,而不是静默丢弃数据。

一次 reset 与失败反馈如何闭环

  1. episode 结束时先判断是否提前 termination;把结果归因给本 episode 的起始 Anchor,更新该 Anchor 的 attempts 与 failures。
  2. 新 episode 先选 motion,再从该 motion 的 Anchor 集合采样 frame,把 time_steps 设为该 frame;之后仍按原时间轴播放 frame → frame + 1 → ...,并保留原有 root pose、root velocity、joint position 等 reset 扰动。
  3. 因此 LKE 只改了episode 从参考动作的哪个 phase 开始,没有改 observation、Actor/Critic、PPO 超参数、动作空间、tracking reward 或 termination 定义。这使它能和 baseline 做清晰 ablation。

自适应采样概率怎么定

每个 Anchor 保存 frame、energy、contact pattern、attempts 和 failures。基础失败率为 \(r_i = \frac{\mathrm{failures}_i}{\max(\mathrm{attempts}_i, 1)}\),采样分数使用 \(s_i = 1 + \alpha r_i\),当前 lke_failure_alpha = 1.0。所以失败率高的 Anchor 会被更多重试,但不会无限放大:

  • 加入 lke_uniform_ratio = 0.20 的均匀探索底座,尚未失败或尚未探索的 Anchor 仍会被采样。
  • 单个 Anchor 的概率截断在 adaptive_max_bin_prob = 0.30,避免一次偶然失败让整个 batch 集中在一个 phase。
  • 面试时我会说明:这个设计把“失败更多就训练更多”的 hard-example mining 和“持续探索其他 phase”的分布覆盖结合起来,避免训练分布塌缩。

future difficulty 和课程学习:当前做到了哪里

我会为每个 Anchor 记录未来 100 帧的最大活跃度 \(D_i = \max_{t\in[i,i+100]} E(t)\)。它回答的是“此刻虽然平静,但接下来会不会立刻进入高动态难段”。在 LKE-Reset Lite 中,这个量用于记录和分析;若尚未进入概率计算,它不是已经验证的显式 curriculum。扩展课程版本才会把 Anchor 按未来难度分级,并按当前/已掌握/前沿难度混合采样;那部分必须单独标为实验中的增强方案,而不能和 Lite 混说。

面试追问:为什么不直接随机 phase,或只采最容易的帧?

  • 随机 phase:会频繁从高速、腾空或即将失衡的状态开始,样本可能在策略来得及学习前就 termination,训练预算被不可恢复起点消耗。
  • 只采最容易的帧:会造成 curriculum collapse,策略只会静态或简单阶段。LKE 通过失败加权、均匀底座和概率上限,目标是从“可恢复”逐渐推向“当前最弱的难点”。
  • 为什么不是改 reward:reward 决定“什么行为好”,reset distribution 决定“策略从哪些状态获得学习信号”。在这组 Fight 实验里,先改变状态分布比继续堆 observation/reward 更直接地改善了早期训练效率。
开放题

12. 试管级高精度操作:RL 专家、真机纠错与通用操作模型

🟠 待补充 · 先说算法闭环,再说工程支撑

先校准问题:不要把自己答成纯运控工程

当团队的关键词是 VLA、Diffusion Policy、灵巧手和数据规模时,面试官通常不只想听“我会接相机、做遥操作、部署真机”。更有说服力的定位是:RL 灵巧操作是为大模型提供可执行的动作先验、覆盖困难接触的交互数据,以及真机验证闭环的小脑层。硬件、传感器和数采仍然重要,但它们是把算法闭环落到真实分布上的支撑,而不是主叙事。

60 秒回答:RL → 数据聚合 → 生成式策略 → VLA 闭环

“我会把高精度抓取拆成一个从低层控制到高层模型的数据与策略闭环。第一步,在仿真中用大规模并行 RL 学到稳定的接触、抓取和恢复行为,并通过域随机化覆盖初始位姿、摩擦、动力学和观测噪声。第二步,策略上真机后,不把仿真数据当成终点,而是用 DAgger 或人工遥操作/纠正策略收集策略实际会访问到的失败状态,把 sim-to-real gap 对应的数据补回来。第三步,对存在多种可行抓取路径或操作风格的任务,用 Diffusion 或 Flow Matching 学习条件动作分布;物理可行性不是模型天然保证的,需要由训练数据、动作约束、低层控制或筛选器共同保障。最后,把这些成功轨迹、失败纠正和低层 skill 作为 VLA 的训练/微调数据与动作先验,并在真机上验证高层指令到低层执行的闭环。这样 RL 不是孤立的运控项目,而是为大模型持续提供可执行数据和验证能力。”

每一层分别解决什么

  1. RL 打底:在 Isaac Gym / Isaac Lab 中并行训练接触丰富的基础技能。密集 reward 让策略先学会抓取成功、姿态稳定和碰撞恢复;域随机化不是“解决 sim2real”,而是扩大训练时看过的变化范围。
  2. DAgger 数据闭环:当策略在真机进入未覆盖状态时,收集状态 s 与专家或纠正器给出的动作 a*,再把这些样本聚合回数据集重新训练。这里必须说清 expert 的来源:可以是遥操作员、优化器/MPC、已有安全策略或人工修正,不能只说“自动有 DAgger”。
  3. Diffusion / Flow Matching:若同一观测下存在多条都合理的轨迹,单点回归容易平均成不自然动作;生成式条件策略建模的是 \(p(a_{t:t+H}\mid o_t, g)\),可采样一段动作序列。它擅长表达多模态与平滑性,但仍要通过 action chunking、约束投影、碰撞检查、力/位姿控制或数据过滤控制执行质量。
  4. 服务 VLA:把语言-视觉-任务条件与高质量轨迹关联。VLA 可负责子任务理解、目标选择和 skill 调度;低层 RL/生成式策略负责高频、接触敏感的执行。训练数据同时应包含成功样本、失败恢复、不同物体/视角和纠正轨迹,否则大模型只会学习理想演示分布。

把已有经历接到这条线上

  • PPO / Isaac Gym 大规模训练:对应低层接触技能与可扩展策略训练,不只是一段仿真经历。
  • DexUMI、UR5 + Inspire 灵巧手真机部署:对应将策略置于真实观测、时延和接触分布下的验证与数据回流入口。
  • Diffusion Policy 论文与端到端操作模型:对应多模态动作分布建模,可承接真机聚合的示范和纠正数据。
  • 双速率架构与跨引擎 Sim2Real:对应让高层模型、低层控制和真实执行器以不同带宽稳定协作,而不是与“大模型”无关的工程细节。

被问“怎么做高精度抓取”时的回答顺序

  1. 先报算法闭环:RL 基础技能 → 真机 DAgger 数据聚合 → 生成式动作建模 → VLA 训练与验证。
  2. 再说明评价指标:成功率、末端/物体位姿误差、接触力或滑移、轨迹平滑度、未见物体/视角泛化,以及真机安全失败率。
  3. 最后补工程前提:相机标定、时间同步、遥操作界面、日志和回放系统,说明它们怎样让数据可信、可对齐、可复现。

这次面试的复盘规则

  • 开放题先从 JD 判断对方要听的层级:问 RL/控制就从低层策略和稳定性展开;问 VLA/操作大模型就先给数据、策略和验证闭环。
  • 每个项目都用“能力 → 产生什么数据或先验 → 对高层模型有什么价值 → 如何在真机验证”翻译一遍。
  • 话题突然切换时停两秒,先判断对方想听控制细节、学习算法,还是面向大模型的数据闭环;避免沿用上一题的工程语境。

完整回答:瓶子抓取和试管操作,为什么不能用同一套训练假设

瓶子抓取通常允许较大的抓取位姿区域:只要夹爪落在可抓取带、闭合后摩擦足够,就可能成功。试管任务则常涉及狭窄夹持、试管架对孔、插入、旋盖或移液,误差从“能抓到”变成“横向偏差、轴向角度、接触力和插入深度都要受控”。因此我不会只把末端追踪 reward 的权重调大,而是把任务重构为视觉相对位姿闭环 + 接触状态闭环 + 分阶段精度课程。

90 秒面试版本:从单任务 expert 到通用模型

“如果目标是试管级高精度操作,我会先把它做成一个可度量、可安全部署的单任务 RL expert,再把真机执行过程变成数据闭环。仿真中我不会只随机化物体位置,而是围绕容差边界构造数据:试管和孔位的横向/轴向偏差、相对轴角、孔径间隙、摩擦、刚度、相机噪声、延迟和夹爪误差。训练采用接近、精对准、接触、插入或抓取完成的阶段化 reward,并用 curriculum 逐渐收紧容差。

部署到真机后,我会记录同步的视觉、机器人状态、动作、末端力/力矩、任务阶段和结果标签。策略遇到卡住、对不准或滑移时,由遥操作员、MPC/优化器或安全恢复策略给出纠正动作;这些策略实际访问到的失败状态和纠正轨迹通过 DAgger 式数据聚合回流,更新 expert。这样完成的试管策略是一个精密操作 expert,而不是通用大脑。未来要做通用模型,就持续训练抓取、插入、旋拧、倒液等多个 expert,并把它们输出为统一 schema 的多任务数据。高层语言视觉模型负责理解任务、选择目标和路由 skill,低层 expert 或条件动作模型负责高频接触执行;新任务和新失败再继续回流。这样形成从 RL 技能、真机纠错数据到通用操作模型的持续闭环。”

1)任务拆分:先让策略知道自己处于哪个精度阶段

  1. 预接近:从安全距离运动到 pre-grasp / pre-insertion pose。此时重点是可达性、避障和保持四足基座稳定,不允许急于追最终接触点。
  2. 精对准:以试管坐标系或孔坐标系为参考,压低横向误差、深度误差和轴向夹角。关键量是相对变换 \({}^{\mathrm{socket}}T_{\mathrm{tube}}\),而不只是世界系 TCP 距离。
  3. 接触判别:接近预期深度或出现力突变时,识别“已接触、偏撞、卡住、夹持滑移”这些状态;接触后要降低位置刚度或切换到混合位置/力控制。
  4. 插入/夹持执行:沿目标轴推进,限制侧向力和扭矩,并监控深度、接触力和物体是否随夹爪运动。
  5. 验证与恢复:检查插入深度、物体姿态、夹持稳定和任务成功条件;失败时先安全退回,再记录失败类别供人类纠错和后续训练。

2)RL 仿真怎么构建:状态、动作、奖励与课程

  • 观测:本体状态、末端位姿/速度、夹爪状态、试管与目标孔的相对 6D 位姿、视觉置信度、最近若干帧误差和力/力矩或接触状态。四足加机械臂还要包含 base 姿态、足端接触、质心相关状态,避免为追末端牺牲支撑稳定。
  • 动作:高层策略输出 action chunk 或末端增量/关节 target;低层 PD、阻抗或位置-力混合控制高频执行。接触期不建议让纯位置控制无限硬推,应限速、限力并保留退让动作。
  • 分阶段 reward:预接近奖励安全接近;对准奖励横向误差 \(\lVert e_\perp\rVert\)、轴向误差 \(|e_\parallel|\) 和角度误差 \(\theta=\arccos(\hat z_{\mathrm{tool}}^T\hat z_{\mathrm{target}})\);接触后奖励正确深度、低侧向力和稳定夹持。另加碰撞、过大接触力、足端滑移、能耗和动作突变惩罚。
  • 成功条件:不能只定义“末端到位”。应同时检查插入深度区间、轴向角度、侧向偏差、接触力范围、夹持/物体状态和稳定保持时间。
  • 课程学习:从大孔径、低摩擦不确定性、静态目标和宽容差起步;逐步缩小孔隙、增加相机/手眼误差、改变摩擦和刚度、引入物体遮挡、基座扰动和控制延迟。课程推进由成功率与安全失败率共同决定。

3)仿真数据分布:随机化什么,为什么不只做 uniform randomization

随机化要围绕真实失败模式,而不是平均撒点。除了试管初始位姿、孔位、相机外参、夹爪零位、质量、摩擦、接触刚度、PD 参数、控制延迟和图像噪声,我会提高容差边界附近样本的比例:例如接近最大允许横向偏差、刚好有角度误差、轻微碰撞或半夹持的状态。因为真正决定高精度能力的通常不是“明显简单”或“明显不可能”的样本,而是策略需要学会判断、微调或安全退出的临界状态。

4)是否需要额外硬件:按精度和接触风险分层回答

  • 基础配置:外部 RGB-D 加可靠手眼标定,可以完成较宽容差的抓取和粗对准;关键不是只看深度图,而是持续估计试管、孔位和 TCP 的相对位姿及置信度。
  • 优先增加 wrist camera:试管进入夹爪、孔位被机械臂遮挡、或需要毫米级相对定位时,末端近景视觉比单个外部相机更有价值;它缩短视角链路并在接近阶段减少遮挡。
  • 接触任务优先考虑六维力/力矩:视觉能说明“几何上看似对齐”,但不能可靠判断“已接触、侧向卡住、插偏还是深度到位”。对插入、旋拧、脆弱物体和力控任务,腕部 F/T 是非常直接的闭环信号。
  • 触觉是增强项:对夹持滑移、微小接触和易碎物体有帮助,但不是所有试管任务的第一件硬件;应先证明视觉标定与腕力传感器仍不足,再增加触觉。
  • 工程底座仍不可省:时间同步、低延迟、TCP/手眼标定、力传感器零漂校正和安全限力,比盲目增加传感器更先决定数据是否可用于训练。

5)真机数据管线:每条轨迹应记录什么

我会把每个 episode 组织成可重放的时间序列,而不是只保存一段视频。所有流使用统一时钟或可对齐时间戳,至少记录:

  • 任务元数据:任务模板、试管/孔位型号、目标位姿、容差、场景配置、硬件与控制参数版本。
  • 感知:多相机 RGB/RGB-D、相机内外参版本、检测/6D pose 结果及其置信度、遮挡或追踪丢失标记。
  • 机器人状态:base、关节位置速度、末端位姿速度、夹爪状态、足端接触、控制模式和安全状态。
  • 接触与动作:腕部力/力矩、触觉(若有)、策略原始动作、经安全层裁剪后的实际动作、低层目标与控制频率。
  • 标签:阶段、成功/失败、失败原因、人工是否接管、纠正起点和恢复结果。失败原因至少区分视觉偏差、几何不对准、接触卡住、滑移、超力、安全停止和时序/延迟问题。

6)Human-in-the-loop:不是“多收一些演示”,而是 DAgger 式纠错

策略直接上真机会访问仿真数据中没有的状态。此时数据价值最高的不是再录一批顺利完成的轨迹,而是记录策略即将失败或已经偏离时的状态,并提供正确恢复动作。具体流程是:

  1. 策略执行,安全监控检测到误差、力、滑移或不确定性超过阈值。
  2. 系统进入减速、保持或安全退回;人通过遥操作/共享控制接管,或由 MPC、优化器、规则恢复器给出动作 \(a^*\)。
  3. 保存策略访问的状态 \(s\)、策略原动作 \(a_\pi\)、纠正动作 \(a^*\)、接管原因和后续结果。
  4. 将这些数据聚合到已有数据集,重新训练 RL residual、行为克隆策略或条件生成式策略;回归测试必须同时覆盖原成功集与新失败集,防止只修新 case 而遗忘旧能力。

必须说清:DAgger 中的 expert 不是凭空存在。它可以是熟练遥操作员、MPC/轨迹优化器、安全恢复策略、规则控制器或人工修正;不同来源的标签质量和成本不同,数据中要保留来源字段。

7)一个任务完成后,它在通用系统里是什么

训练稳定的试管 expert 不是“通用大脑”,而是一个有明确输入、输出、适用场景和失败边界的精密操作技能。例如它可以接受“试管 6D 位姿、孔位 6D 位姿、当前机器人状态、视觉/力观测”,输出高频操作动作,并在不确定性或受力异常时请求重感知、重新对准或安全退出。

要走向通用操作,下一步不是立刻宣称一个模型解决一切,而是建立一组互补 expert:粗抓取、精对准、插入、旋拧、倒液、开关、双臂协作、失败恢复等。它们应共享:

  • 统一的 observation、action、坐标系、时间戳和成功/失败 schema;
  • 统一的任务语言/视觉条件与阶段标签;
  • 统一的安全接口、能力描述和评测协议。

8)多 expert 如何变成“通用大脑”

这里要区分三层,不要把“多个 expert”直接等同于 MoE 网络:

  1. 技能库与路由:先由高层模型根据语言、视觉和当前状态判断该调用抓取、对准、插入还是恢复 expert;这是最可控、最容易落地的路径。
  2. 统一多任务动作模型:把多个 expert 的成功、失败和纠正轨迹统一成条件数据,训练 \(p(a_{t:t+H}\mid o_t, g, z)\)。其中 \(g\) 是语言/任务目标,\(z\) 可以是技能或阶段条件;Diffusion/Flow Matching 适合表达多种可行轨迹。
  3. 语言模型的角色:LLM/VLM 更适合任务分解、约束解释、数据语义标注、失败归因和 skill 调度;不能直接替代毫秒级接触控制。高层说“把试管放入 A3 孔位”,低层 expert 或动作模型完成视觉伺服、力控和恢复。

最后一句话(答完整题时收束)

“我会先把试管操作训练成一个能量化精度、接触和安全边界的 RL expert;再把真机失败和人类纠正变成 DAgger 数据,而不只积累成功演示。这个 expert 不是终点,而是通用操作数据引擎中的一个技能节点:多个 expert 通过统一数据格式沉淀为多任务训练集,由高层语言视觉模型理解任务和调度技能,低层策略负责可靠的接触执行,新失败继续回流。这才是从高精度单任务走向通用具身操作的闭环。”

开放题

13. 本体运控训练好以后,如何冻结小脑并训练上层任务 RL?

🟠 讨论点 · command 接口与可行域是关键

核心设想

先训练一个能够稳定行走和末端追踪的 command-conditioned 运控本体,并冻结其权重。之后训练上层任务 RL:它观察目标物体、任务状态和机器人本体状态,输出运控本体能够接收的 command;低层小脑在高频控制回路中把 command 转成全身动作。上层学习“为了完成抓取应该怎样调整目标和身体”,小脑负责稳定地执行。

如果接口接收每个关节的位置和速度

可以把上层 command 定义为每个关节的参考位置与参考速度 \(q^*, \dot q^*\)。冻结的小脑同时读取当前本体状态 \(q, \dot q\)、base 状态和接触信息,输出关节力矩或稳定化控制量。这样上层可以直接表达四足与机械臂的协调姿态,不局限于“底盘速度 + 末端目标”。

要明确区分:q, qdot 是机器人当前状态;q*, qdot* 才是上层下发的参考 command。把两者混为一谈,会说不清策略究竟在观测什么、控制什么。

接口选择的取舍

  • 关节级接口更灵活:上层可同时调整腿部姿态和机械臂姿态,适合学习全身配合;它也能作为 RL 专家和后续生成模型的统一动作表示。
  • 关节级接口维度较高:若机器人有 n 个可控关节,位置和速度参考约有 2n 维。上层探索难度会上升,指令也可能产生关节限位、足端滑动、失衡或动力学不可行的姿态。
  • 末端/底盘接口更紧凑:上层输出末端目标、底盘速度或支撑意图,再由全身控制器求关节参考;这降低了学习维度,但把可达性、支撑和全身协调责任交给低层控制器。
  • 可采用分层混合接口:上层给末端目标和底盘命令,小脑/全身控制器负责逆运动学与平衡;必要时再允许上层输出低维关节 residual 或姿态 latent,兼顾可控性与表达能力。

训练时不能忽略的前提

  1. 低层必须见过任务 command:如果小脑只在普通行走和少量末端追踪命令上训练,冻结后不应期待它能安全执行任意全身关节参考。训练其 command 分布时要覆盖机械臂伸展、底盘转向、支撑切换和操作扰动。
  2. 限定 command 可行域:对参考位置、速度、加速度和变化率限幅;必要时通过 IK/QP、安全投影或动作滤波器确保关节限位、足端支撑和 base 稳定约束。
  3. 避免逐步任意跳变:上层可按较低频率输出短时参考或 action chunk,低层插值并高频跟踪;奖励中惩罚 command 突变,降低高层策略利用接口漏洞的风险。
  4. 把接触信息纳入闭环:关节位置/速度 command 本身不表示“脚正在支撑”或“末端已接触”。观测应提供足端接触、末端力/力矩或接触阶段;对插入等任务还要加入任务目标和接触状态,否则仅靠关节参考很难可靠地区分继续推进、微调和退出。
  5. 冻结范围要说清:通常冻结低层策略参数;观测归一化统计、输入尺度和 command 编码也要固定并和训练一致。若任务超出低层能力边界,应回到低层扩展训练或调整接口,而不是只靠上层反复试错。

上层任务 RL 如何训练

在 MuJoCo 中构造物体与目标场景,随机化物体位姿、目标容差、视觉误差、摩擦和外部扰动。上层根据目标相对位姿、图像语义/分割结果、本体状态和操作阶段,输出小脑 command。奖励衡量抓取/放置成功、末端或物体误差、稳定保持和安全,同时约束 base 摆动、足端滑移、过大接触力和 command 不连续。先从静止、宽容差任务学起,再加入底盘运动、遮挡、接触不确定性和更小容差。

面试回答

“我会先训练一个 command-conditioned 的运控本体,让它能稳定行走并跟踪操作目标,然后固定低层权重,在它上面训练任务 RL。上层根据视觉目标和本体状态输出低层可执行的 command。如果采用关节级接口,上层可输出每个关节的目标位置和速度,低层结合当前关节状态、base 和接触信息生成高频控制;这种接口表达力强,但维度高,所以要限制 command 的范围和变化率,并确保低层训练时覆盖操作需要的姿态和扰动。任务策略先在 MuJoCo 里通过物体、目标和接触参数的课程学习掌握抓取,再逐步加入全身移动和精度要求。若冻结小脑导致某类任务始终不可达,就说明低层能力或接口不足,需要扩展低层训练,而不是期待上层策略突破这个边界。”

与多专家和生成式大脑的连接

每个任务专家都可以使用一致的 command/action schema 和任务条件训练。之后让学生策略访问专家会遇到的状态,并通过 DAgger 收集对应专家或人类纠正动作;再用带任务、阶段和观测条件的状态-动作序列训练 Flow Matching 或 Diffusion。此时生成模型可以学习上层 command 或短时动作片段,小脑仍负责高频执行与稳定控制。多个专家能否合成一个通用大脑,取决于接口、坐标系、时间尺度和标签定义是否统一,而不是简单把模型权重或轨迹文件拼在一起。

Sim2Real

14. 仿真和真机 PD 参数相同,为什么机器狗抬腿高度仍不一致?

🟠 待补充 · 从闭环链路开始定位

问题本质

本质不是“仿真 PD 会产生力矩、真机 PD 不会”。真机电机最终同样通过电流/力矩对机械系统施力;差别在于从位置/速度命令到实际关节运动的闭环链路和模型是否等效。MuJoCo 的位置执行器按仿真中的 actuator 参数把误差转换为广义力,再由仿真动力学推进;真机的位置模式通常经过驱动器内部的位置环、速度环、电流环和电机本体。即使配置里写了相同的 Kp/Kd,两边的实际响应也可能不同。

理想化的显式关节 PD 可以写成 \(\tau = K_p(q^*-q) + K_d(\dot q^*-\dot q)\)。但真机驱动器的增益单位、内部环路、滤波、延迟、摩擦、力矩/电流限制和饱和处理,未必与仿真相同。

同步运行对照:让差异在同一时间轴上显现

是的,MuJoCo 和真机同步运行能让偏差更容易观察。但“同步”要落到可重复的实验:统一时钟或硬件触发、相同初始姿态、相同控制周期和同一条关节参考轨迹,同时记录目标与实际状态。不能只把两个窗口同时打开观察动画。

  1. 同命令并行回放:仿真和真机在同一时间轴上执行完全相同的 q_des/dq_des 轨迹,比较两边的 q_actual/dq_actual、力矩/电流、base 和足端轨迹。这一组主要看 PD/执行器响应及接触动力学差异。
  2. MuJoCo shadow mode:把真机同步测得的状态作为仿真初始状态,并向仿真回放同一时刻的命令,比较下一步或短时间窗的预测状态和真机实测状态。逐步 state reset/校正可以定位局部模型误差;放开状态让仿真自主滚动,则能观察误差如何累计。
  3. 区分开环对照与策略闭环:若两边各自运行策略,策略会根据不同状态产生不同动作,后续轨迹差异同时包含状态反馈造成的动作差异。要单独比较执行器,先做相同参考命令回放;之后再做策略闭环对比,评估整体迁移效果。

时间对齐时要考虑采集、通信和执行延迟。分析时同时按统一时间戳和步态相位对齐:统一时间轴用于测延迟,按相位对齐用于比较摆动期高度、离地时刻和触地时刻。记录原始时间戳,不要只靠事后肉眼对齐曲线。

完整排查流程:先定位,再校准

  1. 固定对比条件:让仿真和真机同步执行同一条关节参考轨迹,统一初始姿态、控制频率、动作时长和负载。先确认关节顺序、角度单位、正负方向、编码器零位、动作缩放和目标语义一致。
  2. 同步记录:记录 q_des/q_actual、dq_des/dq_actual、真机电流或估算力矩、base 位姿/速度、足端相对机身与世界的位置、接触状态和时间戳。先找出误差最早出现在哪一层。
  3. 做安全的小幅辨识:对单关节做阶跃或低幅正弦命令,再做低速摆腿;比较上升时间、超调、稳态误差、相位延迟、饱和和振荡。避免一开始就用完整高速步态调参。
  4. 按日志分支定位:若真机关节实际轨迹落后于目标,优先检查 PD 语义、控制周期、通信延迟、滤波、力矩/电流限制、摩擦和电机响应;若关节轨迹接近但脚尖位置不同,检查编码器零位、关节轴、连杆尺寸和足端 frame;若足端相对机身接近但世界高度不同,检查 base 下沉/俯仰、支撑腿压缩和地面接触;若差异只出现在离地或落地阶段,检查足底几何、地面高度及接触刚度/阻尼。
  5. 判断该调哪一边:真机闭环稳定且跟踪合格,只是与仿真不同,就以真机为参考校准仿真执行器和动力学参数。若真机自身跟踪过慢、超调、振荡或频繁饱和,才在安全边界内整定真机 PD,再用新响应更新仿真。关节跟踪正常时不要继续调 PD。
  6. 回到完整任务验证:先复测单关节,再测摆腿,最后测完整步态;比较足端高度、关节跟踪误差、base 摆动、足端滑移和任务成功率。模型仍有不确定性时,再对摩擦、延迟、执行器参数等做域随机化。

可执行实验方案:具体测什么、怎么判

  1. 定义比较指标:关节位置 RMS 跟踪误差、速度 RMS 误差、峰值误差、命令到响应的延迟、阶跃上升时间/超调、饱和占比;足端则比较摆动期相对机身最大高度、世界系最大高度、离地/触地时刻和足端轨迹 RMS。先规定允许误差,再开始调参,避免凭动画观感判断。
  2. 从单关节到整腿:先固定其他关节,在安全的小范围测试髋、膝关节;辨识通过后,回放同一条低速摆腿 q_des/dq_des;最后才跑慢速完整步态。每轮只修改一类参数并保留原始日志。
  3. 若关节响应不匹配:真机支持力矩模式时,优先让两边使用相同的显式 PD 力矩律与相同限幅;若只能使用驱动器位置模式,就从实测阶跃/正弦响应拟合等效闭环(增益、阻尼、延迟和饱和)。真机状态良好时,在 MuJoCo 中匹配 actuator gain/bias、控制周期、延迟、力矩范围、关节阻尼/摩擦和 armature;真机本身振荡、迟钝或触发限流时,才由低风险测试逐步整定硬件 PD,再重新拟合仿真。
  4. 若关节响应匹配但脚高不匹配:用同一组实测关节角做正运动学,检查关节零位、轴方向、连杆长度、足端 frame。再分别比较 base-relative foot height 和 world-frame foot height:只有世界系不同通常指向 base 姿态/高度或支撑压缩;两种坐标都不同则优先查几何/坐标。
  5. 若只在接触阶段不匹配:单独对齐地面高度和足底碰撞形状,测支撑期压缩量、触地时刻与滑移;再调整仿真接触刚度/阻尼、摩擦和接触求解参数。不要用抬腿轨迹补偿去掩盖触地模型错误。
  6. 完成后再做策略级验证:先用相同参考命令对照执行器,再分别运行闭环策略。后者两边动作会因状态反馈不同而分叉,应作为整体 Sim2Real 评估,不能替代前面的同命令辨识。

若位置模式下读不到真机电机力矩,不影响第一轮诊断:用 q_des → q_actual 的阶跃/正弦闭环响应做匹配;有电流或力矩遥测时,再用它区分是控制器输出受限,还是负载/接触动力学造成的偏差。

抬腿高度差的因果拆解

足端位置误差可近似理解为 \(\Delta p_{\mathrm{foot}} \approx J_{\mathrm{foot}}(q)\Delta q + \Delta p_{\mathrm{geometry}} + \Delta p_{\mathrm{base}}\):关节跟踪误差会经过足端 Jacobian 放大或缩小;运动学标定误差会改变关节角到足端位置的映射;base 高度和姿态误差则会改变足端世界坐标。接触阶段还会引入地面和足底模型差异。因此“抬腿低”只是现象,不能直接推出“PD 增益太小”。

面试回答

“我认为本质是仿真与真机从关节目标到实际运动的闭环没有完全等效,而不只是 PD 数值是否相同。仿真位置执行器按 actuator 模型生成广义力,真机位置命令则经过驱动器内部控制环和电机动力学。我的第一步会让 MuJoCo 和真机在统一时间轴上执行同一条关节参考,并同步比较目标/实际关节轨迹、力矩或电流、base 状态和足端位置;必要时再用真机状态驱动 MuJoCo 做 shadow 对照。如果关节跟踪不同,就辨识延迟、限幅和执行器响应;如果关节角接近但抬腿高度不同,就查运动学标定和机身姿态;如果偏差集中在接触阶段,再查接触模型。真机闭环合格时校准仿真去匹配硬件,真机本身不合格时才安全整定真机 PD。”

🧠 总结:我的薄弱点

反复卡壳的知识点

答不好的原因

下次同类问题的答题思路

待补的学习清单

🔧 维护说明

显示「新增问题」的卡片模板
<article class="qa">
  <div class="qa-head">
    <span class="tag">技术</span>
    <h3>N. 问题标题</h3>
    <span class="state hard">🔴 没答上 · ⬜ 待攻克</span>
  </div>
  <p class="qa-label">一句话定义</p>
  <p>……</p>
  <p class="qa-label">要点</p>
  <ul>
    <li>……</li>
  </ul>
</article>