NitroGen: An Open Foundation Model for Generalist Gaming Agents

Paper: arXiv:2601.02427 Code: MineDojo/NitroGen Code reference: main @ 32608444 (2026-01-25)

1. Motivation (研究动机)

通用 embodied agents 的核心瓶颈不是“没有更复杂的游戏”,而是缺少可规模化、可复用、带动作标签的交互数据。NLP 可以直接从互联网文本扩展 pre-training,视觉模型可以从互联网图像/视频扩展 pre-training;但 embodied AI 需要 observation-action pairs。传统路线通常有三类成本:人工演示昂贵、RL 需要可编程 simulator 和 reward、游戏内部 API / 状态接口依赖具体环境。这导致许多游戏智能体看起来强,但本质上仍是单游戏或单平台专家。例如 Minecraft VPT / MineDojo 路线证明公开视频和开放环境有价值,但平台集中在 Minecraft。

Voyager / Cradle 等 LLM agent 更强调高层规划和工具/API 使用,但需要结构化接口、手写 skill、文本反馈或游戏特定 prompt。Atari / StarCraft / Dota 系列 RL 则依赖精确定义的 reward、可重复 simulator、环境内部状态或海量自博弈。NitroGen 的出发点是:商业游戏生态已经包含大量人类玩家的视频演示;其中相当一部分视频会叠加 controller input overlay。这些 overlay 等价于“人类动作标签被画在屏幕上”,只要能自动解析,就可以把公开视频转成 video-action demonstrations。

论文要解决的具体问题是:能否从 noisy internet gameplay videos 中自动恢复 frame-level gamepad actions,并用这些数据训练跨游戏的 vision-action foundation model。这个目标比“做一个能通关所有游戏的 agent”更窄,也更现实。NitroGen 不承诺长程规划、不承诺语言跟随、不承诺完全 unseen game 的端到端通关。它要证明的是:互联网规模的游戏视频可以训练出 fast-reacting system-1 sensory policy,作为未来 RL / language following / task-specific post-training 的起点。

这件事值得研究,因为游戏是 physical AI 和真实机器人之间的低成本中间域。游戏有像素 observation、连续控制、稀疏目标、动态物理、不同视角、不同角色和多样 mechanics。但游戏比真实机器人便宜:公开视频规模大、采集风险低、失败代价低、任务类型多。如果这条路线成立,embodied pre-training 就不再完全依赖少量昂贵人工演示或封闭仿真器。NitroGen 还试图补齐另一个缺口:缺少用于 multi-game policies 的统一评测接口。

商业游戏通常没有 Gymnasium API,不能像 MuJoCo 或 Atari 那样 step/reset/read observation。论文因此提出 universal simulator,把任意商业游戏包装成 frame-by-frame interaction loop。这个 simulator 的目标不是提供内部状态,而是只通过屏幕截图和 gamepad 输入构建统一 observation/action interface。从研究问题拆解看,NitroGen 同时处理三个必要条件。第一,数据:从 71,000 小时 raw videos 过滤出约 40,000 小时 labeled gameplay。

第二,评测:10 个商业游戏、30 个任务,覆盖 2D/3D、combat/navigation/game-specific mechanics。第三,模型:500M parameter DiT-based policy,用 flow matching 从视觉上下文生成 future gamepad action chunks。这三个条件必须同时成立:只有数据而无评测,无法判断泛化;只有评测而无数据,无法 scale;只有模型而无动作抽取,也无法从互联网视频中学习。本文的研究价值可以理解为把“游戏 agent”从 handcrafted environment problem 推向 foundation-model data problem。

它关注的是数据源、动作空间、评测协议和可复用模型的共同扩展,而不是单个游戏上的最优策略。

Figure 1 解读:NitroGen 由三部分组成。中心是 multi-game foundation agent,从视觉 observation 预测 gamepad actions;右侧是 internet-scale dataset,把公开视频中的 input overlay 解析成动作标签;左侧是 universal simulator,把商业游戏包装成 Gymnasium-like API。图的关键点是“只从像素和手柄动作学习”:不读取游戏内部状态,不要求每个游戏暴露专用 API,也不要求为每个环境手写 perception module。

从 Figure 1 也可以看出论文的边界。 NitroGen 更像视觉-动作 prior,而不是完整 cognitive agent。 它把 short-horizon reaction 学好,再把 long-horizon planning、language instruction following、自我改进留给 future post-training。 这个边界很重要:如果用“是否能完全自主通关任意游戏”评价 NitroGen,会误解论文贡献;更合理的评价是“是否能把 noisy internet data 转成可迁移的 action foundation model”。

2. Idea (核心思想)

核心 insight:公开视频中的 gamepad overlay 是天然的弱监督动作标签;只要能大规模定位并解析 overlay,就能把互联网 gameplay videos 转成统一 action-labeled demonstrations。NitroGen 的本质创新不是单独提出一个新 transformer block,而是把 data extraction、unified action interface、universal simulator 和 flow-matching policy 组合成一条可扩展的 embodied pre-training pipeline。

它把“玩游戏”抽象为从 RGB frame 到 standardized gamepad vector 的行为克隆问题,从而允许一个模型跨 1,000+ games 学习常见 sensory-motor patterns。关键创新可以拆成四层。第一层是数据层:利用 input overlay,自动构建 40,000 小时 / 1,000+ games 的 video-action dataset。第二层是动作层:把不同游戏的控制统一成 gamepad layout,包括 16 个二值按键和左右摇杆连续坐标。

第三层是环境层:用 universal simulator 在商业游戏中做 frame-by-frame evaluation,形成 10 games / 30 tasks 的 benchmark。第四层是模型层:使用 adapted GR00T N1 / DiT architecture,通过 conditional flow matching 生成 16-step future action chunks。与单游戏 RL 专家相比,NitroGen 牺牲了环境专用 reward 优化,换取跨游戏数据规模。

与 LLM-driven game agents 相比,NitroGen 不依赖语言、API、OCR 文本或高层规划,而是直接学习视觉到控制的 low-level motor prior。与 VPT / MineDojo 相比,NitroGen 不局限于 Minecraft,也不依赖一个开放游戏生态提供统一环境。与 SIMA / GATO 一类多任务行为克隆相比,NitroGen 的数据来源更强调 internet videos + overlay parsing,而不是主要依赖人工/合同工演示或 RL-generated data。论文最重要的设计取舍是把问题限制为 gamepad-based, short-context, reactive control。

这让动作空间能统一,也让数据解析可行。但代价是 keyboard/mouse-heavy games、strategy/simulation games、需要长程推理或复杂界面操作的任务会天然欠覆盖。因此 NitroGen 的“generalist”并不是覆盖所有交互形式,而是在 gamepad-friendly commercial games 中学习跨 genre 的 common gameplay primitives。这种定位使它更像 robotic foundation model 的游戏版。GR00T N1 本来面向 humanoid robot;NitroGen 移除语言和 state encoder,只保留视觉条件下的 action generation。

游戏中的 actions 与机器人关节动作不同,但共同点是都需要把高维视觉压缩成短时动作 chunk。 用 flow matching 生成 action chunk 的好处是避免逐步 autoregressive 产生每个按键/摇杆值。 模型一次生成未来 16 个动作,有助于 temporal consistency,也减少单步抖动。 这与论文观察一致:single-action generation 不如 16-action chunks 稳定;多帧历史反而没有显著收益。 直觉上,许多 action games 的当前画面已经包含足够强的 affordance:敌人位置、角色朝向、平台边缘、障碍物、血条和 UI。 对于“闪避、攻击、跳跃、移动到目标方向”等 system-1 行为,最后一帧往往比长历史更关键。 这也解释了为什么 NitroGen 能在 short tasks 上表现不差,但不能替代规划器。

3. Method (方法)

3.1 Overall framework

NitroGen pipeline 可以从左到右理解为:公开视频 → overlay localization → action parsing → filtering → video-action dataset → flow-matching policy → universal simulator evaluation。 训练数据只需要 RGB frames 和从 overlay 恢复出的 controller states。 评测时模型也只看屏幕截图,并输出 gamepad actions。 这保证了训练和部署之间的 interface 一致:observation 是像素,action 是统一 gamepad vector。 论文的三个模块各自解决一个 scaling bottleneck。 Dataset pipeline 解决“动作标签从哪里来”。

Universal simulator 解决“如何在任意商业游戏中评测”。 Foundation model 解决“如何把大规模 noisy demonstrations 压缩成可迁移 policy”。 三者的交互关系很直接:数据 pipeline 产生 actions;模型拟合这些 actions;simulator 检验模型在真实游戏闭环中的 task completion。

3.2 Internet-scale video-action dataset

数据规模与来源

数据 pipeline 的输入是公开 gameplay videos。 作者先收集约 71,000 小时 raw videos。 经过 overlay 检测、动作解析和质量过滤后,得到约 40,000 小时 gameplay。 论文进一步报告 dataset 包含 38,739 videos,来自 818 个 content creators。 平均视频长度约 1 小时 50 分钟。 数据覆盖 1,000+ unique games,其中 846 个游戏超过 1 小时数据。

互联网视频噪声

这使它成为论文所称 largest labeled video-action dataset for video games。 数据本身仍是 noisy demonstrations。 噪声来源包括 overlay 软件延迟、视频压缩、直播聊天/subscribe prompts/progress trackers、不同玩家的 sensitivity 和 custom button mappings。 NitroGen 的方法选择不是完全消除这些噪声,而是通过规模和过滤让模型学习 robust policy。

Figure 2:动作抽取 pipeline

Figure 2 解读:Figure 2a 展示 overlay videos 的多样性,controller 类型、透明度、布局和压缩伪影都不同;Figure 2b 展示 action extraction pipeline。先从视频中采样 25 帧,用 SIFT/XFeat 与约 300 个 controller templates 做 feature matching,估计 affine transform 并裁出 gamepad 区域;再用 hybrid classification-segmentation model 解析 joystick 和 button states。这个图说明 NitroGen 的关键不是人工标注每帧动作,而是把输入 overlay 当成可自动读取的弱监督信号。

Stage 1 是 template matching。作者维护约 300 个常见 controller templates。对每个视频采样 25 帧。每个采样帧与所有 templates 做 SIFT 和 XFeat feature matching。匹配点用于估计 affine transform。只有至少 20 个 inliers 的匹配才被接受。最终选择 matching score 最高的区域作为 gamepad overlay crop。这个阶段解决的是 overlay localization 问题:不同 creator 会把手柄放在不同位置、缩放到不同大小、设置不同透明度。Stage 2 是 gamepad action parsing。

SegFormer-like action parser

作者使用 fine-tuned SegFormer-like segmentation model。模型输入是连续两帧的 gamepad crop,在空间维度拼接。使用两帧而不是单帧,是为了捕捉短期动态,尤其是 joystick 移动与视频压缩造成的局部变化。输出包括 joystick 的 discrete grid segmentation mask,以及 button 的 binary states。论文明确说 joystick 用 segmentation mask 估计显著优于直接回归坐标。这说明摇杆位置识别更像视觉定位问题,而不是简单数值回归。Stage 2 的训练数据是 synthetic overlay data。

synthetic overlay training data

作者从 NitroGen training frames 中采样背景。然后用 Open Joystick Display、Input Overlay、GamePad Viewer 等软件程序化叠加 controller templates。每个 template 随机 button states 和 joystick positions。总计生成 8M labeled frames。为了模拟真实互联网视频,训练时随机改变 overlay opacity、controller size 和 video compression。

训练与质量过滤

Action parser 用 AdamW,learning rate ,linear decay,weight decay 0.1,batch size 256。Stage 3 是 quality filtering。论文把解析出的动作进一步过滤,目标是保证数据既准确又有 meaningful gameplay。过滤的必要性来自三个事实。第一,模板匹配可能找错 overlay 或在低清晰度视频中不稳定。第二,button/joystick parser 可能受压缩、遮挡、动态 UI 干扰。第三,视频片段可能是菜单、暂停、过场或非 gameplay。如果不过滤,这些错误会直接变成 behavior cloning target。

因此 NitroGen 的数据 pipeline 本质上是 weak-label extraction + synthetic parser training + scale filtering 的组合。

Figure 3:数据分布偏置

Figure 3 解读:Figure 3 展示 NitroGen dataset 按 game title 和 genre 的 gameplay hour 分布。它证明数据覆盖非常广,但也暴露分布偏置:action / gamepad-friendly titles 更容易进入数据集,keyboard-only、strategy、simulation、复杂界面操作类游戏天然更少。这个偏置解释了后续 transfer 结果:common combat/navigation skills 更容易迁移,game-specific mechanics 和稀有视觉风格迁移较弱。

3.3 Universal simulator and benchmark

universal simulator 的 step/reset 替代方案

商业游戏通常不提供可编程 step/reset API。NitroGen 的 universal simulator 通过控制 game engine 的 system clock 来做 frame-by-frame interaction。每一步流程是:暂停游戏或控制仿真时间 → 截屏得到 RGB observation → 让模型预测 action → 注入 gamepad input → 推进游戏。这种方法不修改 game code。它适用于使用 system clock 处理 physics/interactions 的游戏。论文把 real-time 或 asynchronous deployment 留给未来工作。

pause/resume 对物理的影响验证

潜在风险是频繁 pause/resume 可能改变游戏物理。Appendix B.1 对此做了验证:作者录制人类在几个游戏中玩 5 分钟的 video/action ground truth。然后从同一初始位置 replay 相同 actions,一次 real-time 不暂停,一次高频随机 pause/resume。连续动作游戏约 1 分钟后视觉开始 diverge,离散动作游戏约 3 分钟后 diverge。两种 replay 条件的 divergence 时间相同,因此论文认为差异主要来自误差积累,而不是 pause/resume 破坏物理。Benchmark 使用统一 observation/action space。

Observation 是 single RGB frame。Action 是 standardized gamepad vector。按键部分是 16-dimensional binary vector:4 个 d-pad buttons、4 个 face buttons、2 个 shoulders、2 个 triggers、2 个 joystick thumb buttons、start、back。摇杆部分是 4-dimensional continuous vector,对应 left/right joystick positions。

action 与 observation 空间

注意论文 Appendix 的训练公式写 ,released tokenizer config 里 max_action_dim=25。这不是训练配置的直接冲突证据,因为 repo 未发布 checkpoint-specific training config;note 中只把论文动作维度写作 paper formula,把 tokenizer default 作为 released code interface 观察。Evaluation suite 覆盖 10 个商业游戏、30 个任务。其中 5 个是 2D games,5 个是 3D games。

benchmark 任务构成

2D 包含 3 个 side-scrollers 和 2 个 top-down roguelikes with procedurally generated levels。3D 包含 open-world games、action-RPG 和 racing 等类型。任务类型包括 11 个 combat tasks、10 个 navigation tasks、9 个 game-specific tasks。每个任务都有清晰 start state 和 goal state。一次 attempt 通常持续几分钟;有些任务人类可能需要数小时重复尝试。

作者选择 initial visual state 足以触发正确行为的任务,把 language-conditioned task specification 留给未来工作。Success rate 通过 human evaluation 统计。

Figure 4:跨游戏 rollout

Figure 4 解读:Figure 4 给出 NitroGen 在不同 2D/3D 游戏中的 rollout 片段。任务可能从几秒到几分钟不等;有些任务可能依赖固定 layout 的记忆,有些任务在 procedurally generated worlds 中需要即时适应。图的作用不是证明模型会长程规划,而是展示同一个视觉-动作 policy 可以在不同画面风格、视角和控制需求中产生可执行动作。

3.4 Foundation model architecture

NitroGen 的 policy 是 DiT-based flow-matching action generator。它 adapted from GR00T N1,但移除了 language encoder 和 state encoder,只保留视觉输入和一个 action head。RGB 输入 resize 到 。Vision encoder 是 SigLIP 2 ViT。每帧产生 256 image tokens。Action generator 是 diffusion transformer (DiT)。Noisy action chunks 先通过 MLP 编码成每个 timestep 一个 action token。

DiT block 使用交替 self-attention 和 cross-attention。Self-attention 在 action tokens 内建模时间相关性。Cross-attention 让 action tokens condition on encoded frame tokens。最后用独立作用于时间维度的 MLP,把 final action tokens 解码成 continuous action vectors。模型生成的是 16-action chunks,而不是单个下一步动作。论文认为 chunk generation 能提升 temporal consistency。

如果只生成 single action,模型容易在按键和摇杆上逐帧抖动。16-step chunk 则让模型在短时间内输出更连贯的动作片段。论文测试过多帧 visual context,但没有发现明显收益。作者解释是许多 selected tasks 的 initial state 已经提供足够 context。因此主模型使用 single context frame。released README 也强调当前 500M DiT “can only see the last frame”。这与模型定位一致:它是 fast-reacting sensory model,不是 history-aware planner。

3.5 Conditional flow matching objective

给定 ground-truth action chunk 、observation 、flow-matching timestep 、Gaussian noise ,论文构造 noisy action: 条件速度场定义为: Conditional Flow Matching loss 是: 其中 是 DiT action generator, 是 image encoder。 论文 follow GR00T / diffusion-policy style,使用 shifted beta distribution 采样 ,更偏向 small timesteps。 released code 中对应字段是 noise_beta_alpha=1.5noise_beta_beta=1.0noise_s=0.999,但这些是 NitroGen_Config default,不应当被当作论文训练 launch config。 训练时模型输入 noisy trajectory 和视觉 tokens,输出 velocity prediction。 目标是最小化 predicted velocity 与 的 MSE。 推理时初始化: 然后用 Euler integration 迭代: 论文使用 denoising steps,因为更多 steps 没有 measurable improvement。

3.6 Released code status and paper-code gaps

论文公式与 released code 实现差异:GitHub main@32608444 主要发布 inference/server/play pipeline、tokenizer、model forward / get_action 代码;未发布论文中的 video crawling、template matching、SegFormer action parser training、quality filtering、完整 pre-training launch config 或 evaluation task definitions。因此,action extraction 和 dataset construction 的伪代码只能依据论文描述;

flow-matching forward/inference、tokenizer、server/client/gamepad loop 可以依据 released code。论文公式中 action chunk 写作 。released tokenizer config 设有 max_action_dim=25,并且 old_layout 支持不同 action layout。由于 repo 未发布 checkpoint-specific training config,note 不把这个 default 当作论文训练维度的替代,只记录为 code interface 需要注意的实现边界。

README 说明当前模型是 500M parameter DiT,只能看 last frame。

InferenceSession 仍通过 checkpoint modality_cfg.frame_per_sample 和 CLI --ctx 支持 context buffer。 实际 context length 由 checkpoint config / context_length 参数决定;对公开 ng.pt 的论文表述是 last-frame model。 Released code 支持 classifier-free guidance-like inference:get_action_with_cfg 会构造 conditional 和 unconditional tokenized data,并用 pred_cond + cfg_scale * (pred_cond - pred_uncond) 组合速度。 论文正文没有把 CFG 作为主要方法贡献,因此 note 把它列为 released inference option,而不是论文核心训练方法。

3.7 Pseudocode: dataset action extraction (paper-level)

import torch
 
def parse_gamepad_overlay(video, templates, segformer, min_inliers=20):
    """Paper-level pipeline; released GitHub does not include this extractor."""
    sampled_frames = sample_25_frames(video)
    best_affine = None
    best_score = -1
 
    for frame in sampled_frames:
        for template in templates:  # about 300 controller templates
            matches = match_with_sift_and_xfeat(frame, template)
            affine, inliers = estimate_affine_transform(matches)
            if inliers >= min_inliers and inliers > best_score:
                best_score = inliers
                best_affine = affine
 
    if best_affine is None:
        return None
 
    parsed_actions = []
    prev_crop = None
    for frame in iter_video_frames(video):
        crop = crop_gamepad_region(frame, best_affine)
        pair = concatenate_spatially(prev_crop if prev_crop is not None else crop, crop)
        joystick_mask, button_logits = segformer(pair)
        joystick_xy = decode_11x11_grid(joystick_mask)
        buttons = (button_logits.sigmoid() > 0.5).float()
        parsed_actions.append(to_standard_gamepad_vector(joystick_xy, buttons))
        prev_crop = crop
 
    return quality_filter(torch.stack(parsed_actions))

3.8 Pseudocode: tokenizer and action packing (released code)

import torch
 
def pack_actions(buttons, j_left, j_right):
    """Mirrors NitrogenTokenizer / NitroGen action packing."""
    assert buttons.shape[:3] == j_left.shape[:3] == j_right.shape[:3]
    j_left = (j_left + 1.0) / 2.0
    j_right = (j_right + 1.0) / 2.0
    actions = torch.cat([j_left, j_right, buttons], dim=-1)
    actions = actions.squeeze(1)  # remove chunk dimension used by code layout
    return actions
 
def unpack_actions(actions):
    j_left = actions[:, :, :2] * 2.0 - 1.0
    j_right = actions[:, :, 2:4] * 2.0 - 1.0
    buttons = actions[:, :, 4:]
    j_left = torch.clamp(j_left, -1.0, 1.0)
    j_right = torch.clamp(j_right, -1.0, 1.0)
    buttons = (buttons > 0.5).float()
    return {"j_left": j_left, "j_right": j_right, "buttons": buttons}

3.9 Pseudocode: conditional flow-matching training step (released model forward + paper formula)

import torch
import torch.nn.functional as F
 
def nitrogen_forward_loss(model, batch):
    """Condensed from nitrogen/flow_matching_transformer/nitrogen.py::NitroGen.forward."""
    visual_features = model.encode_images(batch["images"])
    actions = batch["actions"]
    noise = torch.randn_like(actions)
    t = model.sample_time(actions.shape[0], device=actions.device, dtype=actions.dtype)
    t = t[:, None, None]
 
    noisy_trajectory = (1.0 - t) * noise + t * actions
    velocity_target = actions - noise
    t_discrete = (t[:, 0, 0] * model.num_timestep_buckets).long()
 
    action_features = model.action_encoder(noisy_trajectory, t_discrete, batch["embodiment_id"])
    vl_embs, sa_embs = model.prepare_input_embs(
        batch["vl_token_ids"],
        batch["sa_token_ids"],
        visual_features,
        action_features,
        batch["dropped_images"],
        game_ids=batch.get("game_id"),
    )
    vl_embs = model.vl_self_attention_model(vl_embs)
    hidden, _ = model.model(
        hidden_states=sa_embs,
        encoder_hidden_states=vl_embs,
        encoder_attention_mask=batch["vl_attn_mask"],
        timestep=t_discrete,
        return_all_hidden_states=True,
    )
    pred_velocity = model.action_decoder(hidden, batch["embodiment_id"])
    pred_velocity = pred_velocity[:, -actions.shape[1]:]
    mask = batch["has_real_action"][:, None, None] * batch["actions_mask"]
    return (F.mse_loss(pred_velocity, velocity_target, reduction="none") * mask).sum() / (mask.sum() + 1e-6)

3.10 Pseudocode: flow-matching inference and session loop (released code)

import torch
 
@torch.no_grad()
def sample_action_chunk(model, tokenized_data):
    """Condensed from NitroGen.get_action."""
    batch_size = tokenized_data["images"].shape[0]
    actions = torch.randn(
        batch_size,
        model.config.action_horizon,
        model.config.action_dim,
        device=tokenized_data["images"].device,
        dtype=tokenized_data["images"].dtype,
    )
    visual_features = model.encode_images(tokenized_data["images"])
    dt = 1.0 / model.num_inference_timesteps
 
    for i in range(model.num_inference_timesteps):
        t_cont = i / float(model.num_inference_timesteps)
        t_discrete = int(t_cont * model.num_timestep_buckets)
        action_features = model.action_encoder(
            actions,
            torch.full((batch_size,), t_discrete, device=actions.device),
            tokenized_data["embodiment_id"],
        )
        vl_embs, sa_embs = model.prepare_input_embs(
            tokenized_data["vl_token_ids"],
            tokenized_data["sa_token_ids"],
            visual_features,
            action_features,
            tokenized_data["dropped_images"],
            game_ids=tokenized_data["game_ids"],
        )
        vl_embs = model.vl_self_attention_model(vl_embs)
        pred = model.model(
            hidden_states=sa_embs,
            encoder_hidden_states=vl_embs,
            encoder_attention_mask=tokenized_data["vl_attn_mask"],
            timestep=torch.tensor([t_discrete], device=actions.device),
        )
        pred_velocity = model.action_decoder(pred, tokenized_data["embodiment_id"])
        actions = actions + dt * pred_velocity[:, -actions.shape[1]:]
 
    return {"action_tensor": actions}
 
def inference_session_step(session, obs):
    """Condensed from nitrogen/inference_session.py::InferenceSession.predict."""
    current_frame = session.img_proc([obs], return_tensors="pt")["pixel_values"]
    session.obs_buffer.append(current_frame)
    pixel_values = torch.cat(list(session.obs_buffer), dim=0)
 
    if session.action_interleaving and len(session.action_buffer) > 0:
        action_tensors = {
            key: torch.cat([a[key] for a in session.action_buffer], dim=0)
            for key in ["buttons", "j_left", "j_right"]
        }
    else:
        action_tensors = {"buttons": None, "j_left": None, "j_right": None}
 
    if session.is_flowmatching:
        predicted = session._predict_flowmatching(pixel_values, action_tensors)
    else:
        predicted = session._predict_ar(pixel_values, action_tensors)
 
    session.action_buffer.append(predicted)
    return {
        "j_left": predicted["j_left"].squeeze().cpu().numpy(),
        "j_right": predicted["j_right"].squeeze().cpu().numpy(),
        "buttons": predicted["buttons"].squeeze().cpu().numpy(),
    }

3.11 Pseudocode: universal simulator interaction loop (released play/server path)

 
def run_game_loop(env, model_client):
    """Condensed behavior of scripts/play.py + scripts/serve.py."""
    obs, info = env.reset()
    model_client.reset()
 
    while True:
        response = model_client.predict(obs)
        actions = convert_prediction_to_gamepad_events(response)
        for action in actions:
            obs, reward, terminated, truncated, info = env.step(action)
            if terminated or truncated:
                obs, info = env.reset()
                model_client.reset()
                break

Code reference: main @ 32608444 (2026-01-25) — pseudocode and mapping based on this commit

Paper ConceptSource FileKey Class/Function
Checkpoint/model loadingnitrogen/inference_session.pyload_model, InferenceSession.from_ckpt
Frame/action history buffersnitrogen/inference_session.pyInferenceSession.__init__, predict, reset
Flow-matching model confignitrogen/flow_matching_transformer/nitrogen.pyNitroGen_Config
Training forward / CFM lossnitrogen/flow_matching_transformer/nitrogen.pyNitroGen.forward
Euler denoising inferencenitrogen/flow_matching_transformer/nitrogen.pyNitroGen.get_action
Optional CFG-style inferencenitrogen/flow_matching_transformer/nitrogen.pyNitroGen.get_action_with_cfg
Action packing / unpackingnitrogen/mm_tokenizers.pyNitrogenTokenizer.pack_actions, unpack_actions, encode, decode
Client APInitrogen/inference_client.pyModelClient.predict, reset, info
Model serverscripts/serve.pyZMQ request loop, InferenceSession.from_ckpt
Commercial-game wrappernitrogen/game_env.pyGamepadEnv, GamepadEmulator, step, pause, unpause
Demo play loopscripts/play.pyGamepadEnv, policy/client integration
Dataset crawler / overlay extractornot released in repopaper-only description at this commit
SegFormer parser trainingnot released in repopaper-only description at this commit
Pre-training launch confignot released in repopaper reports hyperparameters; no source config anchor

4. Experimental Setup (实验设置)

4.1 Datasets

Raw collection:约 71,000 小时 publicly available gameplay videos with gamepad overlays。 Final NitroGen dataset:约 40,000 小时 gameplay videos,覆盖 1,000+ games。 论文更精确地报告:38,739 videos,818 content creators,平均视频长度 1 小时 50 分钟。 覆盖性统计:846 个游戏超过 1 小时数据。 Action parser synthetic data:8M labeled frames,由 NitroGen frames + programmatically overlaid controller templates 生成。 Synthetic parser 数据随机化 button states、joystick positions、overlay opacity、controller size、video compression。

Action parsing benchmark:作者用 OBS 录制 6 个游戏,并随机化 opacity、gamepad size、gamepad type。 该 benchmark 同步记录 ground-truth controller inputs,用于计算 joystick 和 button frame accuracy。 Evaluation suite:10 commercial games、30 tasks。 任务类型:11 combat tasks、10 navigation tasks、9 game-specific tasks。 游戏类型:5 个 2D games、5 个 3D games;2D 包括 side-scrollers 和 top-down roguelikes;3D 包括 open-world/action-RPG/racing 等风格。

4.2 Baselines and comparison protocol

Zero-shot/pre-training evaluation 主要展示单个 pre-trained NitroGen 500M 在多个游戏任务上的 task completion。 Post-training evaluation 使用 held-out game protocol。 作者先在 full dataset excluding one held-out game 上 pre-train NitroGen。 然后在 held-out game 的有限数据上 fine-tune pre-trained checkpoint。 对照组是相同 architecture、相同 fine-tuning data、相同 compute budget 的 scratch model。 这个设置隔离了 pre-training 的价值:如果 fine-tuned NitroGen 优于 scratch,收益来自跨游戏 pre-training,而不是模型结构或下游数据量差异。

论文没有把公开通用 VLM/LLM agents 作为主表格 baseline。 这是因为 NitroGen 的输出是 continuous gamepad actions,而多数 LLM game agents 依赖文本/GUI/API 或高层命令。 因此主要对照是 “pre-trained vs scratch under identical downstream budget”。

4.3 Evaluation metrics

Task completion rate (%):人类评估的任务完成率。

Figure 6 中每个 game/type 的结果来自 3 tasks × 5 rollouts。

Joystick :预测 joystick 坐标与 ground truth 的相关评分,左右 joystick 平均。 Button frame accuracy:逐帧二值 button state accuracy。 Relative improvement (%):fine-tuned pre-trained model 相对 scratch model 的 task completion rate 提升。 Low-data transfer regime:Figure 7b 使用 30 小时 held-out game data,比对 combat/navigation/game-specific tasks 的相对提升。

4.4 Training and inference config

论文报告模型为 500M parameter DiT-based vision-action model。 Vision encoder:SigLIP 2 ViT。 输入分辨率:RGB 。 视觉 token:每帧 256 image tokens。 视觉上下文:主模型使用 single context frame。 Action horizon:16-action chunks。

Paper formula action shape:

Optimization:AdamW。 Weight decay:0.001。 Learning-rate schedule:WSD schedule。 Constant learning-rate phase:。 EMA decay:0.9999。 结果使用 EMA weights。

Augmentations:random brightness、contrast、saturation、hue、random rotation 、random crops。Inference:Euler denoising, denoising steps。Action parser training:AdamW,learning rate ,linear decay,weight decay 0.1,batch size 256。

Released code config anchor:当前 GitHub 不包含完整训练 launch script 或 dataset extraction script。因此上面的训练数值来自论文正文/appendix;note 没有把 NitroGen_Config defaults 或 README inference defaults 当作训练配置。

Released inference usage:README 指示从 HuggingFace nvidia/NitroGen 下载 ng.pt,用 python scripts/serve.py <path_to_ng.pt> 启动 server,再用 python scripts/play.py --process '<game_executable_name>.exe' 连接 Windows game。

5. Experimental Results (实验结果)

5.1 Action extraction quality

Figure 5 解读:Action parser benchmark 在不同 controller families 上评估 joystick 和 buttons。论文报告 joystick position 的 overall average ,button frame accuracy 的 overall average 为 0.96。图中不同 controller family 的表现不完全一致,例如 joystick 大致在 0.77–0.85 区间,button accuracy 大致在 0.91–0.98 区间。结论是自动解析的动作标签足够支撑大规模 noisy behavior cloning,但不是无噪声 ground truth。

这个结果是 NitroGen 数据路线的必要证据。 如果 action parser 不可靠,40,000 小时数据只会放大标签错误。 和 button accuracy 0.96 说明 overlay parsing 足够强,但仍会保留 delayed inputs、compression artifacts、button mapping differences 等噪声。 因此后续模型成功不能被解释为“数据很干净”,而应解释为“规模 + filtering + robust policy 能吸收相当一部分噪声”。

5.2 Zero-shot / pre-training performance

Figure 6 解读:NitroGen 500M 在没有针对具体游戏 fine-tune 的情况下,在多种 game/type 上取得非平凡 task completion。图中可读出的主要平均完成率包括 61.2%、61.5%、55.0%、56.3%、52.0%、54.0%、46.0%、44.8%、37.9%。这些结果横跨 3D、2D top-down、2D side-scrolling 和不同 task types。论文强调:一些任务可能包含训练中见过的固定 layout,另一些任务来自 procedurally generated worlds,每次 playthrough 不同;作者没有发现两类任务之间有显著性能差异。

这个结果支持两个结论。 第一,NitroGen 学到的不只是某些固定视频片段的 playback。 它能在 procedural 或未固定布局中产生有用反应,说明模型获得了跨游戏 sensory-motor regularities。 第二,成功率仍远未接近 solved。 最高一组约 61% task completion,很多组合低于 50%。 因此 NitroGen 是 foundation checkpoint,而不是 finished game-playing system。

从任务类型看,combat 和 navigation 更容易受益。 这些任务有跨游戏共享结构:朝敌人移动、保持距离、攻击/闪避、沿道路前进、避开障碍。 Game-specific mechanics 更难,因为它们依赖单个游戏的特殊 affordances、UI 和规则。

5.3 Transfer / post-training

Figure 7 解读:Post-training 实验采用 held-out game 设置:先排除一个游戏做 pre-training,再用该游戏有限数据 fine-tune,并与同结构 scratch model 对比。Figure 7a 显示数据量增加时 completion rate 上升,isometric roguelike 上平均相对提升 10%,3D action-RPG 上平均相对提升 25%。Figure 7b 显示 30h low-data regime 下,3D action-RPG 的 combat task 相对提升 52%,navigation 提升 25%,game-specific task 只有 5%。这说明 pre-training 主要迁移 common gameplay skills,而不是自动学会每个游戏独有机制。

Transfer 结果是论文最强的 foundation-model 证据。 如果 NitroGen 只是记住训练游戏,它在 held-out game fine-tuning 中不应显著优于 scratch。 相反,在相同 downstream data/compute 下,pre-trained checkpoint 更 sample-efficient。 提升在 3D action-RPG 上更大,作者解释为这类游戏在训练分布中更常见。 Isometric roguelike 的视觉风格和 mechanics 较少见,因此平均提升只有 10%。 这说明 NitroGen 的迁移能力受 pre-training distribution 强烈影响。

它不是“任意游戏无条件泛化”,而是“在训练分布覆盖的 common patterns 上形成 reusable prior”。

5.4 Noise robustness and interpretation

论文明确列出数据噪声:overlay software 可能引入小延迟,parser 会产生误差,视频画面包含 creator-specific artifacts,不同玩家可能有不同 sensitivity 或 custom button mapping。 在这些噪声存在时,NitroGen 仍能训练出 robust multi-game policy。 这说明大规模 behavior cloning 对 label noise 有一定容忍度,尤其当目标是 short-horizon reaction 而不是精确复现某一位玩家策略。 但这些噪声也解释了性能天花板。 如果同一个视觉状态在不同玩家/设置下对应不同 action,BC loss 会学习平均化或多模态动作。 Flow matching 能表达连续 action distribution,但 task success 仍受数据一致性限制。

因此 future work 可能需要更好的 action normalization、game-specific controller mapping metadata、或者 RL post-training 来修正 noisy BC prior。

5.5 Limitations

作者明确指出 NitroGen 是 fast-reacting system-1 sensory model。它不能长程规划。它不能 follow language instructions。它只对短上下文做反应。它不能自我改进。它也不是完全 unseen game 的端到端通关 agent。数据分布偏向 action games 和 gamepad-friendly games。Keyboard-only games、strategy games、simulation games、复杂 keyboard/mouse manipulation 较少代表。这种 bias 会限制模型向需要 planning、界面操作或非手柄输入的 genres 泛化。

Universal simulator 的同步推理也有边界。论文验证 pause/resume 不会额外破坏若干游戏的 replay,但 real-time/asynchronous deployment 仍未解决。如果游戏 physics 不依赖 system clock,或者对暂停非常敏感,simulator 方案可能不适用。Released code 的边界也需要注意。公开 repo 能跑 inference/server/play,但不包含完整 dataset extraction 和 training pipeline。因此复现论文训练结果需要额外未发布工程。

这不影响使用公开 checkpoint,但会影响研究者复刻 pre-training、重新抽取数据或审计 parser 误差。

5.6 Overall conclusion

NitroGen 的主要贡献是验证一条可扩展路线:用互联网 gameplay videos + controller overlay parsing 构建 video-action dataset,并训练跨游戏 vision-action foundation model。 实验显示自动动作解析质量足够高,zero-shot 多游戏任务能取得非平凡完成率,held-out game post-training 能提升 sample efficiency。 最重要的读法是:NitroGen 不是终局 general game player,而是一个开放的 sensory-motor foundation checkpoint 和数据/评测基础设施。 它为后续 language-conditioned agents、RL fine-tuning、long-horizon planning、游戏 QA 自动化和 physical AI simulation pre-training 提供了起点。