尧图精选

清扫机器人强化学习:从稀疏奖励到10项复合奖励工程的实战复盘

🕒 发布时间:2026/9/26 7:39:45 📁 来源:尧图网络
我做这个清扫机器人项目的时候手里的轮式机器人像个没头苍蝇充满电出发要么一头撞上墙角要么在一个区域来回空转覆盖率连40%都摸不到。评估器给出的综合分简单粗暴卡在100分上下纹丝不动。后来我把精力全部转到奖励工程上把奖励函数从1条写到10条从稀疏变成稠密从“只奖结果”变成“既奖过程又管行为”三周时间把评估分拉到了778分。这篇文章就是一次完整复盘从100到77810项复合奖励到底怎么设计、怎么调权重、哪些坑是文档里根本不会告诉你的。适合正在做强化学习落地项目、尤其是机器人类任务的研究生和工程师参考。1. 为什么只拿100分奖励稀疏是原罪1.1 “清扫完成”四个字不等于学会清扫很多入门强化学习的人会有一个朴素想法奖励就等于“任务完成度”。清扫任务嘛扫干净就给正分弄脏扣分一直拖到超时给负分不就完了我一开始也是这么干的。环境返回项就是覆盖率你覆盖率20%就奖励20分60%就奖励60分100%给满100分另外最后超时给个-50。理论上机器人只要一步步把覆盖率做上去就能学到最优策略。实际操作中我发现一个致命问题机器人几乎学不到东西。原因很好理解单个步长的覆盖率增量太小了在几十万步的探索过程中大部分情况下覆盖率纹丝不动策略网络根本不知道哪一步动作让覆盖率涨了、哪一步动作让覆盖率跌了。信号太稀疏强化学习就是瞎子摸象在巨大的动作空间里穷举效率比随机策略好不到哪去。这个现象可以用一个类比来解释。你让一个孩子考试只告诉他“总分及格就行”却不告诉他哪道题对了、哪道题错了、哪个知识点薄弱他只能靠猜。等真正反馈来到的时候他早忘了自己当初做过什么选择。1.2 奖励工程要解决的三件事后来我给自己重新梳理了目标奖励工程阶段要回答三个问题即时反馈每个控制周期都要有局部信号让机器人知道“刚才这个动作带来正面还是负面结果”行为塑形把“清扫完成”这段长旅程拆成多个阶段引导机器人先探索、再覆盖、再返航像教练带训练一样一步一步来约束抑制清扫任务里有大量“坏习惯”需要阻止——撞墙、原地打转、频繁掉头、漏扫角落都需要惩罚项或正则项兜住。把这三件事落到奖励函数上就是一个从稀疏到稠密、从单一到复合的过程。后来我把这套结构总结成一个四层金字塔任务层、过程层、约束层、正则层。每一层承担不同职责分数也会跟着肉眼可见地往上涨。1.3 10项复合奖励的整体轮廓在进入拆解之前先把最终用的10项奖励亮个相后面每一项都会展开说。这里用表格列个总览方便对照编号奖励项类别设计目标1任务完成奖励任务层覆盖率达标并返航后给出一次性大奖励2覆盖率增量奖励过程层每个步长扫到新区域就给正分3栅格新鲜度奖励过程层优先清扫长时间未访问的区域4角落清扫奖励过程层对墙角、边界窄缝区域额外加权5探索新颖性奖励正则层避免长时间停留在一个小范围6路径重复惩罚行为层抑制短时间重复经过同一栅格7碰撞惩罚行为层防止机器人撞击障碍物8能耗惩罚行为层鼓励用更短路径、更小速度变化完成任务9速度平滑度惩罚正则层抑制高频抖动和来回摆头10返航充电奖励任务层强化“扫完回去”的完整闭环这10项不是一次性全部堆上去的而是随着训练阶段逐步加入。这也是奖励工程的核心方法论不要一上来就搞大杂烩而是让模型先学会一个正确行为再把约束条件逐渐加上去。2. 10项复合奖励逐项拆解每一项背后的设计逻辑2.1 任务层完成奖励和返航奖励怎么给任务完成奖励是典型的稀疏奖励但它在整个奖励体系里依然有不可替代的作用。它的作用是给整个episode一个“收敛的锚点”。覆盖率再高机器人如果不知道“扫完需要回来”那它就只是一台无限游荡的扫地机。我用的是分段式完成判定覆盖率超过80%且距离充电桩小于某个阈值时判定为任务完成加上这个奖励如果覆盖率超过80%但没回到充电桩不给如果覆盖率不够就返航给一个很小的负分让它明白“没干完活就回家是不行的”。返航充电奖励是完成奖励的补充。我把它设计成随覆盖率衰减的形态离充电桩越近、覆盖率越高奖励越大。如果机器人在覆盖率50%的时候就急匆匆回去充电这部分奖励会被压得很低。这样做的好处是机器人不会为了贪一个返航奖励而提前放弃清扫任务。这两项加一起权重占比约20%。有朋友会问既然过程奖励已经把清扫行为教得很好了任务级奖励是不是可以不要我的经验是不要省。它虽然稀疏但它定义了“什么叫结束”让强化学习中的贴现回报有个明确的落点价值网络也能学得更稳。2.2 过程层覆盖率增量和栅格新鲜度怎么算覆盖率增量奖励是整个项目里最关键的单项。具体计算方式是维护一张栅格清扫地图机器人每进入一个新栅格或对未清扫栅格完成一次清扫动作给予正分。这里要注意“清扫动作”和“单纯路过”的区别我用的是栅格状态未清扫到已清扫的切换而不是机器人当前所在位置。如果不加这个限制机器人只要拖着身体在房间里面走一遍就算“扫过”那它学到的策略就是快速穿行而不是乖乖清扫所以这一项要跟行为语义严格绑定。栅格新鲜度奖励是为了防止一个隐蔽问题机器人可能只扫了一半区域反复在一个小范围里刷覆盖率增量。栅格新鲜度奖励的定义是某个区域被覆盖后该区域的“新鲜度”随时间衰减机器人清扫一个很久没被访问的位置会得到比清扫刚扫过的位置更高的奖励。这相当于告诉机器人扫完东边要去西边别老盯着一个角落薅。经过实际测量这一项对最终覆盖率的贡献非常明显加入之后整个地图的均匀覆盖程度明显提升。覆盖类奖励的数学表达可以简化成coverage_reward (newly_covered_cells / total_cells) * coverage_weight freshness_reward base_reward * (1 - exp(-time_since_visited / decay_constant))如果你在实际项目里写代码不要忘记把覆盖率增量做归一化。如果不归一化不同环境尺寸下同一个权重的表现会差很多换个地图就得重新调一次参。2.3 角落清扫奖励处理覆盖率指标容易漏掉的盲区在早期版本里我观察到一个现象机器人扫出来的覆盖率图中间区域全是蓝色的已覆盖但四个墙角和家具边缘仍然大片空白。原因是覆盖率只统计面积而角落区域占据面积小即使漏掉总体覆盖率也不会掉太多模型几乎没有动力去处理它们。我的做法是额外设置边界角点栅格集合凡是在这些栅格上发生的“未覆盖到已覆盖”切换获得比普通栅格更高的奖励倍数。本质上是一个二进制注意力掩码把这个掩码乘在覆盖率增量上。下面这行伪代码可以说明思路corner_bonus coverage_increase * (is_corner_cell ? corner_multiplier : 1.0)这个corner_multiplier我一开始设成3.0发现机器人变得过度偏爱贴墙走连中间区域都不正眼看了。后来降到1.8同时把角落奖励的上限做cap模型才平衡下来。这个数字说明了一个通用原则如果你给某个行为的奖励倍率太高模型会“过度拟合”到那个行为上即使它不是任务的主要部分。2.4 行为层碰撞惩罚和能耗惩罚的尺度控制碰撞惩罚的设计其实很直接机器人与障碍物发生碰撞时扣掉一定分数。模型在训练一段时间后会明显减少正对墙壁猛冲的频率。但这里出现过一个有意思的副作用碰撞惩罚设置过高之后机器人开始“龟速蠕动”在一个开阔区域里一步一停死活不敢加速。安全是安全了清扫效率低得可怜。排查之后我意识到碰撞惩罚不应该只看“是否发生碰撞”这个二元事件还应该看碰撞的剧烈程度。我用碰撞瞬间的线速度变化量作为惩罚系数参考轻蹭扣得少猛撞扣得多。这样一来机器人学会了“可以用轻微擦碰纠正路线但不要正面硬怼”行为自然多了。能耗惩罚的初衷是让机器人用更短的路径完成任务。实现方式比较简单每控制周期统计两个数据轮速指令绝对值之和、转向角速度绝对值之和按比例扣分。能耗惩罚的权重不能压得太猛否则机器人会为了省电而不愿意做必要的转向导致覆盖质量下降。我最终用的比例系数是转向惩罚偏低、直线能耗惩罚更低的组合相当于“宁可多转向不要长距离绕路”因为转向对清扫效率的危害比能耗更大。2.5 正则层探索新颖性奖励和速度平滑度惩罚探索新颖性奖励是后期加的一味药。训练到400分阶段时我观察到机器人偶尔会进入一种“小范围震荡”状态在某个局部区域反复画圈看起来忙得一塌糊涂实际上覆盖率的增速几乎为零。位置没变动作倒是很丰富这是典型的局部最优。为此我加入了一项以机器人位置为中心的探索新颖性奖励对机器人在每个栅格位置维持一个停留计时器如果在该位置连续停留超过一定时间则奖励衰减并逐渐转负。速度平滑度惩罚则是为了解决“高频抖动”问题也就是机器人前后轮转速方向快速变化动作频率高得离谱像打摆子一样。这类动作在仿真环境里可能不会立刻造成严重后果但部署到真实机器人上轻则电机过热重则轮子磨损而且清扫覆盖率其实并没有提升。惩罚项取相邻控制周期动作差平方的负值让它“尽量少做无意义的急变速”。正则类奖励最大的特点是它们不会直接提升任务指标但它们能显著缩短训练稳定所需的时间。扔掉它们最终分数可能也能到达600多分但训练曲线会像坐过山车一样起伏不断每次调完参数都要重新等十几个小时。3. 三轮调权迭代从100分到778分的完整实战3.1 第一轮补上覆盖增量奖励从100爬到260第一个版本只有覆盖率比例和完成奖励评估分稳定在100上下。第一轮迭代的目标很明确让“清扫动作”变成“步级可学习反馈”。我加入了覆盖率增量奖励同时保留了基础的覆盖率比例项作为底座权重比例为覆盖率增量:基础覆盖率:完成奖励 ≈ 2:1:5。训练8小时后策略有明显变化。机器人不再原地空转开始走环绕型路线遇到障碍物也知道绕开。评估分涨到260左右。收益是实打实的但问题同样明显——它开始频繁重复清扫同一片区域。原因是覆盖率增量奖励对“新区域”没有定义机器人刚扫过的地方依然可能被再次判定为“清扫动作”于是它就围着客厅中部来回碾压边缘地带一概不管。这一轮给我的核心教训是过程奖励如果不附带“状态变化”条件就会变成刷分漏洞。覆盖率增量奖励如果只看“正在扫”而不看“是否新扫”模型会学到偷懒策略。3.2 第二轮卡住偷懒行为从260爬到480第二轮我做了一次系统性的惩罚补齐。加入路径重复惩罚、碰撞惩罚、能耗惩罚同时把覆盖率增量限定为“该栅格从未被覆盖→被覆盖”才算数重复清扫同一已覆盖栅格不仅不加分还要按重复次数扣分。训练曲线一开始剧烈下跌因为机器人的旧策略突然被扣到怀疑人生。头两小时评估分甚至跌到140我当时差点回滚代码。但坚持等训练曲线重新爬升情况开始好转碰撞次数明显下降重复路径占比大幅收窄地图覆盖率上升了一个台阶。最终评估分稳定在480附近同时每次episode的耗时比第一轮缩短了约20%。这里我想专门提一点在加惩罚项之后看到分数腰斩是正常现象。不要急着回滚要给训练器足够多的时间让策略网络慢慢适应新反馈。判断标准不是前几个小时的分数而是奖励的各项分解是否在按预期方向变化。3.3 第三轮精细正则与课程式权重从480到778480分到778分这段增量主要来自三块角落清扫奖励、探索新颖性奖励、速度平滑度惩罚。这三项加入后覆盖率从80%级别提升到接近93%评估分的上边界大幅上移。第三轮还有一个关键调整权重安排从固定值改成课程式。训练的前1/3阶段把行为约束类权重的比例调低让机器人自由探索先学会“敢跑”中间1/3阶段把覆盖率增量奖励权重逐步拉高引导它关注清扫效率后1/3阶段把返航和正则类奖励权重加到最大帮它收敛出最终策略。在强化学习里这本质上是课程学习curriculum learning在奖励权重维度的实践。最终训练配置的权重分布大致如下奖励项最终权重说明任务完成奖励15.0一次性稀疏覆盖率增量奖励5.0步级归一化栅格新鲜度奖励2.0步级时间衰减角落清扫奖励1.8步级角点掩码探索新颖性奖励1.2步级停留惩罚路径重复惩罚-3.0步级重复扣除碰撞惩罚-2.5步级强度相关能耗惩罚-0.8步级速度积分速度平滑度惩罚-0.5步级动作差分返航充电奖励8.0结束步覆盖率相关这个表不是给读者照抄的。不同机器人底盘、不同传感器精度、不同房间户型都会影响最优权重但“任务级奖励为主、过程级奖励兜底、惩罚类不要喧宾夺主”这个结构是可以直接复用的。4. 那些年踩过的坑奖励工程排查实录4.1 奖励爆炸与尺度失衡第一次把10项奖励全部堆上去的时候我的total_reward瞬间冲上三位数价值损失的数值也在同比例放大。说白了就是每种奖励都有自己的量纲覆盖率增量是零点几的小数能耗惩罚是每步的微小负分完成奖励一次给15分结果总和完全被任务级奖励主导过程奖励的梯度信号被彻底淹没。处理办法是分两层第一层每个子奖励先做归一化或裁剪保证单步输出范围大致落在[-1,1]第二层把所有子奖励乘上各自权重后求和再对最终求和结果做一个全局Clip。这样一来虽然单项权重仍不同但没有任何一个子项能独占梯度。这个习惯后来帮我省了无数次调参时间。另一个经验是如果你发现机器人“什么都不做”或“只做最小动作”第一怀疑对象就是惩罚类奖励整体尺度过大把动作熵压没了。4.2 Reward Hacking机器人作弊的三种姿势Reward Hacking奖励漏洞是我最想强调的实战问题。下面三种情况我都遇到过第一种是原地转圈刷增量奖励。早期版本里覆盖率增量只判断“清扫动作”如果一个区域状态被错误地重复翻转为已覆盖机器人在原地转圈就能蹭到奖励。我把栅格状态的判定改成不可逆状态转移已经覆盖的栅格不能再产生增量奖励再加一个重复清扫惩罚才把这个洞堵上。第二种是撞墙蹭“探索”奖励。加入探索新颖性奖励后机器人发现对着墙反复蹭位置变化微小但动作熵很大能拿到不少“探索奖励”实际却是在浪费时间。修复方式是将探索奖励与“新栅格首次进入事件的间隔”挂钩而不是与动作频率挂钩。第三种是返航奖励作弊。覆盖率刚达到80%阈值就立刻回头不管路径上还有大片漏扫区域。我在返航奖励公式里增加额外条件返航路径的平均覆盖率也达到指标如果中途穿过大块未清扫区域返航奖励直接打折。这是典型的“状态型奖励替代事件型奖励”思路——不奖励“你做了返航这个动作”而奖励“你达成了一次高质量返航”。这三类问题有一个通用诊断方法把训练好的策略放在测试环境里跑一遍用可视化工具盯着机器人的轨迹看它有没有做出“反人类”的操作。只要发现了那就是奖励设计出了漏洞而不是模型本身的问题。如果只想看数字很多奖励漏洞会一直潜伏到最终评估时给你致命一击。4.3 奖励过山车训练振荡的排查方法训练后期我遇到过一个很头疼的问题评估分从650涨到700以上又在一夜之间跌回550然后重新爬到一个新高位如此反复。奖励曲线在训练日志里呈现剧烈的锯齿形振荡。排查第一步我检查了缓冲区里新旧样本的比例。我发现自己在调整某个奖励权重后没有清空旧的replay buffer旧样本里的奖励值全部是按旧公式算出来的带着不一致的信号价值网络自然会精分。第二步是检查网络学习率。调高权重后奖励分布的整体偏移变大了此时价值网络的loss会震荡得很厉害。我的做法是把学习率从3e-4降到1e-4同时增加mini-batch数量稳定价值网络收敛速度。第三步则是“单变量原则”每次只改一个奖励项或一个权重训练至少跑两轮后对比评估分确认没有回归再动下一个变量。这套排查流程执行下来振荡问题被控制住了分数也终于锁在778附近。4.4 仿真与实机的奖励一致性仿真里跑得好好的策略搬到实体机器人上经常失效。最典型的原因是摩擦参数。仿真环境如果不显式建模轮子与地面间的滑动摩擦、底盘受载后的惯性变化那么模型学到的速度控制策略在真实地面上就会表现出明显漂移和打滑。我在项目后期花了大量时间做域随机化。具体操作有两步第一步在仿真环境里对摩擦系数、轮子打滑率、传感器噪声标准差都加了一个随机区间而不是固定值让策略学会在参数扰动下依然有效第二步把奖励函数里对“最高速度”“最大加速度”的要求调到实体机器人底盘真实可行的范围内。比如仿真里机器人能轻松做到3m/s速度转弯但实体底盘在这个速度下的清扫质量会崩坏那就得把速度相关的奖励约束上限降下来。做机器人强化学习仿真结果始终是“必要条件而非充分条件”奖励工程定出来的行为边界最好一开始就贴着物理可行性画这是能省下大量移植调试时间的最重要经验之一。5. 代码落地与监控如何让奖励工程可维护5.1 奖励计算模块的结构化写法10项奖励如果全堆在一个函数里十有八九会变成一坨混乱无比的意大利面。我最后的结构化方案是每个奖励项实现成独立函数输入统一为一个包含状态、动作、环境信息的字典输出为一个浮点数。一个简化的示例结构如下class CleaningReward: def __init__(self, config): self.config config self.cells {} self.record {} def coverage_increment(self, env_info): newly env_info[newly_covered_cells] total env_info[total_cells] return normalize(newly, 0, total) * self.config[coverage_weight] def collision_penalty(self, env_info): impact env_info.get(collision_intensity, 0.0) return -clip(impact, 0, 1) * self.config[collision_weight] def speed_smoothness(self, action_seq): diff (action_seq[-1] - action_seq[-2]) ** 2 return -diff * self.config[smooth_weight] def step_reward(self, state, action, env_info): r 0.0 r self.coverage_increment(env_info) r self.freshness_reward(env_info) r self.corner_bonus(env_info) r self.exploration_novelty(state) r self.repeat_penalty(state) r self.collision_penalty(env_info) r self.energy_penalty(action) r self.speed_smoothness(action) return clip(r, -5, 5) def episode_reward(self, done_info): return self.completion_reward(done_info) self.return_home_reward(done_info)这种写法最大的好处是便于做A/B测试。想验证某个奖励项是否有效注释掉一行或者把权重调成0再对比训练曲线即可。全部耦合在一起你根本不知道自己改的是什么。5.2 奖励日志一条奖励一条命只记录总分是无法做奖励工程的。我在每一行训练日志里都写入10个子奖励项的均值、最大值和最小值的滑动统计同时记录episode长度和覆盖率。这样当分数异常下降时能快速定位是覆盖率增量丢梯度了还是碰撞惩罚在某个区域突然激增。训练可视化这块建议直接用TensorBoard或者WandB之类的工具把每次实验配置也一并记录方便事后回溯。有一个细节值得再强调奖励日志不只是训练时看模型冻结后跑测试集时也要开。评估时不打印不代表它没有用很多reward hacking在训练阶段被平均曲线掩盖了只有逐项回放测试轨迹时才能暴露出来。我现在养成一个习惯每跑一轮完整的训练和评估都会写一小段实验备注记录改了哪些权重、观察到什么现象、怀疑什么问题。初期看起来是浪费时间等实验数量上来之后这些备注直接相当于一份自建的项目文档价值远高于一时半刻的调参效率。5.3 回归测试和单变量原则奖励工程因为改动频繁特别容易发生“改了这个却坏了那个”的隐性回归。我的做法是固定三套标准房间布局作为基准测试环境任何权重修改、公式修改、新奖励项加入都要在这三套布局下各跑一轮短训练确认评估分没有明显回退。这三套布局分别对应开阔单间、窄长走廊型、多障碍物客厅型。有了这个回归测试778分的成绩才能保持稳定而不是某一次运气好刷出来的。关于“单变量原则”我想再补充一点强化学习训练本身带有随机性只跑一次实验两次实验还不能下结论。如果你的资源允许同一个配置至少用3个不同的随机种子跑训取评估分的均值和方差。我后期很多权重调整的结果如果只看单次训练经常会得出错误的结论。三种子平均值才是可比较的。最后再分享一点个人体会奖励工程做到778分收尾我先说一个反常识的话真正让我拿到分数的不是“设计出了完美的奖励函数”而是“建立了能够快速判断某个奖励项到底有没有用的实验体系”。单项奖励设计得再巧妙如果没有日志归因、没有基准测试、没有回归保护最后都会变成玄学调参。反过来只要实验体系在即使一个奖励项设计得不好也能在半天之内发现并修正。做机器人强化学习就是这样奖励函数不是写出来的是榨出来的要一轮一轮地跟模型相互试探直到它在你划定的行为边界里稳定地交出好结果。778分不是终点换一套户型、换一个底盘这套流程依然要在新的环境里重新磨合一轮——但有了这套方法你至少知道下一步该往哪里走。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →