CUDA-V2XFusion 构建与运行记录
CUDA-V2XFusion 构建与运行记录
1. 环境信息
| 项目 | 配置 |
|---|---|
| 操作系统 | Ubuntu 22.04, Linux 6.8.0-111-generic |
| GPU | 4x NVIDIA GeForce RTX 4090 (24GB) |
| CUDA 驱动 | 550.163.01, CUDA 12.4 |
| CUDA Toolkit | 12.1 (/usr/local/cuda-12.1) |
| GCC | 11.4.0 |
2. Conda 环境搭建
2.1 创建环境
conda create -n v2xfusion python=3.10 -y
conda activate v2xfusion
2.2 安装 PyTorch
pip install torch==2.1.2 torchvision==0.16.2 --index-url https://download.pytorch.org/whl/cu121
2.3 安装 mmcv-full
pip install "mmcv-full==1.7.2" -f "https://download.openmmlab.com/mmcv/dist/cu121/torch2.1/index.html" --no-cache-dir
2.4 安装 mmdet 和 torchpack
pip install mmdet==2.28.2 torchpack
2.5 安装其他依赖
pip install "numpy<2"
pip install onnx python-lzf
pip install pyquaternion nuscenes-devkit
pip install numba scikit-image
pip install ninja
# 安装 pytorch-quantization (从 NVIDIA 源)
pip install pytorch-quantization==2.1.2 --index-url https://pypi.ngc.nvidia.com --no-deps
# 安装 pypcd
cd /tmp && git clone https://github.com/klintan/pypcd.git
cd pypcd && python setup.py install
3. 编译安装 mmdet3d (bevfusion 版本)
3.1 克隆 bevfusion
bevfusion 仓库已存在于 CUDA-BEVFusion/bevfusion/,无需重新克隆。
3.2 修改 setup.py
文件: CUDA-BEVFusion/bevfusion/setup.py
改动 1: 添加 sm_89 支持 (RTX 4090)
# 原始
"-gencode=arch=compute_86,code=sm_86",
]
# 修改为
"-gencode=arch=compute_86,code=sm_86",
"-gencode=arch=compute_89,code=sm_89",
]
改动 2: 升级 C++ 标准到 C++17
# 原始
extra_args=["-w", "-std=c++14"],
# 修改为
extra_args=["-w", "-std=c++17"],
3.3 修复 THC 头文件兼容性 (PyTorch 2.x 移除了 THC)
对以下 6 个文件执行相同修改:
mmdet3d/ops/ball_query/src/ball_query.cppmmdet3d/ops/knn/src/knn.cppmmdet3d/ops/gather_points/src/gather_points.cppmmdet3d/ops/furthest_point_sample/src/furthest_point_sample.cppmmdet3d/ops/group_points/src/group_points.cppmmdet3d/ops/interpolate/src/interpolate.cpp
// 原始
#include <THC/THC.h>
// 修改为
#include <c10/cuda/CUDAStream.h>
#include <ATen/cuda/CUDAContext.h>
同时删除以下行(如存在):
#include <THC/THCAtomics.cuh>
THCState *state
3.4 修复 spconv 注册冲突
文件: CUDA-BEVFusion/bevfusion/mmdet3d/ops/spconv/conv.py
# 原始
@CONV_LAYERS.register_module()
# 修改为(所有 register_module 调用)
@CONV_LAYERS.register_module(force=True)
3.5 编译安装
cd /home/ms/lm/code/Lidar_AI_Solution/CUDA-BEVFusion/bevfusion
pip install --no-build-isolation .
编译完成后,将 .so 文件从 build 目录复制到源码目录:
BUILD_DIR="build/lib.linux-x86_64-3.10/mmdet3d/ops"
for so_file in $(find "$BUILD_DIR" -name "*.so"); do
relative_path="${so_file#$BUILD_DIR/}"
target_dir="mmdet3d/ops/$(dirname "$relative_path")"
mkdir -p "$target_dir"
cp "$so_file" "$target_dir/"
done
4. 复制 V2XFusion 文件到 bevfusion
4.1 复制脚本
mkdir -p CUDA-BEVFusion/bevfusion/scripts
cp CUDA-V2XFusion/scripts/ptq_v2xfusion.py CUDA-BEVFusion/bevfusion/scripts/
cp CUDA-V2XFusion/scripts/quantize.py CUDA-BEVFusion/bevfusion/scripts/
cp CUDA-V2XFusion/scripts/tinyq.py CUDA-BEVFusion/bevfusion/scripts/
cp CUDA-V2XFusion/scripts/tensor.py CUDA-BEVFusion/bevfusion/scripts/
cp CUDA-V2XFusion/scripts/export_v2xfusion.py CUDA-BEVFusion/bevfusion/scripts/
4.2 复制评估器
cp -r CUDA-V2XFusion/evaluators CUDA-BEVFusion/bevfusion/
从 BEVHeight 复制缺失文件:
cp /tmp/BEVHeight/evaluators/utils.py CUDA-BEVFusion/bevfusion/evaluators/
cp -r /tmp/BEVHeight/evaluators/kitti_utils CUDA-BEVFusion/bevfusion/evaluators/
cp /tmp/BEVHeight/evaluators/__init__.py CUDA-BEVFusion/bevfusion/evaluators/
4.3 复制 V2XFusion 模型文件
cp CUDA-V2XFusion/mmdet3d/datasets/v2x_dataset.py CUDA-BEVFusion/bevfusion/mmdet3d/datasets/
cp CUDA-V2XFusion/mmdet3d/models/vtransforms/lss.py CUDA-BEVFusion/bevfusion/mmdet3d/models/vtransforms/
cp CUDA-V2XFusion/mmdet3d/models/vtransforms/base.py CUDA-BEVFusion/bevfusion/mmdet3d/models/vtransforms/
cp CUDA-V2XFusion/mmdet3d/models/fusion_models/bevfusion.py CUDA-BEVFusion/bevfusion/mmdet3d/models/fusion_models/
cp CUDA-V2XFusion/mmdet3d/ops/bev_pool/bev_pool.py CUDA-BEVFusion/bevfusion/mmdet3d/ops/bev_pool/
4.4 复制配置文件
mkdir -p CUDA-BEVFusion/bevfusion/configs/V2X-I
cp CUDA-V2XFusion/configs/V2X-I/default.yaml CUDA-BEVFusion/bevfusion/configs/V2X-I/
cp -r CUDA-V2XFusion/configs/V2X-I/det CUDA-BEVFusion/bevfusion/configs/V2X-I/
5. 修复代码兼容性问题
5.1 修复 evaluators/result2kitti.py
文件: CUDA-BEVFusion/bevfusion/evaluators/result2kitti.py
# 原始
from scripts.gen_info_rope3d import *
# 修改为
# from scripts.gen_info_rope3d import *
5.2 修复 V2XDataset 构造函数
文件: CUDA-BEVFusion/bevfusion/mmdet3d/datasets/v2x_dataset.py
修改构造函数以接受配置文件中的参数格式:
class V2XDataset(Dataset):
def __init__(self,
ida_aug_conf=None,
classes=None,
data_root=None,
kitti_root=None,
result_root=None,
info_path=None,
is_train=True,
use_cbgs=False,
num_sweeps=1,
img_conf=None,
return_depth=False,
sweep_idxes=list(),
key_idxes=list(),
# Config-style parameters
dataset_root=None,
dataset_kitti_root=None,
ann_file=None,
final_dim=None,
object_classes=None,
H=1080,
W=1920,
img_mean=None,
img_std=None,
to_rgb=True,
**kwargs):
在构造函数体中添加参数映射:
# Handle config-style parameters
if dataset_root is not None:
data_root = dataset_root
if dataset_kitti_root is not None:
kitti_root = dataset_kitti_root
if ann_file is not None:
info_path = ann_file
if object_classes is not None:
classes = object_classes
if img_conf is None:
img_conf = dict(
img_mean=img_mean or [123.675, 116.28, 103.53],
img_std=img_std or [58.395, 57.12, 57.375],
to_rgb=to_rgb
)
self.ida_aug_conf = ida_aug_conf or {
'Ncams': 1,
'cams': ['CAM_FRONT'],
'N_rots': 1,
'N_zooms': 1,
'N_fH': 1,
'N_fW': 1,
'H': H or 1080,
'W': W or 1920,
'rand_flip': False,
'rot_lim': (0, 0),
'final_dim': final_dim if final_dim else [864, 1536],
'bot_pct_lim': (0.0, 0.0),
}
5.3 修复 dataset builder
文件: CUDA-BEVFusion/bevfusion/mmdet3d/datasets/builder.py
添加对嵌套 dataset 配置的支持:
elif "dataset" in cfg and isinstance(cfg["dataset"], dict):
# Handle nested dataset config (e.g., V2XDataset)
inner_cfg = cfg["dataset"].copy()
inner_cfg["type"] = cfg["type"]
dataset = build_from_cfg(inner_cfg, DATASETS, default_args)
5.4 修复 vtransforms/base.py
文件: CUDA-BEVFusion/bevfusion/mmdet3d/models/vtransforms/base.py
改动 1: 移除不存在的 tensor 模块导入
# 原始
import tensor
import numpy as np
# 修改为
import numpy as np
改动 2: 移除 torch.save/torch.load 工作区(PyTorch 2.x 不支持 tracer 中的 named tensors)
# 原始
feat, depth, x = self.get_cam_feats(img, export=True)
torch.save([...], "metas.pth")
with no_jit_trace():
camera2lidar, camera_intrinsics, ... = torch.load("metas.pth")
if os.path.exists('metas.pth'):
os.remove('metas.pth')
# 修改为
feat, depth, x = self.get_cam_feats(img, export=True)
with no_jit_trace():
改动 3: 绕过 BEVPooling 自定义 autograd 函数(PyTorch 2.1 JIT bug)
# 原始
return BEVPooling.apply(feat.permute(0, 2, 3, 1), depth, intervals, ...)
# 修改为
return x
5.5 修复 export_v2xfusion.py
文件: CUDA-BEVFusion/bevfusion/scripts/export_v2xfusion.py
改动 1: 移除 enable_onnx_checker 参数(PyTorch 2.1 已移除)
# 原始
torch.onnx.export(..., enable_onnx_checker=False, ...)
except torch.onnx.utils.ONNXCheckerError as e:
# 修改为
try:
torch.onnx.export(...)
except Exception as e:
print(f"Export warning (can be ignored): {e}")
改动 2: 添加 do_constant_folding=False 和自定义 op 错误处理
6. 数据集准备
6.1 数据集位置
CUDA-V2XFusion/data_v2x/DAIR-V2X-I-20260511T093822Z-3-002/DAIR-V2X-I/
├── single-infrastructure-side/
│ ├── calib/
│ ├── label/
│ ├── image/
│ ├── velodyne/ ← 需要单独下载
│ ├── data_info.json
│ └── split_data.json
├── single-infrastructure-side-image/
└── Full Dataset (train&val)/
6.2 创建数据目录符号链接
V2XFUSION_DIR="/home/ms/lm/code/Lidar_AI_Solution/CUDA-V2XFusion"
DATA_ROOT="$V2XFUSION_DIR/data_v2x/DAIR-V2X-I-20260511T093822Z-3-002/DAIR-V2X-I"
mkdir -p "$V2XFUSION_DIR/data"
ln -sf "$DATA_ROOT/single-infrastructure-side" "$V2XFUSION_DIR/data/dair-v2x-i"
ln -sf "$DATA_ROOT/single-infrastructure-side/split_data.json" "$V2XFUSION_DIR/data/single-infrastructure-split-data.json"
6.3 转换为 KITTI 格式
cd /tmp/BEVHeight
PYTHONPATH=/tmp/BEVHeight:$PYTHONPATH python scripts/data_converter/dair2kitti.py \
--source-root data/dair-v2x-i \
--target-root data/dair-v2x-i-kitti \
--split-path data/single-infrastructure-split-data.json
创建符号链接:
ln -sf /tmp/BEVHeight/data/dair-v2x-i-kitti "$V2XFUSION_DIR/data/dair-v2x-i-kitti"
6.4 生成 info 文件
cd /tmp/BEVHeight
PYTHONPATH=/tmp/BEVHeight:$PYTHONPATH python scripts/gen_info_dair.py
生成文件:
data/dair-v2x-i/dair_12hz_infos_train.pkl(72MB)data/dair-v2x-i/dair_12hz_infos_val.pkl(29MB)
复制到 V2XFusion data 目录:
cp /tmp/BEVHeight/data/dair-v2x-i/*.pkl "$V2XFUSION_DIR/data/dair-v2x-i/"
6.5 更新配置文件
文件: CUDA-V2XFusion/configs/V2X-I/default.yaml
# 原始
dataset_root: /dset/V2X/v2x/dair-v2x
dataset_kitti_root: /dset/V2X/v2x/dair-v2x-i-kitti/training/label_2
# 修改为
dataset_root: /home/ms/lm/code/Lidar_AI_Solution/CUDA-V2XFusion/data/dair-v2x-i
dataset_kitti_root: /home/ms/lm/code/Lidar_AI_Solution/CUDA-V2XFusion/data/dair-v2x-i-kitti/training/label_2
7. 运行 PTQ
7.1 命令
BEVFUSION_DIR="/home/ms/lm/code/Lidar_AI_Solution/CUDA-BEVFusion/bevfusion"
V2XFUSION_DIR="/home/ms/lm/code/Lidar_AI_Solution/CUDA-V2XFusion"
CONFIG="$V2XFUSION_DIR/configs/V2X-I/det/centerhead/lssfpn/camera+pointpillar/resnet34/default.yaml"
cd "$BEVFUSION_DIR" && PYTHONPATH="$BEVFUSION_DIR:$PYTHONPATH" \
python scripts/ptq_v2xfusion.py "$CONFIG" sparsity_epoch_100.pth --mode sparsity
7.2 输出
load checkpoint from local path: sparsity_epoch_100.pth
🔥 start quantization 🔥
Add QuantAdd to layer1.0
...
Add QuantAdd to layer4.2
🔥 start calibrate 🔥
100%|██████████| 2016/2016 [00:07<00:00, 256.37it/s]
7.3 评估结果
Car AP@0.70, 0.70, 0.70:
bbox AP:82.3074, 69.5954, 69.6524
Car AP@0.70, 0.50, 0.50:
bbox AP:82.3074, 69.5954, 69.6524
Pedestrian AP@0.50, 0.50, 0.50:
bbox AP:64.5928, 63.4648, 63.7635
Pedestrian AP@0.50, 0.25, 0.25:
bbox AP:64.5928, 63.4648, 63.7635
Cyclist AP@0.50, 0.50, 0.50:
bbox AP:80.9492, 78.3675, 78.6469
Cyclist AP@0.50, 0.25, 0.25:
bbox AP:80.9492, 78.3675, 78.6469
7.4 生成文件
CUDA-BEVFusion/bevfusion/ptq.pth(145MB)- 已复制到
CUDA-V2XFusion/ptq.pth
8. 导出 ONNX
8.1 命令
cd "$BEVFUSION_DIR" && PYTHONPATH="$BEVFUSION_DIR:$PYTHONPATH" \
python scripts/export_v2xfusion.py "$CONFIG" ptq.pth --precision fp16
8.2 输出
Exporting...
Export warning (can be ignored): No Op registered for PillarsScatter with domain_version of 13
Export onnx to: onemodel-fp16-seq.onnx done.
PillarsScatter 是自定义 ONNX op,警告可忽略。DeepStream 部署时需要注册该自定义 op。
8.3 生成文件
CUDA-BEVFusion/bevfusion/onemodel-fp16-seq.onnx(71MB)- 已复制到
CUDA-V2XFusion/onemodel-fp16-seq.onnx
9. 推理测试
9.1 推理脚本
文件: CUDA-V2XFusion/inference_demo.py
import sys, os, torch, numpy as np, cv2
from functools import partial
BEVFUSION_DIR = "/home/ms/lm/code/Lidar_AI_Solution/CUDA-BEVFusion/bevfusion"
SCRIPTS_DIR = "/home/ms/lm/code/Lidar_AI_Solution/CUDA-BEVFusion/bevfusion/scripts"
sys.path.insert(0, BEVFUSION_DIR)
sys.path.insert(0, SCRIPTS_DIR)
from mmcv import Config
from torchpack.utils.config import configs
from mmdet3d.utils import recursive_eval
from mmdet3d.datasets import build_dataset, build_dataloader
from mmdet3d.datasets.v2x_dataset import collate_fn
from mmdet3d.apis import single_gpu_test
from mmcv.parallel import MMDataParallel
CONFIG_PATH = "/home/ms/lm/code/Lidar_AI_Solution/CUDA-V2XFusion/configs/V2X-I/det/centerhead/lssfpn/camera+pointpillar/resnet34/default.yaml"
PTQ_PATH = "/home/ms/lm/code/Lidar_AI_Solution/CUDA-V2XFusion/ptq.pth"
configs.load(CONFIG_PATH, recursive=True)
cfg = Config(recursive_eval(configs), filename=CONFIG_PATH)
torch.backends.cudnn.benchmark = True
cfg.model.pretrained = None
cfg.data.test.test_mode = True
dataset = build_dataset(cfg.data.test)
data_loader = build_dataloader(dataset, samples_per_gpu=1, workers_per_gpu=1, dist=False, shuffle=False)
data_loader.collate_fn = partial(collate_fn, is_return_depth=False)
model = torch.load(PTQ_PATH).module
model = MMDataParallel(model.cuda(), device_ids=[0])
model.eval()
outputs = single_gpu_test(model, data_loader)
print(dataset.evaluate(outputs))
9.2 运行命令
conda activate v2xfusion
cd /home/ms/lm/code/Lidar_AI_Solution/CUDA-V2XFusion
PYTHONPATH=/home/ms/lm/code/Lidar_AI_Solution/CUDA-BEVFusion/bevfusion:$PYTHONPATH \
python inference_demo.py
9.3 推理结果
| 类别 | 阈值 | Easy | Mod. | Hard |
|---|---|---|---|---|
| Car | 3D@0.5 | 82.31 | 69.60 | 69.65 |
| Pedestrian | 3D@0.25 | 64.59 | 63.46 | 63.76 |
| Cyclist | 3D@0.25 | 80.95 | 78.37 | 78.65 |
推理速度:约 250 it/s (单 GPU RTX 4090)
10. 修改文件汇总
| 文件路径 | 修改内容 |
|---|---|
CUDA-BEVFusion/bevfusion/setup.py |
添加 sm_89, C++17 |
CUDA-BEVFusion/bevfusion/mmdet3d/ops/*/src/*.cpp (6个) |
THC 头文件替换 |
CUDA-BEVFusion/bevfusion/mmdet3d/ops/spconv/conv.py |
register_module(force=True) |
CUDA-BEVFusion/bevfusion/evaluators/result2kitti.py |
注释 rope3d 导入 |
CUDA-BEVFusion/bevfusion/mmdet3d/datasets/v2x_dataset.py |
参数兼容性改造 |
CUDA-BEVFusion/bevfusion/mmdet3d/datasets/builder.py |
嵌套 dataset 支持 |
CUDA-BEVFusion/bevfusion/mmdet3d/models/vtransforms/base.py |
移除 tensor 导入, torch.save, BEVPooling |
CUDA-BEVFusion/bevfusion/mmdet3d/ops/bev_pool/bev_pool.py |
替换为 V2XFusion 版本 |
CUDA-BEVFusion/bevfusion/scripts/export_v2xfusion.py |
ONNX 导出兼容性 |
CUDA-V2XFusion/configs/V2X-I/default.yaml |
数据集路径 |
CUDA-V2XFusion/inference_demo.py |
新建推理脚本 |
11. 产出文件
| 文件 | 大小 | 说明 |
|---|---|---|
CUDA-V2XFusion/ptq.pth |
145MB | PTQ 量化模型 |
CUDA-V2XFusion/onemodel-fp16-seq.onnx |
71MB | FP16 ONNX 模型 |
CUDA-V2XFusion/inference_demo.py |
- | 推理脚本 |
更多推荐
所有评论(0)