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

cartographer 后端优化流程

目录

子图的保存与任务处理函数

2D后端优化

子图间约束的计算(回环检测)

基于多分辨率地图的分支定界粗匹配

后端计算位姿的方法总结

几种计算相对位姿的方式

4种计算相对位姿的方式总结


节点与约束的概念
节点: tracking_frame的位姿, 子图原点的位姿
约束: tracking_frame与子图原点间的坐标变换
ComputeLocalToGlobalTransform
后端优化的globallocal间的坐标变换, 如果没提前设置的话就是平移0与旋转0.
下边的函数, 如果是后端的第一个节点, GetLocalToGlobalTransform会返回(0, 0, 0), 然后还要乘以这个节点再
local坐标系下的pose, 乘的结果才是global坐标系下第一个节点的位姿
NodeId PoseGraph2D::AddNode(
std::shared_ptr<const TrajectoryNode::Data> constant_data,
const int trajectory_id,
const std::vector<std::shared_ptr<const Submap2D>>& insertion_submaps) {
// GetLocalToGlobalTransform
const transform::Rigid3d optimized_pose(
GetLocalToGlobalTransform(trajectory_id) * constant_data->local_pose);
transform::Rigid3d PoseGraph2D::ComputeLocalToGlobalTransform(
const MapById<SubmapId, optimization::SubmapSpec2D>& global_submap_poses,
const int trajectory_id) const {
auto begin_it = global_submap_poses.BeginOfTrajectory(trajectory_id);
auto end_it = global_submap_poses.EndOfTrajectory(trajectory_id);
// 没找到这个轨迹id
if (begin_it == end_it) {
const auto it = data_.initial_trajectory_poses.find(trajectory_id);
// 如果设置了初始位姿
if (it != data_.initial_trajectory_poses.end()) {
return GetInterpolatedGlobalTrajectoryPose(it->second.to_trajectory_id,it->second.time) *
it->second.relative_pose;
}
// note: 没设置初始位姿就将返回(0,0,0)的平移和旋转
else {
return transform::Rigid3d::Identity();
}
}
// 找到了就获取优化后的最后一个子图的id
const SubmapId last_optimized_submap_id = std::prev(end_it)->id;
// Accessing 'local_pose' in Submap is okay, since the member is const.
// 通过最后一个优化后的 global_pose * local_pose().inverse() 获取 global_pose->local_pose的
坐标变换
// tag: 画图说明一下
return transform::Embed3D(
global_submap_poses.at(last_optimized_submap_id).global_pose) *
data_.submap_data.at(last_optimized_submap_id)
.submap->local_pose()
.inverse();
}

子图的保存与任务处理函数

第一次看到的子图的指针进行保存
data_.submap_data: 保存子图的指针

2D后端优化

子图内约束的计算
子图内约束就是 属于这个submap的节点(是组成submap的节点之一)与这个submap坐标系原点间的坐标变换.
这个坐标变换是在local坐标系下, submap的坐标系原点指向tracking_frame
InitializeGlobalSubmapPoses
data_.global_submap_poses_2d: 全都是优化后的子图在global坐标系下的pose
optimization_problem_->submap_data(): 包含了优化后和还没有进行优化的 子图在global坐标系下的pose
ComputeLocalToGlobalTransform()这个函数的参数, 始终都是data_.global_submap_poses_2d, 计算的是优化后
global指向local的坐标变换
transform::Rigid3d PoseGraph2D::ComputeLocalToGlobalTransform(
const MapById<SubmapId, optimization::SubmapSpec2D>& global_submap_poses,
const int trajectory_id) const {
auto begin_it = global_submap_poses.BeginOfTrajectory(trajectory_id);
auto end_it = global_submap_poses.EndOfTrajectory(trajectory_id);
const SubmapId last_optimized_submap_id = std::prev(end_it)->id;
return transform::Embed3D(
global_submap_poses.at(last_optimized_submap_id).global_pose) *
data_.submap_data.at(last_optimized_submap_id)
.submap->local_pose()
.inverse();
}
// 1 如果只有1个子图
optimization_problem_->AddSubmap(
trajectory_id, transform::Project2D(
ComputeLocalToGlobalTransform( // 会返回(0, 0,0)
data_.global_submap_poses_2d, trajectory_id) *
insertion_submaps[0]->local_pose()));
// 2 有2个子图, 但是第二个子图没保存位姿的情况
const auto& first_submap_pose = submap_data.at(last_submap_id).global_pose;
optimization_problem_->AddSubmap(
trajectory_id,
first_submap_pose *
constraints::ComputeSubmapPose(*insertion_submaps[0]).inverse() *
constraints::ComputeSubmapPose(*insertion_submaps[1]));
计算子图内约束
const transform::Rigid2d local_pose_2d =
transform::Project2D(constant_data->local_pose * // 三维转平面
transform::Rigid3d::Rotation(
constant_data->gravity_alignment.inverse()));
const transform::Rigid2d global_pose_2d =
optimization_problem_->submap_data().at(matching_id).global_pose *
constraints::ComputeSubmapPose(*insertion_submaps.front()).inverse() *
local_pose_2d;
const transform::Rigid2d constraint_transform =
constraints::ComputeSubmapPose(*insertion_submaps[i]).inverse() *
local_pose_2d;

子图间约束的计算(回环检测)

子图间约束就是 不属于这个submap的节点与这个submap坐标系原点间的坐标变换.
与子图内约束是一样的, 也是在local坐标系下, 由这个submap的坐标系原点指向tracking_frame

基于多分辨率地图的分支定界粗匹配

分支定界算法
分支: 对当前层候选解进行分支(扩充), 生成下一层分辨率地图上的4个候选解
排序: 对下一层分辨率地图上的4个候选解进行打分并降序排序
定界: 将当前层的最高得分, 当做下一次分支定界算法的分数阈值
剪枝: 只要当前层的候选解的得分, 有小于传入的阈值的, break, 因为是排好序的

后端计算位姿的方法总结

向优化问题中添加数据
添加节点数据
optimization_problem_->AddTrajectoryNode(
matching_id.trajectory_id,
optimization::NodeSpec2D{constant_data->time, local_pose_2d,
global_pose_2d,
constant_data->gravity_alignment});
添加子图坐标原点数据
optimization_problem_->AddSubmap(
trajectory_id,
first_submap_pose *
constraints::ComputeSubmapPose(*insertion_submaps[0]).inverse() *
constraints::ComputeSubmapPose(*insertion_submaps[1]));
添加其他传感器数据
optimization_problem_->AddImuData(trajectory_id, imu_data);
optimization_problem_->AddOdometryData(trajectory_id, odometry_data);
optimization_problem_->AddFixedFramePoseData(trajectory_id, fixed_frame_pose_data);

几种计算相对位姿的方式

节点在global坐标系下的位姿
const transform::Rigid3d optimized_pose(
GetLocalToGlobalTransform(trajectory_id) * constant_data->local_pose);
ComputeLocalToGlobalTransform() // 传入的始终是data_.global_submap_poses_2d
data_.global_submap_poses_2d // 只有在第一次优化完之后才有数据
节点在global坐标系下的第一帧的位姿, 就是这个节点在local坐标系下的位姿.
之后的节点在global坐标系下的位姿 是通过 globallocal的坐标变换乘以local坐标系下的位姿得到的.
submapglobal坐标系下的位姿
optimization_problem_->AddSubmap(
trajectory_id, transform::Project2D(
• ComputeLocalToGlobalTransform(
• data_.global_submap_poses_2d, trajectory_id) *
• insertion_submaps[0]->local_pose()));
optimization_problem_->AddSubmap(
trajectory_id,
first_submap_pose *
• constraints::ComputeSubmapPose(*insertion_submaps[0]).inverse() *
• constraints::ComputeSubmapPose(*insertion_submaps[1]));
子图在global坐标系下的第一帧的位姿,就是这个子图在local坐标系下的位姿.
之后的子图在global坐标系下的位姿 是通过 第一个子图在global坐标系下的pose 乘以 第一个子图到第二个子图在
local坐标系下的位姿变换 得到的
子图内约束
local坐标系下, 子图原点指向tracking_frame的坐标变换
const transform::Rigid2d local_pose_2d =
transform::Project2D(constant_data->local_pose * // 三维转平面
transform::Rigid3d::Rotation(
constant_data->gravity_alignment.inverse()));
// 计算 子图原点 指向 node坐标 间的坐标变换(子图内约束)
const transform::Rigid2d constraint_transform =
constraints::ComputeSubmapPose(*insertion_submaps[i]).inverse() *
local_pose_2d;
子图间约束
先通过子图在global坐标系下的坐标的逆, 乘以节点在global坐标系下的坐标, 获取子图原点在glboal坐标系下指向
节点的相对坐标变换.
然后根据子图在local坐标系下的位姿乘以这个坐标变换, 得到节点在local坐标系下的预测位姿, 在通过分枝定界粗
匹配与ceres的精匹配, 对这个节点位姿进行校准, 校准后的位姿还是local坐标系下的.
最后, 通过子图在local坐标系下位姿的逆, 乘以这个节点校准后的位姿, 得到子图间约束, local坐标系下, 子图原点
指向节点的相对坐标变换
const transform::Rigid2d initial_relative_pose =
optimization_problem_->submap_data().at(submap_id).global_pose.inverse() *
optimization_problem_->node_data().at(node_id).global_pose_2d;
const transform::Rigid2d initial_pose =
ComputeSubmapPose(*submap) * initial_relative_pose;
const transform::Rigid2d constraint_transform =
ComputeSubmapPose(*submap).inverse() * pose_estimate;

4种计算相对位姿的方式总结

1. 节点 通过 GetLocalToGlobalTransform * constant_data->local_pose 进行global下位姿的计算
2. 子图 通过对前一个子图到后一个子图的坐标变换进行累计, 得到子图在global坐标系下的位姿
3. 子图内约束 local坐标系系下, 子图原点指向节点间的坐标变换
4. 子图间约束 根据global坐标计算初值, 然后通过分支定界算法粗匹配与ceres的精匹配, 获取校准后的位姿,
后计算local坐标系系下, 子图原点指向校准后的节点间的坐标变换
http://www.xdnf.cn/news/17795.html

相关文章:

  • 终端安全检测与防御技术
  • MySQL 存储过程终止执行的方法
  • [TryHackMe]Internal(hydra爆破+WordPress主题修改getshell+Chisel内网穿透)
  • MyBatis 缓存与 Spring 事务相关笔记
  • 安路Anlogic FPGA下载器的驱动安装与测试教程
  • 扩展 Chat2File-deepseek V4.0 正式发布:不仅是更新,更是一次“重塑”
  • 实验-vlan实验
  • 8月12号打卡
  • 常用Linux指令:Java/MySQL/Tomcat/Redis/Nginx运维指南
  • MySql——B树和B+树区别(innoDB引擎为什么把B+树作为默认的数据结构)
  • 什么是 DispatcherServlet?
  • GIT使用攻略
  • HTTP 协议详解:深入理解 Header 与 Body!
  • Windows 命令行:打开命令提示符界面
  • 正式出版!华东数交组编《数据资产化实践:路径、技术与平台构建》
  • 小程序排名优化:功能迭代如何助力排名攀升
  • 【电子硬件】EMI中无源晶振的优势
  • C++11新增关键字和范围for循环
  • SuperMap GIS基础产品FAQ集锦(20250804)
  • 项目实战2——LAMP_LNMP实践
  • C++学习之数据结构:AVL树
  • 学习笔记《区块链技术与应用》ETH 第二天 状态树
  • 云原生作业(nginx)
  • Neo4j Cypher语句
  • 【数据分享】2020-2022年我国乡镇的逐日最高气温数据(Shp/Excel格式)
  • Go 语言中的结构体、切片与映射:构建高效数据模型的基石
  • 超详细基于stm32hal库的esp8266WiFi模块驱动程序(可直接移植)
  • 嵌入式技术公开课精华笔记:CSDN专版
  • 如何将新建的Anaconda虚拟环境导入Juputer内核中?
  • C++11新增可变参数模板