尧图精选

激光雷达+语义分割:动态障碍物祛除的数据链路全解

🕒 发布时间:2026/9/15 7:54:12 📁 来源:尧图网络
移动机器人和自动驾驶领域做高精地图、静态建图或者多帧点云配准的朋友应该都对“动态障碍物”这几个字有切肤之痛。去年我在做园区低速无人车的建图方案时激光雷达扫描到的行人、自行车、过往车辆直接让原本干净的路沿和墙面变成了半透明的“鬼影”体素地图切出来全是拖出来的轨迹残影。后来换了一套“视觉语义分割引导激光点动态障碍物祛除”的流程效果立竿见影。作为这个系列的第一篇我先把数据侧的东西掰开揉碎讲清楚传感器怎么布、标定怎么做、训练数据怎么攒、点云怎么和图像像素对齐以及这一路上的坑在哪里。这篇内容适合正在做多传感器融合感知、机器人建图、以及SLAM前后端优化的工程师。如果你是刚入门的学生也能从中拿到一套可以照着配的数据处理基线。整个方案不需要多么昂贵的计算平台核心是数据链路要对、时间同步要准、分割模型类别要覆盖你要祛除的动态物体最后把“图像Mask”变成“点云Filter”这一步才算真正闭环。1. 项目思路拆解视觉语义分割到底怎么帮激光雷达“祛魅”1.1 动态障碍物为什么必须祛除先说一个容易被经验不足的开发者低估的事实动态障碍物对建图的污染不是简单叠加几条杂线而是会把错误信息扩散到后端。我见过不少团队一开始觉得“只要点云够密、配准够多帧动态点抹掉就行”。实际情况是动态目标本身带有轮廓和反射强度这些点在帧间匹配时会被当成可靠的几何约束参与位姿图优化。于是建图轨迹被“拉着走”墙被拉歪柱子被拉斜最后整个地图出现局部扭曲这个时候你再去抱怨前端里程计精度低其实已经入了动态点干扰的局。动态障碍物祛除望文生义就是把这些动态目标对应的激光点从点云中剔除让后续的建图、定位、导航处理的都是“静态世界”信息。祛除的方式有很多种按依赖信息分大致三类纯几何方法比如前后帧点云差分、占用栅格变化检测依赖物体确实在动并且场景没有太多重复结构。多视角几何极线约束比如DynaSLAM里的多视图一致性做法利用相机帧间的重投影误差判断动态区域但对光照变化和重复纹理比较敏感。语义引导方法先用视觉语义分割把图像上的行人、车、动物等动态类别像素标出来再将激光点投影到图像上打上语义标签凡是落进动态类别的激光点直接剔除。第三种方案的优势在于“一次训练到处剔除”不用管障碍物今天走得快还是慢、是直行还是转弯语义模型直接告诉你这是不是动态类别。它天然契合我要做的视觉激光雷达融合平台所以最终选它作为主线。1.2 视觉语义分割这条线路的取舍视觉语义分割负责回答“图像里的每个像素属于哪个类别”激光雷达则负责回答“空间里每个点的精确坐标”。两者结合的本质是把图像的高层语义抽象和激光雷达的精确几何测量焊在一起。选择这条路线首先是精度层面的考虑。纯几何差分在车、行人密集的主干道场景非常容易失效因为目标一直在可视范围内几乎没有“背景帧”可以对比而语义分割只要识别出这辆车就能直接把它对应的像素全部屏蔽。其次是部署层面的考虑现在稍好一点的工控机或者Jetson板子跑一个轻量级分割网络的实时推理性能已经足够不需要外接昂贵的GPU。当然代价也很明显。分割模型需要提前训练并且数据标注成本不低图像受光照、雨雾影响时分割精度会波动相机和激光雷达标定误差或者时间同步误差会让点云和图像像素错位出现“该删的没删、不该删的被删”的尴尬局面。因此真正做好这件事60%的精力要花在数据和标定上剩下40%才轮得到网络和策略。1.3 数据篇要啃的三块硬骨头既然标题叫“数据篇”那这篇的核心任务非常明确——为后续的模型训练、点云投影和动态剔除打好数据基础。我把整个数据链路拆成三块缺一不可。第一块是传感器数据本身的质量相机图像、激光雷达点云、惯性测量单元的数据都要有精确的时间戳和空间外参否则后续所有投影都是白算。第二块是语义分割模型的监督数据要么选好公开数据集做预训练和微调要么自己采集数据做标注这里涉及类别定义、标注规范和工具选型。第三块是投影数据对的构造与验证数据也就是激光点和图像像素的对应关系、深度缓冲区、可视化校验结果这步决定动态点剔除命令下得准不准。下面每一节我按这个顺序展开直接写当时验证过的参数和处理细节。2. 数据采集与传感器同步数据质量的地基2.1 传感器选型与坐标系关系我用的平台是一台园区巡检车传感器布局很简单一个前视工业相机装在挡风玻璃内侧偏上位置一个16线机械式激光雷达装在车顶前方另外有一个高精度组合导航提供姿态基准。相机和雷达之间的空间关系通过联合标定得到。这里提醒一句传感器选型会直接影响你后续标定的难度。我当时选的是分辨率1920×1080、全局快门的相机低照度表现尚可激光雷达是16线的虽然密度比64线、128线低不少但用于城市园区场景的建图足够。选全局快门主要是为了减少车辆颠簸或高速运动时图像行间畸变——卷帘快门在车运动时会把静止物体拍成斜的投影到点云上就是一层系统误差。坐标系上至少有四套需要理清激光雷达坐标系、相机坐标系、图像像素坐标系、车体/组合导航坐标系。日常处理时我习惯先把所有传感器外参统一到车体坐标系下再单独维护一个“雷达→相机”的变换矩阵。这样后续加IMU、加轮速计都方便。这里不放全量矩阵推导但你在做数据同步前必须把下面这组关系写进配置文件里并反复确认T_base_lidar雷达坐标系到车体坐标系外参T_base_cam相机坐标系到车体坐标系外参T_cam_lidar T_base_cam^{-1} * T_base_lidar雷达坐标系到相机坐标系的变换也就是投影用的外参矩阵2.2 联合标定实操要点雷达和相机联合标定数据采集环节的核心目标是拿到足够多、分布足够均匀的标定板观测。我当时用的是带有黑白棋盘格的平面标定板面积建议不小于1米×1米这尺寸在16线雷达下能有十几条扫描线打在板上角点拟合才不容易飞。实际操作我分成五步固定车辆打开数据采集程序同时录制图像、点云和IMU原始数据。把标定板放在车前方不同距离、不同俯仰和侧倾角度保证板子在图像里完整可见同时雷达扫描线能覆盖板面的大部分区域。每换一个位姿保持整车静止3到5秒便于抽取稳定帧。采集大约15到20个位姿然后离线处理用OpenCV检测棋盘格角点再从点云中拟合标定板平面通过PnP求解初始外参。用非线性优化对外参做精调目标函数是“标定板平面上的角点三维坐标投影到图像后与图像检测角点之间的重投影误差最小”。我在这一步踩过的最大坑是只放远距离板子导致角度约束不足外参在Y轴方向有零点几度的偏差。后来改成近处、中距离、远处各放几组并把标定板稍微旋转一个角度再采一组最终重投影误差从4个像素左右降到了1.5像素以内。对16线雷达来说这个精度已经能让远距离的点云投影到图像上基本贴合物体边缘。如果你不想自己写标定流程现在开源工具也很成熟比如Autoware的Calibration Tools、lidar_camera_calibration这类ROS包基本流程都是采数据、提特征、优化求解。区别在于开源工具对位姿数量和质量的要求更高我建议把“多距离、多角度、板面完整可见”作为铁律。2.3 时间同步比标定更容易被忽略的误差源空间外参对齐之后最影响数据质量的就是时间同步。相机和雷达如果各用各的时钟哪怕只差50毫秒在车速30km/h的情况下同一个物体在图像和点云里就会错开零点四米左右。这个错位直接导致你分割出的“车”的像素Mask和雷达扫描到的“车”的点完全不重合祛除逻辑根本没法用。我用的方案是硬件同步为主组合导航输出PPS脉冲和GPRMC时间戳相机采用外部触发模式激光雷达也接收PPS同步信号所有传感器都以同一时刻为基准。整条采集链路里每个传感器消息都打上主控统一维护的时间戳ROS的message_filters再用ApproximateTime同步策略做近邻匹配。如果你的设备不支持外部触发退而求其次的软件同步也能做但必须额外加一个“时间戳插值”步骤。做法是在处理点云时用当前图像时间戳附近两帧雷达数据按时间比例线性插值出关键帧的点云位姿。这个方案精度逊于硬件同步但至少能消除明显的时间跳变。判断同步是否正常的办法很朴素把车辆停在路边让一个行人贴着车侧走过同步录制图像和点云然后在可视化窗口里同时播放。如果行人脚底的位置始终压在他头顶那几根扫描线上说明同步基本没问题如果人的图像和点云一前一后“跟着游”那就是时间戳偏差大了。3. 语义分割模型的训练数据从哪来3.1 公开数据集怎么选、怎么用动态障碍物祛除需要分割模型认出来的类别主要是行人、自行车/摩托车、小汽车、卡车、公交车以及可能出现的动物或婴儿车。如果纯靠自采数据从零训练成本太高。合理路径是用公开数据集预训练再用自采数据微调。做得比较顺的公开数据集方案如下表数据集传感器特点标注内容是否适合预训练Cityscapes车载相机城市道路为主像素级语义分割19类适合分辨率高、标注质量高KITTI相机激光雷达目标检测、分割等视觉分割标注较粗适合做迁移验证且自带点云BDD100K车载相机道路多样语义分割、可驾驶区域等适合补充夜间/雨天场景Mapillary Vistas全球各地街景像素级分割类别丰富分类很全但部分类别粒度偏细SemanticKITTI激光雷达点云语义标注点云逐点类别不能直接训练图像分割但可用于点云先验/验证我自己的经验是先拿Cityscapes训练一个基础模型再把目标场景的图片拉进去做半精度微调。公开数据集里的场景和你的园区环境差异越大微调的重要性越高。比如Cityscapes里基本没有园区常见的锥桶、地锁、机械臂如果不额外补样本模型很可能把它们当成背景结果全被保留成了“静态障碍物”。3.2 自采数据标注规范与工具自采数据标注是数据篇里体力活最多的一环也是决定分割模型上限的一环。如果不打算雇标注团队那务必要制定一套精简的类别清单别一上来就学Cityscapes搞19类。你要做的只是区分“动态”和“静态”所以类别建议控制在6到8个背景/道路/建筑/植被等静态类别行人两轮车含自行车、电动车、摩托车小汽车卡车/大客车其他动态物如动物、移动锥桶可选这样标注员或者你自己需要判断的语义范围很窄标注速度会快很多模型的类间混淆也小。标注工具我用过LabelMe、CVAT还有国内常用的精灵标注助手。个人最推荐CVAT因为它支持在线协同、自动分割预标注、关键帧插值对连续视频帧做语义分割标注能省一半时间。预标注功能特别适合我们这个场景先用已有的公开模型对视频帧跑一遍伪Mask再把明显错误的类别修正掉。标注规范有几条要写进文档里防止前后标准不一致被遮挡超过50%的目标可以不标避免模型被极不完整的轮廓误导。车窗玻璃、透明雨伞这类带通透性的物体语义标签应该覆盖整个可见区域包括透明部分因为激光雷达很可能打到透过玻璃的内部结构。动态目标离开画面边缘时只标画面内可见部分不要脑补轮廓。雨天、逆光、夜间帧单独保存并标注作为困难样本补充训练。3.3 类别定义直接影响动态祛除上限很多人忽略一个点动态障碍物祛除的上限其实在类别定义时就定死了。如果类别清单里没有“卡车”那就算分割模型能完美区分所有像素卡车对应的激光点也依然会留在点云里。这一点和“清理”很像扫把扫得再快如果垃圾筒没对准效果也是零。所以我在设计类别时会把“车辆”拆成小汽车、卡车/大客车、两轮车而不是全合并成“车”。拆开的好处是后处理里可以针对不同类别做不同策略。比如小汽车在停车场里经常处于静止状态静态车对建图来说是有效特征但巡检场景里路上跑的车基本都是动态直接全删问题不大。这时候如果只有一个“车”的类别你就没法区分“停着的车”和“开着的车”只能连静态车一起删损失地图特征。针对这类问题我的做法是在语义Mask的基础上叠加一个“运动先验”连续帧中同一语义实例的位置变化超过阈值才认定它是动态物体否则保留其点。这样语义分割只负责“认出这是车”运动状态由多帧几何信息判定两者配合既不会误删路边停靠车辆又能把真正跑动的车祛除干净。这个逻辑放到了后面的数据预处理管线里效果比单纯删除所有“车”类像素要好得多。4. 激光点与图像像素对齐的完整实现4.1 三维激光点投影到二维图像的公式推导数据链路从“图像分割”到“点云剔除”中间最关键的环节就是把三维激光点投影到二维图像上。这个投影本质上是坐标系的连环变换雷达坐标系→相机坐标系→图像坐标系→像素坐标系。第一步把雷达点从雷达坐标系变换到相机坐标系P_cam T_cam_lidar * P_lidar其中P_lidar是齐次坐标形式的三维点T_cam_lidar是4×4外参矩阵包含旋转和平移。这一步做完所有点都处于“以相机光心为原点Z轴指向前方”的相机坐标系下。第二步用相机内参矩阵K做透视投影。设K为K [[fx, 0, cx], [0, fy, cy], [0, 0, 1]]那么像素坐标(u, v)满足u fx * X_cam / Z_cam cx v fy * Y_cam / Z_cam cy注意这里一定要判断Z_cam是否大于0。Z_cam小于等于0的点位于相机后方投影出来会产生镜像必须过滤掉。4.2 投影代码与深度缓冲过滤投影本身不难难在投影之后怎么处理遮挡。如果直接把所有雷达点都投到图像上画Mask会出现一个严重问题车道后方被前车挡住的地面点也会投影到前车的像素范围里因为雷达和相机视角存在视差。这时候如果按照“投影像素落在动态Mask里就删点”的粗暴逻辑你会把一面很远的背景墙当成动态点给删了。解决办法是加一个深度缓冲区也就是“Z-buffer”。对所有投影到同一像素位置的激光点只保留距离相机最近的点的语义标签其余点视为被遮挡不参与判断。从代码层面看大概是这样import numpy as np # points: (N, 3) 雷达坐标系坐标 # T_cam_lidar: (4, 4) 外参雷达坐标系 - 相机坐标系 # K: (3, 3) 相机内参 def project_points_to_image(points, T_cam_lidar, K, img_size): H, W img_size n points.shape[0] ones np.ones((n, 1)) pts_lidar_h np.hstack([points, ones]) # (N, 4) pts_cam (T_cam_lidar pts_lidar_h.T).T # (N, 4) X pts_cam[:, 0] Y pts_cam[:, 1] Z pts_cam[:, 2] valid Z 0.1 # 过滤相机后方和过近点 X, Y, Z X[valid], Y[valid], Z[valid] u K[0, 0] * X / Z K[0, 2] v K[1, 1] * Y / Z K[1, 2] u np.round(u).astype(int) v np.round(v).astype(int) # 初始化深度图和索引图 depth_buffer np.full((H, W), np.inf) index_buffer np.full((H, W), -1, dtypeint) for i in range(len(u)): if 0 u[i] W and 0 v[i] H: if Z[i] depth_buffer[v[i], u[i]]: depth_buffer[v[i], u[i]] Z[i] index_buffer[v[i], u[i]] i return u, v, valid, index_buffer, depth_buffer有了Z-buffer之后每个像素位置只会保留“从相机看过去最近的那个激光点”然后再判断这个点对应的语义类别就基本不会出现“前方动态车辆背后的地面点被误删”的错判了。这一步虽然简单但很多人第一次实现时都会漏掉建议直接写进代码里当固定流程。4.3 动态类别筛选与点云剔除策略图像分割模型输出的是一个逐像素类别ID图我们把投影后落在各个像素上的激光点取出来再按照类别ID查表就能判断该点是不是动态点。类别ID到“是否动态”的映射表我单列了一个JSON配置方便随时调{ static_classes: [0, 1, 2, 3, 4], dynamic_classes: [5, 6, 7, 8] }实际筛选时我的策略不是“动态类全删”而是“动态类点删除但保留两类例外”。第一类是距离过远的点当激光点距离超过60米时图像分割模型在该区域的精度已经很低而且远处的动态障碍物对建图影响有限删不删无所谓我选择直接保留避免远距离误删。第二类是带有强烈反射强度的点因为雷达打到车辆牌照、金属护栏时反射强度值会异常高这类点在后续配准中往往是很好的特征如果它不在动态Mask核心区域我会保留。剔除后的点云再送入后续建图模块前我还会做一次统计滤波把孤立点去掉。因为动态目标边缘和背景之间总会残留一些“半动态点”它们可能是分割边界模糊导致的误判也可能是部分遮挡时的残段。统计滤波对这类点有很好的平滑作用能让建图前端看到干净得多的输入。5. 数据质量评估与常见问题排查5.1 标定误差造成“点影分离”怎么发现标定误差在使用阶段非常隐蔽因为你不容易直接看到“错多少像素”。最直观的验证方法是把雷达点按深度着色后叠加到图像上肉眼判断物体边缘处点云是否贴合。行人站在车前2米时头部点云应该正好落在图像行人的头部车侧面的点云应该贴合车身腰线而不是漂浮在半空或嵌进车里。如果发现点云整体像图像某个方向偏移多半是平移外参不准点云在图像中心区域贴合、边缘区域发散多半是旋转外参不准点云随距离增大越来越偏基本可以确定是标定数据采集时姿态约束不够。这类问题只能回到2.2节重新标定别指望在后处理里硬调。5.2 时间戳漂移导致的拖影现象时间不同步的典型表现是“拖影”行人在图像里的位置已经变化了雷达点却还停留在几十毫秒前的位置于是行人点云在图像上形成一串拖出来的尾巴。这个现象在低速场景下不明显车辆或者行人动作一快就非常扎眼。排查思路是观察多组数据如果车辆静止时没有任何拖影车辆行进时拖影变重那基本是各传感器时钟漂移而非标定问题。解决手段优先修复硬件同步实在不行就在软件层给相机帧时间戳加一个固定延迟比如补偿20毫秒然后重新看拖影是否消失反复迭代到一个最小值。5.3 分割模型误检漏检的影响与兜底分割模型不可能100%正确误检和漏检都会传导到点云剔除结果上。漏检好理解没把动态目标像素分割出来对应激光点就不会被删动态残影留在点云里误检则更危险模型把背景当成动态目标墙面和地面点被大片删除地图直接出现“空洞”。兜底方案我用了三层。第一层在分割后处理上对Mask做连通域分析和面积阈值过滤小于一定像素面积的小块动态区域视为噪声不参与点云剔除。第二层在点云侧动态点必须满足“在语义Mask内且深度与最近像素深度一致”如果不一致就保留这能挡住不少由遮挡或投影误差引发的误删。第三层在时间维对连续帧做语义Mask的投票融合如果同一个像素区域只在某一帧被判为动态前后帧都是静态那这一帧的判定大概率是误检单帧不执行删除。5.4 评价指标与可视化调试工具做数据篇的收尾验证不能只看一两张图觉得“看起来还行”。我习惯用三个量化指标评估动态祛除质量指标含义参考目标动态点删除率被正确识别的动态激光点数 / 真值动态点数大于90%静态点保留率保留下来的静态激光点数 / 真值静态点数大于98%误删点占比被删除的静态点数 / 总删除点数小于5%真值怎么来选一段车辆静止、场景里只有一个行人来回走动的数据手动标出静态背景点再用标准流程跑一遍动态祛除对比前后点云就能算出上述指标。可视化调试工具方面我推荐先把投影结果保存成带alpha通道的RGB图动态Mask用红色叠加显示激光点按“保留/删除”用绿/红着色这样一帧一帧翻看视频很快就能定位到是哪一环节出了问题。6. 我个人最想强调的一次现场教训讲了这么多方法最后分享一个我自己栽过的跟头可能比前面所有公式都更有参考价值。项目测试到中期时我发现祛除效果忽好忽坏晴天上午测完一切正常下午推到一片树荫下动态车辆的点云就开始大量残留。排查了一整天才找到原因:树荫下光线骤降相机自动曝光把图像整体拉亮暗部噪声放大分割模型把深色车辆的部分像素误归到了“背景”类偏偏那台车正好是深灰色和柏油路颜色接近模型彻底“隐身”了它。这件事给我的教训是视觉语义分割对光照的敏感度远高于我的预期而数据篇里所有的采集、标注和同步工作如果不在设计阶段就把光照变化纳入考虑后面的算法再努力也是和物理极限硬碰硬。后来我在数据采集规范里写死了一条每个采集时段必须覆盖晴天、阴天、树荫、逆光、傍晚五个光照条件并按比例混合进训练集才彻底解决了同类问题。如果你正在做类似的多传感器融合项目我会建议你从第一天就把“光照变化”当做一个正式的数据维度来管理而不是等到模型上线后再补样本。数据篇的工程本质上就是老老实实把每一个会影响自动化判断的因素提前变成可控的输入。这个原则不只适用于动态障碍物祛除也适用于你手上任何依赖真实世界数据的感知系统。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →