1. 开篇为什么选择RandLA-Net与SemanticKITTI如果你对自动驾驶、机器人或者三维视觉感兴趣那你肯定绕不开“点云”这个技术。简单来说点云就是一堆三维空间中的点记录了物体表面的位置信息通常由激光雷达扫描得到。想象一下你的手机摄像头拍的是二维照片而激光雷达拍的就是三维的“点状照片”。处理这种数据最核心的任务之一就是“语义分割”——给每一个点都打上标签比如这个点是属于汽车、行人、建筑物还是路面。这就是RandLA-Net大显身手的地方。它全称是“Random Sampling and Local Feature Aggregation Network”翻译过来就是“随机采样和局部特征聚合网络”。我当初选择复现它就是因为它用了一个非常聪明的思路直接在原始的大规模点云上进行操作而不是像很多早期方法那样需要先把点云转换成体素网格或者多视图图像。这种“原生”处理方式理论上能保留更多原始几何信息而且效率更高。SemanticKITTI数据集则是目前公开的、规模最大、标注最精细的车载激光雷达点云语义分割数据集是检验算法效果的“黄金标准”。在Ubuntu 18.04上从头到尾走一遍这个流程意义非凡。这不仅仅是跑通一个代码更是深入理解一个现代点云处理pipeline的绝佳机会。你会遇到从数据下载、环境配置、编译坑点到理解核心的体素化预处理再到监控训练、分析结果的全过程。我踩过的坑、总结的经验都会在这篇文章里毫无保留地分享给你。无论你是刚入门的研究生还是想在实际项目中应用点云分割的工程师跟着这篇手把手的指南你都能在自己的机器上成功复现出论文里的效果甚至能根据自己的需求进行修改和调优。2. 项目与数据万事开头准备要细复现任何深度学习项目第一步永远是把“原材料”——代码和数据——准备妥当。这一步看似简单但文件夹结构放错一个地方可能后面整个流程都会报错。我强烈建议你严格按照下面的步骤来可以节省大量排查问题的时间。2.1 获取RandLA-Net官方代码RandLA-Net的作者在GitHub上开源了代码这是我们的起点。我个人的习惯是对于这种经典项目直接下载ZIP压缩包避免使用Git克隆时可能出现的子模块或版本问题。你可以在浏览器里打开项目的GitHub页面找到那个绿色的“Code”按钮选择“Download ZIP”。下载完成后把它解压到你喜欢的位置比如你的家目录下或者桌面上。解压后的文件夹名字可能是RandLA-Net-master里面包含了训练、评估、可视化的所有脚本以及一些工具函数。2.2 下载庞大的SemanticKITTI数据集接下来是重头戏数据。SemanticKITTI数据集基于著名的KITTI视觉里程计基准开发提供了完整的点云和逐点语义标签。你需要访问它的官方网站在下载页面找到三个核心压缩包Velodyne point clouds (约80 GB)这是原始的激光雷达扫描数据每个扫描帧保存为一个.bin文件。Calibration data (约1 MB)传感器的标定参数文件对于数据的正确解析很重要。Label data (约179 MB)人工标注的语义标签每个点对应一个类别。注意80GB的点云数据下载可能需要很长时间请确保你的网络连接稳定并且有足够的磁盘空间解压后更大。下载下来的三个文件通常命名为data_odometry_velodyne.zipdata_odometry_calib.zip和data_odometry_labels.zip。2.3 构建正确的数据集文件夹结构代码读取数据依赖于一个固定的目录结构。你需要在RandLA-Net项目内部创建这个结构。具体操作如下进入解压后的RandLA-Net-master文件夹。依次创建文件夹data/semantic_kitti/dataset/。你可以用命令mkdir -p data/semantic_kitti/dataset/sequences来一键创建所有层级。将你下载的三个压缩包解压到dataset文件夹下。关键来了你需要手动整理使得最终的dataset文件夹内部结构看起来像一棵“标准树”dataset/ └── sequences/ ├── 00/ │ ├── velodyne/ # 存放000000.bin, 000001.bin... │ ├── labels/ # 存放000000.label, 000001.label... │ ├── calib.txt │ ├── poses.txt │ └── times.txt ├── 01/ │ ├── velodyne/ │ ├── labels/ │ ├── calib.txt │ ├── poses.txt │ └── times.txt └── ... (一直到序列21)这意味着你需要把data_odometry_velodyne.zip解压后将其dataset/sequences/00/velodyne等目录下的内容移动或复制到我们新建的sequences/00/velodyne下。标签和标定文件同理。这个过程有点繁琐但至关重要。你可以写个简单的Shell脚本来批量移动或者耐心地手动操作。确保每个序列文件夹00, 01, ..., 21下都有velodyne,labels,calib.txt这五个项目。我当初就是在这里漏了一个poses.txt导致后续预处理时一直报错排查了好久。3. 环境配置与依赖安装打造专属工作间有了代码和数据我们需要一个合适的“工作间”——也就是Python环境。RandLA-Net基于TensorFlow 1.x通常是1.13或1.14开发这与现在主流的TensorFlow 2.x不兼容。因此我们最好使用Conda创建一个隔离的Python 3.6或3.7环境。3.1 创建并激活Conda环境打开终端执行以下命令conda create -n randlanet python3.6 conda activate randlanet这里我们创建了一个名为randlanet的环境并指定Python版本为3.6。激活环境后终端的命令提示符前面应该会出现(randlanet)表示你正在这个环境中工作。3.2 安装项目依赖包进入RandLA-Net-master目录你会发现一个helper_requirements.txt文件。它列出了项目所需的核心Python包。但是由于时间推移有些包的版本可能需要微调才能兼容。我建议直接使用以下内容创建一个新的requirements.txt文件或者修改原文件numpy h5py2.10.0 cython0.29.15 open3d-python0.3.0 pandas0.25.3 scikit-learn0.21.3 scipy1.4.1 PyYAML tensorflow-gpu1.13.1注意我们明确添加了tensorflow-gpu1.13.1。这是为了确保安装的是GPU版本的TensorFlow 1.13.1。如果你没有NVIDIA GPU或者只想用CPU跑非常慢可以安装tensorflow1.13.1。然后使用pip安装为了加速我们可以使用国内的镜像源pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple3.3 编译自定义TensorFlow操作关键步骤RandLA-Net为了实现高效的随机采样和局部特征聚合使用C编写了一些自定义的TensorFlow操作Ops。这些操作需要被编译成.so动态库文件Python才能调用。这是整个环境配置中最容易出错的一步。在项目根目录下有一个compile_op.sh脚本。理论上直接运行sh compile_op.sh即可。但根据我的经验在Ubuntu 18.04上你可能会遇到两个常见问题CUDA路径问题脚本里硬编码了CUDA的头文件路径如/usr/local/cuda-10.0。如果你的CUDA安装在其他位置比如/usr/local/cuda或者版本不同比如11.0编译就会失败。你需要用文本编辑器打开compile_op.sh以及tf_ops文件夹下的tf_interpolate、tf_sampling等子目录中的Makefile文件检查并修改CUDA_PATH和CUDNN_PATH使其指向你系统上正确的CUDA安装目录。GCC/G版本问题TensorFlow 1.13对编译器版本有要求。Ubuntu 18.04默认的GCC版本是7.x有时可能需要切换。如果编译报错提到-D_GLIBCXX_USE_CXX11_ABI相关的问题你可以尝试安装并切换到一个更旧的GCC版本如5.x或者根据错误信息调整编译标志。编译成功后你会在各个tf_ops的子目录下看到新生成的.so文件如tf_interpolate.so。如果编译过程没有报错并且生成了这些文件那么最艰难的一关就过了。4. 数据预处理理解神秘的“体素化”代码和数据都准备好了是不是直接就可以python train.py了别急还差关键一步数据预处理。直接运行main_SemanticKITTI.py会报错因为它期望的输入不是原始的.bin和.label文件而是一种处理后的格式。这个处理过程就是由utils/data_prepare_semantickitti.py这个脚本完成的它的核心操作叫做“体素化”。4.1 修改预处理脚本路径首先用编辑器打开utils/data_prepare_semantickitti.py。找到大概第20行和21行你会看到两个路径变量dataset_path /data/semantic_kitti/dataset # 原始数据路径 output_path /data/semantic_kitti/dataset # 输出数据路径这里的/data是绝对路径。为了让它能在我们的项目目录下工作最简单的办法是在/data前面加一个点.改成相对路径dataset_path ./data/semantic_kitti/dataset output_path ./data/semantic_kitti/dataset这样脚本就会从我们之前精心构建的RandLA-Net-master/data/semantic_kitti/dataset读取数据并把处理结果输出到同目录下。4.2 运行预处理并理解体素化在终端中确保位于项目根目录并且conda环境已激活然后运行python utils/data_prepare_semantickitti.py这个脚本会开始处理所有22个序列00到21的数据。这个过程非常耗时并且占用大量内存和磁盘I/O根据你的CPU和硬盘速度可能需要30分钟到数小时。我建议你在运行期间不要进行其他高负载任务。那么它到底在做什么呢体素化。你可以把三维空间想象成一块巨大的豆腐体素化就是用一把极其细密的网格刀把这块豆腐切成无数个大小完全相同的小立方体格子每个格子就是一个“体素”。原始的点云中点的分布是稀疏且不均匀的。体素化之后我们将每个体素内的所有点可能有很多个也可能没有的信息比如坐标、标签进行聚合或者用体素中心的点来代表这个格子。在RandLA-Net的预处理中grid_size0.06意味着每个体素格子的边长是0.06米。这个值是一个超参数平衡了处理效率和信息保留程度。格子太大会丢失细节格子太小计算量暴增而且很多格子是空的。0.06米是论文作者经过实验确定的一个较好值。处理完成后你会在dataset文件夹旁边发现一个新生成的文件夹名字类似sequences_0.06。这个文件夹可能高达180GB左右里面存储的就是体素化后的、可供网络直接读取的预处理数据。至此真正的“食材”才算是准备完毕了。5. 启动模型训练与过程监控预处理完成后我们终于可以开始训练模型了。这是最激动人心也最考验耐心的阶段。5.1 开始GPU训练训练命令非常简单python main_SemanticKITTI.py --mode train --gpu 0这里--gpu 0指定使用第一块GPU如果你的机器有多块GPU可以尝试--gpu 0,1进行多卡训练。执行这条命令后程序会加载预处理好的数据初始化RandLA-Net网络模型然后开始迭代训练。你需要理解几个关键概念Batch由于数据量太大无法一次性全部放入显卡内存所以需要分批Batch送入。代码里会打印出类似“4750 batches per epoch”的信息意思是把整个训练集过一遍称为一个Epoch需要分成4750个小批来处理。Epoch整个训练集被完整训练一次称为一个Epoch。RandLA-Net在SemanticKITTI上通常需要训练很多个Epoch比如100个才能收敛。学习率与优化器脚本里默认使用了Adam优化器并设置了学习率衰减策略。这些参数你可以在helper_tool.py或main_SemanticKITTI.py的配置部分找到并修改。5.2 监控训练状态与资源占用训练开始后终端会滚动打印日志包括当前Epoch、Batch、损失Loss值、以及当前Batch的处理时间。请务必关注训练速度。如果你用的是GPU速度应该很快。以我之前的经验在一张RTX 3060显卡上设置batch_size4大约1小时能完成2个Epoch。如果你发现训练速度异常缓慢比如几个小时才完成一个Epoch那很可能程序错误地使用了CPU在进行计算。你需要检查TensorFlow是否确实识别到了GPU。可以在训练开始前在Python环境中输入import tensorflow as tf; print(tf.test.is_gpu_available())来确认。另一个有用的监控命令是nvidia-smi。你可以使用watch -n 2 nvidia-smi来每2秒刷新一次GPU状态。在训练过程中你应该看到指定的GPU利用率GPU-Util接近100%并且显存Memory-Usage被大量占用。这是一个健康的训练状态。训练过程会持续很长时间几十个小时。代码会自动保存定期保存模型快照checkpoint通常保存在results/Log_xxx/snapshots目录下。这样即使训练中途中断你也可以从最近的快照恢复训练而不用从头开始。6. 模型评估与结果可视化见证成果训练结束后或者你想中途评估一下某个快照的性能就需要进行评估和可视化。这是检验我们工作成果的时刻。6.1 使用脚本进行批量评估项目提供了一个方便的评估脚本jobs_test_semantickitti.sh。直接运行它sh jobs_test_semantickitti.sh这个脚本会遍历results目录下所有的实验日志文件夹找到最新的模型快照然后在测试集通常是序列08上进行评估。评估的核心指标是平均交并比mean Intersection over Union, mIoU和整体准确率Overall Accuracy。mIoU这是语义分割中最常用的评估指标。对于每个类别计算预测区域和真实标签区域的重叠部分交集除以它们的合并部分并集然后再对所有类别的这个值求平均。这个值越接近1说明分割效果越好。RandLA-Net在SemanticKITTI上的官方mIoU大约在53-55%左右在测试集上这是一个非常不错的成绩。整体准确率所有点中被正确分类的点的比例。评估完成后终端会详细打印出每一个类别的IoU以及最终的mIoU和准确率。你可以将这个结果与论文报告的结果进行对比以验证你的复现是否成功。6.2 点云预测结果可视化数字指标虽然客观但不如视觉直观。RandLA-Net项目内置了基于Open3D的可视化功能可以让我们亲眼看到模型在点云上的分割效果。可视化有两种模式可视化指定序列例如你想看看模型在序列16上的分割效果。python main_SemanticKITTI.py --gpu 0 --mode VIS --test_area 16随机可视化从数据集中随机挑选一些片段进行可视化。python main_SemanticKITTI.py --gpu 0 --mode VIS运行命令后会弹出一个三维可视化窗口。原始的彩色点云通常是基于反射强度着色会被渲染出来同时模型预测的语义标签会以不同的颜色叠加在点上例如红色代表汽车蓝色代表行人绿色代表植被。你可以用鼠标拖拽旋转视角用滚轮缩放。一个非常实用的小技巧在可视化窗口激活的状态下按下键盘上的H键终端里会打印出完整的操作帮助信息包括如何切换显示模式如只显示预测、只显示真值、或同时显示、如何截图、如何调整点的大小等。这个功能对于深入分析模型在哪些场景下表现好、哪些场景下容易出错有极大的帮助。我经常通过可视化发现模型在远处的小物体如行人或者类别边界模糊的区域如停在路边的卡车与建筑物的交界容易出错这为后续的算法改进提供了直观的方向。7. 踩坑记录与进阶建议走完整个流程你可能会遇到一些我未曾提及的问题。这里我分享几个常见的“坑”和对应的解决思路以及一些可以尝试的进阶操作。常见问题排查“No module named ‘tf_ops’” 或类似导入错误这几乎肯定是自定义操作编译失败或路径不对。请回到第3.3步仔细检查编译过程是否有错误输出并确认生成的.so文件是否在正确的目录下且Python能够找到它们通常脚本里已经设置了路径。训练时内存/显存溢出OOMSemanticKITTI数据量极大即使体素化后也很大。如果出现OOM错误尝试在helper_tool.py或训练命令中减小batch_size例如从6减小到4甚至2。同时确保你的交换空间swap足够大以应对内存高峰。评估或可视化时找不到模型确保你运行评估或可视化命令时指定的--test_area和训练/评估的设置一致并且results目录下存在对应的模型快照文件.ckpt文件。Open3D可视化窗口无法弹出或闪退这可能是由于服务器环境没有图形界面或者Open3D的版本与系统图形驱动有兼容性问题。对于服务器环境可以考虑将预测结果保存为文件如PLY格式然后下载到本地用MeshLab等软件查看。也可以尝试安装open3d-python的其它版本。进阶尝试与调优建议调整超参数体素化的grid_size、网络训练时的learning_rate、batch_size、max_epoch等都是可以调整的超参数。你可以设计一些小实验看看它们对最终mIoU的影响。例如适当减小grid_size可能提升细节分割能力但会大幅增加计算负担。尝试其他数据集RandLA-Net的代码框架具有一定的通用性。你可以尝试将其应用到其他点云数据集如S3DIS室内场景数据集上这需要你按照类似的格式准备数据并修改数据加载部分。代码解读与修改如果你不满足于仅仅复现可以深入阅读network.py等核心文件理解随机采样Random Sampling和局部特征聚合Local Feature Aggregation模块的具体实现。甚至可以尝试修改网络结构比如加入注意力机制或者替换不同的聚合函数来提升性能或适应自己的任务。使用PyCharm进行项目管理对于这种大型项目使用IDE如PyCharm Community Edition来管理代码、设置解释器环境、调试运行会高效得多。你可以在Ubuntu Software中直接安装PyCharm然后将RandLA-Net-master文件夹作为项目打开并将解释器设置为之前创建的randlanetConda环境。这样你可以方便地进行代码跳转、断点调试和运行配置。