You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flappy Bird强化学习中基于屏幕识别的奖励函数实现求助

优化Flappy Bird强化学习奖励函数的Numpy实现方案

嘿,你的思路方向完全没问题——用帧变化判断死亡、用特定区域的像素变化检测得分,不过现有代码在稳定性和准确性上还有不少可以打磨的空间,我来帮你一步步梳理改进:

一、死亡判定的优化

你现在通过连续三帧完全一致来判断死亡,但实际玩Flappy Bird的时候,鸟死亡后还会有下落动画,背景甚至可能继续滚动一小段,完全三帧一模一样的情况其实很少见,很容易漏判或者误判。更靠谱的做法有两种:

方案1:检测游戏画面的变化率

计算最近两帧的差异总和,当差异远低于正常游戏时的阈值,说明画面基本静止(比如游戏结束停留在结算界面),就判定死亡:

def _calculate_reward(self, state):
    # 提取最近的两帧(state的第3和第4个通道,索引从0开始)
    frame_last = state[0, :, :, 2]
    frame_current = state[0, :, :, 3]
    
    # 计算两帧的绝对差异总和
    frame_diff_sum = np.sum(np.abs(frame_current - frame_last))
    # 设定死亡阈值(根据你的预处理结果调整,正常游戏时差异至少有几百)
    DEATH_THRESHOLD = 50
    
    if frame_diff_sum < DEATH_THRESHOLD:
        print("Flappy挂啦!")
        return -1000

方案2:直接检测鸟的存在(更准确)

如果你的预处理已经把鸟从背景里分离出来了(比如把鸟的像素统一改成255),那直接检查当前帧里有没有鸟的像素就行——鸟消失了就说明死亡:

# 接上面的代码,替换死亡判定部分
frame_current = state[0, :, :, 3]
# 检查当前帧是否存在鸟的像素(假设鸟的像素值是255)
has_bird = np.any(frame_current == 255)
if not has_bird:
    print("Flappy挂啦!")
    return -1000

二、得分判定的优化

你现在用第一行前50像素的和等于510来判断管道间隙,这个逻辑太依赖固定的像素值(255*2=510),一旦预处理有微小偏差(比如像素值不是严格255)或者管道位置偏移,就会误判。更稳定的方式是跟踪管道的位置变化:

当管道从鸟的前方区域移走时,说明鸟已经穿过了管道,这时候给得分奖励。示例代码如下:

# 先定义鸟和管道的检测区域(根据你的游戏画面调整坐标)
BIRD_Y_RANGE = (150, 200)  # 假设鸟在y轴150到200的区域
PIPE_X_RANGE = (50, 100)   # 鸟前方的管道检测区域(x轴50到100)

# 提取前一帧和当前帧的鸟所在区域
prev_frame = state[0, BIRD_Y_RANGE[0]:BIRD_Y_RANGE[1], PIPE_X_RANGE[0]:PIPE_X_RANGE[1], 2]
curr_frame = state[0, BIRD_Y_RANGE[0]:BIRD_Y_RANGE[1], PIPE_X_RANGE[0]:PIPE_X_RANGE[1], 3]

# 假设预处理后管道是深色像素(值<100),背景是浅色
prev_has_pipe = np.any(prev_frame < 100)
curr_has_pipe = np.any(curr_frame < 100)

# 前一帧有管道,当前帧没有,说明鸟穿过了管道,得分!
if prev_has_pipe and not curr_has_pipe:
    print("得分啦!")
    return 1000

另外,你原来的得分条件需要连续两帧满足510,很可能会重复触发得分,现在这个逻辑只会在管道消失的瞬间触发一次,更准确。

三、整合后的完整函数

把上面的优化点整合起来,还可以给存活但未得分的情况加个小奖励(比如返回1而不是0),这样模型会更愿意探索存活策略:

def _calculate_reward(self, state):
    """计算当前状态的奖励值
    Args:
        state: np.array,形状为(1, height, width, 4) - 连续4帧预处理后的画面
    Returns:
        reward: int,死亡惩罚、得分奖励或存活小奖励
    """
    # 提取最近两帧
    frame_last = state[0, :, :, 2]
    frame_current = state[0, :, :, 3]
    
    # ---------------------- 死亡判定 ----------------------
    # 用帧变化率判断(如果用鸟检测的话替换成方案2的代码)
    frame_diff_sum = np.sum(np.abs(frame_current - frame_last))
    DEATH_THRESHOLD = 50
    if frame_diff_sum < DEATH_THRESHOLD:
        print("Flappy挂啦!")
        return -1000
    
    # ---------------------- 得分判定 ----------------------
    BIRD_Y_RANGE = (150, 200)
    PIPE_X_RANGE = (50, 100)
    
    prev_pipe_area = state[0, BIRD_Y_RANGE[0]:BIRD_Y_RANGE[1], PIPE_X_RANGE[0]:PIPE_X_RANGE[1], 2]
    curr_pipe_area = state[0, BIRD_Y_RANGE[0]:BIRD_Y_RANGE[1], PIPE_X_RANGE[0]:PIPE_X_RANGE[1], 3]
    
    prev_has_pipe = np.any(prev_pipe_area < 100)
    curr_has_pipe = np.any(curr_pipe_area < 100)
    
    if prev_has_pipe and not curr_has_pipe:
        print("得分啦!")
        return 1000
    
    # 存活但未得分,返回小奖励鼓励存活
    return 1

四、关键注意事项

  • 预处理要稳定:确保你的mss+OpenCV预处理流程是一致的,比如统一把背景、鸟、管道转换成固定的像素值,这样后续的Numpy判断才不会出错;
  • 阈值要适配你的画面:所有的阈值(死亡差异阈值、管道像素值阈值、区域坐标)都要根据你的实际游戏画面调整,可以先打印几帧的像素值和区域截图来确定;
  • 奖励值可以微调:-1000和1000的幅度如果训练效果不好,可以适当调整,比如减小死亡惩罚,或者增大存活奖励,帮助模型更快找到最优策略。

内容的提问来源于stack exchange,提问作者k88

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:24:14