【SLAM】VINS-MONO解析——后端优化(代码部分)

2023-05-16

7.2 代码

在estimator.cpp的processImage()的最后,代码如下:

    else//solver_flag = NON_LINEAR,进行非线性优化
    {
        solveOdometry();  //<--1
        //边界判断:检测系统运行是否失败,若失败则重置估计器
        if (failureDetection()) //<--2
        {
            ROS_WARN("failure detection!");
            failure_occur = 1;
            clearState();
            setParameter();
            return;
        }

        slideWindow();//执行窗口滑动函数slideWindow();//<--3
        f_manager.removeFailures();//去除估计失败的点并发布关键点位置//<--4
        // prepare output of VINS
        key_poses.clear();//<--5
        for (int i = 0; i <= WINDOW_SIZE; i++)
            key_poses.push_back(Ps[i]);

        last_R = Rs[WINDOW_SIZE]; //<--6
        last_P = Ps[WINDOW_SIZE];
        last_R0 = Rs[0];
        last_P0 = Ps[0];
    }
}


在主流程中,可以发现干了6件事,其中1,3是最关键的2个步骤。而optimization()恰好在solveOdometry()里,所以可以发现,代码是先优化,后marg。

7.2.1 solveOdometry()和optimization() (核心!)

void Estimator::solveOdometry()
{
    if (frame_count < WINDOW_SIZE)
        return;
    if (solver_flag == NON_LINEAR)
    {
        f_manager.triangulate(Ps, tic, ric);//获得最新特征的深度//<--1
        optimization();//<--2
    }
}

这块也干了2件事,第一件事就是根据当前的位姿三角化特征点,这个在初始化的时候也出现过一次,见6.4.3.。

主要看这个optimization()函数。
第一部分:
(1)非线性优化
a. 声明和引入鲁棒核函数

ceres::Problem problem;
ceres::LossFunction *loss_function;//1.引入鲁棒核函数
loss_function = new ceres::CauchyLoss(1.0);

b. 添加各种待优化量X——位姿优化量

  for (int i = 0; i < WINDOW_SIZE + 1; i++)//还包括最新的第11帧
    {
        ceres::LocalParameterization *local_parameterization = new PoseLocalParameterization();
        problem.AddParameterBlock(para_Pose[i], 7, local_parameterization);
        problem.AddParameterBlock(para_SpeedBias[i], 9);

他把11帧的位姿都加上了。
这块出现了新数据结构para_Pose[i]和para_SpeedBias[i],这是因为ceres传入的都是double类型的,在vector2double()里初始化的。

c. 添加各种待优化量X——相机外参

for (int i = 0; i < NUM_OF_CAM; i++)//2.3.  7维、相机IMU外参
{
    ceres::LocalParameterization *local_parameterization = new PoseLocalParameterization();
    problem.AddParameterBlock(para_Ex_Pose[i], 6, local_parameterization);
    if (!ESTIMATE_EXTRINSIC)//如果IMU-相机外参不需要标定
        problem.SetParameterBlockConstant(para_Ex_Pose[i]);//这个变量固定为constant
}

d. 添加各种待优化量X——IMU-image时间同步误差

if (ESTIMATE_TD)//2.4.  1维,标定同步时间
{
    problem.AddParameterBlock(para_Td[0], 1);
}

待优化量的添加为什么没有视觉部分的待优化量(逆深度)?

e. vector2double()
给ParameterBlock赋值。

f. 添加各种残差——先验信残差

if (last_marginalization_info) 
{
    MarginalizationFactor *marginalization_factor = new MarginalizationFactor(last_marginalization_info);
    problem.AddResidualBlock(marginalization_factor, NULL,last_marginalization_parameter_blocks);
 }

这块出现了2个新的数据结构。在第一次执行这段代码的时候,没有先验信息,所以这段肯定是跳过的。当第二次执行的时候就有了。last_marginalization_info的赋值出现在后面的代码里。

数据结构: last_marginalization_info
这个数据结构定义在marginalization_factor.cpp里面,比较复杂。

class MarginalizationInfo
{
  public:
    ~MarginalizationInfo();
    int localSize(int size) const;
    int globalSize(int size) const;
    void addResidualBlockInfo(ResidualBlockInfo *residual_block_info);//添加参差块相关信息(优化变量,待marg的变量)
    void preMarginalize();//计算每个残差对应的雅克比,并更新parameter_block_data
    void marginalize();
    std::vector<double *> getParameterBlocks(std::unordered_map<long, double *> &addr_shift);

    std::vector<ResidualBlockInfo *> factors;//所有观测项
    int m, n;//m为要边缘化的变量个数,n为要保留下来的变量个数 m表示需marg变量/parameter_block_idx的总localSize n表示需保留变量的总localSize
    std::unordered_map<long, int> parameter_block_size; //global size//<优化变量内存地址,localSize>//为每个优化变量块的变量的大小,以IMU为例,是[7,9,7,9]
    int sum_block_size;
    std::unordered_map<long, int> parameter_block_idx; //local size //<优化变量内存地址,在矩阵中的id>//被merge掉的变量
    std::unordered_map<long, double *> parameter_block_data;//<优化变量内存地址,数据>//每个优化块数据
    //他们的key都同一是long类型的内存地址,而value分别是,各个优化变量的长度,各个优化变量在id以各个优化变量对应的double指针类型的数据。
    std::vector<int> keep_block_size; //global size
    std::vector<int> keep_block_idx;  //local size
    std::vector<double *> keep_block_data;
    //他们是进行边缘化之后保留下来的各个优化变量的长度,各个优化变量在id以各个优化变量对应的double指针类型的数据
    Eigen::MatrixXd linearized_jacobians;//分别指的是边缘化之后从信息矩阵恢复出来雅克比矩阵和残差向量
    Eigen::VectorXd linearized_residuals;
    const double eps = 1e-8;
};                                                                                                                

另一个数据结构就是last_marginalization_parameter_blocks。它指的是先验矩阵对应的状态量X。

数据结构: last_marginalization_parameter_blocks
    vector<double *> last_marginalization_parameter_blocks;//被边缘化留下的先验信息
它的赋值是在,vector<double *> parameter_blocks = marginalization_info->getParameterBlocks(addr_shift);

g. 添加各种残差——IMU残差

for (int i = 0; i < WINDOW_SIZE; i++)
{
    int j = i + 1;
    if (pre_integrations[j]->sum_dt > 10.0)
        continue;
    IMUFactor* imu_factor = new IMUFactor(pre_integrations[j]);
    problem.AddResidualBlock(imu_factor, NULL, para_Pose[i], para_SpeedBias[i], para_Pose[j], para_SpeedBias[j]);

h. 添加各种残差——重投影残差
这里需要再次注意一点,IMU的残差是相邻两帧,但是视觉不是的,见7.1.2-(2)。
分析一下代码,它加入的2帧,这两帧是观测到同一特征的最近两帧。

int f_m_cnt = 0;//统计有多少个特征用于非线性优化
int feature_index = -1;
for (auto &it_per_id : f_manager.feature)
{//遍历每一个特征
    it_per_id.used_num = it_per_id.feature_per_frame.size();
    if (!(it_per_id.used_num >= 2 && it_per_id.start_frame < WINDOW_SIZE - 2))
        continue;//必须满足出现2次以上且在倒数第二帧之前出现过
 
    ++feature_index;//统计有效特征数量          
    //!得到观测到该特征点的首帧
    int imu_i = it_per_id.start_frame, imu_j = imu_i - 1;
    //!得到首帧观测到的特征点的归一化相机坐标
    Vector3d pts_i = it_per_id.feature_per_frame[0].point;

    for (auto &it_per_frame : it_per_id.feature_per_frame)
    {//遍历当前特征在每一帧的信息 
        imu_j++;
        if (imu_i == imu_j)         
            continue;
        Vector3d pts_j = it_per_frame.point;//!得到第二个特征点
        if (ESTIMATE_TD)//在有同步误差的情况下
        {
                ProjectionTdFactor *f_td = new ProjectionTdFactor(pts_i, pts_j, it_per_id.feature_per_frame[0].velocity, it_per_frame.velocity,it_per_id.feature_per_frame[0].cur_td, it_per_frame.cur_td, t_per_id.feature_per_frame[0].uv.y(), it_per_frame.uv.y());

                problem.AddResidualBlock(f_td, loss_function, para_Pose[imu_i], para_Pose[imu_j], para_Ex_Pose[0], para_Feature[feature_index], para_Td[0]);
        }
        else//在没有同步误差的情况下
        {
            ProjectionFactor *f = new ProjectionFactor(pts_i, pts_j);
            problem.AddResidualBlock(f, loss_function, para_Pose[imu_i], para_Pose[imu_j], para_Ex_Pose[0], para_Feature[feature_index]);
        }
        f_m_cnt++;
    }
}

h. 添加各种残差——回环检测

i.求解

ceres::Solver::Options options;
options.linear_solver_type = ceres::DENSE_SCHUR;
options.trust_region_strategy_type = ceres::DOGLEG;
options.max_num_iterations = NUM_ITERATIONS;
if (marginalization_flag == MARGIN_OLD)
    options.max_solver_time_in_seconds = SOLVER_TIME * 4.0 / 5.0;
else
    options.max_solver_time_in_seconds = SOLVER_TIME;
ceres::Solver::Summary summary;
ceres::Solve(options, &problem, &summary);

j. double2vector()
这个地方有2个值需要注意,就是:

Vector3d origin_R0 = Utility::R2ypr(Rs[0]);
Vector3d origin_P0 = Ps[0];

这块固定了先验信息。

至此,第一部分完结。我目前没有想通为什么路标点逆深度没有加入优化。

(2)边缘化
第二部分,边缘化。这一部分,只边缘化,不求解,求解留给下一轮优化的第一部分来进行。这部分是非常难懂的地方了。

k.marg_old
1)首先,把上一轮残存的信息加进来:
很明显,我现在要marg了,要构造新的先验H矩阵,那么要把之前的老先验的遗留信息加进来。

if (marginalization_flag == MARGIN_OLD)
{
    MarginalizationInfo *marginalization_info = new MarginalizationInfo();
    vector2double();
    //! 先验误差会一直保存,而不是只使用一次
    //! 如果上一次边缘化的信息存在
    //! 要边缘化的参数块是 para_Pose[0] para_SpeedBias[0] 以及 para_Feature[feature_index](滑窗内的第feature_index个点的逆深度)
    if (last_marginalization_info)
    {
        vector<int> drop_set;
        for (int i = 0; i < static_cast<int>(last_marginalization_parameter_blocks.size()); i++)
        {//查询last_marginalization_parameter_blocks中是首帧状态量的序号
            if (last_marginalization_parameter_blocks[i] == para_Pose[0] ||
                last_marginalization_parameter_blocks[i] == para_SpeedBias[0])
                drop_set.push_back(i);
        }
        // construct new marginlization_factor构造边缘化的的Factor
        MarginalizationFactor *marginalization_factor = new MarginalizationFactor(last_marginalization_info);
        ResidualBlockInfo *residual_block_info = new ResidualBlockInfo(marginalization_factor, NULL, last_marginalization_parameter_blocks, drop_set);//添加上一次边缘化的参数块
        marginalization_info->addResidualBlockInfo(residual_block_info);
    }

这一段代码用drop_set把最老帧的先验信息干掉了。

2)然后,把这次要marg的IMU信息加进来:

if (pre_integrations[1]->sum_dt < 10.0)
{
    IMUFactor* imu_factor = new IMUFactor(pre_integrations[1]);

    ResidualBlockInfo *residual_block_info = new ResidualBlockInfo(imu_factor, NULL,vector<double *>{para_Pose[0], para_SpeedBias[0], para_Pose[1], para_SpeedBias[1]}, vector<int>{0, 1});

    marginalization_info->addResidualBlockInfo(residual_block_info);
}

很明显,被marg掉的是第0帧信息。

3)然后,把这次要marg的视觉信息加进来:

{//添加视觉的先验,只添加起始帧是旧帧且观测次数大于2的Features
    int feature_index = -1;
    for (auto &it_per_id : f_manager.feature)//遍历滑窗内所有的Features
    {
        it_per_id.used_num = it_per_id.feature_per_frame.size();//该特征点被观测到的次数
        if (!(it_per_id.used_num >= 2 && it_per_id.start_frame < WINDOW_SIZE - 2))
            continue;//Feature的观测次数不小于2次,且起始帧不属于最后两帧

        ++feature_index;

        int imu_i = it_per_id.start_frame, imu_j = imu_i - 1;
        if (imu_i != 0)//只选择被边缘化的帧的Features
            continue;
        //得到该Feature在起始下的归一化坐标
        Vector3d pts_i = it_per_id.feature_per_frame[0].point;

        for (auto &it_per_frame : it_per_id.feature_per_frame)
        {
            imu_j++;
            if (imu_i == imu_j)//不需要起始观测帧
                continue;

            Vector3d pts_j = it_per_frame.point;
            if (ESTIMATE_TD)
            {
                ProjectionTdFactor *f_td = new ProjectionTdFactor(pts_i, pts_j, it_per_id.feature_per_frame[0].velocity, it_per_frame.velocity,it_per_id.feature_per_frame[0].cur_td, it_per_frame.cur_td, it_per_id.feature_per_frame[0].uv.y(), it_per_frame.uv.y());

                ResidualBlockInfo *residual_block_info = new ResidualBlockInfo(f_td, loss_function, vector<double *>{para_Pose[imu_i], para_Pose[imu_j], para_Ex_Pose[0], para_Feature[feature_index], para_Td[0]}, vector<int>{0, 3});

                marginalization_info->addResidualBlockInfo(residual_block_info);
            }
            else
            {
                ProjectionFactor *f = new ProjectionFactor(pts_i, pts_j);

                ResidualBlockInfo *residual_block_info = new ResidualBlockInfo(f, loss_function, vector<double *>{para_Pose[imu_i], para_Pose[imu_j], para_Ex_Pose[0], para_Feature[feature_index]}, vector<int>{0, 3});

                marginalization_info->addResidualBlockInfo(residual_block_info);
            }
        }
    }
}

vins把第0帧看到的特征点全都扔了。

4) 将三个ResidualBlockInfo中的参数块综合到marginalization_info中
其中,计算所有ResidualBlock(残差项)的残差和雅克比,parameter_block_data是参数块的容器。

        marginalization_info->preMarginalize();//<--1

        marginalization_info->marginalize();//<--2
       
        std::unordered_map<long, double *> addr_shift; //<--3
        for (int i = 1; i <= WINDOW_SIZE; i++)
        {
            addr_shift[reinterpret_cast<long>(para_Pose[i])] = para_Pose[i - 1];
            addr_shift[reinterpret_cast<long>(para_SpeedBias[i])] = para_SpeedBias[i - 1];
        }
        for (int i = 0; i < NUM_OF_CAM; i++)//<--4
            addr_shift[reinterpret_cast<long>(para_Ex_Pose[i])] = para_Ex_Pose[i];
        if (ESTIMATE_TD) //<--5
        {
            addr_shift[reinterpret_cast<long>(para_Td[0])] = para_Td[0];
        }
        vector<double*> parameter_blocks = marginalization_info->getParameterBlocks(addr_shift);
//<--6
        if (last_marginalization_info) //<--7
            delete last_marginalization_info;
        last_marginalization_info = marginalization_info; //<--8
        last_marginalization_parameter_blocks = parameter_blocks;       
    }

至此,marg_old结束。

l.marg_new
如果第二最新帧不是关键帧的话,则把这帧的视觉测量舍弃掉(边缘化)而保留IMU测量值在滑动窗口中。(其他步骤和上一步骤相同)
else//如果第二最新帧不是关键帧的话,则把这帧的视觉测量舍弃掉(边缘化)而保留IMU测量值在滑动窗口中。(其他步骤和上一步骤相同

{
    if (last_marginalization_info &&
        std::count(std::begin(last_marginalization_parameter_blocks), std::end(last_marginalization_parameter_blocks), para_Pose[WINDOW_SIZE - 1]))
    {

        MarginalizationInfo *marginalization_info = new MarginalizationInfo();
        vector2double();
        if (last_marginalization_info)
        {
            vector<int> drop_set;
            for (int i = 0; i < static_cast<int>(last_marginalization_parameter_blocks.size()); i++)
            {
                if (last_marginalization_parameter_blocks[i] == para_Pose[WINDOW_SIZE - 1])
                    drop_set.push_back(i);
            }
            // construct new marginlization_factor
            MarginalizationFactor *marginalization_factor = new MarginalizationFactor(last_marginalization_info);

            ResidualBlockInfo *residual_block_info = new ResidualBlockInfo(marginalization_factor, NULL, last_marginalization_parameter_blocks, drop_set);

            marginalization_info->addResidualBlockInfo(residual_block_info);
        }

        marginalization_info->preMarginalize();
        marginalization_info->marginalize();
        
        std::unordered_map<long, double *> addr_shift;
        for (int i = 0; i <= WINDOW_SIZE; i++)
        {
            if (i == WINDOW_SIZE - 1)
                continue;
            else if (i == WINDOW_SIZE)
            {
                addr_shift[reinterpret_cast<long>(para_Pose[i])] = para_Pose[i - 1];
                addr_shift[reinterpret_cast<long>(para_SpeedBias[i])] = para_SpeedBias[i - 1];
            }
            else
            {
                addr_shift[reinterpret_cast<long>(para_Pose[i])] = para_Pose[i];
                addr_shift[reinterpret_cast<long>(para_SpeedBias[i])] = para_SpeedBias[i];
            }
        }
        for (int i = 0; i < NUM_OF_CAM; i++)
            addr_shift[reinterpret_cast<long>(para_Ex_Pose[i])] = para_Ex_Pose[i];
        if (ESTIMATE_TD)
        {
            addr_shift[reinterpret_cast<long>(para_Td[0])] = para_Td[0];
        }
        
        vector<double *> parameter_blocks = marginalization_info->getParameterBlocks(addr_shift);
        if (last_marginalization_info)
            delete last_marginalization_info;
        last_marginalization_info = marginalization_info;
        last_marginalization_parameter_blocks = parameter_blocks;           
    }
}

至此,optimization()结束。

7.2.2 failureDetection()

bool Estimator::failureDetection()
{
    if (f_manager.last_track_num < 2)
        //return true;

    if (Bas[WINDOW_SIZE].norm() > 2.5)
        return true;
    
    if (Bgs[WINDOW_SIZE].norm() > 1.0)
        return true;
    
    Vector3d tmp_P = Ps[WINDOW_SIZE];
    if ((tmp_P - last_P).norm() > 5)
        return true;
    
    if (abs(tmp_P.z() - last_P.z()) > 1)
        return true; 
    
    Matrix3d tmp_R = Rs[WINDOW_SIZE];
    Matrix3d delta_R = tmp_R.transpose() * last_R;
    Quaterniond delta_Q(delta_R);
    double delta_angle;
    delta_angle = acos(delta_Q.w()) * 2.0 / 3.14 * 180.0;
    if (delta_angle > 50)
        //return true;

    return false;
}

7.2.3 slideWindow()
见8.2.

7.2.4 f_manager.removeFailures()

void FeatureManager::removeFailures()
{
    for (auto it = feature.begin(), it_next = feature.begin();
         it != feature.end(); it = it_next)
    {
        it_next++;
        if (it->solve_flag == 2)
            feature.erase(it);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【SLAM】VINS-MONO解析——后端优化(代码部分) 的相关文章

  • Java final修饰符详解

    final 在 Java 中的意思是最终 xff0c 也可以称为完结器 xff0c 表示对象是最终形态的 xff0c 不可改变的意思 final 应用于类 方法和变量时意义是不同的 xff0c 但本质是一样的 xff0c 都表示不可改变 x
  • Python Requests库安装和使用

    Python 提供了多个用来编写爬虫程序的库 xff0c 除了前面已经介绍的 urllib 库之外 xff0c 还有一个很重的 Requests 库 xff0c 这个库的宗旨是 让 HTTP 服务于人类 Requests 是 Python
  • C语言 十进制转十六进制

    问题描述 十六进制数是在程序设计时经常要使用到的一种整数的表示方式 它有0 1 2 3 4 5 6 7 8 9 A B C D E F共16个符号 xff0c 分别表示十进制数的0至15 十六进制的计数方法是满16进1 xff0c 所以十进
  • Pandas知识点超全总结

    Pandas知识点超全总结 一 数据结构1 Series1 创建2 切片 修改3 其他属性 2 DataFrame1 创建2 切片3 增加 修改4 删除5 查看 二 读写数据1 读数据1 excel文件2 csv文件3 sql文件 2 写数
  • socket通信小结

    1 网络中的进程之间如何进行通信 区别于本地的进程间通信 xff0c 我们首要解决的问题是如何唯一标识一个进程 xff0c 否则通信无从谈起 xff01 在本地可以通过进程PID来唯一标识一个进程 xff0c 但是在网络中这是行不通的 其实
  • eclipse alt+/代码智能提示总是报错:problems during content assist

    解决办法如下图 xff1a 依次点击红框标记的地方即可解决问题
  • linux C 遍历目录及其子目录 opendir -> readdir -> closedir

    在 linux 下遍历某一目录下内容 LINUX 下历遍目录的方法一般是这样的 xff1a 打开目录 gt 读取 gt 关闭目录 相关函数是 opendir gt readdir gt closedir xff0c 其原型如下 xff1a
  • 最全面的 linux 信号量解析

    一 xff0e 什么是信号量 信号量的使用主要是用来保护共享资源 xff0c 使得资源在一个时刻只有一个进程 xff08 线程 xff09 所拥有 信号量的值为正的时候 xff0c 说明它空闲 所测试的线程可以锁定而使用它 若为 0 xff
  • vue3学习一:let和const

    在函数的内部 xff0c 定义name变量 xff0c 当i等于false时 xff0c 按照Java语言 xff0c else里是拿不到name的 xff0c 并且会显示报错 xff0c 但是却不会报错 xff0c 这是因为javascr
  • PHP 中最全的设计模式(23种)

    PhpDesignPatterns PHP 中的设计模式 一 Introduction 介绍 设计模式 xff1a 提供了一种广泛的可重用的方式来解决我们日常编程中常常遇见的问题 设计模式并不一定就是一个类库或者第三方框架 xff0c 它们
  • 异常详细信息: System.NullReferenceException: 未将对象引用设置到对象的实例。

    我遇到的出现这种错误的原因一般是以下几种情况 xff1a 1 在绑定数据控件的时候 xff0c 建立数据库连接 OleDbConnection conn 61 new OleDbConnection 34 provider 61 micro
  • 连接不上Github,网络超时的检查和解决办法

    先附上图片吧 连接不上github但是其它网站都是正常上网 解决办法 win 43 r打开运行输入cmd 再命令行里输入 ping github com 看看返回值 我的全是请求超时 xff0c 发送的数据包全丢 xff08 我先ping了
  • 2018校招笔试真题汇总

    2018校招笔试真题汇总 最近看好多牛油贡献了很多考试的真题 xff0c 我把他们汇总在一起给到大家 xff0c 也感谢这些牛油的贡献 xff0c 只要进这个汇总贴的 xff0c 你们都将每人获得一份牛客送出的礼物一份 科大讯飞 xff1a
  • 【2】Docker的启动与停止

    1 xff09 启动 docker systemctl start docker 2 xff09 查看 docker 状态 systemctl status docker 3 xff09 查看 docker 概要信息 docker info
  • 【8】Docker中部署Redis

    1 xff09 拉取镜像 docker pull redis 这是我在 VMware 的 CentOS 中装过的 redis 版本 xff0c 拉取该指定版本使用 docker pull redis 5 0 12 命令 xff0c 不过下面
  • 操作系统学习之系统调用

    目录 一 操作系统学习之系统调用 1 什么是系统调用 2 系统调用有什么用 3 为什么需要系统调用 4 系统调用的具体流程 1 xff09 执行过程 2 如何实现用户态与内态之间的切换 3 系统调用常见名词 4 系统调用如何返回 传递返回值
  • 22-Docker-常用命令详解-docker pull

    常用命令详解 docker pull 前言docker pull语法格式options说明 使用示例未指定tag a 拉取所有 tagged 镜像 前言 本篇来学习docker pull命令 docker pull 作用 xff1a 从镜像
  • 720p,1080p对应像素解释

    720P是1280 720 61 921600 xff0c 即 分辨率为921600 xff0c 即大约92万像素 xff0c 921600接近100万像素 xff08 1280是按照16 9算出来的 xff0c 4 3的另算 xff0c
  • vue3学习二:模板字符串

    模板字符串是为了解决字符串拼接问题 xff0c 在es5中 xff0c 字符串拼接是这样的 xff1a let name 61 34 wjdsg 34 console log 34 您好 34 43 name 而在es6中可以使用模板字符串
  • Canal 读取 mysql bin_log

    场景 xff1a 在微服务开发的过程中多个项目协同完成一个功能 xff0c 工程与工程之间存在数据上的解耦 xff0c 底层服务为上层服务提供数据 而底层服务有需要对数据进行管理 解决方案 xff1a 基本底层服务 通过 canal 获取

随机推荐

  • PuTTY连接Linux服务器被拒绝问题

    PuTTY连接Linux服务器被拒绝问题 1 使用命令 xff1a ssh localhost 查看是否安装ssh1 2需要手动安装ssh1 2 1 输入命令 xff1a 1 2 2 若是出现下图所示 xff1a 1 2 3 查看进程 xf
  • 实时数据同步工具<Maxwell 操作案例>

    文章目录 案例一 xff1a 监控MySQL中的数据并输出到控制台案例二 xff1a Maxwell监控mysql的数据输出到kafka案例三 xff1a 监控MySQL指定表的数据并输出到kafka 案例一 xff1a 监控MySQL中的
  • Docker 镜像 Tag 管理

    Author xff1a rab 良好的镜像版本命名习惯能让我们更好的管理和使用镜像 xff08 如项目上线失败后可有效的进行版本回退等 xff09 xff0c 以下是 Docker 社区常用的 tag 方案 比如我现在已经构建了一个 co
  • APM与Pixhawk间的关系

    1 APM 本文APM指代 xff1a https github com ArduPilot ardupilot 2 Pixhawk 本文Pixhawk指代 xff1a https github com PX4 Firmware 3 关系
  • Pixhawk串口名称与硬件接口对应关系

    Pixhawk提供的串口较多 xff0c 通过ls dev 可以看到有如下7个tty设备 xff1a ttyACM0 ttyS0 ttyS1 ttyS2 ttyS3 ttyS4 ttyS5 ttyS6 但每个串口名称对应到Pixhawk硬件
  • Linux系统大小端判断

    大端模式 大端模式 xff0c 是指数据的低位保存在内存的高地址中 xff0c 而数据的高位保存在内存的低地址中 小端模式 小端模式 xff0c 是指数据的低位保存在内存的低地址中 xff0c 而数据的高位保存在内存的高地址中 判断程序 文
  • C preprocessor fails sanity check

    编译某一产品固件时 xff0c 遇到如下现象 xff1a checking how to run the C preprocessor opt mipsel 24kec linux uclibc bin mipsel 24kec linux
  • VLC同时开启播放多个视频流BAT脚本

    工作中 xff0c 难免会遇到要用同一个程序连续打开多个URL资源 路径的情况 xff0c 一个窗口一个窗口的启动效率太低 这里以VLC同时播放多个码流图像为例 xff0c 写个简单的BAT脚本 xff0c 供需要者参考 PS 1 使用方式
  • 【AI】Ubuntu14.04安装OpenCV3.2.0

    在ubuntu14 04系统上安装OpenCV3 2 0 环境要求 GCC 4 4 x or later CMake 2 8 7 or higher Git if failed you can replace it with git cor
  • 若依代码生成器(mybatis-plus)

    看这篇文章之前 xff0c 先去看一下我前面的文章 xff1a 若依前后端分离整合mybatis plus wjdsg的博客 CSDN博客 用过若依都知道 xff0c 若依自带的代码生成器 xff0c 是下载下来 xff0c 然后自己粘贴到
  • 【AI】基于OpenCV开发自定义程序编译方法

    基于OpenCV开发自定义程序编译方法 OpenCV自带的程序 xff0c 编译均采用cmake统一编译 若我们要基于OpenCV开发自己的程序 xff0c 如何快速编译 xff1f 本文以OpenCV库自带的facedetect cpp程
  • H3C SNMPv3 配置

    1 xff09 H3C SNMPv3 配置 snmp agent mib view included MIB 2 mib 2 noAuthNoPriv xff1a snmp agent group v3 mygroup read view
  • 【SLAM】VINS-MONO解析——综述

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

    各个部分的讲解如下链接 xff1a SLAM VINS MONO解析 综述 SLAM VINS MONO解析 feature tracker SLAM VINS MONO解析 IMU预积分 SLAM VINS MONO解析 vins est
  • 【SLAM】VINS-MONO解析——IMU预积分

    4 IMU预积分 IMU预积分主要干了2件事 xff0c 第一个是IMU预积分获得 值 xff0c 另一个是误差传递函数的获取 本部分的流程图如下图所示 各个部分的讲解如下链接 xff1a SLAM VINS MONO解析 综述 SLAM
  • 【SLAM】VINS-MONO解析——vins_estimator流程

    5 vins estimator 基本上VINS里面绝大部分功能都在这个package下面 xff0c 包括IMU数据的处理 前端 xff0c 初始化 我觉得可能属于是前端 xff0c 滑动窗口 后端 xff0c 非线性优化 后端 xff0
  • 【SLAM】VINS-MONO解析——初始化(理论部分)

    6 初始化 第一个问题 xff0c 为什么要初始化 xff1f 对于单目系统而言 xff0c 1 视觉系统只能获得二维信息 xff0c 损失了一维信息 深度 所以需要动一下 xff0c 也就是三角化才能重新获得损失的深度信息 xff1b 2
  • 【SLAM】VINS-MONO解析——初始化(代码部分)

    6 2 代码解析 这部分代码在estimator processImage 最后面 初始化部分的代码虽然生命周期比较短 xff0c 但是 xff0c 代码量巨大 xff01 主要分成2部分 xff0c 第一部分是纯视觉SfM优化滑窗内的位姿
  • 【SLAM】VINS-MONO解析——后端优化(理论部分)

    7 后端非线性优化 7 1 理论基础 7 1 1 bayes模型 xff0c 因子图和最小二乘 这一部分主要是对董靖博士在公开课 因子图的理论基础 上的回忆和总结 1 bayes模型 假设有黄色是机器人在不同时刻的位姿 xff0c 蓝色是机
  • 【SLAM】VINS-MONO解析——后端优化(代码部分)

    7 2 代码 在estimator cpp的processImage 的最后 xff0c 代码如下 xff1a span class token keyword else span span class token comment solv