接上一篇,看两次计算约束的函数 ComputeConstraint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41// 根据指定的 nodeid 和 submapid 计算其约束
void PoseGraph2D::ComputeConstraint(const NodeId& node_id,
const SubmapId& submap_id)
{
bool maybe_add_local_constraint = false;
bool maybe_add_global_constraint = false;
const TrajectoryNode::Data* constant_data;
const Submap2D* submap;
{
absl::MutexLock locker(&mutex_);
CHECK(data_.submap_data.at(submap_id).state == SubmapState::kFinished);
if (!data_.submap_data.at(submap_id).submap->insertion_finished() )
{
// Uplink server only receives grids when they are finished,
// so skip constraint search before that.
return;
}
// 获取两个id最新的那个时刻
const common::Time node_time = GetLatestNodeTime(node_id, submap_id);
const common::Time last_connection_time =
data_.trajectory_connectivity_state.LastConnectionTime(
node_id.trajectory_id, submap_id.trajectory_id );
// 如果节点与submap在同一轨迹内或者距离上次全局约束时间较短,则计算局部约束
if (node_id.trajectory_id == submap_id.trajectory_id ||
node_time <
last_connection_time +
common::FromSeconds(
options_.global_constraint_search_after_n_seconds()) )
{
maybe_add_local_constraint = true;
}
// 如果不在同一轨迹内,一定间隔计算全局约束
else if (global_localization_samplers_[node_id.trajectory_id]->Pulse() )
{
maybe_add_global_constraint = true;
}
constant_data = data_.trajectory_nodes.at(node_id).constant_data.get();
submap = static_cast<const Submap2D*>(
data_.submap_data.at(submap_id).submap.get() );
}
建图模式,节点与submap在同一轨迹内 或者 存在一个最近的全局约束把节点的轨迹和子图的轨迹连接起来时,使用
local search window
计算局部约束;纯定位模式,节点与submap不在同一轨迹,使用全局搜索窗口计算约束(对整体子图进行回环检测) 。纯定位进行慢,主要就是
global_constraint_search_after_n_seconds
较大导致,迟迟不能确认maybe_add_global_constraint
为true
local约束在求解时,搜索窗口小,有初值; global约束在求解时,搜索窗口大,没有初值。 记住二者都是计算 Constraint::INTER_SUBMAP
准备计算局部和全局约束
1 | if (maybe_add_local_constraint) |
前端得到节点相对于世界的位姿,也可以得到某个子图的世界位姿,因此得到这个节点相对于这个子图的相对位姿,把这个位姿称为 初始位姿 1。 之所以要用世界坐标系作为桥梁,是因为子图和这个节点并不一定在在同一条轨迹坐标系中(local map坐标系)
这里主要是ConstraintBuilder2D
类的两个函数: MaybeAddConstraint
和 MaybeAddGlobalConstraint
,它们只有细微不同,前者的开头有这样两句:1
2
3if (initial_relative_pose.translation().norm() >
options_.max_constraint_distance() )
return;
然后就是其中调用的ConstraintBuilder2D::ComputeConstraint
不同,局部约束的是 ComputeConstraint(submap_id, submap, node_id, false, constant_data, initial_relative_pose, *scan_matcher, constraint);
全局约束的是 ComputeConstraint( submap_id, submap, node_id, true, constant_data, transform::Rigid2d::Identity(), *scan_matcher, constraint);
,也就是 match full submap
局部约束
1 | void ConstraintBuilder2D::MaybeAddConstraint( |
参数max_constraint_distance
很重要,如果建图回到同一位置,但没有出现回环,可能是因为过程中的累计误差过大了,大于这个参数,导致没有求 inter 约束。
DispatchScanMatcherConstruction
针对某一个submap_id
的submap构建一个扫描匹配器,先看返回类型1
2
3
4
5
6
7
8struct SubmapScanMatcher
{
const Grid2D* grid = nullptr;
std::unique_ptr<scan_matching::FastCorrelativeScanMatcher2D>
fast_correlative_scan_matcher;
// 线程池用的 Task
std::weak_ptr<common::Task> creation_task_handle;
};
1 | const ConstraintBuilder2D::SubmapScanMatcher* |
这里就是构造了scan_matcher_task
的 work_item
,返回 scan_matcher
。
继续看MaybeAddConstraint
1
2
3
4
5
6
7
8
9
10
11
12
13
14 // 线程池
auto constraint_task = absl::make_unique<common::Task>();
constraint_task->SetWorkItem([=]() LOCKS_EXCLUDED(mutex_)
{
ComputeConstraint(submap_id, submap, node_id, false, /* match */
constant_data, initial_relative_pose, *scan_matcher,
constraint);
});
// constraint_task 依赖 scan_matcher_task
constraint_task->AddDependency(scan_matcher->creation_task_handle);
auto constraint_task_handle =
thread_pool_->Schedule(std::move(constraint_task));
// finish_node_task_ 依赖 constraint_task
finish_node_task_->AddDependency(constraint_task_handle);
其实MaybeAddConstraint
做的就是下面的工作,接下来的重点就是 ConstraintBuilder2D::ComputeConstraint