尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

手搓视觉SLAM定位算法:第一章

手搓视觉SLAM定位算法:第一章
📅 发布时间:2026/8/3 20:43:12

手搓视觉SLAM定位算法:第一章

默认读者对视觉slam和视觉定位有一定的基础
没有的话可以参考《视觉十四讲》或者作者的另外一个专栏
有一点基础就行


文章目录

  • 手搓视觉SLAM定位算法:第一章
  • 前言
  • 一、理论基础
  • 二、代码实现
    • 2.1 设置参数
    • 2.2 匹配
    • 2.3 求解
    • 2.4 发布
  • 三、实验结果
  • 总结

前言

本章节的目的是实现一个帧到帧的视觉定位算法,跟ros结合
因为还不涉及建图,所以这里还不能叫做slam


一、理论基础

ROS就不介绍了,核心三件套:roscore平台,subscriber订阅器,publisher发布器

这里因为是用KITTI数据集,所以直接从磁盘去读,那么subscriber就用不到了,只有publisher

另外依赖什么的都不需要,有opencv3就行,本人用的是ubuntu18+opencv3.4.16

#----------------------------------------------------------------------------------------------------------------------------------------

因为是帧到帧,所以实际上非常非常的简单,流程就是三步:匹配,求解,发布

匹配:
在参考帧中找n个角点,用LK光流在目标帧中确定这些点的位置,从而获得两组匹配的点对

求解
匹配步骤的结果是两组匹配点对,那么结合相机的内参就可以计算两帧图像的本质矩阵,通过这个矩阵就可以反推位姿
这样就获得了目标帧相对参考帧的运动变换

发布
获得了目标帧相对参考帧的运动变换后,结合参考帧在世界坐标系中的位姿,就可以得到目标帧的位姿
发布目标帧的位姿,然后以目标帧作为新的参考帧,进行下一个循环,直到没有新的图像

二、代码实现

理论上很简单,代码实现也一样的简单,拆开几步走

2.1 设置参数

读取的参数主要就是相机的内参,其他都不是很重要,这里也可以直接手动输入,当然我习惯了还是用config配合launch来读

ros_nh.param<std::string>("mono/camera_topic",camera_topic,"/mono/track_img");// 追踪图像发布topicros_nh.param<std::string>("mono/path_topic",path_topic,"/mono/camera_path");// 相机轨迹发布topicros_nh.param<std::string>("data/image_path",img_path," ");// KITTI图像所在目录ros_nh.param<int>("data/image_nums",img_nums,0);// 读取多少个图像ros_nh.param<float>("mono/intrinsic/fx",fx,0);// 相机内参ros_nh.param<float>("mono/intrinsic/fy",fy,0);ros_nh.param<float>("mono/intrinsic/cx",cx,0);ros_nh.param<float>("mono/intrinsic/cy",cy,0);if(img_path[-1]!='/')img_path+='/';std::cout<<"[Ros_parameter]: camera_topic "<<" ==> "<<camera_topic<<std::endl;std::cout<<"[Ros_parameter]: path_topic "<<" ==> "<<path_topic<<std::endl;std::cout<<"[Ros_parameter]: img_path "<<" ==> "<<img_path<<std::endl;std::cout<<"[Ros_parameter]: img_nums "<<" ==> "<<img_nums<<std::endl;std::cout<<"[Ros_parameter]: intrinsic "<<" ==> "<<fx<<" "<<fy<<" "<<cx<<" "<<cy<<" "<<std::endl;

2.2 匹配

按理讲应该第一张图片提取角点后,后面都不需要再提取,直到点的数量太少了的时候再重新提取角点

但是作者比较懒,所以每个图片都提取了角点,并跟踪

cv::Mat cur_img;cur_img=cv::imread(img_queue[loop_count],cv::IMREAD_UNCHANGED);std::vector<cv::Point2f>points_pre;cv::goodFeaturesToTrack(pre_img,points_pre,230,0.01,10);// 对参考帧提取角点std::vector<cv::Point2f>points_cur;std::vector<uchar>status;std::vector<float>err;cv::calcOpticalFlowPyrLK(// 在目标帧中用LK光流跟踪pre_img,cur_img,points_pre,points_cur,status,err,cv::Size(21,21),3);cv::Mat result;cv::cvtColor(cur_img,result,cv::COLOR_GRAY2BGR);std::vector<cv::Point2f>pre_pts;std::vector<cv::Point2f>cur_pts;for(size_t i=0;i<points_pre.size();i++){// 画图,顺便筛一下错误的匹配点if(status[i]){cv::circle(result,points_cur[i],3,cv::Scalar(0,255,0),-1);cv::line(result,points_pre[i],points_cur[i],cv::Scalar(255,0,0),2);pre_pts.push_back(points_pre[i]);cur_pts.push_back(points_cur[i]);}}// 这里有说道的,我们只做了reference to target的LK光流追踪,为了降低错误,可以反向再来一次// 也就是做一下target to reference的LK光流,结合两个方向的结果来筛选最后正确的匹配点对// 这个方法在VINS-Fusion里使用了,作者比较懒就不搞了

2.3 求解

2.2的输出结果就是pre_pts和cur_pts两个匹配的点对,假设我们相机的内参矩阵是K

那么就可以求解位姿了

cv::Mat E=cv::findEssentialMat(pre_pts,cur_pts,K,cv::RANSAC,0.999,1.0);// 求解本质矩阵std::cout<<"essential_matrix is "<<std::endl<<E<<std::endl;cv::Mat R,t;intinliers=cv::recoverPose(E,pre_pts,cur_pts,K,R,t);// 反向推导位姿

2.4 发布

如果是用Eigen的话,这里还是比较简单的,可惜不是,为了方便测试,我只用了opencv来做这个事情

那么发布位姿就需要按照下面的步骤来

cv::Mat Rt=cv::Mat::eye(4,4,CV_64F);R.copyTo(Rt(cv::Rect(0,0,3,3)));t.copyTo(Rt(cv::Rect(3,0,1,3)));T=T*Rt.inv();cv::Mat R_pose=T(cv::Rect(0,0,3,3));cv::Mat t_pose=T(cv::Rect(3,0,1,3));cv::Mat R_cv;cv::Rodrigues(R_pose,R_cv);// 位姿矩阵转旋转向量doubleangle=cv::norm(R_cv);// 计算旋转角cv::Mat axis=R_cv/angle;// 计算旋转轴geometry_msgs::PoseStamped pose_stamped;pose_stamped.header.stamp=ros::Time::now();pose_stamped.header.frame_id="world";pose_stamped.pose.position.x=t_pose.at<double>(0);pose_stamped.pose.position.y=t_pose.at<double>(1);pose_stamped.pose.position.z=t_pose.at<double>(2);pose_stamped.pose.orientation.x=axis.at<double>(0)*sin(angle/2);pose_stamped.pose.orientation.y=axis.at<double>(1)*sin(angle/2);pose_stamped.pose.orientation.z=axis.at<double>(2)*sin(angle/2);pose_stamped.pose.orientation.w=cos(angle/2);path_msg.poses.push_back(pose_stamped);// 做成pose_stamped来发布pub_camera_path.publish(path_msg);// 别忘了发布图像sensor_msgs::ImagePtr cur_img_ptr=cv_bridge::CvImage(std_msgs::Header(),"rgb8",result).toImageMsg();cur_img_ptr->header.frame_id="world";cur_img_ptr->header.stamp=ros::Time::now();pub_track_img.publish(cur_img_ptr);

三、实验结果

不知道怎么上传视频,就换成gif放上来看看效果吧

不得不感慨KITTI00这个数据集确实适合做SLAM,特征丰富,参数也好

帧到帧的定位问题也很明显了,基本没有鲁棒性,有点干扰就不行

而且不考虑局部多帧的关联,在停车那里直接飞了,这也是local BA和滑窗被引入的意义

完整代码后面再说吧,有人看我再放到github上,开的坑有点多,填不完了


总结

做了一个非常简单的帧到帧视觉定位算法,用KITTI数据集验证,从实践中一点一点感受视觉slam的发展

相关新闻

  • SmartDNS完全指南:如何用智能DNS服务器加速你的网络访问速度
  • Kaneo安全配置:保护你的项目数据的7个关键步骤
  • 武汉复读学校哪家好 - 湖北就要学教育

最新新闻

  • 从0到量产:AI产品图工作流重构指南,缩短83%出图周期,已验证于17个DTC品牌
  • 83个公共Tracker终极指南:专业解决BT下载连接难题的完整方案
  • 2026年大跨钢结构桥梁制造厂家实力解析:从网架到拱桥的系统化选型逻辑 - 优企名品
  • 10分钟上手TALL-forms:新手必备的表单开发技巧
  • 2026边牧犬舍选购指南 靠谱犬舍榜单及避坑测评 - Full19
  • 2026丽水政企宣传片制作公司排行榜TOP5 | 党建宣传片 | 政府汇报片 | 会议拍摄 | 视频直播 | 招商宣传片服务商评测对比 - 政企影像扫地僧

日新闻

  • 112、LLC谐振变换器的输入电压瞬态仿真分析
  • 2026深圳疑难签证办理指南:拒签再签/商务签/高端定制机构怎么选 - 互联网科技品牌测评
  • C-LODOP在Edge等现代浏览器中的部署、适配与实战应用

周新闻

  • 怀化母婴除甲醛公司测甲醛中心怎么选:康之居母婴除甲醛标准、流程、避坑指南 - 信誉隆金银铂奢回收
  • 三步打造你的终极音乐中心:foobox-cn网络电台功能完整指南
  • Lance湖仓格式:为多模态AI工作流设计的终极数据存储方案

月新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号