首先还是local_trajectory_builder_2d
可以对暴力匹配的部分单独提取出来,进行一个重定位 。
AddAccumulatedRangeData 预测time时间的先验位姿,
1 2 3 4 const transform::Rigid3d non_gravity_aligned_pose_prediction = extrapolator_->ExtrapolatePose (time);
对重力方向进行校准,即让z轴和重力方向一致,得到了二维的先验位姿
1 2 3 const transform::Rigid2d pose_prediction = transform::Project2D ( non_gravity_aligned_pose_prediction * gravity_alignment.inverse ());
进行一个自适应体素滤波,就可以进行扫描匹配了
会返回一个pose_estimate_2d,一个扫描匹配之后的位姿
1 2 3 4 std::unique_ptr<transform::Rigid2d> pose_estimate_2d = ScanMatch (time, pose_prediction, filtered_gravity_aligned_point_cloud);
把扫描之后的位姿进行返回,变换到扫描匹配之前的一个状态
1 2 3 const transform::Rigid3d pose_estimate = transform::Embed3D (*pose_estimate_2d) * gravity_alignment;
pose_estimate 就是扫描匹配之后的一个位姿了,然后再校准
1 2 extrapolator_->AddPose (time, pose_estimate);
ScanMatch 如果没有活跃的子图,那么就将先验位姿直接返回
1 2 3 if (active_submaps_.submaps ().empty ()) { return absl::make_unique<transform::Rigid2d>(pose_prediction); }
活跃子图一般有俩,第一个用于扫描匹配,取出子图
1 2 3 std::shared_ptr<const Submap2D> matching_submap = active_submaps_.submaps ().front ();
使用相关性扫描匹配,即暴力匹配,对传入的位姿进行校准
1 2 3 4 5 6 7 8 9 transform::Rigid2d initial_ceres_pose = pose_prediction; if (options_.use_online_correlative_scan_matching ()) { const double score = real_time_correlative_scan_matcher_.Match ( pose_prediction, filtered_gravity_aligned_point_cloud, *matching_submap->grid (), &initial_ceres_pose); kRealTimeCorrelativeScanMatcherScoreMetric->Observe (score); }
使用校准完的Pose进行ceres的基于优化的扫描匹配的实现,由于校准之后的位姿比较准,因为这里的效果会比较好
1 2 3 4 5 6 7 auto pose_observation = absl::make_unique<transform::Rigid2d>();ceres::Solver::Summary summary; ceres_scan_matcher_.Match (pose_prediction.translation (), initial_ceres_pose, filtered_gravity_aligned_point_cloud, *matching_submap->grid (), pose_observation.get (), &summary);
匹配成功之后返回结果pose_observation
scan_matching::RealTimeCorrelativeScanMatcher2D 暴力的实时相关性扫描匹配,其文件位于cartographer/mapping/internal/2d/scan_matching/real_time_correlative_scan_matcher_2d.h
文件中
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 class RealTimeCorrelativeScanMatcher2D { public : explicit RealTimeCorrelativeScanMatcher2D ( const proto::RealTimeCorrelativeScanMatcherOptions& options) ; RealTimeCorrelativeScanMatcher2D (const RealTimeCorrelativeScanMatcher2D&) = delete ; RealTimeCorrelativeScanMatcher2D& operator =( const RealTimeCorrelativeScanMatcher2D&) = delete ; double Match (const transform::Rigid2d& initial_pose_estimate, const sensor::PointCloud& point_cloud, const Grid2D& grid, transform::Rigid2d* pose_estimate) const ; void ScoreCandidates (const Grid2D& grid, const std::vector<DiscreteScan2D>& discrete_scans, const SearchParameters& search_parameters, std::vector<Candidate2D>* candidates) const ; private : std::vector<Candidate2D> GenerateExhaustiveSearchCandidates ( const SearchParameters& search_parameters) const ; const proto::RealTimeCorrelativeScanMatcherOptions options_; };
头文件,只有两个函数 和两个成员变量
其他所有的函数都是通过Match函数进行调用的,因此只看Match,注意Match里面的点云都是相对于Local坐标系的,其点云的原点也是在local坐标系原点的
Match Match函数位于cartographer/mapping/internal/2d/scan_matching/real_time_correlative_scan_matcher_2d.h
中,
RealTimeCorrelativeScanMatcher2D::Match 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 double RealTimeCorrelativeScanMatcher2D::Match ( const transform::Rigid2d& initial_pose_estimate, const sensor::PointCloud& point_cloud, const Grid2D& grid, transform::Rigid2d* pose_estimate) const
把点云旋转到先验位姿上
1 2 3 4 5 6 const Eigen::Rotation2Dd initial_rotation = initial_pose_estimate.rotation ();const sensor::PointCloud rotated_point_cloud = sensor::TransformPointCloud ( point_cloud, transform::Rigid3f::Rotation (Eigen::AngleAxisf ( initial_rotation.cast<float >().angle (), Eigen::Vector3f::UnitZ ())));
SearchParameters是一个配置参数的位置,传入的参数有
linear_search_window 从 配置文件来,线性搜索窗口的大小
angular_search_window 从配置文件来,角度搜索窗口的大小
旋转后的点云
网格的分辨率
1 2 3 4 const SearchParameters search_parameters ( options_.linear_search_window(), options_.angular_search_window(), rotated_point_cloud, grid.limits().resolution()) ;
search_parameters 里面存储了
1 2 3 4 5 6 7 8 9 void ShrinkToFit (const std::vector<DiscreteScan2D>& scans, const CellLimits& cell_limits) ;int num_angular_perturbations; double angular_perturbation_step_size; double resolution;int num_scans; std::vector<LinearBounds> linear_bounds;
按照角度分辨率和旋转后点云集合的个数,对点云进行旋转保存于vector。至于要如何生成 这个个数。可以查看论文
1 2 3 4 const std::vector<sensor::PointCloud> rotated_scans = GenerateRotatedScans (rotated_point_cloud, search_parameters);
对点云进行平移,返回的内容不是点云本身,而是经过平移之后的在栅格中的索引。大vector里面的元素代表着第几个旋转点云
DiscreteScan2D
也是一个vector typedef std::vector<Eigen::Array2i> DiscreteScan2D;
这个vector的元素表示每一个点平移后的索引
1 2 3 4 const std::vector<DiscreteScan2D> discrete_scans = DiscretizeScans ( grid.limits (), rotated_scans, Eigen::Translation2f (initial_pose_estimate.translation ().x (), initial_pose_estimate.translation ().y ()));
// Step: 4 生成所有的候选解
1 2 3 std::vector<Candidate2D> candidates = GenerateExhaustiveSearchCandidates (search_parameters);
需要注意的是:
max_element 可以用的原因是因为类Candidate2D里面重载了< 和 >。
1 2 3 4 5 6 7 8 9 10 11 12 13 ScoreCandidates (grid, discrete_scans, search_parameters, &candidates); const Candidate2D& best_candidate = *std::max_element (candidates.begin (), candidates.end ()); *pose_estimate = transform::Rigid2d ( {initial_pose_estimate.translation ().x () + best_candidate.x, initial_pose_estimate.translation ().y () + best_candidate.y}, initial_rotation * Eigen::Rotation2Dd (best_candidate.orientation)); return best_candidate.score;