我的理解是,求inter约束也是一个 scan to map 的过程,找到和点云最相似的不同时间的子图,也就是找回环。其实是和lidar_localization
的后端用NDT找关键帧的匹配是类似的,但是cartographer是点云和栅格地图匹配,不像点云匹配那样直观,分支定界的score就像NDT匹配的score,不过前者越大越好,后者越小越好。
1 | void ConstraintBuilder2D::ComputeConstraint( |
整个函数是为了计算constraint_transform
(节点 j 和子图 i的关系) ,需要的参数有:
- 节点 j 的
filtered_gravity_aligned_point_cloud
- 分支定界的初值
initial_pose
(节点 j 在local map坐标系的坐标) Match()
的结果pose_estimate
(节点 j 在local map坐标系的坐标).ComputeSubmapPose()
函数 (local map坐标系转到子图 i 坐标系)
计算pose_estimate
的三步:
- 使用 fast correlative scan matcher 做 Fast estimate
- Prune if the score is too low.
- ceres Refine
匹配所有子图 或 局部子图
1 | // 匹配所有子图,对应 MaybeAddGlobalConstraint |
分枝定界求出的位姿被称为 初始位姿 3,这个初始位姿3及其携带的点云作为输入,用于ceres与此子图进行优化匹配
在建立全局约束的时候,直接在一个超大的范围内进行分枝定界搜索,并不需要计算一个特殊的初始位姿2,而直接把初始位姿设置为地图limits的中心点,可以理解为map的中心点。 而且打分的参数也不同了。
全局约束的搜索窗口范围: [1e6 * limits_.resolution(), M_PI]
,角度其实是±180°
ceres refine
ceres优化匹配,得到更加准确的优化位置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// Use the CSM estimate as both the initial and previous pose.
// This has the effect that, in the absence of better information,
// we prefer the original CSM estimate.
ceres::Solver::Summary unused_summary;
// ceres更新pose_estimate,获得节点在local map中的最优位姿
ceres_scan_matcher_.Match(pose_estimate.translation(), pose_estimate,
constant_data->filtered_gravity_aligned_point_cloud,
*submap_scan_matcher.grid, &pose_estimate,
&unused_summary);
// 计算得到node相对子图的位姿
const transform::Rigid2d constraint_transform =
ComputeSubmapPose(*submap).inverse() * pose_estimate;
constraint->reset(new Constraint{submap_id,
node_id,
{ transform::Embed3D(constraint_transform),
options_.loop_closure_translation_weight(),
options_.loop_closure_rotation_weight() },
Constraint::INTER_SUBMAP} );
对于局部约束,constraint_transform
并不是回环边,其实就是子图和节点的普通约束。 全局约束才构造回环边
日志
1 | if (options_.log_matches() ) |