ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LiDAR点云跨域分割:从64线到16线的UDA与结构改进实战

LiDAR点云跨域分割:从64线到16线的UDA与结构改进实战

跨域部署的翻车现场:64线模型遇到16线点云

先说一次让我印象很深的实测:模型是在64线LiDAR点云上训练的,源域验证mIoU做到68.2,换到一辆装了16线激光雷达的采集车上,没做任何适配直接推理,目标域mIoU掉到31.5。道路、建筑这些大目标还算完整,但行人、骑车人基本碎成噪点,护栏和低矮路沿几乎全丢。那一刻我意识到,Road-Object Segmentation的模型结构再花哨,跨传感器、跨场景部署时,Unsupervised Domain Adaptation不是可选项,而是刚需。

这篇文章想把我在这个方向上的完整思路记录下来,主要包括三件事:为什么模型结构要专门为UDA做改进;无监督域适应在LiDAR点云分割里的可用流水线长什么样;以及我在调参、复现、实测过程中踩过的一堆坑。内容偏工程实践,适合做自动驾驶感知、点云分割算法落地,或者正在调研UDA方案的同行参考。

1. 跨域部署的翻车现场:从68.2到31.5的差距是怎么拉开的

1.1 一次"习以为常"的跨设备测试

事情是这样的:项目组里同一套代码,同一个训练配置,在公开基准数据集A上训出来的模型,放到自采工业园数据集B上测试。两个数据集都是LiDAR道路场景,类别也都是道路、车辆、行人、骑车人、建筑那一套。我当时想的是,模型泛化再差,也不至于差到没法看吧。结果一跑验证,直接傻眼。

更让人头疼的不是整体指标,而是可视化结果。远距离行人被漏检,金属护栏被错分,卡车侧面被切成好几块。后来我把点云叠加到真值上一帧一帧看,发现模型在密集的近处点云上表现还行,一旦点云变稀疏,比如15米以外的行人,特征提取基本失效。这个现象不是单个类别的问题,而是所有小物体都中招。

后来我把64线采集车上的数据和16线采集车的同场景数据放在一起比,才意识到:所谓"跨域",最直观的表现就是点云密度和分布的巨大差异。64线在50米处的地面点间距可能只有几厘米,16线到了30米就稀疏得跟筛子一样。模型训练时没见过的这种稀疏模式,直接推理当然扛不住。

1.2 三类域差距:密度、统计分布与标注风格

很多文章把域差距笼统地说成"数据分布不同",但落到LiDAR点云分割上,我总结下来主要有三类:

  • 密度与距离衰减模式:不同线束LiDAR的垂直角分辨率不同,导致各距离区间的点密度差异很大。64线在近距离非常密集,16线在中远距离就已经稀疏到难以支撑局部几何特征。
  • 坐标统计与反射强度:不同设备的扫描范围、安装高度、旋转方式不同,点云的空间坐标统计特征差异明显。更隐蔽的是强度值,不同设备对同一材质物体的反射强度标定差异可能达到几十甚至上百,直接把模型搞懵。
  • 类别定义与标注边界:两个数据集即使类别名相同,标注规则也可能不同。比如"骑车人"里包不包含自行车本身,"行人"在遮挡时标注边界画在哪里,这些都能让同一模型在跨数据集时出现系统性偏差。

理解这三类差距,才能对症下药。我见过不少人一上来就套UDA方法,结果只解决了统计分布对齐,密度差异没管,效果自然上不去。

1.3 为什么不能靠"暴力重新标注"解决问题

有人会问,既然目标域效果差,那就重新标注一批目标域数据不就行了吗?道理是这个道理,但实际工程里行不通。

首先是成本。一帧64线LiDAR点云的语义标注,熟练标注员要花十几分钟到半小时,遇到密集场景甚至更久。一个工业园园区测试集就算只有2000帧,也是几百个工时,做完一次还能忍,做两次三次就完全扛不住。其次是迭代周期。算法每改一版,如果都要重新标注才能验证效果,那开发节奏基本废了。最后是数据获取的实时性。传感器升级、采集车换装、新场景测试,这些都是持续发生的事,靠标注去追永远追不上。

所以我当时的判断是:要么用无监督域适应让模型自己适应目标域,要么就接受"每次换传感器都重训一遍"的糟糕体验。既然目标是做能落地的系统,UDA这条路必须打通。

2. 模型结构改进:让网络在点云稀疏区与稠密区都能"看得准"

2.1 主干选择:单一结构在密度剧烈变化下不可靠

先交代一下背景。我最初用的是业界非常成熟的单分支点云分割网络,靠3D稀疏卷积在体素化后的点云上做特征提取,然后反投影到每个点得到语义类别。这套方案在源域测试集上表现稳定,因为源域数据集中,近处远处、密集稀疏的样本都相对均衡。但它有个致命弱点:体素化会丢失点级高频几何信息。

什么意思?体素大小比如设成0.1m,一个行人的点云被塞进几个体素里,如果密度不够,体素内的点可能只有一两个,稀疏卷积提取出来的特征就变成了"即兴发挥"。16线点云在中远距离恰恰就是这样。单靠稀疏卷积主干去扛,等于在密集和稀疏两个极端之间走钢丝。

我在对比实验里发现:把输入点云人为抽稀到目标域密度水平后,单分支模型的mIoU直接从66.1掉到48.7,而双分支结构只掉了9个点。这个差距说明,面对密度变化,网络结构必须先稳下来,UDA后续才能"接得住"。

2.2 双分支方案的选型逻辑与关键超参数

我最终采用的方案可以概括为"体素分支管结构,点分支管细节":

  • 体素分支:用稀疏卷积在体素空间提取局部结构和上下文语义,负责"看懂"建筑物轮廓、道路走向、车辆姿态这些宏观信息。这个分支对密度有一定容忍度,因为就算体素内点数少,只要局部几何形状还在,卷积核还是能学到模式。
  • 点分支:用MLP直接处理原始点坐标和邻近特征,保留每个点的精确空间信息。远距离稀疏点不会被池化稀释,点分支依然可以基于局部的少量点拟合几何。

两个分支在编码器中间层和解码器输出层各做一次融合。中间层融合用的是通道拼接加轻量注意力,输出层融合用的加权求和。这样低层的高频几何信息和高层的语义信息都能保留。

关键超参数我实测下来是这样:

体素尺寸0.15m比较均衡。0.1m体素在近距离表现好,但远距离体素空置率太高,浪费算力;0.2m体素对细长物体,比如路沿、护栏,形状表达不够。前后对比下来,0.15m是性价比最高的。

点分支的采样范围也很重要,我限制在半径1.5米的球邻域内。太大会把远距离点的特征"稀释"掉,太小则局部上下文信息不足。这个数值不是玄学,我是以行人点云的平均空间尺度为基准调的。

融合权重我采用了一种简单的自适应策略:让网络自己学每层两个分支的权重,初始值各为0.5。收敛后观察,近处点往往更依赖点分支,远处点反而更依赖体素分支,符合直觉。

2.3 损失函数:交叉熵只是基础,不能只有它

结构改了,损失函数也不能太随意。点云分割的类别分布天然不平衡,道路和建筑占了大多数,行人、骑车人只有一点点,普通交叉熵会被大类别把梯度淹没。

我用了加权交叉熵加Lovász-Softmax的组合。加权交叉熵负责把少量类别拉起来,Lovász-Softmax直接优化IoU相关的代理损失,对整体mIoU的提升比较友好。权重方面,我按类别频率的倒数做了截断,最小权重不低于0.1,最大不超过3.0。为什么不直接把稀有类别权重拉到10?因为太高的权重会让模型在小类别上过拟合,在源域验证集上漂亮,跨域时反而更快崩掉。

提示:做UDA任务时,损失函数选择不要只看源域mIoU。一个在源域上"过拟合到完美"的模型,目标域上往往更弱,因为特征容易被源域特有模式绑架。

2.4 为什么"先改结构,再谈域适应"是对的

我最初也走错过方向,先不管结构,直接套了一个当时很火的对抗式域适应模块,结果目标域mIoU只从31.5涨到34.2,非常有限。后来仔细分析才发现,模型在稀疏区域连基本几何特征都提取不出来,你让它做域对齐,它对齐的其实是一堆噪声特征。

换个顺序做实验:先把双分支结构加上,不引入任何域适应,目标域mIoU直接从31.5涨到43.8。这时候再做域对齐,才能再往上走。这个经验让我确信:结构是地基,UDA是上层建筑,地基不稳,上层再花哨都白搭。

3. UDA流水线:先用"保守伪标签"把学生模型扶起来

3.1 整体流程:源域预训练、目标域伪标签、交替微调

模型结构确定之后,UDA流水线本身我也走了好几轮迭代,最后稳定下来的是一个偏传统的自训练框架,外加轻量化的特征约束。流程分四步:

  1. 源域预训练:用标注数据训练一个初始模型,不追求极致精度,但要保证基本的类别区分能力。
  2. 生成伪标签:用预训练模型推理目标域点云,每个点得到各类别置信度,按阈值和规则生成伪标签。
  3. 联合微调:把源域标注数据和带伪标签的目标域数据混合,一起训练一个"学生模型",一般迭代两到三轮。
  4. 可选特征约束:在最后几轮加入特征统计对齐约束,抑制目标域逗留的分布偏移。

这个流程的关键是"保守"。伪标签只保留高置信度的点,宁可漏标也不引入噪声。UDA最大的风险不是伪标签太少,而是伪标签里的系统性错误会被模型当作真理学进去,越学越偏。

3.2 伪标签的获取不是"置信度大于某值"这么简单

很多人写UDA代码,伪标签就是confidence > 0.95,全部mask保留。但LiDAR点云分割跟2D图像不一样,类别之间天然有几何混淆关系。

我举几个典型的例子:

  • 行人跟骑车人:在点云里,行人和骑车人的几何外形很接近,尤其是当骑车人身体部位遮挡严重时,模型几乎分不清。置信度0.9以上的伪标签也可能错。
  • 护栏跟墙体:如果点云密度低,护栏的稀疏点在体素化后表现成一片平面,非常容易被当成墙体。
  • 地面跟低矮植被:地面点的局部特征和低矮灌木很像,尤其在点云稀疏时。

所以我最终放弃"统一阈值"方案,改成"类别自适应阈值加几何先验约束":

车辆、建筑、地面这些大类,置信度阈值拉到0.95以上;行人、骑车人、摩托车这些易混小类,阈值放宽到0.85,给模型更多机会学到目标域的形态变化。然后加一层几何约束:地面点的z值必须在局部邻域内连续,行人点的高度范围必须在合理区间内,否则即使置信度达标也丢弃。

3.3 伪标签不是越自信越好:把"标错"的点识别出来

还有一类问题容易被忽视:空间分布。目标域点云不同区域,模型置信度分布差异很大。近距离点云特征丰富,伪标签置信度高;远距离点云特征稀疏,置信度普遍低。如果按全局置信度阈值直接过滤,远距离物体几乎没有伪标签,学生模型在远距离上又变成"睁眼瞎"。

我的做法是分距离区间设置阈值。0到20米用0.9,20到40米用0.8,40米以上用0.7。虽然远距离的伪标签噪声更大,但不给样本,模型对远距离就完全没有学习信号。测试下来,远距离类别IoU的提升比“一刀切”方案多出约5个点。

3.4 用EMA模型当伪标签生成器,比在线模型更稳

自训练框架里,一个很容易踩的坑是:学生模型如果直接从当前轮次模型生成伪标签,很容易陷入"模型偏误自我强化"的恶性循环。这个轮次模型把某个错分的类别当作正常,下一轮它继续用这个错误伪标签训练自己,越错越深。

我的解决方案是用指数滑动平均模型(EMA)。每当学生模型更新一步,EMA模型就按0.99的衰减因子更新。伪标签始终由EMA模型生成,学生模型只负责学习。这样即使学生模型偶尔走偏,EMA模型还保留着更接近源域分布的"记忆",伪标签不会跟着一起崩。

3.5 特征域对齐:把它放在最后,别一开始就加

很多对抗式UDA方法强调从一开始就加域判别器,让特征"域无关"。我的实测结论是:在语义分割任务里,初期就强推域对齐会伤语义表达。原因很简单,分割需要的是语义判别性特征,而域判别器要求特征无法区分数据来源,这两个目标早期是冲突的。

打个稍微生活化的比方:你让一个刚学会区分猫狗的孩子,同时要求他"别去听动物声音来自哪个录音设备",如果两个任务同时学,他会连猫狗都有些分不清。先学会区分,再让特征对设备不敏感,顺序很重要。

我在实验里试过两种方案:第一种从第一轮就加对抗域判别器,第二种等第三轮才加,最终目标域mIoU相差了3.4个点。所以我把域对齐模块的启用时机放在了自训练已经收敛之后,并且梯度反转层的权重设在0.01这个很小的量级。

4. 调参日志与踩坑记录:从42.3到57.1的完整链路

4.1 一个反向下降案例:域判别器加太强反而掉点

有一轮实验特别值得复盘。我给流程加上了对抗域判别器,梯度反转权重开到0.1,目标域mIoU不升反降,从52.8掉到50.1。一开始我以为是随机性,重跑两次还是差不多。后来把特征可视化出来才发现,域判别器太强的时候,模型把某些本可以用来区分类别的方向性特征也"对齐"掉了。

最典型的是路沿与低矮墙体这类由空间朝向区分的物体。域判别器把两个域的这些特征强行拉到一起,反而把类间差异给磨平了。后面我把梯度反转权重降到0.01,并且只在网络后三分之一层级施加约束,效果才稳住,最终反而比不加判别器时高出2.3个点。

提示:域对齐不是越彻底越好。分割任务的标签空间是语义,域对齐操纵的是特征空间,两者必须找一个平衡点,而不是"非黑即白"地追求特征完全不可分。

4.2 置信度阈值与类别不平衡的拉锯战

伪标签的置信度阈值,我做了几组对比:

阈值策略目标域mIoU有效伪标签点数(相对)现象
全局0.9551.268%行人骑车人几乎没伪标签
全局0.849.892%噪声太多,模型被带偏
类别自适应0.85/0.9555.781%大类别稳,小类别有收益
类别自适应+距离分段57.179%远距离小物体终于有提升

注意第三和第四行差了1.4个点,这个提升主要来自远距离伪标签的加入。代价是噪声也更多,但对分割任务来说,"有噪声的监督信号"远好过"没有监督信号",前提是噪声占比不超过一定上限。

4.3 四个反复踩的工程坑

第一个坑是强度归一化。不同LiDAR的反射强度标定差异极大,我的源域模型几乎完全依赖强度值来区分金属栏杆和树木。目标域强度分布一变,这个线索直接失效。我后来强制把源域和目标域的强度值分别做分位数归一化,再作为输入特征,效果立刻改善。这件事必须在数据加载层面解决,不要在模型里靠学习补救。

第二个坑是地面点处理。有人喜欢先用地面分割算法把地面点去掉,再送入分割网络。这种做法在密集点云上效果好,但16线点云地面分割本身就不稳,一旦把地面点误删,道路区域就会变成"空洞"。我是反过来,保留所有点输入网络,让模型自己学地面模式,反而更稳。

第三个坑是评估指标虚高。自训练微调后,目标域mIoU涨了不少,但是我单独抽查了200帧目标域数据,用人工标注真值去算IoU,发现比伪标签评估低大概3到4个点。原因很简单:伪标签生成的评估集带有模型自身的偏误。如果模型在"行人"这个类别上误判为"骑车人",而伪标签也这么标,那评估时这两个类别都"恰好正确"了,实际却是错的。所以UDA实验里,必须留一部分人工标注的目标域数据做最终评估。

第四个坑是时序数据必须打乱。我一开始直接用连续帧序列做伪标签自训练,结果模型mIoU看着很高,换到验证集直接崩。原因很直观:相邻帧点云高度相似,伪标签也高度相似,模型学到了"复读机"模式,根本没有泛化到帧间变化。把序列数据充分打乱混合后,性能才回归正常。

4.4 从42.3到57.1我是靠哪些决策一步步走到的

我把整个优化过程拆成里程碑,方便大家对照参考:

阶段操作目标域mIoU
起点单分支模型,无UDA31.5
结构改进双分支模型,无UDA43.8
伪标签两轮类别自适应阈值+距离分段55.7
域对齐模块梯度反转0.01,最后两轮启用57.1

注意,从43.8到55.7这11.9个点的提升,主要来自自训练。再加域对齐只贡献了1.4个点。这不是说域对齐没用,而是说明在自训练已经效果很好的情况下,域对齐的边际收益在变小。如果你算力或时间有限,优先做结构和伪标签自训练,性价比最高。

5. 实测效果与理性判断:哪些涨点是真实的,哪些只是"数字游戏"

5.1 分类别涨跌表现:大物体大涨,小物体原地踏步

看整体mIoU容易产生错觉,好像UDA让所有类别都变好了。实际上分类别看,差异非常明显:

类别初始结构改进后完整UDA后
道路62.371.878.5
建筑54.666.372.0
车辆60.169.475.2
行人18.728.531.4
骑车人21.327.229.8
护栏25.135.038.9

道路、建筑、车辆这些类别,UDA带来的提升非常可观,因为它们的伪标签置信度高,可学习样本多。行人、骑车人这种小目标,结构改进带来的提升比UDA更大,而UDA只贡献了2到3个点。护栏这类细长物体居中。

结论是:UDA解决的是"目标域可学习样本不足"的问题,但解决不了"模型本身对稀疏小目标捕捉能力弱"的问题。后者必须靠结构改进、数据增强、时序融合这些手段去补。

5.2 纯UDA的上限:不要幻想"零标注追平全监督"

我在实验中还做了一个对照:把目标域所有数据全部人工标注,用完整监督训练同结构模型,目标域mIoU可以到69.5。纯UDA最好成绩是57.1,差距还有12.4个点。

所以说句实话:在LiDAR道路物体分割里,纯无监督域适应可以让一个新场景的模型从"基本不可用"变成"能跑起来",但要追平完全重新标注,是不现实的。那些宣称UDA逼近全监督的工作,多半是在特定类别和特定场景下取得的,放到长尾类别上照样失真。

最理性的定位是:UDA用于冷启动,让模型先有一个可用的底子,再配合少量关键帧人工标注做修正,才是性价比最高的路线。

5.3 后续可以扩展的三个方向

如果时间充裕,我会在三个方向接着做:

一是多帧时序信息。LiDAR单帧点云对行人和骑车人的几何表达先天不足,多帧叠加可以显著提升小型物体的几何密度。如果UDA配合时序特征一致性约束,伪标签稳定性会进一步改善。

二是物理层面增强。比如模拟雨雾噪点、模拟低线束LiDAR的稀疏模式,在源域直接把目标域可能遇到的物理变化提前引入。这个思路可以降低域差距,让UDA更轻松。

三是半监督与UDA结合。我已经在私下实验里验证过:只标注目标域5%的关键帧,加上无监督域适应,目标域mIoU能从57.1直接拉到63.8。相比纯UDA的提升,这个收益几乎是"免费"的,也很贴近真实工程。未来如果条件允许,我会把这条路线继续推下去。

经历了这些实验,我最大的体会是:无论是结构改进还是域适应,本质上都是在跟"信息量不足"作斗争。源域模型掌握的规律,到了目标域变得不可靠,不是模型笨,而是它训练时没见过这些情况的变体。解决路径也很朴素:先把网络结构做得更耐受稀疏变化,再用保守的策略从目标域本身一点点收集可学习的信号。看起来没有特别惊艳的trick,但每一步踩实了,最后的效果反而很扎实。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进