LeGO-LOAM 几个特有函数的分析(2)

news2024/11/17 23:57:50

接上回LeGO-LOAM 几个特有函数的分析(1)

二、广度优先遍历

广度优先遍历(Breadth-First Search, BFS)是一种用于遍历或搜索树或图的算法。这种算法从树的根(或图的某一指定节点)开始,然后探索邻近的节点,之后对每一个邻近的节点,它再去探索它们各自相邻的节点,这个过程持续进行直到访问所有可达的节点。

广度优先遍历的主要特点是它按照距离起始点的“层次”来遍历。首先访问距离起点最近的节点,然后是它们的邻居,如此类推。

2.1 广度优先遍历的步骤:

  1. 初始化:首先将起始节点放入队列中。

  2. 遍历

    • 从队列中弹出一个节点。
    • 检查该节点是否为目标节点。如果是,则完成搜索。
    • 将该节点的所有未访问过的邻居节点加入队列。
  3. 重复:重复步骤2,直到队列为空或找到目标节点。

  4. 结束:当队列为空且目标未找到,或已找到目标节点时,算法结束。

2.2基于 BFS 的点云聚类和外点剔除

2.2.1原理

 

 2.2.2源码注释

    void labelComponents(int row, int col){
        // use std::queue std::vector std::deque will slow the program down greatly
        // 声明所需的变量,输入的ROW和col是单帧点云第几行第几列的点
        // 用于存储距离和角度计算的临时变量
        float d1, d2, alpha, angle;
        // 用于存储索引的变量
        int fromIndX, fromIndY, thisIndX, thisIndY;
        // 标记是否每个扫描线都至少有一个点被添加
        bool lineCountFlag[N_SCAN] = {false};
        //用两个数组分别保存x,y
        queueIndX[0] = row;
        queueIndY[0] = col;
        //算法标志
        int queueSize = 1;
        // 队列开始的索引
        int queueStartInd = 0;
        // 队列结束的索引
        int queueEndInd = 1;
        // 初始化聚类数组
        allPushedIndX[0] = row;
        allPushedIndY[0] = col;
        //计数
        int allPushedIndSize = 1;
        //很巧妙,有有效邻点就加一,每次循环减1,实现bfs广度优先遍历关键
        while(queueSize > 0){
            // Pop point
            // 取出当前点x,y坐标
            fromIndX = queueIndX[queueStartInd];
            fromIndY = queueIndY[queueStartInd];
            //队列大小减一
            --queueSize;
            //索引加一
            ++queueStartInd;
            // Mark popped point
            // 标记该点为一类,聚类就是给点加标签,标签一致的就是一类
            labelMat.at<int>(fromIndX, fromIndY) = labelCount;
            // Loop through all the neighboring grids of popped grid
            // 检查所有邻点
            for (auto iter = neighborIterator.begin(); iter != neighborIterator.end(); ++iter){
                // new index
                // 计算邻点的索引,其实就是上下左右四个点
                thisIndX = fromIndX + (*iter).first;
                thisIndY = fromIndY + (*iter).second;
                // index should be within the boundary
                // 如果raw为0或者15,上或者下没有邻点,跳过
                if (thisIndX < 0 || thisIndX >= N_SCAN)
                    continue;
                // at range image margin (left or right side)
                //设置矩阵最两边的点也为邻点,因为VLP16是360度
                //在cow为0时左边的邻点,在1799
                if (thisIndY < 0)
                    thisIndY = Horizon_SCAN - 1;
                //在cow为1799时左边的邻点,在0
                if (thisIndY >= Horizon_SCAN)
                    thisIndY = 0;
                // prevent infinite loop (caused by put already examined point back)
                // 如果该点已被标记,则跳过
                if (labelMat.at<int>(thisIndX, thisIndY) != 0)
                    continue;
                // 计算角度差以决定是否将邻点加入到当前区域
                // 距离雷达远的是D1,近的是D2
                d1 = std::max(rangeMat.at<float>(fromIndX, fromIndY),
                              rangeMat.at<float>(thisIndX, thisIndY));
                d2 = std::min(rangeMat.at<float>(fromIndX, fromIndY), 
                              rangeMat.at<float>(thisIndX, thisIndY));
                //(0,-1),(0,1),意味着是一条线上的点,角度是360/1800*3.14/180=0.0035
                if ((*iter).first == 0)
                    alpha = segmentAlphaX;
                else
                //(1,0),(-1,0),意味着是上下两条线上的点,角度是30/(16-1)*3.14/180=0.035
                    alpha = segmentAlphaY;
                //计算图中angle角度
                angle = atan2(d2*sin(alpha), (d1 -d2*cos(alpha)));
                //如果角度大于60度
                if (angle > segmentTheta){
                    //把此邻点放入队列
                    queueIndX[queueEndInd] = thisIndX;
                    queueIndY[queueEndInd] = thisIndY;
                    //增加size
                    ++queueSize;
                    //末尾索引右移
                    ++queueEndInd;
                    //把此邻点赋上和之前取出来的点一样的标签
                    labelMat.at<int>(thisIndX, thisIndY) = labelCount;
                    //这行有点被标记过
                    lineCountFlag[thisIndX] = true;
                    //保存聚类结果
                    allPushedIndX[allPushedIndSize] = thisIndX;
                    allPushedIndY[allPushedIndSize] = thisIndY;
                    ++allPushedIndSize;
                }
            }
        }

        // check if this segment is valid
        bool feasibleSegment = false;
        //如果聚类大于30则认为是一个好的聚类
        if (allPushedIndSize >= 30)
            feasibleSegment = true;
        //如果大于5,而且都是竖着的超过3个,也认为是一个好聚类,可能是树,电线杆
        else if (allPushedIndSize >= segmentValidPointNum){
            int lineCount = 0;
            for (size_t i = 0; i < N_SCAN; ++i)
                if (lineCountFlag[i] == true)
                    ++lineCount;
            if (lineCount >= segmentValidLineNum)
                feasibleSegment = true;            
        }
        // segment is valid, mark these points
        //如果聚类成功,标签加一
        if (feasibleSegment == true){
            ++labelCount;
        }else{ // segment is invalid, mark these points
            for (size_t i = 0; i < allPushedIndSize; ++i){
                //不成功,则标记为999999,代表依托答辩
                labelMat.at<int>(allPushedIndX[i], allPushedIndY[i]) = 999999;
            }
        }
    }
 需要注意的点:
一是 邻点的定义,就是代表取当前点上下左右四个点
std::pair<int8_t, int8_t> neighbor;
neighbor.first = -1; neighbor.second =  0; neighborIterator.push_back(neighbor);
neighbor.first =  0; neighbor.second =  1; neighborIterator.push_back(neighbor);
neighbor.first =  0; neighbor.second = -1; neighborIterator.push_back(neighbor);
neighbor.first =  1; neighbor.second =  0; neighborIterator.push_back(neighbor);
 二是 巧妙的通过queueSize 实现广度优先遍历算法的核心

开始是int queueSize =1,让其进入循环

while(queueSize > 0){
    //队列大小减一
    --queueSize;
    for (auto iter = neighborIterator.begin(); iter != neighborIterator.end(); ++iter){
        //如果角度大于60度
        if (angle > segmentTheta){
            //增加size
            ++queueSize;
        }
    }
}
三是 聚类时候,大于30个点,或者大于5个点,但是有三个竖着的聚为一类

我觉得原因是考虑到竖着的点距离远的因素

四是 通过计算角度来判断是否是邻点

想象一下,是不是D1越长,angle越小

2.3函数的调用

用此种方式实现了一帧雷达所有点的聚类

        for (size_t i = 0; i < N_SCAN; ++i)
            for (size_t j = 0; j < Horizon_SCAN; ++j)
                //上一个函数说过地面点label被置为1 
                //如果这个点既不是地面点也没有聚类过,开始聚类
                if (labelMat.at<int>(i,j) == 0)
                    labelComponents(i, j);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1359241.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件测试|教你如何使用UPDATE修改数据

简介 在SQL&#xff08;Structured Query Language&#xff09;中&#xff0c;UPDATE语句用于修改数据库表中的数据。通过UPDATE语句&#xff0c;我们可以更新表中的特定记录或多条记录&#xff0c;从而实现数据的修改和更新。本文将详细介绍SQL UPDATE语句的语法、用法以及一…

selenium 用webdriver.Chrome 访问网页闪退解决方案

1.1.1. 解决方案&#xff1a; 1.1.1.1. 移动插件到谷歌的安装目录下 1.1.1.2. 设置环境变量 1.1.1.3. 重启电脑检查成功 解决时间&#xff1a;5min

Windows系统中Wireshark抓包工具的安装使用

在使用Windows服务器时&#xff0c;如果我们发现网络流量异常或存在异常的外发数据包行为&#xff0c;我们可以利用抓包工具来捕获网络流量包&#xff0c;并对这些流量包进行特征分析&#xff0c;以查看其来源和目的地。通过这些信息&#xff0c;我们可以进一步诊断问题。 以下…

第01章_C语言入门

第01章_C语言入门 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 适合对象 考研同学&#xff0c;且考试科目中包含数据结构等&#xff08;需要使用C/C写代码&#xff09;考研同学&#xff0c;考…

EG网关网口连接触控一体机操作说明V1.0 20210809

前言&#xff1a;工业现场中&#xff0c;很多情况下都会使用触摸屏连接PLC这一套设备&#xff0c;此一体机是将触摸屏和PLC两款设备集成到一起&#xff0c;可以简化现场设备结构&#xff0c;方便维护。同时&#xff0c;使用蓝蜂 EG系列网关&#xff0c;可以实现远程在线监控数据…

Unity AVProVideo安卓播放视频问题

Pico4播放全景8K视频播放不了&#xff0c;普通视频也播放不了解决方案&#xff1a; 1.Vulkan去掉&#xff0c;或者优先级放下面2.Pico4打包ARM64&#xff0c;插件里arm64里的几个库都设置arm64&#xff0c;平台选择安卓 Pico其他设置参考官方文档即可。

新手学习易语言中文编程,易语言从入门到精通教学

一、教程描述 本套教程共有100集&#xff0c;并且有大量的课件资料&#xff0c;可能是截止到目前为止&#xff0c;最为全面系统的易语言教程了&#xff0c;其中有些视频是.exe文件&#xff0c;可以下载到本地播放。本套易语言教程&#xff0c;大小14.59G&#xff0c;共有6个压…

凝聚层次聚类及DBscan算法详解与Python实例

凝聚层次聚类及DBscan算法详解与Python实例 凝聚层次聚类DBscan算法实例演示 在本篇博客中&#xff0c;我们将深入探讨凝聚层次聚类&#xff08;Agglomerative Hierarchical Clustering&#xff09;和DBscan算法&#xff0c;并通过Python实例演示它们的应用。这两种算法都属于聚…

Es6的Proxy代理是什么?有哪些用法?

一.什么是Proxy? Proxy 对象是ES6新出的一个特性&#xff0c;用于创建一个对象的代理&#xff0c;从而实现基本操作的拦截和自定义&#xff08;如属性查找、赋值、枚举、函数调用等&#xff09;。 需要知道的是&#xff0c;在Vue2中双向数据绑定原理&#xff08;数据劫持&…

《网络是怎样连接的》2.1节图表(自用)

图3.1&#xff1a;协议栈的组成 图3.2&#xff1a;netstat命令查看套接字 上图中每一行就是一个套接字 图3.3&#xff1a;协议栈在浏览器访问DNS服务器与web服务器时的具体工作流程 套接字由协议栈创建 应用程序通过Socket库中的程序组件与协议栈交互 图3.4&#xff1a;TCP头…

【OJ】单链表刷题

力扣刷题 1. 反转链表&#xff08;206&#xff09;1.1 题目描述1.2 题目分析1.2.1 头插法1.2.2 箭头反转 1.3 题目代码1.3.1 头插入1.3.2 箭头反转 2.合并两个有序链表&#xff08;21&#xff09;2.1 题目描述2.2 题目分析2.3 题目代码 1. 反转链表&#xff08;206&#xff09;…

<六>Python的字符串切片及常见操作

字符串的表示 在Python里&#xff0c;可以使用一对单引号、一对双引号或者一对三个双引号、一对三个单引号表示字符串。 a "Im Tom" # 一对双引号 b Tom said:"I am Tom" # 一对单引号c Tom said:"I\m Tom" # 转义字符d Tom said:"…

nginx 一、安装与conf浅析

文章目录 一、安装nginxdocker方式安装linux方式安装Ubuntu 或 Debian 系统&#xff1a;CentOS 或 RHEL 系统&#xff1a; macOS 系统&#xff08;使用 Homebrew&#xff09;&#xff1a;Windows 系统&#xff1a; 二、nginx.conf浅析 Nginx&#xff08;发音为“engine-x”&…

有能力,但是不赚钱,往往是因为没有这三个能力!2024最适合创业的细分行业,2024最适合创业的行业

很多人非常有能力&#xff0c;在学校是学霸&#xff0c;在公司是高管&#xff0c;但是出来自己创业就不行了。觉得是自己的能力不够&#xff0c;其实不是你的能力不够&#xff0c;而是你欠缺下面这三种能力。如果你能掌握这三种能力&#xff0c;就算之前是普通人尝试创业&#…

论文阅读_InP-Based_Generic_Foundry_Platform_for_Photonic_Integrated_Circuits

InP-Based_Generic_Foundry_Platform_for_Photonic_Integrated_Circuits 时间&#xff1a;2018年 作者&#xff1a;Luc M. Augustin, Member, IEEE, Rui Santos, Erik den Haan, Steven Kleijn, Peter J. A. Thijs, Sylwester Latkowski, Senior Member, IEEE, Dan Zhao, Wei…

1月5日代码随想录完全二叉树的节点个数

222.完全二叉树的节点个数 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在…

认识CUDA

1 基本概念 1.1 什么是CUDA&#xff1f; CUDA(ComputeUnified Device Architecture)&#xff0c;是显卡厂商NVIDIA推出的运算平台。 CUDA是一种由NVIDIA推出的通用并行计算架构&#xff0c;该架构使GPU能够解决复杂的计算问题。 CUDA&#xff08;Compute Unified Device Arc…

paddlehub 文本检测使用

PaddleHub负责模型的管理、获取和预训练模型的使用。 参考&#xff1a;https://github.com/PaddlePaddle/PaddleHub/tree/develop/modules/image/text_recognition/chinese_text_detection_db_server import paddlehub as hub import cv2 # from utils import cv_show import…

如何使用Plex在Windows系统搭建个人媒体站点公网可访问

文章目录 1.前言2. Plex网站搭建2.1 Plex下载和安装2.2 Plex网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1.前言 用手机或者平板电脑看视频&#xff0c;已经算是生活中稀松平常的场景了&#xff0c;特别是各…

开发知识点-Java网络编程-Netty

Netty P1 Netty-导学分布式网络返回 异步结果dubbo rabbitmqtest 测试案例多线程 日志 第1章_01_nio三大组件-channel-buffer网络编程 框架jdk 1.4 之后才有 nio这个 APIChannel 数据传输通道 &#xff08;双向&#xff09;Buffer 内存缓冲区 &#xff08;暂存Channel 的 数据&…