--- title: "酒店场景建模项目 - 数据格式规范" date: 2026-05-20 draft: false tags: ["iPhone", "3D 重建", "RoomPlan", "SLAM", "IMU", "点云"] categories: ["worldmodel"] --- # 酒店场景建模项目 - 数据格式规范 ## 📋 文档概述 本文档详细定义了酒店场景数字孪生项目中所有数据的存储格式、命名规范和交换标准,确保数据的可复用性、可扩展性和跨平台兼容性。 --- ## 一、数据组织结构 ### 1.1 目录树结构 ``` hotel_dataset/ ├── metadata.json # 全局元数据 ├── calibration/ # 传感器标定 │ ├── camera_intrinsics.yaml │ ├── lidar_camera_extrinsics.yaml │ ├── imu_camera_extrinsics.yaml │ └── calibration_report.pdf ├── scenes/ # 场景数据 │ ├── lobby_01/ # 大堂场景1 │ │ ├── scene_info.json # 场景元数据 │ │ ├── raw/ # 原始数据 │ │ │ ├── lidar/ # LiDAR点云 │ │ │ │ ├── 0000000.pcd │ │ │ │ ├── 0000001.pcd │ │ │ │ └── ... │ │ │ ├── rgb/ # RGB图像 │ │ │ │ ├── 0000000.jpg │ │ │ │ ├── 0000001.jpg │ │ │ │ └── ... │ │ │ ├── depth/ # 深度图 │ │ │ │ ├── 0000000.png # 16-bit PNG │ │ │ │ ├── 0000001.png │ │ │ │ └── ... │ │ │ ├── imu/ # IMU数据 │ │ │ │ └── imu_data.csv │ │ │ ├── polarized/ # 偏振图像(卫生间) │ │ │ │ ├── 0deg/ │ │ │ │ ├── 45deg/ │ │ │ │ ├── 90deg/ │ │ │ │ └── 135deg/ │ │ │ └── timestamps.txt # 时间戳对齐 │ │ ├── processed/ # 处理后数据 │ │ │ ├── slam/ # SLAM结果 │ │ │ │ ├── trajectory.txt # 相机轨迹 │ │ │ │ ├── sparse_map.ply # 稀疏点云 │ │ │ │ ├── dense_map.ply # 稠密点云 │ │ │ │ └── loop_closures.json # 回环信息 │ │ │ ├── colmap/ # COLMAP输出 │ │ │ │ ├── cameras.txt │ │ │ │ ├── images.txt │ │ │ │ ├── points3D.txt │ │ │ │ └── sparse/ │ │ │ ├── 3dgs/ # 3D Gaussian Splatting │ │ │ │ ├── point_cloud.ply # 初始点云 │ │ │ │ ├── cameras.json # 相机参数 │ │ │ │ ├── cfg_args # 训练配置 │ │ │ │ ├── input.ply # 优化后高斯 │ │ │ │ └── iteration_30000/ # 检查点 │ │ │ ├── nerf/ # NeRF/Ref-NeRF │ │ │ │ ├── transforms.json # NeRF格式位姿 │ │ │ │ ├── model.pth # 模型权重 │ │ │ │ └── config.yaml │ │ │ ├── mesh/ # 网格模型 │ │ │ │ ├── scene.obj # 主网格 │ │ │ │ ├── scene.mtl # 材质文件 │ │ │ │ ├── textures/ # 纹理贴图 │ │ │ │ │ ├── diffuse.png │ │ │ │ │ ├── normal.png │ │ │ │ │ └── roughness.png │ │ │ │ └── scene_watertight.obj # 水密网格 │ │ │ ├── semantic/ # 语义信息 │ │ │ │ ├── detections/ # 2D检测 │ │ │ │ │ ├── yolo_results.json │ │ │ │ │ └── sam_masks/ │ │ │ │ ├── instances_3d.json # 3D实例 │ │ │ │ ├── scene_graph.json # 场景图 │ │ │ │ ├── clip_features.npy # CLIP特征 │ │ │ │ └── semantic_map.ply # 语义点云 │ │ │ └── physics/ # 物理属性 │ │ │ ├── scene.usd # USD场景 │ │ │ ├── collision_meshes/ # 碰撞网格 │ │ │ ├── articulation.json # 铰接信息 │ │ │ └── material_props.json # 材质物理属性 │ │ └── renders/ # 渲染结果 │ │ ├── novel_views/ # 新视角 │ │ ├── depth_maps/ # 深度图 │ │ └── semantic_maps/ # 语义图 │ ├── room_301/ # 客房场景 │ │ └── ...(结构同上) │ └── bathroom_301/ # 卫生间场景 │ └── ...(结构同上) ├── annotations/ # 人工标注(可选) │ ├── object_labels.json │ ├── spatial_relations.json │ └── physics_properties.json └── benchmarks/ # 评测数据 ├── test_views/ # 测试视角 ├── ground_truth/ # 真值数据 └── metrics/ # 评测结果 ``` ### 1.2 命名规范 ```yaml 场景命名: 格式: {scene_type}_{id} 示例: lobby_01, room_301, bathroom_301 scene_type: - lobby: 大堂 - corridor: 走廊 - room: 客房 - bathroom: 卫生间 - restaurant: 餐厅 - gym: 健身房 文件命名: 时序数据: {frame_id:07d}.{ext} 示例: 0000000.jpg, 0000001.pcd 处理结果: {descriptor}_{version}.{ext} 示例: dense_map_v2.ply, scene_graph_final.json 版本控制: 格式: v{major}.{minor}.{patch} 示例: v1.0.0, v1.2.3 ``` --- ## 二、原始数据格式 ### 2.1 点云数据(LiDAR) #### 格式:PCD(Point Cloud Data) ```yaml 文件格式: ASCII 或 Binary PCD 编码: UTF-8 字段: - x, y, z: 坐标(float32, 单位:米) - intensity: 反射强度(uint8, 0-255) - timestamp: 时间戳(float64, Unix时间) - ring: 激光环ID(uint16, 可选) 示例文件头: VERSION 0.7 FIELDS x y z intensity timestamp SIZE 4 4 4 1 8 TYPE F F F U F COUNT 1 1 1 1 1 WIDTH 65536 HEIGHT 1 VIEWPOINT 0 0 0 1 0 0 0 POINTS 65536 DATA binary ``` **Python读取示例**: ```python import open3d as o3d import numpy as np def load_pcd(filepath): """加载PCD点云""" pcd = o3d.io.read_point_cloud(filepath) # 提取数据 points = np.asarray(pcd.points) # (N, 3) # 如果有颜色 if pcd.has_colors(): colors = np.asarray(pcd.colors) # (N, 3) # 如果有法线 if pcd.has_normals(): normals = np.asarray(pcd.normals) # (N, 3) return pcd, points # 保存PCD def save_pcd(filepath, points, colors=None, normals=None): """保存PCD点云""" pcd = o3d.geometry.PointCloud() pcd.points = o3d.utility.Vector3dVector(points) if colors is not None: pcd.colors = o3d.utility.Vector3dVector(colors) if normals is not None: pcd.normals = o3d.utility.Vector3dVector(normals) o3d.io.write_point_cloud(filepath, pcd, write_ascii=False) ``` ### 2.2 RGB图像 #### 格式:JPEG / PNG ```yaml RGB图像: 格式: JPEG(有损压缩) 分辨率: 1920×1080 或更高 色彩空间: sRGB 质量: 95(JPEG质量参数) 命名: {frame_id:07d}.jpg 高质量纹理: 格式: PNG(无损) 分辨率: 4K (3840×2160) 或更高 色彩深度: 8-bit per channel 命名: texture_{id:04d}.png ``` **EXIF元数据**(嵌入图像): ```json { "DateTime": "2026:05:16 10:30:45", "Make": "Apple", "Model": "iPhone 15 Pro Max", "FocalLength": 24.0, "FNumber": 1.78, "ISO": 100, "ExposureTime": "1/120", "GPSLatitude": 31.2304, "GPSLongitude": 121.4737, "GPSAltitude": 10.5 } ``` ### 2.3 深度图 #### 格式:16-bit PNG ```yaml 格式: PNG 位深度: 16-bit unsigned integer 编码: 深度值(毫米) 范围: 0-65535 mm (0-65.535米) 无效值: 0(表示无深度数据) 深度值计算: depth_meters = pixel_value / 1000.0 示例: pixel_value = 1500 → depth = 1.5米 pixel_value = 0 → 无效深度 ``` **Python读取示例**: ```python import cv2 import numpy as np def load_depth(filepath): """加载16-bit深度图""" depth_mm = cv2.imread(filepath, cv2.IMREAD_ANYDEPTH) depth_m = depth_mm.astype(np.float32) / 1000.0 # 标记无效深度 depth_m[depth_mm == 0] = np.nan return depth_m def save_depth(filepath, depth_m): """保存深度图""" depth_mm = (depth_m * 1000.0).astype(np.uint16) depth_mm[np.isnan(depth_m)] = 0 cv2.imwrite(filepath, depth_mm) ``` ### 2.4 IMU数据 #### 格式:CSV ```csv timestamp,accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z,mag_x,mag_y,mag_z 1715875845.123456,-0.05,0.02,9.81,0.001,-0.002,0.000,25.3,10.2,-42.1 1715875845.133456,-0.04,0.03,9.80,0.002,-0.001,0.001,25.4,10.1,-42.0 ... ``` **字段说明**: ```yaml timestamp: Unix时间戳(秒,float64) accel_x/y/z: 加速度(m/s², float32) gyro_x/y/z: 角速度(rad/s, float32) mag_x/y/z: 磁场强度(μT, float32, 可选) 坐标系: 右手坐标系 X: 前 Y: 左 Z: 上 ``` ### 2.5 时间戳对齐文件 #### 格式:timestamps.txt ``` # Timestamp alignment file # Format: frame_id timestamp_unix sensor_type # timestamp_unix: seconds since epoch (float64) 0000000 1715875845.123456 lidar 0000000 1715875845.125678 rgb 0000000 1715875845.126789 depth 0000001 1715875845.223456 lidar 0000001 1715875845.225678 rgb 0000001 1715875845.226789 depth ... ``` --- ## 三、处理后数据格式 ### 3.1 SLAM轨迹 #### 格式:TUM格式(trajectory.txt) ``` # TUM RGB-D SLAM trajectory format # timestamp tx ty tz qx qy qz qw # timestamp: Unix time (float64) # tx ty tz: translation (meters) # qx qy qz qw: rotation quaternion 1715875845.123456 0.000 0.000 0.000 0.000 0.000 0.000 1.000 1715875845.223456 0.050 0.001 0.002 0.001 0.002 0.003 0.999 1715875845.323456 0.102 0.003 0.004 0.002 0.004 0.006 0.998 ... ``` **Python读取示例**: ```python import numpy as np from scipy.spatial.transform import Rotation def load_trajectory(filepath): """加载TUM格式轨迹""" data = np.loadtxt(filepath) timestamps = data[:, 0] positions = data[:, 1:4] # (N, 3) quaternions = data[:, 4:8] # (N, 4) [qx, qy, qz, qw] # 转换为旋转矩阵 rotations = Rotation.from_quat(quaternions).as_matrix() # (N, 3, 3) # 构建4x4变换矩阵 poses = np.zeros((len(data), 4, 4)) poses[:, :3, :3] = rotations poses[:, :3, 3] = positions poses[:, 3, 3] = 1.0 return timestamps, poses def save_trajectory(filepath, timestamps, poses): """保存TUM格式轨迹""" N = len(timestamps) data = np.zeros((N, 8)) data[:, 0] = timestamps data[:, 1:4] = poses[:, :3, 3] # 位置 # 旋转矩阵转四元数 rotations = Rotation.from_matrix(poses[:, :3, :3]) data[:, 4:8] = rotations.as_quat() # [qx, qy, qz, qw] np.savetxt(filepath, data, fmt='%.6f') ``` ### 3.2 COLMAP格式 #### cameras.txt ``` # Camera list with one line of data per camera: # CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[] # Number of cameras: 1 1 PINHOLE 1920 1080 1066.778 1067.487 960.000 540.000 ``` #### images.txt ``` # Image list with two lines of data per image: # IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME # POINTS2D[] as (X, Y, POINT3D_ID) # Number of images: 500 1 0.999 0.001 0.002 0.003 0.000 0.000 0.000 1 0000000.jpg 512.5 384.2 1234 678.9 456.1 5678 ... 2 0.998 0.002 0.004 0.006 0.050 0.001 0.002 1 0000001.jpg 510.3 382.7 1235 680.1 458.3 5679 ... ... ``` #### points3D.txt ``` # 3D point list with one line of data per point: # POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX) # Number of points: 123456 1 0.123 0.456 0.789 255 128 64 0.5 1 512 2 510 ... 2 0.234 0.567 0.890 200 150 100 0.3 1 678 3 680 ... ... ``` ### 3.3 3D Gaussian Splatting格式 #### point_cloud.ply(初始点云) ``` ply format binary_little_endian 1.0 element vertex 123456 property float x property float y property float z property uchar red property uchar green property uchar blue property float nx property float ny property float nz end_header ``` #### cameras.json ```json [ { "id": 0, "img_name": "0000000", "width": 1920, "height": 1080, "position": [0.0, 0.0, 0.0], "rotation": [ [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0] ], "fy": 1067.487, "fx": 1066.778 }, ... ] ``` #### input.ply(优化后的高斯) ``` ply format binary_little_endian 1.0 element vertex 500000 property float x property float y property float z property float nx property float ny property float nz property float f_dc_0 property float f_dc_1 property float f_dc_2 property float f_rest_0 ... property float f_rest_44 property float opacity property float scale_0 property float scale_1 property float scale_2 property float rot_0 property float rot_1 property float rot_2 property float rot_3 end_header ``` **字段说明**: ```yaml x, y, z: 高斯中心位置 nx, ny, nz: 法线(可选) f_dc_*: 球谐函数DC分量(RGB) f_rest_*: 球谐函数高阶分量(45个) opacity: 不透明度 scale_*: 缩放(3个轴) rot_*: 旋转四元数(4个分量) ``` ### 3.4 NeRF格式 #### transforms.json ```json { "camera_angle_x": 0.6911112070083618, "frames": [ { "file_path": "./images/0000000", "rotation": 0.012566370614359171, "transform_matrix": [ [0.999, -0.001, 0.002, 0.000], [0.001, 0.999, -0.003, 0.000], [-0.002, 0.003, 0.999, 0.000], [0.0, 0.0, 0.0, 1.0] ] }, ... ] } ``` ### 3.5 网格模型(Mesh) #### OBJ格式 ``` # Wavefront OBJ file # Vertices: 123456 # Faces: 234567 mtllib scene.mtl v 0.123 0.456 0.789 v 0.234 0.567 0.890 ... vn 0.577 0.577 0.577 vn 0.707 0.000 0.707 ... vt 0.500 0.500 vt 0.600 0.400 ... usemtl material_0 f 1/1/1 2/2/2 3/3/3 f 4/4/4 5/5/5 6/6/6 ... ``` #### MTL格式(材质) ``` # Material file newmtl material_0 Ka 0.2 0.2 0.2 Kd 0.8 0.8 0.8 Ks 0.5 0.5 0.5 Ns 96.078431 map_Kd textures/diffuse_0.png map_Bump textures/normal_0.png map_Ks textures/roughness_0.png ``` --- ## 四、语义数据格式 ### 4.1 2D检测结果(YOLO) #### yolo_results.json ```json { "version": "1.0", "model": "yolov9-world", "images": [ { "image_id": "0000000", "image_path": "rgb/0000000.jpg", "width": 1920, "height": 1080, "detections": [ { "detection_id": 0, "label": "bed", "confidence": 0.95, "bbox": [100, 200, 800, 600], "bbox_format": "xyxy", "mask_rle": "...", "clip_feature": [0.123, 0.456, ...] }, { "detection_id": 1, "label": "desk", "confidence": 0.89, "bbox": [1000, 300, 1500, 900], "bbox_format": "xyxy" } ] } ] } ``` ### 4.2 3D实例 #### instances_3d.json ```json { "version": "1.0", "scene_id": "room_301", "coordinate_system": "right_hand_z_up", "unit": "meter", "instances": [ { "instance_id": "bed_001", "label": "bed", "category": "furniture", "confidence": 0.95, "centroid": [1.5, 2.0, 0.5], "bbox_min": [0.5, 1.0, 0.0], "bbox_max": [2.5, 3.0, 1.0], "oriented_bbox": { "center": [1.5, 2.0, 0.5], "extent": [2.0, 2.0, 1.0], "rotation": [0.0, 0.0, 0.0, 1.0] }, "volume": 4.0, "mesh_path": "mesh/bed_001.obj", "point_indices": [1234, 5678, ...], "semantic_feature": [0.123, 0.456, ...], "attributes": { "material": "fabric", "color": "white", "state": "made", "affordance": ["sittable", "lyable"] } } ] } ``` ### 4.3 场景图 #### scene_graph.json ```json { "version": "1.0", "scene_id": "room_301", "metadata": { "room_type": "hotel_room", "floor_area": 25.5, "ceiling_height": 2.8, "capture_date": "2026-05-16" }, "nodes": [ { "node_id": "bed_001", "type": "object", "label": "bed", "instance_ref": "bed_001", "properties": { "size": "queen", "material": "fabric", "color": "white" } }, { "node_id": "nightstand_001", "type": "object", "label": "nightstand", "instance_ref": "nightstand_001" }, { "node_id": "lamp_001", "type": "object", "label": "lamp", "instance_ref": "lamp_001" } ], "edges": [ { "edge_id": 0, "source": "lamp_001", "target": "nightstand_001", "relation": "supported_by", "confidence": 0.98, "properties": { "contact_area": 0.05, "stability": "stable" } }, { "edge_id": 1, "source": "nightstand_001", "target": "bed_001", "relation": "next_to", "confidence": 0.95, "properties": { "distance": 0.1, "side": "left" } } ] } ``` --- ## 五、物理数据格式 ### 5.1 USD场景(Universal Scene Description) #### scene.usd(文本格式示例) ```python #usda 1.0 ( defaultPrim = "World" metersPerUnit = 1 upAxis = "Z" ) def Xform "World" { def Mesh "Bed" { float3[] extent = [(-1, -1, 0), (1, 1, 1)] int[] faceVertexCounts = [4, 4, 4, 4, 4, 4] int[] faceVertexIndices = [0, 1, 3, 2, ...] point3f[] points = [(0.5, 1.0, 0.0), ...] # 物理属性 def PhysicsRigidBodyAPI { bool kinematicEnabled = true } def PhysicsMassAPI { float mass = 50.0 } def PhysicsCollisionAPI { } def PhysicsMaterialAPI { float staticFriction = 0.5 float dynamicFriction = 0.4 float restitution = 0.1 } } def Mesh "Wardrobe_Door" { # 铰接物体 def PhysicsRevoluteJoint "Hinge" { rel body0 = rel body1 = point3f localPos0 = (0, 0, 0) point3f localPos1 = (0.5, 0, 0) float3 axis = (0, 0, 1) float lowerLimit = 0.0 float upperLimit = 120.0 } } } ``` ### 5.2 铰接信息 #### articulation.json ```json { "version": "1.0", "scene_id": "room_301", "articulated_objects": [ { "object_id": "wardrobe_door_001", "base_link": "wardrobe_001", "joint_type": "revolute", "joint_axis": [0, 0, 1], "joint_origin": [1.0, 2.0, 1.0], "joint_limits": { "lower": 0.0, "upper": 2.094, "effort": 10.0, "velocity": 1.0 }, "damping": 0.5, "friction": 0.1 }, { "object_id": "drawer_001", "base_link": "desk_001", "joint_type": "prismatic", "joint_axis": [1, 0, 0], "joint_origin": [1.5, 1.0, 0.7], "joint_limits": { "lower": 0.0, "upper": 0.4, "effort": 5.0, "velocity": 0.5 } } ] } ``` ### 5.3 材质物理属性 #### material_props.json ```json { "version": "1.0", "materials": [ { "material_id": "wood_oak", "density": 600.0, "static_friction": 0.5, "dynamic_friction": 0.4, "restitution": 0.3, "young_modulus": 11000000000.0, "poisson_ratio": 0.3, "damping": 0.1 }, { "material_id": "fabric_cotton", "density": 200.0, "static_friction": 0.7, "dynamic_