从双目视差到三维点云:原理剖析与C++实战
1. 双目视觉基础从人眼到相机人类能够感知三维空间很大程度上依赖于双眼视差。当你看一个物体时左右眼看到的图像会有细微差别大脑通过这种差异计算出物体的距离。双目相机系统正是模拟了这一生物机制。在机器人导航中我们通常使用两个完全相同的相机水平排列并保持固定距离称为基线baseline。就像人的双眼一样这两个相机从不同角度观察同一场景产生两幅有视差的图像。我曾经在一个扫地机器人项目中使用过这种配置实测下来发现基线距离的选择很关键——太近会导致深度计算不准确太远又会增加设备体积。相机的内部参数决定了它如何将三维世界投影到二维图像上。最重要的参数包括焦距(fx,fy)相当于相机的视力范围数值越大看得越远光心(cx,cy)图像的中心点坐标 这些参数通过相机标定获得我推荐使用OpenCV的calibrateCamera函数它支持棋盘格标定法实测精度能达到亚像素级别。2. 视差图的奥秘从像素差异到深度线索视差图是双目视觉的核心产出物。每个像素值代表左右图像对应点的水平位移差这个差值越大说明物体距离相机越近。计算视差的过程就像玩找不同游戏需要在两幅图像中匹配相同的特征点。常用的视差计算方法有BM算法速度快但精度一般适合实时性要求高的场景SGBM算法效果更好但计算量较大我在室内机器人项目中使用的是这个深度学习法如PSMNet精度最高但对硬件要求也高这里有个实际项目中的经验视差图往往包含大量噪声和无效值。建议先进行以下预处理视差归一化将原始视差值映射到0-255范围方便可视化空洞填充使用形态学操作处理缺失区域滤波去噪高斯滤波或双边滤波效果都不错// OpenCV示例视差计算 cv::Ptrcv::StereoSGBM sgbm cv::StereoSGBM::create( 0, 96, 9, 8*9*9, 32*9*9, 1, 63, 10, 100, 32, cv::StereoSGBM::MODE_SGBM_3WAY); sgbm-compute(leftImg, rightImg, disparity);3. 深度计算三角测量的工程实践视差到深度的转换基于三角测量原理这个看似简单的公式Z(f×b)/d背后有几个关键点需要注意基线b的测量要精确到毫米级我在项目中用游标卡尺实测过焦距f必须使用像素单位而非物理毫米单位视差d不能为0否则会导致除零错误实际项目中会遇到一些特殊情况无限远点视差接近0深度值会非常大遮挡区域左右图像不匹配导致视差计算错误反射表面如玻璃会产生异常视差值建议在代码中加入有效性检查if(disparity 0.0 || disparity max_disparity) { return INVALID_DEPTH; } depth (focal_length * baseline) / disparity;4. 点云生成从二维像素到三维世界将深度图转换为点云就像给每个像素装上深度尺让它们从平面图像中站立起来。这个过程涉及相机坐标系到世界坐标系的转换核心公式如下X (u - cx) * Z / fx Y (v - cy) * Z / fy Z depth在实际编码时有几点优化建议使用并行计算点云生成很适合多线程加速内存预分配提前为点云容器预留足够空间坐标变换考虑相机安装位置带来的坐标系转换这是我常用的点云生成代码框架struct Point3D { float x, y, z; uint8_t confidence; }; void generatePointCloud(cv::Mat depthMap, std::vectorPoint3D cloud) { cloud.reserve(depthMap.rows * depthMap.cols); for(int v0; vdepthMap.rows; v) { for(int u0; udepthMap.cols; u) { float Z depthMap.atfloat(v,u); if(Z 0) continue; Point3D p; p.x (u - cx) * Z / fx; p.y (v - cy) * Z / fy; p.z Z; cloud.push_back(p); } } }5. 工程优化让算法真正可用在实际机器人项目中原始点云往往不能直接使用。我们需要一系列后处理点云滤波统计离群点去除消除飞点噪声体素网格滤波降低数据量保持形状半径滤波去除孤立点置信度处理 给每个点添加置信度评分很有必要可以基于视差计算时的匹配代价左右一致性检查结果局部纹理丰富程度内存优化使用内存池管理点云数据采用八叉树等空间数据结构实现分块加载机制在最近的一个AGV项目中通过以下优化将点云处理速度提升了3倍将浮点运算改为定点数运算使用SIMD指令并行化计算实现双缓冲机制避免内存拷贝6. 实战案例扫地机器人的环境建模去年我参与开发了一款商用扫地机器人其核心导航模块就采用了双目视觉方案。系统工作流程如下硬件选型使用全局快门相机避免运动模糊基线距离设计为12cm加装红外补光灯应对弱光环境参数标定在1m×1m标定场地上采集20组图像采用非线性优化计算内外参标定后重投影误差控制在0.2像素内实时处理640×480分辨率下达到15fps点云密度约5万点/帧最终定位精度达到±2cm这个项目让我深刻体会到理论公式到工程实现之间需要解决大量细节问题。比如我们发现相机安装架的微小形变就会严重影响深度精度后来改用碳纤维材料解决了这个问题。7. 进阶话题与其他传感器的融合纯视觉方案在某些场景下会遇到挑战弱光环境低纹理区域反光表面这时可以考虑传感器融合与IMU组合提高动态场景下的稳定性加入轮式里程计提供运动先验融合激光雷达补充几何特征在代码实现上建议采用松耦合架构class SensorFusion { public: void updatePointCloud(const PointCloud pc); void updateIMU(const IMUData imu); void fuseData(); private: PointCloud filtered_cloud_; Pose current_pose_; };最近我在试验一个有趣的方案用深度学习网络直接预测视差图的置信度然后用这个置信度指导多传感器融合的权重分配初步测试显示效果不错。