/*--------------------------------------------------------------- jacobiansPoseComposition ---------------------------------------------------------------*/ void CPosePDF::jacobiansPoseComposition( const CPose2D &x, const CPose2D &u, CMatrixDouble33 &df_dx, CMatrixDouble33 &df_du, const bool compute_df_dx, const bool compute_df_du ) { const double spx = sin(x.phi()); const double cpx = cos(x.phi()); if (compute_df_dx) { /* df_dx = [ 1, 0, -sin(phi_x)*x_u-cos(phi_x)*y_u ] [ 0, 1, cos(phi_x)*x_u-sin(phi_x)*y_u ] [ 0, 0, 1 ] */ df_dx.unit(3,1.0); const double xu = u.x(); const double yu = u.y(); df_dx.get_unsafe(0,2) = -spx*xu-cpx*yu; df_dx.get_unsafe(1,2) = cpx*xu-spx*yu; } if (compute_df_du) { /* df_du = [ cos(phi_x) , -sin(phi_x) , 0 ] [ sin(phi_x) , cos(phi_x) , 0 ] [ 0 , 0 , 1 ] */ // This is the homogeneous matrix of "x": df_du.get_unsafe(0,2) = df_du.get_unsafe(1,2) = df_du.get_unsafe(2,0) = df_du.get_unsafe(2,1) = 0; df_du.get_unsafe(2,2) = 1; df_du.get_unsafe(0,0) = cpx; df_du.get_unsafe(0,1) = -spx; df_du.get_unsafe(1,0) = spx; df_du.get_unsafe(1,1) = cpx; } }
/*--------------------------------------------------------------- HornMethod ---------------------------------------------------------------*/ double scanmatching::HornMethod( const vector_double &inVector, vector_double &outVector, // The output vector bool forceScaleToUnity ) { MRPT_START vector_double input; input.resize( inVector.size() ); input = inVector; outVector.resize( 7 ); // Compute the centroids TPoint3D cL(0,0,0), cR(0,0,0); const size_t nMatches = input.size()/6; ASSERT_EQUAL_(input.size()%6, 0) for( unsigned int i = 0; i < nMatches; i++ ) { cL.x += input[i*6+3]; cL.y += input[i*6+4]; cL.z += input[i*6+5]; cR.x += input[i*6+0]; cR.y += input[i*6+1]; cR.z += input[i*6+2]; } ASSERT_ABOVE_(nMatches,0) const double F = 1.0/nMatches; cL *= F; cR *= F; CMatrixDouble33 S; // S.zeros(); // Zeroed by default // Substract the centroid and compute the S matrix of cross products for( unsigned int i = 0; i < nMatches; i++ ) { input[i*6+3] -= cL.x; input[i*6+4] -= cL.y; input[i*6+5] -= cL.z; input[i*6+0] -= cR.x; input[i*6+1] -= cR.y; input[i*6+2] -= cR.z; S.get_unsafe(0,0) += input[i*6+3]*input[i*6+0]; S.get_unsafe(0,1) += input[i*6+3]*input[i*6+1]; S.get_unsafe(0,2) += input[i*6+3]*input[i*6+2]; S.get_unsafe(1,0) += input[i*6+4]*input[i*6+0]; S.get_unsafe(1,1) += input[i*6+4]*input[i*6+1]; S.get_unsafe(1,2) += input[i*6+4]*input[i*6+2]; S.get_unsafe(2,0) += input[i*6+5]*input[i*6+0]; S.get_unsafe(2,1) += input[i*6+5]*input[i*6+1]; S.get_unsafe(2,2) += input[i*6+5]*input[i*6+2]; } // Construct the N matrix CMatrixDouble44 N; // N.zeros(); // Zeroed by default N.set_unsafe( 0,0,S.get_unsafe(0,0) + S.get_unsafe(1,1) + S.get_unsafe(2,2) ); N.set_unsafe( 0,1,S.get_unsafe(1,2) - S.get_unsafe(2,1) ); N.set_unsafe( 0,2,S.get_unsafe(2,0) - S.get_unsafe(0,2) ); N.set_unsafe( 0,3,S.get_unsafe(0,1) - S.get_unsafe(1,0) ); N.set_unsafe( 1,0,N.get_unsafe(0,1) ); N.set_unsafe( 1,1,S.get_unsafe(0,0) - S.get_unsafe(1,1) - S.get_unsafe(2,2) ); N.set_unsafe( 1,2,S.get_unsafe(0,1) + S.get_unsafe(1,0) ); N.set_unsafe( 1,3,S.get_unsafe(2,0) + S.get_unsafe(0,2) ); N.set_unsafe( 2,0,N.get_unsafe(0,2) ); N.set_unsafe( 2,1,N.get_unsafe(1,2) ); N.set_unsafe( 2,2,-S.get_unsafe(0,0) + S.get_unsafe(1,1) - S.get_unsafe(2,2) ); N.set_unsafe( 2,3,S.get_unsafe(1,2) + S.get_unsafe(2,1) ); N.set_unsafe( 3,0,N.get_unsafe(0,3) ); N.set_unsafe( 3,1,N.get_unsafe(1,3) ); N.set_unsafe( 3,2,N.get_unsafe(2,3) ); N.set_unsafe( 3,3,-S.get_unsafe(0,0) - S.get_unsafe(1,1) + S.get_unsafe(2,2) ); // q is the quaternion correspondent to the greatest eigenvector of the N matrix (last column in Z) CMatrixDouble44 Z, D; vector_double v; N.eigenVectors( Z, D ); Z.extractCol( Z.getColCount()-1, v ); ASSERTDEB_( fabs( sqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] + v[3]*v[3] ) - 1.0 ) < 0.1 ); // Make q_r > 0 if( v[0] < 0 ){ v[0] *= -1; v[1] *= -1; v[2] *= -1; v[3] *= -1; } CPose3DQuat q; // Create a pose rotation with the quaternion for(unsigned int i = 0; i < 4; i++ ) // insert the quaternion part outVector[i+3] = q[i+3] = v[i]; // Compute scale double num = 0.0; double den = 0.0; for( unsigned int i = 0; i < nMatches; i++ ) { num += square( input[i*6+0] ) + square( input[i*6+1] ) + square( input[i*6+2] ); den += square( input[i*6+3] ) + square( input[i*6+4] ) + square( input[i*6+5] ); } // end-for // The scale: double s = std::sqrt( num/den ); // Enforce scale to be 1 if( forceScaleToUnity ) s = 1.0; TPoint3D pp; q.composePoint( cL.x, cL.y, cL.z, pp.x, pp.y, pp.z ); pp*=s; outVector[0] = cR.x - pp.x; // X outVector[1] = cR.y - pp.y; // Y outVector[2] = cR.z - pp.z; // Z return s; // return scale MRPT_END }
/*--------------------------------------------------------------- se3_l2 (old "HornMethod()") ---------------------------------------------------------------*/ bool se3_l2_internal( std::vector<mrpt::math::TPoint3D> & points_this, // IN/OUT: It gets modified! std::vector<mrpt::math::TPoint3D> & points_other, // IN/OUT: It gets modified! mrpt::poses::CPose3DQuat & out_transform, double & out_scale, bool forceScaleToUnity) { MRPT_START ASSERT_EQUAL_(points_this.size(),points_other.size()) // Compute the centroids TPoint3D ct_others(0,0,0), ct_this(0,0,0); const size_t nMatches = points_this.size(); if (nMatches<3) return false; // Nothing we can estimate without 3 points!! for(size_t i = 0; i < nMatches; i++ ) { ct_others += points_other[i]; ct_this += points_this [i]; } const double F = 1.0/nMatches; ct_others *= F; ct_this *= F; CMatrixDouble33 S; // Zeroed by default // Substract the centroid and compute the S matrix of cross products for(size_t i = 0; i < nMatches; i++ ) { points_this[i] -= ct_this; points_other[i] -= ct_others; S.get_unsafe(0,0) += points_other[i].x * points_this[i].x; S.get_unsafe(0,1) += points_other[i].x * points_this[i].y; S.get_unsafe(0,2) += points_other[i].x * points_this[i].z; S.get_unsafe(1,0) += points_other[i].y * points_this[i].x; S.get_unsafe(1,1) += points_other[i].y * points_this[i].y; S.get_unsafe(1,2) += points_other[i].y * points_this[i].z; S.get_unsafe(2,0) += points_other[i].z * points_this[i].x; S.get_unsafe(2,1) += points_other[i].z * points_this[i].y; S.get_unsafe(2,2) += points_other[i].z * points_this[i].z; } // Construct the N matrix CMatrixDouble44 N; // Zeroed by default N.set_unsafe( 0,0,S.get_unsafe(0,0) + S.get_unsafe(1,1) + S.get_unsafe(2,2) ); N.set_unsafe( 0,1,S.get_unsafe(1,2) - S.get_unsafe(2,1) ); N.set_unsafe( 0,2,S.get_unsafe(2,0) - S.get_unsafe(0,2) ); N.set_unsafe( 0,3,S.get_unsafe(0,1) - S.get_unsafe(1,0) ); N.set_unsafe( 1,0,N.get_unsafe(0,1) ); N.set_unsafe( 1,1,S.get_unsafe(0,0) - S.get_unsafe(1,1) - S.get_unsafe(2,2) ); N.set_unsafe( 1,2,S.get_unsafe(0,1) + S.get_unsafe(1,0) ); N.set_unsafe( 1,3,S.get_unsafe(2,0) + S.get_unsafe(0,2) ); N.set_unsafe( 2,0,N.get_unsafe(0,2) ); N.set_unsafe( 2,1,N.get_unsafe(1,2) ); N.set_unsafe( 2,2,-S.get_unsafe(0,0) + S.get_unsafe(1,1) - S.get_unsafe(2,2) ); N.set_unsafe( 2,3,S.get_unsafe(1,2) + S.get_unsafe(2,1) ); N.set_unsafe( 3,0,N.get_unsafe(0,3) ); N.set_unsafe( 3,1,N.get_unsafe(1,3) ); N.set_unsafe( 3,2,N.get_unsafe(2,3) ); N.set_unsafe( 3,3,-S.get_unsafe(0,0) - S.get_unsafe(1,1) + S.get_unsafe(2,2) ); // q is the quaternion correspondent to the greatest eigenvector of the N matrix (last column in Z) CMatrixDouble44 Z, D; vector<double> v; N.eigenVectors( Z, D ); Z.extractCol( Z.getColCount()-1, v ); ASSERTDEB_( fabs( sqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] + v[3]*v[3] ) - 1.0 ) < 0.1 ); // Make q_r > 0 if( v[0] < 0 ) { v[0] *= -1; v[1] *= -1; v[2] *= -1; v[3] *= -1; } // out_transform: Create a pose rotation with the quaternion for(unsigned int i = 0; i < 4; i++ ) // insert the quaternion part out_transform[i+3] = v[i]; // Compute scale double s; if( forceScaleToUnity ) { s = 1.0; // Enforce scale to be 1 } else { double num = 0.0; double den = 0.0; for(size_t i = 0; i < nMatches; i++ ) { num += square( points_other[i].x ) + square( points_other[i].y ) + square( points_other[i].z ); den += square( points_this[i].x ) + square( points_this[i].y ) + square( points_this[i].z ); } // end-for // The scale: s = std::sqrt( num/den ); } TPoint3D pp; out_transform.composePoint( ct_others.x, ct_others.y, ct_others.z, pp.x, pp.y, pp.z ); pp*=s; out_transform[0] = ct_this.x - pp.x; // X out_transform[1] = ct_this.y - pp.y; // Y out_transform[2] = ct_this.z - pp.z; // Z out_scale=s; // return scale return true; MRPT_END } // end se3_l2_internal()
/*--------------------------------------------------------------- leastSquareErrorRigidTransformation Compute the best transformation (x,y,phi) given a set of correspondences between 2D points in two different maps. This method is intensively used within ICP. ---------------------------------------------------------------*/ bool tfest::se2_l2( const TMatchingPairList& in_correspondences, TPose2D& out_transformation, CMatrixDouble33* out_estimateCovariance) { MRPT_START const size_t N = in_correspondences.size(); if (N < 2) return false; const float N_inv = 1.0f / N; // For efficiency, keep this value. // ---------------------------------------------------------------------- // Compute the estimated pose. Notation from the paper: // "Mobile robot motion estimation by 2d scan matching with genetic and // iterative // closest point algorithms", J.L. Martinez Rodriguez, A.J. Gonzalez, J. Morales // Rodriguez, A. Mandow Andaluz, A. J. Garcia Cerezo, // Journal of Field Robotics, 2006. // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // For the formulas of the covariance, see: // http://www.mrpt.org/Paper:Occupancy_Grid_Matching // and Jose Luis Blanco's PhD thesis. // ---------------------------------------------------------------------- #if MRPT_HAS_SSE2 // SSE vectorized version: //{ // TMatchingPair dumm; // MRPT_COMPILE_TIME_ASSERT(sizeof(dumm.this_x)==sizeof(float)) // MRPT_COMPILE_TIME_ASSERT(sizeof(dumm.other_x)==sizeof(float)) //} __m128 sum_a_xyz = _mm_setzero_ps(); // All 4 zeros (0.0f) __m128 sum_b_xyz = _mm_setzero_ps(); // All 4 zeros (0.0f) // [ f0 f1 f2 f3 ] // xa*xb ya*yb xa*yb xb*ya __m128 sum_ab_xyz = _mm_setzero_ps(); // All 4 zeros (0.0f) for (TMatchingPairList::const_iterator corrIt = in_correspondences.begin(); corrIt != in_correspondences.end(); corrIt++) { // Get the pair of points in the correspondence: // a_xyyx = [ xa ay | xa ya ] // b_xyyx = [ xb yb | yb xb ] // (product) // [ xa*xb ya*yb xa*yb xb*ya // LO0 LO1 HI2 HI3 // Note: _MM_SHUFFLE(hi3,hi2,lo1,lo0) const __m128 a_xyz = _mm_loadu_ps(&corrIt->this_x); // *Unaligned* load const __m128 b_xyz = _mm_loadu_ps(&corrIt->other_x); // *Unaligned* load const __m128 a_xyxy = _mm_shuffle_ps(a_xyz, a_xyz, _MM_SHUFFLE(1, 0, 1, 0)); const __m128 b_xyyx = _mm_shuffle_ps(b_xyz, b_xyz, _MM_SHUFFLE(0, 1, 1, 0)); // Compute the terms: sum_a_xyz = _mm_add_ps(sum_a_xyz, a_xyz); sum_b_xyz = _mm_add_ps(sum_b_xyz, b_xyz); // [ f0 f1 f2 f3 ] // xa*xb ya*yb xa*yb xb*ya sum_ab_xyz = _mm_add_ps(sum_ab_xyz, _mm_mul_ps(a_xyxy, b_xyyx)); } alignas(MRPT_MAX_ALIGN_BYTES) float sums_a[4], sums_b[4]; _mm_store_ps(sums_a, sum_a_xyz); _mm_store_ps(sums_b, sum_b_xyz); const float& SumXa = sums_a[0]; const float& SumYa = sums_a[1]; const float& SumXb = sums_b[0]; const float& SumYb = sums_b[1]; // Compute all four means: const __m128 Ninv_4val = _mm_set1_ps(N_inv); // load 4 copies of the same value sum_a_xyz = _mm_mul_ps(sum_a_xyz, Ninv_4val); sum_b_xyz = _mm_mul_ps(sum_b_xyz, Ninv_4val); // means_a[0]: mean_x_a // means_a[1]: mean_y_a // means_b[0]: mean_x_b // means_b[1]: mean_y_b alignas(MRPT_MAX_ALIGN_BYTES) float means_a[4], means_b[4]; _mm_store_ps(means_a, sum_a_xyz); _mm_store_ps(means_b, sum_b_xyz); const float& mean_x_a = means_a[0]; const float& mean_y_a = means_a[1]; const float& mean_x_b = means_b[0]; const float& mean_y_b = means_b[1]; // Sxx Syy Sxy Syx // xa*xb ya*yb xa*yb xb*ya alignas(MRPT_MAX_ALIGN_BYTES) float cross_sums[4]; _mm_store_ps(cross_sums, sum_ab_xyz); const float& Sxx = cross_sums[0]; const float& Syy = cross_sums[1]; const float& Sxy = cross_sums[2]; const float& Syx = cross_sums[3]; // Auxiliary variables Ax,Ay: const float Ax = N * (Sxx + Syy) - SumXa * SumXb - SumYa * SumYb; const float Ay = SumXa * SumYb + N * (Syx - Sxy) - SumXb * SumYa; #else // Non vectorized version: float SumXa = 0, SumXb = 0, SumYa = 0, SumYb = 0; float Sxx = 0, Sxy = 0, Syx = 0, Syy = 0; for (TMatchingPairList::const_iterator corrIt = in_correspondences.begin(); corrIt != in_correspondences.end(); corrIt++) { // Get the pair of points in the correspondence: const float xa = corrIt->this_x; const float ya = corrIt->this_y; const float xb = corrIt->other_x; const float yb = corrIt->other_y; // Compute the terms: SumXa += xa; SumYa += ya; SumXb += xb; SumYb += yb; Sxx += xa * xb; Sxy += xa * yb; Syx += ya * xb; Syy += ya * yb; } // End of "for all correspondences"... const float mean_x_a = SumXa * N_inv; const float mean_y_a = SumYa * N_inv; const float mean_x_b = SumXb * N_inv; const float mean_y_b = SumYb * N_inv; // Auxiliary variables Ax,Ay: const float Ax = N * (Sxx + Syy) - SumXa * SumXb - SumYa * SumYb; const float Ay = SumXa * SumYb + N * (Syx - Sxy) - SumXb * SumYa; #endif out_transformation.phi = (Ax != 0 || Ay != 0) ? atan2(static_cast<double>(Ay), static_cast<double>(Ax)) : 0.0; const double ccos = cos(out_transformation.phi); const double csin = sin(out_transformation.phi); out_transformation.x = mean_x_a - mean_x_b * ccos + mean_y_b * csin; out_transformation.y = mean_y_a - mean_x_b * csin - mean_y_b * ccos; if (out_estimateCovariance) { CMatrixDouble33* C = out_estimateCovariance; // less typing! // Compute the normalized covariance matrix: // ------------------------------------------- double var_x_a = 0, var_y_a = 0, var_x_b = 0, var_y_b = 0; const double N_1_inv = 1.0 / (N - 1); // 0) Precompute the unbiased variances estimations: // ---------------------------------------------------- for (TMatchingPairList::const_iterator corrIt = in_correspondences.begin(); corrIt != in_correspondences.end(); corrIt++) { var_x_a += square(corrIt->this_x - mean_x_a); var_y_a += square(corrIt->this_y - mean_y_a); var_x_b += square(corrIt->other_x - mean_x_b); var_y_b += square(corrIt->other_y - mean_y_b); } var_x_a *= N_1_inv; // /= (N-1) var_y_a *= N_1_inv; var_x_b *= N_1_inv; var_y_b *= N_1_inv; // 1) Compute BETA = s_Delta^2 / s_p^2 // -------------------------------- const double BETA = (var_x_a + var_y_a + var_x_b + var_y_b) * pow(static_cast<double>(N), 2.0) * static_cast<double>(N - 1); // 2) And the final covariance matrix: // (remember: this matrix has yet to be // multiplied by var_p to be the actual covariance!) // ------------------------------------------------------- const double D = square(Ax) + square(Ay); C->get_unsafe(0, 0) = 2.0 * N_inv + BETA * square((mean_x_b * Ay + mean_y_b * Ax) / D); C->get_unsafe(1, 1) = 2.0 * N_inv + BETA * square((mean_x_b * Ax - mean_y_b * Ay) / D); C->get_unsafe(2, 2) = BETA / D; C->get_unsafe(0, 1) = C->get_unsafe(1, 0) = -BETA * (mean_x_b * Ay + mean_y_b * Ax) * (mean_x_b * Ax - mean_y_b * Ay) / square(D); C->get_unsafe(0, 2) = C->get_unsafe(2, 0) = BETA * (mean_x_b * Ay + mean_y_b * Ax) / pow(D, 1.5); C->get_unsafe(1, 2) = C->get_unsafe(2, 1) = BETA * (mean_y_b * Ay - mean_x_b * Ax) / pow(D, 1.5); } return true; MRPT_END }