Stixel World这个名字,在自动驾驶视觉感知圈子里不算新,但每次聊到“怎么把立体相机输出的深度信息,变成下游路径规划能直接吃的东西”,它总会被人提起来。我第一次看到那幅标志性的可视化效果图时,第一反应是:这不就是把稠密深度图压缩成了一排“竖直条形码”吗?后来自己动手复现、调参、接到跟踪模块里,才真正明白这种“看似简单”的表达,背后藏着一套非常完整的立体视觉建模逻辑。
这篇内容适合正在做ADAS、低速无人车、移动机器人感知的同学,也适合对立体匹配、深度估计有兴趣但被“点云+栅格”方案搞得头大的朋友。我尽量不堆公式,而是把Stixel World从“为什么存在”到“怎么落地”完整讲一遍,附带我在实车上踩过的坑和调参心得。
1. Stixel World到底在解决什么问题
1.1 现实痛点:稠密视差和实时性打架
先聊一个很现实的场景。你装一对双目相机,离线跑一帧SGM立体匹配,能得到一张还算不错的稠密视差图,单个像素都有对应的深度信息。可一旦放到车载嵌入式设备上,问题马上就来了:1024乘400左右的图像,稠密视差图本身就占掉一大块内存,每个像素还带着深度噪声、边缘毛刺、空洞;你要是让路径规划模块直接基于这种稠密视差去计算可行驶区域,算力开销和稳定性都吃不消。
我当时测试过一个方案:把视差图直接投射成伪激光雷达点云,再做栅格化。效果倒是直观,但噪声点非常多,地面上的一颗小石子都会变成一个栅格占用的“障碍物”。后续还得做一堆滤波、聚类、地面拟合,整条链路的调试成本远高于预期。这个经历让我意识到,稠密深度信息虽然“信息量最大”,但如果不加抽象,它就是一堆数据垃圾。
1.2 Stixel World是什么:一列有高度的“像素墙”
Stixel World的核心思想,就是把稠密视差图里那些杂乱的像素,按照极线方向(图像纵向)聚合成一个个竖直的条形单元。每个Stixel代表一个固定的图像列区间、一个纵向高度范围、一个平均深度值以及深度方差。换句话说,它假设真实世界里的障碍物(车辆、行人、墙、树干)在图像上都可以用“立在路面上的竖直平板”来近似。
这个假设并不完美,却能抓住自动驾驶环境感知中最重要的几何结构:物体在地面上方占据一定的竖直高度,具有近似的平面深度。它把原本几十万个像素的深度信息,压缩成几十到几百个Stixel,每一根Stixel都自带“下方确定路面、上方看到天空或背景”的语义暗示,这让下游模块在极低的算力下就能快速构建一条走廊级的可行驶区域。
1.3 为什么这种表达能“活”到今天
Stixel World最早提出的时候,深度学习还没有全面接管视觉感知。当年的算力水平和嵌入式平台,根本跑不起逐像素的语义分割,而激光雷达又贵得要命。Stixel World提供了一条极其务实的路线:用便宜的双目相机,加上轻量级后处理,产出稳定且可解释的环境结构表达。
到今天,虽然很多场景已经能用深度神经网络直接预测BEV空间下的障碍物,但Stixel World的生命力依然很强。原因有三个:一是不依赖巨大标注数据集,只要有立体相机标定参数就能工作;二是计算量极低,很适合对延迟敏感的实车决策;三是输出结构非常接近“规则”,规划模块可以直接把它当成几何约束条件,不用再单独训练一套感知头来解释传感器数据。在我接触过的几个量产预研项目里,Stixel World甚至被用来做深度学习输出的“几何安全网”,用于校验和兜底。
2. 把视差图“竖起来”:Stixel建模的完整思路
2.1 从稠密视差图出发
Stixel World的输入,严格来说不是图像本身,而是经过立体校正和匹配得到的稠密视差图。视差图里每个像素的数值,表示左右相机对应点的水平位移(以像素为单位)。有了视差d、基线B和焦距f,深度Z就能用Z = f*B/d算出来。
大多数公开实现里,这一步用的是半全局匹配(SGM),它比局部窗口匹配更稳,能在弱纹理和重复纹理区域表现更好,但代价是计算量高。实际工程中,很多方案会用GPU加速的SGM或者轻量级近似算法来跑视差图,Stixel算法本身是纯CPU友好的。它不关心视差图是怎么来的,只要你能给它一张质量尚可的视差图,它就能往下走。
2.2 用V-disparity分离地面前景和障碍物
这是Stixel World最值得一提的一步。经典的稠密障碍物检测,要么在视差图上做聚类,要么在3D点云上拟合平面,计算量都不小。V-disparity的思路很巧妙:把视差图按极线方向(v坐标)做累加,统计每一行中不同视差值的像素数量,形成一张二维直方图。
地面在图像中是一条从近到远变化的曲线,在V-disparity空间里,因为地面像素的视差值和图像行坐标存在固定的几何关系,它们会聚集在一条直线或平滑曲线上。你要做的就是在这张统计图上拟合出地面视差线,然后把地面以下、以及地面视差线附近的像素过滤掉。这一步跑得飞快,因为V-disparity的本质只是“按行统计直方图”,完全不需要聚类迭代。
2.3 极线投影、竖直分割与高度估计
地面分离之后,剩余的前景像素就可以开始聚合成Stixel了。具体做法是沿图像列方向扫描,对每一列做纵向的连通域分析。一个理想障碍物的像素,在深度上应该比较集中,在纵向高度上连续分布。算法会把这些像素看作一个候选Stixel,计算它的底部位置、顶部位置和平均视差。
这里有个工程上的关键点:不能简单地把所有前景像素直接连成一个大Stixel,否则紧挨着的行人和车辆会被合并成一面墙。比较靠谱的做法是,先按视差一致性把像素分成多个候选段,再对候选段做深度合并与分裂判断。很多开源实现里都包含“高度规则化”和“深度规则化”两项约束,前者避免Stixel高度突变,后者避免同一Stixel内部深度跳变过大。
2.4 后处理稳一稳:平滑、高度规则化
纯几何方法输出的Stixel World,噪声主要来自视差图的局部误匹配和地面拟合的小偏差。单一帧可能问题不大,一旦接上跟踪,Stixel的位置和高度会在连续帧之间抖得非常厉害。
后处理通常做三件事:一是对同类深度的相邻Stixel做纵向平滑,减少锯齿;二是检查每根Stixel的高度是否在物理合理范围内(比如行人0.8米到2.5米,车辆1.2米到3米),过滤掉明显离谱的“飞条”;三是与上一帧结果做简单的时间滤波,比如带权重的移动平均,让输出更稳定。我在实际项目中还加了一步:只保留底部纵坐标落在已估计路面附近的Stixel,这能显著减少树冠、天桥等高于地面的悬空物体带来的误报,虽然这会漏掉一些真实悬空障碍,但对常规道路场景来说值得。
3. 核心原理逐步拆解:从V-disparity到Stixel生成
3.1 输入和输出:一对图像换成Stixel列表
整条Stixel World链路可以用一个很明确的输入输出描述来概括。输入是双目相机的一对校正图像和相机标定参数,输出是为每一列(或每隔N列)生成的Stixel列表,每个Stixel包含以下核心字段。
| 字段 | 含义 | 下游使用场景 |
|---|---|---|
| column range | 该Stixel占据的图像列区间 | 目标检测、多目标跟踪 |
| bottom row | 障碍物底部在图像中的纵坐标 | 求障碍物到自车的距离 |
| top row | 障碍物顶部纵坐标 | 估算高度、判断是否可跨越 |
| disparity / depth | 平均视差或换算后的距离 | 可行驶区域生成、决策 |
| height | 由底/顶与深度换算的物理高度 | 障碍物分类粗筛 |
这套输出特别适合接“数值化”的下游逻辑,比如用Stixel的深度方差判断“这根柱子是否可信”,用连续N帧Stixel的横向位置一致性判断“它是不是静止物体”。
3.2 地面拟合:让算法先知道“哪里是路”
V-disparity最核心的数学关系,就是地面点的视差与图像行坐标近似线性相关。假设相机水平安装、俯仰角固定,那么图像中第v行的地面点视差d大约满足d ≈ a*v + b。这里的a和b由相机安装高度、俯仰角、焦距和基线决定。
在V-disparity图像上先按行累加像素数量,然后用Hough变换或RANSAC拟合一条直线段。因为真实道路会带坡度、起伏,有些实现会把地面模型扩展到二次曲线甚至分段线性模型。不要小看这一步:地面拟合错了,后续所有Stixel的底部边界都会错。我曾经把一条带减速带的路段跑得满屏幕Stixel飞条,后来发现地面线的斜率被减速带上的错误匹配带偏了整整几个像素,修正地面模型后立刻恢复正常。
3.3 Stixel的深度、宽度、高度与方差
每个Stixel的深度,不是简单取该列像素视差的均值,而是要做“稳健统计”。因为视差图边缘经常有错误极值,一个极小值或极大值就会把平均值拉得很离谱。比较稳的做法是取该列像素视差的中位数,或者先做范围裁剪,去掉前后若干个极值后再求均值。
Stixel的宽度通常与图像列分辨率一致,即每列生成一个Stixel,相邻Stixel再按深度相似度合并。高度则需要结合深度换算:物理高度H = Z * (top_row - bottom_row) / f,这个公式适用于近似垂直平面的物体。如果相机安装高度已知,还能直接利用Stixel底部纵坐标推算障碍物距离,这是Stixel World被大量用于“盲区预警”的关键原因——它不依赖目标检测,只要有一个竖直边缘,就能告诉你“前方多远有大物体”。
3.4 为什么不像点云或鸟瞰栅格那样用
实际项目中我还被很多人问过:已经有激光雷达点云和BEV栅格了,为什么还要Stixel?我是这样理解的。点云在稠密场景里非常吃算力,一个64线雷达,一帧几十万点,全量处理在低算力平台很难跑满实时帧率;BEV栅格则对投影误差敏感,物体在边界上稍微偏一下就出现“格子跳变”。
Stixel天然是“列对齐”的结构,这种结构和相机的极线几何绑定,不会因为扇形扫描密度不均产生奇怪空洞。而且在多模态融合中,Stixel能很自然地和毫米波雷达目标做“横向位置匹配”,因为我们可以直接把雷达目标的横向位置映射到图像列区间,从而找到对应的Stixel做融合决策。这比拿点云做最近邻检索要简单得多。
4. 从论文到工程:一套可复现的Stixel Pipeline
4.1 环境准备与数据获取
如果你想快速跑通Stixel World,最简单的方式是用某开源仓库的现成实现,配合公开的立体图像数据集。环境方面不需要很夸张的设备:一台有基础CPU的笔记本就够跑离线demo,如果要看实时效果,建议准备一颗桌面级GPU做SGM加速,CPU单独跑Stixel后处理。
数据方面,我推荐先找包含多种道路场景的立体图像数据集,包含晴天、雨天、夜晚、隧道等不同光照条件,这样在验证阶段能尽早暴露出地面拟合和深度噪声的短板。注意数据集必须提供双目相机的内外参数和左右图像矫正映射表,否则Stixel生成结果会直接偏掉。
4.2 核心模块走通:SGM视差到Stixel
整条pipeline我习惯分三块独立验证,别一上来就整个串起来。第一步先单独跑立体匹配,输出视差图,把它保存成图片或npy格式,人工看一下车内场景、地面材质、物体边缘是否清晰。第二步再做V-disparity可视化,这一步极其有价值:你能直观看到底是不是只有一条清晰的地面线,如果地面线模糊成一团,说明视差图质量不达标,直接回去调SGM参数,而不是在Stixel后处理里硬救。
第三步才把Stixel生成模块接上。导出结果时别只看最终的彩色可视化,要同时导出Stixel的文本日志,每根Stixel的列、底、顶、深度、高度都打出来。这样定位问题时,你能直接查“这一帧为什么在图像第400列多了一根深度只有2米的Stixel”,而不是盯着渲染图瞎猜。
4.3 参数调理:三个最常改的量
以我在某开源实现上的调参经验来看,有三个参数对结果影响最大。第一是V-disparity地面拟合的Hough阈值,它决定了地面线能容忍多少离群点;阈值设太高,地面拟合会变得很脆,一遇到桥面接缝就断;阈值设太低,地面线会被其他结构带偏。第二是Stixel的最小高度阈值,通常按像素距离来设,比如20个像素以下的前景段直接丢弃。这个阈值必须随图像分辨率缩放,我不建议写死。第三是深度一致性窗口大小,它决定一个候选Stixel内部深度允许变化多少,窗口越小,分割越细,噪声越敏感。
我自己的习惯是先用一组“保守大窗口”参数跑通,再逐渐缩小窗口直到出现碎条,然后回调到冒碎条前的临界值。这个过程其实比看文档更有用,因为不同相机的分辨率、基线长度不同,文档里的默认参数几乎不可能直接适合你的传感器。
4.4 输出可视化与下游衔接
Stixel的可视化很容易做得“好看”,红黄绿三色分别表示不同距离区间,或者用颜色编码深度。但真正重要的是把Stixel列表转成可供路径决策使用的几何约束。我在项目里做过一个很简单的转换:把Stixel World离散成一条“纵向栅栏带”,每一列读它的深度,凡是深度小于安全距离的列,就禁止规划轨迹通过。对于深度大于安全距离的列,不限制。
这样一个极简的“可行驶走廊”就能直接喂给纯几何规划的采样模块。实测下来,Stixel比单纯用单目深度估计生成的“深度图阈值”靠谱得多,因为单目深度图的尺度漂移会让你在高速路段频繁误刹车,而Stixel基于立体几何,尺度稳定,横向位置又按图像列对齐,空间逻辑非常干净。
5. 实战中绕不开的坑:参数、场景与传感器问题
5.1 地面弯曲和坡道,第一类翻车现场
Stixel World最经典的问题,就是在地道、驼峰桥、地下停车场坡道上失效。原因在于V-disparity如果默认拟合直线,只能表达平坦路面;一旦路面带明显坡度,地面视差线和直线模型误差变大,结果要么是大量Stixel被误删,要么是地面被当成障碍物。
处理办法有两个方向。一是把地面模型从直线升级成二次曲线或分段线性模型,让拟合更自由。二是引入低频惯性测量单元或车辆CAN信号里的俯仰角,直接补偿相机姿态变化,让V-disparity的地面先验始终准确。我建议优先做姿态补偿,因为二次曲线拟合虽然缓解了缓坡,但对突然的桥面接缝仍会失效,而姿态信息能从源头上稳住几何关系。
5.2 图像分辨率一变,所有阈值都得重调
另一个高频问题,是把算法从低分辨率图像换到高分辨率图像后,输出质量骤降。这通常不是算法坏了,而是阈值没有随分辨率缩放。例如Stixel高度合并阈值、V-disparity直方图分桶数,都依赖图像像素的绝对数量。
我的做法是写一个参数配置文件,把和分辨率相关的参数用公式表达,比如直方图分桶数是图像高度的0.5倍,最小Stixel高度是图像高度的3%。这样即使相机从720P换成1080P,参数也能自动跟着变,不用每次重新盲调。这对项目维护尤其重要,因为实车改相机型号太常见了。
5.3 光照和弱纹理,SGM先崩了Stixel就跟着废
Stixel World输出的好坏,很大程度上被上游立体匹配的质量锁死。夜间、雨天、雪地、白墙这种弱纹理场景,SGM会输出大量低置信度的视差值。特别是白墙,一整块墙面几乎没有任何纹理特征,SGM倾向于匹配成平滑的平面,这时V-disparity会把墙也当成地面,导致Stixel被大面积漏检。
面对这个问题,我的经验是引入置信度图参与Stixel生成。具体做法是在视差图的每个像素同时输出一个匹配代价得分,低于阈值的像素直接标记为“不可用”,不参与V-disparity统计和Stixel聚类。宁可少输出一些Stixel,也不要输出错误的Stixel,因为错误的障碍物信息在车里往往会造成更危险的决策。
5.4 遮挡和截断,让Stixel在图像边缘“撑不住”
当一个障碍物走到图像边缘,或者被另一个更近的障碍物遮挡时,它的Stixel会变得残缺。遮挡问题尤其麻烦,因为两个不同深度的物体在图像上重叠,甚至会挤在同一根Stixel里,深度均值就变成中间值,下游决策就会错判“前方障碍物不够近”。
我的应对方案是,在Stixel生成阶段强制检查“同一列Stixel的深度直方图”:如果深度直方图出现多峰,就说明存在前景和后景叠合,算法应果断返回较近的深度并标注“遮挡不确定”。下游模块可以给这类Stixel增加一个保守安全距离,当作更近的障碍处理。这个办法非常简单但有效,比在融合阶段再处理要省事很多。
5.5 时间滤波:不加会抖,加多了会延迟
Stixel是逐帧独立生成的,即使地面拟合非常稳定,相邻帧的Stixel高度和位置也会有轻微抖动。接视觉跟踪或决策模块之后,这种抖动量会被放大成路径规划的横向摆振。时间滤波能有效解决这个问题,但滤波强度必须谨慎调。
我踩过的坑是:用一个简单的一阶低通滤波平滑Stixel深度,结果弯道场景中,障碍物从侧向进入视野时,Stixel的深度响应明显变慢,导致自车已经快要绕行时,Stixel还“拖”在旧位置,差点触发紧急制动。后来我改为帧间匹配之后再滤波:先根据Stixel列位置和深度做最近邻关联,只对成功匹配的Stixel做滤波,新出现的Stixel直接用当前帧原始值。这样既稳定了连续帧,又不会让新目标变得迟钝。
6. Stixel之后的演进方向
6.1 与深度学习的结合:用CNN做地面分割和初步聚类
现在的趋势,已经不满足于纯几何的Stixel提取了。一个很自然的演进方向,是用轻量级语义分割网络或单目深度估计网络,先给每个像素打一个“地面/非地面”标签,把V-disparity的地面拟合环节替换掉。这样做的好处是,能利用语义信息识别出可行驶区域,而不仅依赖几何平整性。
我在实验里试过一种折中方案:保留V-disparity做地面假设,同时用一个很小的二分类分割头预测“路/非路”概率,两者输出一致时高度置信,不一致时以几何结果为主并标记低置信度。这种设计在弱纹理、强光照变化场景下能大幅减少误检,而且不会引入太高的算力负担。
6.2 让Stixel从“像素柱”升级为“实例柱”
传统Stixel对紧挨着的行人和自行车,往往合并成同一根柱体。如果能把目标检测的语义类别信息投影到Stixel上,就可以做到“一个Stixel对应一个实例”,再配合多目标跟踪,整个感知链路就会更自然。
实现上,不需要等目标检测结果出来再做投影,可以直接在Stixel生成阶段加入“极线方向上的类别一致性”约束,检测结果出来后用IOU或深度匹配来关联。这种“先几何、后语义”的融合顺序,能避免检测模型在远距离小目标上的漏检连累整个感知系统,因为几何Stixel本身就能正常工作。
6.3 从纯前向到多相机:环视Stixel的尝试
前向单目的Stixel,只能覆盖前方视野,这对城市低速掉头、泊车场景不够。我关注到一些团队已经在做环视Stixel:利用多个双目相机的视差图,先把每个相机视角下的Stixel提取出来,再通过外参变换投影到统一的车体坐标系。
这个方向难度主要不在算法,而在工程:多相机的标定、时间同步、曝光一致性,都会直接影响V-disparity的质量和Stixel的稳定度。一旦这些问题处理好了,环视Stixel可以直接替代部分超声波雷达和近距离传感器的功能,在城市低速场景里提供完整的360度近距离障碍物分布,算力消耗远低于多路点云融合。
一些个人的实际感受
把这套Stixel World从论文读到代码,再读到实车部署,我最大的感受是:它让我对“几何先验”和“数据表达”的关系有了全新的理解。深度学习时代,大家都默认“网络直接输出想要的一切”,但Stixel World提醒我们,把传感器的物理特性(极线几何、深度一致性)转化成结构化的中间表达,很多时候比堆模型更经济、更可靠。
如果你也想快速验证这个思路,我的建议是:不要一开始就追求“超越论文精度”,而是先把标准流程跑通,拿到可控的Stixel输出,再逐步替换其中的地面拟合、深度滤波、实例关联等环节。你会发现每一次替换,都是一次对立体视觉几何约束的更深刻理解。最后再分享一个小技巧:调试Stixel World时,记得把V-disparity可视化窗口固定下来,每次改动参数只跑同一段测试视频,对比地面线和Stixel密集度的变化趋势,这会比看一堆量化指标更直接地帮助你建立直觉。