PageRenderTime 56ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/scanmatching/src/opt_trans_3d.cpp

http://github.com/gamman/MRPT
C++ | 361 lines | 211 code | 62 blank | 88 comment | 18 complexity | ae7721bf8dd2dcd10e3ac44fcb62eb30 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause
  1. /* +---------------------------------------------------------------------------+
  2. | The Mobile Robot Programming Toolkit (MRPT) C++ library |
  3. | |
  4. | http://www.mrpt.org/ |
  5. | |
  6. | Copyright (C) 2005-2011 University of Malaga |
  7. | |
  8. | This software was written by the Machine Perception and Intelligent |
  9. | Robotics Lab, University of Malaga (Spain). |
  10. | Contact: Jose-Luis Blanco <jlblanco@ctima.uma.es> |
  11. | |
  12. | This file is part of the MRPT project. |
  13. | |
  14. | MRPT is free software: you can redistribute it and/or modify |
  15. | it under the terms of the GNU General Public License as published by |
  16. | the Free Software Foundation, either version 3 of the License, or |
  17. | (at your option) any later version. |
  18. | |
  19. | MRPT is distributed in the hope that it will be useful, |
  20. | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  21. | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  22. | GNU General Public License for more details. |
  23. | |
  24. | You should have received a copy of the GNU General Public License |
  25. | along with MRPT. If not, see <http://www.gnu.org/licenses/>. |
  26. | |
  27. +---------------------------------------------------------------------------+ */
  28. #include <mrpt/scanmatching.h> // Precompiled header
  29. #include <mrpt/scanmatching/scan_matching.h>
  30. #include <mrpt/poses/CPosePDFGaussian.h>
  31. #include <mrpt/poses/CPosePDFSOG.h>
  32. #include <mrpt/random.h>
  33. #include <mrpt/math/CMatrixD.h>
  34. #include <mrpt/math/utils.h>
  35. #include <mrpt/math/CQuaternion.h>
  36. #include <algorithm>
  37. using namespace mrpt;
  38. using namespace mrpt::scanmatching;
  39. using namespace mrpt::random;
  40. using namespace mrpt::utils;
  41. using namespace std;
  42. /*---------------------------------------------------------------
  43. HornMethod
  44. ---------------------------------------------------------------*/
  45. double scanmatching::HornMethod(
  46. const vector_double &inVector,
  47. vector_double &outVector, // The output vector
  48. bool forceScaleToUnity )
  49. {
  50. MRPT_START
  51. vector_double input;
  52. input.resize( inVector.size() );
  53. input = inVector;
  54. outVector.resize( 7 );
  55. // Compute the centroids
  56. TPoint3D cL(0,0,0), cR(0,0,0);
  57. const size_t nMatches = input.size()/6;
  58. ASSERT_EQUAL_(input.size()%6, 0)
  59. for( unsigned int i = 0; i < nMatches; i++ )
  60. {
  61. cL.x += input[i*6+3];
  62. cL.y += input[i*6+4];
  63. cL.z += input[i*6+5];
  64. cR.x += input[i*6+0];
  65. cR.y += input[i*6+1];
  66. cR.z += input[i*6+2];
  67. }
  68. ASSERT_ABOVE_(nMatches,0)
  69. const double F = 1.0/nMatches;
  70. cL *= F;
  71. cR *= F;
  72. CMatrixDouble33 S; // S.zeros(); // Zeroed by default
  73. // Substract the centroid and compute the S matrix of cross products
  74. for( unsigned int i = 0; i < nMatches; i++ )
  75. {
  76. input[i*6+3] -= cL.x;
  77. input[i*6+4] -= cL.y;
  78. input[i*6+5] -= cL.z;
  79. input[i*6+0] -= cR.x;
  80. input[i*6+1] -= cR.y;
  81. input[i*6+2] -= cR.z;
  82. S.get_unsafe(0,0) += input[i*6+3]*input[i*6+0];
  83. S.get_unsafe(0,1) += input[i*6+3]*input[i*6+1];
  84. S.get_unsafe(0,2) += input[i*6+3]*input[i*6+2];
  85. S.get_unsafe(1,0) += input[i*6+4]*input[i*6+0];
  86. S.get_unsafe(1,1) += input[i*6+4]*input[i*6+1];
  87. S.get_unsafe(1,2) += input[i*6+4]*input[i*6+2];
  88. S.get_unsafe(2,0) += input[i*6+5]*input[i*6+0];
  89. S.get_unsafe(2,1) += input[i*6+5]*input[i*6+1];
  90. S.get_unsafe(2,2) += input[i*6+5]*input[i*6+2];
  91. }
  92. // Construct the N matrix
  93. CMatrixDouble44 N; // N.zeros(); // Zeroed by default
  94. N.set_unsafe( 0,0,S.get_unsafe(0,0) + S.get_unsafe(1,1) + S.get_unsafe(2,2) );
  95. N.set_unsafe( 0,1,S.get_unsafe(1,2) - S.get_unsafe(2,1) );
  96. N.set_unsafe( 0,2,S.get_unsafe(2,0) - S.get_unsafe(0,2) );
  97. N.set_unsafe( 0,3,S.get_unsafe(0,1) - S.get_unsafe(1,0) );
  98. N.set_unsafe( 1,0,N.get_unsafe(0,1) );
  99. N.set_unsafe( 1,1,S.get_unsafe(0,0) - S.get_unsafe(1,1) - S.get_unsafe(2,2) );
  100. N.set_unsafe( 1,2,S.get_unsafe(0,1) + S.get_unsafe(1,0) );
  101. N.set_unsafe( 1,3,S.get_unsafe(2,0) + S.get_unsafe(0,2) );
  102. N.set_unsafe( 2,0,N.get_unsafe(0,2) );
  103. N.set_unsafe( 2,1,N.get_unsafe(1,2) );
  104. N.set_unsafe( 2,2,-S.get_unsafe(0,0) + S.get_unsafe(1,1) - S.get_unsafe(2,2) );
  105. N.set_unsafe( 2,3,S.get_unsafe(1,2) + S.get_unsafe(2,1) );
  106. N.set_unsafe( 3,0,N.get_unsafe(0,3) );
  107. N.set_unsafe( 3,1,N.get_unsafe(1,3) );
  108. N.set_unsafe( 3,2,N.get_unsafe(2,3) );
  109. N.set_unsafe( 3,3,-S.get_unsafe(0,0) - S.get_unsafe(1,1) + S.get_unsafe(2,2) );
  110. // q is the quaternion correspondent to the greatest eigenvector of the N matrix (last column in Z)
  111. CMatrixDouble44 Z, D;
  112. vector_double v;
  113. N.eigenVectors( Z, D );
  114. Z.extractCol( Z.getColCount()-1, v );
  115. ASSERTDEB_( fabs( sqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] + v[3]*v[3] ) - 1.0 ) < 0.1 );
  116. // Make q_r > 0
  117. if( v[0] < 0 ){ v[0] *= -1; v[1] *= -1; v[2] *= -1; v[3] *= -1; }
  118. CPose3DQuat q; // Create a pose rotation with the quaternion
  119. for(unsigned int i = 0; i < 4; i++ ) // insert the quaternion part
  120. outVector[i+3] = q[i+3] = v[i];
  121. // Compute scale
  122. double num = 0.0;
  123. double den = 0.0;
  124. for( unsigned int i = 0; i < nMatches; i++ )
  125. {
  126. num += square( input[i*6+0] ) + square( input[i*6+1] ) + square( input[i*6+2] );
  127. den += square( input[i*6+3] ) + square( input[i*6+4] ) + square( input[i*6+5] );
  128. } // end-for
  129. // The scale:
  130. double s = std::sqrt( num/den );
  131. // Enforce scale to be 1
  132. if( forceScaleToUnity )
  133. s = 1.0;
  134. TPoint3D pp;
  135. q.composePoint( cL.x, cL.y, cL.z, pp.x, pp.y, pp.z );
  136. pp*=s;
  137. outVector[0] = cR.x - pp.x; // X
  138. outVector[1] = cR.y - pp.y; // Y
  139. outVector[2] = cR.z - pp.z; // Z
  140. return s; // return scale
  141. MRPT_END
  142. }
  143. //! \overload
  144. double scanmatching::HornMethod(
  145. const vector_double &inPoints,
  146. mrpt::poses::CPose3DQuat &outQuat,
  147. bool forceScaleToUnity )
  148. {
  149. vector_double outV;
  150. const double s = HornMethod(inPoints,outV,forceScaleToUnity);
  151. for (int i=0;i<7;i++)
  152. outQuat[i]=outV[i];
  153. return s;
  154. }
  155. //*---------------------------------------------------------------
  156. // leastSquareErrorRigidTransformation6D
  157. // ---------------------------------------------------------------*/
  158. bool scanmatching::leastSquareErrorRigidTransformation6D(
  159. const TMatchingPairList &in_correspondences,
  160. CPose3DQuat &out_transformation,
  161. double &out_scale,
  162. const bool forceScaleToUnity)
  163. {
  164. MRPT_START
  165. if( in_correspondences.size() < 3 )
  166. THROW_EXCEPTION( "[leastSquareErrorRigidTransformation6D]: Error: at least 3 correspondences must be provided" );
  167. CPoint3D cL, cR;
  168. CMatrixD S, N;
  169. CMatrixD Z, D;
  170. vector_double v;
  171. const size_t nMatches = in_correspondences.size();
  172. double s; // Scale
  173. // Compute the centroid
  174. TMatchingPairList::const_iterator itMatch;
  175. for(itMatch = in_correspondences.begin(); itMatch != in_correspondences.end(); itMatch++)
  176. {
  177. cL.x_incr( itMatch->other_x );
  178. cL.y_incr( itMatch->other_y );
  179. cL.z_incr( itMatch->other_z );
  180. cR.x_incr( itMatch->this_x );
  181. cR.y_incr( itMatch->this_y );
  182. cR.z_incr( itMatch->this_z );
  183. }
  184. const double F = 1.0/nMatches;
  185. cL *= F;
  186. cR *= F;
  187. TMatchingPairList auxList( in_correspondences );
  188. TMatchingPairList::iterator auxIt;
  189. // Substract the centroid
  190. for( auxIt = auxList.begin(); auxIt != auxList.end(); auxIt++ )
  191. {
  192. auxIt->other_x -= cL.x();
  193. auxIt->other_y -= cL.y();
  194. auxIt->other_z -= cL.z();
  195. auxIt->this_x -= cR.x();
  196. auxIt->this_y -= cR.y();
  197. auxIt->this_z -= cR.z();
  198. }
  199. // Compute the S matrix of products
  200. S.setSize(3,3);
  201. S.fill(0);
  202. for( auxIt = auxList.begin(); auxIt != auxList.end(); auxIt++ )
  203. {
  204. S(0,0) += auxIt->other_x * auxIt->this_x;
  205. S(0,1) += auxIt->other_x * auxIt->this_y;
  206. S(0,2) += auxIt->other_x * auxIt->this_z;
  207. S(1,0) += auxIt->other_y * auxIt->this_x;
  208. S(1,1) += auxIt->other_y * auxIt->this_y;
  209. S(1,2) += auxIt->other_y * auxIt->this_z;
  210. S(2,0) += auxIt->other_z * auxIt->this_x;
  211. S(2,1) += auxIt->other_z * auxIt->this_y;
  212. S(2,2) += auxIt->other_z * auxIt->this_z;
  213. }
  214. N.setSize(4,4);
  215. N.fill(0);
  216. N(0,0) = S(0,0) + S(1,1) + S(2,2);
  217. N(0,1) = S(1,2) - S(2,1);
  218. N(0,2) = S(2,0) - S(0,2);
  219. N(0,3) = S(0,1) - S(1,0);
  220. N(1,0) = N(0,1);
  221. N(1,1) = S(0,0) - S(1,1) - S(2,2);
  222. N(1,2) = S(0,1) + S(1,0);
  223. N(1,3) = S(2,0) + S(0,2);
  224. N(2,0) = N(0,2);
  225. N(2,1) = N(1,2);
  226. N(2,2) = -S(0,0) + S(1,1) - S(2,2);
  227. N(2,3) = S(1,2) + S(2,1);
  228. N(3,0) = N(0,3);
  229. N(3,1) = N(1,3);
  230. N(3,2) = N(2,3);
  231. N(3,3) = -S(0,0) - S(1,1) + S(2,2);
  232. // q is the quaternion correspondent to the greatest eigenvector of the N matrix (last column in Z)
  233. N.eigenVectors( Z, D );
  234. Z.extractCol( Z.getColCount()-1, v );
  235. CPose3DQuat q;
  236. for(unsigned int i = 0; i < 4; i++ ) // Set out_transformation [rotation]
  237. out_transformation[i+3] = q[i+3] = v[i];
  238. // Compute scale
  239. double num = 0.0;
  240. double den = 0.0;
  241. for( auxIt = auxList.begin(); auxIt != auxList.end(); auxIt++ )
  242. {
  243. den += square(auxIt->other_x) + square(auxIt->other_y) + square(auxIt->other_z);
  244. num += square(auxIt->this_x) + square(auxIt->this_y) + square(auxIt->this_z);
  245. }
  246. s = sqrt( num/den );
  247. // Enforce scale to be 1
  248. out_scale = s;
  249. if (forceScaleToUnity)
  250. s = 1.0;
  251. CPoint3D pp, aux;
  252. q.composePoint( cL.x(), cL.y(), cL.z(), pp.x(), pp.y(), pp.z() );
  253. pp*=s;
  254. // Set out_transformation [traslation]
  255. out_transformation[0] = cR.x() - pp.x(); // X
  256. out_transformation[1] = cR.y() - pp.y(); // Y
  257. out_transformation[2] = cR.z() - pp.z(); // Z
  258. MRPT_END
  259. return true;
  260. /*---------------------------------------------------------------
  261. leastSquareErrorRigidTransformation in 6D
  262. ---------------------------------------------------------------*/
  263. // Algorithm:
  264. // 0. Preliminary
  265. // pLi = { pLix, pLiy, pLiz }
  266. // pRi = { pRix, pRiy, pRiz }
  267. // -------------------------------------------------------
  268. // 1. Find the centroids of the two sets of measurements:
  269. // cL = (1/n)*sum{i}( pLi ) cL = { cLx, cLy, cLz }
  270. // cR = (1/n)*sum{i}( pRi ) cR = { cRx, cRy, cRz }
  271. //
  272. // 2. Substract centroids from the point coordinates:
  273. // pLi' = pLi - cL pLi' = { pLix', pLiy', pLiz' }
  274. // pRi' = pRi - cR pRi' = { pRix', pRiy', pRiz' }
  275. //
  276. // 3. For each pair of coordinates (correspondences) compute the nine possible products:
  277. // pi1 = pLix'*pRix' pi2 = pLix'*pRiy' pi3 = pLix'*pRiz'
  278. // pi4 = pLiy'*pRix' pi5 = pLiy'*pRiy' pi6 = pLiy'*pRiz'
  279. // pi7 = pLiz'*pRix' pi8 = pLiz'*pRiy' pi9 = pLiz'*pRiz'
  280. //
  281. // 4. Compute S components:
  282. // Sxx = sum{i}( pi1 ) Sxy = sum{i}( pi2 ) Sxz = sum{i}( pi3 )
  283. // Syx = sum{i}( pi4 ) Syy = sum{i}( pi5 ) Syz = sum{i}( pi6 )
  284. // Szx = sum{i}( pi7 ) Szy = sum{i}( pi8 ) Szz = sum{i}( pi9 )
  285. //
  286. // 5. Compute N components:
  287. // [ Sxx+Syy+Szz Syz-Szy Szx-Sxz Sxy-Syx ]
  288. // [ Syz-Szy Sxx-Syy-Szz Sxy+Syx Szx+Sxz ]
  289. // N = [ Szx-Sxz Sxy+Syx -Sxx+Syy-Szz Syz+Szy ]
  290. // [ Sxy-Syx Szx+Sxz Syz+Szy -Sxx-Syy+Szz ]
  291. //
  292. // 6. Rotation represented by the quaternion eigenvector correspondent to the higher eigenvalue of N
  293. //
  294. // 7. Scale computation (symmetric expression)
  295. // s = sqrt( sum{i}( square(abs(pRi')) / sum{i}( square(abs(pLi')) ) )
  296. //
  297. // 8. Translation computation (distance between the Right centroid and the scaled and rotated Left centroid)
  298. // t = cR-sR(cL)
  299. }