强化学习实战-用强化学习打跑酷游戏 GreatWallRun 第三节 强化学习层构建 临时笔记
强化学习层技术笔记 — GreatWallRun基于 Stable Baselines3 PPO 的手游自动化 RL 训练系统核心代码ppo_acting.py/game_env.py/train_ppo.py一、整体架构三层分离 多线程通信1.1 设计哲学系统严格遵循“感知层只感知决策层只决策”的职责分离原则。三层之间通过共享内存变量和同步原语通信不允许跨层直接调用。┌─────────────────────────────────────────────────────────────────┐ │ train_ppo.py │ │ (训练编排层) │ │ PPO(MlpPolicy, env, tensorboard_log...) │ │ model.learn(total_timesteps100000) │ │ 断点续训 / 最优模型保存 / CtrlC 中断保护 │ └──────────────────────────┬──────────────────────────────────────┘ │ env.step(action) │ env.reset() ▼ ┌─────────────────────────────────────────────────────────────────┐ │ game_env.py │ │ (环境决策层 — Master) │ │ │ │ gymnasium.Env 接口: │ │ • observation_space: Box(207,) — 200 terrain 7 state │ │ • action_space: Discrete(3) — 0:待机 1:跳 2:射 │ │ │ │ 内部逻辑: │ │ • _calculate_reward(action) — 8 项奖励/惩罚 │ │ • _get_obs() — 构建 207 维观测向量 │ │ • _update_terrain() — 200 维地形采样 │ │ • _update_run_stats(action) — 当局统计 │ │ │ │ 与 Worker 通信: │ │ • queue.put(action) → 发送动作指令 │ │ • action_done.wait() ← 等待执行完成 │ │ • 读取共享状态变量 ← 感知数据 │ │ • death_triggered / env_restart_order ⇄ 死亡重开握手 │ └──────────────────────────┬──────────────────────────────────────┘ │ queue.Queue(maxsize1) │ threading.Event (action_done) │ 共享变量 (共享内存) ▼ ┌─────────────────────────────────────────────────────────────────┐ │ ppo_acting.py │ │ (感知执行层 — Worker) │ │ │ │ Thread 1 — 帧抓取线程 (全速 30 FPS): │ │ cap.read() → _latest_frame (共享缓冲Lock 保护) │ │ │ │ Thread 2 — 主循环 (YOLO 渲染 动作执行): │ │ _latest_frame → 悬崖检测 → YOLO → RuleEngine → 更新共享状态 │ │ → 处理动作队列 → 渲染 3 窗口 → cv2.waitKey(1) │ │ │ │ Thread 3 — OCR 线程 (每 0.3s): │ │ _latest_frame → Tesseract → shared_lives/arrows/coins/distance │ └─────────────────────────────────────────────────────────────────┘1.2 为什么不用 OpenAI Gym 标准的单进程模式标准 Gym 中env.step()内部直接执行动作并返回下一状态。但在本项目中动作延迟不可忽略— ADB 点击到游戏画面变化有 300-500ms 延迟必须等待感知必须持续运行— YOLO 推理不能因为step()没被调用就停止否则画面滞后多消费者— OCR、YOLO、game_env 三者都需要最新的游戏画面因此采用了独立 daemon 线程 队列同步的方案ppo_acting的感知循环永远运行不依赖step()调用game_env.step()只是一个下单 → 等结果的同步点这种模式类似于异步环境或client-server RL architecture1.3 同步原语设计原语类型方向语义env_action_queuequeue.Queue(maxsize1)Master → Worker动作指令容量为 1 强制同步action_donethreading.EventWorker → Master动作已执行 冷却完成 感知已更新perception_readythreading.EventWorker → Master首帧感知完成环境可以开始death_triggeredboolWorker → Master检测到死亡lives0 或距离停滞env_restart_orderboolMaster → Worker确认死亡执行重开点击流程_stats_lockthreading.Lock双向保护训练统计 dict 的读写_frame_lockthreading.Lock单向保护_latest_frame的并发访问二、观测空间设计2.1 总览observation_space Box(0, 1, shape(207,), dtypefloat32) ┌─────────────────────┬──────────┬──────────────────────────┐ │ 部分 │ 维度 │ 来源 │ ├─────────────────────┼──────────┼──────────────────────────┤ │ 地形向量 │ 200 │ 像素级地面颜色扫描 │ │ 最近障碍物距离 │ 1 │ RuleEngine.danger_list │ │ 最近敌人距离 │ 1 │ RuleEngine.enemy_list │ │ 最近 Soul 距离 │ 1 │ RuleEngine.soul_list │ │ 最近金币距离 │ 1 │ RuleEngine.coin_list │ │ 箭矢数 │ 1 │ OCR shared_arrows │ │ 金币数 (累计) │ 1 │ OCR shared_coins │ │ 生命值 │ 1 │ OCR shared_lives │ │ 总计 │ 207 │ │ └─────────────────────┴──────────┴──────────────────────────┘2.2 地形向量 (200 维)采样方法从主角马匹的右边界horse_pos[2]开始向右每隔 3 像素采样一次固定 200 个点。每个采样点读取画面底部往上 15 像素处的颜色与GROUND_COLORS对比判定是否为地面。forxinrange(start_x,min(start_x200*3,w),3):pixelframe[h-15,x]is_ground(pixel ≈ GROUND_COLORS[0]orpixel ≈ GROUND_COLORS[1])terrain_slice.append(1.0ifis_groundelse0.0)归一化直接为[0, 1]值无需额外缩放。为什么采样 200 个点1920×1080 分辨率下 200×3 600px 足够覆盖马匹前方到屏幕右边缘的全部地面200 维足够让 MLP 策略网络感知到前方空洞 悬崖的 pattern3px 间隔在精度和向量长度间折中2.3 状态向量 (7 维)距离归一化danger_dist/500# 超过 500px 视为无穷远enemy_dist/500soul_dist/500coin_dist/500选择 500 作为分母是因为游戏画面中超过 500px 的物体已接近屏幕边缘Agent 不需要区分 500px 和 800px —— 都很远。HUD 归一化arrows/100# 箭矢上限约 99coins/500# 金币可能累积数百lives/10# 生命值通常 1-5生命值不做上限截断——如果lives 10也能正确归一化到 1.0策略网络会从数值本身学到生命很多。三、动作空间设计3.1 动作定义Action名称ADB 操作游戏效果0待机无角色自然奔跑1跳跃adb_tap(300, 300)跳过障碍/悬崖/拾取头顶金币2射击adb_swipe(horse → enemy, offset300)消灭前方敌人3.2 为什么只有 3 个动作大招加速未加入— 需要消耗金币决策复杂度过高先不纳入 RL没有方向控制— 游戏是自动奔跑的卷轴跑酷角色自动前进没有向下滑— 游戏不提供下蹲/滑铲操作射击方向自动瞄准— swipe 的终点根据shoot_target最近敌人自动计算3.3 射击坐标计算# 从马的位置向敌人方向延伸 SHOOT_OFFSET300pxhx,hyhorse 中心坐标 ex,ey最近敌人的中心坐标 dx,dyex-hx,ey-hy ratioSHOOT_OFFSET/dist end_xhxdx*ratio end_yhydy*ratio# 远距离射击时加 Y 轴补偿箭矢抛物线ifdistDISTANCE_SWITCH(240px):end_ySHOOT_Y_OFFSET(-50px)# 向上抬模拟抛物线3.4 动作冷却不同动作的执行时间不同冷却时间也不同Action冷却原因0 待机0.0s无物理操作1 跳跃0.5stap 瞬间完成但需要等角色起跳→落地2 射击0.6sswipe 100ms 箭矢飞行 游戏动画冷却以非阻塞方式实现执行动作后记录cooldown_until now interval下一帧信号检查时若冷却已过 新帧感知完成 → 才action_done.set()。四、奖励函数设计4.1 设计原则密集信号优先— 距离增量每步都给Agent 每步都有反馈关键事件高权重— 悬崖跨越 20、死亡 -50明确强化/抑制防止 OCR 噪声— 所有 OCR 来源的 delta 都做上限裁剪引导合理行为— 战斗惩罚引导 Agent 不浪费箭矢、不放任敌人4.2 完整奖励项详见REWARD.md此处只列核心逻辑def_calculate_reward(action):reward0.0# P1 生存税 — 打破全正奖励reward-0.005# R1 距离增量 — 核心正向驱动delta_distcurrent_dist-prev_distif|delta_dist|50:# OCR 防抖rewarddelta_dist*0.1# R2 金币增量delta_coinscurrent_coins-prev_coinsif0delta_coins10:# OCR 防抖rewarddelta_coins*1.0# R3 障碍越过 — 跟踪 nearest danger 的 x 坐标变化ifnearest_danger_x 换了:reward2.0# R4 悬崖跨越 — True → Falseifcliff 解除:reward20.0# P2 无目标射击 — action2 但无敌或 300pxifaction2andnotenemy_close:reward-0.3# P3 有敌不射 — 敌人 300px 但 action≠2ifaction!2andenemy_close:reward-0.8# P4 死亡ifdone:reward-50.04.3 典型一局预算跑 2000m, 捡 20 币, 过 15 障碍, 跨 2 悬崖, 射 10 次 生存税: -0.005 × 400步 -2.0 距离: 0.1 × 2000 200.0 金币: 1.0 × 20 20.0 障碍: 2.0 × 15 30.0 悬崖: 20.0 × 2 40.0 射击奖惩: ≈ 0.0 ──────────────────────────────── 合计: ~288.0 死亡: -50.0 (17%)死亡惩罚约占一局总正奖励的 17%既能起到威慑作用又不会让 Agent 过度保守不敢冒险跳跃。4.4 奖励 shaping 的反面为什么不加按键惩罚有些 RL 项目对每次行动action≠0施加小惩罚防止 Agent 疯狂按键。本项目不需要冷却机制已经限制频率— Agent 每秒最多 2 次动作无效射击已有专门惩罚— 比通用按键惩罚更有针对性生存税已经打破全正奖励— 无需额外 per-action 惩罚五、死亡检测与重开闭环5.1 死亡判定双重机制主判定OCR 读到 lives0 → 立即标记 death_triggeredTrue └─ 优点零延迟不会有死后还在选动作的问题 兜底判定距离停滞 2s → 标记 death_triggeredTrue └─ 用途OCR 偶尔漏读 lives0 的情况5.2 端到端时序时间线: T0.0s lives0 → death_triggeredTrue → step() 返回 done T0.0s PPO 调用 reset() T1.0s 等待 OCR 稳定0.3s×3 周期 T1.0s 采样 dist_before T3.0s 采样 dist_after T3.0s dist_before dist_after → 确认死亡 T3.0s env_restart_order True → Worker 收到指令 T3.0s _death_check_enabled False ← 锁住死亡检测 T3.0s tap(300, 300) — 进入死亡页面 T6.0s tap(960, 1000) — 点击重开 T8.0s 等待游戏加载 T8.0s _death_check_enabled True ← 恢复死亡检测 T8.0s reset() 返回初始 obs — 新局开始5.3 关键保护机制保护机制防止的问题OCR 滞后误判等 1s 再采样确保 OCR 稳定lives 归零但距离读数还在更新 → 误判为还活着2s 距离验证两次采样对比OCR 短暂异常读数 → 误判死亡_death_check_enabled锁重开期间完全关闭死亡检测死画面上 lives0 持续触发 → 无限循环重启六、多线程交互时序6.1 正常 Step 的完整交互train_ppo game_env ppo_acting 主循环 OCR 线程 │ │ │ │ │ env.step(1) │ │ │ │ ──────────────► │ │ │ │ │ queue.put(1) │ │ │ │ ────────────────────► │ │ │ │ │ │ │ │ action_done.wait() │ │ │ │ (阻塞, 最多 2s) │ │ │ │ │ │ │ │ ┌─────┴─────┐ │ │ │ │ pop 动作 1 │ │ │ │ │ adb_tap │ │ │ │ │ 设 cooldown│ │ │ │ │ pending │ │ │ │ │ True │ │ │ │ └─────┬─────┘ │ │ │ │ │ │ │ ┌──────────┴──────────┐ │ │ │ │ 继续跑感知循环 │ │ │ │ │ 读帧 → 悬崖 → YOLO │ │ │ │ │ → 更新共享状态 │ │ │ │ └──────────┬──────────┘ │ │ │ │ │ │ │ ┌─────┴─────┐ │ │ │ │ cooldown │ │ │ │ │ 已过 │ │ │ │ │ YES → │ │ │ │ │ action_ │ │ │ │ │ done.set()│ │ │ │ └─────┬─────┘ │ │ │ │ │ │ │ ◄─ action_done 收到 ──┘ │ │ │ │ │ │ │ 读共享状态: │ │ │ │ horse_pos, │ │ │ │ danger_list, │ │ │ │ shared_distance ... │ │ │ │ │ │ │ │ _calculate_reward() │ │ │ │ _get_obs() │ │ │ │ │ │ │ ◄─ (obs, r, done)│ │ │ │ │ │ │6.2 关键时序保证为什么action_done一定在感知更新后主循环的执行顺序保证了while True: 1. 读新帧 ← 上轮动作的效果已经体现在画面中 2. 感知 (YOLO rule_engine) ← 更新所有共享状态 3. 如果 pending_signal cooldown_done: action_done.set() ← game_env 醒来读到的一定是动作后的状态 4. 如果 queue 非空: 执行动作 ← 标记 pending_signalTrue 5. 渲染当 game_env 被action_done.set()唤醒时第 2 步已经完成共享状态是最新的。为什么选择maxsize1的 Queuemaxsize1意味着 game_env 的queue.put()会阻塞直到 Worker 消费了上一个动作这自然形成了背压机制Agent 不可能领先 Worker 超过 1 个动作比无限 queue 手动同步更简洁健壮七、PPO 训练配置7.1 超参数与设计意图参数值设计意图policyMlpPolicy207 维观测 → 3 维动作MLP 足够。不需要 CNNn_steps256每 256 步做一次策略更新。约等于 2-3 局游戏batch_size64256 步分 4 个 batch 更新平衡训练速度和稳定性n_epochs5每轮数据重放 5 次。RL 环境数据不能过拟合learning_rate3e-4PPO 标准值太大了策略震荡太小了收敛慢gamma0.99重视长期回报。游戏可以跑很久远期的生存也很重要gae_lambda0.95标准值平衡优势估计的偏差和方差clip_range0.2PPO 核心限制每次更新的幅度防止策略崩溃ent_coef0.05略高鼓励探索。游戏有随机性敌人/障碍随机出现vf_coef0.5标准值价值函数损失权重7.2 模型保存策略类型触发用途ppo_greatwall_N_steps.zip每 10000 步定期 checkpoint断点续训ppo_greatwall_best.zipepisode reward 创新高训练中最佳模型ppo_greatwall_final.zip训练完成最终模型ppo_greatwall_interrupt.zipCtrlC中断保护BestModelCallback是自定义的——监听Monitor包装器在每个 episode 结束时写入infos的episode字段比较ep_reward。八、设计决策 FAQQ: 为什么 RuleEngine 仍然在 ppo_acting 中运行RL 不是不应该用规则吗RuleEngine 的输出danger_list,enemy_list等是作为观测的一部分输入给 RL 的不是用来做决策的。它本质上是一个特征提取器——把 YOLO 的边界框列表转化为最近的障碍物距离 230px这种结构化信息。PPO 策略自己决定跳不跳。Q: 为什么 terrain 向量用像素采样而不是 YOLO 检测悬崖是地面缺失不是可检测的物体。YOLO 不知道地面长什么样。像素扫描是唯一可靠的悬崖检测方式。Q: 为什么 OCR 读 HUD 而不是让 YOLO 检测数字OCR 识别任意数字比训练 YOLO 检测每个数字0-9更灵活通用。Tesseract 专门优化了数字识别。Q: reset() 中的 2s 等待会不会浪费时间只在死亡时发生。一局游戏可能几百上千步一次 3 秒的 reset 开销可忽略。更重要的是它确保了重开可靠性。占位图​​我们挑几张验证效果看看​

相关新闻

TMS320F28335 XINTF与ADC时序配置实战:从手册参数到稳定系统

TMS320F28335 XINTF与ADC时序配置实战:从手册参数到稳定系统

1. 项目概述:从芯片手册到稳定系统的桥梁如果你正在使用TI的TMS320F28335这颗经典的浮点DSP进行开发,尤其是在做电机控制、数字电源或者需要高速数据采集的项目,那么有两个模块的时序问题你绝对绕不开:外部接口(XINTF&…

2026/7/27 8:01:24阅读更多 →
AO3镜像站完整指南:如何轻松访问全球最大同人创作平台

AO3镜像站完整指南:如何轻松访问全球最大同人创作平台

AO3镜像站完整指南:如何轻松访问全球最大同人创作平台 【免费下载链接】AO3-Mirror-Site 项目地址: https://gitcode.com/gh_mirrors/ao/AO3-Mirror-Site 还在为无法访问Archive of Our Own(AO3)而烦恼吗?AO3镜像站项目为…

2026/7/27 8:01:24阅读更多 →
MySQL SQL执行全链路解析:从Parser到Executor的完整生命周期

MySQL SQL执行全链路解析:从Parser到Executor的完整生命周期

在数据库开发中,我们每天都在与 SQL 语句打交道。你是否曾好奇,当你在 MySQL 客户端敲下SELECT * FROM users WHERE id 1;并按下回车后,到屏幕上显示出结果,这背后究竟发生了什么?是数据库“魔法般”地瞬间完成了任务…

2026/7/27 8:01:24阅读更多 →
RANSAC算法在三维点云处理中的原理与实践

RANSAC算法在三维点云处理中的原理与实践

1. 项目概述在计算机视觉和三维点云处理领域,RANSAC(Random Sample Consensus)算法就像一位经验丰富的侦探,能够从充满噪声的数据中找出真正的规律。我第一次接触这个算法是在处理激光雷达点云数据时,当时面对大量离群…

2026/7/27 9:26:21阅读更多 →
Mac终端清理大师Mole:一体化系统优化工具深度解析

Mac终端清理大师Mole:一体化系统优化工具深度解析

Mac终端清理大师Mole:一体化系统优化工具深度解析 【免费下载链接】Mole 🐹 Clean, uninstall, analyze, optimize, and monitor your Mac from the terminal. 项目地址: https://gitcode.com/GitHub_Trending/mole15/Mole 在Mac开发者的工具箱中…

2026/7/27 9:26:19阅读更多 →
群体智能预测:用数字沙盘预见未来的无限可能

群体智能预测:用数字沙盘预见未来的无限可能

群体智能预测:用数字沙盘预见未来的无限可能 【免费下载链接】MiroFish A Simple and Universal Swarm Intelligence Engine, Predicting Anything. 简洁通用的群体智能引擎,预测万物 项目地址: https://gitcode.com/GitHub_Trending/mi/MiroFish …

2026/7/27 9:26:19阅读更多 →
NSGA-II算法在无人机三维路径规划中的实战应用

NSGA-II算法在无人机三维路径规划中的实战应用

1. 项目概述:当无人机遇上多目标优化去年调试四旋翼时,我遇到个头疼的问题:在复杂城区环境下,无人机既要避开高楼又要保持信号稳定,还得考虑电池续航。传统A*算法规划出的路径虽然最短,但转弯角度太陡导致耗…

2026/7/27 9:26:18阅读更多 →
C++ STL函数对象值传递陷阱与状态保持解决方案

C++ STL函数对象值传递陷阱与状态保持解决方案

1. 项目概述:函数对象的状态与传递陷阱在C STL的日常使用中,for_each、transform、sort这些算法和函数对象(Functor)打交道是家常便饭。很多朋友,包括我自己在初学阶段,都曾掉进过一个看似简单却影响深远的…

2026/7/27 9:26:17阅读更多 →
如何用FunASR构建智能语音识别系统:从个人应用到企业部署的完整指南

如何用FunASR构建智能语音识别系统:从个人应用到企业部署的完整指南

如何用FunASR构建智能语音识别系统:从个人应用到企业部署的完整指南 【免费下载链接】FunASR Open-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serv…

2026/7/27 9:24:17阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/27 1:14:34阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/27 1:14:52阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/27 1:14:56阅读更多 →
SPI实战指南:从时钟模式到寄存器配置,解决嵌入式通信难题

SPI实战指南:从时钟模式到寄存器配置,解决嵌入式通信难题

1. 项目概述:从寄存器手册到实战指南 如果你手头有一份类似德州仪器(TI)TMS320x240xA系列DSP的SPI模块技术手册,看着里面密密麻麻的寄存器位定义、时序图和公式,是不是感觉头大?这份资料虽然权威&#xff0…

2026/7/27 0:00:24阅读更多 →
【JAVA毕设源码分享】基于springboot的水果购物管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

【JAVA毕设源码分享】基于springboot的水果购物管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/27 0:00:24阅读更多 →
2007-2023年各市区县生态文明建设示范区DID

2007-2023年各市区县生态文明建设示范区DID

数据简介 自改革开放以来,我国依赖高投入、高资源消耗和高污染等传统发展模式实现了经济短期内的快速增长, 然而这也导致了严重的生态环境危机。因此,国家有力于推动企业高质量经济发展,协同生态保护的方针,从而从201…

2026/7/27 0:00:24阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/25 23:03:25阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/26 19:05:21阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/26 19:05:21阅读更多 →