cartographer代码学习-概率栅格地图(ActiveSubmaps2D与Submaps2D)

概率栅格地图是二维激光SLAM的特点,能够将环境通过地图的形式表达出来。

ActiveSubmaps2D作为概率栅格地图中的重要成分,这个对象主要在LocalTrajectoryBuilder2D这里被使用
第一次调用:

active_submaps_(options.submaps_options())

传入一个submap的配置参数。具体的参数在trajectory_builder_2d.lua内:

submaps = {num_range_data = 90,          -- 一个子图里插入雷达数据的个数的一半grid_options_2d = {grid_type = "PROBABILITY_GRID", -- 地图的种类, 还可以是tsdf格式resolution = 0.05,},range_data_inserter = {range_data_inserter_type = "PROBABILITY_GRID_INSERTER_2D",-- 概率占用栅格地图的一些配置probability_grid_range_data_inserter = {insert_free_space = true,hit_probability = 0.55,miss_probability = 0.49,},-- tsdf地图的一些配置tsdf_range_data_inserter = {truncation_distance = 0.3,maximum_weight = 10.,update_free_space = false,normal_estimation_options = {num_normal_samples = 4,sample_radius = 0.5,},project_sdf_distance_to_scan_normal = true,update_weight_range_exponent = 0,update_weight_angle_scan_normal_to_ray_kernel_bandwidth = 0.5,update_weight_distance_cell_to_hit_kernel_bandwidth = 0.5,},},},

最上面几个是外部参数,其中range_data_inserter_type参数决定了在建立地图写入器的时候选择使用哪个地图写入器:

// 创建地图数据写入器
std::unique_ptr<RangeDataInserterInterface>
ActiveSubmaps2D::CreateRangeDataInserter() {switch (options_.range_data_inserter_options().range_data_inserter_type()) {// 概率栅格地图的写入器case proto::RangeDataInserterOptions::PROBABILITY_GRID_INSERTER_2D:return absl::make_unique<ProbabilityGridRangeDataInserter2D>(options_.range_data_inserter_options().probability_grid_range_data_inserter_options_2d());// tsdf地图的写入器case proto::RangeDataInserterOptions::TSDF_INSERTER_2D:return absl::make_unique<TSDFRangeDataInserter2D>(options_.range_data_inserter_options().tsdf_range_data_inserter_options_2d());default:LOG(FATAL) << "Unknown RangeDataInserterType.";}
}

下面细分两块概率栅格地图参数以及tsdf地图参数。
第二次调用:

if (active_submaps_.submaps().empty())

判断地图是否为空。
第三次调用:

// 使用active_submaps_的第一个子图进行匹配std::shared_ptr<const Submap2D> matching_submap =active_submaps_.submaps().front();

取出一个子地图进行扫面匹配。
第四次调用:

// 将点云数据写入到submap中std::vector<std::shared_ptr<const Submap2D>> insertion_submaps =active_submaps_.InsertRangeData(range_data_in_local);

将点云写入栅格地图中。

ActiveSubmaps2D的具体实现是在submap2D.cc文件中:

// ActiveSubmaps2D构造函数
ActiveSubmaps2D::ActiveSubmaps2D(const proto::SubmapsOptions2D& options): options_(options), range_data_inserter_(CreateRangeDataInserter()) {}

这里实际上是使用了一个CreateRangeDataInserter()函数,这个函数创建了一个地图写入器。

ActiveSubmaps2D类中这里面还有一个函数:InsertRangeData

// 将点云数据写入到submap中
std::vector<std::shared_ptr<const Submap2D>> ActiveSubmaps2D::InsertRangeData(const sensor::RangeData& range_data) {// 如果第二个子图插入节点的数据等于num_range_data时,就新建个子图// 因为这时第一个子图应该已经处于完成状态了if (submaps_.empty() ||submaps_.back()->num_range_data() == options_.num_range_data()) {AddSubmap(range_data.origin.head<2>());}// 将一帧雷达数据同时写入两个子图中for (auto& submap : submaps_) {submap->InsertRangeData(range_data, range_data_inserter_.get());}// 第一个子图的节点数量等于2倍的num_range_data时,第二个子图节点数量应该等于num_range_dataif (submaps_.front()->num_range_data() == 2 * options_.num_range_data()) {submaps_.front()->Finish();}return submaps();
}

这个函数就是用来处理点云到子图的函数。cartographer会同时维护两个子图。当激光雷达的插入次数达到一定的次数时,会停止该子图的插入,然后新建一个新的子图。原来的一个子图会通过Addsubmap函数插入到地图中。
submap子图的思路如下:
算法配置文件默认参数为90。
初始时只有一个子图,当第一个地图达到90帧时,建立第二个子图;
当第一个地图到达180帧时,第二个子图到达90帧,此时将第一个子图的指针删除,同时建立第三个子图,将指针指向第二个与第三个子图。注意这里第一个子图本身是没有被删除的。删除的是指向它的一个指针。
当第二个子图到达180帧时,重复第二步

可以看到子图是以每180次激光数据的处理为一个新的更新的。

新建子图的代码如下:

// 以当前雷达原点为地图原件创建地图
std::unique_ptr<GridInterface> ActiveSubmaps2D::CreateGrid(const Eigen::Vector2f& origin) {// 地图初始大小,100个栅格constexpr int kInitialSubmapSize = 100;float resolution = options_.grid_options_2d().resolution(); // param: grid_options_2d.resolutionswitch (options_.grid_options_2d().grid_type()) {// 概率栅格地图case proto::GridOptions2D::PROBABILITY_GRID:return absl::make_unique<ProbabilityGrid>(MapLimits(resolution,// 左上角坐标为坐标系的最大值, origin位于地图的中间origin.cast<double>() + 0.5 * kInitialSubmapSize *resolution *Eigen::Vector2d::Ones(),CellLimits(kInitialSubmapSize, kInitialSubmapSize)),&conversion_tables_);// tsdf地图case proto::GridOptions2D::TSDF:return absl::make_unique<TSDF2D>(MapLimits(resolution,origin.cast<double>() + 0.5 * kInitialSubmapSize *resolution *Eigen::Vector2d::Ones(),CellLimits(kInitialSubmapSize, kInitialSubmapSize)),options_.range_data_inserter_options().tsdf_range_data_inserter_options_2d().truncation_distance(),               // 0.3options_.range_data_inserter_options().tsdf_range_data_inserter_options_2d().maximum_weight(),                    // 10.0&conversion_tables_);default:LOG(FATAL) << "Unknown GridType.";}
}

可以看出来它支持两种格式的地图:概率栅格地图以及TSDF格式地图。
对于概率地图,它以机器人当前位姿为原点建立一个一定大小的栅格地图。默认是100x100个栅格。当前原点的世界坐标会存入submap类中。对于概率地图,其需要两个参数:MapLimits以及conversion_tables_,这里的conversion_tables_是指转换表,其将0.1-0.9的概率值转换成0-65530的整数形式进行存储,方便进行运算。

总体来说,ActiveSubmaps2D函数执行了三个事情:
1、调用构造函数 CreateRangeDataInserter()新建ProbabilityGridRangeDataInserter2D类的对象
2、调用submaps()函数获取指向submap2D的shared_ptr指针的vector
3、调用雷达写入数据的InsertRangeData函数,同时该函数会调用addsubmap()函数以及submap2D::InsertRangeData函数。addsubmap()调用CreateGrid()函数创建概率地图。同时该函数调用地图相关的几个类函数:MapList、Grid2D以及ProbabilityGrid。

上述操作第三步调用了submap2D的操作,其继承了Submap,对于submap,其有三个私有变量:

const transform::Rigid3d local_pose_; // 子图原点在local坐标系下的坐标
int num_range_data_ = 0;
bool insertion_finished_ = false;

local_pose_代表子图原点在local坐标系下的坐标,num_range_data_为插入激光数据的数量,insertion_finished_代表这张子图是否执行完成。
对于submap的初始化而言,它需要传入一个local_submap_pose作为子图的原点:

Submap(const transform::Rigid3d& local_submap_pose): local_pose_(local_submap_pose) {}

除此之外就是几个简单的赋值以及返回参数的函数,所以submap这块的内容相对而言内容不多,还是比较简单的。

而submap2D主要继承了submap,同时将其没有具体实现的函数进行了实现。此外,对于submap2D的初始化需要的参数主要包含以下部分:


/*** @brief 构造函数* * @param[in] origin Submap2D的原点,保存在Submap类里* @param[in] grid 地图数据的指针* @param[in] conversion_tables 地图数据的转换表*/
Submap2D::Submap2D(const Eigen::Vector2f& origin, std::unique_ptr<Grid2D> grid,ValueConversionTables* conversion_tables): Submap(transform::Rigid3d::Translation(Eigen::Vector3d(origin.x(), origin.y(), 0.))),conversion_tables_(conversion_tables) {grid_ = std::move(grid);
}

可以看到这里的参数其实最终都是传递到submap的。此外,这个类主要也是包含了几个简单的函数调用以及实现,不作具体展开,到这里基本ActiveSubmaps2D与Submaps2D的内容告一段落。

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

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

相关文章

P1598 垂直柱状图

链接 ​​​​​​题目链接&#xff1a;P1598 垂直柱状图 题目 题目描述 写一个程序从输入文件中去读取四行大写字母&#xff08;全都是大写的&#xff0c;每行不超过 $100$ 个字符&#xff09;&#xff0c;然后用柱状图输出每个字符在输入文件中出现的次数。严格地按照输出…

为什么要学习SpringBoot呢?

学习SpringBoot的原因众多&#xff0c;以下是其中的一些主要理由&#xff1a; 简化Spring应用开发&#xff1a;SpringBoot极大地简化了Spring应用的开发过程。它提供了许多默认的配置&#xff0c;使开发者能够更快速地构建和运行Spring应用&#xff0c;而无需花费大量时间在繁…

【Bing】开启代理后使用 Bing 搜索引擎总是: 没有与此相关的结果

【Bing】开启代理后使用 Bing 搜索引擎总是: 没有与此相关的结果 1 问题描述2 解决方法2.1 修改代理规则2.1.1 Clash Verge2.1.2 Clash Verge Rev2.1.3 V2RayN 1 问题描述 当我开了代理访问 Bing 时&#xff0c;经常会出现下面的页面: 2 解决方法 我所知的有三种方法: 手动关…

关于JVM-三色标记算法剖析

相关系列 深入理解JVM垃圾收集器-CSDN博客 深入理解JVM垃圾收集算法-CSDN博客 深入理解jvm执行引擎-CSDN博客 jvm优化原则-CSDN博客 jvm流程图-CSDN博客 三色标记产生的原因&#xff1f; 在并发标记的过程中&#xff0c;因为标记期间应用线程还在继续跑&#xff0c;对象间的引…

使用Matplotlib绘制打断图Broken Axis

使用Matplotlib绘制打断坐标轴Broken Axis 对于一批存在离群点的或者两极分化的数据&#xff0c;为了突出其值域差异&#xff0c;时常需要用到打断坐标轴效果。 使用Matplotlib绘制的效果如下&#xff1a; 对于同样的数据&#xff0c;使用brokenaxes库的绘制效果如下&#x…

Ubuntu20.04安装和编译运行lidar_align来联合标定lidar与imu的外参

硬件&#xff1a;树霉派4b 1、下载并安装lidar_align mkdir -p lidar_align/src cd lidar_align/src git clone https://github.com/ethz-asl/lidar_align.git 将 lidar_align/src/lidar_align/NLOPTConfig.cmake 文件移动到 lidar_align/src/ 下(与lidar_align同级) NLOP…

ShardingSphere-ShardingSphere读写分离和数据脱敏

文章目录 一、读写分离1.1 读写分离1.2 读写分离应用方案1.3 分表+读写分离1.4 分库分表+读写分离二、ShardingSphere-JDBC读写分离2.1 创建SpringBoot并添加依赖2.2 创建实体类2.3 创建mapper2.4 配置读写分离2.5 测试测试插入数据测试读测试事务一致性测试负载均衡一、读写分…

免费泛域名SSL证书申请

二级域名使用较多时&#xff0c;一个个申请证书有点麻烦。泛域名证书一次性解决。 申请SSL免费证书 生成SSL最佳配置 https://ssl-config.mozilla.org/ Install curl https://get.acme.sh | sh -s emailzengsmosong.cc alias acme.sh~/.acme.sh/acme.sh# 证书机构二选一&am…

C语言每日一题(66)三数之和

题目链接 力扣15.三数之和 题目描述 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答…

KMP算法的实现示例(伪代码)

KMP算法&#xff0c;即Knuth-Morris-Pratt算法&#xff0c;是一个线性时间复杂度的字符串匹配算法。它能在O(nm)的时间复杂度内完成一个长度为n的文本串S和一个长度为m的模式串T的匹配工作&#xff0c;其中n和m分别代表文本串和模式串的长度。相比于朴素字符串匹配算法&#xf…

vue3+echarts:echarts地图打点显示的样式

colorStops是打点的颜色和呼吸灯、label为show是打点是否显示数据、rich里cnNum是自定义的过滤模板用来改写显示数据的样式 series: [{type: "effectScatter",coordinateSystem: "geo",rippleEffect: {brushType: "stroke",},showEffectOn: &quo…

【H3C】开启web管理页面H3C S5120V2 Series

配置步骤 1.创建对应vlan并放行通过 2.添加vlanfi的ip地址 3.开启http和https的服务 4.创建用户&#xff0c;配置密码&#xff0c;配置服务类型&#xff0c;赋予权限 假设终端连接在交换机的g1/0/1口 假设终端的ip地址为(Ubuntu)&#xff1a;192.168.200.11 /24 假设交换机vlan…

video替换webRtc视频流

经过测试&#xff0c;本地mp4可播放的视频可替换成功&#xff1b;在线的视频会报错跨域&#xff1b;https安全要求; 核心api video.captureStream()html <video id"yyhTestVideo" src"./tmp.mp4" width"1280" height"720" autopl…

【Java 刷题记录】双指针

双指针 1. 移动零 283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: n…

关于Ansible模块 ④

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 继《关于Ansible的模块 ①》、《关于Ansible的模块 ②》与《关于Ansible的模块 ③》之后&#xff0c;继续学习ansible常用模块之…

P3654 First Step (ファーストステップ) Python深搜

First Step (ファーストステップ) 题目背景 知らないことばかりなにもかもが&#xff08;どうしたらいいの&#xff1f;&#xff09; 一切的一切 尽是充满了未知数&#xff08;该如何是好&#xff09; それでも期待で足が軽いよ&#xff08;ジャンプだ&#xff01;&#xff09…

已解决:windows 下无法加载文件 xxx.ps1,因为在此系统上禁止运行脚本

目录 1&#xff0c;问题描述2&#xff0c;问题解决 1&#xff0c;问题描述 当通过 npm 全局安装依赖后&#xff08;比如 ts 对应的 tsc 命令&#xff0c;还有 pnpm&#xff09;&#xff0c;想直接使用安装的命令&#xff0c;就会报错&#xff1a; 2&#xff0c;问题解决 以管…

12+炫酷地图可视化效果,这次还真的有源码。

2023-09-17 22:35贝格前端工场 Hi&#xff0c;大家好&#xff0c;我是贝格前端工场&#xff0c;之前分享过各类UI图、动图、3D图、流程图&#xff0c;好多粉丝朋友给我要源文件&#xff0c;因为种种原因&#xff0c;无法提供。 本次分享12个炫酷的地图可视化效果&#xff0c;…

实现几何对象按照一定距离向外缓冲

1、首先&#xff0c;确保你已经引入了Turf.js库。你可以通过在HTML文件中添加以下代码来引入 <script src"https://cdn.jsdelivr.net/npm/turf/turf6.5.0/turf.min.js"></script>2、使用turf.buffer实现几何对象按照设定距离扩充 let originalCoordinat…

LeetCode 使数组连续的最少操作数

地址&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 难度&#xff1a;困难 题目描述&#xff1a;给你一个整数数组 nums 。每一次操作中&#xff0c;你可以将 nums 中 任意 一个元素替换成 **任意 **整数。 如果 nums 满足以下条件&#xff0c;那么它是 连续的 &#x…