目前在雷达3D检测领域中,工业中常用的方法有两种,分别为基友传统算法的欧式聚类算法以及基于深度学习的pointpillars,简单归纳一下两种常用算法的优缺点。
欧式聚类算法:
不论是基于固定阈值的欧式聚类,或者是基于自适应阈值的欧式聚类来说,聚类效果可以说还凑合,个人理解,在复杂场景下,检测效果并没有预期的好,而且不能检测出物体的类别,优点当然是速度快,简单容易理解。
PointPillars:
在目标检测领域中,视觉有着很大的优势,概括起来就是,首先,视觉具有丰富的信息,而且是在一个矩阵中表达,各个像素块的排列是比较规整的,而且数据量较少,更容易进行特征表达。在雷达的深度学习领域中,难点在于:
1、激光雷达的点云会随着距离进行发散,导致点云的稀疏,而且是一个三维的空间表示,会造成点云的发散以及无序性;
2、点云的数据量很大,不好处理;
SO 现在的研究思路就是将点云变为 伪图像,之后再按照图像的检测算法进行特征提取等,最后加一个维度的单独回归,其中,流行的有voxelnet,其思想也就是将点云空间进行划分,之后进行特征提取,这种方式的速度仍然不能达到实时性。那么,pillars出现了,他的字面意思就是“柱子”,也就是对点云进行划分,一个网格上只有一个柱子,所以他的速度表现很SOTA。
为什么要记录这篇博客,目前大家可以拿到的示例demo,都是车辆检测,而在目标检测中,我们通常要检测的大致目标包括人、车、骑自行车的人等,这就需要我们自己创立数据集进行训练。在训练中遇到很多问题,这里进行记录。
直接上干货!~~~~~~
环境搭建,这真是头大,尽量用我验证过的环境进行测试吧
1、安装Anaconda,正常安装就行,顺便说一句,正常安装针对的是x86的架构,像什么TX2, XAVIER等,都是arm架构,需要在github上进行源码安装;
2、操作系统选择:ubuntu18.04
3、安装NVIDIA驱动,本人使用的是460.80,根据自己的显卡版本进行安装,当然,TX2等设备需要在nvidia官网进行烧录;
4、CUDA:由于cuda的版本是向上兼容的,所以安装一个10.0就行了,我进行了验证,这个环境是ok的;
5、安装cuDNN,找与CUDA对应的版本即可
5、安装pytorch ,我选择的是pytorch1.4
6、安装torchvision 0.5.0
这里插一句,cuda的版本是可以在电脑上多版本共存的,所以大可不必重装系统;安装好了,记得验证一下,例如nvcc -V等,巴拉巴拉~~~~~
准备创建一个虚拟环境,为了不把自己的系统搞废了,创建虚拟环境单独搞
1、查看anaconda是否正确: shell ~ conda list,正常是加载出一些模块
2、创建环境并激活:
conda create -n pcdetect python = 3.7
conda activate pcdetect
conda create -n pcdetect(自己起的名字) python=3.7
3、克隆项目:git chone https://github.com/AbangLZU/EasyPointPillars
4、减压准备数据集,这里涉及到两个数据集,分别为kitti和waymo,咱也不知道怎么下载这么大的数据集,这里用的是kitti,建议直接在网上百度云搜索kitti数据集,是有的!~!!!
一共需要下载四个数据文件,分别为:
data_object_velodyne.zip
data_object_cali.zip
data_object_label_2.zip
data_object_image_2.zip
下载完成后,减压放在工程中的data文件下,目录结构如下:
EasyPointPillars
|-----data
|-----|-kitti
|-----|------ImageSets
|-----|------training
|-----|------|---------cali & velodyne & label_2 & image_2
|-----|------testing
|-----|------I----------cali & velodyne & image_2
5、安装工程依赖
cd EasyPointPillars
pip install -r requirements.txt -i https://pypi.douban.com/simple
6、安装spconv 稀疏卷积模块,咱也不知道这具体什么,照着安就完事了!!1
在你激活的虚拟环境下,在EasyPointPillars文件夹下,输入命令:
git clone https://github.com/AbangLZU/spconv.get --recursive
亲测下载没反应,那就去这个网站直接克隆文件吧,克隆后,重点来了,看看spconv/third_party/pybind11是不是空的,如果是,那恭喜你,你需要点开下载链接中的这个文件,他是有有个下载跳转链接的,你下载下来,减压放在pybind11下就可以了,到这里,你就超越好多人,哈哈!~~
7、编译spconv
由于编译此项目需要cmake版本大于3.13,所以需要升级ubuntu中的cmake版本,具体的更换方法自己查吧,更换好,记得在终端看看,输入cmake --veision更换好了就可以开始编译了,最坑的是这个
cd spconv/
python setup.py bdist_wheel
会自动多三个文件夹,包括build, dist,egg_info...
接下来,使用pip安装编译生成的whl文件
cd dist
pip install **whl 自己tab一下,就出来了
8、安装EasyPointPillars项目
cd EasyPointPillars
python setup.py develop
安装完成后,可以在conda环境中使用 pip show pcdet查看信息: 会打印出项目的相关信息
9、模型训练:
cd EasyPointPillars/
python -m pcdet.datasets.kitti.kitti_dataset create_kitti_info tools/cfgs/dataset_config/kitti_dataset.yaml
期间或报错,yaml.load(sys)...... 416行
在加载yaml文件,修改为
with open(sys.argv[2],"r") as f:
dataset_cfg = EasyDict(yaml.load(f,Loader=yaml.FullLoader))
完成数据集整理后,会输出如下信息:
就是各个类别的数据集数量
10、开始训练
cd tools/
python train.py --cfg_file cfg/kitti_models/pointpillars.yaml
期间会报错,key_Error[road_plant]...
将该文件中的这个路面数据集注释掉。
pcdet/datasets/augmentor.py
call函数修改为
def __call__(self, data_dict):
"""
Args:
data_dict:
gt_boxes: (N, 7 + C) [x, y, z, dx, dy, dz, heading, ...]
Returns:
"""
gt_boxes = data_dict['gt_boxes']
gt_names = data_dict['gt_names'].astype(str)
existed_boxes = gt_boxes
total_valid_sampled_dict = []
for class_name, sample_group in self.sample_groups.items():
if self.limit_whole_scene:
num_gt = np.sum(class_name == gt_names)
sample_group['sample_num'] = str(int(self.sample_class_num[class_name]) - num_gt)
if int(sample_group['sample_num']) > 0:
sampled_dict = self.sample_with_fixed_number(class_name, sample_group)
sampled_boxes = np.stack([x['box3d_lidar'] for x in sampled_dict], axis=0).astype(np.float32)
if self.sampler_cfg.get('DATABASE_WITH_FAKELIDAR', False):
sampled_boxes = box_utils.boxes3d_kitti_fakelidar_to_lidar(sampled_boxes)
iou1 = iou3d_nms_utils.boxes_bev_iou_cpu(sampled_boxes[:, 0:7], existed_boxes[:, 0:7])
iou2 = iou3d_nms_utils.boxes_bev_iou_cpu(sampled_boxes[:, 0:7], sampled_boxes[:, 0:7])
iou2[range(sampled_boxes.shape[0]), range(sampled_boxes.shape[0])] = 0
iou1 = iou1 if iou1.shape[1] > 0 else iou2
valid_mask = ((iou1.max(axis=1) + iou2.max(axis=1)) == 0).nonzero()[0]
valid_sampled_dict = [sampled_dict[x] for x in valid_mask]
valid_sampled_boxes = sampled_boxes[valid_mask]
existed_boxes = np.concatenate((existed_boxes, valid_sampled_boxes), axis=0)
total_valid_sampled_dict.extend(valid_sampled_dict)
sampled_gt_boxes = existed_boxes[gt_boxes.shape[0]:, :]
if total_valid_sampled_dict.__len__() > 0:
data_dict = self.add_sampled_boxes_to_scene(data_dict, sampled_gt_boxes, total_valid_sampled_dict)
data_dict.pop('gt_boxes_mask')
return data_dict
至此,就可以开始训练了,默认是80个epoch