0x00 概要
HIL-SERL 不是单一算法,而是一套高度集成的混合 RL 系统。它用 SAC 给机器人灵动的手臂、用 DQN 给机器人果断的夹爪、用二值分类器替代手工奖励设计、用人类干预数据弥合 RL 探索与 BC 模仿之间的鸿沟。这篇我们从算法角度逐层拆解它的设计逻辑。

HIL-SERL-train

0x01 HIL-SERL 的总体算法思路
1.1 HIL-SERL 的算法基因图谱
HIL-SERL 的核心算法血统是:

[MDP 框架]


[SAC (Off-policy, 双Q网络)] → [最大熵RL: 探索 + 鲁棒性]

│ + 离线专家数据

[Offline RL (BCQ/BEAR/CQL 思想)] → [Q_exp 作为先验: 解决稀疏奖励 + 冷启动]

│ SAC + Q_exp 先验
│ + offline demonstration replay = RLPD

[SERL] → [带专家先验的在线RL: 解决离线RL无法超越专家]

│ + 人类实时纠正

[DAgger / Interactive Learning] → [在策略分布下收集纠正数据: 解决分布偏移]

│ SERL + HIL

[HIL-SERL] → [高精度灵巧操作的完整解决方案]
这不是一条"算法演进"的树形图谱,而是一条明确的工程叠加链——HIL-SERL 的每一个组件都在解决真实世界 RL 的一个具体痛点:

SAC 提供样本高效的连续控制基类,通过熵正则化保证探索稳定性。
RLPD 通过 50/50 混合采样(online + demo)和高 UTD 比率,让 SAC 能同时利用离线专家数据和在线交互数据训练,解决从零开始 RL 样本效率不足的问题。
HIL-SERL 在 RLPD 之上叠加三层工程创新:人类实时纠正(解决 RL 在真实环境中探索成本过高的问题)、混合动作空间(解决夹爪连续输出犹豫不决的问题)、二值分类器奖励(解决复杂视觉任务中奖励工程困难的问题)。
1.2 三类预训练/先验机制
HIL-SERL 包含三类"预训练/先验"机制,每一类解决不同层面的冷启动问题:

视觉 encoder 使用 ImageNet-1K 预训练 ResNet-10:解决真实图像复杂度高、从头训练数据效率太低的问题
每个任务单独训练 reward classifier:解决奖励函数难以手工设计的问题
少量人类 demonstrations 做 BC 预训练 / RLPD 加速:解决 RL 冷启动探索盲目性的问题
但这些都不是"跨机器人形态、跨任务的通用价值函数"。它们是任务特定的、工程导向的先验。

1.3 HIL-SERL 的训练流程
整个训练过程可以分为四个阶段:

阶段一:离线准备

人类通过遥操作收集两类数据:

200 张成功 + 1000 张失败图像 → 训练 Reward Classifier(约 5 分钟)
20-30 条成功演示轨迹 → 初始化 Demo Buffer
阶段二:专家 Q 函数预训练(Offline)

在离线演示数据上执行标准 Bellman Backup 训练 Q_exp(s, a),用于初始化在线 Critic。这防止了在线训练启动时 Critic 从零开始的盲目性。

阶段三:在线 HIL-SERL 训练

策略执行 π(a|s) → 人类观察
├─ 机器人正常执行 → 继续自主
└─ 机器人出错/卡住 → 人类通过 SpaceMouse 接管

干预数据存入 D = D_exp ∪ D_RL ∪ D_HIL(双通道)

SAC 策略更新(含 RLPD 50/50 采样 + Q_exp 先验引导)

干预频率随策略提升逐步下降,直至策略自主完成任务
阶段四:部署

训练好的策略部署到真实机器人。由于策略是通过 RL 而非纯 BC 训练的,它学会了比人类操作更快的动作模式(论文报告约 1.8x faster)。

训练循环

具体训练循环如下:

HIL-SERL-HIL-SERL 训练循环

训练数据流

训练数据流如下:

HIL-SERL-训练数据流

1.4 为什么 HIL-SERL 能超越模仿学习
论文报告 HIL-SERL 相比 imitation learning baseline 有显著提升。我们从机制上拆解原因:

模仿学习的根本局限:BC 训练的是"在专家状态下输出专家动作"。一旦机器人偏离专家状态,BC 没见过这些 OOD 状态,无法恢复。

HIL-SERL 的打破方式:人类纠偏正好发生在策略出错时。这些数据不是普通演示,而是"从错误状态恢复"的演示。这类数据对解决 compounding errors 非常有效。

RL 还能优化速度和路径:论文还强调 HIL-SERL 不只是提高成功率,也能降低 cycle time。RL 不只是模仿人类路径,它可以在任务 reward 驱动下探索更快、更适合机器人的动作模式。人在操作时会有冗余动作(手抖、犹豫、绕路),SAC 的目标是最大化 Q 值,在训练中会发现"更短、更直"的路径。

0x02 核心算法:RLPD + 人类干预
2.1 RLPD 的"干预版"
HIL-SERL 依然以 RLPD 为核心,但数据来源变了。传统的 RLPD 只用开始采集好的 Demo,而 HIL-SERL 在训练过程中通过人类干预不断注入新的高质量数据。人在训练过程中如果发现机器人要搞砸了,可以用 SpaceMouse 实时接管。这些接管的数据会被存入 Buffer。

算法本质依然是带有高 UTD 和 LayerNorm 的 SAC,但它通过人类干预解决了 RL 在复杂任务(如插拔正时皮带、组装仪表)中"探索不到成功状态"的难题。

HIL-SERL-RLPD

为什么 50/50 采样有效? 在线数据提供最新的状态覆盖,让 Critic 学习到当前策略分布下的价值;Demo 数据(含干预)提供高价值 recovery 轨迹,防止 Critic 忘记专家先验。两者缺一不可。

2.2 BC Loss 的作用
在 rlpd.py 中有一行 bc_loss(Behavioral Cloning Loss)。在升级 Actor 时,如果不仅让它最大化 Q 值,还强制让它模仿演示数据里的动作,这对训练初期的稳定性有很大帮助。

为什么 BC Loss 能稳定初期训练?因为模仿学习的"真理信号"来自人类演示,而不是来自不一定靠谱的自奖励网络。

BC 的 Loss:
Loss

(
a

a
demo
)
2
——目标很明确,动作要离人类近
SAC 的 Loss:
Loss

α
log
π
(
a
|
s
)

Q
(
s
,
a
)
——目标是熵要大且 Q 值要大
当 Q 值萎缩到 0 时:SAC 的 Loss 变成了
α
log
π
(
a
|
s
)

0
。为了最小化这个 Loss,智能体必须最大化熵(随机性)。后果是智能体开始胡乱甩动,彻底忘记人类教过什么。BC Loss 的引入正是为了防止这种退化。

2.3 训练数据流的双通道设计
HIL-SERL-双流

0x03 混合动作空间:SAC + DQN
HIL-SERL 作者发现用 SAC 去夹爪的"开关"这种二进制动作动作效率很低,所以单独拆了一个 DQN 出来专门练"抓取评价"。

SAC:负责控制机械臂的 6D 末端位姿(连续动作)。通过正则化,SAC 的探索是"平滑"的。它在动作周围进行微小的、有目的的试探。这也是为什么 SAC 更适合精细动作。
DQN(Deep Q-Network):专门负责控制夹爪的开/关(离散动作)。DQN 的探索是"抽风式"。大部分时间选最好的,小部分时间随机乱选一个动作。这比较生硬。
这解决了机器人操作中一个非常现实的矛盾:手臂需要丝滑的连续移动,而手指(夹爪)通常只需要果断的开关动作。

3.1 为什么要"分而治之"?
机器人操作的动作由两个截然不同的部分组成:

机械臂运动(连续空间):手臂需要在三维空间中精确移动,位置坐标
(
x
,
y
,
z
)
是连续的实数。SAC 的 Gaussian policy 通过输出均值
μ
和方差
σ
,天然适合这类"具有无限可能性的平滑运动"。

夹爪动作(离散空间):夹爪通常只有两到三个离散状态——张开、保持、闭合。如果用 SAC 的连续输出去拟合,会产生类似 0.13、-0.27 的中间值,夹爪执行时被阈值化,学习信号不稳定。更重要的是,"何时闭合夹爪"是一个非常关键的离散决策——闭早了抓空,闭晚了错过物体。

这就引出了一个自然的设计:用 SAC 给机器人灵动的手臂,用 DQN 给机器人果断的夹爪。

3.2 SAC + DQN 如何协同工作
在 HIL-SERL 的网络中,SAC 和 DQN 不是两个独立的进程,而是同一个主干下的两个分支:

HIL-SERL-网络架构

共享主干:所有相机图像经过 ResNet-10(共享权重)编码后,与 proprioception 的 MLP 编码拼接,输入共享的 MLP Head。

SAC 分支:输入特征 → 输出均值 μ 和方差 σ → 采样得到连续的 Delta 位移向量(单臂 6 维,双臂 12 维)。

DQN 分支(GraspCritic):输入特征 → 输出 3 个 Q 值(关/保持/开)→ argmax 选出最大 Q 的动作。

统一更新:虽然算法不同,但它们在同一个训练循环中被同步优化。

具体来说,HIL-SERL 在这些任务中分别求解两个马尔可夫决策过程MDP
M
1

{
S
,
A
1
,
ρ
1
,
P
1
,
r
,
γ
}

M
2

{
S
,
A
2
,
ρ
2
,
P
2
,
r
,
γ
}

其中
A
1

A
2
分别是连续和离散动作空间。它们都接收来自环境的相同状态观测,如图像、本体感受、夹爪状态等。对于
M
2
的critic更新,遵循标准的DQN方法,并引入额外的目标网络以稳定训练。

在训练或推理时,HIL-SERL 首先从策略
M
1
中查询连续动作,然后在
M
2
中通过对评论家critic的输出取argmax来获得离散动作,最后作者将连接的动作应用于机器人。

3.3 Rollout 时的配合
在 sample_actions 方法中,两个分支的协作流程非常清晰:

def sample_actions(self, observations, *, seed, argmax=False, **kwargs):
# SAC 部分:生成连续动作(位置+方向)
dist = self.forward_policy(observations, rng=seed, train=False)
if argmax:
ee_actions = dist.mode()
else:
ee_actions = dist.sample(seed=seed)

# DQN 部分:选择离散抓取动作
grasp_q_values = self.forward_grasp_critic(observations, rng=grasp_key, train=False)
grasp_action = grasp_q_values.argmax(axis=-1)  # 贪心选择

# 组合动作
return jnp.concatenate([ee_actions, grasp_action[..., None]], axis=-1)

连续控制分支的 Rollout 符合经典 SAC 范式——只依赖 Actor 网络,Critic 不参与推理。

离散控制分支则不同——由于没有独立的 Actor 网络,GraspCritic(DQN)在推理时直接充当"决策者",通过 argmax 选出最优离散动作。

3.4 训练时的配合
训练时,SAC 和 DQN 使用不同的损失函数和不同的回传目标,但共享同一套观测编码:

Critic 训练:只使用连续动作部分
actions = batch[“actions”][…, :-1] # 去掉最后一维夹爪
target_q = rewards + γ · min_i Q_target_i(s’, a’)
critic_loss = MSE(predicted_q, target_q)

GraspCritic 训练:只使用离散夹爪部分 + grasp_penalty
grasp_rewards = batch[“rewards”] + batch[“grasp_penalty”]
target_grasp_q = grasp_rewards + γ · max Q_target(s’, a’_grasp)
grasp_critic_loss = MSE(predicted_q, target_grasp_q)

Actor 训练:采样连续动作,最大化 Q(s,a) - α·log π(a|s)
actor_loss = -mean(predicted_q - temperature * log_probs)
这种设计确保了 reward 的分工:SAC Critic 只关心末端执行器的连续动作价值,GraspCritic 则额外学习"不要做无意义夹爪动作"的惩罚信号。

3.5 输出维度对比
设计者把动作空间拆成了两部分:

前 6 维连续动作:由 SAC actor 输出
最后 1 维夹爪动作:由 GraspCritic(DQN 风格)选择
在 update 中,agent 明确要求单臂动作维度为 7:

chex.assert_shape(batch[“actions”], (batch_size, 7))
具体对比如下:

Agent 类型 SAC 输出 DQN 输出 最终动作维度
SingleArm 6 维 (3位置+3方向) 3 维 (关/保持/开) 7 维
DualArm 12 维 (双臂各6维) 9 维 (3×3双臂组合) 14 维
3.6 SAC vs DQN:靶向设计
两者在 Target 计算上的差异反映了各自面对的问题域完全不同:

DQN 的 Target(硬最大):

Target

r
+
γ
max
a

Q
target
(
s

,
a

)
它假设下一时刻一定选分数最高的动作。因为动作空间是离散且极小的(2-3 个),通过 target_net 的缓慢更新已经能抵消大部分高估问题。

SAC 的 Target(软预期):

Target

r
+
γ
E
a


π
(

|
s

)
[
Q
target
(
s

,
a

)

α
log
π
(
a

|
s

)
]
它不选最高的,而是对当前策略输出的所有可能动作求期望,并加上熵奖励。面对无穷多个连续动作,SAC 必须用"双网络取最小值(Clipped Double-Q)"来暴力压制高估。

把这两个差异总结成一句话:DQN 是"轻量级防守"(离散空间够小,target_net 足矣),SAC 是"重量级防守"(连续空间高估无限放大,必须双 Q + min)。这个区别反映在代码上就是:DQN 找最大值,SAC 则需要从 Actor 里采样一个动作
a

出来算 Q 值。

0x04 GraspCritic:夹爪离散决策网络
GraspCritic 是 HIL-SERL 中一个关键但容易被忽视的设计。它不是普通 SAC 里的 Critic(s, a),而是一个只输入 observation、不显式输入 action 的离散动作 Q 网络。

4.1 定位与架构
GraspCritic 不是普通 SAC 里的 Critic(s, a),而是一个只输入 observation、不显式输入 action的离散动作 Q 网络。

GraspCritic 的输入输出:
输入: observation (无 action)
输出: 3 个 Q 值 → 夹爪开(0) / 保持(1) / 关(2)
训练: DQN 式目标(在线网络选动作 + 目标网络评估)
奖励: grasp_rewards = batch[“rewards”] + batch[“grasp_penalty”]

网络结构同样采用 ResNet-10 + MLP,但 MLP 维度比 Critic 小(单臂为 [128, 128]),因为它只需要学习夹爪的离散决策,不需要建模连续动作的精细价值。

class GraspCritic(nn.Module):
encoder: Optional[nn.Module]
network: nn.Module
output_dim: Optional[int] = 3 # 默认3个离散动作

def __call__(self, observations, train=False):
    obs_enc = self.encoder(observations)  # ResNet编码
    outputs = self.network(obs_enc, train)  # MLP
    value = nn.Dense(self.output_dim)(outputs)
    return value  # (batch_size, 3)

核心前向逻辑是:

输入 observation;
经过视觉 / proprioception encoder;
经过 MLP;
输出 output_dim 个 Q 值。
默认 output_dim=3,对应单臂夹爪的 3 个离散动作:

0 -> 环境动作 -1
1 -> 环境动作 0
2 -> 环境动作 +1

代码里训练时把环境动作最后一维从 {-1, 0, 1} 映射到 {0, 1, 2}:

grasp_action = jnp.round(batch[“actions”][…, -1]).astype(jnp.int16) + 1

因此,GraspCritic(obs) 的输出可以理解为:

[
Q_grasp(obs, close_or_negative),
Q_grasp(obs, keep),
Q_grasp(obs, open_or_positive)
]

也就是说,它在回答一个很具体的问题:在当前图像和机器人状态下,夹爪应该关、保持,还是开?每个选择未来能带来多大价值?

4.2 为什么需要单独的 GraspCritic?
考虑一个完整的动作向量:[x, y, z, roll, pitch, yaw, gripper]

前 6 维是连续控制,SAC 的 Gaussian policy 天然适合。但最后一维 gripper 本质上不是平滑连续信号。如果把夹爪也塞进连续的 SAC actor 里:

动作语义离散但 policy 输出连续:SAC 会输出类似 0.13、-0.27 这种值,夹爪真实执行时被阈值化,导致学习信号不稳定
夹爪动作稀疏且关键:抓取任务里,"什么时候闭合夹爪"是一个关键离散决策。闭早了抓空,闭晚了错过物体
夹爪误操作需要额外惩罚:代码里有 grasp_penalty,惩罚不必要的开合动作。例如 USB 插入任务中,夹爪已经接近闭合还继续执行关闭动作,会产生惩罚
所以这份实现把动作空间拆开:

连续末端执行器动作:SAC actor + SAC critic 学
离散夹爪动作:GraspCritic 用 DQN 风格学

4.3 学习原理:Double DQN 风格的离散动作价值学习
GraspCritic 的训练采用的是 DQN 式 Bellman 回归
Q
θ
(
s
t
,
a
g
t
)

r
g
t
+
γ

max
a
g
t
+
1
Q
¯
θ
(
s
t
+
1
,
a
g
t
+
1
)
,但代码里更接近 Double DQN:

Step 1:用 online grasp critic 选择下一步最优夹爪动作

next_grasp_qs = self.forward_grasp_critic(batch[“next_observations”], rng=rng)
best_next_grasp_action = next_grasp_qs.argmax(axis=-1)

Step 2:用 target grasp critic 评估这个动作的 Q 值

target_next_grasp_qs = self.forward_target_grasp_critic(…)
target_next_grasp_q = target_next_grasp_qs[jnp.arange(batch_size), best_next_grasp_action]

Step 3:构造目标值(含 grasp_penalty)

grasp_rewards = batch[“rewards”] + batch[“grasp_penalty”]
target_grasp_q = grasp_rewards + discount * masks * target_next_grasp_q

Step 4:当前网络只取实际执行的夹爪动作对应的 Q

predicted_grasp_q = predicted_grasp_qs[jnp.arange(batch_size), grasp_action]

Step 5:用 MSE 做 TD 回归

grasp_critic_loss = jnp.mean((predicted_grasp_q - target_grasp_q) ** 2)

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐