之前说到ComputeConstraintsForNode,完成了初始化全局submap的位姿,和子图间内约束。
ComputeConstraintsForNode 在文件WorkItem::Result PoseGraph2D::ComputeConstraintsForNode
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 for (const auto & submap_id : finished_submap_ids) { ComputeConstraint (node_id, submap_id); } if (newly_finished_submap) { const SubmapId newly_finished_submap_id = submap_ids.front (); for (const auto & node_id_data : optimization_problem_->node_data ()) { const NodeId& node_id = node_id_data.id; if (newly_finished_submap_node_ids.count (node_id) == 0 ) { ComputeConstraint (node_id, newly_finished_submap_id); } }
本文详细说明一下子图间的约束,即点和子图的约束.
ComputeConstraint 计算子图间约束(回环检测)。传入的内容是节点的ID和submap的ID.mapping/internal/2d/pose_graph_2d.cc
1 2 3 4 5 6 7 8 9 10 11 12 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;
如果地图未完成状态,则立马退出
1 2 3 4 5 6 if (!data_.submap_data.at (submap_id).submap->insertion_finished ()) { return ; }
比较此node和submap中最近一次添加的node时间做对比
1 2 const common::Time node_time = GetLatestNodeTime (node_id, submap_id);
如果在建图的过程中,此if一直为true,则设置添加局部约束为true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const common::Time last_connection_time = data_.trajectory_connectivity_state.LastConnectionTime ( node_id.trajectory_id, submap_id.trajectory_id); 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 ; }
在重定位的过程中,可能if会不成立,则设置添加全局约束
1 2 3 else if (global_localization_samplers_[node_id.trajectory_id]->Pulse ()) { maybe_add_global_constraint = true ; }
获取节点信息数据与地图数据
1 2 3 constant_data = data_.trajectory_nodes.at (node_id).constant_data.get (); submap = static_cast <const Submap2D*>( data_.submap_data.at (submap_id).submap.get ());
通过局部搜索进行回环检测,调用MaybeAddConstraint添加约束
1 2 3 4 5 6 7 8 9 10 11 12 13 if (maybe_add_local_constraint) { 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; constraint_builder_.MaybeAddConstraint ( submap_id, submap, node_id, constant_data, initial_relative_pose); }
定位时才有可能执行这块,调用MaybeAddGlobalConstraint添加约束
1 2 3 4 5 6 else if (maybe_add_global_constraint) { constraint_builder_.MaybeAddGlobalConstraint (submap_id, submap, node_id, constant_data); }
MaybeAddConstraint 此函数添加的是节点和子图的约束, mapping/internal/contraints/contraint_builder_2d.cc
总的来说一共做了一下内容
计算量的过滤
新建匹配器(构成七张地图的匹配器)送入线程池
把计算节点和submap的约束的函数送入线程池,并添加相应的依赖
具体实现
超过范围的不进行约束的计算,此距离可以在配置文件中设置,pose_graph.lua文件中,constraint_builder的max_constraint_distance。对局部子图进行回环检测时能成为约束的最大距离(什么是局部子图)
1 2 3 4 5 max_constraint_distance if (initial_relative_pose.translation ().norm () > options_.max_constraint_distance ()) { return ; }
在队列中新建一个指向Constraint数据的指针
1 2 3 4 constraints_.emplace_back (); kQueueLengthMetric->Set (constraints_.size ()); auto * const constraint = &constraints_.back ();
为子图新建一个匹配器,传入的是此submap的栅格地图
1 2 3 4 const auto * scan_matcher = DispatchScanMatcherConstruction (submap_id, submap->grid ());
新建约束任务,进行添加, 注意此ComputeConstraint 非 之前的ComputeConstraint
1 2 3 4 5 6 auto constraint_task = absl::make_unique<common::Task>();constraint_task->SetWorkItem ([=]() LOCKS_EXCLUDED (mutex_) { ComputeConstraint (submap_id, submap, node_id, false , constant_data, initial_relative_pose, *scan_matcher, constraint); });
为此任务添加依赖,注意,这是放入线程池因此不会直接运行,而是等待调度
1 2 3 4 5 6 7 constraint_task->AddDependency (scan_matcher->creation_task_handle); auto constraint_task_handle = thread_pool_->Schedule (std::move (constraint_task)); finish_node_task_->AddDependency (constraint_task_handle);
finishnode_task 在每一次运行到这的时候都会添加一个依赖项,因此他可能会依赖上百个任务,来表示这个NODE到底有没有完成优化task
匹配器的建立 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 const ConstraintBuilder2D::SubmapScanMatcher*ConstraintBuilder2D::DispatchScanMatcherConstruction (const SubmapId& submap_id, const Grid2D* const grid) { CHECK (grid); if (submap_scan_matchers_.count (submap_id) != 0 ) { return &submap_scan_matchers_.at (submap_id); } auto & submap_scan_matcher = submap_scan_matchers_[submap_id]; kNumSubmapScanMatchersMetric->Set (submap_scan_matchers_.size ()); submap_scan_matcher.grid = grid; auto & scan_matcher_options = options_.fast_correlative_scan_matcher_options (); auto scan_matcher_task = absl::make_unique<common::Task>(); scan_matcher_task->SetWorkItem ( [&submap_scan_matcher, &scan_matcher_options]() { submap_scan_matcher.fast_correlative_scan_matcher = absl::make_unique<scan_matching::FastCorrelativeScanMatcher2D>( *submap_scan_matcher.grid, scan_matcher_options); }); submap_scan_matcher.creation_task_handle = thread_pool_->Schedule (std::move (scan_matcher_task)); return &submap_scan_matchers_.at (submap_id); }
下面继续走
ComputeConstraintsForNode 在计算完上面的节点和子图之间的局部和全部约束之后,调用函数constraintbuilder .NotifyEndOfNode();来通知该节点的约束构建完毕
1 2 constraint_builder_.NotifyEndOfNode ();
NotifyEndOfNode
添加finishnode_task 的任务,因为之前都已经设置好了他的依赖了,所以依赖完成一个他加一个1
添加whendone_task 任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void ConstraintBuilder2D::NotifyEndOfNode () { absl::MutexLock locker (&mutex_) ; CHECK (finish_node_task_ != nullptr ); finish_node_task_->SetWorkItem ([this ] { absl::MutexLock locker (&mutex_); ++num_finished_nodes_; }); auto finish_node_task_handle = thread_pool_->Schedule (std::move (finish_node_task_)); finish_node_task_ = absl::make_unique<common::Task>(); when_done_task_->AddDependency (finish_node_task_handle); ++num_started_nodes_; }