当前位置: 首页 > news >正文

cartographer 概率栅格地图

目录

概率栅格地图简介

地图的坐标系与原点

cartographer中的栅格地图的相关概念与公式

贝汉明算法


概率栅格地图简介

栅格地图是二维激光SLAM的特点, 能够将环境通过地图的形式表达出来.
栅格地图的实现是二维激光SLAM的一个难点
三维激光SLAM形成的点云地图不需要自己手动实现点云的数据结构, PCL中有写好的数据类型, 直接调用就行. 视觉 SLAM形成的点云地图也可以用PCL来实现
唯独二维激光SLAM的栅格地图需要自己手动实现, 目前所有的二维激光SLAM的栅格地图都是SLAM作者自己写的, 没有通用的数据结构

地图的坐标系与原点

/**
* ros的地图坐标系 cartographer的地图坐标系 cartographer地图的像素坐标系
*
* ^ y ^ x 0------> x
* | | |
* | | |
* 0 ------> x y <------0 y
*
* ros的地图坐标系: 左下角为原点, 向右为x正方向, 向上为y正方向, 角度以x轴正向为0, 逆时针为正
* cartographer的地图坐标系: 坐标系右下角为原点, 向上为x正方向, 向左为y正方向
* 角度正方向以x轴正向为0, 逆时针为正
* cartographer地图的像素坐标系: 左上角为原点, 向右为x正方向, 向下为y正方向
*/
栅格地图的原点的设置
sensor::RangeData range_data_in_local =
TransformRangeData(gravity_aligned_range_data,
transform::Embed3D(pose_estimate_2d->cast<float>()));
// 将校正后的雷达数据写入submap
std::unique_ptr<InsertionResult> insertion_result = InsertIntoSubmap(
time, range_data_in_local, filtered_gravity_aligned_point_cloud,
pose_estimate, gravity_alignment.rotation());
// 将点云数据写入到submap中
std::vector<std::shared_ptr<const Submap2D>> ActiveSubmaps2D::InsertRangeData(
const sensor::RangeData& range_data) {
if (submaps_.empty() ||
submaps_.back()->num_range_data() == options_.num_range_data()) {
AddSubmap(range_data.origin.head<2>());
}
return submaps();
}
void ActiveSubmaps2D::AddSubmap(const Eigen::Vector2f& origin) {
if (submaps_.size() >= 2) {
submaps_.erase(submaps_.begin());
}
// 新建一个子图
submaps_.push_back(absl::make_unique<Submap2D>(
origin,
std::unique_ptr<Grid2D>(
static_cast<Grid2D*>(CreateGrid(origin).release())),
&conversion_tables_));
}

第一个雷达数据到来时的栅格地图的原点是如何确定的
std::unique_ptr<transform::Rigid2d> LocalTrajectoryBuilder2D::ScanMatch(
const common::Time time, const transform::Rigid2d& pose_prediction,
const sensor::PointCloud& filtered_gravity_aligned_point_cloud) {
if (active_submaps_.submaps().empty()) {
return absl::make_unique<transform::Rigid2d>(pose_prediction);
}
//...
}
std::unique_ptr<LocalTrajectoryBuilder2D::MatchingResult>
LocalTrajectoryBuilder2D::AddAccumulatedRangeData(){
const transform::Rigid3d non_gravity_aligned_pose_prediction =
extrapolator_->ExtrapolatePose(time);
// 将三维位姿先旋转到姿态为0, 再取xy坐标将三维位姿转成二维位姿
const transform::Rigid2d pose_prediction = transform::Project2D(
non_gravity_aligned_pose_prediction * gravity_alignment.inverse());
}
// 预测得到time时刻 tracking frame 在 local 坐标系下的位姿
transform::Rigid3d PoseExtrapolator::ExtrapolatePose(const common::Time time) {
const TimedPose& newest_timed_pose = timed_pose_queue_.back();
CHECK_GE(time, newest_timed_pose.time);
// 如果本次预测时间与上次计算时间相同 就不再重复计算
if (cached_extrapolated_pose_.time != time) {
// 预测tracking frame在local坐标系下time时刻的位置
const Eigen::Vector3d translation =
ExtrapolateTranslation(time) + newest_timed_pose.pose.translation();
// 预测tracking frame在local坐标系下time时刻的姿态
const Eigen::Quaterniond rotation =
newest_timed_pose.pose.rotation() *
ExtrapolateRotation(time, extrapolation_imu_tracker_.get());
cached_extrapolated_pose_ =
TimedPose{time, transform::Rigid3d{translation, rotation}};
}
return cached_extrapolated_pose_.pose;
}
// 如果Extrapolator没有初始化就进行初始化
void LocalTrajectoryBuilder2D::InitializeExtrapolator(const common::Time time) {
// 如果已经初始化过了就直接返回
if (extrapolator_ != nullptr) {
return;
}
// 初始化位姿推测器
extrapolator_ = absl::make_unique<PoseExtrapolator>(
::cartographer::common::FromSeconds(options_.pose_extrapolator_options()
.constant_velocity()
.pose_queue_duration()), // 0.001s
options_.pose_extrapolator_options()
.constant_velocity()
.imu_gravity_time_constant()); // 10
// 添加初始位姿
extrapolator_->AddPose(time, transform::Rigid3d::Identity());
}

cartographer中的栅格地图的相关概念与公式

probability: 栅格被占据的概率
kMinProbability = 0.1, kMaxProbability = 0.9, kUnknownProbabilityValue = 0
Odds: probability / (1.0f - probability)
CorrespondenceCost: 栅格是free的概率; CorrespondenceCost + probability = 1
kMinCorrespondenceCost = 0.1, kMaxCorrespondenceCost = 0.9
kUnknownCorrespondenceValue = 0, kUpdateMarker = 32768
Value: 代码里存储的栅格值, [0, 32767]范围内的 uint16 整数
value_to_correspondence_cost_table_: [0, 1~32767] 映射到 [0, 0.1~0.9] 的转换表
hit_table_ 计算[0, 1~32767] 按照占用概率0.55更新之后的值
miss_table_ 计算[0, 1~32767] 按照空闲概率0.49更新之后的值

贝汉明算法

Bresenham画线算法 https://www.jianshu.com/p/d63bf63a0e28
Bresenham算法主要思路说白了就是:下一个要画的点该画在哪里,要么在斜边,要么在侧边。斜边就是x坐标加1的同时y坐标也加1 。侧边就是只加x或者只加y坐标。要如何确定画在哪个边呢?因为已知起点坐标和终点坐标分别(x1, y1),(x2, y2),所以可以确定这条线段的位置。最后根据要画的这个点距离这个线段的位置的大小,来确定该画在斜边还是侧边。斜边近就画斜边,侧边近就画侧边
图解 cartographer之雷达模型CastRay https://blog.csdn.net/chaosir1991/article/details/109561010
http://www.dtcms.com/a/307562.html

相关文章:

  • JVM面试通关指南:内存区域、类加载器、双亲委派与GC算法全解析
  • 一万字讲解Java中的IO流——包含底层原理
  • GCC/G++ + Makefile/make 使用
  • Visual Studio调试技巧与函数递归详解
  • “0 成本开跨境店” 噱头下的优哩哩:商业模式深度剖析
  • 5G 单兵终端 + 无人机:消防应急场景的 “空 - 地” 救援协同体系
  • 【可用有效】Axure RP 9 授权码
  • imx6ull-驱动开发篇5——新字符设备驱动实验
  • springcloud04——网关gateway、熔断器 sentinel
  • cas自定义返回信息和自定义认证
  • 考研408_数据结构笔记(第三章栈、队列和数组)
  • 解构衡石嵌入式BI:统一语义层与API网关的原子化封装架构
  • Vue 中使用 Dexie.js
  • 城市客运安全员证考试难不难?如何高效备考
  • CUDA系列之常用Runtime API简介
  • BatchNorm 一般放在哪里?
  • Ⅹ—6.计算机二级综合题19---22套
  • 接口自动化测试以及requests
  • TS语法最佳实践
  • 【笔记】热力学定律推导(6)热力学第二定律推导
  • 【MATLAB】(二)基础知识
  • Golang 指针
  • Valgrind终极指南:深入内存安全与性能瓶颈检测
  • 云原生运维与混合云运维:如何选择及 Wisdom SSH 的应用
  • Android依赖注入框架Hilt入门指南
  • 大白话畅谈:stm32中断和FreeRTOS的中断
  • 【源力觉醒 创作者计划】_巅峰对话文心 4.5 与通义千问、DeepSeek 能力对比解析
  • 【工具】NVM完全指南:Node.js版本管理工具的安装与使用详解
  • 如何将照片从 realme 手机传输到电脑?
  • MongoDB系列教程-第四章:MongoDB Compass可视化和管理MongoDB数据库