首页
/ airi-factorio 纯视觉方案实战:从 Docker 化游戏客户端到浏览器端 YOLO 实时目标检测

airi-factorio 纯视觉方案实战:从 Docker 化游戏客户端到浏览器端 YOLO 实时目标检测

2026-09-09 18:32:21作者:江焘钦

导读

AIRI 是一款以"让 AI 拥有具身数字生命"为目标的开源项目,而 airi-factorio 是其让 AI 学会玩《异星工厂》(Factorio) 的探索分支。本文基于 AIRI 维护者 [@LemonNekoGH] 的月度开发日志(DevLog @ 2025.08.26),完整复现一条"纯视觉(pure vision)"闭环流水线:把 Factorio 客户端装进 Docker 并通过 VNC 暴露到浏览器、用 Lua 脚本自动采集游戏画面并标注数据、基于 YOLO11n 训练目标检测模型、再借助 onnxruntime-web 与 WebGPU 在浏览器里完成近实时推理。读完本文,你将掌握一套可复制的"游戏视觉感知栈",并为后续"让 AI 看懂画面再决策操控"打下基础。

背景:在上一期 DevLog @ 2025.07.18 中,作者基于 Factorio Learning Environment 论文梳理了 airi-factorio 的改进方向;本期则聚焦其中"纯视觉"路线的具体落地成果。

一、整体思路:为什么先做"纯视觉"

在作者此前的方案里,AI 是通过 Mod + RCON 直接读写游戏状态的(用 TypeScript 写 Mod、经 RCON 执行 /c 命令、让 LLM 生成 Lua 代码操控游戏)。这套方案虽然能拿到精确的游戏数据,但调试繁琐、命令长度受限、健壮性差。

纯视觉方向则完全不同:让 AI 像人一样直接"看屏幕"。触发点是 2025 年 6 月社区发布的近实时 VLM Playground 演示,作者决定先实现"简单的实时图像识别",再交给 AI 决策,最后以某种方式输出动作到游戏。这条路线分为三个可独立验证的环节:

  1. 构建一个可控、开箱即用的游戏运行环境(Docker + 虚拟显示 + VNC);
  2. 训练一个能识别游戏内物体的目标检测模型(YOLO);
  3. 在浏览器端完成近实时的推理与结果后处理(NMS)。

最终效果是一个网页版的 Playground:通过 VNC 在浏览器里玩 Factorio,右侧几乎实时地显示物体检测结果(演示视频见 airi-factorio-yolo-v0-playground-vnc.mp4)。

二、把 Factorio 客户端装进 Docker

要让 AI"看见"游戏画面,前提是游戏运行在一个不受窗口大小、位置干扰的受控环境中,且能开箱即用。Factorio 官方虽然提供 Docker 镜像,但那是纯服务器版;要看到画面并操控游戏,需要的是客户端。社区没有现成的客户端镜像(且 Factorio 许可证不允许以这种方式分发客户端),因此只能自行打包,并且打包后的镜像同样不能公开分发,只能共享 Dockerfile

把客户端装进 Docker 一共三步:

步骤 目标 对应组件
1 拿到 Factorio 客户端本体 factorio-dl 下载脚本
2 准备虚拟显示,让图形程序有地方画画面 xvfb + 最小 X 环境
3 提供 VNC 服务,把虚拟显示内容传出并接收输入 x11vnc

至于音频——目前 AI 还听不见,暂时忽略。

2.1 下载 Factorio 客户端

官方下载需要人工登录,不利于自动化流程。作者使用了一个非常复杂的 shell 脚本 factorio-dl:给定用户名、密码和目标版本后,它会根据系统架构自动下载对应客户端。这一步为后续在 CI / 容器构建脚本中无人工下载客户端提供了前提。

2.2 准备虚拟显示

图形应用必须有显示服务才能运行,但并不需要完整的桌面环境或窗口管理器——一个最小化的 X 环境加一个显示服务器就足够了。安装命令:

sudo apt install -y xvfb x11-apps mesa-utils

组件说明:

  • xvfb:虚拟帧缓冲(Virtual Framebuffer)和 X 服务器,让图形程序在"看不见的屏幕"上渲染;
  • x11-apps:一些 X 相关工具,安装它会把 X 环境一并装好;
  • mesa-utils:Mesa 相关工具;Mesa 是 OpenGL 的软件实现,提供工具用于测试和调试 OpenGL 应用(Factorio 的渲染依赖 OpenGL,因此这一步必不可少)。

2.3 准备 VNC 服务

VNC(Virtual Network Computing)是一种远程桌面协议,可以像坐在电脑前一样远程控制另一台机器:

sudo apt install -y x11vnc

到这里,我们已经可以在 Docker 里跑起 Factorio 客户端并通过 VNC 控制它了。但作者的最终目标是在浏览器里游玩并实时推理,而浏览器只支持 HTTP 协议,还需要两件东西:

  • websockify:把 VNC 协议转换为 HTTP(WebSocket)协议;
  • novnc:一个网页端的 VNC 客户端界面,方便调试时直接显示 VNC 画面。
sudo apt install -y websockify novnc

至此 Docker 镜像准备完毕。这四类系统包(xvfb / x11-apps / mesa-utils / x11vnc / websockify / novnc)共同构成了容器内的显示与远程访问链路。

三、训练目标检测模型

为了快速验证思路,作者直接用 YOLO11n 的预训练权重作为底座来训练自己的检测模型,而不是从零训练。

3.1 用 Lua 脚本自动采集数据集

数据采集完全在游戏内自动化完成,利用 Factorio 的 Lua API:

  1. surface.create_entity 在场景随机位置放置机器,同时记录其选择框(selection box)的尺寸与位置;
  2. game.take_screenshot 在不同缩放级别、不同光照条件(白天)下截取画面;
  3. 依据选择框生成标注数据,再用 helpers.write_file 写入文件。

实现上的关键点:数据采集脚本用 TypeScript 编写,通过 typescript-to-lua 编译为 Lua,再经 RCON 传给 Factorio 执行——这正是上一期 DevLog 沉淀下来的工具链(TypeScript 写 Mod + RCON 通信)。为了调试采集脚本,作者还开发了一个 VSCode 插件,提供 CodeLens 操作,一键编译并执行脚本。

数据集规模(v0):采集了 3 种组装机(assemblers)和传送带(conveyors),每种机器 20 张图,每张 1280×1280 分辨率、不带 UI。

3.2 组织数据集并上传 Ultralytics Hub

采集到图像和标注后,需要按 YOLO 官方数据集格式 组织目录,之后上传到 Ultralytics Hub 查看效果:

Ultralytics Hub 上的训练预览与指标

图为数据上传 Ultralytics Hub 后的训练预览界面,可直观看到标注框与训练趋势。

3.3 训练与导出

训练代码直接沿用了 Ultralytics 官方 Get Started 示例,几行即可完成训练与导出:

from ultralytics import YOLO

model = YOLO("yolo11n.pt")
model.train(data="./dataset/detect.yaml", epochs=100, imgsz=640, device="mps")
model.export(format="onnx")

关键训练参数与结果(来自日志原文,可在相应环境复现验证):

项目 取值
输入分辨率 640×640
训练设备 mps(macOS 下 Metal 性能加速)
轮数 100 epochs,每 epoch 5 个 batch
最优权重 约在第 70 epoch 达到最佳
导出格式 ONNX
训练耗时 约 8 分钟
模型大小 约 10MB

四、浏览器端实时推理

数据链路在浏览器端打通,作者选用了两个库:

  1. @novnc/novnc:在浏览器显示 VNC 画面,同时提取 canvas 数据喂给模型;
  2. onnxruntime-web:在浏览器执行推理,支持 WebGPU 以利用 GPU 性能。

4.1 性能优化的三次跳跃

初始推理非常慢,约 400ms,而且会冻结 UI、导致 VNC 卡得没法用。作者靠两条经验把延迟一步步压了下来:

第一步:分离推理与显示。 快速学习 WebWorker 用法后,把推理从渲染线程挪到 Worker 里,解决了 UI 冻结问题——但此时速度依然慢,因为根本没有真正启用 WebGPU。

第二步:显式声明执行后端。 只写 executionProviders: ['webgpu'] 并不够,必须同时声明 WASM 作为兜底,WebGPU 不可用时自动切换:

ort.InferenceSession.create(model, { executionProviders: ['webgpu', 'wasm'] })

启用 WebGPU 后,推理速度提升到约 80ms

第三步:用乘法替换除法。 在对像素颜色值做归一化时,原始代码反复执行除以 255。改为先计算 1/255 一次,再直接乘上这个值,避免每次除法。看似微不足道的改动,让推理速度进一步提升到约 20ms,体验已经相当流畅。

这一小段经历也点出了一个容易被忽视的常识:除法通常比乘法慢。在 8400 个候选框 × 每个框若干像素的归一化循环里,替换除法的收益被显著放大。

4.2 处理模型输出:IOU 与 NMS

模型输出的是一个 84000 个元素的数组,其 dims[1, 10, 8400]:84000 按每 10 个一组划分,每组包含边界框中心 x、中心 y、宽、高,以及 6 个类别的置信度,共 8400 组候选结果。

直接输出没法用,需要两步后处理:

  1. 置信度过滤:过滤掉置信度低于 0.6 的框;
  2. NMS 去重:用 IOU(Intersection over Union,交并比)作为依据,去除互相重叠的框。

IOU 的直观定义:把两个框的面积相加,减去重叠区域得到"实际占用的并集面积",再用重叠面积除以并集面积:

IOU = 交集面积 / 并集面积 = 交集面积 / (框1面积 + 框2面积 - 交集面积)

作者的 NMS 实现非常简洁:按置信度降序排序,从最高分开始遍历,凡是与当前最高分框 IOU 大于 0.7 的都视为同一物体并剔除:

function nms(boxes: Box[], iouThreshold: number): Box[] {
  // 1. Filter by confidence and sort in descending order
  const candidates = boxes
    .filter(box => box.confidence > 0.6)
    .sort((a, b) => b.confidence - a.confidence)

  const result: Box[] = []

  while (candidates.length > 0) {
    // 2. Pick the box with the highest confidence
    const bestCandidate = candidates.shift()!
    result.push(bestCandidate)

    // 3. Compare with remaining boxes and remove ones with high IOU
    for (let i = candidates.length - 1; i >= 0; i--) {
      // The iou() function needs to be implemented separately, as described in the article.
      if (iou(bestCandidate, candidates[i]) > iouThreshold) {
        candidates.splice(i, 1)
      }
    }
  }

  return result
}

仓库内还有一个配套的 IOU/NMS 可视化组件:基于 Vue 3 + @vueuse/coreuseDraggableuseElementBounding 实现,页面中有两个可拖拽的检测框(box_1 / box_2),组件实时计算交集矩形(取 Math.max 的左/上边界与 Math.min 的右/下边界)、并集面积与 IOU 值并展示计算过程,让 IOU/NMS 的几何含义一目了然,是理解上文算法的最佳配套教具。

4.3 实践中发现的问题

通过这次完整实践,作者总结出纯视觉方案当前的三处短板:

  1. 无法识别非方形图像:一旦输入不是正方形,模型所有结果的置信度都会变得极低,甚至为 0;
  2. 类别混淆:模型能区分一级和二级组装机,但会把箱子这类方形物体也误识别为组装机;
  3. UI 叠加干扰:实际游玩时,机器贴图上常叠加状态指示(电力、当前配方、已安装模块等),会干扰模型识别。

这些问题也为后续改进指明了方向——提升模型性能、让 AI 真正上手控制游戏,是下一阶段的核心任务(作者在 DevLog @ 2026.02.16 中记录了将该纯视觉 Playground 复用到 Dome Keeper 等更多游戏的计划)。

五、总结:一条可复用的"游戏视觉感知"流水线

回顾这条纯视觉路线,它实际上沉淀了一套与具体游戏解耦的通用流程:

游戏客户端 Docker 化(xvfb + x11vnc + websockify + novnc)
→ Lua 脚本自动采集画面 + 标注(RCON 驱动)
→ YOLO 训练与 ONNX 导出(Ultralytics)
→ 浏览器端 onnxruntime-web + WebGPU 推理
→ 置信度过滤 + NMS 后处理 → 输出检测结果

几个可以直接迁移到其他项目的关键经验:

  • 图形应用跑在容器里并不需要完整桌面环境,xvfb 加最小 X 环境即可;
  • 浏览器只能走 HTTP,VNC 到浏览器的链路需要 websockify + novnc 桥接;
  • onnxruntime-web 必须显式声明 ['webgpu', 'wasm'] 双后端,否则 WebGPU 不生效;
  • 像素归一化时用 1/255 乘法替代除法,在 8400 级循环上是实打实的性能优化;
  • 检测结果后处理 = 置信度阈值(0.6)+ 按置信度排序的贪心 NMS(IOU 阈值 0.7),实现不过数十行代码。

作者也坦诚列出了局限:非方形输入失效、类别混淆、UI 叠加干扰。如果你对"AI 玩游戏"的纯视觉路线感兴趣,这套方案是一个完整、可运行、且被真实项目验证过的起点。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.89 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
602
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
526