资讯动态

四足机器人强化学习:Isaac Lab中奖励函数设计的5个实战技巧

发布时间:2026/9/18 9:29:02 来源:尧图企业网站定制
最近四周都在折腾 Unitree B2W 的强化学习仿真从 Isaac Lab 里导入模型到最终让后腿轮式结构稳定跑出大步态最深的体会是四足机器人训练里决定上限的往往不是网络结构也不是训练时间而是那一堆看起来不起眼的奖励函数。这篇文章从 Unitree B2W 这台轮腿混合四足出发聊一聊我在 Isaac Lab 里反复踩坑后总结出来的 5 个奖励函数实战技巧。内容主要面向已经在用或准备用 Isaac Lab、RSL-RL 做四足运动控制的同学也适合刚入坑强化学习、想搞懂“到底怎么调 reward 才能不炸”的新手。我会把每个技巧背后的原理、代码思路和调试现场都摊开讲保证是可落地的经验不是泛泛而谈。1. 为什么奖励函数决定四足机器人的“性格”1.1 从任务目标开始四足运动学习到底在学什么四足机器人运动控制问题本质上是一个高维、强耦合、非线性的序列决策问题。每个时刻机器人都要决定 12 个关节B2W 还要再加 2 个轮子该输出多少力矩目标是让机身按指令前进、转向、保持平衡。传统方法要建精确的动力学模型还要设计复杂的 MPC 控制器而强化学习的思路是用“试错”代替“建模”通过奖励函数告诉策略网络什么行为是好的、什么行为是要避免的。在 Isaac Lab 里训练 B2W策略网络其实根本“看不懂”机器人长什么样。它只能通过观察空间机身倾角、角速度、关节位置、关节速度等感知身体状态然后输出动作目标关节位置或力矩增量。让网络学会“站立”“行走”“奔跑”的唯一信息来源就是奖励函数这盏信号灯。奖励函数给多少、什么时候给、什么条件下扣直接就决定了策略网络会往哪个方向搜索。所以奖励函数不只是训练配置里的几行代码它其实是你在告诉机器人“这个任务我真正看重的是什么”。这就是为什么同一套 PPO 超参数、同一个网络结构换个奖励写法训练结果可能天差地别。1.2 reward hacking奖励函数不仔细机器人就“走捷径”做强化学习的人都绕不开一个词reward hacking。机器人太“聪明”了它不会老老实实按你的意图执行它只会最大化累积奖励所以你设了一条奖励公式它就拼了命去刷分哪怕行为完全变形。我在训练 B2W 时遇到过特别典型的例子为了让机器人快速前进我在奖励函数里写了“机身前进速度越接近目标速度奖励越高”。一开始觉得很合理结果训练到一半发现机器人学会了“后腿轮子高速空转、前腿拖着地、机身躺着往前滑”——速度达标了姿态完全崩了。这就暴露了奖励函数设计的一个核心矛盾你给的目标越简单机器人越容易找到投机取巧的路径。所以四足机器人的奖励函数从来不是一条公式搞定而是一组互相制衡的约束组合。你既要“快”也要“稳”、要“像样”、要“省力”这些目标彼此冲突正是它们的冲突才把行为“逼”回正轨。在 Isaac Lab 这类仿真平台里reward hacking 问题还会更隐蔽仿真环境有精准的状态读取机器人可能利用物理引擎的数值误差、接触模型的漏洞“刷”出不可能的成绩。比如用超高频率抖动关节骗过接触检测或者利用摩擦模型的不连续性实现“蛇皮走位”。这些行为在仿真里看着没问题一上真机立刻完蛋。所以要时刻保持警惕任何一条奖励都要想清楚“如果我是机器人我会怎么钻这个空子”。2. 开始前Isaac Lab 里的 B2W 模型与环境准备2.1 模型导入需要注意的动力学细节Unitree B2W 是 B2 系列的轮腿混合版本后腿末端是轮子中低速时可以用四足模式行走速度起来后可以切换成轮式滚动。这个结构在真实机器人上很灵活但在仿真里却是奖励函数设计的“变数源”。先在 Isaac Lab 里导入 B2W 模型。我用的是从宇树官方 SDK 导出后转成 URDF 的模型。有几个细节必须确认它们直接影响后面训练的稳定性第一轮子与地面的接触参数。轮式结构在仿真里最怕的是“穿透”和“弹跳”。我遇到过轮子陷进地面一半、机身高度直接下降 3 厘米的情况后来把接触刚度调高、阻尼调匹配才解决。不同物理引擎PhysX、MuJoCo对轮子接触的默认参数差异很大建议先在空场景里做一次自由落体和推搡测试观察车身回弹是否自然。第二关节的阻尼和摩擦力矩。URDF 文件里的阻尼系数默认往往偏小导致关节接近“无摩擦”训练出来的策略会利用这种过于理想的条件输出高频抖动的动作。真机上电机响应没这么快部署时会失败。为了让策略更鲁棒我会在仿真里适当加大关节阻尼让关节运动“重一点”。第三机身的质量分布。B2W 的躯干、电池仓、负载模块位置会影响重心。URDF 里默认惯性参数未必与实际完全一致这属于模型误差后面可以用域随机化缓解。但在奖励函数调试阶段先用默认参数跑通流程就好。提示导入模型后第一步不是急着写奖励而是先在 Isaac Lab 里用键盘或脚本控制关节运动确认每个关节的转向、极限位置、速度单位都正常。我在第一次调试时发现后轮速度符号反了直接导致前期所有训练数据全废。2.2 动作空间与观察空间怎么定B2W 比其他四足多两个轮子动作空间变成 14 维前腿 4 个髋关节、2 个大腿关节、2 个小腿关节后腿的髋关节和大腿关节共 4 个再加 2 个轮子的目标速度。这里要注意轮子的控制方式和腿关节完全不同腿关节用位置控制目标角度轮子用速度控制目标角速度。在 Isaac Lab 的 action term 配置里需要区分处理不能在同一个 joint position actuator 里混着写。观察空间我采用的是“本体感知 历史动作”的组合机身线速度3 维机身角速度3 维机身姿态四元数或欧拉角所有关节的位置和速度14 维 14 维脚底/轮子的接触状态4 维上一时刻的动作输出14 维B2W 因为轮子存在地面接触信息比纯腿式更重要。轮子是否触地、打滑状态如何直接决定是四足步态还是轮式滚动。我一般会把轮子转速与机身前进速度的比值——也就是滑移率——作为额外观察量加进去这个信息对网络判断“轮子是否在空转”帮助很大。2.3 训练器与超参数起步参考Isaac Lab 支持 RSL-RL、RLlib 和 rl_games 等训练器。四足运动控制在学术界和工业界最常用的是 RSL-RL 加 PPO这套组合在 Unitree Go2、A1 上验证次数最多教程也最丰富。B2W 没有现成示例可以直接基于 go2 的任务代码改造。我的起步超参参考如下参数值说明训练器rsl_rlPPO 实现稳定environment_num4096并行环境数越多训练越稳learning_rate1e-4初始学习率num_minibatches4minibatch 数量update_epochs5每次更新遍历样本轮数gamma0.99奖励折现因子lam0.95GAE 参数clip_param0.2PPO clip 范围max_iterations1500迭代上限4096 个并行环境对 B2W 这种轮腿混合结构来说不算多我试过降到 2048训练发散概率明显上升。建议有条件的直接用官方推荐的 4096否则后续调 reward 时很难判断是代码问题还是样本不足问题。3. 五个实战技巧逐个拆给你看3.1 技巧一速度跟踪奖励要配合位姿管束很多初学者写四足行走奖励第一反应是设一个机身速度跟踪项lin_vel_error torch.sum(torch.square(target_velocity - base_linear_velocity), dim-1) reward torch.exp(-lin_vel_error)这个写法本身没毛病让机器人学“达到指定速度”是强化学习最直觉的引导方式。但它有一个致命缺陷只约束了速度没约束速度是怎么实现的。于是机器人可以躺着滑、用轮子蹭、甚至跳着翻滚只要前进方向速度够快奖励就照给。我在 B2W 上的解决办法是给速度跟踪项一个“前提条件”先把机身姿态拉回合理范围再谈速度。具体做法是分解成三个独立惩罚项和一个目标项目标项速度误差越小越好用指数函数把误差映射到 0 到 1 区间误差放大系数选 1.0 左右船大难掉头一开始系数太猛会导致梯度爆炸。姿态约束项机身 roll 和 pitch 偏离期望角度接近 0越远惩罚越重同样用指数形式。这个项其实就是告诉机器人“想拿速度奖励先把肚子放平”。高度约束项机身在站立模式下应保持在合理高度范围B2W 站立高度大约 0.4 到 0.5 米之间低于或高于这个区间都扣分。三个惩罚项和一个目标项的比例我调了很多轮最终比较稳定的是速度奖励权重 1.0姿态惩罚权重 2.0 到 3.0高度惩罚权重 1.5。姿态惩罚的权重必须大于速度奖励否则早期探索时机器人很容易为了“向前窜”而牺牲姿态稳定性一旦形成滚动策略后面纠正成本极高。值得注意的是B2W 轮式滚动时机身姿态和四足行走时差异很大滚动状态允许一定前倾。如果全程用同一个姿态惩罚权重轮式模式很难被学出来。我现在是在不同状态下做分段权重检测到轮子高速运转、腿基本不迈步时姿态惩罚的权重自动降低。3.2 技巧二给足部“按时落地”的引导——步态相位注入与抬脚奖励四足机器人的行走不是简单的“往前挪腿”而是一个节律性运动四条腿按一定相位轮流摆动和支撑。强化学习能不能自己涌现出对称的节律性步态理论上可以但训练成本极高而且经常收敛到诡异频率的颤抖步态。我在 4096 个并行环境的配置下跑了 800 迭代发现概率最高的收敛结果还不是优美对角步态而是“四腿快速小碎步”——能往前走但速度和能效都差。我总结下来光靠“速度接近目标”这种高层奖励很难让网络在巨大的动作空间里自发找到周期性的步态模式。解决方法是把“节律”这个先验知识直接写进观察空间和奖励函数里。第一步在观察空间里注入步态相位。我用一个随时间线性增长、然后归零的相位变量 phase范围 0 到 2π。不同腿的期望相位根据标准对角步态设定左前和右后同相位右前和左后同相位两组相差 π。观察空间里给每条腿一个当前相位对应的 sin 和 cos 值。这样策略网络每个 step 都能“看到”自己处在步态周期的哪个位置不需要自己隐式推断周期。第二步在奖励函数里加“相位匹配奖励”当某条腿处于摆动相时它的脚应该离开地面处于支撑相时应该接触地面。实现是计算每条腿的期望接触状态与实际接触状态的一致性四个腿的一致率越高奖励越高。第三步也是很多机器人团队常用的 trick加“脚离地高度惩罚”而不是强制指定腿部轨迹。强制指定轨迹会把网络的学习空间压得很死而且轨迹设计本身就需要大量调参。我选择的是给脚掌一个目标离地高度范围比如摆动相最高离地 0.1 到 0.2 米超出范围就惩罚。网络可以自己决定怎么在时间上分配抬腿动作只要满足“该抬的时候抬、该落的时候落”。我自己测试下来加了相位注入之后训练收敛到稳定步态的迭代数从 900 多轮降到了 600 轮左右提升非常明显。3.3 技巧三用镜像对称项切断“螃蟹步”做四足训练的兄弟应该都见过这种画面机器人学了一身“螃蟹步”四条腿同侧一起动横着往前扭。它不是不能走而是走得又慢又丑而且非常不稳定。为什么会出现这种步态因为四足机器人在结构化奖励下其实存在大量行为等价解只要平均速度够步态的具体形式对奖励函数来说不敏感。要切断这些“非主流”步态最常见的办法是引入左右对称约束。四条腿按身体中心线分成左右两组机器人身体的坐标系里左边和右边的结构完全对称所以期望动作也应该关于中线对称。比如右前腿的摆动规律应该和左前腿相差 π 相位且执行同样的轨迹形状。我在奖励函数里做了一个“镜像误差惩罚”mirror_error torch.sum( torch.square( left_hip_positions - right_hip_positions ) torch.square( left_thigh_velocities - right_thigh_velocities ), dim-1 ) reward mirror_bonus * torch.exp(-mirror_error)实际做的时候要小心坐标换算左右关节的参考方向不一定相同比如左髋关节顺时针为正、右髋关节逆时针为正要先把方向统一再算误差。这个项的权重不能太大太大会导致所有腿动作完全一致变成“兔子跳”权重适中时它的作用只是“排除明显非对称的坏策略”。另一个常见做法是用动作镜像的经验回放让训练数据自动增广左右样本。但奖励层面加对称项在 Isaac Lab 里实现起来更快而且能直接可视化地看到优化方向的变化我推荐先从这个入手。3.4 技巧四奖励项系数动态缩放与课程化起初我会把速度奖励、姿态惩罚、步态匹配奖励、能耗惩罚、力矩惩罚等六七项系数一次性设好然后在训练日志里观察哪些项在“带节奏”、哪些项毫无梯度。但这里有个很尴尬的问题初期机器人在学站、学走根本还不会“跑”你给它一个大的速度奖励系数它只会原地打转而那个巨大的速度误差不断产生大梯度把姿态项学到的平衡能力又冲掉了。这就是奖励函数设计的经典问题奖励之间的相对尺度不平衡会导致策略陷入局部最优。一个很有效的解法是“课程化”奖励系数训练早期降低速度奖励权重、抬高姿态和存活奖励让机器人先学会站稳站得稳了再逐步提高速度奖励权重速度上去了再加入步态质量惩罚。在 Isaac Lab 里我通过读取训练迭代数来动态调整奖励系数progress_ratio current_iteration / max_iterations if progress_ratio 0.2: weight[lin_vel] 0.3 weight[orientation] 3.0 elif progress_ratio 0.6: weight[lin_vel] 1.0 weight[orientation] 2.0 else: weight[lin_vel] 1.5 weight[orientation] 1.0这种分段调整比连续曲线更直观也更容易定位问题。但要注意课程化改变奖励分布后价值网络的估计会不稳定所以调整系数时要同步降低学习率给价值网络一点适应时间。除了奖励课程动作空间本身也可以课程化。B2W 跑得快与走得稳是两种模式可以在前期限制动作输出幅度让机器人只能小角度活动防止它一开始就在极端姿态里狂奔随着训练推进再逐步放开幅度上限。3.5 技巧五为仿真到真实部署留出奖励余量最后一个技巧也是很多人会忽略但最容易导致真机翻车的一点你的奖励函数是在仿真环境里拟合出来的仿真和现实的差异会直接转化为部署时的行为偏差。四足机器人的控制链路特别敏感一点接触参数差异、一点电机延迟差异都可能让仿真里顺滑的步态变成真机上的“原地摔”。奖励函数怎么为 sim-to-real 留余量我的经验是不要追求奖励函数在所有指标上同时压榨到极限。具体来说第一不要设置“极端速度奖励”。B2W 的轮式模式理论最高速度很高但在仿真里把目标速度设到极限策略会严重依赖仿真里理想化的地面摩擦模型换成真实地面立刻打滑。我会把目标速度设为真机安全速度的 80% 左右留出控制余量。第二不要过度惩罚“能耗”。如果奖励函数里力矩惩罚权重特别大策略会学出一套“缩手缩脚”的省力步态——动作幅度极小、关节几乎不发生大幅运动全靠轮子推着走。这种策略在数值上很好看但在真实场景里没有鲁棒性遇到一点地形起伏就趴窝。第三加上域随机化再训练。Isaac Lab 天生支持对质量、摩擦、地面硬度、电机强度注入噪声我会在训练后期开启域随机化让策略学到“在不同条件下都能走”的解。这个解往往不会在仿真奖励分数上刷到最高但它的行为泛化性最好。提示从仿真到真机奖励函数不是一步到位的。我的习惯是先在仿真里用“严格版”奖励训出一版策略看行为质量再切到“加强版域随机化 放松奖励约束”训第二版两版分别部署做对比。真机测试时重点看身体姿态、步态周期是否和仿真接近差异大的地方回到奖励函数里针对性调整。4. 代码级实现在 Isaac Lab 里把奖励函数挂起来4.1 一个 B2W 任务的配置框架Isaac Lab 的任务配置是基于字典的把环境配置、观察空间、动作空间、奖励函数串起来。B2W 没有官方示例我的做法是从isaaclab.envs.mdp四足任务模板改的。核心配置片段如下from isaaclab.envs import ManagerBasedRLEnvCfg from isaaclab.assets import ArticulationCfg b2w_asset_cfg ArticulationCfg( prim_path/World/envs/env_.*/B2W, spawnURDFAssetCfg( asset_pathassets/b2w/b2w.urdf, collision_propsFixedCollisionCfg(), rigid_propsRigidBodyPropertiesCfg(), mass_propsMassPropsCfg(), ), init_stateArticulationCfg.InitialStateCfg( pos(0.0, 0.0, 0.6), joint_pos{ .*hip.*: 0.0, .*thigh.*: -0.8, .*knee.*: 1.6, }, ), ) env_cfg ManagerBasedRLEnvCfg( sceneSceneCfg( num_envs4096, env_spacing2.0, replicate_physicsTrue, ), observationsObservationsCfg( policyPolicyObservationGroupCfg( obsObsCfg( base_lin_velVecLinVelCfg(), base_ang_velVecAngVelCfg(), base_orientationQuatCfg(), joint_posJointPosCfg(), joint_velJointVelCfg(), contact_stateContactStateCfg(), phasePhaseObsCfg(), ) ) ), actionsActionsCfg( joint_actJointPositionActionCfg( asset_namerobot, joint_names[.*], scale0.5, ), wheel_actJointVelocityActionCfg( asset_namerobot, joint_names[left_wheel, right_wheel], scale10.0, ), ), rewardsRewardsCfg( track_velVelocityTrackingReward(), orientation_penaltyOrientationPenalty(), phase_matchPhaseMatchReward(), action_rate_penaltyActionRatePenalty(), ), episode_length_s10.0, )关节初始位置很关键直接给到接近站立姿态的弯曲角度能省掉前期大量“爬起来”阶段的无效探索。我给的 -0.8 和 1.6 是 B2 系列站立姿态的大腿和小腿关节角度参考值实际以你的 URDF 为准。4.2 奖励函数代码骨架在 Isaac Lab 的 manager-based 架构里每个奖励函数是一个接受env参数的类返回每个环境的奖励张量。一个综合性的奖励函数集合看起来像这样def track_linear_velocity(env, target_velocity1.0): current_velocity env.root_physx_view.get_linear_velocities()[:, :2] target torch.tensor([target_velocity, 0.0], deviceenv.device) error torch.sum((current_velocity - target) ** 2, dim-1) return torch.exp(-error) def orientation_penalty(env): roll, pitch, _ get_euler_from_quat(env.root_physx_view.get_quats()) orientation_error torch.square(roll) torch.square(pitch) return torch.exp(-orientation_error / 0.1) def phase_match_reward(env): contact_states get_foot_contact_states(env) expected_contacts compute_expected_contact_from_phase(env.phase) match torch.mean((contact_states expected_contacts).float(), dim-1) return match def foot_height_penalty(env): foot_heights compute_foot_heights(env) swing_mask env.phase_in_swing desired_height 0.1 height_error torch.abs(foot_heights - desired_height) * swing_mask return torch.sum(height_error, dim-1)这里不需要完全照抄关键是理解每个函数在做什么把真实状态与期望状态的误差映射成一个 0 到 1 之间、越接近 1 越好的量再用指数函数控制灵敏度。注意所有操作都要支持 batch 计算Isaac Lab 并行环境会一次性处理几千个实例。4.3 训练命令与日志监控配置写好后训练命令很简单python scripts/train.py --task B2WTask --num_envs 4096 --headless启动后我一般盯着三个关键指标看mean_reward平均奖励是否在稳步上升如果剧烈波动多半是某个奖励项权重过大导致策略在极端行为之间跳转。policy_loss和value_loss正常情况下两者应该稳定下降。如果 value loss 一直居高不下考虑奖励项太多、目标不一致。episode length在 Boulder 这种提前终止设计里episode length 越长说明机器人越少摔倒是平衡能力最直接的指标。实际调参时我会同时打开 TensorBoard把每个奖励分量单独画出来观察哪些项在主导行为变化。这一步比看总奖励有用得多。5. 常见问题与排查技巧实录5.1 训练一开始就发散症状训练前几百个迭代奖励曲线像坐过山车策略有时会完全站不起来像“喝醉的蜘蛛”。我排查的顺序是先检查动作空间的 scale。如果 scale 设置太大初始策略随便一抖就是 45 度的关节角度机器人根本稳不住。B2W 的腿部关节初始 scale 我控制在 0.5 以内轮子速度控制在 10 rad/s 以内。再看环境初始化是否合理。如果所有环境都从完全相同的初始状态开始策略容易过拟合到特定初始条件。建议把初始关节角度加 ±0.1 弧度的均匀随机噪声。最后检查奖励权重是否过大。指数型奖励的系数如果太大比如 5.0 以上初期梯度会爆炸把策略推到极限动作区域。我第一次拿到 B2W 就踩了这个坑速度奖励系数设成 10结果训练全程没有一次能站起来。5.2 步态不对称或“僵尸跳”症状机器人三条腿正常、一条腿僵住或者四腿同步蹦跳像僵尸跳速度很低但奖励分数不低。僵尸跳的根因通常是步态相位奖励没加或者权重太低。没有相位约束时四条腿同步摆动是早期最容易发现的高奖励策略因为同步动作比交替动作更简单、更容易被 PPO 探索到。解决办法就是把 3.2 里的相位匹配奖励加上并且权重适当拉高。一条腿僵硬的问题多数是奖励函数里“脚掌离地高度惩罚”对某条腿的权重设置有问题。这个腿可能一直被压在地面拖着走网络觉得“抬腿的代价太贵”于是放弃使用它。可以单独打印每条腿的离地高度和接触状态确认是哪条腿、哪个奖励项在限制它。5.3 奖励被“刷爆”机器人找到了你的漏洞这是我个人觉得最有意思的问题。某天训练指标突然飙升我兴冲冲地打开仿真回放结果看到机器人正在用后轮疯狂原地摩擦、前腿完全离地靠着反作用力在场上“抽风式”前进。这类 reward hacking 的共性规律是策略学会了利用奖励函数的死角而不是真正完成任务。排查方法是把奖励函数的每个分量按环境实例单独可视化找出“异常高分”来源于哪个分量再针对性修补。比如发现是“轮子速度一致性奖励”太高机器人学会了在三腿站立时让轮子高速空转刷分那就改奖励定义只在轮子触地时才计算一致性。一个更系统的做法是加“行为正则化”除了任务奖励加一个与参考行为分布的 KL 散度约束或者在观察空间里强制要求某些身体部位不能被明显操纵。简单说不要让奖励函数成为唯一的监督信号加入一些“行为应该长什么样”的先验。下面是一个可以直接保存的问题排查速查表现象可能原因排查顺序训练初期发散动作 scale 太大 / 初始状态不合理 / 奖励权重过大动作 scale → 初始化噪声 → 奖励权重僵尸跳相位奖励缺失或权重过低检查相位观察 → 加大相位匹配奖励单腿僵硬该腿的离地惩罚过高分腿打印离地高度 → 降低惩罚系数横着走/螃蟹步缺少镜像对称约束加对称误差项 → 检查左右坐标方向后轮高速空转刷分轮速奖励定义有漏洞只在轮子触地时算轮速奖励奖励分数高但速度低速度奖励权重不足或步态质量差调高速度权重 → 加入步态质量项真机与仿真行为差异大仿真摩擦/质量参数与真机偏差加域随机化 → 降低极端奖励目标注意出现 reward hacking 时不要急着删掉出问题的奖励项。直接删除往往会让策略瞬间崩溃更好的做法是保留该项、降低权重新增一个惩罚项来封堵漏洞。删与堵之间优先选择“堵”。从最开始连跑都跑不起来到现在 B2W 在仿真里能稳定切换四足行走和轮式滚动我在奖励函数上的心得是它不该被当作一堆数学公式更像是一门“表达艺术”。你写下的每一行奖励都是在告诉机器人“这个我真正在意的东西是什么”。而真正可靠的步态永远是目标引导、物理约束和先验知识三者之间的平衡。最后再分享一个实操细节我每次调奖励前都会把改动前后的真机视频放一起对比而不是只看训练曲线。毕竟机器人动起来的样子好不好数值曲线看不出来只有视觉反馈最诚实。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价