Autoware.Auto
ndt_optimization_problem.hpp
Go to the documentation of this file.
1 // Copyright 2019 the Autoware Foundation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // Co-developed by Tier IV, Inc. and Apex.AI, Inc.
16 
17 
18 // This file contains modified code from the following open source projects
19 // published under the licenses listed below:
20 //
21 // Software License Agreement (BSD License)
22 //
23 // Point Cloud Library (PCL) - www.pointclouds.org
24 // Copyright (c) 2010-2011, Willow Garage, Inc.
25 // Copyright (c) 2012-, Open Perception, Inc.
26 //
27 // All rights reserved.
28 //
29 // Redistribution and use in source and binary forms, with or without
30 // modification, are permitted provided that the following conditions
31 // are met:
32 //
33 // * Redistributions of source code must retain the above copyright
34 // notice, this list of conditions and the following disclaimer.
35 // * Redistributions in binary form must reproduce the above
36 // copyright notice, this list of conditions and the following
37 // disclaimer in the documentation and/or other materials provided
38 // with the distribution.
39 // * Neither the name of the copyright holder(s) nor the names of its
40 // contributors may be used to endorse or promote products derived
41 // from this software without specific prior written permission.
42 //
43 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
44 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
45 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
46 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
47 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
48 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
49 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
50 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
51 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
53 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
54 // POSSIBILITY OF SUCH DAMAGE.
55 
56 #ifndef NDT__NDT_OPTIMIZATION_PROBLEM_HPP_
57 #define NDT__NDT_OPTIMIZATION_PROBLEM_HPP_
58 
59 #include <ndt/ndt_map.hpp>
60 #include <ndt/ndt_scan.hpp>
61 #include <ndt/ndt_config.hpp>
63 #include <optimization/utils.hpp>
64 #include <ndt/utils.hpp>
65 #include <ndt/constraints.hpp>
66 #include <experimental/optional>
67 #include <Eigen/Core>
68 #include <Eigen/Geometry>
69 #include <tuple>
70 #include "common/types.hpp"
71 
74 
75 namespace autoware
76 {
77 namespace localization
78 {
79 namespace ndt
80 {
81 
82 template<typename ScalarT>
84 {
85  bool8_t ret = true;
86  if (std::isnan(p) || p > ScalarT{1.0} || p < ScalarT{0.0}) {
87  ret = false;
88  }
89  return ret;
90 }
91 
96 template<typename MapT,
98 class P2DNDTObjective : public common::optimization::CachedExpression<P2DNDTObjective<MapT>,
99  EigenPose<Real>, 1U, 6U, common::optimization::EigenComparator>
100 {
101 public:
102  // getting aliases from the base class.
106  using Value = typename ExpressionT::Value;
107  using Jacobian = typename ExpressionT::Jacobian;
108  using Hessian = typename ExpressionT::Hessian;
109  using Map = MapT;
110  using Scan = P2DNDTScan;
111  using Point = Eigen::Vector3d;
112  using Comparator = common::optimization::EigenComparator;
114  using PointGrad = Eigen::Matrix<float64_t, 3, 6>;
115  using PointHessian = Eigen::Matrix<float64_t, 18, 6>;
116 
128  const P2DNDTScan & scan, const Map & map, const P2DNDTOptimizationConfig config)
129  : m_scan_ref(scan), m_map_ref(map)
130  {
131  init(config.outlier_ratio());
132  }
133 
134  void evaluate_(const DomainValue & x, const ComputeMode & mode)
135  {
136  // Convert pose vector to transform matrix for easy point transformation
137  Eigen::Transform<float64_t, 3, Eigen::Affine, Eigen::ColMajor> transform;
138  transform.setIdentity();
140 
141  Value score{0.0};
142 
144  std::experimental::optional<GradientAngleParameters> grad_params;
145 
147  std::experimental::optional<HessianAngleParameters> hessian_params;
148 
149  {
150  // Angle parameters to be used by all elements (eq. 6.12) [Magnusson 2009]
151  const AngleParameters angle_params{x};
152  // Only construct jacobian/hessian variables if they are needed.
153  if (mode.jacobian() || mode.hessian()) {
154  jacobian.setZero();
155  grad_params.emplace(angle_params);
156  }
157  if (mode.hessian()) {
158  hessian.setZero();
159  hessian_params.emplace(angle_params);
160  }
161  }
162 
163  for (const auto & pt : m_scan_ref) {
164  PointGrad point_gradient;
165  PointHessian point_hessian;
166 
167  if (mode.jacobian() || mode.hessian()) {
168  point_gradient.setZero();
169  point_gradient.block<3, 3>(0, 0).setIdentity();
170  compute_point_gradients(grad_params.value(), pt, point_gradient);
171 
172  if (mode.hessian()) {
173  point_hessian.setZero();
174  compute_point_hessians(hessian_params.value(), pt, point_hessian);
175  }
176  }
177 
178  const Point pt_trans = transform * pt;
179  const auto & cells = m_map_ref.cell(pt_trans);
180 
181  for (const auto & cell : cells) {
182  const Point pt_trans_norm = pt_trans - cell.centroid();
183  // Cell iteration used for compatibility with maps with multi-cell lookup
184  if (cell.usable()) {
185  const auto & inv_cov = cell.inverse_covariance();
186  // e^(-d_2/2 * (x_k - mu_k)^T Sigma_k^-1 (x_k - mu_k)) Equation 6.9 [Magnusson 2009]
187  Real e_x_cov_x = std::exp(
188  -m_gauss_d2 * pt_trans_norm.dot(
189  inv_cov * pt_trans_norm) / 2.0);
190 
191  if (mode.score()) {
192  score += -m_gauss_d1 * e_x_cov_x;
193  }
194 
195  if (mode.jacobian() || mode.hessian()) {
196  const auto d2_e_x_cov_x = m_gauss_d2 * e_x_cov_x;
197 
198  // Error checking for invalid values.
199  // TODO(yunus.caliskan): Can be removed after covariance is checked
200  // for definiteness #216
201  if (!is_valid_probability(d2_e_x_cov_x)) {
202  continue;
203  }
204 
205  // Reusable portion of Equation 6.12 and 6.13 [Magnusson 2009]
206  const auto d1_d2_e_x_cov_x = m_gauss_d1 * d2_e_x_cov_x;
207 
208  for (auto i = 0U; i < jacobian.rows(); ++i) {
209  const Point cov_dxd_pi = inv_cov * point_gradient.col(i);
210  if (mode.jacobian()) {
211  jacobian(i) += pt_trans_norm.dot(cov_dxd_pi) * d1_d2_e_x_cov_x;
212  }
213  if (mode.hessian()) {
214  for (auto j = 0U; j < hessian.cols(); ++j) {
215  hessian(i, j) += d1_d2_e_x_cov_x * (-m_gauss_d2 * pt_trans_norm.dot(cov_dxd_pi) *
216  pt_trans_norm.dot(inv_cov * point_gradient.col(j)) +
217  pt_trans_norm.dot(inv_cov * point_hessian.block<3, 1>(3 * i, j)) +
218  point_gradient.col(j).dot(cov_dxd_pi));
219  }
220  }
221  }
222  }
223  }
224  }
225  }
226  if (mode.score()) {
227  this->set_score(score);
228  }
229  if (mode.jacobian()) {
230  this->set_jacobian(jacobian);
231  }
232  if (mode.hessian()) {
233  this->set_hessian(hessian);
234  }
235  }
236 
237 private:
239  struct AngleParameters
240  {
241 public:
242  static constexpr auto approx_thresh{10e-5};
243  AngleParameters() = delete;
244  explicit AngleParameters(const DomainValue & pose)
245  {
246  if (std::fabs(pose(3)) < approx_thresh) {
247  cx = 1.0;
248  sx = 0.0;
249  } else {
250  cx = std::cos(pose(3));
251  sx = std::sin(pose(3));
252  }
253 
254  if (std::fabs(pose(4)) < approx_thresh) {
255  cy = 1.0;
256  sy = 0.0;
257  } else {
258  cy = std::cos(pose(4));
259  sy = std::sin(pose(4));
260  }
261 
262  if (std::fabs(pose(5)) < approx_thresh) {
263  cz = 1.0;
264  sz = 0.0;
265  } else {
266  cz = std::cos(pose(5));
267  sz = std::sin(pose(5));
268  }
269  }
270 
271  Real cx{0.0};
272  Real cy{0.0};
273  Real cz{0.0};
274  Real sx{1.0};
275  Real sy{1.0};
276  Real sz{1.0};
277  };
278 
280  struct GradientAngleParameters
281  {
282 public:
283  GradientAngleParameters() = delete;
284  explicit GradientAngleParameters(const AngleParameters & params)
285  {
286  j_ang_a(0) = -params.sx * params.sz + params.cx * params.sy * params.cz;
287  j_ang_a(1) = -params.sx * params.cz - params.cx * params.sy * params.sz;
288  j_ang_a(2) = -params.cx * params.cy;
289 
290  j_ang_b(0) = params.cx * params.sz + params.sx * params.sy * params.cz;
291  j_ang_b(1) = params.cx * params.cz - params.sx * params.sy * params.sz;
292  j_ang_b(2) = -params.sx * params.cy;
293 
294  j_ang_c(0) = -params.sy * params.cz;
295  j_ang_c(1) = params.sy * params.sz;
296  j_ang_c(2) = params.cy;
297 
298  j_ang_d(0) = params.sx * params.cy * params.cz;
299  j_ang_d(1) = -params.sx * params.cy * params.sz;
300  j_ang_d(2) = params.sx * params.sy;
301 
302  j_ang_e(0) = -params.cx * params.cy * params.cz;
303  j_ang_e(1) = params.cx * params.cy * params.sz;
304  j_ang_e(2) = -params.cx * params.sy;
305 
306  j_ang_f(0) = -params.cy * params.sz;
307  j_ang_f(1) = -params.cy * params.cz;
308  j_ang_f(2) = 0.0;
309 
310  j_ang_g(0) = params.cx * params.cz - params.sx * params.sy * params.sz;
311  j_ang_g(1) = -params.cx * params.sz - params.sx * params.sy * params.cz;
312  j_ang_g(2) = 0.0;
313 
314  j_ang_h(0) = params.sx * params.cz + params.cx * params.sy * params.sz;
315  j_ang_h(1) = params.cx * params.sy * params.cz - params.sx * params.sz;
316  j_ang_h(2) = 0.0;
317  }
318 
319  Point j_ang_a, j_ang_b, j_ang_c, j_ang_d, j_ang_e, j_ang_f, j_ang_g, j_ang_h;
320  };
321 
323  struct HessianAngleParameters
324  {
325 public:
326  HessianAngleParameters() = delete;
327  explicit HessianAngleParameters(const AngleParameters & params)
328  {
329  h_ang_a2(0) = -params.cx * params.sz - params.sx * params.sy * params.cz;
330  h_ang_a2(1) = -params.cx * params.cz + params.sx * params.sy * params.sz;
331  h_ang_a2(2) = params.sx * params.cy;
332 
333  h_ang_a3(0) = -params.sx * params.sz + params.cx * params.sy * params.cz;
334  h_ang_a3(1) = -params.cx * params.sy * params.sz - params.sx * params.cz;
335  h_ang_a3(2) = -params.cx * params.cy;
336 
337  h_ang_b2(0) = params.cx * params.cy * params.cz;
338  h_ang_b2(1) = -params.cx * params.cy * params.sz;
339  h_ang_b2(2) = params.cx * params.sy;
340 
341  h_ang_b3(0) = params.sx * params.cy * params.cz;
342  h_ang_b3(1) = -params.sx * params.cy * params.sz;
343  h_ang_b3(2) = params.sx * params.sy;
344 
345  h_ang_c2(0) = -params.sx * params.cz - params.cx * params.sy * params.sz;
346  h_ang_c2(1) = params.sx * params.sz - params.cx * params.sy * params.cz;
347  h_ang_c2(2) = 0.0;
348 
349  h_ang_c3(0) = params.cx * params.cz - params.sx * params.sy * params.sz;
350  h_ang_c3(1) = -params.sx * params.sy * params.cz - params.cx * params.sz;
351  h_ang_c3(2) = 0.0;
352 
353  h_ang_d1(0) = -params.cy * params.cz;
354  h_ang_d1(1) = params.cy * params.sz;
355  h_ang_d1(2) = params.sy;
356 
357  h_ang_d2(0) = -params.sx * params.sy * params.cz;
358  h_ang_d2(1) = params.sx * params.sy * params.sz;
359  h_ang_d2(2) = params.sx * params.cy;
360 
361  h_ang_d3(0) = params.cx * params.sy * params.cz;
362  h_ang_d3(1) = -params.cx * params.sy * params.sz;
363  h_ang_d3(2) = -params.cx * params.cy;
364 
365  h_ang_e1(0) = params.sy * params.sz;
366  h_ang_e1(1) = params.sy * params.cz;
367  h_ang_e1(2) = 0.0;
368 
369  h_ang_e2(0) = -params.sx * params.cy * params.sz;
370  h_ang_e2(1) = -params.sx * params.cy * params.cz;
371  h_ang_e2(2) = 0.0;
372 
373  h_ang_e3(0) = params.cx * params.cy * params.sz;
374  h_ang_e3(1) = params.cx * params.cy * params.cz;
375  h_ang_e3(2) = 0.0;
376 
377  h_ang_f1(0) = -params.cy * params.cz;
378  h_ang_f1(1) = params.cy * params.sz;
379  h_ang_f1(2) = 0.0;
380 
381  h_ang_f2(0) = -params.cx * params.sz - params.sx * params.sy * params.cz;
382  h_ang_f2(1) = -params.cx * params.cz + params.sx * params.sy * params.sz;
383  h_ang_f2(2) = 0.0;
384 
385  h_ang_f3(0) = -params.sx * params.sz + params.cx * params.sy * params.cz;
386  h_ang_f3(1) = -params.cx * params.sy * params.sz - params.sx * params.cz;
387  h_ang_f3(2) = 0.0;
388  }
389 
390  Point h_ang_a2, h_ang_a3,
391  h_ang_b2, h_ang_b3,
392  h_ang_c2, h_ang_c3,
393  h_ang_d1, h_ang_d2, h_ang_d3,
394  h_ang_e1, h_ang_e2, h_ang_e3,
395  h_ang_f1, h_ang_f2, h_ang_f3;
396  };
397 
398  void compute_point_gradients(
399  const GradientAngleParameters & params,
400  const Point & x,
401  PointGrad & point_gradient)
402  {
403  point_gradient(1, 3) = x.dot(params.j_ang_a);
404  point_gradient(2, 3) = x.dot(params.j_ang_b);
405  point_gradient(0, 4) = x.dot(params.j_ang_c);
406  point_gradient(1, 4) = x.dot(params.j_ang_d);
407  point_gradient(2, 4) = x.dot(params.j_ang_e);
408  point_gradient(0, 5) = x.dot(params.j_ang_f);
409  point_gradient(1, 5) = x.dot(params.j_ang_g);
410  point_gradient(2, 5) = x.dot(params.j_ang_h);
411  }
412 
413  void compute_point_hessians(
414  const HessianAngleParameters & params,
415  const Point & x,
416  PointHessian & point_hessian)
417  {
418  const Point a{0.0, x.dot(params.h_ang_a2), x.dot(params.h_ang_a3)};
419  const Point b{0.0, x.dot(params.h_ang_b2), x.dot(params.h_ang_b3)};
420  const Point c{0.0, x.dot(params.h_ang_c2), x.dot(params.h_ang_c3)};
421  const Point d{x.dot(params.h_ang_d1), x.dot(params.h_ang_d2),
422  x.dot(params.h_ang_d3)};
423  const Point e{x.dot(params.h_ang_e1), x.dot(params.h_ang_e2),
424  x.dot(params.h_ang_e3)};
425  const Point f{x.dot(params.h_ang_f1), x.dot(params.h_ang_f2),
426  x.dot(params.h_ang_f3)};
427 
428  point_hessian.block<3, 1>(9, 3) = a;
429  point_hessian.block<3, 1>(12, 3) = b;
430  point_hessian.block<3, 1>(15, 3) = c;
431  point_hessian.block<3, 1>(9, 4) = b;
432  point_hessian.block<3, 1>(12, 4) = d;
433  point_hessian.block<3, 1>(15, 4) = e;
434  point_hessian.block<3, 1>(9, 5) = c;
435  point_hessian.block<3, 1>(12, 5) = e;
436  point_hessian.block<3, 1>(15, 5) = f;
437  }
438 
442  void init(Real outlier_ratio)
443  {
444  if (!is_valid_probability(outlier_ratio)) {
445  throw std::domain_error("Outlier ratio must be between 0 and 1");
446  }
447  const auto c_size = m_map_ref.cell_size();
448  // The gaussian fitting parameters below are taken from the PCL implementation.
449  // 10.0 seems to be a magic number. For details on the gaussian
450  // approximation of the mixture probability in see [Biber et al, 2004] and [Magnusson 2009].
451  const auto gauss_c1 = 10.0 * (1.0 - outlier_ratio);
452  const auto gauss_c2 = outlier_ratio / static_cast<Real>(c_size.x * c_size.y * c_size.z);
453  const auto gauss_d3 = -std::log(gauss_c2);
454  m_gauss_d1 = -std::log(gauss_c1 + gauss_c2) - gauss_d3;
455  m_gauss_d2 = -2 *
456  std::log((-std::log(gauss_c1 * std::exp(-0.5) + gauss_c2) - gauss_d3) / m_gauss_d1);
457  }
458 
459  // references as class members to be initialized at constructor.
460  const Scan & m_scan_ref;
461  const Map & m_map_ref;
462  // States:
463  Real m_gauss_d1{0.0};
464  Real m_gauss_d2{0.0};
465 };
466 
467 template<typename MapT>
470  6U>;
471 } // namespace ndt
472 } // namespace localization
473 } // namespace autoware
474 
475 #endif // NDT__NDT_OPTIMIZATION_PROBLEM_HPP_
This constraint expresses the map interface requirements for the localizer.
Definition: ndt/include/ndt/constraints.hpp:65
double float64_t
Definition: types.hpp:37
bool8_t is_valid_probability(ScalarT p)
Definition: ndt_optimization_problem.hpp:83
Definition: common/optimization/include/optimization/utils.hpp:38
DifferentialEquation f
Definition: kinematic_bicycle.snippet.hpp:44
Definition: ndt_scan.hpp:99
Eigen::Vector3d Point
Definition: ndt_optimization_problem.hpp:111
Real outlier_ratio() const noexcept
Definition: ndt_config.hpp:65
Eigen::Matrix< T, 6U, 1U > EigenPose
Definition: localization/ndt/include/ndt/utils.hpp:83
bool bool8_t
Definition: types.hpp:33
Config class for p2d optimziation problem.
Definition: ndt_config.hpp:54
This file includes common type definition.
Definition: ndt_optimization_problem.hpp:98
bool8_t score() const noexcept
Definition: utils.cpp:47
P2DNDTObjective(const P2DNDTScan &scan, const Map &map, const P2DNDTOptimizationConfig config)
Definition: ndt_optimization_problem.hpp:127
Requires
Definition: ndt/include/ndt/constraints.hpp:29
void pose_to_transform(const PoseT &pose, TransformT &transform)
float64_t Real
Definition: ndt_common.hpp:39
void evaluate_(const DomainValue &x, const ComputeMode &mode)
Definition: ndt_optimization_problem.hpp:134
typename ExpressionT::DomainValue DomainValue
Definition: ndt_optimization_problem.hpp:105
a
Definition: catr_diff.py:22
DifferentialState x
Definition: kinematic_bicycle.snippet.hpp:28
Definition: optimization_problem.hpp:109
MapT Map
Definition: ndt_optimization_problem.hpp:109
typename ExpressionT::Hessian Hessian
Definition: ndt_optimization_problem.hpp:108
typename ExpressionT::Value Value
Definition: ndt_optimization_problem.hpp:106
bool8_t jacobian() const noexcept
Definition: utils.cpp:48
typename ExpressionT::Jacobian Jacobian
Definition: ndt_optimization_problem.hpp:107
bool8_t hessian() const noexcept
Definition: utils.cpp:49
Eigen::Matrix< float64_t, 3, 6 > PointGrad
Definition: ndt_optimization_problem.hpp:114
Eigen::Matrix< float64_t, 18, 6 > PointHessian
Definition: ndt_optimization_problem.hpp:115
Generic equality comparison functor for eigen matrices.
Definition: common/optimization/include/optimization/utils.hpp:86
This file defines the lanelet2_map_provider_node class.
Definition: quick_sort.hpp:24