VINS-Fusion代码阅读(五)

2023-05-16

对应解析13页,四、前端视觉处理
主要包括特征点检测特征点跟踪两部分,似乎是基于openCV实现的。openCV2.4官方文档,(暂时未找到openCV3.4有类似的文档)
这一节在解析中的内容较少(可能崔神认为比较简单),然而于我个人而言,它依然是个新东西。所以,仍然得仔细推敲。
(发现VINS-Mono和VINS-Fusion之中的文件组织结构也略有差别,之后进行一下详细对比,先主要阅读VINS-Fusion的代码)

featureTracker文件夹下有个feature_tracker.h文件,其中定义了一个FeatureTracker类。
发现通篇仅有一个成员函数FeatureTracker::trackImage()中调用了cv::goodFeaturesToTracke()函数来检测特征点,因此,从该函数开始分析:
输入参数:_cur_time_img_img1
返回参数:map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> (乍看起来可复杂,细细分析之)首先是一个map,键值对中的是一个vector,其中每个元素是一个pair。具体什么含义?

map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> trackImage(double _cur_time, const cv::Mat &_img, const cv::Mat &_img1 = cv::Mat());

(洋洋洒洒写了200行左右,看来并不简单)
TicToc是一个类,感觉和计时有关,使用std::chrono实现(The elements in this header deal with time.)暂时不展开。
predict_pts,cur_pts:类数据成员,vector<cv::Point2f> predict_pts,cur_pts;
hadPrediction:数据成员,bool hasPrediction;,初始值为什么?(254行赋值为hasPrediction = false;)

如果值为true,则调用openCV的函数进行光流跟踪:
发现该cv::calcOpticalFlowPyrLK()函数所需参数好多。因为是核心,此处展开来讲(calcOpticalFlowPyrLK官方介绍):
函数功能:Calculates an optical flow for a sparse feature set using the iterative Lucas-Kanade method with pyramids.
使用带金字塔的迭代Lucas Kanade方法 计算稀疏特征集的光流。
C++ API:
void calcOpticalFlowPyrLK(InputArray prevImg, InputArray nextImg, InputArray prevPts, InputOutputArray nextPts, OutputArray status, OutputArray err, Size winSize=Size(21,21), int maxLevel=3, TermCriteria criteria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01), int flags=0, double minEigThreshold=1e-4 )
详细解释每个参数的含义:
prevImg – first 8-bit input image or pyramid constructed by buildOpticalFlowPyramid().

nextImg – second input image or pyramid of the same size and the same type as prevImg.

prevPts – vector of 2D points for which the flow needs to be found; point coordinates must be single-precision floating-point numbers.
需要找到流的二维点(存放在vector里),点坐标必须是单精度浮点型数字。

nextPts – output vector of 2D points containing the calculated new positions of input features in the second image; when OPTFLOW_USE_INITIAL_FLOW flag is passed, the vector must have the same size as in the input.
在第二个图像中,计算得到的输入特征所在的新位置的二维点(存放在vector里);当标志位为OPTFLOW_USE_INITIAL_FLOW时,该vector必须与输入中的vector大小相同。

status – output status vector (of unsigned chars); each element of the vector is set to 1 if the flow for the corresponding features has been found, otherwise, it is set to 0.

err – output vector of errors; each element of the vector is set to an error for the corresponding feature, type of the error measure can be set in flags parameter; if the flow wasn’t found then the error is not defined (use the status parameter to find such cases).

winSize – size of the search window at each pyramid level.

maxLevel – 0-based maximal pyramid level number; if set to 0, pyramids are not used (single level), if set to 1, two levels are used, and so on; if pyramids are passed to input then algorithm will use as many levels as pyramids have but no more than maxLevel.

criteria – parameter, specifying the termination criteria of the iterative search algorithm (after the specified maximum number of iterations criteria.maxCount or when the search window moves by less than criteria.epsilon.
算法终止条件,1.最大迭代次数次数maxCount;2.精度criteria.epsilon

flags –
OPTFLOW_USE_INITIAL_FLOW uses initial estimations, stored in nextPts; if the flag is not set, then prevPts is copied to nextPts and is considered the initial estimate.
OPTFLOW_LK_GET_MIN_EIGENVALS use minimum eigen values as an error measure (see minEigThreshold description); if the flag is not set(默认度量), then L1 distance between patches around the original and a moved point, divided by number of pixels in a window, is used as a error measure.
作为误差度量

minEigThreshold – the algorithm calculates the minimum eigen value (最小特征值)of a 2x2 normal matrix of optical flow equations (this matrix is called a spatial gradient matrix in [Bouguet00]), divided by number of pixels in a window; if this value is less than minEigThreshold, then a corresponding feature is filtered out and its flow is not processed, so it allows to remove bad points and get a performance boost(剔除坏点,提高性能).

有了这些理解,看一下代码中调用的时候,传入参数表示的含义:
prev_img, cur_img:上一帧图片,当前帧图片;
prev_pts, cur_pts:特征点在上一帧中的位置(等待找到对应的流),特整点在当前帧中的位置(用以保存该算法计算结果);
statusvector<uchar> status;
errvector<float> err;
cv::Size(21, 21):在每层金字塔中搜索窗的大小( 21 × 21 21\times 21 21×21);
1: 对应两层;
cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 30, 0.01):终止条件——迭代次数 和 精度;
cv::OPTFLOW_USE_INITIAL_FLOW:保持和原来一样多特征点数。

然后,统计跟踪成功的点数(即status1的个数)
【如果】跟踪成功的点数少于10个,对应一些处理。(再执行一次,这次修改传入参数中金字塔的层数为4层)

============ 分割线 =====================
假设跟踪成功的点的数目足够多了,进行下一步处理操作:
FLOW_BACK:暂时未找到该变量定义的地方。。
但是,其对应函数块的含义是清晰的,即,当FLOW_BACKtrue时,再调用cv::calcOpticalFlowPyrLK()进行一次反向的光流跟踪(传入参数时将两幅图像、特征点做对应调换)

符合以下要求的才认为是跟踪成功的点:
status[i] && reverse_status[i] && distance(prev_pts[i], reverse_pts[i]) <= 0.5

=============== 分割线 ==================
将跟踪失败的点删除(即当status[i]值为0,删除各vector中下标i对应的元素):
reduceVector(prev_pts, status);
reduceVector(cur_pts, status);
reduceVector(ids, status);
reduceVector(track_cnt, status);
ids, track_cntFeatureTracker类的数据成员vector<int> ids;vector<int> track_cnt;

============ 分割线 ====================
vectortrack_cnt中所有的计数值,统一 +1。
调用一个setMask()成员函数,其主要完成了按照track_cntcur_pts, ids, track_cnt的排序,并且创建了一个mask,以点cur_pts为圆心,填充了一个给定大小的黑色圆,其余位置为白色。
详细的介绍放在了 VINS-Fusion代码阅读(六)里。

调用另外一个openCV函数cv::goodFeaturesToTrack(cur_img, n_pts, MAX_CNT - cur_pts.size(), 0.01, MIN_DIST, mask);,可以猜测其想实现的功能为,在图片的其他位置,再检测出给定数目MAX_CNT - cur_pts.size()的特征点,放在n_pts中。
同样,详细的介绍放在了 VINS-Fusion代码阅读(六)里。

这样,将新检测到的特征点n_pts添加到cur_pts中去,并赋予相应的idstrack_cnt

++++++++++++++ 新分割线 +++++++++++++++++
下一步,调用undistortedPts成员函数,该函数比较简单,主要想实现的功能是
将像素坐标系下的坐标,转换为归一化相机坐标系下的坐标?即cur_un_pts为归一化相机坐标系下的坐标。为什么使用的是m_camera[0]
cur_un_pts = undistortedPts(cur_pts, m_camera[0]);
其中,vector<camodocal::CameraPtr> m_camera;是一个vector,其中元素为CameraPtr类型,下一步需要了解一下该类型中成员函数liftProjective()的功能与实现。

vector<cv::Point2f> FeatureTracker::undistortedPts(vector<cv::Point2f> &pts, camodocal::CameraPtr cam)
{
    vector<cv::Point2f> un_pts;
    for (unsigned int i = 0; i < pts.size(); i++)
    {
        Eigen::Vector2d a(pts[i].x, pts[i].y);
        Eigen::Vector3d b;
        cam->liftProjective(a, b);
        un_pts.push_back(cv::Point2f(b.x() / b.z(), b.y() / b.z()));
    }
    return un_pts;
}

接下来,计算pts_velocity,其为当前帧相对于前一帧 特征点沿x,y方向的像素移动速度。
pts_velocity = ptsVelocity(ids, cur_un_pts, cur_un_pts_map, prev_un_pts_map);
cur_un_pts_map中存放ids[i]cur_un_pts[i]构成的键值对。
prev_un_pts_map非空的情况下,对每一个cur_un_pts_map中的键值对都去寻找是否在prev_un_pts_map中存在,若存在像素移动速度不难计算;若不存在则为0;
如果prev_un_pts_map是空的情况下,置零。

vector<cv::Point2f> FeatureTracker::ptsVelocity(vector<int> &ids, vector<cv::Point2f> &pts, 
                                            map<int, cv::Point2f> &cur_id_pts, map<int, cv::Point2f> &prev_id_pts)
{
    vector<cv::Point2f> pts_velocity;
    cur_id_pts.clear();
    for (unsigned int i = 0; i < ids.size(); i++)
    {
        cur_id_pts.insert(make_pair(ids[i], pts[i]));
    }

    // caculate points velocity
    if (!prev_id_pts.empty())
    {
        double dt = cur_time - prev_time;
        
        for (unsigned int i = 0; i < pts.size(); i++)
        {
            std::map<int, cv::Point2f>::iterator it;
            it = prev_id_pts.find(ids[i]);
            if (it != prev_id_pts.end())
            {
                double v_x = (pts[i].x - it->second.x) / dt;
                double v_y = (pts[i].y - it->second.y) / dt;
                pts_velocity.push_back(cv::Point2f(v_x, v_y));
            }
            else
                pts_velocity.push_back(cv::Point2f(0, 0));

        }
    }
    else
    {
        for (unsigned int i = 0; i < cur_pts.size(); i++)
        {
            pts_velocity.push_back(cv::Point2f(0, 0));
        }
    }
    return pts_velocity;
}

解析(14页)关于这一部分——前段视觉处理就结束了;代码中,由于支持双目相机因此下面对于双目相机的另一幅图像_img1进行处理。

++++++++++++ 分割线 +++++++++++++++++++++
if(!_img1.empty() && stereo_cam){ }
不太理解的是,为什么双目的时候,光流跟踪是在左右两幅图像之间进行?
cv::calcOpticalFlowPyrLK(cur_img, rightImg, cur_pts, cur_right_pts, status, err, cv::Size(21, 21), 3);
不同之处:使用的是m_camera[1]
cur_un_right_pts = undistortedPts(cur_right_pts, m_camera[1]);
像素速度的计算是依据 右边的先后两个点云:
right_pts_velocity = ptsVelocity(ids_right, cur_un_right_pts, cur_un_right_pts_map, prev_un_right_pts_map);

+++++++++++++ 分割线 +++++++++++++++++++
最最后,是一些数据的保存工作。重要的数据成员罗列如下:
prev_img = cur_img;
prev_pts = cur_pts;
prev_un_pts = cur_un_pts;
prev_un_pts_map = cur_un_pts_map;
prev_time = cur_time;
prevLeftPtsMap[ids[i]] = cur_pts[i]; IDcur_pts像素位置的点云图(用在了绘图展示中)

最后,返回值map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> featureFrame
其中Eigen::Matrix<double, 7, 1> xyz_uv_velocity;包含跟踪点归一化相机坐标系下的坐标3,像素平面上的坐标2,像素移动速度2。
featureFrame[feature_id].emplace_back(camera_id, xyz_uv_velocity);
此处很神奇,我们可以看出没有使用make_pair,更没有使用vector.push_back(),简洁的一句.emplace_back()就搞定了!了解其使用!

同样地,对右边一个相机有相同的操作,featureFrame[feature_id].emplace_back(camera_id, xyz_uv_velocity);不同的是camera_id,左边是0,右边是1

============== 下面是源代码 可以不看了呀!==============

map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> FeatureTracker::trackImage(double _cur_time, const cv::Mat &_img, const cv::Mat &_img1)
{
    TicToc t_r;
    cur_time = _cur_time;
    cur_img = _img;
    row = cur_img.rows;
    col = cur_img.cols;
    cv::Mat rightImg = _img1;  // 注意此处是一个浅拷贝
    
    cur_pts.clear(); // vector的clear成员函数

    if (prev_pts.size() > 0)
    {
        TicToc t_o;
        vector<uchar> status;
        vector<float> err;
        if(hasPrediction)
        {
            cur_pts = predict_pts;
            cv::calcOpticalFlowPyrLK(prev_img, cur_img, prev_pts, cur_pts, status, err, cv::Size(21, 21), 1, 
            cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 30, 0.01), cv::OPTFLOW_USE_INITIAL_FLOW);
            
            int succ_num = 0;
            for (size_t i = 0; i < status.size(); i++)
            {
                if (status[i])
                    succ_num++;
            }
            if (succ_num < 10)
               cv::calcOpticalFlowPyrLK(prev_img, cur_img, prev_pts, cur_pts, status, err, cv::Size(21, 21), 3);
        }
        else
            cv::calcOpticalFlowPyrLK(prev_img, cur_img, prev_pts, cur_pts, status, err, cv::Size(21, 21), 3);
        // reverse check
        if(FLOW_BACK)
        {
            vector<uchar> reverse_status;
            vector<cv::Point2f> reverse_pts = prev_pts;
            cv::calcOpticalFlowPyrLK(cur_img, prev_img, cur_pts, reverse_pts, reverse_status, err, cv::Size(21, 21), 1, 
            cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 30, 0.01), cv::OPTFLOW_USE_INITIAL_FLOW);
            //cv::calcOpticalFlowPyrLK(cur_img, prev_img, cur_pts, reverse_pts, reverse_status, err, cv::Size(21, 21), 3); 
            for(size_t i = 0; i < status.size(); i++)
            {
                if(status[i] && reverse_status[i] && distance(prev_pts[i], reverse_pts[i]) <= 0.5)
                {
                    status[i] = 1;
                }
                else
                    status[i] = 0;
            }
        }
        
        for (int i = 0; i < int(cur_pts.size()); i++)
            if (status[i] && !inBorder(cur_pts[i]))
                status[i] = 0;
        reduceVector(prev_pts, status);
        reduceVector(cur_pts, status);
        reduceVector(ids, status);
        reduceVector(track_cnt, status);
        ROS_DEBUG("temporal optical flow costs: %fms", t_o.toc());
        //printf("track cnt %d\n", (int)ids.size());
    }

    for (auto &n : track_cnt)
        n++;

    if (1)
    {
        //rejectWithF();
        ROS_DEBUG("set mask begins");
        TicToc t_m;
        setMask();
        ROS_DEBUG("set mask costs %fms", t_m.toc());

        ROS_DEBUG("detect feature begins");
        TicToc t_t;
        int n_max_cnt = MAX_CNT - static_cast<int>(cur_pts.size());
        if (n_max_cnt > 0)
        {
            if(mask.empty())
                cout << "mask is empty " << endl;
            if (mask.type() != CV_8UC1)
                cout << "mask type wrong " << endl;
            cv::goodFeaturesToTrack(cur_img, n_pts, MAX_CNT - cur_pts.size(), 0.01, MIN_DIST, mask);
        }
        else
            n_pts.clear();
        ROS_DEBUG("detect feature costs: %f ms", t_t.toc());

        for (auto &p : n_pts)
        {
            cur_pts.push_back(p);
            ids.push_back(n_id++);
            track_cnt.push_back(1);
        }
        //printf("feature cnt after add %d\n", (int)ids.size());
    }

    cur_un_pts = undistortedPts(cur_pts, m_camera[0]);
    pts_velocity = ptsVelocity(ids, cur_un_pts, cur_un_pts_map, prev_un_pts_map);

    if(!_img1.empty() && stereo_cam)
    {
        ids_right.clear();
        cur_right_pts.clear();
        cur_un_right_pts.clear();
        right_pts_velocity.clear();
        cur_un_right_pts_map.clear();
        if(!cur_pts.empty())
        {
            //printf("stereo image; track feature on right image\n");
            vector<cv::Point2f> reverseLeftPts;
            vector<uchar> status, statusRightLeft;
            vector<float> err;
            // cur left ---- cur right
            cv::calcOpticalFlowPyrLK(cur_img, rightImg, cur_pts, cur_right_pts, status, err, cv::Size(21, 21), 3);
            // reverse check cur right ---- cur left
            if(FLOW_BACK)
            {
                cv::calcOpticalFlowPyrLK(rightImg, cur_img, cur_right_pts, reverseLeftPts, statusRightLeft, err, cv::Size(21, 21), 3);
                for(size_t i = 0; i < status.size(); i++)
                {
                    if(status[i] && statusRightLeft[i] && inBorder(cur_right_pts[i]) && distance(cur_pts[i], reverseLeftPts[i]) <= 0.5)
                        status[i] = 1;
                    else
                        status[i] = 0;
                }
            }

            ids_right = ids;
            reduceVector(cur_right_pts, status);
            reduceVector(ids_right, status);
            // only keep left-right pts
            /*
            reduceVector(cur_pts, status);
            reduceVector(ids, status);
            reduceVector(track_cnt, status);
            reduceVector(cur_un_pts, status);
            reduceVector(pts_velocity, status);
            */
            cur_un_right_pts = undistortedPts(cur_right_pts, m_camera[1]);
            right_pts_velocity = ptsVelocity(ids_right, cur_un_right_pts, cur_un_right_pts_map, prev_un_right_pts_map);
        }
        prev_un_right_pts_map = cur_un_right_pts_map;
    }
    if(SHOW_TRACK)
        drawTrack(cur_img, rightImg, ids, cur_pts, cur_right_pts, prevLeftPtsMap);

    prev_img = cur_img;
    prev_pts = cur_pts;
    prev_un_pts = cur_un_pts;
    prev_un_pts_map = cur_un_pts_map;
    prev_time = cur_time;
    hasPrediction = false;

    prevLeftPtsMap.clear();
    for(size_t i = 0; i < cur_pts.size(); i++)
        prevLeftPtsMap[ids[i]] = cur_pts[i];

    map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> featureFrame;
    for (size_t i = 0; i < ids.size(); i++)
    {
        int feature_id = ids[i];
        double x, y ,z;
        x = cur_un_pts[i].x;
        y = cur_un_pts[i].y;
        z = 1;
        double p_u, p_v;
        p_u = cur_pts[i].x;
        p_v = cur_pts[i].y;
        int camera_id = 0;
        double velocity_x, velocity_y;
        velocity_x = pts_velocity[i].x;
        velocity_y = pts_velocity[i].y;

        Eigen::Matrix<double, 7, 1> xyz_uv_velocity;
        xyz_uv_velocity << x, y, z, p_u, p_v, velocity_x, velocity_y;
        featureFrame[feature_id].emplace_back(camera_id,  xyz_uv_velocity);
    }

    if (!_img1.empty() && stereo_cam)
    {
        for (size_t i = 0; i < ids_right.size(); i++)
        {
            int feature_id = ids_right[i];
            double x, y ,z;
            x = cur_un_right_pts[i].x;
            y = cur_un_right_pts[i].y;
            z = 1;
            double p_u, p_v;
            p_u = cur_right_pts[i].x;
            p_v = cur_right_pts[i].y;
            int camera_id = 1;
            double velocity_x, velocity_y;
            velocity_x = right_pts_velocity[i].x;
            velocity_y = right_pts_velocity[i].y;

            Eigen::Matrix<double, 7, 1> xyz_uv_velocity;
            xyz_uv_velocity << x, y, z, p_u, p_v, velocity_x, velocity_y;
            featureFrame[feature_id].emplace_back(camera_id,  xyz_uv_velocity);
        }
    }

    //printf("feature track whole time %f\n", t_r.toc());
    return featureFrame;
}

PS. 如何使用QT打开已有的ROS工作空间?
目前使用的方法仅仅可以打开并查看,其中一系列的“未找到定义”的错误。当然也无法编译。
相关的文章主要查看了:
创客空间——ROS与C++入门教程-搭建开发环境(QT+ros_qtc_plugin),通过最后新建工作空间的方法。
QT打开ROS工作空间时遇到的问题和解决方法,按照该方法执行了,但依然未能成功。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VINS-Fusion代码阅读(五) 的相关文章

  • SLAM之小觅相机跑开源方案(ORB_SLAM2,VINS MONO,VINS FUSION,RTAB-Map)

    传感器 xff1a 小觅相机标准版 开源SLAM方案 xff1a ORB SLAM2 xff0c VINS MONO xff0c VINS FUSION xff0c RTAB Map 测试地点 xff1a 室内大厅 xff08 光线不均 x
  • 工程(十一)——NUC11+D435i+VINS-FUSION+ESDF建图(github代码)

    博主的合并代码 git 64 github com huashu996 VINS FUSION ESDFmap git 一 D435i深度相机配置 1 1 SDK 43 ROS 参考我之前的博客 xff0c 步骤和所遇见的问题已经写的很详细
  • 【SLAM】VINS-MONO解析——综述

    目前网上有很多分析文章 xff0c 但是都只是一些比较基础的原理分析 xff0c 而且很多量 xff0c 虽然有推倒 xff0c 但是往往没有讲清楚这些量是什么 xff0c 为什么要有这些量 xff0c 这些量是从哪来的 xff0c 也没有
  • 【SLAM】VINS-MONO解析——IMU预积分

    4 IMU预积分 IMU预积分主要干了2件事 xff0c 第一个是IMU预积分获得 值 xff0c 另一个是误差传递函数的获取 本部分的流程图如下图所示 各个部分的讲解如下链接 xff1a SLAM VINS MONO解析 综述 SLAM
  • vins 解读_代码解读 | VINS_Mono中的鱼眼相机模型

    本文作者是计算机视觉life公众号成员蔡量力 xff0c 由于格式问题部分内容显示可能有问题 xff0c 更好的阅读体验 xff0c 请查看原文链接 xff1a 代码解读 VINS Mono中的鱼眼相机模型 VINS Mono中的鱼眼相机模
  • Ubuntu 18.04 运行PL-VINS

    代码地址 https span class token operator span span class token comment github com cnqiangfu PL VINS span 安装过程出错参考 PL VINS配置
  • Mac上vmware fusion装的ubuntu不能与主机复制粘贴的问题

    解决方法一 xff1a 安装vmware tools 依次点击 xff1a 虚拟机 gt 安装vmware tools 会在ubuntu桌面上出现vmware tools xff0c 双击打开 解压tar gz包 xff0c 执行解压命令t
  • VINS-Mono

    文章目录 初始化框架缺点ORB SLAM的Local Map VINS的滑窗 逐次逼近式去畸变给后端提供的特征点信息光流追踪对极约束F去除外点 rejectWithF 特征点均匀化预积分系统初始化初始化时不校正bias a误差卡尔曼滤波误差
  • NVIDIA Jetson Xavier NX部署VINS-fusion-GPU

    组内大佬师兄今天抽出时间总结了一篇博客 xff0c 主要内容是 xff1a 把在阿木P450无人机上 xff0c 对自带的NVIDIA Jetson Xavier NX边缘计算机部署VINS fusion GPU教程 xff0c 并进行实验
  • Ubuntu20.04运行Vins-fusion

    因已安装ROS noetic xff0c eigen xff0c opencv及ceres等库 xff0c 这部分环境配置就不细讲 xff0c 直接下载VINS FUSION的代码库 下载编译 cd span class token ope
  • VINS-FUSION-GPU在jetson nx上的实现

    需要安装经过修改的Ubuntu18系统 https span class token operator span span class token comment developer nvidia com zh cn embedded do
  • TX2上布置vins_fusion_gpu指南

    1 参考链接 如果初次安装 xff0c 新的TX2环境 xff0c 请参考文档 https github com arjunskumar vins fusion gpu tx2 nano 2 问题记录 1 xff0c 自己的环境情况 我的环
  • 在TUMVI数据集上测试VINS-Fusion算法

    VINS Fusion算法是一个非常优秀的视觉惯性里程计 但原版VINS Fusion并没有提供与TUM数据集相应的配置文件 因此需要自己进行写yaml文件 修改配置文件 tum mono yaml span class token dir
  • VINS-mono 解析 新特征

    在17 12 29 xff0c VINS更新了代码加入了新的特征 xff0c 包括map merge 地图合并 pose graph reuse 位姿图重利用 online temporal calibration function 在线时
  • 从零实现vins-mono+fast-planner+M100无人机实验在现实场景中的应用

    版权声明 本文为博主原创文章 未经博主允许不能随意转载 本文链接 https blog csdn net AnChenliang 1002 article details 109535355 最近由于科研的需要 要将VINS mono与fa
  • VINS记录

    euroc launch lt launch gt lt arg name 61 34 config path 34 default 61 34 find feature tracker config euroc euroc config
  • VINS-RGBD运行指令

    创建工程VINS RGBD catkin ws 将代码放入src文件夹当中 git clone https github com STAR Center VINS RGBD 进行编译 cd VINS RGBD catkin ws catki
  • Ubuntu 18.04 ———(Intel RealSense D435i)运行VINS-Mono

    Intel RealSense D435i 一 准备工作二 修改参数rs camera launchrealsense color config yaml 参考文献 一 准备工作 1 Intel Realsense D435i Ubuntu
  • 如何解决:自定义 MSBuild 任务需要在 AppBase 外部进行组装

    我有一个自定义任务 想要在构建 C 项目时执行 此任务位于 MyTask dll 中 它引用另一个程序集 MyCommon DLL 问题是 MyCommon dll 相对于 MyTask dll 位于 Common MyCommon dll
  • Java 流惰性 vs 融合 vs 短路

    我试图对 Java 流 API 中惰性求值的应用形成一个简洁而连贯的理解 目前我的理解是这样的 元素仅在需要时才被消耗 即流是惰性的 并且中间操作是惰性的 例如过滤器 仅在需要时进行过滤 中间操作可以融合在一起 如果它们是无状态的 短路操作

随机推荐

  • OpenCV图像处理学习十九,像素重映射cv::remap

    一 像素重映射概念 重映射就是把输入图像中各个像素按照制定的规则顺序映射到另外一张图像的对应位置上去 xff0c 形成一张新的图像 二 像素映射API函数接口 cv remap xff08 InputArray src 输入图像 Outpu
  • OpenCV图像处理学习二十一,直方图比较方法

    一 直方图比较 直方图比较是对输入的两张图像进行计算得到直方图H1与H2 xff0c 归一化到相同的尺度空间 xff0c 然后可以通过计算H1与H2的之间的距离得到两个直方图的相似程度 xff08 每张图像都有唯一的直方图与之对应 xff0
  • 嵌入式FreeRTOS学习九,任务链表的构成,TICK时间中断和任务状态切换调度

    一 tskTaskControlBlock 函数结构体 在tskTaskControlBlock 任务控制块结构体中 xff0c 其中有任务状态链表和事件链表两个链表成员 xff0c 首先介绍任务状态链表这个结构 xff0c 这个链表通常用
  • SOAP传输协议

    一 HTTP传输协议 超文本传输协议 xff08 HyperText Transfer Protocol xff0c 缩写 xff1a HTTP xff09 xff0c 它是基于请求 响应的模式协议 xff0c 客户端发出请求 xff0c
  • ONVIF简介

    一 什么是ONVIF ONVIF规范描述了网络视频的模型 接口 数据类型以及数据交互的模式 并复用了一些现有的标准 xff0c 如WS系列标准等 ONVIF规范的目标是实现一个网络视频框架协议 xff0c 使不同厂商所生产的网络视频产品 x
  • gsoap工具生成onvif设备搜索(remotediscovery)代码框架

    什么是gsoap工具 xff1f gSOAP 提供了两个工具来方便开发人员使用 C C 43 43 语言快速开发Web 服务应用 xff0c 通过 gSOAP 提供的这两个工具 xff0c 开发人员可以快速生成服务端与客户端代码框架 xff
  • Latex之给字符上加横线、波浪等

    Latex 前几天想在 x x x 上加波浪号 xff0c 一时间忘记怎么打 xff0c 现在记录下来 xff0c 以后好查阅 加 号 xff1a hat x 加横线 xff1a overline x 加宽 xff1a widehat x
  • 数据结构笔记-2(线性表)

    线性表 2 1 线性表 1 定义 是零个或多个具有相同类型的数据元素的有序数列 xff1b xff08 长度等于零的线性表为空表 xff09 非空线性表通常记为 xff1a L xff1d a 1 xff0c a 2 xff0c xff0c
  • 数据结构-6(图)

    图 图的逻辑结构 图的定义 xff1a 图是由顶点的有穷非空集合和顶点之间边的集合组成 xff0c 通常表示为 xff1a G 61 V xff0c E 其中 xff1a G表示一个图 xff0c V是图G中顶点的集合 xff0c E是图G
  • 【leetcode常见面试题】螺旋矩阵解题思路

    文章目录 螺旋矩阵解题思路先找行进路线找每条路线的结束位置再找每条路线的结束位置模拟行走 螺旋矩阵 II总结 螺旋矩阵 解题思路 本题可以采用模拟的方式 xff0c 设4种行走方向 xff0c 如下图 xff1a 先找行进路线 4个方向的行
  • C++面向对象程序设计学习心得

    C 43 43 面向对象程序设计学习心得 经过几周c 43 43 面向对象程序设计的学习 xff0c 对面向对象程序设计有了一些了解 递归 简单地讲 xff0c 递归就是程序直接或间接调用本身的编程技巧 xff0c 通过把一个不能或不好解决
  • STL学习心得

    STL概述 STL组件 1 容器 xff08 Container xff09 xff0d 管理某类对象的集合 2 迭代器 xff08 Iterator xff09 xff0d 在对象集合上进行遍历 xff08 注意 xff1a 这些集合可能
  • 安装nodejs和vue出现问题

    安装nodejs国内镜像时报错 npm install g cnpm registry 61 https registry npm taobao org不知道这是啥错误 xff0c 怎么改啊 xff1f 安装vue 从官网点击下载 下载后点
  • Jetson nano/nx通过网线连接电脑实现远程控制

    Jetson nano nx通过网线连接电脑实现远程控制 摘要1 nano nx桌面共享设置2 安装dconf editor解除加密3 自启VNC server4 网络共享5 获取IP地址6 安装PuTTy7 安装VNC Viewwer8
  • 一文解决MySQL突击面试,关键知识点总结

    文章目录 MySQL重要知识点回顾一 索引1 为什么需要索引2 索引的结构3 避免索引失效3 1 联合索引不满足最左匹配原则3 2 隐式转换3 3 like查询3 4 索引列存在运算或者使用函数3 5 优化器 4 执行计划4 1 type4
  • 51单片机应用篇-- --数码管60秒计时,独立按键可调

    开篇先说一句废话 本旺名字叫萨摩耶 xff0c xff0c Please 叫我旺财 xff0c xff0c xff0c 哈哈 xff0c 招财进宝嘛 xff01 缘由 本来按照我的学习计划 xff0c 我现在应该是单片机的学习过程 xff0
  • 【ESP32+freeRTOS学习笔记之“ESP32环境下使用freeRTOS的特性分析(3-多核环境下的调度)”】

    目录 1 不同核心上分别调度2 tick中断3 关于抢占4 关于同优级的任务按时间片调度5 空闲任务6 调度程序暂停7 启动和终止8 禁用中断9 总结 Vanilla FreeRTOS调度器是具有时间切片的固定优先级抢占调度器 xff0c
  • C++ STL 总结(持续更新)

    因为机试需要用c 43 43 xff0c 暴风吸入式学习 xff08 套用 xff09 它的模板 xff0c 发现还真的挺好用的 xff0c 总结一下 时间紧急 xff0c 取自各个网上的博客 xff0c 没来得及仔细整理 xff0c 都给
  • STM32F103笔记(二)——GPIO原理

    GPIO的工作原理与两个实验实例 一 STM32F103 GPIO说明1 stm32 GPIO引脚的主要功能2 GPIO相关配置寄存器的简介3 STM32F103 GPIO的8种工作方式4种输入模式4种输出模式 二 点亮LED实例 xff0
  • VINS-Fusion代码阅读(五)

    对应解析13页 xff0c 四 前端视觉处理 主要包括特征点检测和特征点跟踪两部分 xff0c 似乎是基于openCV实现的 openCV2 4官方文档 xff0c xff08 暂时未找到openCV3 4有类似的文档 xff09 这一节在