从物理引擎到赛道策略:拆解DeepRacer奖励函数背后的数学与工程逻辑
在自动驾驶赛车领域,DeepRacer作为一款基于强化学习的模拟平台,其核心挑战在于如何设计一个既能引导智能体快速完成赛道,又能保持稳定行驶的奖励函数。本文将深入剖析奖励函数设计中的关键数学原理与工程实现细节,帮助开发者掌握可迁移到其他强化学习环境的设计方法论。
1. 赛道几何建模与最优路径计算
任何赛车游戏的核心都是赛道几何的数学表示。DeepRacer采用离散化的赛道点集合来定义赛道中心线,每个点包含坐标、建议速度和到达时间三个关键参数:
racing_track = [ [3.07857, 0.7234, 3.2, 0.04483], [3.22295, 0.71246, 3.2, 0.04525], # ...数百个数据点 ]1.1 最近点搜索算法
车辆实时位置到赛道中心线的距离计算采用两阶段最近邻搜索:
- 全量距离计算:遍历所有赛道点,计算与车辆位置的欧氏距离
- 次近邻优化:排除最近点后再次搜索,确保获得相邻的两个参考点
def closest_2_racing_points_index(racing_coords, car_coords): distances = [] for i in range(len(racing_coords)): distance = dist_2_points(racing_coords[i][0], car_coords[0], racing_coords[i][1], car_coords[1]) distances.append(distance) closest_index = distances.index(min(distances)) distances_no_closest = distances.copy() distances_no_closest[closest_index] = 999 second_closest_index = distances_no_closest.index(min(distances_no_closest)) return [closest_index, second_closest_index]1.2 垂直距离的几何推导
获得两个最近点后,通过海伦公式计算车辆到中心线线段的垂直距离。这个计算过程需要考虑几种边界情况:
| 情况 | 处理方法 | 数学表达 |
|---|---|---|
| 线段退化点 | 直接取点距 | distance = b |
| 钝角三角形 | 取较短边距 | min(b, c) |
| 正常情况 | 海伦公式 | 见下方代码 |
def dist_to_racing_line(closest_coords, second_closest_coords, car_coords): a = dist_2_points(closest_coords[0], second_closest_coords[0], closest_coords[1], second_closest_coords[1]) b = dist_2_points(car_coords[0], closest_coords[0], car_coords[1], closest_coords[1]) c = dist_2_points(car_coords[0], second_closest_coords[0], car_coords[1], second_closest_coords[1]) try: distance = abs(-(a**4) + 2*(a**2)*(b**2) + 2*(a**2)*(c**2) - (b**4) + 2*(b**2)*(c**2) - (c**4))**0.5 / (2*a) except: distance = b return distance2. 航向角与赛道方向的动态匹配
2.1 航向预测模型
为判断车辆行驶方向是否符合赛道走向,需要建立航向预测机制:
- 根据当前heading角度生成单位向量
- 虚拟延长车辆位置形成预测点
- 比较预测点到两个参考点的距离
def next_prev_racing_point(closest_coords, second_closest_coords, car_coords, heading): heading_vector = [math.cos(math.radians(heading)), math.sin(math.radians(heading))] new_car_coords = [car_coords[0] + heading_vector[0], car_coords[1] + heading_vector[1]] # 距离比较逻辑...2.2 方向偏差计算
通过反正切函数计算赛道中心线方向,并与车辆航向角比较:
def racing_direction_diff(closest_coords, second_closest_coords, car_coords, heading): next_point, prev_point = next_prev_racing_point(...) track_direction = math.atan2(next_point[1] - prev_point[1], next_point[0] - prev_point[0]) track_direction = math.degrees(track_direction) direction_diff = abs(track_direction - heading) if direction_diff > 180: direction_diff = 360 - direction_diff return direction_diff注意:方向差超过30度时会触发严重惩罚,这是防止车辆失控的重要安全机制
3. 速度策略与时间预测模型
3.1 速度奖励的二次惩罚
速度奖励采用非线性惩罚机制,对偏离建议速度的情况进行梯度惩罚:
SPEED_DIFF_NO_REWARD = 1 SPEED_MULTIPLE = 2 speed_diff = abs(optimals[2] - speed) if speed_diff <= SPEED_DIFF_NO_REWARD: speed_reward = (1 - (speed_diff/SPEED_DIFF_NO_REWARD)**2)**2 else: speed_reward = 0 reward += speed_reward * SPEED_MULTIPLE3.2 单圈用时预测
通过历史步骤数据预测完成单圈所需时间,建立时间奖励机制:
def projected_time(first_index, closest_index, step_count, times_list): current_actual_time = (step_count - 1) / 15 indexes_traveled = indexes_cyclical(first_index, closest_index, len(times_list)) current_expected_time = sum([times_list[i] for i in indexes_traveled]) total_expected_time = sum(times_list) try: projected_time = (current_actual_time/current_expected_time)*total_expected_time except: projected_time = 9999 return projected_time4. 工程实现中的边界处理
4.1 异常处理机制
在实际应用中必须考虑各种边界情况:
- 除零错误处理(赛道点重合)
- 车辆完全偏离赛道时的降级处理
- 初始位置校准问题
try: # 可能抛出异常的计算 distance = complex_geometry_formula() except: # 降级处理 distance = simple_distance()4.2 多因素奖励组合
最终的奖励函数是多个因素的加权组合:
| 奖励因素 | 权重 | 说明 |
|---|---|---|
| 赛道距离 | 1.0 | 保持接近中心线 |
| 速度匹配 | 2.0 | 遵循建议速度 |
| 方向正确 | 否决项 | 防止反向行驶 |
| 用时预测 | 动态 | 鼓励提高效率 |
# 综合奖励计算示例 reward = 1.0 # 基础奖励 reward += distance_reward * DISTANCE_MULTIPLE reward += speed_reward * SPEED_MULTIPLE if direction_diff > 30: reward = 1e-3 # 方向错误严重惩罚 if speed_diff_zero > 0.5: reward = 1e-3 # 速度过慢惩罚在实际调试中发现,权重系数需要根据赛道特性动态调整——直道多的赛道应提高速度权重,而弯道多的赛道则需要加强方向控制的奖励。