4. 奖励函数设计入门
奖励函数是强化学习策略优化的“指挥棒”。在自动驾驶场景中,奖励函数不仅决定智能体学什么,还决定它愿意冒多大风险、追求多快、是否礼让、是否保守。一个设计不当的奖励,可能让车辆学会原地不动、绕圈刷分,甚至以危险方式抢行。因此,理解奖励的基本形式,是策略优化工程的第一道门槛。
一、核心原理:奖励信号的四种基本形式
在自动驾驶强化学习中,奖励信号可以按“何时给、给什么、给多少”拆解为四种基本形式:
- 稀疏奖励(Sparse Reward):只在任务完成或失败时给出信号,例如到达终点 +100,碰撞 -100,其余时刻为 0。优点是目标明确、不易被“刷分”;缺点是学习信号稀少,早期探索困难。
- 稠密奖励(Dense Reward):每一步或每个短周期都给出反馈,例如沿车道中心行驶、速度接近期望、与前车保持安全距离等。优点是学习快、梯度稳定;缺点是权重难调,容易诱导局部最优。
- 目标奖励(Goal Reward):面向最终任务目标的奖励,例如到达目的地、完成变道、通过路口。它通常与稀疏奖励结合,作为“终局信号”。
- 惩罚项与安全约束(Penalty & Safety Constraint):对危险、违规、低效行为进行扣分或硬性限制,例如碰撞、压线、超速、急刹、占用应急车道。安全约束可以是软惩罚,也可以是硬约束(动作屏蔽或终止条件)。
这四类信号并非互斥,而是通常组合使用。一个典型的自动驾驶奖励函数可以写成:
R_total = w_goal * R_goal
+ w_dense * R_dense
- w_penalty * R_penalty
- w_safety * R_safety_violation
其中权重 w_* 决定了策略的偏好:提高 w_safety 会让车辆更保守,提高 w_dense 中的速度项会让车辆更激进。权重不是拍脑袋定的,而是通过仿真评测、消融实验和策略行为分析反复调整。
二、方法流程:从任务目标到奖励函数
设计奖励函数可以遵循一个可复用的流程,避免一上来就写公式:
- 明确任务目标:是到达终点、完成变道、通过无保护左转,还是保持车道巡航?目标不同,奖励结构完全不同。
- 定义成功与失败:成功条件是什么?失败条件是什么?例如“到达终点且无碰撞”为成功,“碰撞或驶出可行驶区域”为失败。
- 选择稀疏或稠密:如果仿真环境支持大量并行采样,可以先用稀疏目标奖励;如果训练早期几乎无法到达终点,则需要加入稠密引导。
- 加入过程奖励:例如车道中心偏差、航向角误差、速度误差、舒适度( jerk )、通行效率。
- 加入惩罚与安全约束:碰撞、压线、超速、急加速、急转向、距离过近等。
- 权重调参与消融:固定其他项,单独调整某一项权重,观察策略行为变化。
- 仿真评测与复盘:用成功率、碰撞率、平均速度、舒适度、违规次数等指标评估,并回看典型失败片段。
三、案例/场景:一个城市路口通行奖励
假设任务是通过一个无保护左转路口。我们可以这样设计奖励:
| 奖励项 | 类型 | 触发条件 | 建议值 |
|---|---|---|---|
| 到达目标点 | 目标奖励 | 车辆进入目标区域且无碰撞 | +100 |
| 碰撞 | 惩罚项 | 与车辆、行人、障碍物发生碰撞 | -100,并终止 |
| 车道中心偏差 | 稠密奖励 | 每步计算横向偏差,偏差越小奖励越高 | +0.1 ~ +1.0 |
| 速度跟踪 | 稠密奖励 | 实际速度接近期望速度 | +0.5 |
| 急刹/急转 | 惩罚项 | 加速度或 jerk 超过阈值 | -0.5 |
| 压线/越界 | 安全约束 | 车轮压到实线或驶出可行驶区域 | -10,可终止 |
| 礼让行人 | 稠密奖励 | 行人处于冲突区域时停车等待 | +2.0 |
这个奖励表体现了“目标 + 过程 + 惩罚 + 安全”的组合。注意,礼让行人给正奖励,而不是只给碰撞负奖励,这是为了在稀疏碰撞信号之外,提供更早的引导信号。
四、误区与复盘:奖励设计中最容易踩的坑
误区一:只给稀疏奖励,期待智能体自己探索。在自动驾驶这种高维连续动作空间中,随机探索几乎不可能碰到终点。稀疏奖励适合作为终局信号,但通常需要稠密引导或课程学习配合。
误区二:稠密奖励权重过大,导致“刷分”。例如速度奖励过高,车辆可能学会绕圈刷速度;车道中心奖励过高,车辆可能不敢变道。解决方法是加入目标奖励和惩罚项,并做消融实验。
误区三:惩罚项太弱,安全约束形同虚设。如果碰撞惩罚只有 -1,而到达目标奖励是 +100,智能体可能愿意冒险撞车。安全相关惩罚通常需要足够大,或者直接作为终止条件。
误区四:奖励项之间相互冲突。例如“快速通行”和“绝对安全”在路口场景中可能矛盾。此时需要通过权重调节和约束分层来处理,而不是简单叠加。
五、奖励函数的基本形式总结
从工程实现角度,奖励函数可以抽象为以下形式:
def compute_reward(state, action, next_state):
reward = 0.0
# 目标奖励
if reached_goal(next_state):
reward += 100.0
done = True
# 稠密过程奖励
reward += 1.0 * lane_center_reward(next_state)
reward += 0.5 * speed_tracking_reward(next_state)
reward += 2.0 * yield_pedestrian_reward(next_state)
# 惩罚项
reward -= 0.5 * jerk_penalty(action)
reward -= 1.0 * off_road_penalty(next_state)
# 安全约束
if collision(next_state):
reward -= 100.0
done = True
return reward, done
这段伪代码展示了奖励函数的典型结构:先判断终局条件,再叠加稠密项,最后处理惩罚和安全约束。实际工程中,每一项都需要独立可配置、可开关、可记录,方便后续调参和复盘。