VINS_FUSION

2023-05-16

VINS_FUSION意义 

 VINS Fusion在VINS Mono的基础上,添加了GPS等可以获取全局观测信息的传感器,使得VINS可以利用全局信息消除累计误差,进而减小闭环依赖。此外,全局信息可以使分多次运行的VINS Mono统一到一个坐标系,从而方便协同建图和定位。

局部传感器(如IMU,相机,雷达等)被广泛应用与建图与定位算法。尽管这些传感器能在没有GPS信息的区域,实现良好的局部定位和建图效果,但这些传感器只能提供局部观测,限制了其应用场景:

1、第一个问题是局部观测数据缺乏全局约束,当我们每次在不同的位置运行算法时,都会得到不同坐标系下的定位和建图结果,因而难以将这些测量结果结合起来,形成全局效果。

2、第二个问题时基于局部观测的估计算法必然存在累积漂移,尽管回环检测可以在一定程度上消除漂移,但是对于数据量较大的大范围场景,算法依然难以处理。

相比于局部传感器,全局传感器(如GPS,气压计和磁力计等)可以提供全局观测,这些传感器使用全局统一坐标系,并且输出的观测数据的方差不随时间累积而增加,但这些传感器也存在一些问题,导致无法直接用于精确定位和建图,以GPS为例,GPS数据通常不平滑,存在噪声,且输出速率低,因此,一个简单而直观的想法是将局部传感器和全局传感器结合起来,以达到局部精确全局零漂的效果,也即是VINS Fusion的核心。

其算法框架如下:

 下图以因子图的方式表示观测和状态之间的约束:

其中圆形为状态量(如位姿,速度,偏置等),黄色正方形为局部观测的约束,即来自VO/VIO的相对位姿变换;而其他颜色的正方形为全局观测的约束,比如紫色正方形为来自GPS的约束。

局部约束(残差)的构建参考vins mono论文,计算的是相邻两帧之间的位姿残差。这里只讨论GPS带来的全局约束。首先当然是将GPS坐标,也就是经纬度转换为大地坐标系。习惯上选择右手坐标系,x,y,z轴正方向分别是北东地或东北天方向。接下来就可以计算得到全局约束的残差:

其中z为GPS测量值,X为状态预测,h方程为观测方程。X可以通过上一时刻状态以及当前时刻与上一时刻的位姿变换计算出来。具体到本文的方法,就是利用VIO得到当前时刻和上一时刻的相对位姿dX,加到上一时刻的位姿上X(i-1),得到当前时刻的位姿Xi。需要注意的是,这里的X以第一帧为原点。通过观测方程h,将当前状态的坐标转换到GPS坐标系下。这样就构建了一个全局约束。

之后的优化就交给BA优化器进行迭代优化,vins fusion沿用了ceres作为优化器。

代码解析

1. GPS和VIO数据输入

需要明确的一点是,VIO的输出是相对于第一帧的累计位姿变换,也就是以第一帧为原点。VINS Fusion接收vio输出的局部位姿变换(相对于第一帧),以及gps输出的经纬度坐标,进行融合。接受数据输入的接口在global_fusion/src/globaOptNode.cpp文件中,接口定义的关键代码如下:

/*******************************************************
 * Copyright (C) 2019, Aerial Robotics Group, Hong Kong University of Science and Technology
 * 
 * This file is part of VINS.
 * 
 * Licensed under the GNU General Public License v3.0;
 * you may not use this file except in compliance with the License.
 *
 * Author: Qin Tong (qintonguav@gmail.com)

 	GPS和VIO数据输入
 *******************************************************/

#include "ros/ros.h"
#include "globalOpt.h"
#include <sensor_msgs/NavSatFix.h>
#include <nav_msgs/Odometry.h>
#include <nav_msgs/Path.h>
#include <eigen3/Eigen/Dense>
#include <eigen3/Eigen/Geometry>
#include <iostream>
#include <stdio.h>
#include <visualization_msgs/Marker.h>
#include <visualization_msgs/MarkerArray.h>
#include <fstream>
#include <queue>
#include <mutex>

GlobalOptimization globalEstimator;
ros::Publisher pub_global_odometry, pub_global_path, pub_car;
nav_msgs::Path *global_path;
double last_vio_t = -1;
std::queue<sensor_msgs::NavSatFixConstPtr> gpsQueue;
std::mutex m_buf;

void publish_car_model(double t, Eigen::Vector3d t_w_car, Eigen::Quaterniond q_w_car)
{
    visualization_msgs::MarkerArray markerArray_msg;
    visualization_msgs::Marker car_mesh;
    car_mesh.header.stamp = ros::Time(t);
    car_mesh.header.frame_id = "world";
    car_mesh.type = visualization_msgs::Marker::MESH_RESOURCE;
    car_mesh.action = visualization_msgs::Marker::ADD;
    car_mesh.id = 0;

    car_mesh.mesh_resource = "package://global_fusion/models/car.dae";

    Eigen::Matrix3d rot;
    rot << 0, 0, -1, 0, -1, 0, -1, 0, 0;
    
    Eigen::Quaterniond Q;
    Q = q_w_car * rot; 
    car_mesh.pose.position.x    = t_w_car.x();
    car_mesh.pose.position.y    = t_w_car.y();
    car_mesh.pose.position.z    = t_w_car.z();
    car_mesh.pose.orientation.w = Q.w();
    car_mesh.pose.orientation.x = Q.x();
    car_mesh.pose.orientation.y = Q.y();
    car_mesh.pose.orientation.z = Q.z();

    car_mesh.color.a = 1.0;
    car_mesh.color.r = 1.0;
    car_mesh.color.g = 0.0;
    car_mesh.color.b = 0.0;

    float major_scale = 2.0;

    car_mesh.scale.x = major_scale;
    car_mesh.scale.y = major_scale;
    car_mesh.scale.z = major_scale;
    markerArray_msg.markers.push_back(car_mesh);
    pub_car.publish(markerArray_msg);
}

void GPS_callback(const sensor_msgs::NavSatFixConstPtr &GPS_msg)
{
    //printf("gps_callback! \n");
    m_buf.lock();
    gpsQueue.push(GPS_msg); //每次接收到的gps消息添加到gpsqueue队列中
    m_buf.unlock();
}

void vio_callback(const nav_msgs::Odometry::ConstPtr &pose_msg)
{
    //printf("vio_callback! \n");
    double t = pose_msg->header.stamp.toSec();
    last_vio_t = t;
    Eigen::Vector3d vio_t(pose_msg->pose.pose.position.x, pose_msg->pose.pose.position.y, pose_msg->pose.pose.position.z);//平移矩阵
    Eigen::Quaterniond vio_q; //旋转四元阵
    vio_q.w() = pose_msg->pose.pose.orientation.w;
    vio_q.x() = pose_msg->pose.pose.orientation.x;
    vio_q.y() = pose_msg->pose.pose.orientation.y;
    vio_q.z() = pose_msg->pose.pose.orientation.z;
    globalEstimator.inputOdom(t, vio_t, vio_q);//将时间、平移,四元数作为预测添加到globalEstimator


    m_buf.lock();
    while(!gpsQueue.empty())
    {
        sensor_msgs::NavSatFixConstPtr GPS_msg = gpsQueue.front();
        double gps_t = GPS_msg->header.stamp.toSec();
        printf("vio t: %f, gps t: %f \n", t, gps_t);
        // 10ms sync tolerance 找到vio里程数据相差在10ms之内的数据,作为匹配数据
        if(gps_t >= t - 0.01 && gps_t <= t + 0.01)
        {
            //printf("receive GPS with timestamp %f\n", GPS_msg->header.stamp.toSec());
            double latitude = GPS_msg->latitude;
            double longitude = GPS_msg->longitude;
            double altitude = GPS_msg->altitude;
            //int numSats = GPS_msg->status.service;
            double pos_accuracy = GPS_msg->position_covariance[0];
            if(pos_accuracy <= 0)
                pos_accuracy = 1;
            //printf("receive covariance %lf \n", pos_accuracy);
            //if(GPS_msg->status.status > 8)
                globalEstimator.inputGPS(t, latitude, longitude, altitude, pos_accuracy);//关键,将gps数据作为观测输入到globalEstimator中
            gpsQueue.pop(); //满足条件的gps信息弹出
            break;
        }
        else if(gps_t < t - 0.01)
            gpsQueue.pop();//过时的gps消息弹出
        else if(gps_t > t + 0.01)
            break;//说明gps消息是后来的,就不要改gps队列,退出
    }
    m_buf.unlock();

    Eigen::Vector3d global_t;
    Eigen:: Quaterniond global_q;
    globalEstimator.getGlobalOdom(global_t, global_q);

    nav_msgs::Odometry odometry;
    odometry.header = pose_msg->header;
    odometry.header.frame_id = "world";
    odometry.child_frame_id = "world";
    odometry.pose.pose.position.x = global_t.x();
    odometry.pose.pose.position.y = global_t.y();
    odometry.pose.pose.position.z = global_t.z();
    odometry.pose.pose.orientation.x = global_q.x();
    odometry.pose.pose.orientation.y = global_q.y();
    odometry.pose.pose.orientation.z = global_q.z();
    odometry.pose.pose.orientation.w = global_q.w();
    pub_global_odometry.publish(odometry);
    pub_global_path.publish(*global_path);
    publish_car_model(t, global_t, global_q);


    // write result to file
    std::ofstream foutC("/home/tony-ws1/output/vio_global.csv", ios::app);
    foutC.setf(ios::fixed, ios::floatfield);
    foutC.precision(0);
    foutC << pose_msg->header.stamp.toSec() * 1e9 << ",";
    foutC.precision(5);
    foutC << global_t.x() << ","
            << global_t.y() << ","
            << global_t.z() << ","
            << global_q.w() << ","
            << global_q.x() << ","
            << global_q.y() << ","
            << global_q.z() << endl;
    foutC.close();
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "globalEstimator");
    ros::NodeHandle n("~");

    global_path = &globalEstimator.global_path;

    ros::Subscriber sub_GPS = n.subscribe("/gps", 100, GPS_callback);
    ros::Subscriber sub_vio = n.subscribe("/vins_estimator/odometry", 100, vio_callback);
    pub_global_path = n.advertise<nav_msgs::Path>("global_path", 100);
    pub_global_odometry = n.advertise<nav_msgs::Odometry>("global_odometry", 100);
    pub_car = n.advertise<visualization_msgs::MarkerArray>("car_model", 1000);
    ros::spin();
    return 0;
}

2. GPS和VIO融合

VINS Fusion融合GPS和VIO数据的代码在global_fusion/src/globalOpt.cpp文件中,下面进行详细介绍。

a. 接收GPS数据,接收VIO数据并转到GPS坐标系

// 接收上面输入的vio数据
void GlobalOptimization::inputOdom(double t, Eigen::Vector3d OdomP, Eigen::Quaterniond OdomQ){
    vector<double> localPose{OdomP.x(), OdomP.y(), OdomP.z(),
                             OdomQ.w(), OdomQ.x(), OdomQ.y(), OdomQ.z()};
    localPoseMap[t] = localPose;
    // 利用vio的局部坐标进行坐标变换,得到当前帧的全局位姿
    Eigen::Quaterniond globalQ;
    globalQ = WGPS_T_WVIO.block<3, 3>(0, 0) * OdomQ;
    Eigen::Vector3d globalP = WGPS_T_WVIO.block<3, 3>(0, 0) * OdomP + WGPS_T_WVIO.block<3, 1>(0, 3);
    vector<double> globalPose{globalP.x(), globalP.y(), globalP.z(),
                              globalQ.w(), globalQ.x(), globalQ.y(), globalQ.z()};
    globalPoseMap[t] = globalPose;
}
 
// 接收上面输入的gps数据
void GlobalOptimization::inputGPS(double t, double latitude, double longitude, double altitude, double posAccuracy)
{
    double xyz[3];
    GPS2XYZ(latitude, longitude, altitude, xyz); // 将GPS的经纬度转到地面笛卡尔坐标系
    vector<double> tmp{xyz[0], xyz[1], xyz[2], posAccuracy};
    GPSPositionMap[t] = tmp;
    newGPS = true;
}

上面出现了VIO数据的局部坐标转到GPS坐标的计算过程,其公式如下:

这个公式中的GPS2VIO出现在后面的优化过程中,计算方法为:

b. 融合优化

这里是融合的关键代码,可以看出其流程如下:

  1. 构建t_array和q_array,用来存入平移和旋转变量,方便输入优化方程,以及在优化后取出。
  2. 利用RelativeRTError::Create()构建VIO两帧之间的约束,输入优化方程
  3. 利用TError::Create()构建GPS构成的全局约束,输入优化方程
  4. 取出优化后的数据
void GlobalOptimization::optimize()
{
    while(true)
    {
        if(newGPS)
        {
            newGPS = false;
            // ceres定义部分略去
            // add param
            mPoseMap.lock();
            int length = localPoseMap.size();
            // ********************************************************
            // ***  1. 构建t_array, q_array用来存优化变量,等优化后取出  ***
            // ********************************************************
            double t_array[length][3];
            double q_array[length][4];
            map<double, vector<double>>::iterator iter;
            iter = globalPoseMap.begin();
            for (int i = 0; i < length; i++, iter++)
            {
                // 取出数据部分省略
                // 添加了parameterblock
                problem.AddParameterBlock(q_array[i], 4, local_parameterization);
                problem.AddParameterBlock(t_array[i], 3);
            }
 
            map<double, vector<double>>::iterator iterVIO, iterVIONext, iterGPS;
            int i = 0;
            for (iterVIO = localPoseMap.begin(); iterVIO != localPoseMap.end(); iterVIO++, i++)
            {
                // ********************************************************
                // *********************   2. VIO约束   *******************
                // ********************************************************
                iterVIONext = iterVIO;
                iterVIONext++;
                if(iterVIONext != localPoseMap.end())
                {
                    Eigen::Matrix4d wTi = Eigen::Matrix4d::Identity(); // 第i帧的变换矩阵
                    Eigen::Matrix4d wTj = Eigen::Matrix4d::Identity(); // 第j帧的变换矩阵
                    // 取出数据部分省略
                    Eigen::Matrix4d iTj = wTi.inverse() * wTj; // 第j帧到第i帧的变换矩阵
                    Eigen::Quaterniond iQj; // 第j帧到第i帧的旋转
                    iQj = iTj.block<3, 3>(0, 0);
                    Eigen::Vector3d iPj = iTj.block<3, 1>(0, 3);  // 第j帧到第i帧的平移
 
                    ceres::CostFunction* vio_function = RelativeRTError::Create(iPj.x(), iPj.y(), iPj.z(),
                                                                                iQj.w(), iQj.x(), iQj.y(), iQj.z(),
                                                                                0.1, 0.01);
                    problem.AddResidualBlock(vio_function, NULL, q_array[i], t_array[i], q_array[i+1], t_array[i+1]);
                }
                 
                // ********************************************************
                // *********************   3. GPS约束   *******************
                // ********************************************************
                double t = iterVIO->first;
                iterGPS = GPSPositionMap.find(t);
                if (iterGPS != GPSPositionMap.end())
                {
                    ceres::CostFunction* gps_function = TError::Create(iterGPS->second[0], iterGPS->second[1],
                                                                       iterGPS->second[2], iterGPS->second[3]);
                    problem.AddResidualBlock(gps_function, loss_function, t_array[i]);
                }
 
            }
            ceres::Solve(options, &problem, &summary);
             
            // ********************************************************
            // *******************   4. 取出优化结果   *****************
            // ********************************************************
            iter = globalPoseMap.begin();
            for (int i = 0; i < length; i++, iter++)
            {
                // 取出优化结果
                vector<double> globalPose{t_array[i][0], t_array[i][1], t_array[i][2],
                                          q_array[i][0], q_array[i][1], q_array[i][2], q_array[i][3]};
                iter->second = globalPose;
                if(i == length - 1)
                {
                    Eigen::Matrix4d WVIO_T_body = Eigen::Matrix4d::Identity();
                    Eigen::Matrix4d WGPS_T_body = Eigen::Matrix4d::Identity();
                    // 剩余的存入部分省略,得到WGPS_T_WVIO
                    WGPS_T_WVIO = WGPS_T_body * WVIO_T_body.inverse();
                }
            }
            updateGlobalPath();
            mPoseMap.unlock();
        }
        std::chrono::milliseconds dura(2000);
        std::this_thread::sleep_for(dura);
    }
    return;
}

上述代码中出现了一个关键的部分,即WGPS_T_WVIO的计算。从之前的代码中知道,这个4*4矩阵是用来做VIO到GPS坐标系的变换的。按道理讲,这个矩阵应当是不需要反复计算的,毕竟第一帧和GPS的坐标变换是确定的。但是运行结果来看,这个矩阵的值是在变化的,而且有时变化比较大。这里不太懂,知乎刘知说是为了避免VIO漂移过大。

3. 总结(摘自知乎刘知)

  1. 使用场景

根据上文中分析的优化策略,global fusion的应用场景应该是GPS频率较低,VIO频率较高的系统。fusion 默认发布频率位10hz,而现在的GPS可以达到20hz,如果在这种系统上使用,你可能还需要修改下VIO或者GPS频率。另外,使用的GPS是常见的误差较大的GPS,并不是RTK-GPS。

    2. GPS与VIO时间不同步

上文提到了,在多传感器融合系统中,传感器往往需要做时钟同步,那么global Fusion需要么?GPS分为为很多种,我们常见的GPS模块精度较低,十几米甚至几十米的误差,这种情况下,同不同步没那么重要了,因为GPS方差太大。另外一种比较常见的是RTK-GPS ,在无遮挡的情况下,室外精度可以达到 2cm之内,输出频率可以达到20hz,这种情况下,不同步时间戳会对系统产生影响,如果VIO要和RTK做松耦合,这点还需要注意。

三、前后端详解

图片

图片

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

VINS_FUSION 的相关文章

  • 树莓派4B(ubuntu mate系统)使用d435i运行vins

    树莓派4B xff08 ubuntu mate系统 xff09 使用d435i运行vins 提示本文为随手笔记 xff0c 并不严谨 xff0c 可参考 xff1a 博客和博客进行配置 树莓派 ubuntu mate 20系统安装ros的步
  • vins-fusion代码解读[一] vio主体

    SLAM新手 xff0c 欢迎讨论 港科大vins fusion代码解读 一 vins fusion与vins mono代码结构有很大相似性 这次先看看vins estimator节点内的内容 1 程序入口 xff1a 1 vins est
  • vins-fusion代码解读[二] 惯性视觉里程结果与GPS松耦合

    感谢 slam萌新 xff0c 本篇博客部分参考 xff1a https blog csdn net weixin 41843971 article details 86748719 欢迎讨论 惯性视觉里程结果与GPS松耦合 xff1a g
  • 【SLAM】VINS-Fusion解析——流程

    VINS Fusion分析 因为时间原因 xff0c 没有像vins mono看的和写的那么具体 有时间的话我会补充完整版 vins fusion不像mono那样有三个node xff0c 它只有一个node xff0c 在rosNodeT
  • VINS-Mono 加rgbd

    通过对比VINS Mono与其RGBD版本 xff0c 分析其改动思路 一 feature tracker feature tracker node cpp 头文件加入了ros的多传感器时间戳 include lt message filt
  • realsense435i运行vins-mono,标定部分

    相机标定 1 安装kalibr xff1b 参考 xff1a https blog csdn net wangbaodong070411209 article details 112248834 https blog csdn net we
  • VINS-Mono代码学习记录(四)---estimator_node

    写在前面的话 终于把feature tracker这一个node给整理好了 xff0c 那些都是之前就已经看过的内容 xff0c 所以整理起来比较快 xff0c 接下来就慢慢边学边整理吧 xff0c 这次先来看estimator node
  • VINS-Mono跑Kitti数据集

    参考文章 xff1a VINS Mono KITT00 测试 知乎 如何在kitti raw data上跑起vins mono 知乎 实际上我参考的是LIO SAM里将KITTI转化为bag的方法 Debug https blog csdn
  • vins位姿图优化

    我们 的滑动窗口和边缘化方案限制了计算的复杂性 xff0c 但也给系统带来了累积漂移 更确切地说 xff0c 漂移发生在全局三维位置 x y z 和围绕重力方向的旋转 yaw 为了消除漂移 xff0c 提出了一种与单目VIO无缝集成的紧耦合
  • ubuntu20.04跑PL-VINS

    PL VINS源码 xff1a https github com cnqiangfu PL VINS 编译时报错 catkin make Ceres报错 报错信息 CMake Error at usr local lib cmake Cer
  • VINS-Fusion跑kitti stereo及stereo+GPS数据

    Stereo source vfusion devel setup bash roslaunch vins vins rviz launch source vfusion devel setup bash rosrun loop fusio
  • 关于VINS-MONO与VIO轨迹漂移问题定位的一些方向

    整个VINS MONO系统 xff0c 较容易在系统静止或外力给予较大冲击时产生轨迹漂移 xff0c 原因是imu的bias在预积分中持续发散 xff0c 视觉重投影误差产生的约束失效 如静止 xff0c 先验约束可能会在LM的线性求解器中
  • VINS-Mono

    文章目录 初始化框架缺点ORB SLAM的Local Map VINS的滑窗 逐次逼近式去畸变给后端提供的特征点信息光流追踪对极约束F去除外点 rejectWithF 特征点均匀化预积分系统初始化初始化时不校正bias a误差卡尔曼滤波误差
  • IMU误差模型简介及VINS使用说明

    1 IMU误差来源 2 IMU噪声模型 Noise and Bias kalibr中的imu noise model 参考 xff1a https github com ethz asl kalibr wiki IMU Noise Mode
  • RealSenseD435i (四):运行 VINS-mono代码

    一 必读博客 nbsp https blog csdn net hltt3838 article details 120691764 nbsp nbsp nbsp 一 https blog csdn net hltt3838 article
  • VINS-mono 解析 新特征

    在17 12 29 xff0c VINS更新了代码加入了新的特征 xff0c 包括map merge 地图合并 pose graph reuse 位姿图重利用 online temporal calibration function 在线时
  • vins中的坐标系变换及g2r函数

    slam中经常会需要表示一个刚体的位姿 例如imu的位姿 xff0c 相机的位姿 首先我们需要在一个刚体上架上一个坐标系 这个坐标系为本体坐标系 怎么架一个坐标系 xff1f imu本身就有规定其本身的x y z轴的方向 相机一般认为 xf
  • VINS-Mono学习(二)——松耦合初始化

    初始化 xff1a 如何当好一个红娘 xff1f 图解SfM 视觉和IMU的羁绊 怎么知道发生了闭环 xff1f 位姿图优化与滑窗优化都为哪般 xff1f 闭环优化 xff1a 拉扯橡皮条 整体初始化流程如下 xff1a 1 SFM纯视觉估
  • VINS-Mono学习(五)——闭环优化4DoF

    这里再重写一边VINS开启的新线程 xff1a 前端图像跟踪后端非线性优化闭环检测闭环优化 闭环优化是跟在闭环检测之后步骤 首先回顾闭环检测的过程 xff1a 1 pose graph node cpp开启process闭环检测线程 xff
  • VINS-Mono代码解读——视觉跟踪 feature_trackers

    前言 本文主要介绍VINS的视觉处理前端的视觉跟踪模块 xff08 feature trackers xff09 论文第四章A节 xff08 IV MEASUREMENT PREPROCESSING A Vision Processing

随机推荐

  • datetimepicker 控件验证问题

    34 baseStudents activistTime 34 trigger 39 blur 39 validators notEmpty message 39 确定积极分子时间不能为空 39 span class hljs tag lt
  • eclipse中SVN分支合并到主干

    在项目开发中 xff0c 需要添加一些新的功能 xff0c 但是又不想影响到其他开发人员的项目进度 xff0c 所以决定使用SVN分支进行开发 xff0c 分支开发完毕后再合并到主干 本文介绍如何在eclipse中合并分支到主干 要想将分支
  • 阿里云服务器

    一年多之前 xff0c 也就11年5月份的样子 xff0c 阿里云云服务器产品线终于上线了 但那时候 xff0c 国内完全没有能称得上云服务器的 xff0c 很多小公司就是搞个VPS就叫云服务器了 以至于阿里云云服务器刚出来的时候 xff0
  • 双控机制信息化系统管理平台建设的趋势和必要性

    什么是安全双控体系 xff1f 双控体系即风险分级管控和隐患排查治理双重预防机制 xff0c 目的是对生产经营单位内的所有安全隐患进行系统性的全面排查 xff0c 结合相关安全隐患的危险程度 发生的可能性以及带来的严重后果进行分级别的管控
  • mac 下 使用 iterm2 配置及快键键使用

    mac 下 使用 iterm2 配置及快键键使用 标签 xff08 空格分隔 xff09 xff1a mac 之前介绍过一篇关于mac 下使用和配置 iterm2的blog 今天这篇稍微详细一点介绍 并且搭配 zsh zsh 会单独开一篇博
  • 登录报错后,状态码是401并弹出登录框

    前后端分离的项目 xff0c 登录失败后会弹出一个非前端页面登录框 这是因为登录失败 xff0c 返回的响应表头里添加了WWW Authenticate属性 WWW Authenticate Basic realm 61 34 oauth2
  • 解决secureCRT账号密码正确,无法连接服务器,那大概因为不支持新的密钥交换算法

    连接比较新版本的linux类服务器 xff0c 是否出现下面这些问题 xff1f 或者是openstack新建centos7镜像的时候 xff0c 无法连接新创建的centos7系统 我百度或者谷歌好像都没有找到答案啊 xff0c 所以才写
  • 树莓派SSH连接-SSH服务安装与开机自动启动

    1 SSH连接 SSH连接比Telnet远程桌面连接使用更为安全 xff0c 已经成为行业标准 使用SSH连接树莓派 xff0c 可以对树莓派进行远程控制与编程开发 xff0c 在没有桌面环境的条件下使用SSH连接是非常合适的选择 第2节和
  • 【VSCode Git】stage和stash的区别

    VSCode Git stage和stash的区别 问题来源 用vscode提交变更的文件时 xff0c 会发现2个相似的选项 Stage Changes 和 Stash Changes xff0c 乍一看不知道用哪个 xff0c 它们有什
  • canal文档

    简介 github地址 canal k n l xff0c 译意为水道 管道 沟渠 xff0c 主要用途是基于 MySQL 数据库增量日志解析 xff0c 提供增量数据订阅和消费 canal 工作原理 canal 模拟 MySQL slav
  • 自然拼读与词根词缀简版

    词根词缀 词根词缀重点 1 ab abs 表示远离 或否定 2 ac acr 表示尖 xff0c 酸 xff1b 3 aer aero 表示空气 xff0c 天空 4 am 表示爱 5 ambi ambul 表示周围 xff1b 6 ani
  • MySQL递归查询上下级菜单

    正文 在传统的后台管理系统里面经常会需要展示多级菜单关系 xff0c 今天我们来学一下如何使用一条SQL语句展示多级菜单 现在我们有一张corpinfo单位表 xff0c 里面有一个belong字段指向上级单位 xff0c 首先来看一下现在
  • 基于ESP32双无刷FOC电机的瓦力平衡机器人(2)

    恍恍惚惚中 xff0c 感觉瓦力已经慢慢悠悠的向我走来 xff0c 看了他的孤独 xff0c 感觉自己的也就不算什么了 断断续续搞了差不多两周的时间 xff0c 总算是把这些底层模块都调通了 xff08 虽然还完全看不出任何瓦力的影子 xf
  • 嵌入式 职位描述 职位要求

    来于智联招聘 前程无忧 xff0c 有关工作经验 xff0c 管理经验 xff0c 学历一并删掉 xff0c 只剩职位描述 职位要求 看看自己还缺些什么 嵌入式软件工程师 关专业 xff0c 本科或以上学历 xff1b 2 基础扎实 xff
  • 信道脉冲响应CIR

    博客写作技巧 xff1a 遇到的问题 如何解决问题 需要那种帮助 信道脉冲响应 xff1a CIR 问题 xff1a 场强测量系统需要获取场强和信道信息 xff0c 那么CIR是什么 xff1f 如何利用CIR反映信道特性 xff1f 解决
  • OpenMV数据打包发送以及STM32对数据的解析(串口方式)

    今天尝试了使用Openmv用串口发送数据 xff0c 32接收 xff0c 遇到了一些坑 xff0c 但是最后还是实现了 xff0c 难住我的地方并不是关于传输的代码 xff0c 而是那个板子串口3不知道因为什么原因接收到的数据是错误的 x
  • linux下 c++ 服务器开发(一)

    苦逼的c 43 43 程序员还没找到工作 xff0c 所以顺便开始写服务器练手 对内容不满意不要喷我我是写给自己看的 xff08 把自己犯得错误记下来 xff09 1 我的电脑是win10的 xff0c 所以先去网上下虚拟机 xff0c 我
  • 最优化算法——常见优化算法分类及总结

    之前做特征选择 xff0c 实现过基于群智能算法进行最优化的搜索 xff0c 看过一些群智能优化算法的论文 xff0c 在此做一下总结 最优化问题 在生活或者工作中存在各种各样的最优化问题 xff0c 比如每个企业和个人都要考虑的一个问题
  • 利用手机摄像头采集图片运行ORB-SLAM2

    一 ROS配置安装 二 ORB SLAM2配置安装 可参考前文 ROS仿真环境安装与配置 身在江湖的郭大侠的博客 CSDN博客 三 Android手机摄像头与ROS建立通信 GitHub有个开源的项目 xff0c 可以通过wifi将摄像头捕
  • VINS_FUSION

    VINS FUSIO xff2e 意义 VINS Fusion在VINS Mono的基础上 xff0c 添加了GPS等可以获取全局观测信息的传感器 xff0c 使得VINS可以利用全局信息消除累计误差 xff0c 进而减小闭环依赖 此外 x