参考网址:
peitianyu/lite_slam (github.com)
scan_context | 某飞行员的随笔 (peitianyu.github.io)
ScanContext
代码流程
1 2 3 4 5 6 7 8 9 10 11
| - 构建KeyFrame(id, pose, key, scan_context) - 输入(id, pose, scan)MakeScanContext构造scan_context - 这里key使用scan_context在ring方向上pca构造 - AddKeyFrame添加关键帧用于之后的匹配 - DetectLoopClosure()寻找回环id并输出回环坐标
- 关键: * scan_context构建 * key构建 * nanoflann构建kdtree并搜索key * AlignScanContext()对齐scan_context获得先验坐标
|
原理简述
1 2 3 4
| 1. 将距离分成60份,角度分成60份,组成60*60矩阵,然后对距离pca化得到60维向量 2. 通过kdtree搜索key找到回环 3. 通过对齐60*60矩阵找到粗匹配,距离偏差与角度偏差,得到d_pose 4. 在loop_pose基础变换得到粗匹配值
|
代码体现
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
| void ScanContext::AddKeyFrame(const size_t &id, const Eigen::Vector3f& curr_pose, const std::vector<Eigen::Vector2f> &scan) { Eigen::MatrixXf scan_context = MakeScanContext(scan); KeyFrame key_frame = KeyFrame{id, curr_pose, scan_context}; m_key_frames->push_back(key_frame); }
Eigen::VectorXf CalculatePcaForKeyResult() { Eigen::MatrixXf tmp_scan_context = scan_context; tmp_scan_context.colwise() -= tmp_scan_context.rowwise().mean(); Eigen::MatrixXf cov = tmp_scan_context.transpose() * tmp_scan_context * (1 / tmp_scan_context.rows() - 1); Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> eigen_solver(cov);
return eigen_solver.eigenvectors().rightCols(1); }
const int ScanContext::DetectLoopClosure(const std::vector<Eigen::Vector2f> &scan, Eigen::Vector3f &loop_pose) { Eigen::MatrixXf scan_context = MakeScanContext(scan); KeyFrame key_frame = KeyFrame{std::numeric_limits<size_t>::max(), Eigen::Vector3f::Zero(), scan_context};
if(m_key_frames->size() < m_params.num_exclude_recent + 1){ return -1; }
std::vector<size_t> indexes(m_params.leaf_max_size); FindNearestNeighbor(key_frame, *m_key_frames, indexes);
float min_dist = std::numeric_limits<float>::max(); size_t nearest_id = 0; for(size_t &index: indexes){ float dist = (m_key_frames->key_frames[index].key_pose.head(2) - key_frame.key_pose.head(2)).norm(); if(dist < min_dist){ min_dist = dist; nearest_id = index; } } if(min_dist < m_params.min_dist){ Eigen::Vector3f delta_pose = AlignScanContext(scan_context, m_key_frames->key_frames[nearest_id].scan_context); loop_pose = TransformAdd(m_key_frames->key_frames[nearest_id].key_pose, delta_pose); NormalizePose(loop_pose); return m_key_frames->key_frames[nearest_id].id; }
return -1; }
Eigen::MatrixXf ScanContext::MakeScanContext(const std::vector<Eigen::Vector2f> &scan) { Eigen::MatrixXf scan_context(m_params.num_ring, m_params.num_sector); scan_context.setZero();
for(auto point: scan) { float angle = std::atan2(point(0), point(1)) / M_PI * 180.0f + 180.0f; float dist = std::hypot(point(0), point(1));
if(dist >= m_params.min_range && dist <= m_params.max_range){ int ring = std::max(std::min(m_params.num_ring - 1, static_cast<int>(round((dist / m_params.max_range) * m_params.num_ring))), 0); int sector = std::max(std::min(m_params.num_sector - 1, static_cast<int>(round((angle / 360.0f) * m_params.num_sector))), 0);
scan_context(ring, sector) += 1; } } return scan_context; }
Eigen::Vector3f ScanContext::AlignScanContext(const Eigen::MatrixXf &curr_scan_context, const Eigen::MatrixXf &loop_scan_context) { Eigen::MatrixXf tmp_scan_context = loop_scan_context; float sum = std::numeric_limits<float>::max(); int d_sector = 0; for(int i = 0; i < m_params.num_sector; i++) { tmp_scan_context.setZero(); tmp_scan_context.rightCols(i) = loop_scan_context.leftCols(i); tmp_scan_context.leftCols(m_params.num_sector - i) = loop_scan_context.rightCols(m_params.num_sector - i); float d_sum = 0; for(int j = 0; j < m_params.num_sector; j++){ d_sum += fabs(tmp_scan_context.col(j).sum() - curr_scan_context.col(j).sum()); }
if( sum > d_sum){ sum = d_sum; d_sector = i; } }
sum = std::numeric_limits<float>::max(); int d_ring = 0; for(int i = 0; i < m_params.num_ring; i++) { tmp_scan_context.setZero(); tmp_scan_context.topRows(i) = loop_scan_context.bottomRows(i); tmp_scan_context.bottomRows(m_params.num_ring - i) = loop_scan_context.topRows(m_params.num_ring - i); float d_sum = 0; for(int j = 0; j < m_params.num_ring; j++){ d_sum += fabs(tmp_scan_context.row(j).sum() - curr_scan_context.row(j).sum()); }
if( sum > d_sum){ sum = d_sum; d_ring = i; } } int tmp_d_ring = m_params.num_ring - d_ring; if(tmp_d_ring > m_params.num_ring / 2) tmp_d_ring - m_params.num_ring;
float angle = static_cast<float>((d_sector + 0.5) * 360 / m_params.num_sector) / 180.0f * M_PI; float min_dist = static_cast<float>(tmp_d_ring + 0.5) * m_params.max_range / m_params.num_ring; if(angle > M_PI) angle -= 2 * M_PI; return Eigen::Vector3f(min_dist * cos(angle), min_dist * sin(angle), angle); }
|