cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
point_to_point_icp_impl.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include <algorithm>
7#include <numeric>
8#include <future>
9#include <mutex>
10
11namespace toolbox::pcl
12{
13
14template<typename DataType, typename KNNSearcher>
16 const transformation_t& initial_guess, result_type& result)
17{
18 // 初始化
19 transformation_t current_transform = initial_guess;
20 transformation_t previous_transform = current_transform;
21
22 DataType previous_error = std::numeric_limits<DataType>::max();
23 bool converged = false;
24
25 // 创建源点云的副本用于变换
26 point_cloud transformed_source = *this->m_source_cloud;
27
28 // 迭代优化
29 for (std::size_t iter = 0; iter < this->m_max_iterations; ++iter) {
30 // 变换源点云
31 for (std::size_t i = 0; i < transformed_source.size(); ++i) {
32 const auto& p = this->m_source_cloud->points[i];
33 Eigen::Vector4<DataType> pt(p.x, p.y, p.z, 1.0);
34 Eigen::Vector4<DataType> transformed_pt = current_transform * pt;
35 transformed_source.points[i].x = transformed_pt[0];
36 transformed_source.points[i].y = transformed_pt[1];
37 transformed_source.points[i].z = transformed_pt[2];
38 }
39
40 // 查找对应关系
41 std::vector<std::pair<std::size_t, std::size_t>> correspondences;
42 std::vector<DataType> distances;
43 find_correspondences(transformed_source, correspondences, distances);
44
45 if (correspondences.empty()) {
46 LOG_ERROR_S << "未找到有效的对应关系 / No valid correspondences found";
47 result.termination_reason = "no correspondences";
48 return false;
49 }
50
51 // 剔除异常值
52 if (m_outlier_rejection_ratio > 0) {
53 reject_outliers(correspondences, distances);
54 }
55
56 // 计算当前误差
57 DataType current_error = compute_error(distances);
58 DataType error_change = std::abs(current_error - previous_error);
59
60 // 记录迭代状态
61 this->record_iteration(result, iter, current_transform, current_error,
62 error_change, correspondences.size());
63
64 // 检查收敛
65 std::string termination_reason;
66 if (this->has_converged(iter, current_transform, previous_transform,
67 current_error, previous_error, termination_reason)) {
68 converged = true;
69 result.termination_reason = termination_reason;
70 break;
71 }
72
73 // 计算新的变换(从变换后的源到目标的变换)
74 transformation_t delta_transform = compute_transformation(
75 transformed_source, *this->m_target_cloud, correspondences, distances);
76
77 // 更新变换
78 previous_transform = current_transform;
79 current_transform = delta_transform * current_transform;
80 previous_error = current_error;
81 }
82
83 // 设置结果
84 result.transformation = current_transform;
85 result.converged = converged;
86 result.iterations_performed = result.history.size();
87 result.final_error = previous_error;
88
89 if (!converged && result.termination_reason.empty()) {
90 result.termination_reason = "maximum iterations reached";
91 }
92
93 return true;
94}
95
96template<typename DataType, typename KNNSearcher>
98 const point_cloud& transformed_source,
99 std::vector<std::pair<std::size_t, std::size_t>>& correspondences,
100 std::vector<DataType>& distances)
101{
102 correspondences.clear();
103 distances.clear();
104 correspondences.reserve(transformed_source.size());
105 distances.reserve(transformed_source.size());
106
107 if (m_enable_parallel) {
108 // 并行版本 - 先收集到线程局部存储,再合并
110 const std::size_t num_threads = pool.get_thread_count();
111 const std::size_t chunk_size = std::max(std::size_t(1),
112 transformed_source.size() / num_threads);
113
114 std::vector<std::future<void>> futures;
115 std::vector<std::vector<std::pair<std::size_t, std::size_t>>> thread_correspondences(num_threads);
116 std::vector<std::vector<DataType>> thread_distances(num_threads);
117
118 for (std::size_t t = 0; t < num_threads; ++t) {
119 std::size_t start = t * chunk_size;
120 std::size_t end = (t == num_threads - 1) ? transformed_source.size() : (t + 1) * chunk_size;
122 futures.emplace_back(pool.submit([this, &transformed_source, start, end,
123 &thread_correspondences, &thread_distances, t]() {
124 std::vector<std::size_t> indices;
125 std::vector<DataType> dists;
126
127 for (std::size_t i = start; i < end; ++i) {
128 const auto& point = transformed_source.points[i];
129 this->m_knn_searcher->kneighbors(point, 1, indices, dists);
130
131 if (!indices.empty() && dists[0] <= this->m_max_correspondence_distance * this->m_max_correspondence_distance) {
132 thread_correspondences[t].emplace_back(i, indices[0]);
133 thread_distances[t].push_back(std::sqrt(dists[0]));
135 }
136 }));
137 }
138
139 // 等待所有任务完成
140 for (auto& fut : futures) {
141 fut.wait();
142 }
143
144 // 合并结果
145 for (std::size_t t = 0; t < num_threads; ++t) {
146 correspondences.insert(correspondences.end(),
147 thread_correspondences[t].begin(),
148 thread_correspondences[t].end());
149 distances.insert(distances.end(),
150 thread_distances[t].begin(),
151 thread_distances[t].end());
152 }
153 } else {
154 // 串行查找对应关系
155 std::vector<std::size_t> indices;
156 std::vector<DataType> dists;
157
158 for (std::size_t i = 0; i < transformed_source.size(); ++i) {
159 const auto& point = transformed_source.points[i];
160 m_knn_searcher->kneighbors(point, 1, indices, dists);
161
162 if (!indices.empty() && dists[0] <= this->m_max_correspondence_distance * this->m_max_correspondence_distance) {
163 correspondences.emplace_back(i, indices[0]);
164 distances.push_back(std::sqrt(dists[0]));
165 }
166 }
167 }
168}
169
170template<typename DataType, typename KNNSearcher>
173 const point_cloud& source,
174 const point_cloud& target,
175 const std::vector<std::pair<std::size_t, std::size_t>>& correspondences,
176 const std::vector<DataType>& distances)
177{
178 using Vector3 = Eigen::Matrix<DataType, 3, 1>;
179 using Matrix3 = Eigen::Matrix<DataType, 3, 3>;
180
181 // 计算质心
182 Vector3 source_centroid = Vector3::Zero();
183 Vector3 target_centroid = Vector3::Zero();
184
185 for (const auto& [src_idx, tgt_idx] : correspondences) {
186 const auto& src_pt = source.points[src_idx];
187 const auto& tgt_pt = target.points[tgt_idx];
188
189 source_centroid += Vector3(src_pt.x, src_pt.y, src_pt.z);
190 target_centroid += Vector3(tgt_pt.x, tgt_pt.y, tgt_pt.z);
191 }
192
193 const DataType num_corr = static_cast<DataType>(correspondences.size());
194 source_centroid /= num_corr;
195 target_centroid /= num_corr;
196
197 // 计算去质心的协方差矩阵
198 Matrix3 W = Matrix3::Zero();
199
200 for (const auto& [src_idx, tgt_idx] : correspondences) {
201 const auto& src_pt = source.points[src_idx];
202 const auto& tgt_pt = target.points[tgt_idx];
203
204 Vector3 src_centered(src_pt.x - source_centroid[0],
205 src_pt.y - source_centroid[1],
206 src_pt.z - source_centroid[2]);
207
208 Vector3 tgt_centered(tgt_pt.x - target_centroid[0],
209 tgt_pt.y - target_centroid[1],
210 tgt_pt.z - target_centroid[2]);
211
212 W += src_centered * tgt_centered.transpose();
213 }
214
215 // SVD分解
216 Eigen::JacobiSVD<Matrix3> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);
217 Matrix3 U = svd.matrixU();
218 Matrix3 V = svd.matrixV();
219
220 // 计算旋转矩阵
221 Matrix3 R = V * U.transpose();
222
223 // 处理反射情况
224 if (R.determinant() < 0) {
225 V.col(2) *= -1;
226 R = V * U.transpose();
227 }
228
229 // 计算平移向量
230 Vector3 t = target_centroid - R * source_centroid;
231
232 // 构建变换矩阵
233 transformation_t transform = transformation_t::Identity();
234 transform.template block<3, 3>(0, 0) = R;
235 transform.template block<3, 1>(0, 3) = t;
236
237 return transform;
238}
239
240template<typename DataType, typename KNNSearcher>
242 const std::vector<DataType>& distances) const
243{
244 if (distances.empty()) {
245 return std::numeric_limits<DataType>::max();
246 }
247
248 DataType sum = std::accumulate(distances.begin(), distances.end(),
249 static_cast<DataType>(0),
250 [](DataType a, DataType b) { return a + b * b; });
251
252 return sum / static_cast<DataType>(distances.size());
253}
254
255template<typename DataType, typename KNNSearcher>
257 std::vector<std::pair<std::size_t, std::size_t>>& correspondences,
258 std::vector<DataType>& distances)
259{
260 if (correspondences.empty() || m_outlier_rejection_ratio <= 0) {
261 return;
262 }
263
264 // 创建索引数组用于排序
265 std::vector<std::size_t> indices(distances.size());
266 std::iota(indices.begin(), indices.end(), 0);
267
268 // 按距离排序索引
269 std::sort(indices.begin(), indices.end(),
270 [&distances](std::size_t a, std::size_t b) {
271 return distances[a] < distances[b];
272 });
273
274 // 计算保留的对应关系数量
275 std::size_t num_to_keep = static_cast<std::size_t>(
276 correspondences.size() * (1.0 - m_outlier_rejection_ratio));
277 num_to_keep = std::max(num_to_keep, std::size_t(1));
278
279 // 创建新的对应关系和距离
280 std::vector<std::pair<std::size_t, std::size_t>> new_correspondences;
281 std::vector<DataType> new_distances;
282 new_correspondences.reserve(num_to_keep);
283 new_distances.reserve(num_to_keep);
284
285 for (std::size_t i = 0; i < num_to_keep; ++i) {
286 std::size_t idx = indices[i];
287 new_correspondences.push_back(correspondences[idx]);
288 new_distances.push_back(distances[idx]);
289 }
290
291 correspondences = std::move(new_correspondences);
292 distances = std::move(new_distances);
293}
294
295} // namespace toolbox::pcl
static thread_pool_singleton_t & instance()
获取单例实例/Get the singleton instance
Definition thread_pool_singleton.hpp:23
Eigen::Matrix< DataType, 4, 4 > transformation_t
Definition base_fine_registration.hpp:43
bool align_impl(const transformation_t &initial_guess, result_type &result)
执行配准 / Perform registration
Definition point_to_point_icp_impl.hpp:15
transformation_t compute_transformation(const point_cloud &source, const point_cloud &target, const std::vector< std::pair< std::size_t, std::size_t > > &correspondences, const std::vector< DataType > &distances)
计算变换矩阵(使用SVD) / Compute transformation matrix (using SVD)
Definition point_to_point_icp_impl.hpp:172
void find_correspondences(const point_cloud &transformed_source, std::vector< std::pair< std::size_t, std::size_t > > &correspondences, std::vector< DataType > &distances)
查找对应关系 / Find correspondences
Definition point_to_point_icp_impl.hpp:97
Eigen::Matrix< DataType, 4, 4 > transformation_t
Definition base_fine_registration.hpp:43
void reject_outliers(std::vector< std::pair< std::size_t, std::size_t > > &correspondences, std::vector< DataType > &distances)
剔除异常值 / Reject outliers
Definition point_to_point_icp_impl.hpp:256
DataType compute_error(const std::vector< DataType > &distances) const
计算配准误差 / Compute registration error
Definition point_to_point_icp_impl.hpp:241
std::vector< point_t< T > > points
点坐标 / Point coordinates
Definition point.hpp:270
auto size() const -> std::size_t
获取点云中的点数 / Get number of points in cloud
Definition point_impl.hpp:293
#define LOG_ERROR_S
ERROR级别流式日志的宏 / Macro for ERROR level stream logging.
Definition thread_logger.hpp:1332
Definition base_correspondence_generator.hpp:18
细配准结果 / Fine registration result
Definition registration_result.hpp:46
std::vector< iteration_state_t< DataType > > history
迭代历史(可选) / Iteration history (optional)
Definition registration_result.hpp:61
std::string termination_reason
终止原因 / Termination reason
Definition registration_result.hpp:58
transformation_t transformation
最终变换 / Final transformation
Definition registration_result.hpp:49
bool converged
是否收敛 / Whether converged
Definition registration_result.hpp:57
DataType final_error
最终误差 / Final error
Definition registration_result.hpp:53
std::size_t iterations_performed
执行的迭代次数 / Iterations performed
Definition registration_result.hpp:55