DeepRacer 奖励函数设计:从赛道几何到速度优化的实战解析
1. DeepRacer奖励函数设计基础
当你第一次接触DeepRacer时,最让人头疼的就是如何设计一个有效的奖励函数。这就像教一个刚学开车的小朋友,你需要明确告诉他什么行为值得表扬,什么行为应该避免。奖励函数就是AI赛车手的"教练",它决定了赛车在训练过程中学习什么样的驾驶策略。
在DeepRacer比赛中,赛道通常由一系列航点(waypoints)组成,这些点连起来形成了赛道的中心线。我们的首要目标就是让赛车尽可能贴近这条中心线行驶。想象一下,就像骑自行车时保持平衡一样,离中心线越近,获得的奖励就应该越高。
这里有个简单的距离奖励计算公式:
def distance_reward(params): track_width = params['track_width'] distance_from_center = params['distance_from_center'] # 计算距离中心线的比例(0到1之间) normalized_distance = distance_from_center / (track_width/2) # 确保奖励在0到1之间 reward = 1 - normalized_distance return max(0.001, reward) # 避免零奖励这个基础版本虽然简单,但在实践中你会发现它有几个明显问题:赛车可能会为了保持绝对居中而牺牲速度,或者在弯道处表现不佳。我曾经在一个简单赛道上测试过这个基础版本,结果赛车在直道上表现不错,但一到弯道就会明显减速,最终圈速很不理想。
2. 赛道几何与航向角优化
2.1 航向角偏差惩罚
单纯考虑距离中心线是不够的,我们还需要关注赛车的朝向是否正确。这就引入了航向角(heading)的概念 - 即赛车当前方向与赛道方向的夹角。在急转弯处,这个角度差可能很大,我们需要相应调整奖励。
计算航向角偏差的关键代码如下:
def heading_reward(params): waypoints = params['waypoints'] closest_waypoints = params['closest_waypoints'] heading = params['heading'] # 计算赛道方向(当前点和下一个点的连线方向) next_point = waypoints[closest_waypoints[1]] prev_point = waypoints[closest_waypoints[0]] track_direction = math.atan2(next_point[1]-prev_point[1], next_point[0]-prev_point[0]) track_direction = math.degrees(track_direction) # 计算方向差异(0-180度之间) direction_diff = abs(track_direction - heading) if direction_diff > 180: direction_diff = 360 - direction_diff # 方向差异越大,奖励越小 if direction_diff > 30: # 超过30度就给予严重惩罚 return 0.001 else: return 1 - (direction_diff / 30)2.2 动态权重调整
在实际比赛中,我发现直道和弯道需要不同的奖励策略。直道上可以更注重速度,而弯道则需要更关注航向准确性。于是我对奖励函数做了动态调整:
# 计算当前赛段的曲率(判断是直道还是弯道) def calculate_curvature(waypoints, closest_waypoints, lookahead=5): points = waypoints[closest_waypoints[0]:closest_waypoints[0]+lookahead] if len(points) < 3: return 0 # 使用三点法计算曲率 x = [p[0] for p in points] y = [p[1] for p in points] dx = np.gradient(x) dy = np.gradient(y) d2x = np.gradient(dx) d2y = np.gradient(dy) curvature = np.abs(dx*d2y - dy*d2x) / (dx*dx + dy*dy)**1.5 return np.mean(curvature) # 根据曲率动态调整奖励权重 curvature = calculate_curvature(waypoints, closest_waypoints) if curvature > 0.1: # 弯道 heading_weight = 0.7 speed_weight = 0.3 else: # 直道 heading_weight = 0.3 speed_weight = 0.73. 速度优化策略
3.1 理想速度曲线
DeepRacer比赛中最大的挑战之一就是速度控制。赛车不能一直全速前进,特别是在弯道处。我们需要为每个赛段定义理想速度,这通常通过分析最优赛车线得到。
一个实用的速度奖励函数如下:
def speed_reward(params): speed = params['speed'] waypoints = params['waypoints'] closest_waypoints = params['closest_waypoints'] # 获取当前赛段的理想速度(可以预先计算存储) optimal_speed = get_optimal_speed(closest_waypoints[0]) # 计算速度差异 speed_diff = abs(optimal_speed - speed) # 小差异不惩罚,大差异二次惩罚 if speed_diff <= 0.5: return 1 elif speed_diff <= 1.0: return 1 - (speed_diff-0.5)**2 else: return 0.0013.2 加速度控制
除了绝对速度,加速度控制也很重要。突然的加速或减速都会影响赛车稳定性。我们可以通过记录上一步的速度来判断加速度:
class SpeedMonitor: def __init__(self): self.last_speed = 0 self.last_step = 0 def acceleration_reward(self, params): current_speed = params['speed'] steps = params['steps'] # 计算加速度(m/s^2) if steps == self.last_step + 1: acceleration = (current_speed - self.last_speed) * 15 # 15 steps per second else: acceleration = 0 # 更新记录 self.last_speed = current_speed self.last_step = steps # 加速度在±3m/s^2内不惩罚 if abs(acceleration) > 5: # 急加速/急刹车 return 0.001 elif abs(acceleration) > 3: return 0.5 else: return 14. 高级奖励策略
4.1 进度奖励
为了让赛车尽快完成比赛,我们需要引入进度奖励。这就像告诉赛车手:"你完成得越快,奖励越多"。但要注意平衡即时奖励和长期奖励。
def progress_reward(params): progress = params['progress'] steps = params['steps'] # 基准步数(根据赛道长度和理想速度估算) benchmark_steps = 150 # 当前进度对应的理想步数 ideal_steps = steps / (progress/100) if progress > 0 else float('inf') # 计算进度奖励 if ideal_steps <= benchmark_steps: return 1 + (benchmark_steps - ideal_steps)/benchmark_steps else: return max(0.001, 1 - (ideal_steps - benchmark_steps)/benchmark_steps)4.2 赛道边界处理
赛车偶尔偏离赛道是难免的,但我们要确保它能快速回到正轨。一个有效的边界处理策略是:
def track_edge_reward(params): all_wheels_on_track = params['all_wheels_on_track'] distance_from_center = params['distance_from_center'] track_width = params['track_width'] if not all_wheels_on_track: return 0.001 # 完全出界 # 接近边界时给予警告 edge_distance = (track_width/2) - distance_from_center if edge_distance < 0.1: # 离边界只有10cm return 0.3 elif edge_distance < 0.2: return 0.7 else: return 14.3 综合奖励函数
最后,我们需要将所有奖励组件合理地组合起来。经过多次实验,我发现这样的权重分配效果不错:
def reward_function(params): # 计算各组件奖励 dist_reward = distance_reward(params) head_reward = heading_reward(params) speed_rew = speed_reward(params) accel_rew = speed_monitor.acceleration_reward(params) progress_rew = progress_reward(params) edge_rew = track_edge_reward(params) # 动态权重调整 curvature = calculate_curvature(params['waypoints'], params['closest_waypoints']) if curvature > 0.1: # 弯道 weights = { 'distance': 0.4, 'heading': 0.3, 'speed': 0.1, 'acceleration': 0.1, 'progress': 0.05, 'edge': 0.05 } else: # 直道 weights = { 'distance': 0.2, 'heading': 0.1, 'speed': 0.4, 'acceleration': 0.2, 'progress': 0.05, 'edge': 0.05 } # 综合奖励 reward = ( dist_reward * weights['distance'] + head_reward * weights['heading'] + speed_rew * weights['speed'] + accel_rew * weights['acceleration'] + progress_rew * weights['progress'] + edge_rew * weights['edge'] ) return float(max(0.001, reward))在实际比赛中,我使用这个奖励函数后,赛车圈速提升了约15%。最关键的是,赛车在不同类型的赛道上都表现稳定,既能快速通过直道,又能优雅地过弯。
