Nuscenes数据集实战:从数据加载到3D目标检测全流程解析(附Python代码)
·
Nuscenes数据集实战:从数据加载到3D目标检测全流程解析
自动驾驶技术的快速发展离不开高质量数据集的支撑。在众多公开数据集中,Nuscenes以其丰富的传感器配置和精细的标注质量脱颖而出,成为3D目标检测领域的重要基准。本文将带您深入探索Nuscenes数据集的核心结构,并通过Python代码实战演示从数据加载到3D检测模型训练的全流程。
1. Nuscenes数据集概览与准备工作
Nuscenes数据集由1000个精心采集的驾驶场景组成,每个场景时长20秒,包含来自6个摄像头、1个激光雷达和5个毫米波雷达的多模态数据。与KITTI等传统数据集相比,Nuscenes最显著的特点是采用了基于token的关联索引系统,这使得数据组织更加灵活,但也增加了初学者的理解难度。
1.1 数据集获取与安装
首先需要从Nuscenes官网下载数据集,推荐从v1.0-mini版本开始入手:
# 安装官方开发工具包
pip install nuscenes-devkit
数据集目录结构通常如下:
v1.0-mini/
├── samples/ # 关键帧传感器数据
├── sweeps/ # 非关键帧传感器数据
├── maps/ # 高精地图
└── v1.0-mini/ # 元数据JSON文件
1.2 核心数据结构解析
Nuscenes采用七层数据关联结构:
| 层级 | 描述 | 关键字段 |
|---|---|---|
| Scene | 20秒连续驾驶场景 | first_sample_token, last_sample_token |
| Sample | 2Hz标注关键帧 | data(传感器token), anns(标注token) |
| SampleData | 传感器原始数据 | calibrated_sensor_token, ego_pose_token |
| SampleAnnotation | 3D标注框 | instance_token, category_name |
| Instance | 物体实例轨迹 | first_annotation_token, last_annotation_token |
| Category | 物体类别 | name (如vehicle.car) |
| Attribute | 物体状态 | name (如vehicle.moving) |
2. 数据加载与可视化实战
2.1 初始化数据集对象
from nuscenes import NuScenes
# 初始化数据集接口
nusc = NuScenes(version='v1.0-mini',
dataroot='/path/to/v1.0-mini',
verbose=True)
初始化后会加载所有JSON元数据,典型输出:
====== Loading NuScenes tables for version v1.0-mini...
23 category, 8 attribute, 4 visibility, 911 instance,
12 sensor, 120 calibrated_sensor, 31206 ego_pose,
8 log, 10 scene, 404 sample, 31206 sample_data,
18538 sample_annotation, 4 map
Done loading in 0.356 seconds.
2.2 场景与样本遍历
获取第一个场景及其样本序列:
scene = nusc.scene[0]
print(f"场景描述: {scene['description']}")
# 获取该场景的第一个样本
first_sample = nusc.get('sample', scene['first_sample_token'])
# 遍历场景中的所有样本
current_sample = first_sample
while current_sample['next'] != '':
print(f"样本时间戳: {current_sample['timestamp']}")
current_sample = nusc.get('sample', current_sample['next'])
2.3 点云数据加载与可视化
加载激光雷达数据并可视化:
# 获取激光雷达数据
lidar_data = nusc.get('sample_data', first_sample['data']['LIDAR_TOP'])
print(f"点云文件路径: {lidar_data['filename']}")
# 可视化点云和标注
nusc.render_sample_data(lidar_data['token'])
关键点云处理代码:
from pyquaternion import Quaternion
def load_point_cloud(file_path):
"""加载二进制点云数据"""
points = np.fromfile(file_path, dtype=np.float32)
return points.reshape(-1, 5) # x,y,z,intensity,ring_index
def transform_to_ego_frame(points, calibrated_sensor):
"""将点云转换到自车坐标系"""
# 获取标定参数
rotation = Quaternion(calibrated_sensor['rotation'])
translation = np.array(calibrated_sensor['translation'])
# 应用变换
points[:, :3] = rotation.rotation_matrix @ points[:, :3].T + translation.reshape(3,1)
return points
3. 3D标注数据处理技巧
3.1 标注框解析与转换
Nuscenes中的标注框使用全局坐标系,需要转换到传感器坐标系:
def box_to_sensor_frame(box, calibrated_sensor):
"""将全局标注框转换到传感器坐标系"""
# 获取传感器标定
cs_record = nusc.get('calibrated_sensor',
calibrated_sensor['token'])
# 全局到自车的变换
global_to_ego = transform_matrix(
cs_record['translation'],
Quaternion(cs_record['rotation']),
inverse=True)
# 应用变换
box.translate(-np.array(cs_record['translation']))
box.rotate(Quaternion(cs_record['rotation']).inverse)
return box
3.2 标注统计分析
对数据集进行统计分析有助于理解数据分布:
from collections import defaultdict
category_stats = defaultdict(int)
for ann in nusc.sample_annotation:
category_stats[ann['category_name']] += 1
# 输出类别分布
print("标注类别统计:")
for cat, count in sorted(category_stats.items(), key=lambda x: -x[1]):
print(f"{cat:30s}: {count:4d}")
典型输出结果:
vehicle.car : 8432
human.pedestrian.adult : 4215
vehicle.truck : 1567
human.pedestrian.child : 892
4. 构建3D目标检测数据管道
4.1 自定义数据加载器
from torch.utils.data import Dataset
class NuScenesDataset(Dataset):
def __init__(self, nusc, split='train'):
self.nusc = nusc
self.samples = self._prepare_samples(split)
def _prepare_samples(self, split):
"""筛选指定split的样本"""
samples = []
for scene in nusc.scene:
if scene['name'] in split_scenes[split]:
sample_token = scene['first_sample_token']
while sample_token != '':
sample = nusc.get('sample', sample_token)
samples.append(sample)
sample_token = sample['next']
return samples
def __getitem__(self, idx):
sample = self.samples[idx]
# 加载点云
lidar_data = nusc.get('sample_data',
sample['data']['LIDAR_TOP'])
points = load_point_cloud(
os.path.join(nusc.dataroot, lidar_data['filename']))
# 获取标注框
boxes = []
for ann_token in sample['anns']:
ann = nusc.get('sample_annotation', ann_token)
box = nusc.get_box(ann_token)
boxes.append(box)
return points, boxes
4.2 数据增强策略
为提高模型鲁棒性,需要实施数据增强:
def apply_augmentation(points, boxes):
"""应用随机数据增强"""
# 全局旋转
angle = np.random.uniform(-np.pi/4, np.pi/4)
rot_mat = np.array([[np.cos(angle), -np.sin(angle), 0],
[np.sin(angle), np.cos(angle), 0],
[0, 0, 1]])
points[:, :3] = points[:, :3] @ rot_mat.T
# 全局缩放
scale = np.random.uniform(0.9, 1.1)
points[:, :3] *= scale
# 对每个框应用相同变换
for box in boxes:
box.rotate(Quaternion(axis=[0,0,1], angle=angle))
box.scale(scale)
return points, boxes
4.3 点云体素化处理
大多数现代3D检测器采用体素化输入:
from spconv.utils import VoxelGenerator
voxel_generator = VoxelGenerator(
voxel_size=[0.1, 0.1, 0.1],
point_cloud_range=[-50, -50, -3, 50, 50, 3],
max_num_points=10,
max_voxels=20000)
def points_to_voxels(points):
"""将点云转换为体素表示"""
voxels, coords, num_points = voxel_generator.generate(points)
# 归一化点特征
voxel_features = np.concatenate([
points[:, :3] - points[:, :3].mean(0),
points[:, 3:]], axis=1)
return {
'voxels': voxels,
'coordinates': coords,
'num_points': num_points
}
5. 3D目标检测模型实战
5.1 基于PointPillars的检测实现
PointPillars是兼顾精度和效率的经典3D检测架构:
import torch
import torch.nn as nn
from spconv import SparseConvTensor
class PointPillars(nn.Module):
def __init__(self, num_classes=10):
super().__init__()
# Pillar特征网络
self.pfn = nn.Sequential(
nn.Linear(9, 64),
nn.BatchNorm1d(64),
nn.ReLU(),
nn.Linear(64, 64),
nn.BatchNorm1d(64),
nn.ReLU()
)
# 2D CNN骨干网络
self.backbone = nn.Sequential(
nn.Conv2d(64, 64, 3, stride=2, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
# 更多卷积层...
)
# 检测头
self.cls_head = nn.Conv2d(256, num_classes, 1)
self.reg_head = nn.Conv2d(256, 7, 1) # dx,dy,dz,w,l,h,θ
def forward(self, voxel_features, coords, batch_size):
# 1. Pillar特征提取
pillar_features = self.pfn(voxel_features)
# 2. 创建伪图像
canvas = torch.zeros((batch_size, 64, 1200, 1200),
device=voxel_features.device)
canvas[coords[:,0], :, coords[:,2], coords[:,1]] = pillar_features
# 3. 2D CNN处理
features = self.backbone(canvas)
# 4. 预测输出
cls_pred = self.cls_head(features)
reg_pred = self.reg_head(features)
return cls_pred, reg_pred
5.2 损失函数设计
3D检测需要同时优化分类和回归任务:
def calculate_loss(cls_pred, reg_pred, gt_boxes, gt_labels):
# 分类损失 (Focal Loss)
cls_loss = FocalLoss()(cls_pred, gt_labels)
# 回归损失 (Smooth L1)
pos_mask = gt_labels > 0 # 正样本掩码
reg_loss = SmoothL1Loss()(reg_pred[pos_mask],
gt_boxes[pos_mask])
# 方向分类损失
dir_loss = CrossEntropyLoss()(dir_pred[pos_mask],
gt_directions[pos_mask])
return cls_loss + reg_loss + dir_loss
5.3 模型训练流程
完整的训练循环实现:
def train(model, dataloader, optimizer, epochs=50):
model.train()
for epoch in range(epochs):
for batch in dataloader:
points, gt_boxes, gt_labels = batch
# 体素化
voxel_dict = points_to_voxels(points)
# 前向传播
cls_pred, reg_pred = model(
voxel_dict['voxels'],
voxel_dict['coordinates'],
batch_size=len(points))
# 计算损失
loss = calculate_loss(cls_pred, reg_pred,
gt_boxes, gt_labels)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"Epoch {epoch} Loss: {loss.item():.4f}")
6. 检测结果后处理与评估
6.1 非极大值抑制(NMS)
from nms import rotate_nms
def apply_nms(boxes, scores, iou_threshold=0.3):
"""应用旋转框NMS"""
keep_indices = rotate_nms(
boxes.cpu().numpy(), # [N,7] (x,y,z,w,l,h,θ)
scores.cpu().numpy(),
iou_threshold)
return boxes[keep_indices], scores[keep_indices]
6.2 官方评估指标实现
Nuscenes使用独特的评估指标:
from nuscenes.eval.detection.evaluate import NuScenesEval
# 初始化评估器
nusc_eval = NuScenesEval(
nusc,
config_path='configs/detection_cvpr_2019.json',
result_path='./results.json',
eval_set='mini_val',
output_dir='./eval_results')
# 运行评估
metrics = nusc_eval.main()
print("mAP: %.4f" % metrics['mean_ap'])
关键评估指标说明:
| 指标 | 说明 | 权重 |
|---|---|---|
| mAP | 平均精度(0.5-4.0m阈值) | 40% |
| NDS | 综合检测分数 | 60% |
| ATE | 中心误差 | 包含在NDS中 |
| ASE | 尺寸误差 | 包含在NDS中 |
| AOE | 方向误差 | 包含在NDS中 |
6.3 结果可视化与分析
def visualize_detections(sample_token, pred_boxes):
"""可视化预测结果"""
sample = nusc.get('sample', sample_token)
lidar_data = nusc.get('sample_data',
sample['data']['LIDAR_TOP'])
# 渲染点云和预测框
nusc.render_sample_data(lidar_data['token'],
override_boxes=pred_boxes)
典型问题分析技巧:
- 检查远处小物体检测效果
- 观察遮挡物体的召回率
- 分析方向预测准确性
- 检查尺寸预测的系统偏差
更多推荐



所有评论(0)