20 KiB
20 KiB
酒店场景建模项目 - 数据格式规范
📋 文档概述
本文档详细定义了酒店场景数字孪生项目中所有数据的存储格式、命名规范和交换标准,确保数据的可复用性、可扩展性和跨平台兼容性。
一、数据组织结构
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 命名规范
场景命名:
格式: {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)
文件格式: 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读取示例:
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
RGB图像:
格式: JPEG(有损压缩)
分辨率: 1920×1080 或更高
色彩空间: sRGB
质量: 95(JPEG质量参数)
命名: {frame_id:07d}.jpg
高质量纹理:
格式: PNG(无损)
分辨率: 4K (3840×2160) 或更高
色彩深度: 8-bit per channel
命名: texture_{id:04d}.png
EXIF元数据(嵌入图像):
{
"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
格式: 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读取示例:
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
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
...
字段说明:
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读取示例:
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
<binary data>
cameras.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
<binary data>
字段说明:
x, y, z: 高斯中心位置
nx, ny, nz: 法线(可选)
f_dc_*: 球谐函数DC分量(RGB)
f_rest_*: 球谐函数高阶分量(45个)
opacity: 不透明度
scale_*: 缩放(3个轴)
rot_*: 旋转四元数(4个分量)
3.4 NeRF格式
transforms.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
{
"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
{
"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
{
"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(文本格式示例)
#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 = </World/Wardrobe>
rel body1 = </World/Wardrobe_Door>
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
{
"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
{
"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_