cartographer的几个scanmatcher这里说的蛮好的,但是real_time_correlative_scan_matcher这是没有用到高/低分辨率地图以及分支定界算法的。(也可能是我没看明白)
先看一下这个文档!!!!!!!!
Real-Time Correlative Scan Matching完全解析(CSM帧匹配算法)
总的来说 real_time_correlative_scan_matcher 就是给定先验位置、点云、概率地图、搜索边界然后打散点通过概率地图的得分和再获取最大和然后输出得分最大的位姿。
1、准备知识介绍
- 概率栅格图
占据栅格图(occupancy grid map),就是利用了栅格化的思路,将障碍物在地图中以占据栅格进行表示。概率越高,说明该位置很有可能有障碍物;相反概率越低,也就意味着该位置很可能是空的。下面两图分别表示占据栅格图局部示意、以及整张地图。
2、Real Time 匹配算法综述
- 激光打到障碍物才返回,也就是说当前激光数据可以很好的反应出当前车辆附近障碍物的轮廓。
- 通过激光扫描到的轮廓,和上面栅格图进行对比,通过最佳匹配位置找到机器人的位姿。
- 如何找到最佳匹配位置了。
1、在设定的初始位置,在窗口范围内搜索,提取出每个候选位姿。
2、计算出每个候选位姿下,点云打在栅格图上面的概率,计算其和。
3、概率和最大的候选位姿,就为所要寻找的最佳位姿。
3、代码详解
具体的部分只贴下面一段 Match的代码
double RealTimeCorrelativeScanMatcher2D::Match(
const transform::Rigid2d& initial_pose_estimate, const sensor::PointCloud& point_cloud, const Grid2D& grid, transform::Rigid2d* pose_estimate) const
{
CHECK(pose_estimate != nullptr);
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())));
const SearchParameters search_parameters(
options_.linear_search_window(), options_.angular_search_window(),
rotated_point_cloud, grid.limits().resolution());
const std::vector<sensor::PointCloud> rotated_scans =
GenerateRotatedScans(rotated_point_cloud, search_parameters);
const std::vector<DiscreteScan2D> discrete_scans = DiscretizeScans(
grid.limits(), rotated_scans,
Eigen::Translation2f(initial_pose_estimate.translation().x(),
initial_pose_estimate.translation().y()));
// 初始化在xy平移空间内偏移量,即窗口大小和偏移量
// 获取整个搜索空间的候选解
std::vector<Candidate2D> candidates =
GenerateExhaustiveSearchCandidates(search_parameters);
// 对所有候选解打分
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;
}
上面函数的有一下两个主要逻辑:
1、GenerateExhaustiveSearchTransforms:创建搜索空间,也就是说在一定范围内,等间隔划分搜索位置,这所有搜索位置的集合为搜索空间。
2、ScoreCandidate:最佳位置,应该出现在上面搜索空间中的某一个搜索位置。该函数的目的就是,对搜索空间中的所有搜索位置进行打分,找出打分最高的,就认为是最佳位置。
下面详细看这两个函数的代码,了解其细节
std::vector<transform::Rigid3f>
RealTimeCorrelativeScanMatcher::GenerateExhaustiveSearchTransforms(
const float resolution, const sensor::PointCloud& point_cloud) const {
std::vector<transform::Rigid3f> result;
//线性搜索步长=线性搜索距离(x,y,z方向)÷ 分辨率(每个格子的长度)
const int linear_window_size =
common::RoundToInt(options_.linear_search_window() / resolution);
// We set this value to something on the order of resolution to make sure that
// the std::acos() below is defined.
float max_scan_range = 3.f * resolution;
for (const Eigen::Vector3f& point : point_cloud) {
const float range = point.norm();
max_scan_range = std::max(range, max_scan_range);
}
const float kSafetyMargin = 1.f - 1e-3f;
const float angular_step_size = //采用余弦定理计算角度步长,具体理解见下面图片。
kSafetyMargin * std::acos(1.f - common::Pow2(resolution) /
(2.f * common::Pow2(max_scan_range)));
const int angular_window_size =
common::RoundToInt(options_.angular_search_window() / angular_step_size);
for (int z = -linear_window_size; z <= linear_window_size; ++z) {
for (int y = -linear_window_size; y <= linear_window_size; ++y) {
for (int x = -linear_window_size; x <= linear_window_size; ++x) {
for (int rz = -angular_window_size; rz <= angular_window_size; ++rz) {
for (int ry = -angular_window_size; ry <= angular_window_size; ++ry) {
for (int rx = -angular_window_size; rx <= angular_window_size;
++rx) {
const Eigen::Vector3f angle_axis(rx * angular_step_size,
ry * angular_step_size,
rz * angular_step_size);
result.emplace_back(
Eigen::Vector3f(x * resolution, y * resolution,
z * resolution),
transform::AngleAxisVectorToRotationQuaternion(angle_axis));
}
}
}
}
}
}
return result;
}
角度步长angular_step_size
的计算如下:
- 对候选位置进行打分:
ScoreCandidate
float RealTimeCorrelativeScanMatcher::ScoreCandidate(
const HybridGrid& hybrid_grid,
const sensor::PointCloud& transformed_point_cloud,
const transform::Rigid3f& transform) const {
float score = 0.f;
//获得每个point,在概率栅格图hybrid_grid上面的概率score, 求和,然后求平均。
for (const Eigen::Vector3f& point : transformed_point_cloud) {
score += hybrid_grid.GetProbability(hybrid_grid.GetCellIndex(point));
}
score /= static_cast<float>(transformed_point_cloud.size());
const float angle = transform::GetAngle(transform);
//求得的分值score,要乘以对应的权重。候选位置的平移量translation,旋转量angle越大,
//说明越偏离初始值,可靠性越差,对应的权重就越小。
score *=
std::exp(-common::Pow2(transform.translation().norm() *
options_.translation_delta_cost_weight() +
angle * options_.rotation_delta_cost_weight()));
CHECK_GT(score, 0.f);
return score;
}
从这段看目前没看到高/低分辨率地图、分支定界这块(应该是在ceres_scan_matcher这里,也就是说第一个超链接讲的范围不太对),实际是暴力匹配。