小工具知识网

当前位置: 首页 > 行业知识 > SLAM算法与工程实践——相机篇:RealSense T265相机使用(2)

行业知识

SLAM算法与工程实践——相机篇:RealSense T265相机使用(2)

2026-10-20 20:44:52 来源:互联网转载

SLAM算法与工程实践系列文章

下面���SLAM算法与工程实践系列文章的总链接,本人发表这个系列的文章链接均收录于此

SLAM算法与工程实践系列文章链接


下面是专栏地址:

SLAM算法与工程实践系列专栏


文章目录

  • SLAM算法与工程实践系列文章
    • SLAM算法与工程实践系列文章链接
    • SLAM算法与工程实践系列专栏
    • 前言
    • SLAM算法与工程实践——相机篇:RealSense T265相机使用(2)
      • 校正畸变
        • 棋盘标定法
        • 横向展开法
        • 经纬度法
          • ① 经度
          • ② 纬度
          • 接收和发布图像
            • 同时订阅双目图像
              • 在主函数中实现
              • 在类的成员函数中实现
              • 使用Opencv库订阅T265图像

                前言

                这个系列的文章是分享SLAM相关技术算法的学习和工程实践


                SLAM算法与工程实践——相机篇:RealSense T265相机使用(2)

                校正畸变

                参考:

                一文讲透鱼眼相机畸变矫正,及目标检测项目应用

                一文讲透鱼眼相机畸变矫正,及目标检测项目应用

                AVM环视系统——鱼眼相机去畸变算法

                无论是单目相机还是双目相机,拍摄的图像都会存在畸变。

                它们和鱼眼相机的畸变矫正原理也是一样的:核心是求解一个“好”的重映射矩阵(remap matrix)。

                从而将原图中的部分像素点(或插值点)进行重新排列,“拼”成一张矩形图。

                “好”是跟最终需求挂钩的,不同任务往往采用不同的矫正/变形方案。

                比如:

                (1)单目相机的畸变矫正

                对于单目相机,为了得到相机像素坐标系和三维世界坐标系的对应关系,我们需要对相机的桶形畸变和枕形畸变进行矫正。

                (2)双目相机的畸变矫正

                而对于双目相机,为了做极线对齐,实现深度估计。

                我们需要将两个相机,输出变换到同一个坐标系下。

                张正友的棋盘标定法,通过标志物的位置坐标,估计出相机的内外参数和畸变系数,从而计算出remap matrix。该方法是目前上述两类相机,矫正效果最好的方法。

                (3)鱼眼相机的矫正变形

                对于鱼眼相机,主要有三种方法:棋盘标定法、横向展开法、经纬度法。

                下图是某款鱼眼相机的采集图像,而真正有效的监控区域,是内部的圆形区域。

                棋盘标定法

                棋盘矫正法的目的,是将鱼眼图“天生”的桶形畸变进行矫正。

                具体效果类似于“用手对着圆形中心做挤压,把它压平”,使得真实世界中的直线,在矫正后依然是直线。

                采用棋盘标定法进行矫正后:

                我们发现:

                ① 现实世界中的直线,在鱼眼图中发生了扭曲(如鱼眼图中的蓝色和绿色曲线),矫正后变成了直线(如正方形图中的蓝色和绿色直线);

                ② 矫正图只占据了鱼眼图中间的一部分(如鱼眼图中的红色曲线)。

                从这个矫正效果中,可以看出:棋盘标定法的缺点,是靠近圆周(外围区域)的区域,会被拉伸的很严重,视觉效果变差。

                所以一般会进行切除,导致矫正后的图片只保留了原图的中间区域。

                基于以上特点,在实际使用中,我会把棋盘标定法,作为简单测量的前置任务(矫正图中的两点距离和真实世界中的两点距离,存在一一对应的关系)。

                也可以作为鱼眼图像拼接的前置任务(真实世界中的三点共线,在拼接图中依然共线)。

                横向展开法

                横向展开法,主要是利用鱼眼相机的大FOV和俯视拍摄的特点,来进行变形。

                比如我们把上图中的红点,想象成一个观察者,当他身体旋转360度,看到的什么样的画面呢?

                上图是经过横向展开法,变形后的画面。

                可以看到,从原先的俯视视角变为了正视视角。

                因此可以根据区域功能,进行切片,再用普通视角的检测模型,做后续任务。

                但是缺点也一目了然,比如展开图的左右两侧,在真实世界中应该是连通的。

                所以当有目标在鱼眼图中穿过分界线时,在展开图中该目标会从左侧消失,右侧出现(或者倒过来),看起来不是很自然。

                基于以上特点,在实际使用中,我会利用鱼眼相机,覆盖面积大的特点(比如3米层高的情况下,至少覆盖100平米),在“某些场景”中取代枪机或半球机,画面展开后用正常的检测器去完成后续任务。

                这里还要补充两点:

                ① COCO数据集上训练的人体检测器,在鱼眼图中直接使用是不会work的;

                ② 与棋盘标定法不同,横向展开不会损失像素,所以展开图也可以再remap回鱼眼原图

                经纬度法

                经纬度法主要分为两个方面:

                ① 经度

                下图是鱼眼图沿着经度对齐矫正后的画面。

                该方法与棋盘矫正法相比,没有像素损失,也不需要标定(人为设计规则求解remap matrix)。

                但是缺点也很明显,它只对竖直方向(图中的蓝色线和绿色线)进行了矫正,而水平方向(红色线)依然是扭曲的。

                ② 纬度

                下图是鱼眼图沿着纬度对齐矫正后的画面。

                可以看到,只对水平方向(图中的蓝色线和绿色线)进行了矫正,而竖直方向(红色线)依然是扭曲的。

                基于以上特点,在实际落地中,我没有采用经纬度矫正法。

                更多的是在学习和研究阶段,把它当作设计和计算remap matrix的一个作业。

                接收和发布图像

                参考:

                ROS中C++ boost编程,类内回调函数

                关于ROS在一个回调函数中处理两个订阅话题消息(多话题回调、多参数回调问题)

                ROS之订阅多个话题并对其进行同步处理(多传感器融合)

                同时订阅双目图像

                订阅多个话题并对其进行同步处理,我这里订阅两个图像后再对图像做处理,使用同一个回调函数

                在主函数中实现

                利用全局变量 TimeSynchronizer

                opencv官方方法:http://wiki.ros.org/action/fullsearch/message_filters?action=fullsearch&context=180&value=linkto%3A%22message_filters%22#Subscriber

                #include    2 #include    3 #include    4 #include    5 #include    6    7 using namespace sensor_msgs;   8 using namespace message_filters;   9   10 void callback(const ImageConstPtr& image, const CameraInfoConstPtr& cam_info)  11 {  12   // Solve all of perception here...  13 }  14   15 int main(int argc, char** argv)  16 {  17   ros::init(argc, argv, "vision_node");  18   19   ros::NodeHandle nh;  20   message_filters::Subscriber image_sub(nh, "image", 1);  21   message_filters::Subscriber info_sub(nh, "camera_info", 1);  22   23   typedef sync_policies::ExactTime MySyncPolicy;  24   // ExactTime takes a queue size as its constructor argument, hence MySyncPolicy(10)  25   Synchronizer sync(MySyncPolicy(10), image_sub, info_sub);  26   sync.registerCallback(boost::bind(&callback, _1, _2));  27   28   ros::spin();  29   30   return 0;  31 }

                我的实现

                #include "UseT265Cam.h"#include #include #include #include #include #include void StereoImageCallback(const sensor_msgs::ImageConstPtr &img1_msg, const sensor_msgs::ImageConstPtr &img2_msg) {    // 将ROS图像消息转换为OpenCV图像    cv_bridge::CvImagePtr cv_image_left = cv_bridge::toCvCopy(img1_msg, sensor_msgs::image_encodings::MONO8);    cv_bridge::CvImagePtr cv_image_right = cv_bridge::toCvCopy(img2_msg, sensor_msgs::image_encodings::MONO8);    cv::Mat image_left = cv_image_left->image.clone();    cv::Mat image_right = cv_image_right->image.clone();    std::cout     // 初始化ROS节点    ros::init(argc, argv, "t265_viewer");    // 创建RealsenseViewer T265对象    UseT265Cam *t265_cam = new UseT265Cam();ros::NodeHandle nh;message_filters::Subscriber    sub_1 = new message_filters::SubscriberregisterCallback(boost::bind(&UseT265Cam::StereoImageCallback, this, _1, _2));

                使用Opencv库订阅T265图像

                T265启动后的话题为

                /camera/accel/imu_info/camera/accel/metadata/camera/accel/sample/camera/fisheye1/camera_info/camera/fisheye1/image_raw/camera/fisheye1/image_raw/compressed/camera/fisheye1/image_raw/compressed/parameter_descriptions/camera/fisheye1/image_raw/compressed/parameter_updates/camera/fisheye1/image_raw/compressedDepth/camera/fisheye1/image_raw/compressedDepth/parameter_descriptions/camera/fisheye1/image_raw/compressedDepth/parameter_updates/camera/fisheye1/image_raw/theora/camera/fisheye1/image_raw/theora/parameter_descriptions/camera/fisheye1/image_raw/theora/parameter_updates/camera/fisheye1/metadata/camera/fisheye2/camera_info/camera/fisheye2/image_raw/camera/fisheye2/image_raw/compressed/camera/fisheye2/image_raw/compressed/parameter_descriptions/camera/fisheye2/image_raw/compressed/parameter_updates/camera/fisheye2/image_raw/compressedDepth/camera/fisheye2/image_raw/compressedDepth/parameter_descriptions/camera/fisheye2/image_raw/compressedDepth/parameter_updates/camera/fisheye2/image_raw/theora/camera/fisheye2/image_raw/theora/parameter_descriptions/camera/fisheye2/image_raw/theora/parameter_updates/camera/fisheye2/metadata/camera/gyro/imu_info/camera/gyro/metadata/camera/gyro/sample/camera/odom/metadata/camera/odom/sample/camera/realsense2_camera_manager/bond/camera/tracking_module/parameter_descriptions/camera/tracking_module/parameter_updates

                常用的有

                /camera/fisheye1/image_raw/camera/fisheye2/image_raw/camera/accel/imu_info/camera/accel/metadata/camera/accel/sample/camera/gyro/imu_info/camera/gyro/metadata/camera/gyro/sample
                 #include#include#include #include #include#includeusing namespace std;using namespace cv;int main(int argc,char** argv){    rs2::config cfg;    // 使能 左右目图像数据    cfg.enable_stream(RS2_STREAM_FISHEYE,1, RS2_FORMAT_Y8);    cfg.enable_stream(RS2_STREAM_FISHEYE,2, RS2_FORMAT_Y8);    // 使能 传感器的POSE和6DOF IMU数据    cfg.enable_stream(RS2_STREAM_POSE, RS2_FORMAT_6DOF);    rs2::pipeline pipe;    pipe.start(cfg);    rs2::frameset data;    while (1)   {    data = pipe.wait_for_frames();// Get a frame from the pose streamauto f = data.first_or_default(RS2_STREAM_POSE);auto pose = f.as().get_pose_data();cout

上一篇:minecraft怎么创造服务器?

下一篇:公司的服务器转让怎么操作?