447 lines
14 KiB
Text
447 lines
14 KiB
Text
/*
|
|
* 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 <cfloat>
|
|
#include <fstream>
|
|
using std::flush;
|
|
#include <cstring>
|
|
|
|
#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 <Scan *> 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! "<<endl;
|
|
|
|
ccs = solveSparseCholesky(B, bd);
|
|
|
|
// delete ptPairs
|
|
for (int i = 0; i < gr.getNrLinks(); i++) {
|
|
ptpairs[i]->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;
|
|
}
|
|
|