
1. 从特征点法到光流法为什么视觉里程计需要另一条路视觉里程计的核心任务是根据相邻图像帧之间的像素变化推算相机自身的运动。这件事听起来简单做起来却有很多条路。最经典的一条路是特征点法提取关键点、计算描述子、匹配、然后求解位姿。这套流程在《视觉SLAM十四讲》的前几章已经讲得很透了但真正上手跑过代码的人都知道特征点法有几个绕不开的痛点。第一个痛点是耗时。ORB特征提取本身就不便宜一张640×480的图像提取上千个特征点再逐个计算描述子在普通CPU上动辄十几毫秒。如果图像分辨率更高或者场景纹理丰富导致特征点数量暴涨这个时间还会线性增长。对于需要实时运行的SLAM系统来说每帧十几毫秒的特征提取开销意味着帧率直接被压到三四十帧以下留给后端优化和建图的时间就更少了。第二个痛点是特征点分布不均。特征点法天然倾向于在纹理丰富的区域比如墙角、文字、树叶提取大量关键点而在纹理稀疏的区域比如白墙、天空、光滑地面几乎提不出点。这就导致位姿估计的约束集中在图像局部解算出来的运动估计在某些方向上精度很差。你可以做均匀化处理但那是额外的工程成本。第三个痛点是丢失了图像的大部分信息。一张图像有几十万像素特征点法只用了其中几百到几千个点剩下的像素信息全扔了。这在纹理丰富、光照良好的场景下问题不大但在弱纹理或者运动模糊的场景下特征点本身就提不出来整个系统直接歇菜。光流法走的是另一条路。它不提取描述子不进行特征匹配而是直接利用图像像素的灰度信息通过追踪像素点在时间上的运动来估计相机运动。你可以把它理解为特征点法是在每帧图像里找“ landmark”然后匹配这些 landmark光流法是在第一帧里选一批像素点然后在后续帧里追踪这些点跑到了哪里。追踪的过程不需要描述子只需要假设“同一个空间点在相邻帧里的灰度值不变”。这个假设就是灰度不变假设是光流法的基石。听起来很理想化但在实际场景中只要相机运动不是特别剧烈、光照变化不是特别突然这个假设在短时间间隔内是近似成立的。基于这个假设光流法可以做到亚像素级的追踪精度而且计算量比特征点匹配小得多。《视觉SLAM十四讲》在视觉里程计部分专门用一章讲光流法目的就是让读者理解除了特征点法还有一条更轻量、更直接的路可以走。这条路在后续的稀疏直接法、半直接法SLAM中都会被反复用到。理解了光流法你才能真正理解直接法SLAM的底层逻辑。这篇文章会从光流法的数学原理讲起然后拆解它在视觉里程计中的完整实现流程包括像素点选取、光流追踪、位姿求解、以及实际编码中会遇到的各种坑。我会尽量把每个步骤的“为什么”讲清楚让你不仅知道怎么调OpenCV的calcOpticalFlowPyrLK更知道它背后在算什么、什么时候会失效、失效了怎么救。2. 光流法的数学原理灰度不变假设到底在说什么2.1 从单像素到窗口Lucas-Kanade光流的推导光流法的数学推导其实不复杂但很多教程一上来就甩公式容易把人绕晕。我用一个更直观的方式来解释。假设在时刻 ( t )图像上某个像素点 ( (x, y) ) 的灰度值是 ( I(x, y, t) )。经过极短的时间 ( dt ) 后这个空间点运动到了新的位置 ( (xdx, ydy) )在时刻 ( tdt ) 的灰度值是 ( I(xdx, ydy, tdt) )。灰度不变假设说同一个空间点在不同时刻的灰度值不变。写成公式就是[ I(xdx, ydy, tdt) I(x, y, t) ]对左边做一阶泰勒展开[ I(xdx, ydy, tdt) \approx I(x, y, t) \frac{\partial I}{\partial x}dx \frac{\partial I}{\partial y}dy \frac{\partial I}{\partial t}dt ]两边消掉 ( I(x, y, t) )再除以 ( dt )得到[ \frac{\partial I}{\partial x}\frac{dx}{dt} \frac{\partial I}{\partial y}\frac{dy}{dt} \frac{\partial I}{\partial t} 0 ]记 ( I_x \frac{\partial I}{\partial x} )( I_y \frac{\partial I}{\partial y} )( I_t \frac{\partial I}{\partial t} )以及像素运动速度 ( u \frac{dx}{dt} )( v \frac{dy}{dt} )上式变成[ I_x u I_y v I_t 0 ]这就是光流约束方程。一个方程两个未知数 ( u ) 和 ( v )欠定。这就是著名的“光流孔径问题”单个像素的光流无法唯一确定因为沿边缘方向的运动看不出来。Lucas-Kanade的解法是假设一个窗口内的所有像素具有相同的运动。比如取一个 ( 5\times5 ) 的窗口里面25个像素都满足上面的方程那就有了25个方程未知数还是2个。用最小二乘法就能解出 ( u ) 和 ( v )。写成矩阵形式[ \begin{bmatrix} I_{x1} I_{y1} \ I_{x2} I_{y2} \ \vdots \vdots \end{bmatrix} \begin{bmatrix} u \ v \end{bmatrix} -\begin{bmatrix} I_{t1} \ I_{t2} \ \vdots \end{bmatrix} ]记作 ( A\mathbf{u} \mathbf{b} )最小二乘解是 ( \mathbf{u} (A^T A)^{-1} A^T \mathbf{b} )。这里 ( A^T A ) 是一个 ( 2\times2 ) 的矩阵它的可逆性决定了光流能否被稳定求解。如果窗口内的像素梯度 ( I_x ) 和 ( I_y ) 都很小比如白墙区域( A^T A ) 接近奇异解出来的光流就不可靠。如果梯度只在一个方向上有值比如纯边缘( A^T A ) 的条件数很大沿边缘方向的光流分量会很不准。这就是为什么光流法也需要选“好”的像素点来追踪。2.2 金字塔分层解决大运动的关键手段基本的Lucas-Kanade光流有一个致命限制它只能追踪很小的运动。因为泰勒展开是一阶近似只有当 ( dx ) 和 ( dy ) 足够小的时候才成立。如果两帧之间像素移动了几十个像素一阶近似直接失效光流追踪就会失败。解决方案是图像金字塔。把图像逐层降采样得到一系列分辨率递减的图像。最底层是原始分辨率上一层是降采样一半再上一层再降一半通常做3到4层。追踪的时候从最顶层分辨率最低开始算光流。在最顶层原本几十像素的运动被缩小到了几像素一阶近似成立。算出一个粗略的光流值后把这个值乘以2传到下一层作为下一层的初始值再在下一层继续优化。逐层往下光流值越来越精细最终在原始分辨率上得到亚像素级的追踪结果。这个策略叫由粗到精coarse-to-fine。OpenCV的calcOpticalFlowPyrLK内部就是干这个的你只需要指定金字塔层数maxLevel剩下的它帮你处理。实操心得maxLevel不是越大越好。层数太多最顶层的图像可能只有几十个像素细节全丢了反而影响追踪精度。一般3到4层足够覆盖大部分场景。如果你的相机运动特别快可以适当加到5层但要注意最顶层图像不能太小。2.3 正向-逆向光流如何判断追踪是否可靠光流追踪有一个天然的问题你怎么知道追踪结果是对的如果某个像素点被遮挡了、跑出图像边界了、或者光照突变导致灰度不变假设失效了追踪出来的光流就是错的。如果不加筛选地把所有追踪结果都扔进位姿求解错误的光流会严重污染解算结果。一个常用的可靠性检验方法是正向-逆向光流一致性检查。思路很简单从第一帧追踪到第二帧得到一个光流 ( \mathbf{u}{forward} )再从第二帧反向追踪回第一帧得到 ( \mathbf{u}{backward} )。如果追踪是可靠的那么正向和逆向的光流应该大小相近、方向相反即 ( \mathbf{u}{forward} \approx -\mathbf{u}{backward} )。如果两者差异很大说明这个点的追踪不可靠应该剔除。这个检查的代价是光流计算量翻倍但在精度要求高的场合非常值得。OpenCV本身不直接提供正向-逆向检查的接口需要你手动调用两次calcOpticalFlowPyrLK一次正向一次逆向然后比较结果。除了正向-逆向检查还有几个常用的筛选条件边界检查追踪后的点是否跑出了图像范围。光流幅值检查光流大小是否超过合理阈值比如超过图像宽度的三分之一过大的光流通常是误匹配。状态标志检查calcOpticalFlowPyrLK返回的status数组0表示追踪失败1表示成功。把这些检查组合起来可以过滤掉大部分不可靠的追踪点剩下的点再用于位姿求解精度会稳定很多。3. 光流法视觉里程计的完整实现流程3.1 整体架构从图像到位姿的每一步光流法视觉里程计的整体流程可以拆成五个步骤图像采集与预处理读取相邻两帧图像转灰度必要时做直方图均衡化或去畸变。像素点选取在第一帧中选取一批适合追踪的像素点。通常用Shi-Tomasi角点检测或者直接用FAST角点也可以手动指定网格点。光流追踪用Lucas-Kanade金字塔光流追踪这些点在第二帧中的位置。可靠性筛选剔除追踪失败、跑出边界、正向-逆向不一致的点。位姿求解用剩下的追踪点对通过2D-2D的对极几何或者3D-2D的PnP求解相机运动。这五步里第2步和第4步是光流法特有的特征点法里对应的是特征提取和特征匹配。第5步两者是共通的都是根据点对关系解算位姿。下面我逐个步骤拆解把每个环节的细节和坑都讲清楚。3.2 像素点选取什么样的点适合光流追踪光流追踪的质量很大程度上取决于你选了什么样的点来追踪。前面推导Lucas-Kanade的时候已经说了( A^T A ) 的可逆性决定了光流能否稳定求解。( A^T A ) 的特征值反映了窗口内像素梯度在两个方向上的强度。如果两个特征值都大说明这个点在两个方向上都有明显梯度是角点适合追踪。如果一个特征值大一个小说明是边缘沿边缘方向的光流不可靠。如果两个都小说明是平坦区域完全没法追踪。所以选点的原则很明确选角点不选边缘更不选平坦区域。OpenCV提供了goodFeaturesToTrack函数底层就是Shi-Tomasi角点检测。它的核心参数有两个maxCorners最多选多少个点。一般设100到300之间太多会增加计算量太少会导致位姿求解约束不足。qualityLevel质量阈值相对于最强角点的比例。比如设0.01表示特征值小于最强角点1%的点会被丢弃。这个值越大选出来的点越少但质量越高。还有一个容易被忽略的参数是minDistance两个角点之间的最小像素距离。如果不设这个参数角点会扎堆在纹理最丰富的区域导致图像其他区域没有约束。设一个合理的minDistance比如10到20像素可以让角点分布更均匀。实操心得goodFeaturesToTrack选出来的点仍然可能集中在某些区域。如果你发现位姿估计在某些方向上不稳定可以试试把图像划分成网格每个网格里单独选点强制让点分布均匀。这个技巧在纹理分布不均的场景下特别有用。除了Shi-Tomasi也可以直接用FAST角点。FAST的优势是快缺点是角点质量参差不齐需要额外做非极大值抑制。在光流法里因为后续有正向-逆向检查兜底用FAST也是可以的但Shi-Tomasi的追踪稳定性通常更好。3.3 光流追踪calcOpticalFlowPyrLK的参数怎么调calcOpticalFlowPyrLK是OpenCV里最常用的稀疏光流函数。它的函数签名比较长我挑几个关键参数讲。void calcOpticalFlowPyrLK( InputArray prevImg, // 前一帧图像 InputArray nextImg, // 后一帧图像 InputArray prevPts, // 前一帧的点 InputOutputArray nextPts,// 后一帧追踪到的点 OutputArray status, // 追踪状态 OutputArray err, // 追踪误差 Size winSize Size(21,21), // 搜索窗口大小 int maxLevel 3, // 金字塔层数 TermCriteria criteria ... // 迭代终止条件 );winSize决定了Lucas-Kanade在每一层用的窗口大小。窗口越大对噪声越鲁棒但运动假设窗口内所有像素运动相同越难满足而且计算量越大。窗口越小对局部运动更敏感但容易受噪声影响。21×21是一个比较通用的默认值。如果你的图像分辨率很高比如1080p可以适当加大到31×31如果分辨率很低比如320×240可以减小到11×11。maxLevel前面说过了3到4层是常规选择。层数越多能处理的运动越大但最顶层图像太小会导致追踪精度下降。criteria是迭代终止条件包括最大迭代次数和精度阈值。默认是30次迭代或者精度达到0.01。一般不需要改除非你发现光流追踪特别慢可以适当降低最大迭代次数。err是输出的追踪误差可以用来做筛选。误差大的点通常追踪质量差可以设一个阈值剔除。还有一个隐藏的坑calcOpticalFlowPyrLK默认使用OPTFLOW_USE_INITIAL_FLOW标志吗不是。如果你不传这个标志它会从零开始搜索光流。如果你有一个粗略的光流初始估计比如来自IMU或者上一帧的运动模型可以传这个标志让光流从初始值开始优化能提高追踪速度和精度。在纯视觉里程计里通常没有可靠的初始估计所以不传这个标志。3.4 位姿求解2D-2D对极几何与3D-2D PnP的选择追踪到点对之后怎么解算相机运动这取决于你手里有什么信息。如果你只有两帧图像的2D点对没有任何深度信息那就用2D-2D对极几何。核心是求解本质矩阵 ( E ) 或者基础矩阵 ( F )然后从 ( E ) 中分解出旋转 ( R ) 和平移 ( t )。OpenCV提供了findEssentialMat和recoverPose两个函数配合使用很方便。2D-2D的问题是尺度不确定性。从本质矩阵分解出来的平移 ( t ) 是一个单位向量没有真实的尺度。你只知道相机往哪个方向移动了不知道移动了多少米。对于纯视觉里程计来说尺度可以通过后续的三角化或者与IMU融合来恢复但在单目纯视觉的情况下尺度是漂移的。如果你有3D点比如通过三角化得到了前一帧点的深度那就用3D-2D PnP。PnP直接求解相机位姿不需要分解本质矩阵精度通常更高而且没有尺度问题因为3D点本身带尺度。OpenCV的solvePnP或者solvePnPRansac都可以用。在实际的视觉里程计里通常的做法是第一帧和第二帧之间用2D-2D初始化三角化出一批3D点。后续帧用3D-2D PnP求解位姿同时继续三角化新的3D点。当追踪点数量不足或者追踪质量下降时重新用2D-2D做一次初始化。这个策略兼顾了初始化的鲁棒性和后续帧的精度。注意事项无论是2D-2D还是3D-2D都强烈建议用RANSAC。光流追踪再怎么做可靠性筛选也难免有漏网之鱼。RANSAC可以进一步剔除误匹配让位姿解算更稳定。findEssentialMat和solvePnPRansac都内置了RANSAC直接用就行。3.5 完整代码框架从两帧图像到位姿输出下面给一个简化的完整代码框架展示光流法视觉里程计的核心流程。代码用C和OpenCV写Python版本逻辑一样只是API调用略有不同。#include opencv2/opencv.hpp #include vector using namespace cv; using namespace std; int main() { // 1. 读取两帧图像 Mat img1 imread(frame1.png, IMREAD_GRAYSCALE); Mat img2 imread(frame2.png, IMREAD_GRAYSCALE); // 2. 选取追踪点 vectorPoint2f pts1; goodFeaturesToTrack(img1, pts1, 200, 0.01, 15); // 3. 光流追踪 vectorPoint2f pts2; vectoruchar status; vectorfloat err; calcOpticalFlowPyrLK(img1, pts1, img2, pts2, status, err, Size(21,21), 3); // 4. 正向-逆向检查 vectorPoint2f pts1_back; vectoruchar status_back; calcOpticalFlowPyrLK(img2, pts2, img1, pts1_back, status_back, err, Size(21,21), 3); vectorPoint2f good_pts1, good_pts2; for (size_t i 0; i pts1.size(); i) { if (status[i] 0 || status_back[i] 0) continue; float dx pts1[i].x - pts1_back[i].x; float dy pts1[i].y - pts1_back[i].y; if (sqrt(dx*dx dy*dy) 1.0) continue; // 正向-逆向不一致 good_pts1.push_back(pts1[i]); good_pts2.push_back(pts2[i]); } // 5. 位姿求解2D-2D对极几何 Mat K (Mat_double(3,3) 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1); Mat E findEssentialMat(good_pts1, good_pts2, K, RANSAC); Mat R, t; recoverPose(E, good_pts1, good_pts2, K, R, t); cout Rotation:\n R endl; cout Translation:\n t endl; return 0; }这段代码跑通之后你会得到一个旋转矩阵 ( R ) 和一个平移向量 ( t )。注意 ( t ) 是单位向量没有真实尺度。如果你需要真实尺度要么用双目/RGBD要么用IMU融合要么在场景里放一个已知尺寸的标定物。4. 光流法实战中的坑与排查技巧4.1 光流追踪失败的典型场景与应对光流法虽然好用但它的失效场景比特征点法更隐蔽。特征点法提不出点的时候你至少知道“没点可用”光流法追踪失败的时候它可能给你一个看起来合理但完全错误的光流值你不做检查根本发现不了。场景一大运动导致追踪丢失。相机快速旋转或者快速平移时两帧之间像素位移可能超过金字塔能处理的范围。表现是大量追踪点的status为0或者正向-逆向检查大面积不通过。应对方法是增加金字塔层数或者降低图像分辨率相当于变相增大金字塔层数。如果还是不行说明运动太快需要考虑提高帧率或者用IMU辅助。场景二光照突变导致灰度不变假设失效。从暗处走到亮处、自动曝光调整、闪光灯等都会导致灰度突变。表现是光流方向混乱追踪点四处乱跑。应对方法是在光流追踪前做直方图均衡化或者光照归一化减小两帧之间的灰度差异。如果光照变化太剧烈光流法基本无解只能靠特征点法或者直接法硬扛。场景三弱纹理区域追踪漂移。白墙、天空、光滑地面这些区域像素梯度很小( A^T A ) 接近奇异光流解算不稳定。表现是这些区域的追踪点缓慢漂移短时间内看不出来累积几帧之后误差就很大了。应对方法是在选点阶段就避开这些区域或者在位姿求解时给这些点更低的权重。场景四遮挡和边界。前景物体遮挡背景、或者追踪点跑出图像边界都会导致追踪失败。表现是status为0或者追踪点坐标超出图像范围。应对方法是检查追踪点坐标是否在图像内以及用正向-逆向检查剔除遮挡点。4.2 正向-逆向检查的阈值怎么定正向-逆向检查的阈值是一个经验参数。阈值太小会把很多正确的追踪点误杀阈值太大会放过错误的追踪点。我一般用1个像素作为阈值。也就是说正向追踪和逆向追踪的结果差异超过1个像素就认为不可靠。这个阈值在大部分场景下工作良好。如果你的图像分辨率很高比如4K可以适当放宽到2个像素如果分辨率很低比如320×240可以收紧到0.5个像素。还有一个变种是归一化正向-逆向误差把正向-逆向差异除以光流幅值。这样可以避免大光流点的绝对误差天然比小光流点大的问题。公式是[ \text{error} \frac{|\mathbf{u}{forward} \mathbf{u}{backward}|}{|\mathbf{u}{forward}| |\mathbf{u}{backward}|} ]这个值在0到1之间越接近0越可靠。一般设0.1到0.2作为阈值。4.3 追踪点数量不足时的补救策略光流法的一个实际问题是随着帧数增加追踪点会逐渐减少。因为每帧都有一些点追踪失败被剔除而新的点没有及时补充。跑几十帧之后可能只剩几十个点位姿求解精度严重下降。补救策略有两个策略一定期补充新点。每隔几帧在当前帧重新用goodFeaturesToTrack选一批新点和已有的追踪点合并。合并的时候要注意去重避免同一个位置选了两个点。这个策略简单有效是大多数光流法SLAM系统的标配。策略二动态调整选点数量。如果当前追踪点数量低于阈值比如100个就多选一些新点如果追踪点数量充足就少选或者不选。这样可以保持追踪点数量在一个合理范围内既不会太少导致精度下降也不会太多导致计算量暴涨。实操心得补充新点的时候最好避开已有追踪点附近的区域。可以用一个掩膜mask把已有追踪点周围一定半径内的区域屏蔽掉再在剩余区域选新点。这样可以让追踪点分布更均匀避免扎堆。4.4 光流法与特征点法的性能对比实测我在同一台机器上跑过光流法和ORB特征点法的对比测试场景是一个室内走廊图像分辨率640×480帧率30fps。测试结果如下指标光流法ORB特征点法单帧耗时提取追踪/匹配8-12ms15-25ms追踪点数量150-200300-500位姿估计精度相对误差1.5%-3%1%-2%弱纹理场景成功率较低很低大运动场景成功率中等较高光照变化鲁棒性较低中等从数据可以看出光流法在速度上有明显优势耗时只有特征点法的一半左右。但精度略低主要是因为追踪点数量少而且光流追踪本身有漂移。在弱纹理场景下两者都不太行但光流法因为可以选网格点稍微好一点。在大运动场景下特征点法因为描述子的存在匹配鲁棒性更好光流法容易丢失追踪。这个对比说明光流法和特征点法不是替代关系而是互补关系。在实际SLAM系统里经常是两者结合使用——光流法做快速追踪特征点法做重定位和回环检测。4.5 常见问题速查表问题现象可能原因排查方法解决方案追踪点数量急剧减少大运动、光照突变、遮挡检查status数组和正向-逆向检查通过率增加金字塔层数、补充新点、检查光照光流方向混乱灰度不变假设失效可视化光流向量看是否杂乱无章直方图均衡化、降低帧间时间间隔位姿估计抖动大追踪点分布不均或数量不足检查追踪点空间分布网格化选点、增加追踪点数量尺度漂移严重单目2D-2D的固有缺陷对比不同帧的平移向量模长三角化恢复尺度、融合IMU追踪点缓慢漂移弱纹理区域光流解算不稳定检查追踪点是否在弱纹理区域选点时避开弱纹理、给低权重正向-逆向检查通过率低阈值太严或追踪质量差统计通过率调整阈值放宽阈值、检查图像质量5. 光流法在视觉SLAM中的定位与扩展5.1 光流法与直接法的关系光流法和直接法经常被放在一起讨论因为两者都基于灰度不变假设都利用像素灰度信息。但它们的侧重点不同。光流法关注的是像素运动给定一个像素点追踪它在下一帧的位置。追踪到位置之后位姿求解是独立的一步可以用对极几何或者PnP。直接法关注的是位姿优化直接构造光度误差把位姿作为优化变量通过最小化光度误差来求解位姿。像素运动是优化过程中的中间产物不是最终目标。你可以把光流法理解为直接法的一个特例光流法先追踪像素再求解位姿两步分开直接法把两步合并同时优化像素运动和位姿。所以直接法通常精度更高但实现更复杂对初始值更敏感。《视觉SLAM十四讲》在讲完光流法之后紧接着讲直接法就是这个逻辑先理解光流再理解直接法循序渐进。5.2 光流法在稀疏直接法SLAM中的应用稀疏直接法SLAM比如LSD-SLAM、DSO的核心思想是不提取特征点直接在像素上构造光度误差优化位姿和地图点。但“不提取特征点”不等于“随便选像素”。实际上稀疏直接法也需要选一批“好”的像素来构造误差选点的标准和光流法一模一样选梯度大的点避开弱纹理区域。所以光流法的选点策略、金字塔策略、可靠性检查策略在稀疏直接法里全都能用上。理解了光流法你再看DSO的代码会发现很多地方似曾相识。5.3 光流法IMU视觉惯性里程计的常见组合纯视觉里程计有两个绕不开的问题尺度不确定单目和快速运动容易丢失追踪。IMU可以很好地弥补这两个问题。IMU提供角速度和加速度积分之后可以得到粗略的旋转和平移估计。这个估计虽然漂移快但在短时间间隔内精度足够高可以作为光流追踪的初始值。前面提到calcOpticalFlowPyrLK支持OPTFLOW_USE_INITIAL_FLOW标志把IMU的预测光流作为初始值传进去可以显著提高大运动场景下的追踪成功率。同时IMU的加速度计可以提供尺度信息帮助单目视觉里程计恢复真实尺度。视觉和IMU的融合通常用紧耦合的优化框架把光度误差和IMU预积分误差放在一起优化。这是目前视觉惯性里程计的主流方案。5.4 从光流法到深度学习光流技术演进脉络传统光流法Lucas-Kanade、Horn-Schunck统治了几十年直到深度学习光流的出现。FlowNet、PWC-Net、RAFT这些网络用卷积神经网络直接预测稠密光流在精度上大幅超越了传统方法尤其是在大运动、遮挡、弱纹理等困难场景下。但深度学习光流有一个致命问题慢。RAFT在GPU上跑一对图像需要几十毫秒甚至上百毫秒对于需要实时运行的SLAM系统来说太慢了。所以目前SLAM系统里还是以传统光流法为主深度学习光流主要用于离线处理或者对实时性要求不高的场合。不过这个局面在变化。随着轻量化网络和专用硬件的出现深度学习光流的速度在快速提升。未来几年我们可能会看到更多SLAM系统采用学习型光流。但无论技术怎么演进Lucas-Kanade光流的基本原理——灰度不变假设、金字塔分层、最小二乘求解——仍然是理解光流法的基石。我在实际项目里用光流法做过室内机器人的视觉里程计也用过特征点法做无人机的位置估计。踩过几次坑之后我的体会是光流法不是万能的但在纹理丰富、运动平缓、光照稳定的场景下它比特征点法更快、更直接。如果你的场景满足这些条件光流法值得一试。如果场景里有大量弱纹理区域或者光照变化剧烈还是老老实实用特征点法或者上直接法IMU融合。选什么方案最终取决于你的场景和硬件没有银弹。