lsd-slam源码解读第四篇:tracking

本文深入解读lsd-slam的Tracking模块,包括LSGX类中的最小二乘法实现、TrackingReference的帧导入与点云构建、SE3Tracker的核心跟踪算法以及Relocalizer的重定位策略。通过代码分析,详细解释了关键函数的作用和流程,帮助读者理解Tracking过程中的帧间变换计算和优化方法。
摘要由CSDN通过智能技术生成

lsd-slam源码解读第四篇:tracking

标签(空格分隔): lsd-slam

进入这一篇之前,我希望你已经很明白了lsd-slam的算法,这很重要,如果不明白算法,阅读后续的代码无异于阅读天书,如果不太明白算法,请先阅读我写的第三篇:lsd-slam源码解读第三篇:算法解析


LGSX

于是就进入了Tracking文件夹里的第一个类型LSGX,这里面有3个类,分别是LSG4,LSG6和LSG7,他们定义了4个参数6个参数以及7个参数的最小二乘法
我想值得注意的是这段代码:

     inline void update(const Vector6& J, const float& res, const float& weight)
      {
        A.noalias() += J * J.transpose() * weight;
        b.noalias() -= J * (res * weight);
        error += res * res * weight;
        num_constraints += 1;
      }

这里使用了Eigen库里面的noalias机制,下面是Eigen库的官方文档:
2016-06-22-103823_1221x617_scrot.png-89.4kB

为了讲解这个用法,请看下面这个程序

#include <iostream>
#include <stdio.h>

#include <eigen3/Eigen/Dense>

using namespace std;

template <typename T>
class A {
public:
    A() {printf("A()\n");}
    ~A() {printf("~A()\n");}
    template <typename U>
    A operator*(A<U>&){
        A a;
        return a;
    }

};

int main()
{
    A<int> a1,b1;
    A<int> c1;
    c1 = a1*b1;
    Eigen::Matrix2f a, b, c;

    c.noalias() = a * b;
    a(0, 0) = 0;
    a(0, 1) = 1;
    a(1, 0) = 1;
    a(1, 1) = 0;
    b(0, 0) = 1;
    b(0, 1) = 2;
    b(1, 0) = 3;
    b(1, 1) = 4;
    c.noalias()  = a * b;
    std::cout << c << std::endl;

    return 0;
}

运行结果:

A()
A()
A()
A()
~A()
3 4
1 2
~A()
~A()
~A()

什么意思呢?注意我自己写的模板类A,在main函数里面有一个乘法c1 = a1*b1;,这个会调用重载运算符,然后先构造一个对象,再拷贝,最后析构,这样对于大量使用继承的程序会大大影响效率(构造基类构造子类,析构基类,析构子类,而且如果有内存申请,拷贝,释放,效率会降低更多),那怎么办呢?Eigen使用了一种机制,让我们减少一次对象的构造,这个机制就是用noalias()调用,这样相当于吧运行a*b的结果直接拷贝到c里面,不再去构造中间对象
如何实现呢,其实由于要考虑到泛化,代码内部实现比较复杂,但原理实际上是写一个算法类,算法类中储存了算法的函数对象(仿函数)或者函数指针和储存算法运算的结果容器的引用或者指针,然后调用算法的时候,不再去构造那个复杂的数据结构,转而构造一个简单的算法类,将运行结果赋值给容器的引用或指针,从而降低内存开销
程序中调用noalias()函数,实际上返回的是一个NoAlias的对象,这个对象维护了真正的乘法(实际上为了泛化,还向下有调用,可能会查看的代码和文档链接:NoAlias.h, MatrixBase.hNoAlias< Derived, MatrixBase > noalias()EigenBase< Derived > Struct Template Reference)


TrackingReference

讲了一大堆Eigen库和c++模板语法的东西,如果没有理解,忘了它吧,这个和Tracking算法没太大关系,我只是为了表现一下我的c++语法功底,扯了扯题外话而已。
不知你还记不记得之前第三篇里面讲的Tracking,如果记得的话,那么应该很清楚第一个过程是是选择参考帧,程序里面体现的就是这个类了

class TrackingReference
{
public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW

    /** Creates an empty TrackingReference with optional preallocation per level. */
    TrackingReference();
    ~TrackingReference();
    void importFrame(Frame* source);

    Frame* keyframe;
    boost::shared_lock<boost::shared_mutex> keyframeLock;
    int frameID;

    void makePointCloud(int level);
    void clearAll();
    void invalidate();
    Eigen::Vector3f* posData[PYRAMID_LEVELS];   // (x,y,z)
    Eigen::Vector2f* gradData[PYRAMID_LEVELS];  // (dx, dy)
    Eigen::Vector2f* colorAndVarData[PYRAMID_LEVELS];   // (I, Var)
    int* pointPosInXYGrid[PYRAMID_LEVELS];  // x + y*width
    int numData[PYRAMID_LEVELS];

private:
    int wh_allocated;
    boost::mutex accessMutex;
    void releaseAll();
};

void importFrame(Frame* source);

让我们来看第一个函数void importFrame(Frame* source);

void TrackingReference::importFrame(Frame* sourceKF)
{
    boost::unique_lock<boost::mutex> lock(accessMutex);
    keyframeLock = sourceKF->getActiveLock();
    keyframe = sourceKF;
    frameID=keyframe->id();


    // reset allocation if dimensions differ (shouldnt happen usually)
    if(sourceKF->width(0) * sourceKF->height(0) != wh_allocated)
    {
        releaseAll();
        wh_allocated = sourceKF->width(0) * sourceKF->height(0);
    }
    clearAll();
    lock.unlock();
}

前面几行是不难理解的,后面是一个判断,就是判断是否需要重置资源,如果宽度和高度的乘积和先前的高度与宽度的乘积一样大,那么就不用重新配置了,直接用先前的资源即可,如果不同,那么就释放先前左右的资源(请自行查看releaseAll()),然后把资源配置的记录记录成当前的配置数,最后再清空点云的记录数据numData(实际上就是令它等于0,请自行查看clearAll())


void makePointCloud(int level);

第二个重要的函数就是这个void makePointCloud(int level)了,代码如下

void TrackingReference::makePointCloud(int level)
{
    assert(keyframe != 0);
    boost::unique_lock<boost::mutex> lock(accessMutex);

    if(numData[level] > 0)
        return; // already exists.

    int w = keyframe->width(level);
    int h = keyframe->height(level);

    float fxInvLevel = keyframe->fxInv(level);
    float fyInvLevel = keyframe->fyInv(level);
    float cxInvLevel = keyframe->cxInv(level);
    float cyInvLevel = keyframe->cyInv(level);

    const float* pyrIdepthSource = keyframe->idepth(level);
    const float* pyrIdepthVarSource = keyframe->idepthVar(level);
    const float* pyrColorSource = keyframe->image(level);
    const Eigen::Vector4f* pyrGradSource = keyframe->gradients(level);

    if(posData[level] == nullptr) posData[level] = new Eigen::Vector3f[w*h];
    if(pointPosInXYGrid[level] == nullptr)
        pointPosInXYGrid[level] = (int*)Eigen::internal::aligned_malloc(w*h*sizeof(int));;
    if(gradData[level] == nullptr) gradData[level] = new Eigen::Vector2f[w*h];
    if(colorAndVarData[level] == nullptr) colorAndVarData[level] = new Eigen::Vector2f[w*h];

    Eigen::Vector3f* posDataPT = posData[level];
    int* idxPT = pointPosInXYGrid[level];
    Eigen::Vector2f* gradDataPT = gradData[level];
    Eigen::Vector2f* colorAndVarDataPT = colorAndVarD
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值