第243篇 视觉SLAM前端之直接法——不用特征点的另一条路

发布时间:2026/8/23 9:36:34
第243篇 视觉SLAM前端之直接法——不用特征点的另一条路 前面几篇讲的都是特征点法提取特征、匹配特征、用匹配关系估计位姿。这篇讲另一条完全不同的路——直接法。直接法不提取特征点不用描述子不用匹配直接用像素的灰度值来做位姿估计。听起来很激进但它确实是一条走得通的路。直接法的代表是LSD-SLAM和DSO。它们的思路和特征点法完全不同但在某些场景下效果更好。比如纹理稀少但有大量弱梯度像素的场景比如走廊、白墙特征点法可能找不到足够的特征点直接法却能利用这些弱梯度像素来估计位姿。面试的时候直接法和特征点法的对比是常见考点你得知道两者的优劣和适用场景。直接法的核心思想直接法基于一个假设同一个空间点在不同图像上的像素灰度值是不变的。这就是光度不变假设photometric assumption。用数学表示I₁(p) I₂(ω(p))其中I₁和I₂是两幅图像的灰度值p是第一个图像上的像素坐标ω(p)是这个点在第二幅图像上的投影位置。ω取决于相机的位姿和场景的深度。这个假设在现实中并不完美成立。光照变化、曝光差异、反射表面都会导致同一个点的灰度值在不同图像上不同。但在短时间内、光照稳定的场景下这个假设是足够好的近似。这也是直接法在室内场景表现更好的原因之一。如果位姿估计是准的那ω(p)算出来的位置上的灰度值应该和I₁(p)接近。如果不准灰度值就会有差异。直接法就是最小化这个灰度差异来求解最优位姿。# 直接法的目标函数简化版 def photometric_error(pose, img1, img2, depth_map): total_error 0 for u, v in selected_pixels(img1): # 用深度和位姿投影到img2 u2, v2 project(u, v, depth_map[u,v], pose) # 计算灰度差异 error img1[u,v] - interpolate(img2, u2, v2) total_error error ** 2 return total_error直接法 vs 特征点法两种方法的核心区别在于利用的信息不同。特征点法只用图像中有辨识度的点角点、边缘忽略了大量灰度均匀区域的像素。直接法用的是所有像素或者选一部分梯度大的像素利用的信息量更大。信息量大意味着在困难场景下纹理少、重复纹理有更多约束可以利用。特征点法需要提取和匹配描述子计算量不小。直接法跳过了特征提取和匹配直接算灰度误差理论上更快。特征点法对光照变化有一定的鲁棒性描述子做了归一化。直接法对光照变化非常敏感因为光度不变假设在光照变化时直接失效。特征点法能构建稀疏地图只有特征点的3D坐标。直接法可以构建半稠密或稠密地图因为它利用了大量像素的信息。直接法的几种形式稀疏直接法只选梯度大的像素比如角点和边缘优化这些像素的灰度误差。计算量小但地图是稀疏的。SVOSemi-direct Visual Odometry就是这个思路。半稠密直接法选梯度超过一定阈值的像素不要求是特征点。地图是半稠密的。LSD-SLAM是典型代表能构建半稠密的深度地图。稠密直接法用所有像素。计算量很大但地图最完整。DTAM是早期的稠密直接法现在用得少了。DSODirect Sparse Odometry是目前最成功的直接法SLAM系统之一。它结合了特征点法和直接法的优点只选梯度大的像素像特征点法一样高效但不用描述子匹配像直接法一样直接优化灰度。DSO还做了很多工程优化边缘化旧帧控制问题规模、用IMU预积分提供运动先验、优化相机内参和曝光参数。最终效果在精度和速度上都达到了很高的水平。DSO的一个关键创新是同时优化位姿和深度。传统方法先估计位姿再估计深度或者反过来DSO把它们放在一起优化。这样位姿和深度可以互相约束最终结果更准确。# 不同直接法的像素选择策略 def select_pixels_sparse(img): return detect_corners(img) # 只选角点 def select_pixels_semidense(img, threshold20): grad compute_gradient(img) return np.where(grad threshold) # 选梯度大的像素 def select_pixels_dense(img): return all_pixels(img) # 用所有像素面试中怎么对比两种方法面试官问直接法和特征点法的区别你要从五个维度回答信息利用特征点法只用少量特征点直接法用大量像素。直接法利用的信息更多在纹理少的场景下优势明显。光照鲁棒性特征点法好直接法差。直接法的光度不变假设在光照变化时会失效这是它最大的弱点。地图稠密度直接法能构建半稠密或稠密地图特征点法只能构建稀疏地图。需要稠密地图做避障的场景直接法更合适。计算效率直接法省去了特征提取和匹配的时间但需要优化大量像素的灰度误差。在像素选择合理的情况下直接法的速度可以做到很快。精度特征点法的精度通常更高因为特征点的定位精度比一般像素高。直接法在光照好的场景下精度也不错但光照变化时精度下降明显。DSO和ORB-SLAM哪个更好 不能简单说哪个更好。在光照稳定、纹理丰富的场景下两者精度差不多。在光照变化大的场景下ORB-SLAM更稳定。在纹理稀少的场景下DSO的直接法可能更好因为它能利用更多像素。实际选型要看具体应用场景。直接法能不能和特征点法结合 可以。R-VIO就是一个例子它用特征点做跟踪用直接法做稠密地图构建。两者的优势可以互补。直接法的工程挑战实际做直接法SLAM有几个难点要克服。光度校准是必须的。相机的自动曝光、自动白平衡都会破坏光度不变假设。做直接法之前必须关掉这些自动功能或者做光度校准。深度估计的初始化很关键。初始化的质量直接决定了后续跟踪的稳定性。直接法需要从单目图像中估计深度这是一个病态问题。LSD-SLAM用的是深度滤波的方法类似平面先验多帧融合DSO用的是联合优化位姿和深度的方法。直接法容易陷入局部最优。因为灰度误差的函数不是凸的如果初始位姿偏差太大优化会收敛到错误的解。所以直接法通常要求帧间运动不能太大或者用IMU提供好的初始值。还有一个问题是运动模糊。快速运动时图像会产生运动模糊像素的灰度值会发生变化光度不变假设就不成立了。直接法在快速运动场景下的表现通常不如特征点法。直接法的优化用的是Gauss-Newton或Levenberg-Marquardt方法。目标函数对所有像素的灰度误差求和对位姿参数求导数然后迭代更新。导数的计算用图像梯度和深度信息这就是为什么直接法需要图像的梯度信息。# 直接法的优化步骤简化版 for iteration in range(max_iter): H np.zeros((6, 6)) # 海塞矩阵 b np.zeros(6) # 梯度向量 for p in selected_pixels: J compute_jacobian(p, depth, pose) # 6维雅可比 r compute_residual(p, depth, pose, img1, img2) H J.reshape(6,1) J.reshape(1,6) b J * r delta np.linalg.solve(H, -b) # 求解增量 pose pose exp_map(delta) # 更新位姿直接法是视觉SLAM的另一条重要路线和特征点法互为补充在特定场景下各有优势。理解了两种方法的优劣你就能根据具体场景做出正确的技术选型。上一篇第242篇 视觉SLAM前端之PnP问题——从2D到3D的位姿估计下一篇我们进入视觉SLAM后端聊BA优化——Bundle Adjustment的原理和实现。BA是SLAM后端最核心的优化手段所有现代SLAM系统都离不开它。如果你在做SLAM项目建议先用特征点法ORB-SLAM2跑通整个流程理解了前端后端的架构之后再尝试直接法DSO。直接法的调试难度比特征点法大不少有了基础再上手会轻松很多。如果这篇文章对你有帮助欢迎点赞支持一下你的鼓励是我持续更新的动力