/* * ghelix6DQ2 implementation * * Copyright (C) Peter Schneider, Jan Elseberg, Andreas Nuechter * * Released under the GPL version 3. * */ /** * @file * @brief The implementation of globally consistent scan matching algorithm by using helix correction * @author Peter Schneider. Institute of Computer Science, University of Koblenz, Germany. * @author Andreas Nuechter. Jacobs University Bremen gGmbH, Germany * @author Jan Elseberg. Jacobs University Bremen gGmbH, Germany */ #ifdef _MSC_VER #if !defined _OPENMP && defined OPENMP #define _OPENMP #endif #endif #include "slam6d/ghelix6DQ2.h" #include "slam6d/icp6Dhelix.h" #include "sparse/csparse.h" #include #include using std::flush; #include #include "slam6d/globals.icc" using std::ofstream; /** * Constructor * * @param my_icp6Dminimizer Pointer to ICP minimization functor * @param mdm Maximum PtoP distance to which point pairs are collected for ICP * @param max_dist_match Maximum PtoP distance to which point pairs are collected for LUM * @param max_num_iterations Maximal number of iterations for ICP * @param quiet Suspesses all output to std out * @param meta Indicates if metascan matching has to be used * @param rnd Indicates if randomization has to be used * @param eP Extrapolate odometry? * @param anim Animate which frames? * @param epsilonICP Termination criterion for ICP * @param nns_method Specifies which NNS method to use * @param epsilonLUM Termination criterion for LUM */ ghelix6DQ2::ghelix6DQ2(icp6Dminimizer *my_icp6Dminimizer, double mdm, double max_dist_match, int max_num_iterations, bool quiet, bool meta, int rnd, bool eP, int anim, double epsilonICP, int nns_method, double epsilonLUM) : graphSlam6D(my_icp6Dminimizer, mdm, max_dist_match, max_num_iterations, quiet, meta, rnd, eP, anim, epsilonICP, nns_method, epsilonLUM) { } /** * Destructor */ ghelix6DQ2::~ghelix6DQ2() { delete my_icp; } /** * This function generates the matrices B and Bd that are used for solving B * c = Bd. * This function has to be called once for every linked scan-pair. * * @param firstScanNum The number of the first scan of the linked scan-pair * @param secondScanNum The number of the second scan of the linked scan-pair * @param ptpairs Vector that holds all point-pairs for the actual scan-pair * @param B Matrix with dimension (6*(number of scans-1)) x (6 * (number of scans-1)) * @param Bd Vector with dimension (6*(number of scans-1)) * @return returns the sum of square distance */ double ghelix6DQ2::genBBdForLinkedPair( int firstScanNum, int secondScanNum, vPtPair *ptpairs, Matrix *B, ColumnVector *Bd ) { double Btemp1[6][3]; double Btemp2[6][3]; memset(&Btemp1[0][0], 0, 18 * sizeof(double)); memset(&Btemp2[0][0], 0, 18 * sizeof(double)); double bd1[6]; double bd2[6]; memset(&bd1[0], 0, 6 * sizeof(double)); memset(&bd2[0], 0, 6 * sizeof(double)); double p1x, p1y, p1z, p2x, p2y, p2z, pDistX, pDistY, pDistZ, px2Sq, py2Sq, pz2Sq; int n = (*ptpairs).size(); double sum = 0; for (int i = 0; i < n; i++) { p1x = (*ptpairs)[i].p1.x; p1y = (*ptpairs)[i].p1.y; p1z = (*ptpairs)[i].p1.z; p2x = (*ptpairs)[i].p2.x; p2y = (*ptpairs)[i].p2.y; p2z = (*ptpairs)[i].p2.z; //px1Sq = p1x * p1x; //py1Sq = p1y * p1y; //pz1Sq = p1z * p1z; px2Sq = p2x * p2x; py2Sq = p2y * p2y; pz2Sq = p2z * p2z; Btemp1[4][0] += -p2z; // Btemp1[3][1] += p2z; Btemp1[5][0] += p2y; // Btemp1[3][2] += -p2y; Btemp1[4][2] += p2x; // Btemp1[5][1] += -p2x; Btemp1[0][0] += pz2Sq + py2Sq; Btemp1[1][0] += p2y*-p2x; Btemp1[2][0] += -p2z*p2x; Btemp1[1][1] += pz2Sq + px2Sq; Btemp1[2][1] += p2z*-p2y; Btemp1[2][2] += px2Sq + py2Sq; pDistX = p1x - p2x; pDistY = p1y - p2y; pDistZ = p1z - p2z; bd1[0] += -p1z*pDistY + p1y*pDistZ; bd1[1] += p1z*pDistX - p1x*pDistZ; bd1[2] += -p1y*pDistX + p1x*pDistY; bd1[3] += pDistX; bd1[4] += pDistY; bd1[5] += pDistZ; bd2[0] += -p2z*-pDistY + p2y*-pDistZ; bd2[1] += p2z*-pDistX - p2x*-pDistZ; bd2[2] += -p2y*-pDistX + p2x*-pDistY; bd2[3] += -pDistX; bd2[4] += -pDistY; bd2[5] += -pDistZ; sum += pDistX*pDistX + pDistY*pDistY + pDistZ*pDistZ; } #ifdef _OPENMP #pragma omp critical (enterB) #endif { int matPlace1 = (firstScanNum-1) * 6; if(firstScanNum != 0) { (*B)(matPlace1+4,matPlace1+4) += n; (*B)(matPlace1+5,matPlace1+5) += n; (*B)(matPlace1+6,matPlace1+6) += n; (*B)(matPlace1+1,matPlace1+5) += Btemp1[4][0]; (*B)(matPlace1+5,matPlace1+1) += Btemp1[4][0]; (*B)(matPlace1+2,matPlace1+4) += -Btemp1[4][0]; (*B)(matPlace1+4,matPlace1+2) += -Btemp1[4][0]; (*B)(matPlace1+1,matPlace1+6) += Btemp1[5][0]; (*B)(matPlace1+6,matPlace1+1) += Btemp1[5][0]; (*B)(matPlace1+3,matPlace1+4) += -Btemp1[5][0]; (*B)(matPlace1+4,matPlace1+3) += -Btemp1[5][0]; (*B)(matPlace1+3,matPlace1+5) += Btemp1[4][2]; (*B)(matPlace1+5,matPlace1+3) += Btemp1[4][2]; (*B)(matPlace1+2,matPlace1+6) += -Btemp1[4][2]; (*B)(matPlace1+6,matPlace1+2) += -Btemp1[4][2]; (*B)(matPlace1+1,matPlace1+2) += Btemp1[1][0]; (*B)(matPlace1+2,matPlace1+1) += Btemp1[1][0]; (*B)(matPlace1+1,matPlace1+3) += Btemp1[2][0]; (*B)(matPlace1+3,matPlace1+1) += Btemp1[2][0]; (*B)(matPlace1+2,matPlace1+3) += Btemp1[2][1]; (*B)(matPlace1+3,matPlace1+2) += Btemp1[2][1]; (*B)(matPlace1+1,matPlace1+1) += Btemp1[0][0]; (*B)(matPlace1+2,matPlace1+2) += Btemp1[1][1]; (*B)(matPlace1+3,matPlace1+3) += Btemp1[2][2]; (*Bd)(matPlace1+1) += bd1[0]; (*Bd)(matPlace1+2) += bd1[1]; (*Bd)(matPlace1+3) += bd1[2]; (*Bd)(matPlace1+4) += bd1[3]; (*Bd)(matPlace1+5) += bd1[4]; (*Bd)(matPlace1+6) += bd1[5]; } //------------------------------------------- unsigned int matPlace2 = (secondScanNum-1) * 6; (*B)(matPlace2+4,matPlace2+4) += n; (*B)(matPlace2+5,matPlace2+5) += n; (*B)(matPlace2+6,matPlace2+6) += n; (*B)(matPlace2+1,matPlace2+5) += Btemp1[4][0]; (*B)(matPlace2+5,matPlace2+1) += Btemp1[4][0]; (*B)(matPlace2+2,matPlace2+4) += -Btemp1[4][0]; (*B)(matPlace2+4,matPlace2+2) += -Btemp1[4][0]; (*B)(matPlace2+1,matPlace2+6) += Btemp1[5][0]; (*B)(matPlace2+6,matPlace2+1) += Btemp1[5][0]; (*B)(matPlace2+3,matPlace2+4) += -Btemp1[5][0]; (*B)(matPlace2+4,matPlace2+3) += -Btemp1[5][0]; (*B)(matPlace2+3,matPlace2+5) += Btemp1[4][2]; (*B)(matPlace2+5,matPlace2+3) += Btemp1[4][2]; (*B)(matPlace2+2,matPlace2+6) += -Btemp1[4][2]; (*B)(matPlace2+6,matPlace2+2) += -Btemp1[4][2]; (*B)(matPlace2+1,matPlace2+2) += Btemp1[1][0]; (*B)(matPlace2+2,matPlace2+1) += Btemp1[1][0]; (*B)(matPlace2+1,matPlace2+3) += Btemp1[2][0]; (*B)(matPlace2+3,matPlace2+1) += Btemp1[2][0]; (*B)(matPlace2+2,matPlace2+3) += Btemp1[2][1]; (*B)(matPlace2+3,matPlace2+2) += Btemp1[2][1]; (*B)(matPlace2+1,matPlace2+1) += Btemp1[0][0]; (*B)(matPlace2+2,matPlace2+2) += Btemp1[1][1]; (*B)(matPlace2+3,matPlace2+3) += Btemp1[2][2]; (*Bd)(matPlace2+1) += bd2[0]; (*Bd)(matPlace2+2) += bd2[1]; (*Bd)(matPlace2+3) += bd2[2]; (*Bd)(matPlace2+4) += bd2[3]; (*Bd)(matPlace2+5) += bd2[4]; (*Bd)(matPlace2+6) += bd2[5]; //------------------------------------------- if( firstScanNum != 0) { (*B)(matPlace1+4,matPlace2+4) -= n; (*B)(matPlace1+5,matPlace2+5) -= n; (*B)(matPlace1+6,matPlace2+6) -= n; (*B)(matPlace1+1,matPlace2+5) += -Btemp1[4][0]; (*B)(matPlace1+5,matPlace2+1) += -Btemp1[4][0]; (*B)(matPlace1+2,matPlace2+4) += Btemp1[4][0]; (*B)(matPlace1+4,matPlace2+2) += Btemp1[4][0]; (*B)(matPlace1+1,matPlace2+6) += -Btemp1[5][0]; (*B)(matPlace1+6,matPlace2+1) += -Btemp1[5][0]; (*B)(matPlace1+3,matPlace2+4) += Btemp1[5][0]; (*B)(matPlace1+4,matPlace2+3) += Btemp1[5][0]; (*B)(matPlace1+3,matPlace2+5) += -Btemp1[4][2]; (*B)(matPlace1+5,matPlace2+3) += -Btemp1[4][2]; (*B)(matPlace1+2,matPlace2+6) += Btemp1[4][2]; (*B)(matPlace1+6,matPlace2+2) += Btemp1[4][2]; (*B)(matPlace1+1,matPlace2+2) += -Btemp1[1][0]; (*B)(matPlace1+2,matPlace2+1) += -Btemp1[1][0]; (*B)(matPlace1+1,matPlace2+3) += -Btemp1[2][0]; (*B)(matPlace1+3,matPlace2+1) += -Btemp1[2][0]; (*B)(matPlace1+2,matPlace2+3) += -Btemp1[2][1]; (*B)(matPlace1+3,matPlace2+2) += -Btemp1[2][1]; (*B)(matPlace1+1,matPlace2+1) += -Btemp1[0][0]; (*B)(matPlace1+2,matPlace2+2) += -Btemp1[1][1]; (*B)(matPlace1+3,matPlace2+3) += -Btemp1[2][2]; //---------------------------------------- (*B)(matPlace2+4,matPlace1+4) -= n; (*B)(matPlace2+5,matPlace1+5) -= n; (*B)(matPlace2+6,matPlace1+6) -= n; (*B)(matPlace2+1,matPlace1+5) += -Btemp1[4][0]; (*B)(matPlace2+5,matPlace1+1) += -Btemp1[4][0]; (*B)(matPlace2+2,matPlace1+4) += Btemp1[4][0]; (*B)(matPlace2+4,matPlace1+2) += Btemp1[4][0]; (*B)(matPlace2+1,matPlace1+6) += -Btemp1[5][0]; (*B)(matPlace2+6,matPlace1+1) += -Btemp1[5][0]; (*B)(matPlace2+3,matPlace1+4) += Btemp1[5][0]; (*B)(matPlace2+4,matPlace1+3) += Btemp1[5][0]; (*B)(matPlace2+3,matPlace1+5) += -Btemp1[4][2]; (*B)(matPlace2+5,matPlace1+3) += -Btemp1[4][2]; (*B)(matPlace2+2,matPlace1+6) += Btemp1[4][2]; (*B)(matPlace2+6,matPlace1+2) += Btemp1[4][2]; (*B)(matPlace2+1,matPlace1+2) += -Btemp1[1][0]; (*B)(matPlace2+2,matPlace1+1) += -Btemp1[1][0]; (*B)(matPlace2+1,matPlace1+3) += -Btemp1[2][0]; (*B)(matPlace2+3,matPlace1+1) += -Btemp1[2][0]; (*B)(matPlace2+2,matPlace1+3) += -Btemp1[2][1]; (*B)(matPlace2+3,matPlace1+2) += -Btemp1[2][1]; (*B)(matPlace2+1,matPlace1+1) += -Btemp1[0][0]; (*B)(matPlace2+2,matPlace1+2) += -Btemp1[1][1]; (*B)(matPlace2+3,matPlace1+3) += -Btemp1[2][2]; } } // of pragma omp critical return sqrt( sum / (double) n ); } /** * This function is used to match a set of laser scans with any minimally * connected Graph, using the globally consistent LUM-algorithm in 3D. * * @param gr Some Graph with no real subgraphs except for itself * @param allScans Contains all laser scans * @param nrIt The number of iterations the LUM-algorithm will run * @return Euclidian distance of all pose shifts */ double ghelix6DQ2::doGraphSlam6D(Graph gr, vector allScans, int nrIt) { #ifdef WRITE_GRAPH_NET // for debug only: static int d = 0; cout << "writing graph.dat ....................................." << endl; d++; string gfilename = "graph_" + to_string(d, 3) + ".net"; ofstream out(gfilename.c_str()); for (int i=0; i < gr.getNrLinks(); i++) { int from = gr.getLink(i,0); int to = gr.getLink(i,1); // shouldn't be necessary, just in case a (out of date) graph file is loaded: if (from < (int)allScans.size() && to < (int)allScans.size()) { out << allScans[from]->get_rPos()[0] << " " << allScans[from]->get_rPos()[1] << " " << allScans[from]->get_rPos()[2] << endl << allScans[to ]->get_rPos()[0] << " " << allScans[to ]->get_rPos()[1] << " " << allScans[to ]->get_rPos()[2] << endl << endl; } } out.close(); out.clear(); #endif // the IdentityMatrix to transform some Scans with double id[16]; M4identity(id); vPtPair **ptpairs = 0; // Contains sets of point pairs for all links Matrix B ( 6 * (gr.getNrScans()-1), 6 * (gr.getNrScans()-1) ); ColumnVector ccs( 6*(gr.getNrScans()-1) ), bd( 6*(gr.getNrScans()-1) ); B = 0.0; bd = 0.0; double sum_position_diff = 0; double ret = DBL_MAX; for(int iteration = 0; iteration < nrIt && ret > epsilonLUM; iteration++) { sum_position_diff = 0; if (nrIt > 1) cout << "Iteration match " << iteration << endl; if (ptpairs != 0) delete [] ptpairs; ptpairs = new vPtPair*[gr.getNrLinks()]; for (int i = 0; i < gr.getNrLinks(); i++) { ptpairs[i] = new vPtPair; } // Get all point pairs after ICP int end_loop = gr.getNrLinks(); #ifdef _OPENMP omp_set_num_threads(OPENMP_NUM_THREADS); #pragma omp parallel for schedule(dynamic) #endif for ( int i = 0; i < end_loop; i++) { cout << "P" << i << flush; Scan * FirstScan = allScans[gr.getLink(i,0)]; Scan * SecondScan = allScans[gr.getLink(i,1)]; #ifdef _OPENMP int thread_num = omp_get_thread_num(); #else int thread_num = 0; #endif double dummy_centroid_m[3]; double dummy_centroid_d[3]; double dummy_sum; Scan::getPtPairs(ptpairs[i], FirstScan, SecondScan, thread_num, (int)my_icp->get_rnd(), (int)max_dist_match2_LUM, dummy_sum, dummy_centroid_m, dummy_centroid_d); // faulty network if (ptpairs[i]->size() <= 1) { cout << "Error: Link (" << gr.getLink(i,0) << " - " << gr.getLink(i, 1) << " ) is empty with " << ptpairs[i]->size() << " Corr. points. iteration = " << iteration << endl; // exit(1); } else // build the matrix B and vector bd genBBdForLinkedPair( gr.getLink(i,0), gr.getLink(i,1), ptpairs[i], &B, &bd ); } cout <<" building matrices done! "<clear(); delete (ptpairs[i]); } ColumnVector t0(3), t(3), tlast(3); int vectorOffset; int loop_end = gr.getNrScans(); double alignxfLum[16]; for(int i = 1; i < loop_end; i++) { vectorOffset = (i-1) * 6; icp6D_HELIX::computeRt( &ccs, vectorOffset, alignxfLum); // Update the Pose cout << "Old pose estimate, Scan " << i << endl; cout << "x: " << allScans[i]->get_rPos()[0] << " y: " << allScans[i]->get_rPos()[1] << " z: " << allScans[i]->get_rPos()[2] << " tx: " << allScans[i]->get_rPosTheta()[0] << " ty: " << allScans[i]->get_rPosTheta()[1] << " tz: " << allScans[i]->get_rPosTheta()[2] << endl; if (i < loop_end - 1) { allScans[i]->transform(alignxfLum, Scan::LUM, 1); } else { allScans[i]->transform(alignxfLum, Scan::LUM, 2); } cout << "x: " << allScans[i]->get_rPos()[0] << " y: " << allScans[i]->get_rPos()[1] << " z: " << allScans[i]->get_rPos()[2] << " tx: " << allScans[i]->get_rPosTheta()[0] << " ty: " << allScans[i]->get_rPosTheta()[1] << " tz: " << allScans[i]->get_rPosTheta()[2] << endl << endl; sum_position_diff += sqrt( sqr(alignxfLum[12]) + sqr(alignxfLum[13]) + sqr(alignxfLum[14])); } cout << "Sum of Position differences = " << sum_position_diff << endl << endl; ret = (sum_position_diff / (double)gr.getNrScans()); } delete [] ptpairs; ptpairs = 0; return ret; }