cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
ndt_impl.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include <algorithm>
7#include <numeric>
8#include <cmath>
9#include <mutex>
10
11namespace toolbox::pcl
12{
13
14template<typename DataType>
16{
17 if (this->m_target_cloud && (this->m_target_updated || m_voxel_grid_updated)) {
18 LOG_INFO_S << "构建目标点云的NDT体素网格 / Building NDT voxel grid for target cloud";
19 build_voxel_grid();
20 m_voxel_grid_updated = false;
21 }
22
23 // 计算高斯相关常数
24 m_gauss_d1 = -std::log(2 * M_PI) / 2.0;
25 m_gauss_d2 = -std::log(m_outlier_ratio * std::sqrt(2 * M_PI)) - 0.5;
26}
27
28template<typename DataType>
30 const transformation_t& initial_guess, result_type& result)
31{
32 // 初始化
33 transformation_t current_transform = initial_guess;
34 transformation_t previous_transform = current_transform;
35
36 DataType previous_error = std::numeric_limits<DataType>::max();
37 bool converged = false;
38
39 // More-Thuente线搜索参数
40 DataType step_size = m_step_size;
41
42 // 迭代优化
43 for (std::size_t iter = 0; iter < this->m_max_iterations; ++iter) {
44 // 计算目标函数值、梯度和Hessian
45 Vector6 gradient;
46 Matrix6 hessian;
47 DataType current_score = compute_objective(current_transform, &gradient, &hessian);
48
49 // 转换为最小化问题(NDT最大化似然,所以取负)
50 DataType current_error = -current_score;
51 DataType error_change = std::abs(current_error - previous_error);
52
53 // 记录迭代状态
54 this->record_iteration(result, iter, current_transform, current_error,
55 error_change, m_voxel_grid.size());
56
57 // 检查收敛
58 std::string termination_reason;
59 if (this->has_converged(iter, current_transform, previous_transform,
60 current_error, previous_error, termination_reason)) {
61 converged = true;
62 result.termination_reason = termination_reason;
63 break;
64 }
65
66 // 检查梯度范数
67 if (gradient.norm() < 1e-6) {
68 converged = true;
69 result.termination_reason = "gradient converged";
70 break;
71 }
72
73 // 求解牛顿方向:H * delta = -g
74 Vector6 delta = hessian.ldlt().solve(-gradient);
75
76 // 线搜索找到合适的步长
77 DataType optimal_step = line_search(current_transform, delta, step_size);
78
79 if (optimal_step < 1e-10) {
80 converged = true;
81 result.termination_reason = "line search failed";
82 break;
83 }
84
85 // 更新变换
86 Vector6 update = optimal_step * delta;
87 transformation_t delta_transform = vector_to_transformation(update);
88
89 previous_transform = current_transform;
90 current_transform = delta_transform * current_transform;
91 previous_error = current_error;
92
93 // 自适应调整步长
94 if (optimal_step > 0.8 * step_size) {
95 step_size = std::min(1.0, step_size * 1.5);
96 } else if (optimal_step < 0.1 * step_size) {
97 step_size = std::max(0.001, step_size * 0.5);
98 }
99 }
100
101 // 设置结果
102 result.transformation = current_transform;
103 result.converged = converged;
104 result.iterations_performed = result.history.size();
105 result.final_error = previous_error;
106
107 if (!converged && result.termination_reason.empty()) {
108 result.termination_reason = "maximum iterations reached";
109 }
110
111 return true;
112}
113
114template<typename DataType>
116{
117 m_voxel_grid.clear();
118
119 if (!this->m_target_cloud || this->m_target_cloud->empty()) {
120 return;
121 }
122
123 // 第一遍:将点分配到体素
124 std::unordered_map<std::size_t, std::vector<std::size_t>> voxel_indices;
125
126 for (std::size_t i = 0; i < this->m_target_cloud->size(); ++i) {
127 const auto& point = this->m_target_cloud->points[i];
128 Vector3 p(point.x, point.y, point.z);
129
130 auto voxel_idx = compute_voxel_index(p);
131 std::size_t key = get_voxel_key(voxel_idx);
132
133 voxel_indices[key].push_back(i);
134 }
135
136 // 第二遍:计算每个体素的统计信息(暂时禁用并行,避免编译错误)
137 for (const auto& [key, indices] : voxel_indices) {
138 if (indices.size() < 5) {
139 continue; // 需要至少5个点来计算有效的协方差
140 }
141
142 voxel_cell_t& cell = m_voxel_grid[key];
143 cell.num_points = indices.size();
144
145 // 计算均值
146 for (std::size_t idx : indices) {
147 const auto& p = this->m_target_cloud->points[idx];
148 cell.mean += Vector3(p.x, p.y, p.z);
149 }
150 cell.mean /= static_cast<DataType>(cell.num_points);
151
152 // 计算协方差
153 for (std::size_t idx : indices) {
154 const auto& p = this->m_target_cloud->points[idx];
155 Vector3 diff = Vector3(p.x, p.y, p.z) - cell.mean;
156 cell.covariance += diff * diff.transpose();
157 }
158 cell.covariance /= static_cast<DataType>(cell.num_points - 1);
159
160 // 正则化协方差矩阵,避免奇异
161 Matrix3 reg = Matrix3::Identity() * 0.01 * m_resolution * m_resolution;
162 cell.covariance += reg;
163
164 // 计算协方差的逆
165 cell.covariance_inv = cell.covariance.inverse();
166 cell.valid = true;
167 }
168
169 LOG_INFO_S << "构建了 " << m_voxel_grid.size() << " 个有效体素 / "
170 "Built " << m_voxel_grid.size() << " valid voxels";
171}
172
173template<typename DataType>
174std::array<int, 3> ndt_t<DataType>::compute_voxel_index(const Vector3& point) const
175{
176 std::array<int, 3> index;
177 index[0] = static_cast<int>(std::floor(point[0] / m_resolution));
178 index[1] = static_cast<int>(std::floor(point[1] / m_resolution));
179 index[2] = static_cast<int>(std::floor(point[2] / m_resolution));
180 return index;
181}
182
183template<typename DataType>
184std::size_t ndt_t<DataType>::get_voxel_key(const std::array<int, 3>& index) const
185{
186 // 使用hash组合三个索引
187 std::size_t h1 = std::hash<int>()(index[0]);
188 std::size_t h2 = std::hash<int>()(index[1]);
189 std::size_t h3 = std::hash<int>()(index[2]);
190
191 // 组合hash值
192 return h1 ^ (h2 << 1) ^ (h3 << 2);
193}
194
195template<typename DataType>
197 const transformation_t& transform,
198 Vector6* gradient,
199 Matrix6* hessian) const
200{
201 if (gradient) gradient->setZero();
202 if (hessian) hessian->setZero();
203
204 DataType total_score = 0;
205
206 // 串行版本(暂时禁用并行,避免编译错误)
207 for (std::size_t i = 0; i < this->m_source_cloud->size(); ++i) {
208 const auto& p = this->m_source_cloud->points[i];
209 Vector3 point(p.x, p.y, p.z);
210
211 total_score += compute_point_contribution(point, transform, gradient, hessian);
212 }
213
214 return total_score / static_cast<DataType>(this->m_source_cloud->size());
215}
216
217template<typename DataType>
219 const Vector3& point,
220 const transformation_t& transform,
221 Vector6* gradient,
222 Matrix6* hessian) const
223{
224 // 变换点
225 Eigen::Vector4<DataType> p_h(point[0], point[1], point[2], 1.0);
226 Eigen::Vector4<DataType> p_transformed_h = transform * p_h;
227 Vector3 p_transformed = p_transformed_h.template head<3>();
228
229 // 找到对应的体素
230 auto voxel_idx = compute_voxel_index(p_transformed);
231 std::size_t key = get_voxel_key(voxel_idx);
232
233 auto it = m_voxel_grid.find(key);
234 if (it == m_voxel_grid.end() || !it->second.valid) {
235 // 没有找到有效体素,返回异常值分数
236 return m_gauss_d2;
237 }
238
239 const voxel_cell_t& cell = it->second;
240
241 // 计算到体素中心的差值
242 Vector3 x_diff = p_transformed - cell.mean;
243
244 // 计算指数部分
245 DataType exp_arg = -0.5 * x_diff.transpose() * cell.covariance_inv * x_diff;
246
247 // 防止数值下溢
248 if (exp_arg < -20) {
249 return m_gauss_d2;
250 }
251
252 DataType exp_val = std::exp(exp_arg);
253 DataType score = m_gauss_d1 + exp_arg;
254
255 // 如果需要计算梯度和Hessian
256 if (gradient || hessian) {
257 // 计算雅可比矩阵 J = d(T*p)/d(params)
258 Eigen::Matrix<DataType, 3, 6> jacobian;
259 compute_jacobian(p_transformed, jacobian);
260
261 // 一阶导数项
262 Vector3 d_exp_dx = -cell.covariance_inv * x_diff;
263
264 if (gradient) {
265 *gradient += jacobian.transpose() * d_exp_dx * exp_val;
266 }
267
268 if (hessian) {
269 // 二阶导数项
270 Matrix3 d2_exp_dx2 = -cell.covariance_inv;
271
272 // 外积项
273 Matrix3 outer = d_exp_dx * d_exp_dx.transpose();
274
275 // 完整的Hessian
276 Matrix3 point_hessian = (d2_exp_dx2 + outer) * exp_val;
277
278 *hessian += jacobian.transpose() * point_hessian * jacobian;
279 }
280 }
281
282 return score;
283}
284
285template<typename DataType>
287 const transformation_t& current_transform,
288 const Vector6& direction,
289 DataType initial_step_size)
290{
291 // More-Thuente线搜索
292 DataType alpha = initial_step_size;
293 DataType alpha_min = 0;
294 DataType alpha_max = 1.0;
295
296 // 计算初始函数值
297 DataType f0 = compute_objective(current_transform);
298
299 // 计算初始梯度在搜索方向上的投影
300 Vector6 g0;
301 compute_objective(current_transform, &g0);
302 DataType dg0 = g0.dot(direction);
303
304 if (dg0 >= 0) {
305 // 不是下降方向
306 return 0;
307 }
308
309 // Wolfe条件参数
310 const DataType c1 = 1e-4;
311 const DataType c2 = 0.9;
312
313 for (std::size_t iter = 0; iter < m_line_search_max_iterations; ++iter) {
314 // 计算新的变换
315 Vector6 update = alpha * direction;
316 transformation_t delta = vector_to_transformation(update);
317 transformation_t new_transform = delta * current_transform;
318
319 // 计算新的函数值
320 DataType f_new = compute_objective(new_transform);
321
322 // 检查Armijo条件
323 if (f_new <= f0 + c1 * alpha * dg0) {
324 // 计算新的梯度
325 Vector6 g_new;
326 compute_objective(new_transform, &g_new);
327 DataType dg_new = g_new.dot(direction);
328
329 // 检查曲率条件
330 if (std::abs(dg_new) <= c2 * std::abs(dg0)) {
331 return alpha;
332 }
333
334 if (dg_new >= 0) {
335 alpha_max = alpha;
336 } else {
337 alpha_min = alpha;
338 }
339 } else {
340 alpha_max = alpha;
341 }
342
343 // 更新步长
344 if (alpha_max - alpha_min < 1e-10) {
345 break;
346 }
347
348 alpha = 0.5 * (alpha_min + alpha_max);
349 }
350
351 return alpha;
352}
353
354template<typename DataType>
357{
358 transformation_t transform = transformation_t::Identity();
359
360 // 平移部分
361 transform(0, 3) = vec[0];
362 transform(1, 3) = vec[1];
363 transform(2, 3) = vec[2];
364
365 // 旋转部分(使用ZYX欧拉角)
366 DataType roll = vec[3];
367 DataType pitch = vec[4];
368 DataType yaw = vec[5];
369
370 DataType sr = std::sin(roll);
371 DataType cr = std::cos(roll);
372 DataType sp = std::sin(pitch);
373 DataType cp = std::cos(pitch);
374 DataType sy = std::sin(yaw);
375 DataType cy = std::cos(yaw);
376
377 transform(0, 0) = cy * cp;
378 transform(0, 1) = cy * sp * sr - sy * cr;
379 transform(0, 2) = cy * sp * cr + sy * sr;
380
381 transform(1, 0) = sy * cp;
382 transform(1, 1) = sy * sp * sr + cy * cr;
383 transform(1, 2) = sy * sp * cr - cy * sr;
384
385 transform(2, 0) = -sp;
386 transform(2, 1) = cp * sr;
387 transform(2, 2) = cp * cr;
388
389 return transform;
390}
391
392template<typename DataType>
394 DataType x_bar, DataType x, const Matrix3& c_inv) const
395{
396 DataType d = x_bar - x;
397 DataType exp_arg = -0.5 * d * d * c_inv(0, 0);
398 return std::exp(exp_arg);
399}
400
401template<typename DataType>
403 const Vector3& point,
404 Eigen::Matrix<DataType, 3, 6>& jacobian) const
405{
406 // 雅可比矩阵: J = [I, -[p]×]
407 // 其中 [p]× 是点p的反对称矩阵
408
409 jacobian.setZero();
410
411 // 对平移的导数是单位矩阵
412 jacobian(0, 0) = 1.0;
413 jacobian(1, 1) = 1.0;
414 jacobian(2, 2) = 1.0;
415
416 // 对旋转的导数是 -[p]×
417 jacobian(0, 3) = 0;
418 jacobian(0, 4) = -point[2];
419 jacobian(0, 5) = point[1];
420
421 jacobian(1, 3) = point[2];
422 jacobian(1, 4) = 0;
423 jacobian(1, 5) = -point[0];
424
425 jacobian(2, 3) = -point[1];
426 jacobian(2, 4) = point[0];
427 jacobian(2, 5) = 0;
428}
429
430} // namespace toolbox::pcl
Eigen::Matrix< DataType, 4, 4 > transformation_t
Definition base_fine_registration.hpp:43
void compute_jacobian(const Vector3 &point, Eigen::Matrix< DataType, 3, 6 > &jacobian) const
计算雅可比矩阵 / Compute Jacobian matrix
Definition ndt_impl.hpp:402
Eigen::Matrix< DataType, 6, 1 > Vector6
Definition ndt.hpp:58
void build_voxel_grid()
构建体素网格 / Build voxel grid
Definition ndt_impl.hpp:115
transformation_t vector_to_transformation(const Vector6 &vec) const
将优化向量转换为变换矩阵 / Convert optimization vector to transformation matrix
Definition ndt_impl.hpp:356
DataType line_search(const transformation_t &current_transform, const Vector6 &direction, DataType initial_step_size)
执行More-Thuente线搜索 / Perform More-Thuente line search
Definition ndt_impl.hpp:286
Eigen::Matrix< DataType, 6, 6 > Matrix6
Definition ndt.hpp:59
DataType compute_objective(const transformation_t &transform, Vector6 *gradient=nullptr, Matrix6 *hessian=nullptr) const
计算目标函数和梯度 / Compute objective function and gradient
Definition ndt_impl.hpp:196
std::array< int, 3 > compute_voxel_index(const Vector3 &point) const
计算体素索引 / Compute voxel index
Definition ndt_impl.hpp:174
std::size_t get_voxel_key(const std::array< int, 3 > &index) const
获取体素键值 / Get voxel key
Definition ndt_impl.hpp:184
bool align_impl(const transformation_t &initial_guess, result_type &result)
执行配准 / Perform registration
Definition ndt_impl.hpp:29
DataType gaussian_d1(DataType x_bar, DataType x, const Matrix3 &c_inv) const
计算高斯分布的值 / Compute Gaussian distribution value
Definition ndt_impl.hpp:393
void preprocess_impl()
预处理(构建体素网格) / Preprocessing (build voxel grid)
Definition ndt_impl.hpp:15
DataType compute_point_contribution(const Vector3 &point, const transformation_t &transform, Vector6 *gradient=nullptr, Matrix6 *hessian=nullptr) const
计算单个点的贡献 / Compute contribution of single point
Definition ndt_impl.hpp:218
Eigen::Matrix< DataType, 3, 3 > Matrix3
Definition ndt.hpp:57
Eigen::Matrix< DataType, 3, 1 > Vector3
Definition ndt.hpp:56
#define LOG_INFO_S
INFO级别流式日志的宏 / Macro for INFO level stream logging.
Definition thread_logger.hpp:1330
Definition base_correspondence_generator.hpp:18
体素单元结构 / Voxel cell structure
Definition ndt.hpp:64
std::size_t num_points
点数 / Number of points
Definition ndt.hpp:68
bool valid
是否有效 / Is valid
Definition ndt.hpp:69
Matrix3 covariance_inv
协方差矩阵的逆 / Inverse of covariance
Definition ndt.hpp:67
Vector3 mean
均值 / Mean
Definition ndt.hpp:65
Matrix3 covariance
协方差矩阵 / Covariance matrix
Definition ndt.hpp:66