# Microduck 奖励函数设计:那些让鸭子”学会走路”的玄学,其实是血泪教训
来源: https://www.whatled.com/wp-json/wp/v2/posts
发布: 2026-09-26
Meta Description: 复刻 Microduck 的 PPO 步态策略,最难的从来不是把环境跑起来,而是把奖励函数写对。拆解 microduck_rl 仓库里每一条”死磕出来”的奖励设计规则——符号约定陷阱、无 jackpot 原则、potential-based shaping,还带具体权重和 std 数值。
—
复刻 Microduck 的 PPO 步态策略,很多人第一步就栽了。环境能起来、观测是 61 维、PPO 能迭代,但鸭子要么原地弹簧跳,要么背着地”装死”,要么整只扑街。问题九成不在库,在奖励函数。
Pollen Robotics 在 microduck_rl 仓库的 AGENTS.md 里,把踩过的坑几乎一条条写成了军规。这篇不聊怎么搭环境,专门拆奖励函数——那些”别人写出来你就抄”的权重,背后全是钱买来的教训。
先搞清楚符号约定:一个负号能让策略”奖励”作恶
这是最阴的坑,官方直言”bit 掉过好几个环境”。mjlab 基座自带的一些 cost 函数返回的是正数(≥0),惩罚项要用负权重;而 microduck 自写的 *_penalty、*_l1 函数返回的是负数(≤0,自我取负),这时必须配正权重。
如果给一个自我取负的惩罚项配了负权重,双重否定会让它变成”奖励违反规则”——策略会反过来去刷这个奖励,于是出现撅屁股跳(butt-hop)或者瘫坐(crash-sit)这种诡异的局部收敛。
官方给的”不会错”核验法很硬核:每一轮训练,wandb 里所有 Episode_Reward/ 项必须 ≤ 0。但凡看到某个惩罚项翻正,先别调权重,先查符号。
没有 jackpot:到达目标就按步给钱,等于买暴力
“到达 X 就给奖励”这种写法,RL 会当场表演什么叫贪婪。如果提前到达一个目标态,后面每一步还能继续拿钱,那就是一个 jackpot——策略会不惜代价提前冲进去,然后用最粗暴的动作留在那。
Microduck 的处理是加斜坡(slewed):对受控的状态迁移,内部维护一个匀速融合的目标值(constant-rate blend)。奖励只跟”有没有跟上斜坡”挂钩,提前到一点都不多给。于是对策略来说,慢,才是 argmax——稳稳跟上比狂飙更赚。
原话更直白:单单靠速度上限惩罚,积分下来是个有界的代价,根本压不住策略的暴力倾向。
别再正奖励里奖励”坏状态”
如果某个正向奖励只能在倒地、趴低这种坏状态下拿到,策略会干脆停在那最便宜的姿态里刷钱。解决思路是 potential-based shaping——奖励”进步量”而不是”状态本身”。
比如奖励 Δcos(tilt):躯干立起来的过程中角度余旋值上升才给钱,保持直立给 0,刷无可刷。官方给的检验清单很实用:对每一个正向奖励项,默问一句——如果鸭子躺平(仰面/趴着/侧躺)还能拿回大部分奖励吗?能,那策略就一定会躺。
头重脚轻的代价:跟踪 std 到底该多紧
Microduck 一只 800g、25cm 的鸭子,头部占了体重的 38%。这个 38% 的头,走路时物理上必然要晃。如果给头部姿态跟踪设一个过紧的瞬时 Gaussian std,等于给走路本身重税,策略最后会干脆站着不动。
教训是:Gaussian 的 std ≈ 你还在乎的误差量,不是最大误差。太松了在小误差处没有梯度,太紧了惩罚掉你本来就想要的行为。但也不能直接躺平——只惩罚”可逃逸”的那部分:比如用 L1 惩罚 1 秒 EMA 上的直流偏差(DC bias),让高频晃动互相抵消,只留下真的偏头。
乘法复合优于加法求和:妥协盆地克星
当一堆加法奖励项凑在一起,常常出现一个”妥协盆地”——比如每项都拿 80%,靠一个前倾姿势同时糊弄全部。加法和会给这种半吊子高分。
乘法的 Gaussian 复合则不同:任何一个单项拖后腿,乘积直接被压垮。官方建议在目标态附近用乘法复合。前提是 std 要留够宽,让当前策略的分数肉眼可见地有梯度,否则梯度不可见,改了等于没改。
再看一眼具体的权重配置,感受一下量级(来自 microduck_velocity_env_cfg.py):
upright(直立)权重 2.0,std 取 sqrt(0.05)track_linear_velocity权重 2.0,速度 std 取 sqrt(0.1)air_time(离地时间)权重 3.0,阈值区间 0.125–0.300 秒foot_slip(脚滑)权重 -0.1action_rate_l2(动作平滑)权重 -0.1foot_clearance(抬脚高度)目标从 0.01 提到 0.02,专治拖脚- 还专门加了个
self_collisions惩罚,防止腿怼进躯干
注意 air_time 权重 3.0,是全场最大——它直接决定”会不会走”。
平滑项要后上车:先学技能,再讲纪律
正则项分两类。运动阻断型(角速度、角动量、姿态 std)惩罚的是动态动作本身的物理需求,动态任务里得压得很低;平滑型(action_rate、关节力矩变化率)能压抖动又不挡慢的大幅动作,可以放心加权,但要等技能被学出来之后再引入。
为什么?在某个硬技能还在探索期时,任何”尝试税”都会让”什么都不做”变成最优解。Microduck 的做法是课程式(curriculum):平滑项从近乎 0 开始,随着训练阶段逐步加重。
还要留意一个坑:跨环境抄权重前要先比”奖励质量”而不是”权重”。PPO 看到的是相对优势——同样的 action_rate 权重,在正任务堆大 4 倍的环境里,实际效果会弱 4 倍。
别硬扛物理:25cm 的机器人就是要翻
一条容易被忽略的sim2real军规:25cm 的小机器人摔倒时天然就有 3.5–5.5 rad/s 的角速度。别拿人类尺度感觉去加旋转速度上限——正确做法是把反暴力压力放在冲击和甩动上(|a_z| 加速度、action_rate、支撑门控),而不是限速。
写在最后
奖励函数从来不是”调几个权重”的事,它是把你要的行为翻译成损失语言。Microduck 仓库里那句”每一轮惩罚项必须 ≤ 0″、那句”RL 优化的是奖励的字面意思”,值得复刻机器人的人打印出来贴在屏幕前。
数据来源:pollen-robotics/microduck_rl 仓库的 AGENTS.md 奖励设计章节与 microduck_velocity_env_cfg.py 源码(均有 MIT 开源协议,可公开查阅)。
—
声明:本文分析基于官方开源仓库 microduck_rl 的公开文档与源码,所有参数、权重、std 数值均来自该仓库可验证的实际配置,未做任何编造。
