- 先来看开篇部分, 关于SLAM学习的那个所谓"第三方库门槛", 与之有着核心地位的一方面是, 1.1所涉及的是机器人和自动驾驶领域的相关第三方库生态。
在机器人同步定位与地图构建以及自动驾驶融合定位这个领域当中, C++ 是处于无可替代地位的核心编程语言,然而第三方库却是达成复杂算法所需依靠的那个类似 "基石" 一样不可缺少一部分。
先从机器人操作系统ROS说起, 再到点云处理库PCL, 接着是矩阵运算库Eigen, 然后是优化库Ceres、G2O、GTSAM, 最后是视觉处理库, 这些库一起构成了SLAM开发的技术栈。
在这当中, 它身为开源计算机视觉库里的标杆, 给出了从图像的读取, 再到接着的预处理, 之后是特征提取, 一直到目标检测的一整套流程的接口, 是视觉SLAM里"感知前端"的关键依赖, 不管是单目SLAM的特征点匹配, 还是多传感器融合中的图像预处理, 都缺少不了它的支持。
1.2 初学者的核心痛点:"看得懂语法,看不懂源码"
相当多正在起步的学习者, 于掌握了C++基础语法之后, 面对诸如ORB-SLAM3这类的SLAM开源项目的源码, 依旧会处于"寸步难行"的状况, 核心问题存在于:
身为视觉SLAM的入门级核心库, 对于其API的掌握水平, 直接对后续学习的效率起到决定性作用。
1.3 本文核心价值:结合 SLAM 实战,学透
本文摆脱了那种仅仅是把 API 罗列出来的固有的学习方式, 而是以 SLAM 实战所需要的条件为指引方向, 把相关的关键知识跟机器人 SLAM 所处的场景紧密地联系在一起:
- 库核心知识体系, 其中发展历程方面, 包括了进行 SLAM 应用时的图像读取以及格式转换, 此过程要适配各异传感器输入, 还要开展图像预处理, 像是降噪、缩放以及金字塔构建, 还有特征点提取与匹配, 比如 ORB 以及 SIFT 特征, 这依赖相关的特征检测模块, 另外有相机标定与畸变校正, 要计算基础矩阵、单应性矩阵, 最后是图像拼接与地图可视化, 涉及多帧图像融合。核心知识体系还有结构化梳理, 涵盖头文件、类以及函数。
常用函数在核心类, 核心类于核心模块、关键头文件里找, 数据结构能用, 被应用到 SLAM 场景中, 是这样的情况。
图像数据基础
Mat、、Vec3b
Mat()、clone()、()
图像存储、点云数据载体、特征点矩阵
矩阵区域操作
Mat
()、()、row()、col()
特征点筛选、点云区域裁剪、ROI 提取
图像金字塔
Mat
()、pyrUp()、()
多尺度特征提取、尺度不变性匹配
形态学操作
Mat、
()、erode()

障碍物边缘优化、噪声去除
特征检测
、
()、()、match()
ORB/SIFT 特征提取与匹配(SLAM 核心步骤)
相机标定
Mat(相机内参矩阵)
()、()
图像畸变校正、相机参数求解
2.三基础概念以及重点学习模块划分, 二点三点一核心基础概念, 二点三点二重点学习模块, 也就是 SLAM 场景优先级排序, 图像数据基础, 属于 Mat 类和像素类型;它是所有视觉处理的起始点, 必须要掌握;特征检测与匹配, 是个模块;它是 SLAM 前端的核心所在, 直接决定着定位精度;图像金字塔与预处理, 是个模块;它是多尺度匹配以及噪声去除的关键;相机标定, 是个模块;它能解决图像畸变问题, 是视觉 SLAM 的前提条件;形态学操作, 是个模块;它辅助优化感知结果, 提升算法鲁棒性。3. 详细解析核心 API: 概念加上语法, 再加上 SLAM 实战代码 3.1, 关于图像数据基础: Mat 类以及像素类型, 3.1.1 核心概念和语法示例。
text
CV_[位数][类型][通道数]
,例如:
text
// 创建3行2列、8位无符号三通道矩阵,初始值(0,0,255)(红色)
cv::Mat M(3, 2, CV_8UC3, cv::Scalar(0, 0, 255));
std::cout << "M = " << std::endl << " " << M << std::endl;
3.1.2 SLAM 场景实战代码(图像读取 / 初始化)
情况是这样的, 在单目 SLAM 当中, 要读取相机所拍摄出的图像, 随后进行初始化操作, 为特征点去存储矩阵, 并且要适配 ORB 特征提取方式。
text
/**
* @brief SLAM前端:图像读取与特征点矩阵初始化
* @author @csdn 行知SLAM&机器人及自动驾驶
*/
#include
#include
#include
int main(int argc, char** argv) {
// 1. 读取相机图像(SLAM中对应相机回调函数输入)
cv::Mat img = cv::imread("camera_frame.png", cv::IMREAD_COLOR); // 读取彩色图
if (img.empty()) {
std::cerr << "Error: 无法读取图像文件,请检查路径!" << std::endl;
return -1;
}
// 2. 初始化特征点存储矩阵(ORB特征:2D坐标(x,y) + 响应值,共3通道)
const int MAX_FEATURES = 500;
cv::Mat orb_keypoints(MAX_FEATURES, 3, CV_32FC1, cv::Scalar(0.0)); // 32位浮点3通道
// 3. 图像预处理:转换为灰度图(ORB特征提取需灰度图输入)
cv::Mat img_gray;
cv::cvtColor(img, img_gray, cv::COLOR_BGR2GRAY); // BGR转灰度图(OpenCV默认BGR存储)
// 4. 打印图像信息(SLAM调试常用)
std::cout << "图像宽度:" << img.cols << ", 高度:" << img.rows << ", 通道数:" << img.channels() << std::endl;
std::cout << "特征点矩阵尺寸:" << orb_keypoints.rows << "x" << orb_keypoints.cols << std::endl;
// 5. 显示图像(SLAM可视化调试)
cv::imshow("SLAM Camera Frame", img);
cv::waitKey(0); // 等待按键退出
return 0;
}
3.1.3易错知识点以及需要留意的事项, 通道顺序方面的问题: 读取彩色图像时默认存储格式是BGR格式, 然而SLAM可视化(像是ROS这种情况)通常所采用的是RGB格式, 此时需要运用()展开转换, 不然的话就会出现"颜色失真"的状况;深度和浅度拷贝相关陷阱: 直接进行赋值操作cv::Mat img2=img1的话仅仅会复制矩阵头, 要是对img2作出修改会对img1产生影响, 在SLAM里存储特征点以及图像数据的时候需要借助clone()或者()做到深拷贝;数据类型相互匹配: 特征点坐标、相机内参矩阵理应采用(32位浮点), 防止因使用不当致使精度有所丢失, 进而对定位的结果造成影响;图像空指针的判断情形: 在SLAM当中相机有可能出现断连现象或者图像路径出现错误, 务必要先对img.empty()进行判断, 否则后续开展的操作将会崩溃。3.2, 矩阵区域进行操作, /, 函数3.2.1, 核心概念以及语法示例。
text
// 行区间:[startrow, endrow)(左闭右开)
cv::Mat cv::Mat::rowRange(int startrow, int endrow) const;
cv::Mat cv::Mat::rowRange(const cv::Range& r) const;
// 列区间:[startcol, endcol)
cv::Mat cv::Mat::colRange(int startcol, int endcol) const;
cv::Mat cv::Mat::colRange(const cv::Range& r) const;
text
// 创建3x3矩阵
cv::Mat Test = (cv::Mat_(3,3) << 0,1,2, 3,4,5, 6,7,8);
std::cout << "原始矩阵:" << std::endl << Test << std::endl;
// 提取第0行(左闭右开,0到1即第0行)
cv::Mat row0 = Test.rowRange(0, 1).clone(); // clone()深拷贝,避免修改子矩阵影响原矩阵
std::cout << "第0行:" << std::endl << row0 << std::endl;
// 提取第0列
cv::Mat col0 = Test.colRange(0, 1).clone();
std::cout << "第0列:" << std::endl << col0 << std::endl;
3.具有点云以及特征点提取区域筛选情况的2.2 SLAM场景实战代码。
在 SLAM 的场景里, 相机视野的边缘部分容易出现畸变的情况, 故而呢, 要对图像中心区域的特征点展开筛选工作, 以此来提高匹配的精度。
text
/**
* @brief SLAM特征点筛选:提取图像中心区域的ORB特征点
* @author @csdn 行知SLAM&机器人及自动驾驶
*/
#include
#include
#include
#include
#include
int main(int argc, char** argv) {
// 1. 读取图像并转换为灰度图
cv::Mat img = cv::imread("camera_frame.png", cv::IMREAD_COLOR);
cv::Mat img_gray;
cv::cvtColor(img, img_gray, cv::COLOR_BGR2GRAY);
// 2. 定义图像中心区域(避开边缘100像素,避免畸变影响)
int img_cols = img_gray.cols;
int img_rows = img_gray.rows;
int margin = 100; // 边缘余量
cv::Range row_range(margin, img_rows - margin); // 行区间:[100, rows-100)
cv::Range col_range(margin, img_cols - margin); // 列区间:[100, cols-100)
// 3. 提取中心区域子图像(仅创建矩阵头,不复制数据)
cv::Mat img_roi = img_gray(row_range, col_range);
// 4. 提取ORB特征点(仅对中心区域操作)
cv::Ptr orb = cv::ORB::create(500, 1.2f, 8, 31, 0, 2, cv::ORB::HARRIS_SCORE, 31, 20);
std::vector keypoints;
cv::Mat descriptors;
orb->detectAndCompute(img_roi, cv::noArray(), keypoints, descriptors);
// 5. 还原特征点坐标到原始图像(关键:子图像坐标需加上边缘偏移)
for (auto& kp : keypoints) {
kp.pt.x += margin; // 列坐标偏移
kp.pt.y += margin; // 行坐标偏移
}
// 6. 绘制特征点并显示
cv::Mat img_with_kp;
cv::drawKeypoints(img, keypoints, img_with_kp, cv::Scalar(0, 255, 0), cv::DrawMatchesFlags::DEFAULT);
cv::imshow("ORB Keypoints (Center ROI)", img_with_kp);
cv::waitKey(0);
std::cout << "原始图像特征点数量:" << keypoints.size() << std::endl;
return 0;
}