第 6 章 奖励与课程学习¶
强化学习的工程量,大头不在算法而在"怎么把任务意图翻译成奖励、怎么把难度铺开、怎么让策略在仿真里学到的东西能扛住真机的扰动"。本章把 Isaac Lab 里的三件配套工具讲清:奖励项的组织与权重调参、以 event/curriculum manager 承载的课程学习、以及域随机化(domain randomization)的事件表达。示例以内置的四足速度任务与机械臂 lift 任务为骨架,符号均为源码可查的真实配置。
1. 奖励项与权重调参¶
奖励不是一个标量函数,而是一组 RewTerm 的加权和。一个典型的四足平地速度跟踪任务(anymal 类 velocity 任务)把"要跟的速度"与"别乱动"拆成正负两组项:
奖励项(RewTerm) |
权重 | 作用 |
|---|---|---|
track_lin_vel_xy_exp |
+1.0 | 期望:水平速度跟踪(指数核,std 控容差) |
track_ang_vel_z_exp |
+0.5 | 期望:偏航角速度跟踪 |
feet_air_time |
+0.125 | 鼓励抬脚、防拖步 |
lin_vel_z_l2 |
−2.0 | 惩罚:机身竖直抖动 |
ang_vel_xy_l2 |
−0.05 | 惩罚:非偏航的角速度 |
joint_torques_l2 |
−1.0e−5 | 惩罚:力矩大 |
joint_acc_l2 |
−2.5e−7 | 惩罚:关节加速度大 |
action_rate_l2 |
−0.01 | 惩罚:动作跳变(平滑正则) |
undesired_contacts |
−1.0 | 惩罚:不该碰的部位接触地面 |
三条经验性判据:
- 正项给"要什么",负项给"别怎样",且惩罚量级要压得住——否则策略会靠牺牲平滑度换跟踪奖励。力矩/加速度这类物理量数值大,权重取到
1e−5 ~ 1e−7才能与[-1,1]尺度的跟踪项平衡。 weight=0.0是一项被显式关掉的开关。内置配置里flat_orientation_l2、joint_pos_limits常设 0——留作调参旋钮,按需开。- 指数核(
*_exp+std)比硬l2更宽容小误差、对大误差饱和,是速度跟踪的常用形式;std越小越"较真"。
2. 早停与 bonus 权衡¶
termination 不只是"回合结束",它是奖励塑形的一部分:
- 早停(真终止) 切断已经失败或退化的高奖励态。例如
bad_orientation(机身翻转)、joint_pos_out_of_limit(关节超限)设time_out=False,让策略从失败里早点重来,而不是在烂状态上继续刷分。 - 超时截断(
time_out=True) 表示"回合到时长上限",价值网络需对末态 bootstrap——它不该被当成失败。 - bonus/惩罚项(如
feet_air_time、action_rate_l2)与早停是相互制衡的:bonus 太猛会诱导"刷动作"(为抬脚而抬脚),需配合惩罚项和合理的max_episode_length夹住。
判据:一个健康配置里,episode 平均长度应从早期的短(频繁失败早停)随训练先升后稳(第 5 章 cartpole 从 8.8 撑满到 300 即此形);若长度长期贴着上限但 reward 不涨,多半是早停太松或 bonus 设错。
3. 事件化课程¶
课程学习(curriculum)由 curriculum manager 承载,配置项是 CurriculumTermCfg。Isaac Lab 里两种形态:
难度自适应(四足地形)。terrain_levels_vel 依据"机器人是否走够了期望距离"把环境整体搬到更难或更简单的地形块上——走得远就上移难度、走不动就降级:
curriculum = CurriculumTermCfg(
func=velocity_env_cfg.terrain_levels_vel, # 任务侧定义的课程函数
)
## 仅当 TerrainImporter 用 terrain_generator(分层难度地形)时可用
它靠的是场景里的 TerrainImporter 把环境网格排成难度梯度,课程函数按每环境表现搬移网格索引——这是"事件化课程"最直接的形态:难度是环境属性,被 manager 每步动态调。
成功率驱动的自适应域随机化(ADR)。lift 任务的课程把"难度"本身做成一个被课程维护的量。一项 CurrTerm(func=mdp.DifficultyScheduler, …)(CurrTerm 即 CurriculumTermCfg 的别名)按每环境成功率在其上下界间升降难度;另有一组课程项 func=mdp.modify_term_cfg,用 address 指到目标配置叶子(如关节位置噪声的 n_min/n_max),以 modify_fn=mdp.initial_final_interpolate_fn 按当前难度在 initial_value 与 final_value 间插值:
@configclass
class CurriculumCfg:
# 难度调度:按成功率在 0~10 间升降(ADR 的核心)
adr = CurrTerm(func=mdp.DifficultyScheduler,
params={"init_difficulty": 0, "min_difficulty": 0, "max_difficulty": 10})
# 另一项课程:把某配置叶子随难度从初值插值到终值
# 这里把关节位置观测噪声的下界从 0 推到 -0.1
joint_pos_noise_min = CurrTerm(
func=mdp.modify_term_cfg,
params={"address": "observations.proprio.joint_pos.noise.n_min",
"modify_fn": mdp.initial_final_interpolate_fn,
"modify_params": {"initial_value": 0.0, "final_value": -0.1,
"difficulty_term_str": "adr"}},
)
二者合起来把"成功率→难度→扰动范围/噪声/终止边界"连成闭环:策略越强、难度升得越高、施加在它身上的噪声与随机扰动也越大。这正是第 9 章 sim-to-real 稳健性的训练侧来源。
两类课程的驱动量都是"每环境表现",落点不同:
flowchart LR
perf["每环境表现<br/>走够距离 / 成功率"] --> cm["curriculum manager"]
cm -->|"terrain_levels_vel"| terr["TerrainImporter<br/>难度梯度地形网格"]
cm -->|"DifficultyScheduler(ADR)"| diff["难度 d ∈ [min, max]"]
diff --> mod["modify_term_cfg<br/>address 指向配置叶子"]
mod -->|"initial_final_interpolate_fn"| noise["关节位置噪声等 term<br/>随 d 插值"]
坑:地形课程依赖分层地形
terrain_levels_vel 只在 TerrainImporter 用 terrain_generator 生成难度梯度地形时可用;平地场景里挂它等于没有课程。
4. 域随机化¶
域随机化落在 event manager 的 EventTermCfg,靠 mode 决定触发时机:"startup"(仅初始化一次)、"reset"(每次环境重置)、"interval"(固定周期)。函数从 mdp 里取,参数以分布区间给(内部用 isaaclab.utils.math.sample_uniform 采样):
@configclass
class EventCfg:
physics = EventTermCfg(
func=mdp.randomize_rigid_body_mass, mode="reset",
params={"asset_cfg": SceneEntityCfg("robot"),
"mass_distribution_params": (0.8, 1.5), # 质量随机到标称的 0.8–1.5 倍
"operation": "scale"},
)
randomize = EventTermCfg(
func=mdp.randomize_joint_parameters, mode="reset",
params={"asset_cfg": SceneEntityCfg("robot", joint_names=[".*"]),
"friction_distribution_params": (0.0, 3.0),
"armature_distribution_params": (0.01, 0.1),
"operation": "abs"},
)
常用随机化函数:randomize_rigid_body_mass、randomize_rigid_body_material(摩擦/恢复系数)、randomize_actuator_gains(PD 增益)、randomize_joint_parameters、randomize_rigid_body_inertia、randomize_physics_scene_gravity,以及初始态抖动的 reset_root_state_uniform、reset_joints_by_offset。
一条后端相关的随机化边界要记:randomize_rigid_body_material 里,Newton 后端只支持单一摩擦系数,dynamic_friction_range 这类要分静/动摩擦的随机化在 Newton 上退化(PhysX 才区分)。写域随机化配置时,同一份区间在两个后端可生效的范围不同,跨后端前先确认字段是否被该后端消费。
判据:成功率与多样性一起报
成功率 95% 但挤在同一初始位姿,比 60% 覆盖整个工作空间更没用;数据集质量按“成功 + 覆盖”两个维度同时评。
5. 要点¶
- 奖励是一组
RewTerm加权和:正项"要什么"、负项"别怎样",物理量级大的惩罚要压到1e−5 ~ 1e−7;weight=0.0是显式关掉的调参旋钮。 - termination 参与塑形:真终止切断失败态(早停)、
time_out=True是截断需 bootstrap;bonus 与惩罚、时长上限相互制衡,episode 长度"先升后稳"是健康信号。 - 课程两类:
terrain_levels_vel按表现搬环境难度(需分层地形);lift 的 ADR 用mdp.DifficultyScheduler按成功率升降难度、再用mdp.modify_term_cfg+initial_final_interpolate_fn把配置叶子(噪声/边界/重力)随难度插值。 - 域随机化是 event term,靠
mode(startup/reset/interval)定时机、*_distribution_params给区间、内部sample_uniform采样;范围要按后端能力核对。 - Newton 与 PhysX 对同一随机化字段支持度不同(如 Newton 单一摩擦系数),跨后端配置不能假设逐字段通用。