# MinecraftSim **Repository Path**: kaiserkatze/MinecraftSim ## Basic Information - **Project Name**: MinecraftSim - **Description**: 利用 Minecraft 输出 EuRoC 风格的仿真数据集 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-14 - **Last Updated**: 2026-08-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # MinecraftSim 一个基于 **Minecraft 1.21.1 + NeoForge 21.1.248** 的数据采集 Mod。用于为玩家录制**单目图像、IMU 惯性数据和真实(地面真值)轨迹**,并输出 **EuRoC 风格数据集** 或 **ROS 2 bag 文件**,供视觉惯性里程计(VIO,如 ORB-SLAM3、VINS-Mono、VINS-Fusion 等)算法的开发与评测使用。 本项目**只负责图像与轨迹录制**,不包含任何里程计算法实现。 --- ## 目录 - [项目目的](#项目目的) - [功能特性](#功能特性) - [技术细节](#技术细节) - [总体架构](#总体架构) - [坐标系统与约定](#坐标系统与约定) - [图像采集](#图像采集) - [IMU 解算](#imu-解算) - [相机内参](#相机内参) - [EuRoC 数据集格式](#euroc-数据集格式) - [ROS 2 bag 格式](#ros-2-bag-格式) - [环境要求](#环境要求) - [构建方式](#构建方式) - [安装方式](#安装方式) - [使用方法](#使用方法) - [游戏内命令](#游戏内命令) - [输出目录结构](#输出目录结构) - [独立转换工具](#独立转换工具) - [配置项](#配置项) - [注意事项与已知限制](#注意事项与已知限制) --- ## 项目目的 视觉惯性里程计(VIO)算法的开发与评估需要**带有真实轨迹标注**的「图像 + IMU」数据集。真实场景的数据集(如 EuRoC MAV)采集成本高、真值依赖外部动作捕捉系统。本项目利用 Minecraft 游戏引擎: - 天然具备**精确、无噪声的地面真值**(玩家位置/朝向完全已知); - 可**完全自定义场景与运动模式**; - 提供**物理/运动学一致的 IMU**(由位姿差分推导,而非随机噪声)。 Mod 实时截取游戏渲染画面作为单目相机图像,同时根据玩家角色的实时运动位置、朝向,用运动学/物理学知识合成 IMU 数据,并把两者与真实轨迹一起写出为标准数据集。 --- ## 功能特性 - 游戏内命令录制,支持两种输出格式(**同一时间只能选一种**,冲突时聊天栏警告): - `/minecraftsim record euroc [sample_rate]` → 输出 EuRoC 风格数据集 - `/minecraftsim record ros2 [sample_rate]` → 输出 EuRoC 数据集并自动转换为 ROS 2 bag - 停止录制时打印**统计信息**(录制时长、轨迹长度、图像帧数、IMU 样本数)以及**可点击打开的输出目录 URI**。 - IMU 采样率可配置,**默认 200 Hz**;图像固定 20 Hz(EuRoC 标准)。 - RGB 彩色图像(PNG)、单目相机。 - 纯 Java 实现,**不依赖 ROS 2 / OpenCV / 任何 C++ 工具链**:ROS 2 bag 由内置的纯 Java 转换器直接生成(SQLite3 存储 + CDR 序列化)。 --- ## 技术细节 ### 总体架构 项目为**纯 Java** 项目,由两部分组成: 1. **录制 Mod(NeoForge)**:负责抓帧、位姿采样、IMU 合成、写出 EuRoC 数据集。 2. **EuRoC → ROS 2 bag 转换器**:纯 Java 移植版 `ros2-euroc2bag`,读取 EuRoC CSV 数据直接写 rosbag2(SQLite3)bag。 ``` src/main/java/com/minecraftsim/ ├── MinecraftSimMod.java # Mod 入口:注册命令 ├── MinecraftSimClient.java # 客户端入口:注册渲染钩子 ├── Config.java # 全局配置(重力、尺度、采样率) ├── command/ │ └── RecordCommand.java # /minecraftsim record ... 命令 ├── recording/ │ ├── RecordingManager.java # 录制状态机(互斥、时钟、统计、URI) │ ├── OutputFormat.java # NONE / EUROC / ROS2 │ ├── Pose.java # 位姿(位置 + 四元数,含插值) │ └── ImuGenerator.java # 由位姿差分解算 IMU 与地面真值 ├── capture/ │ ├── CameraCapture.java # glReadPixels 抓取帧缓冲 │ └── AsyncImageWriter.java # 后台线程编码 PNG + 写 CSV ├── writer/ │ ├── CsvWriter.java # 线程安全的 CSV 写出器 │ └── EuRoCDatasetWriter.java # 创建 mav0 目录结构、写 CSV/sensor.yaml ├── util/ │ └── Quaternion.java # 四元数运算、轴角、旋转矩阵、slerp └── euroc2bag/ ├── EuRoCToBag.java # EuRoC → rosbag2 转换主逻辑 ├── Rosbag2Writer.java # rosbag2 SQLite3 存储写入器 └── CdrWriter.java # CDR(XCDR v1 小端)序列化 ``` **线程模型**:命令在服务端线程发出,所有采样/磁盘 I/O 在客户端**渲染线程**执行;图像 PNG 编码与落盘在独立后台线程完成(有界队列,避免内存无限增长与丢帧)。 ### 坐标系统与约定 | 坐标系 | 定义 | |--------|------| | 世界系 `W` | Minecraft 世界坐标,Y 轴向上,单位米(`1 block = 1 m`) | | 载体/IMU 系 `B` | x = 前向(视线方向),y = 左,z = 上(与 EuRoC IMU 系一致) | | 相机系 `C` | 与载体系重合(`T_BC = I`),图像直接由玩家视角截取 | - 地面真值四元数为 `q_WB`(载体 → 世界),以 `(w, x, y, z)` 顺序存储。 - 重力方向为世界系 `-Y`,大小为 Minecraft 实际重力(见下)。 ### 图像采集 - 钩子 `RenderLevelStageEvent.Stage.AFTER_LEVEL`(HUD 渲染之前),读取主帧缓冲。 - `glReadPixels`(`GL_RGBA` / `GL_UNSIGNED_BYTE`)抓取当前帧,OpenGL 底左顺序在编码时垂直翻转,输出 **RGB 8-bit PNG**。 - 图像以实际窗口分辨率输出(内参与分辨率保持一致),默认 20 Hz。 - PNG 编码与磁盘写入在后台线程完成,避免阻塞渲染。 ### IMU 解算 IMU 由玩家位姿序列通过**有限差分**推导,与真实轨迹严格自洽: - 对相邻采样位姿 `(p_k, q_k)`: - 线速度 `v_k = (p_k − p_{k−1}) / Δt` - 线加速度 `a_k = (v_k − v_{k−1}) / Δt` - 角速度 `ω_k`:`q_rel = q_{k−1}⁻¹ ⊗ q_k` 的轴角除以 `Δt`(载体系) - **比力(加速度计测量)**:`f = R_WBᵀ · (a_world + g)`,其中 `g = (0, 32, 0) m/s²` - Minecraft 重力为 `0.08 block/tick²`,在 `1 block = 1 m`、20 tick/s 下等于 `32 m/s²`,因此数据在物理上与轨迹自洽,VIO 可直接积分。 - 采样率由命令参数控制,**默认 200 Hz**;通过渲染帧位姿插值(线性位置 + slerp 姿态)在任意帧率下保持固定采样率。 - 零偏 `b_w`、`b_a` 记为 0。 ### 相机内参 由渲染投影矩阵推导(针孔模型、方形像素、无畸变): ``` fovY = options.fov() // 游戏 FOV 设置(度) fx = fy = (H / 2) / tan(fovY / 2) cx = W / 2, cy = H / 2 畸变系数全为 0 ``` > 内参写入 `cam0/sensor.yaml`,与图像分辨率一致。如需更高精度可自行标定。 ### EuRoC 数据集格式 ``` mav0/ ├── cam0/ │ ├── data/ # 图像 .png │ ├── data.csv # "#timestamp [ns],filename" │ └── sensor.yaml # 相机内参 ├── imu0/ │ ├── data.csv # "#timestamp [ns],w_x,w_y,w_z,a_x,a_y,a_z" │ └── sensor.yaml └── state_groundtruth_estimate0/ ├── data.csv # timestamp,p_x,p_y,p_z,q_w,q_x,q_y,q_z, │ # v_x,v_y,v_z,b_w_x,b_w_y,b_w_z,b_a_x,b_a_y,b_a_z └── sensor.yaml ``` - `imu0/data.csv`:角速度单位 `rad/s`,比力单位 `m/s²`。 - `state_groundtruth_estimate0/data.csv`:位置 `m`、四元数 `(w,x,y,z)`、速度 `m/s`、零偏。 ### ROS 2 bag 格式 rosbag2 **SQLite3 存储**(`metadata.yaml` + `_0.db3`),消息以 `cdr` 序列化,Topics 与原版 `ros2-euroc2bag` 一致: | Topic | 类型 | |-------|------| | `/cam0/image_raw` | `sensor_msgs/msg/Image`(`rgb8`,或灰度图时 `mono8`) | | `/cam1/image_raw` | `sensor_msgs/msg/Image`(可选) | | `/imu0` | `sensor_msgs/msg/Imu` | | `/groundtruth/pose` | `geometry_msgs/msg/PoseStamped` | | `/leica0/pose`、`/vicon0/pose` | `geometry_msgs/msg/PoseStamped`(可选) | --- ## 环境要求 | 项 | 要求 | |----|------| | JDK | **21**(构建与运行均需,如 Temurin 21 / Microsoft Build of OpenJDK 21) | | Gradle | 8.x(仓库已包含 wrapper,`gradlew` 会自动下载,无需手动安装) | | 磁盘 | 首次构建需约 1.5 GB 下载缓存(NeoForge + Minecraft 反编译重编译),建议预留 4 GB 以上 | > 本项目开发环境将 JDK 装在 `D:\Program Files\Java\jdk-21.0.12+8`,并把 Gradle 用户目录重定向到 `D:\Program Files\gradle\gradle-home`(`GRADLE_USER_HOME`),以避免系统盘空间不足。 --- ## 构建方式 ```powershell # 1. 确保 JAVA_HOME 指向 JDK 21 $env:JAVA_HOME = "D:\Program Files\Java\jdk-21.0.12+8" # 2. 构建(首次会下载 NeoForge 依赖并重编译 Minecraft,耗时较长) .\gradlew.bat build ``` 产物:`build/libs/minecraftsim-0.1.0.jar`(已通过 NeoForge `jarJar` 内嵌 `sqlite-jdbc`,可独立安装)。 --- ## 安装方式 ### 方式一:开发环境运行(推荐调试) ```powershell $env:JAVA_HOME = "D:\Program Files\Java\jdk-21.0.12+8" .\gradlew.bat runClient ``` ### 方式二:安装到正式客户端 1. 安装 [NeoForge 1.21.1](https://neoforged.net/)。 2. 将 `build/libs/minecraftsim-0.1.0.jar` 复制到 `.minecraft/mods/` 目录。 3. 启动游戏(单人生存/创造均可)。 --- ## 使用方法 ### 游戏内命令 命令根为 `/minecraftsim`(同时提供 `/record` 别名): | 命令 | 说明 | |------|------| | `/minecraftsim record euroc [sample_rate]` | 开始录制 EuRoC 风格数据集 | | `/minecraftsim record ros2 [sample_rate]` | 开始录制并生成 ROS 2 bag | | `/minecraftsim record stop` | 停止录制,打印统计信息与输出 URI | - `[sample_rate]`:**IMU 采样率(Hz)**,默认 `200`,范围 `1 ~ 1000`。图像帧率固定 20 Hz。 - 同一时间只能进行一种录制;在已有录制进行时再次执行 `record` 会在聊天栏发出**黄色警告**。 - 停止后聊天栏输出示例: ``` [MinecraftSim] 录制完成 (euroc) 时长: 12.34 s 轨迹长度: 45.678 m 图像: 246 帧 IMU: 2468 样本 输出: D:\...\.minecraft\minecraftsim\euroc\20260814_123456\mav0 ← 点击可直接打开目录 ``` `ros2` 模式停止时先输出 EuRoC 源路径,转换完成后追加输出 bag 目录 URI。 ### 输出目录结构 输出位于 `<游戏目录>/minecraftsim/`: ``` minecraftsim/ ├── euroc/<会话时间戳>/mav0/... # euroc 模式 └── ros2/<会话时间戳>/ ├── mav0/... # EuRoC 中间数据(保留) └── bag/ # rosbag2 输出(metadata.yaml + *.db3) ``` ### 独立转换工具 转换器可脱离游戏单独运行,将任意 EuRoC 数据集转为 ROS 2 bag: ```powershell java -cp "minecraftsim-0.1.0.jar;sqlite-jdbc-3.x.jar" com.minecraftsim.euroc2bag.EuRoCToBag ``` > 由于 `sqlite-jdbc` 以 `jarJar` 方式内嵌在 Mod jar 内(供 NeoForge 类加载器使用),独立命令行运行时需额外将 `sqlite-jdbc` 放到 `classpath` 上。 --- ## 配置项 编译期常量位于 `src/main/java/com/minecraftsim/Config.java`: | 常量 | 默认值 | 说明 | |------|--------|------| | `GRAVITY` | `32.0` | 重力加速度 `m/s²`(Minecraft 实际物理值,保证 IMU 与轨迹自洽) | | `BLOCK_TO_METER` | `1.0` | 每个方块的米数(世界 → 公制缩放) | | `CAMERA_RATE_HZ` | `20` | 图像采集帧率 | | `DEFAULT_IMU_RATE_HZ` | `200` | IMU 默认采样率 | --- ## 注意事项与已知限制 - 录制仅支持**单人 / 集成客户端**(依赖客户端渲染循环);专用服务器上命令会提示不可用。 - 游戏暂停(`ESC` / 窗口失焦)时录制时钟与采样同步暂停,不会产生空洞数据,也不会截到暂停菜单。 - 建议在**中等分辨率**窗口下录制,以确保 PNG 编码速率能跟上 20 Hz 采集(有界队列会施加背压)。 - 图像为游戏视角直接截取(相机与载体重合 `T_BC = I`),如需标准光轴约定可自行在 VIO 配置中设置外参。 - 内参由 FOV 设置推导,为近似针孔模型;若追求高精度请自行标定。 - 世界系为 Minecraft 原始坐标(Y 向上);数据集内所有量纲保持自洽。