VINS-Mono代码阅读笔记(十四):posegraph的存储和加载

2023-05-16

本篇笔记紧接着VINS-Mono代码阅读笔记(十三):posegraph中四自由度位姿优化,来分析位姿图的存储和加载。

完整(也是理想的)的SLAM的使用应该是这样的:搭载有SLAM程序的移动设备在一个陌生环境中运行一圈来对该环境进行建图;建图完成后存储该地图;当设备再次运行在该环境中的时候加载已经创建的地图帮助定位。

VINS-Mono系统当中存储的是posegraph,先来看看论文中对posegraph的保存、加载和合并的描述,然后再看代码实现。

1.位姿图的存储、加载和合并

1)位姿图的存储结构

VINS-Mono系统当中位姿图的结构比较简单,只需要存储顶点和边,还有每个关键帧(也就是顶点)的描述子。原始图像被丢弃以减少内存消耗。具体来说,保存的第i个关键帧的状态描述如下:

                                                             \left [i,\hat{p}_i^w,\hat{q}_i^w,v,\hat{p}_{iv}^i,\hat{\psi }_{iv},D(u,v,des) \right ]

对其中的元素描述如下:

i-----是帧的index值;

\hat{p}_i^{w}\hat{q}_i^w-----分别是从VIO中估计得到的帧的位置和旋转;

v------如果当前帧有闭环帧的话,v就是闭环帧的index值;

\hat{p}_{iv}^i\hat{\psi }_{iv}------是当前帧和其闭环帧之间的相对位置和偏航角,这两个值从重定位中获得;

D(u,v,des)------是特征集合,每个特征包含2d定位(u,v为其坐标)和描述子des

 


2)位姿图的加载

加载关键帧的时候使用和存储的结构相同的方式来加载,每个关键帧就是位姿图当中的一个顶点。顶点的初始化位姿是\hat{p}_i^w\hat{q}_i^w,闭环边通过闭环信息\hat{p}_{iv}^i,\hat{\psi }_{iv}来建立。每一个关键帧和其相邻的多个关键帧建立了多个序列边。加载完位姿图后,立刻执行一个4自由度的位姿图。位姿图的保存和加载速度和位姿图的大小线性相关。

3)位姿图的合并

位姿图不光可以优化当前的地图,还可以将当前的地图和之前已经建立的地图进行合并。如果已经加载了一个之前创建的地图,并且发现两个地图之间有闭环连接,则可以将两个地图合并到一起。因为所有的边都是相对约束,位姿图优化可以通过闭环连接自动的将两个地图合并到一起。如论文中展示的下图所示,当前地图已经通过闭环边合入了之前的地图。每个顶点和边都是相对变量,因此,只需要固定位姿图当中的第一个顶点。

2.代码实现

1)posegraph的保存

posegraph的保存路径在配置文件中进行设置:

pose_graph_save_path: "/home/shaozu/output/pose_graph/" # save and load path

保存后的结果如下图所示:

最后边还有一个pose_graph.txt文件,内容如下所示:

前边说过,在command线程当中持续检测键盘输入,如果输入为's'的话,就保存位姿图。

void command()
{
    if (!LOOP_CLOSURE)
        return;
    while(1)
    {
        //检查用户键盘输入是否为s
        char c = getchar();
        if (c == 's')
        {
            m_process.lock();
            //用户键盘输入s后,保存当前的位姿图(地图)
            posegraph.savePoseGraph();
            m_process.unlock();
            printf("save pose graph finish\nyou can set 'load_previous_pose_graph' to 1 in the config file to reuse it next time\n");
            // printf("program shutting down...\n");
            // ros::shutdown();
        }
        //检查用户键盘输入是否为n,为n则开始一个新的图像序列
        if (c == 'n')
            new_sequence();

        std::chrono::milliseconds dura(5);
        std::this_thread::sleep_for(dura);
    }
}

posegraph.savePoseGraph()用于保存位姿图,代码如下:

/**
 * 用户键盘输入s的时候保存当前地图(位姿图)
*/
void PoseGraph::savePoseGraph()
{
    m_keyframelist.lock();
    TicToc tmp_t;
    FILE *pFile;
    printf("pose graph path: %s\n",POSE_GRAPH_SAVE_PATH.c_str());
    printf("pose graph saving... \n");
    string file_path = POSE_GRAPH_SAVE_PATH + "pose_graph.txt";
    pFile = fopen (file_path.c_str(),"w");
    //fprintf(pFile, "index time_stamp Tx Ty Tz Qw Qx Qy Qz loop_index loop_info\n");
    list<KeyFrame*>::iterator it;
    //遍历关键帧列表
    for (it = keyframelist.begin(); it != keyframelist.end(); it++)
    {
        std::string image_path, descriptor_path, brief_path, keypoints_path;
        //1.保存图像
        if (DEBUG_IMAGE)
        {
            image_path = POSE_GRAPH_SAVE_PATH + to_string((*it)->index) + "_image.png";
            imwrite(image_path.c_str(), (*it)->image);
        }
        Quaterniond VIO_tmp_Q{(*it)->vio_R_w_i};
        Quaterniond PG_tmp_Q{(*it)->R_w_i};
        Vector3d VIO_tmp_T = (*it)->vio_T_w_i;
        Vector3d PG_tmp_T = (*it)->T_w_i;
        //2.往pose_graph.txt文件当中写入位姿图相关信息
        fprintf (pFile, " %d %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %d %f %f %f %f %f %f %f %f %d\n",(*it)->index, (*it)->time_stamp, 
                                    VIO_tmp_T.x(), VIO_tmp_T.y(), VIO_tmp_T.z(), 
                                    PG_tmp_T.x(), PG_tmp_T.y(), PG_tmp_T.z(), 
                                    VIO_tmp_Q.w(), VIO_tmp_Q.x(), VIO_tmp_Q.y(), VIO_tmp_Q.z(), 
                                    PG_tmp_Q.w(), PG_tmp_Q.x(), PG_tmp_Q.y(), PG_tmp_Q.z(), 
                                    (*it)->loop_index, 
                                    (*it)->loop_info(0), (*it)->loop_info(1), (*it)->loop_info(2), (*it)->loop_info(3),
                                    (*it)->loop_info(4), (*it)->loop_info(5), (*it)->loop_info(6), (*it)->loop_info(7),
                                    (int)(*it)->keypoints.size());

        // write keypoints, brief_descriptors   vector<cv::KeyPoint> keypoints vector<BRIEF::bitset> brief_descriptors;
        assert((*it)->keypoints.size() == (*it)->brief_descriptors.size());
        //3.保存描述子
        brief_path = POSE_GRAPH_SAVE_PATH + to_string((*it)->index) + "_briefdes.dat";
        std::ofstream brief_file(brief_path, std::ios::binary);
        //4.保存关键点
        keypoints_path = POSE_GRAPH_SAVE_PATH + to_string((*it)->index) + "_keypoints.txt";
        FILE *keypoints_file;
        keypoints_file = fopen(keypoints_path.c_str(), "w");
        for (int i = 0; i < (int)(*it)->keypoints.size(); i++)
        {
            brief_file << (*it)->brief_descriptors[i] << endl;
            fprintf(keypoints_file, "%f %f %f %f\n", (*it)->keypoints[i].pt.x, (*it)->keypoints[i].pt.y, 
                                                     (*it)->keypoints_norm[i].pt.x, (*it)->keypoints_norm[i].pt.y);
        }
        brief_file.close();
        fclose(keypoints_file);
    }
    fclose(pFile);

    printf("save pose graph time: %f s\n", tmp_t.toc() / 1000);
    m_keyframelist.unlock();
}

 

2)posegraph的加载

在posegraph节点的main函数中,有下面这一段代码:

//4.加载先前保存的位姿图
if (LOAD_PREVIOUS_POSE_GRAPH)
{
    printf("load pose graph\n");
    m_process.lock();
    posegraph.loadPoseGraph();
    m_process.unlock();
    printf("load pose graph finish\n");
    load_flag = 1;
}

也就是说posegraph节点中开始运行的时候,首先要加载已有的位姿图。其中loadPoseGraph为加载位姿图的函数,代码如下:

//加载先前保存的位姿图
void PoseGraph::loadPoseGraph()
{
    TicToc tmp_t;
    FILE * pFile;
    string file_path = POSE_GRAPH_SAVE_PATH + "pose_graph.txt";
    printf("lode pose graph from: %s \n", file_path.c_str());
    printf("pose graph loading...\n");
    //打开位姿图文件
    pFile = fopen (file_path.c_str(),"r");
    if (pFile == NULL)
    {
        printf("lode previous pose graph error: wrong previous pose graph path or no previous pose graph \n the system will start with new pose graph \n");
        return;
    }
    int index;
    double time_stamp;
    double VIO_Tx, VIO_Ty, VIO_Tz;
    double PG_Tx, PG_Ty, PG_Tz;
    double VIO_Qw, VIO_Qx, VIO_Qy, VIO_Qz;
    double PG_Qw, PG_Qx, PG_Qy, PG_Qz;
    double loop_info_0, loop_info_1, loop_info_2, loop_info_3;
    double loop_info_4, loop_info_5, loop_info_6, loop_info_7;
    int loop_index;
    int keypoints_num;
    Eigen::Matrix<double, 8, 1 > loop_info;
    int cnt = 0;
    //fscanf 从一个流中执行格式化输入,fscanf遇到空格和换行时结束,注意空格时也结束。
    while (fscanf(pFile,"%d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %d %lf %lf %lf %lf %lf %lf %lf %lf %d", &index, &time_stamp, 
                                    &VIO_Tx, &VIO_Ty, &VIO_Tz, 
                                    &PG_Tx, &PG_Ty, &PG_Tz, 
                                    &VIO_Qw, &VIO_Qx, &VIO_Qy, &VIO_Qz, 
                                    &PG_Qw, &PG_Qx, &PG_Qy, &PG_Qz, 
                                    &loop_index,
                                    &loop_info_0, &loop_info_1, &loop_info_2, &loop_info_3, 
                                    &loop_info_4, &loop_info_5, &loop_info_6, &loop_info_7,
                                    &keypoints_num) != EOF) 
    {
        /*
        printf("I read: %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %d %lf %lf %lf %lf %lf %lf %lf %lf %d\n", index, time_stamp, 
                                    VIO_Tx, VIO_Ty, VIO_Tz, 
                                    PG_Tx, PG_Ty, PG_Tz, 
                                    VIO_Qw, VIO_Qx, VIO_Qy, VIO_Qz, 
                                    PG_Qw, PG_Qx, PG_Qy, PG_Qz, 
                                    loop_index,
                                    loop_info_0, loop_info_1, loop_info_2, loop_info_3, 
                                    loop_info_4, loop_info_5, loop_info_6, loop_info_7,
                                    keypoints_num);
        */
        cv::Mat image;
        std::string image_path, descriptor_path;
        //读取当前位姿对应的image
        if (DEBUG_IMAGE)
        {
            image_path = POSE_GRAPH_SAVE_PATH + to_string(index) + "_image.png";
            image = cv::imread(image_path.c_str(), 0);
        }

        Vector3d VIO_T(VIO_Tx, VIO_Ty, VIO_Tz);
        Vector3d PG_T(PG_Tx, PG_Ty, PG_Tz);
        //构造位姿四元数
        Quaterniond VIO_Q;
        VIO_Q.w() = VIO_Qw;
        VIO_Q.x() = VIO_Qx;
        VIO_Q.y() = VIO_Qy;
        VIO_Q.z() = VIO_Qz;
        Quaterniond PG_Q;
        PG_Q.w() = PG_Qw;
        PG_Q.x() = PG_Qx;
        PG_Q.y() = PG_Qy;
        PG_Q.z() = PG_Qz;
        Matrix3d VIO_R, PG_R;
        VIO_R = VIO_Q.toRotationMatrix();
        PG_R = PG_Q.toRotationMatrix();
        Eigen::Matrix<double, 8, 1 > loop_info;
        loop_info << loop_info_0, loop_info_1, loop_info_2, loop_info_3, loop_info_4, loop_info_5, loop_info_6, loop_info_7;

        if (loop_index != -1)
            if (earliest_loop_index > loop_index || earliest_loop_index == -1)
            {
                earliest_loop_index = loop_index;
            }

        // load keypoints, brief_descriptors   加载关键点和BRIEF描述子
        string brief_path = POSE_GRAPH_SAVE_PATH + to_string(index) + "_briefdes.dat";
        std::ifstream brief_file(brief_path, std::ios::binary);
        string keypoints_path = POSE_GRAPH_SAVE_PATH + to_string(index) + "_keypoints.txt";
        FILE *keypoints_file;
        keypoints_file = fopen(keypoints_path.c_str(), "r");
        vector<cv::KeyPoint> keypoints;
        vector<cv::KeyPoint> keypoints_norm;
        vector<BRIEF::bitset> brief_descriptors;
        //遍历关键点数
        for (int i = 0; i < keypoints_num; i++)
        {
            BRIEF::bitset tmp_des;
            brief_file >> tmp_des;
            brief_descriptors.push_back(tmp_des);
            cv::KeyPoint tmp_keypoint;
            cv::KeyPoint tmp_keypoint_norm;
            double p_x, p_y, p_x_norm, p_y_norm;
            if(!fscanf(keypoints_file,"%lf %lf %lf %lf", &p_x, &p_y, &p_x_norm, &p_y_norm))
                printf(" fail to load pose graph \n");
            tmp_keypoint.pt.x = p_x;
            tmp_keypoint.pt.y = p_y;
            tmp_keypoint_norm.pt.x = p_x_norm;
            tmp_keypoint_norm.pt.y = p_y_norm;
            keypoints.push_back(tmp_keypoint);
            keypoints_norm.push_back(tmp_keypoint_norm);
        }
        brief_file.close();
        fclose(keypoints_file);
        //构建关键帧
        KeyFrame* keyframe = new KeyFrame(time_stamp, index, VIO_T, VIO_R, PG_T, PG_R, image, loop_index, loop_info, keypoints, keypoints_norm, brief_descriptors);
        //加载新构造的关键帧
        loadKeyFrame(keyframe, 0);
        //计数器,每间隔20帧发布一次topic
        if (cnt % 20 == 0)
        {
            publish();
        }
        cnt++;
    }
    fclose (pFile);
    printf("load pose graph time: %f s\n", tmp_t.toc()/1000);
    base_sequence = 0;
}

loadKeyFrame当中将该关键帧加入了位姿图显示当中,需要注意的是这里调用loadKeyFrame的时候传入的第二个参数是0,也就是说不需要进行闭环检测。该函数中将该关键帧加入了关键帧列表,并在显示关键帧的时候显示了关键帧之间的序列边。loadKeyFrame代码如下:

void PoseGraph::loadKeyFrame(KeyFrame* cur_kf, bool flag_detect_loop)
{
    cur_kf->index = global_index;
    global_index++;
    int loop_index = -1;
    //闭环检测,这里传入的flag_detect_loop为0,也就是说在加载之前的位姿图的时候,不需要进行闭环检测
    if (flag_detect_loop)
       loop_index = detectLoop(cur_kf, cur_kf->index);
    else
    {
        addKeyFrameIntoVoc(cur_kf);
    }
    if (loop_index != -1)
    {
        printf(" %d detect loop with %d \n", cur_kf->index, loop_index);
        KeyFrame* old_kf = getKeyFrame(loop_index);
        if (cur_kf->findConnection(old_kf))
        {
            if (earliest_loop_index > loop_index || earliest_loop_index == -1)
                earliest_loop_index = loop_index;
            m_optimize_buf.lock();
            //将当前关键帧加入到优化队列中进行位姿优化
            optimize_buf.push(cur_kf->index);
            m_optimize_buf.unlock();
        }
    }
    m_keyframelist.lock();
    Vector3d P;
    Matrix3d R;
    cur_kf->getPose(P, R);
    Quaterniond Q{R};
    geometry_msgs::PoseStamped pose_stamped;
    pose_stamped.header.stamp = ros::Time(cur_kf->time_stamp);
    pose_stamped.header.frame_id = "world";
    pose_stamped.pose.position.x = P.x() + VISUALIZATION_SHIFT_X;
    pose_stamped.pose.position.y = P.y() + VISUALIZATION_SHIFT_Y;
    pose_stamped.pose.position.z = P.z();
    pose_stamped.pose.orientation.x = Q.x();
    pose_stamped.pose.orientation.y = Q.y();
    pose_stamped.pose.orientation.z = Q.z();
    pose_stamped.pose.orientation.w = Q.w();
    base_path.poses.push_back(pose_stamped);
    base_path.header = pose_stamped.header;

    //draw local connection 显示序列边
    if (SHOW_S_EDGE)
    {
        list<KeyFrame*>::reverse_iterator rit = keyframelist.rbegin();
        for (int i = 0; i < 1; i++)
        {
            if (rit == keyframelist.rend())
                break;
            Vector3d conncected_P;
            Matrix3d connected_R;
            if((*rit)->sequence == cur_kf->sequence)
            {
                (*rit)->getPose(conncected_P, connected_R);
                posegraph_visualization->add_edge(P, conncected_P);
            }
            rit++;
        }
    }
    /*
    if (cur_kf->has_loop)
    {
        KeyFrame* connected_KF = getKeyFrame(cur_kf->loop_index);
        Vector3d connected_P;
        Matrix3d connected_R;
        connected_KF->getPose(connected_P,  connected_R);
        posegraph_visualization->add_loopedge(P, connected_P, SHIFT);
    }
    */
    //keyframelist中加入该关键帧
    keyframelist.push_back(cur_kf);
    //publish();
    m_keyframelist.unlock();
}

 

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

VINS-Mono代码阅读笔记(十四):posegraph的存储和加载 的相关文章

  • 多智能体系统集群协同控制实验平台详解与典型案例

    目录 一 机器人实验是智能体集群研究必要手段 二 动作捕捉系统解决智能体集群实验系统多个痛点 三 多智能体集群协同控制实验平台 1 Crazyswarm多无人机集群编队实验平台 2 Robotarium机器人平台 3 中科院自动化所智能集群
  • NOKOV度量动作捕捉协助完成无人机室内定位研究

    随着工业发展 技术进步 xff0c 无人机的使用在各行各业愈发普遍 xff0c 开始出现无人机飞行送外卖 智能无人机自主巡检等多方面应用 在这一过程中 xff0c 无人机飞行定位就成为了重中之重 西北工业大学无人机特种技术国防科技重点实验室
  • 光学动作捕捉系统构成

    一套光学动作捕捉系统由红外动作捕捉镜头 动作捕捉软件 反光标识点 POE交换机 和若干配件组成 xff08 如标定框和镜头固定装置等 xff09 其本质是定位系统 xff0c 通过计算分析 xff0c 来获取与其相关的速度 加速度等多种运动
  • vscode命令行起本地服务,可发送http请求

    在我们vscode中默认打开的是file协议 xff0c 但是往往我们会有ajax等请求 xff0c 需要发送http等其他协议 xff0c 所以我们需要搭起本地服务器 xff1a 1 xff1a cd 进去到文件位置 xff0c 运行 n
  • 动作捕捉用于仿生机器人的运动规划

    随着机器人 三维动画 虚拟现实等产业的发展 xff0c 关于仿生机器人的动作研究早已成为重要的热点课题 如何让机器人或虚拟人物做出合理 流畅的姿态呢 xff1f 这就要涉及到逆运动学算法研究 人体很复杂 xff0c 传统算法需优化 由于人体
  • 智能化人机协作 遮挡情况下准确识别目标信息

    研究背景 废旧产品 xff08 end of life products xff09 的拆卸是工程全生命周期管理的一个基本步骤 在减少资源消耗和温室气体排放的同时 xff0c 回收可重复使用的部件可能创造相当的经济价值 xff0c 同时也能
  • 线下·香港 | 工业大数据与智能系统前沿会议

    由香港理工大学主办的工业大数据与智能系统前沿会议将于2023年4月28日至5月1日在香港举行 届时来自海外 内地及香港的知名科学家将聚首 xff0c 将围绕大会主题 面向人机共融的工业转型 发表演讲 xff0c 分享他们的独到见解并探讨最新
  • 人机耦合模型研究及其在下肢外骨骼机器人设计中的应用

    在外骨骼研究中 xff0c 一个合适的人机耦合模型非常重要 xff0c 它可以帮助预测外骨骼系统直接作用于人体产生的影响 xff0c 避免不必要的伤害和能量损失 xff0c 同时也有助于优化外骨骼系统的设计和控制 xff0c 提高其佩戴的舒
  • STM32】 DMA原理,步骤超细详解,一文看懂DMA

    如需转载请注明地址 xff1a https blog csdn net as480133937 article details 104927922 DMA的基本介绍 什么是DMA DMA的基本定义 DMA xff0c 全称Direct Me
  • float类型数据在报文中的传输方法

    方法1 xff1a 转化成整型传输 假如保留float类型数据为两位小数 xff0c 我们可以将float数据 100 转换成整型数据传输 xff0c 对端收到后 xff0c 再 100 xff0c 转换成float类型 方法2 xff1a
  • 101、104规约解析

    转载 xff1a 电网101 104规约解析 xff08 Java xff09 张二狗和苗翠花的博客 CSDN博客 iec101 java 1 前言 最近在研究广东电网的101与104规约 xff0c 也就是DL T634 5101 200
  • Ubuntu:Python多版本切换。

    使用 update alternatives更改系统Python版本 1 查看可选版本 sudo update alternatives list python 如果提示 xff1a update alternatives error no
  • ROS(melodic)安装问题汇总及解决方法

    终于装上了ROS xff0c 费了很大的波折 xff0c 基本上能遇到的问题都遇到了 xff0c 记在这里希望能给遇到同样问题的朋友一点参考 首先是在虚拟机上装ubuntu 18 04 xff0c 这个没什么问题 xff0c 所用的镜像文件
  • Http请求出现invalid http response问题的原因分析

    发生场景 xff1a A系统发送Http请求调用B系统提供的接口 xff1b 发生问题 xff1a A系统报错 xff0c 提示 invalid http response 错误信息 xff1b 问题分析 xff1a 根据翻译 xff0c
  • STM32利用CUBEMX建立自定义HID工程,并且完成64字节的IN,OUT传输功能。

    STM32 Customed HID开发流程 本文介绍的是STM32的cubeMX自定义HID的开发流程 cubeMX配置customed HID模式 更多详细配置壳查看代码CubeMX的配置文件 修改usbd custome hid if
  • STM32 uart 单线半双工模式(cube版本)

    STM32 uart 单线半双工模式 xff08 cube版本 xff09 1 引言 在某些场合下需要进行三线制串口通信 xff08 信号线只有一根 xff09 xff0c 这就要求进行单线半双工的模式进行通信 在这种情况进行数据协议传输的
  • AS5600磁编码器开发记录

    AS5600使用简介 xff08 程序员版 xff09 本文由 智御电子 提供 xff0c 同时提供范例教程 xff0c 以便电子爱好者交流学习 前言 xff1a 最近由于工作需要接触到AS5600这颗磁角度传感器 xff0c 以前就对相关
  • STM32 硬件UART接收超时检测设置

    STM32 硬件UART接收超时检测设置 本文作者 智御电子 xff0c 期待与电子爱好者交流学习 应用场景 在uart应用中有时候需要进行双工通信 xff0c 主机需要对从机的数据进行接收超时检测 xff0c 例如modbus协议 xff
  • 发送GET请求 示例

    发送GET请求 64 param url 请求地址 64 param param 请求参数 64 param headers 64 return private String requestByGet String url Map lt S
  • STL不同容器的优缺点

    一 容器的分类 1 序列容器 xff08 1 xff09 vector 典型的序列容器 xff0c 任意元素的读取 修改具有O 1 xff0c 在序列尾部进行插入 删除是O 1 xff0c 但在序列的头部插入 删除的时间复杂度是O n xf

随机推荐

  • c语言(数组)

    交换算法 xff08 将最小值换到第一位 xff0c 最大值换到最后一位 xff09 include lt stdio h gt void main int o 61 0 int buf 10 接收用户输入的数组 for o lt 10 o
  • STM32外设串口资源用完了怎么办--------串口模拟解决问题(再也不用多个STM32或其它MCU)

    之前做项目的时候遇到了一个问题 xff0c 当把MCU本身的串口资源用完的时候 xff0c 却还需要使用多几个串口 xff0c 又不想使用几个MCU解决这个问题 那么模拟串口是解决这个问题的一种方法 下图是我对串口通信时序图的个人理解 xf
  • scanf函数中的格式字符串及注意事项

    scanf函数称为格式输入函数 xff0c 即按用户指定的格式从键盘上把数据输入到指定的变量之中 scanf函数的一般形式为 xff1a scanf 格式控制字符串 地址表列 xff1b 格式字符串的一般形式为 xff1a 输入数据宽度 长
  • 【C/C++】标准库, STL, Boost等的联系

    Backto C C 43 43 Index 标准库 最最开始 只有 C 语言 使用着使用着 一些常用的功能被写成了库 各种组织都是自己私有的库 后来为了方便统一使用和交流 就制定了标准 标准里的库 就是 C 标准库 后来 C 43 43
  • 数组与链表的优缺点和区别

    1 数组 xff1a 数组是将元素在内存中连续存放 xff0c 由于每个元素占用内存相同 xff0c 可以通过下标迅速访问数组中任何元素 但是如果要 在数组中增加一个元素 xff0c 需要移动大量元素 xff0c 在内存中空出一 个元素的空
  • 堆空间与栈空间的区别

    1 栈区 xff08 stack xff09 xff1a 又编译器自动分配释放 xff0c 存放函数的参数值 xff0c 局部变量的值等 xff0c 其操作方式类似于数据结构的 栈 2 堆区 xff08 heap xff09 xff1a 一
  • strtok函数及其实现

    头文件 xff1a include lt string h gt 定义函数 xff1a char strtok char s const char delim 函数说明 xff1a strtok 用来将字符串分割成一个个片段 参数s 指向欲
  • 服务器和客户机的信息函数以及读写函数

    1 服务器和客户机的信息函数 xff08 1 xff09 字节转换函数 在网络上面有着许多类型的机器 xff0c 这些机器在表示数据的字节顺序是不同的 xff0c 比如i386芯片是低字节在内存地址的低 端 xff0c 高字节在高端 xff
  • TCP协议的拥塞控制机制

    最初的TCP协议只有基于窗口的流控制 xff08 flow control xff09 机制而没有拥塞控制机制 流控制作为接受方管理发送方发送 数据的方式 xff0c 用来防止接受方可用的数据缓存空间的溢出 流控制是一种局部控制机制 xff
  • 构造函数为什么不能是虚函数

    1 从存储空间角度 xff0c 虚函数对应一个指向vtable虚函数表的指针 xff0c 这大家都知道 xff0c 可是这个指向vtable的指针其实是存储在对象的内存空间的 问题出来了 xff0c 如果构造函数是虚的 xff0c 就需要通
  • openmv学习五:OLED

    首先需要将SSD1306x py这个文件放到OPENMV中 代码 from machine import I2C Pin 从 machine 模块导入 I2C Pin 子模块 from ssd1306x import SSD1306 I2C
  • 自旋锁的实现及优化

    自旋锁也是一种互斥锁 xff0c 和mutex锁相比 xff0c 它的特点是不会阻塞 xff0c 如果申请不到锁 xff0c 就会不断地循环检测锁变量的状态 xff0c 也就是自旋 自旋锁的实现算法大多使用TAS算法或者CAS算法 TAS算
  • C语言----详解字符串相关的库函数(建议收藏)

    文章目录 系列文章目录前言一 C语言相关字符串库函数一览表 二 strlen函数 三 strcpy函数四 strcat函数 五 strcmp函数 六 strncpy函数 七 strncat函数 八 strncmp函数 九 strstr函数
  • C/C++头文件与变量的声明和定义

    C C 43 43 头文件与变量的声明和定义 最近遇到了变量重复包含的问题 xff0c 才发现自己有好多知识已经模糊了 xff0c 真惭愧 首先说下头文件 xff0c 其实头文件对计算机而言没什么作用 xff0c 她只是在预编译时在 inc
  • C语言寄存器变量register

    用register声明的变量是寄存器变量 xff0c 是存放在CPU的寄存器里的 而我们平时声明的变量是存放在内存中的 虽说内存的速度已经很快了 xff0c 不过跟寄存器比起来还是差得远 寄存器变量和普通变量比起来速度上的差异很大 xff0
  • Curl(C++)使用教程

    1 Curl简介 2 Easy interface 3 Multi interface 1 Curl简介 libcurl作为是一个多协议的便于客户端使用的URL传输库 xff0c 基于C语言 xff0c 提供C语言的API接口 xff0c
  • GPS原始RMC数据解析之DDMM.MMMM

    环境 GPS BD 定位模块 1 模块输出数据如下 GNRMC 100756 000 V 4000 0005 N 11559 9745 E 6 21 223 00 050313 N 68 2 了解换算规则 ddmm mmmm规则和dd dd
  • VINS-Mono代码阅读笔记(三):vins_estimator中main函数分析

    在VINS Mono代码阅读笔记 xff08 一 xff09 xff1a 从Euroc数据集的运行开始 中我们已经初步知道了vins estimator中订阅和发布的topic xff0c 那么 xff0c 在研究vins estimato
  • VINS-Mono代码阅读笔记(十三):posegraph中四自由度位姿优化

    本篇笔记紧接着VINS Mono代码阅读笔记 xff08 十二 xff09 xff1a 将关键帧加入位姿图当中 xff0c 来学习pose graph当中的紧耦合优化部分 在重定位完成之后 xff0c 进行位姿图优化是为了将已经产生的所有位
  • VINS-Mono代码阅读笔记(十四):posegraph的存储和加载

    本篇笔记紧接着VINS Mono代码阅读笔记 xff08 十三 xff09 xff1a posegraph中四自由度位姿优化 xff0c 来分析位姿图的存储和加载 完整 xff08 也是理想的 xff09 的SLAM的使用应该是这样的 xf