原文发表在知乎,辛苦移步:《具身智能hil-serl强化学习算法在lerobot机械臂上复现-案例2

笔者在两个月前发表的文章《具身智能hil-serl强化学习算法在lerobot机械臂上复现》得到了大家的热情反馈,成为所有文章中点赞数最多的。有一定影响力的知名微信公众号也申请了转载,还有很多微信网友加了好友咨询,也了解到了最少有两名网友也基于笔者的代码成功复现。笔者发表文章的初衷首先是在学习的过程中作为一个笔记的记录,时时的回顾总结;其次笔者常常读一些优秀的论文,文章,开源项目代码,经常性的会感慨正是大家的开源奉献精神,才能让后人站在巨人的肩膀之上做出更多创新,所以也希望自己在吸收知识的同时能有少量输出。看到发表的文章能够帮助到大家,也挺开心的。感谢各位的点赞支持。
在这里插入图片描述

回到正题,上一篇hil-serl复现的文章中的案例有点简单,就是推送一个盒子到一个较大的目标区域(上图),任务的轨迹是两个弯(Z形)。笔者经过了2小时候的持续human in the loop,能看到一个较高的成功率。近期笔者在研究强化学习,希望在一个稍微复杂点的任务之上做一些实验,所以设计了一个稍微复杂点的任务,介绍如下。

任务介绍
如下图所示,有一个带方向的矩形积木(上面的黑色贴纸代表方向),初始位置在桌面上的黑色区域内,黑色区域大小跟积木面积一致,同时积木方向朝左。任务的目标是用机械臂将积木进行180度掉头,然后回到绿色区域内,绿色区域比黑色区域的边界扩展了0.5cm。具体任务的过程可参考视频。设计此任务有一些现实的考虑:

此任务复杂度比上一个任务高出不少,机械臂推积木的点位相差半厘米,积木的移动轨迹会有显著的差别。由此产生的状态空间会更复杂。因为机械臂本身的误差问题,想让积木完美的落在黑色区域内难度太高,所以笔者扩展了一下边界(绿色),减少难度。另外,本任务的运动轨迹复杂度也提升了不少。任务时长上有明显的体现,上一个任务的任务平均时间8秒左右(人工),此任务的时间平均18秒左右(人工)。
因为笔者只有键盘接管,通过键盘不容易输出复杂的动作,例如roll/picth/yaw,只能输出x/y/z。所以很多经典的任务是无法进行的。其实笔者只想输出x/y,加上z是没有办法, 因为机械臂即使在平面上移动,由于它的精度问题,它会实不实的往下探或往上翘,所以必须加上z以缓解这些问题。动作空间跟上一个案例也是一致的。x/y/z输出的是相对当前抓手位置的delta。例如输出(1,0,0)代表在x方向上正向移动0.5cm。坐标系介绍可参考上一篇文章。
桌面黑色区域扩展绿色边界,除了减少复杂度的原因外,另一个原因也是相机视角的限制,因为从相机视角来看,积木块会遮挡黑色区域,所以需要大一些的目标区域来判断积木是否是其内。其它硬件环境跟上一个任务也是一致的。
在这里插入图片描述
https://www.zhihu.com/zvideo/1997230185163020106

点击可播放视频

Hil-Serl
3 播放 · 0 赞同 视频
代码:
https://github.com/hxdoit/lerobot.git,切换到分支:hil-serl。

本次发现了4个bug均已修复并提交,bug列表可参考本文附录。

实验并不是一帆风顺,笔者前后经过了多次尝试,最终大概8小时左右的human in the loop,才训练出了一个较高成功率的版本,若持续训练,效果肯定会持续提升。下面讲一下实验过程,若关注结果可直接切换到实验四。

实验一

实验环境:

与最终的环境有一些不同:在这里插入图片描述

没有扩展绿色区域
机械臂初始位置在积木左侧约15cm处
实验配置:

训练配置用官方默认配置,跟上一个案例一样:《具身智能hil-serl强化学习算法在lerobot机械臂上复现》
实验结果:

human in the loop大概2小时后(与上一个案例持平),没有看到什么效果,在小段轨迹上能学习到理想的策略,人工中断实验,怀疑哪里有问题。

分析:

此实验下,人工操作一个episode约23秒左右,是上一个案例的3倍。在任务的最开头部分,完全没有观察到理想策略的影子,所以猜测可能长时序任务中discount的问题。经查看配置,默认discount=0.97。
,discount就是左边belman方程中的
,从结束状态开始往前回溯,每个step的Q值都会打一个折扣。
,而
,若discount较低的话,回溯到任务的最开始,Q值基本就没有了,所以对于时长较长的任务,建议discount可以放大一点,例如0.990-0.997
任务中机械臂初始位置距离积木较远,挪过去得几秒钟,所以可以将机械臂初始位置放在积木旁边,可以将23秒的任务减少到18秒。减轻长时序任务的训练难度。
目标黑色区域太小,此区域被遮挡后,难以分辨积木是否比较好的压在了目标区域上。所以将此目标区域周边扩大0.5cm,一方面减少实验目标达到的难度,另一方面也可较易分辨积木是否达到了目标区域。有一个原则:若人工仅通过相机可以较好的完成任务,那么实验环境(例如相机位置,个数等)问题不大。
操作经验:

刚开始的时候,人工操作一致性也差一些,例如积木在朝向不符合预期的时候,可以通过不同的方法来调整,如果人工操作每次都根据喜好来随机确定,那么就增加了训练的难度和时长。所以人工操作需要有一致性,建议操作员事先经过1小时的反复练习。针对此案例来说,可以事先确定操作的细节,例如要达成目标,可以先让积木掉头(即先调整朝向),然后再调整横向位置,再调整纵向位置。推积木的时候,建议机械臂与积木接触的位置相对固定,不固定的接触位置,会导致积木的移动轨迹发生较大的变化(大家可以试下),这也是这个任务的难点之一。

实验二
实验环境:

对实验一中分析的第2,3点进行了改进 ,如图,机械臂抓手位置为初始位置:
在这里插入图片描述

实验配置:

在实验一基础之上改了部分配置:

图像128128->200386,因为当初觉得128太小了,看不清楚,不利于较高精度的操作。
online buffer capacity: 30000->9000,分辨率提高后,显存就不够用了,不得不大幅缩小数据缓冲区
discount: 0.97->0.997,针对实验一的分析1进行优化
batch size: 256->180,也是显存不够用了,只能缩小batch size
lr: 0.0003 -> 0.0001, 因为batch size缩小了,所以lr也要缩小。另一方面,当初觉得0.0003学习率过高
实验结果:

human in the loop大概2.3小时后,跟实验一一样没有看到什么效果。部分指标曲线如下所示,其中粉色的是案例一成功的指标,青色为当前实验。可以发现整体上训练不太稳定。

分析:

因为buffer是循环覆盖更新的,buffer的大小大幅缩短后,里面充斥的数据都是最近的数据,所以sample数据时,数据的多样性会减弱,造成不稳定。
增加了图像size,每秒优化的次数从13减少为4,同时又减少了lr,在这双重修改下,即使配置没有问题,按上一个案例成功的经验,本实验预期训练需要更久的时间,这个时间大概率也是无法接受的。所以计划还是恢复上次成功的配置。

实验三
实验环境:

跟实验二完全一样

实验配置:

回退实验一的配置,只在实验一的基础之上修改discount:

discount: 0.97->0.995
实验结果:

human in the loop,坚持了三小时(共120k),实验现象跟实验一和二是一样的,没有看到效果。因为需要休息一下,所以紧接着的120k->180k完全用buffer内数据训练,human not in the loop。与上一个成功案例的曲线对比(浅红色为成功案例,深红为当前实验):

分析:

可以发现120k-180k human not in the loop后,基本所有的指标都开始恶化。这是一个典型的offline强化学习的问题。因为没有与环境之间的交互,数据固化了,当Q值进行优化时(belman方程),若策略产生的动作不在数据集范围内,Q值的预测可能是不准的,这个不准的值一直得不到新数据的纠正,就会产生Q值爆炸的问题(上面指标图中的q_predict)。
前面120k没有太大的问题,倒是80-120k中间训练有小幅波动,可能是由于笔者在策略很不成熟的时候,尝试增加一些策略与环境交互+人工接管的数据,这些数据导致训练有小幅波动。
实验四:
实验环境:

跟实验二完全一样

实验配置:

实验四其实是实验三的延续,从实验三的80k开始resume训练:

discount: 0.97->0.995
control_time_s:从200秒修改为30秒,也就是任务的最长时长是30秒,超过30秒将被截断并直接返回reward=0。以前是200秒,也就是总会有人工进行接管兜底,总会成功并收到reward=1。为什么会改这个配置呢?因为正常任务18秒就结束了,但若经常由于人工的长期接管,导致任务时长较长的话,不仅会遇到实验一分析中所述的discount问题,也就是收益无法传递太远,导致任务开头的阶段很难学习到有效的策略。另一方面,若一个不好的动作被纠正后,最终达到成功,这样会产生一条成功的轨迹,若这种成功的轨迹量级很大且类似的话,强化学习会将这种“成功”的轨迹学习到,导致最终学习到了一个局部最优解。相反,此时直接中断返回无reward,算是一种负样本,对一些不好动作进行惩罚。
实验结果:

从实验曲线图上看一切正常且有上一个成功案例的信心,所以笔者坚信一定可以看到成功,只是任务复杂了,时间需要更长一些。持续到260k(共6.5h)后,效果已经开始展现,从以前的局部有点正常任务轨迹的迹像,提升为整体上可有一定的成功率。最终,340k(共8.5h)的坚持不懈下,整体上的成功率已经可以达到较高水平(>70%)。

分析:

下面两张指标图是260k之前的数据,其中粉色的线是以前案例成功的曲线,深红色的线代表当前实验。对于当前实验,80-260k又分为两部分,

80-170k,由于wandb的问题(数据可以insert,但不会update,所以造成一个x轴点对应会有两个y值,最终曲线会出现锯尺,详情可参考本文附录),此部分叠加了实验三的数据,这部分的曲线图一般可认为:y值较异常的是实验三,y值较正常的是实验四
170k-260k,完全是实验四的数据
从训练指标上来看,(实验4)都比较稳定和健康。
在这里插入图片描述
在这里插入图片描述

笔者继续训练了2h,达到340k,到340k的时候,笔者感觉策略探索的力度变大了,体现就是动作会经常跑出正常的轨迹之外,跟下图中的温度指标也能应对上,按大模型的建议,这是一个好的现象,说明策略在前期学习到东西且较为确定的情况下,sac算法会增加温度以提升探索性,伴随着critic/actor的norm/loss的波动性有所增大,但在合理范围之内。2h后整体的成功率提升了很多(>70%)。整体预测的q值下降,这是一个好的现象。整体上继续训练,效果应该会持续提升,由于训练时间关系,本实验先告一段落。

在这里插入图片描述
在这里插入图片描述

人工操作建议:

在策略初期,很不成熟时,可以完全由人工操作,此过程根据任务的复杂度可能需要很长时间。
当策略可以部分完成某段任务后,可以通过人工+策略组合的方式,逐渐增加策略自主的比例。
最终策略成熟度很高后,可由策略完全自主探索,人工在安全/或需要提高效率的时候再介入。
整体上,建议在可能的情况下,尽早增加策略的比例。

Q值的分析:

官方代码没有输出这个指标,笔者在实验三中加入了这个指标,经过340k的训练后,此指标收敛在了0.5左右,在此,可以根据理论计算一下,看看实际与理论的差异:

Q值代表当前状态s下,采取动作a所获得的收益和后续所有收益的期望值。

因为只有任务成功才会有reward=1,按一个20秒后成功的任务(hz=10)来计算:

Q_avg = (0.9951 + 0.9952 + … + 0.995**200) / 200 = 0.63

所以理论值与预测值还是挺接近的,最少在量级上是对得上的。

附录:

lerobot中关于hil-serl的实现仍然存在很多问题,如下(均已修复提交):

关于从训练中恢复:
需要在启动训练命令行所指定的配置文件中修改以下两个配置:

  • “output_dir”: null,
  • “output_dir”: “/home/ubuntu/Downloads/embodient/lerobot/lerobot/outputs/train/2026-01-17/16-50-02_default/”,
    “job_name”: “default”,
  • “resume”: false,
  • “resume”: true,
    若想在resume的同时,修改个别配置,不要在这里继续改了,因为上面的两个配置指定后,就会从output_dir目录中来寻找配置文件了,这个代码逻辑在learner.py中的handle_resume_logic中。要修改个别配置,可直接改output_dir里面缓存的配置文件train_config.json。笔者修改了几个配置如下,特别需要注意的是:因为缓存的配置文件与命令行传入的配置文件有些不同,内容会更丰富,针对同一个配置项可能存在多个地方,所以需要把涉及的地方全修改了。例如critic_lr配置,笔者这里就存在于两个地方,都需要修改。

wandb的小问题:
如果从训练中恢复的话,对于wandb会有一些小问题。例如上一次训练到88k停止了,wandb中已经存在了80k-88k这期间的数据了。这次重新从80k开始恢复训练,会重新记录80-88k之间的数据,所以对于同一个step会记录两份数据,所以wandb的曲线图会看到锯尺,如下所示,笔者分析可能是由于wandb云端的数据库只可以insert,不提供update功能导致的。

修复的bug列表:
bug1:

现象:每次按m键标记此episode成功时,下一个episode在进行了一个step后,会自动标记成功,并再切换到下一个episode(发生概率很高)。

方案:只捕获键盘按下的事件(val=True),弹起的事件忽略,否则弹起的事件容易遗留到下一个step,造成状态错误。

— a/src/lerobot/teleoperators/keyboard/teleop_keyboard.py
+++ b/src/lerobot/teleoperators/keyboard/teleop_keyboard.py
@@ -204,10 +204,11 @@ class KeyboardEndEffectorTeleop(KeyboardTeleop):
delta_z = -int(val)
elif key == ‘f’:
delta_z = int(val)

  •        else:
    
  •        elif val:
    

bug2:

现象:resume后的程序显存占用显著降低(20G-4G),训练性能下降(14hz-11hz),发现是由于buffer数据从显存切换到了内存导致的。

+++ b/src/lerobot/rl/learner.py
@ -970,6 +971,7 @@ def initialize_replay_buffer(
lerobot_dataset=dataset,
capacity=cfg.policy.online_buffer_capacity,
device=device,

  •    storage_device=storage_device,
    

bug3:

现象:找不到配置

— a/src/lerobot/configs/train.py
+++ b/src/lerobot/configs/train.py
@@ -78,7 +78,8 @@ class TrainPipelineConfig(HubMixin):
self.policy.pretrained_path = Path(policy_path)
elif self.resume:
# The entire train config is already loaded, we just need to get the checkpoint dir

  •        config_path = parser.parse_arg("config_path")
    
  •        #config_path = parser.parse_arg("config_path")
    
  •        config_path = f"{self.output_dir}/checkpoints/last/pretrained_model/train_config.json"
    

bug4:

现象:resume时加载预训练模型会有问题,也就是其实没有加载,还是from scatch初始化模型。

— a/src/lerobot/rl/learner.py
+++ b/src/lerobot/rl/learner.py
checkpoint_cfg = TrainRLServerPipelineConfig.from_pretrained(checkpoint_cfg_path)

  • checkpoint_cfg.policy.pretrained_path = cfg.policy.pretrained_path
Logo

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

更多推荐