四足机器人平地“翘腿”问题:Reward 调参检查表

问题背景

平地 locomotion 训练里,有时会看到一个很典型的坏习惯:机器人在平地上走着走着,某条腿或某几条腿明显“翘起来”,看起来像在主动抬腿、拖腿、单腿悬空,甚至形成不自然的跳步。

这类现象不一定是 policy “不会走”,更常见是 reward 组合给了它一个可钻的空子:

速度跟踪奖励够高
+ 抬腿/腾空奖励过强或门控太松
+ 接触/滑脚/姿态/能耗约束不够
= 平地上也愿意把腿抬得很夸张

这篇整理一下:如果目标是平地稳定行走,不希望无意义翘腿,四足机器人强化学习里哪些 reward 最值得优先检查和调整。

先看官方基线怎么做

以 legged_gym / Isaac Lab 的 velocity tracking 任务为参考,常见 reward 结构大概是:

tracking_lin_vel      # xy 线速度跟踪,正奖励
tracking_ang_vel # yaw 角速度跟踪,正奖励
lin_vel_z # base 垂向速度惩罚
ang_vel_xy # roll/pitch 角速度惩罚
orientation # 身体姿态/重力方向惩罚
base_height # 身体高度惩罚,可选
torques # 力矩惩罚
dof_acc # 关节加速度惩罚
action_rate # action 变化惩罚
feet_air_time # 脚腾空时间奖励
collision # 非足端碰撞惩罚
feet_contact_forces # 足端接触力过大惩罚,可选
stand_still # 零速度命令下的关节偏离惩罚,可选

legged_gym 默认 flat/rough locomotion 里,feet_air_time 是一个很关键的项:它奖励“长步幅/有节奏迈步”,但如果权重、阈值、门控条件不合适,也容易鼓励不必要的抬腿。

Isaac Lab 的 Anymal-C flat 配置里也能看到类似思路:平地任务会保留速度跟踪、姿态、力矩、feet air time 等项,同时平地版会关闭高度扫描和地形 curriculum。

一句话结论

如果平地出现“翘腿”,我会按这个优先级调:

1. 降低或重新门控 feet_air_time
2. 增强足端接触稳定性:脚滑、接触力、接触时序
3. 增强姿态/高度约束:orientation、base_height、lin_vel_z、ang_vel_xy
4. 增强动作代价:action_rate、dof_acc、torques、dof_vel
5. 检查速度命令分布和站立命令:不要让低速/零速也被奖励抬腿

1. feet_air_time:最容易导致“翘腿”的 reward

feet_air_time 本意不是让机器人“把腿抬高”,而是避免拖着脚走,让机器人形成有步态周期的迈步。

在 legged_gym 里,它大致逻辑是:

contact = foot_contact_force_z > threshold
first_contact = feet_air_time > 0 and contact
feet_air_time += dt
reward = sum((feet_air_time - 0.5) * first_contact)
reward *= norm(command_xy) > 0.1
feet_air_time reset when contact

这个设计有几个要点:

  • 只在脚重新落地的瞬间给奖励;
  • 奖励的是“腾空时间超过阈值”的部分;
  • 速度命令很小时不给这个奖励;
  • 接触检测如果噪声大,会影响这个 reward 的真实性。

如果平地翘腿明显,优先检查:

调参建议

1)降低 feet_air_time 权重

如果当前权重比较大,可以先降到原来的 1/2 或 1/4。

例如:

# legged_gym 风格
feet_air_time = 1.0 # 如果翘腿明显,可以先试 0.5、0.25、0.1

Isaac Lab flat 里 Anymal-C 示例曾把 flat 任务的 feet_air_time.weight 设置为 0.5;而通用 velocity rough cfg 中有更保守的 0.125。这说明这个项本来就需要按任务调,不能盲目套默认值。

2)提高触发门槛,而不是奖励无限抬腿

如果 reward 写成“脚越高越好”或“离地越久越好”,policy 很容易学成夸张抬腿。

更稳的写法是只奖励合理区间:

air_time 太短:可能拖脚,少奖励
air_time 合理:奖励
air_time 太长:不再增加,甚至惩罚

可以把线性奖励改成 clipped reward:

air_time_good = torch.clamp(last_air_time - 0.20, min=0.0, max=0.25)

也就是只鼓励“离地一小段时间”,不要鼓励一直悬空。

3)低速/零速命令下关闭 feet_air_time

这是非常关键的一条。

如果 command_xy 很小,机器人本来应该站稳或慢走,不应该为了拿 air-time 奖励而原地抬腿。

建议:

moving = torch.norm(commands[:, :2], dim=1) > 0.15
reward_feet_air_time *= moving

对于平地低速任务,可以把阈值从 0.1 提到 0.15 ~ 0.25 试试。

2. feet clearance:不要把“抬脚高度”奖励写得过强

很多项目会额外加 feet_clearance,鼓励摆动腿离地,避免拖脚。

这在崎岖地形上有用,但在平地上要小心:如果 clearance reward 太强,就会直接把 policy 推向“高抬腿”。

调参建议

平地建议把 clearance 写成目标区间,而不是越高越好:

目标:摆动腿离地 3~8 cm 足够
超过目标:不给更多奖励,必要时惩罚
支撑腿:不应该因为 clearance 得分

示例思路:

target = 0.05  # 5 cm
swing = foot_not_in_contact
err = torch.square(foot_height - target)
reward_clearance = torch.exp(-err / sigma) * swing * moving

如果已经有 feet_air_time,平地任务里可以先不加 clearance;如果必须加,权重要比速度跟踪、姿态稳定小得多。

3. feet_slide / contact stability:防止用奇怪接触骗速度

翘腿有时不是单独发生,而是伴随:

  • 支撑脚在地上滑;
  • 另一条腿长期悬空;
  • 身体靠几条腿硬拖着走;
  • 接触力尖峰很大。

这时应该补足接触稳定相关惩罚。

推荐检查项

1)脚滑惩罚 feet_slide

逻辑:脚在接触地面时,足端水平速度不应该太大。

contacts = contact_force_z > threshold
feet_xy_vel = torch.norm(feet_vel[:, :, :2], dim=-1)
penalty_feet_slide = torch.sum(feet_xy_vel * contacts, dim=1)

如果平地走路像“溜冰”或“拖脚”,这个项很重要。

2)接触力过大惩罚 feet_contact_forces

legged_gym 里有类似项:超过 max_contact_force 的部分才惩罚。

penalty = sum(clip(norm(contact_force) - max_contact_force, min=0))

这能减少用砸地、跳步换速度的策略。

3)非足端碰撞 collision / undesired_contacts

如果大腿、小腿、机身碰地,应该惩罚或直接 reset。

平地翘腿如果伴随大腿乱甩,这项会很有用。

4. 姿态与高度:让身体没有理由通过翘腿维持奇怪姿态

平地行走的身体应该相对稳定。需要重点看这几项:

lin_vel_z      # base 垂向速度惩罚
ang_vel_xy # roll/pitch 角速度惩罚
orientation # 身体倾斜惩罚
base_height # 身体高度偏离惩罚

调参建议

1)增强 orientation / flat_orientation_l2

Isaac Lab 的 Anymal-C flat 配置里,flat_orientation_l2.weight = -5.0,比 rough 任务更强调平地姿态稳定。

如果身体晃动很大,或者靠倾斜身体换步态,可以增强这个项。

2)打开或增强 base_height

legged_gym 默认里 base_height 有时权重是 0。若平地出现蹲低、翘腿、跳步,可以加入身体高度目标。

base_height_target = nominal_stand_height
base_height = -k * (base_height - target)^2

注意:高度目标不要太死,否则会牺牲自然步态。可以先用小权重。

3)不要只靠高度惩罚解决翘腿

翘腿是足端/步态问题,base_height 只能约束身体,不一定能直接约束腿。更推荐和 feet_air_time、feet_slide、action_rate 一起调。

5. 动作平滑与能耗:抑制“猛抬腿”

如果 policy 抬腿动作很突兀,通常要看:

action_rate  # 相邻 action 差分
dof_acc # 关节加速度
dof_vel # 关节速度
torques # 力矩/能耗

调参建议

1)先调 action_rate

action_rate 对抑制动作跳变很直接。

penalty_action_rate = sum((last_action - action)^2)

如果机器人突然抽腿、抖腿,可以把权重从 -0.01 往 -0.02、-0.05 试。

2)再调 dof_acc / dof_vel

关节加速度惩罚能让动作更柔和,但过大会让机器人迈不开腿。

建议小步增加,不要一次调太大。

3)torques 不要压得过狠

力矩惩罚太强可能导致机器人不敢支撑,反而出现拖脚、塌陷或奇怪步态。它适合做能耗约束,但不是解决翘腿的第一优先级。

6. 速度跟踪:别让 policy 为了速度牺牲步态

速度跟踪 reward 一般是主任务:

tracking_lin_vel = exp(-lin_vel_error / sigma)
tracking_ang_vel = exp(-ang_vel_error / sigma)

如果 tracking 权重远大于稳定性和代价项,policy 会优先“速度达标”,哪怕姿态很丑、腿乱抬。

调参建议

  • 不要一开始把速度范围开太大;
  • 平地先从低速到中速 curriculum;
  • 检查是否大量采样横向速度 lin_vel_y,有些机器人会为了横移学出奇怪抬腿;
  • 低速命令下要有 stand_still 或低速稳定约束;
  • 如果只关心前进,先缩小 lin_vel_y 和 ang_vel_yaw 范围。

7. stand_still:解决“原地也翘腿”

如果机器人在零速度命令下也抬腿,优先检查:

feet_air_time 是否在低速时仍有奖励
stand_still 是否开启
默认关节姿态是否合理

legged_gym 里 stand_still 的思路是:当速度命令很小时,惩罚关节偏离默认姿态。

penalty = sum(abs(dof_pos - default_dof_pos)) * (norm(command_xy) < 0.1)

平地任务建议打开这个项,但权重不要太大,否则慢速起步会变迟钝。

推荐调参顺序

不要一次改十几个 reward。建议按下面顺序做 ablation:

Step 1:先看行为视频和每项 reward 曲线

重点看:

rew_feet_air_time 是否异常高
rew_tracking_lin_vel 是否已经很好
penalty_action_rate / dof_acc 是否太小
orientation / base_height 是否几乎不起作用
feet contact 是否有长期单脚悬空

Step 2:先降 feet_air_time

推荐试验:

feet_air_time: 1.0 -> 0.5 -> 0.25 -> 0.1

同时确保:

feet_air_time *= norm(command_xy) > 0.15

Step 3:加脚滑和接触力惩罚

如果没有 feet_slide,建议加。

feet_slide: -0.05 ~ -0.2
feet_contact_forces: -0.005 ~ -0.05

具体数值要看 reward 的量级,先看 TensorBoard 里每项平均值,保证单项不会压过主任务。

Step 4:增强平地姿态

orientation / flat_orientation_l2: 适度增大
lin_vel_z: 保持较强惩罚
ang_vel_xy: 适度增大
base_height: 从小权重开始打开

Step 5:最后调动作代价

action_rate: -0.01 -> -0.02 / -0.05
dof_acc: 小步增加
torques: 小步增加,不要过强

一个可操作的平地 reward 配方

下面不是标准答案,只是一个 debug 起点:

class rewards:
class scales:
tracking_lin_vel = 1.0
tracking_ang_vel = 0.5

lin_vel_z = -2.0
ang_vel_xy = -0.05
orientation = -2.0 # 平地可适度增强
base_height = -0.5 # 从小权重开始试

feet_air_time = 0.25 # 先比默认更保守
feet_slide = -0.1 # 如果框架里没有,需要自己加
feet_contact_forces = -0.01
collision = -1.0

torques = -1e-5
dof_acc = -2.5e-7
action_rate = -0.02
stand_still = -0.2 # 低速/零速防止原地乱抬腿

如果训练后机器人变得“不敢迈步”,说明约束过强:先放松 stand_still / action_rate / dof_acc / orientation,而不是马上把 feet_air_time 拉很高。

实验记录建议

每次只改一组参数,记录:

1. seed
2. reward scales diff
3. 命令速度范围
4. 训练迭代数
5. 是否出现翘腿
6. 是否脚滑
7. 是否速度跟踪变差
8. 视频链接或截图

推荐对比表:

实验 feet_air_time stand_still feet_slide orientation 现象
A baseline 1.0 0 0 0/-? 平地翘腿明显
B lower air time 0.25 0 0 0/-? 翘腿是否减少
C add stand still 0.25 -0.2 0 0/-? 原地是否稳定
D add slide penalty 0.25 -0.2 -0.1 -2.0 是否更自然

常见误区

误区 1:看到翘腿就直接加大 torque penalty

不一定有效。翘腿通常是步态奖励/接触奖励的问题,torque penalty 只是间接约束。

误区 2:把 feet_air_time 直接关掉

可以作为 debug,但不一定是最终方案。完全关掉后可能学出拖脚、小碎步。更好的方式是降低权重、加门控、限制奖励区间。

误区 3:平地也使用很强的 clearance reward

崎岖地形需要 clearance,平地不一定需要。平地上 clearance 太强,很容易变成高抬腿。

误区 4:只看总 reward

总 reward 变高不代表步态更好。一定要看每项 reward 和视频。

总结

平地翘腿不是单个参数的问题,通常是 reward balance 出了偏差。最常见原因是:

feet_air_time / clearance 鼓励抬腿
但 contact stability / posture / smoothness 没有把动作拉回自然范围

我的调参优先级是:

先降 air-time 和 clearance 的激励
再补 feet_slide / contact force / undesired contact
再增强 orientation / base_height / lin_vel_z / ang_vel_xy
最后小步调 action_rate / dof_acc / torques

平地任务的目标不是“腿抬得漂亮”,而是:

速度跟踪准确
脚不拖、不滑、不乱悬空
身体稳定
动作平滑省力

参考资料

  • Nikita Rudin et al., Learning to Walk in Minutes Using Massively Parallel Deep Reinforcement Learning, CoRL 2021 / arXiv:2109.11978.
  • leggedrobotics/legged_gym:官方 README 说明 reward scale 非零即启用同名 reward function,并给出 base locomotion reward 配置。
  • legged_gym/envs/base/legged_robot_config.py:tracking_lin_vel、tracking_ang_vel、feet_air_time、lin_vel_z、ang_vel_xy、torques、dof_acc、action_rate 等默认配置。
  • legged_gym/envs/base/legged_robot.py:_reward_feet_air_time、_reward_tracking_lin_vel、_reward_orientation、_reward_base_height、_reward_feet_contact_forces 等实现。
  • Isaac Lab locomotion velocity task:RewardsCfg 与 Anymal-C flat config 中的 flat_orientation_l2、dof_torques_l2、feet_air_time 设置。
知识共享许可协议