4. 基于深度学习的三维点云目标检测(PointPillars)

任务目标

本案例使用PointPillars模型对KITTI格式的三维点云进行目标检测,输出目标的3D边界框(中心坐标、尺寸、朝向角)与类别标签。实现范围:加载预训练模型权重,对单帧点云执行前向推理,解析并输出检测结果。

环境与依赖

  • Ubuntu 20.04 / 22.04,Python 3.8+
  • PyTorch ≥ 1.10,CUDA ≥ 11.3(CPU亦可运行,速度较慢)
  • OpenPCDet(PointPillars官方实现库)
  • 依赖安装:pip install openpcdet torch numpy open3d

实现代码

以下代码完成从加载模型到输出检测结果的完整闭环。模型权重需预先下载(见下文说明)。

import torch
import numpy as np
from openpcdet.models import build_network
from openpcdet.config import cfg, cfg_from_yaml_file
from openpcdet.datasets import DatasetTemplate

class PointCloudProcessor(DatasetTemplate):
    def __init__(self, cfg):
        super().__init__(dataset_cfg=cfg.DATA_CONFIG, class_names=cfg.CLASS_NAMES, training=False)
        self.cfg = cfg

    def prepare_input(self, points):
        """将原始点云转换为模型输入格式"""
        input_dict = {
            'points': torch.from_numpy(points).float().cuda(),
            'frame_id': 'demo'
        }
        data_dict = self.prepare_data(data_dict=input_dict)
        return data_dict

def load_model(config_path, ckpt_path, class_names):
    """加载PointPillars模型与预训练权重"""
    cfg_from_yaml_file(config_path, cfg)
    cfg.CLASS_NAMES = class_names
    cfg.MODEL.POST_PROCESSING.RECALL_MODE = 'specified'
    model = build_network(model_cfg=cfg.MODEL, num_class=len(class_names), dataset=cfg.DATA_CONFIG)
    checkpoint = torch.load(ckpt_path, map_location='cuda:0')
    model.load_state_dict(checkpoint['model_state_dict'])
    model.cuda().eval()
    return model, cfg

def detect_objects(model, processor, points, threshold=0.3):
    """执行推理并返回检测结果"""
    with torch.no_grad():
        data_dict = processor.prepare_input(points)
        pred_dicts, _ = model.forward(data_dict)
    # 解析预测结果
    boxes_3d = pred_dicts[0]['pred_boxes'].cpu().numpy()   # [N, 7] (x,y,z,dx,dy,dz,heading)
    scores = pred_dicts[0]['pred_scores'].cpu().numpy()     # [N]
    labels = pred_dicts[0]['pred_labels'].cpu().numpy()     # [N]
    # 置信度过滤
    mask = scores >= threshold
    return boxes_3d[mask], scores[mask], labels[mask]

if __name__ == '__main__':
    # 配置路径(按实际路径修改)
    CONFIG_PATH = 'tools/cfgs/kitti_models/pointpillar.yaml'
    CKPT_PATH = 'pointpillar_7728.pth'  # 预训练权重
    CLASS_NAMES = ['Car', 'Pedestrian', 'Cyclist']
    
    # 生成示例点云(模拟KITTI点云,实际使用时替换为真实数据)
    np.random.seed(42)
    demo_points = np.random.rand(5000, 4).astype(np.float32)
    demo_points[:, 0] *= 40  # x范围0-40m
    demo_points[:, 1] -= 20  # y范围-20~20m
    demo_points[:, 2] *= 2   # z范围0-2m
    
    # 加载模型与数据处理器
    model, cfg = load_model(CONFIG_PATH, CKPT_PATH, CLASS_NAMES)
    processor = PointCloudProcessor(cfg)
    
    # 执行检测
    boxes, scores, labels = detect_objects(model, processor, demo_points)
    
    # 输出结果(预期输出:空检测,因随机点云无真实目标)
    print(f'检测到 {len(boxes)} 个目标')
    for i in range(len(boxes)):
        print(f'类别: {CLASS_NAMES[labels[i]-1]}, 置信度: {scores[i]:.3f}')
        print(f'3D框: cx={boxes[i][0]:.2f}, cy={boxes[i][1]:.2f}, cz={boxes[i][2]:.2f}, '
              f'l={boxes[i][3]:.2f}, w={boxes[i][4]:.2f}, h={boxes[i][5]:.2f}, '
              f'yaw={boxes[i][6]:.3f} rad')

运行与验证

  1. 准备权重:从OpenPCDet官方模型库下载PointPillars KITTI预训练权重(文件名pointpillar_7728.pth),放入当前目录。
  2. 运行:python pointpillars_demo.py
  3. 验证方法:使用真实KITTI点云数据(替换demo_points为实际点云数组,shape为[N,4],列为x,y,z,intensity),观察输出3D框是否与点云中的车辆/行人位置吻合。判定通过条件:程序无报错运行完成,输出检测目标数量与真实场景目视相符(如KITTI样本通常检测到5~15个目标),且3D框中心坐标位于点云有效范围内。

预期输出说明:上述代码使用随机点云作为输入,因无真实目标,预期输出为检测到 0 个目标。若替换为KITTI真实数据(如编号000008.bin),将输出对应检测框信息。

关键说明

  • PointPillars将点云划分为垂直柱体(Pillar),通过特征编码网络提取特征,再经2D卷积骨干网络与检测头输出3D框。本案例聚焦推理闭环,训练过程不在范围内。
  • 若需可视化检测结果,可使用Open3D:o3d.visualization.draw_geometries([pcd, boxes]),其中boxes由boxes_3d转换得到。
  • 模型对输入点云范围敏感,KITTI默认检测范围x∈[0,69.12],y∈[-39.68,39.68],z∈[-3,1]。超出范围的点会被预处理过滤。