You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

491 lines
12 KiB
C++

/*
* graphSlam6D implementation
*
* Copyright (C) Dorit Borrmann, Jan Elseberg, Kai Lingemann, Andreas Nuechter
*
* Released under the GPL version 3.
*
*/
/**
* @file
* @brief The implementation of globally consistent scan matching algorithm
* @author Dorit Borrman. Institute of Computer Science, University of Osnabrueck, Germany.
* @author Jan Elseberg. Institute of Computer Science, University of Osnabrueck, Germany.
* @author Kai Lingemann. Institute of Computer Science, University of Osnabrueck, Germany.
* @author Andreas Nuechter. Institute of Computer Science, University of Osnabrueck, Germany.
*/
#ifdef _MSC_VER
#if !defined _OPENMP && defined OPENMP
#define _OPENMP
#endif
#endif
#include "slam6d/graphSlam6D.h"
#include "sparse/csparse.h"
#include <cfloat>
#include <fstream>
using std::ofstream;
using std::flush;
#include "slam6d/globals.icc"
using namespace NEWMAT;
/**
* 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
*/
graphSlam6D::graphSlam6D(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)
{
this->nns_method = nns_method;
this->quiet = quiet;
this->epsilonLUM = epsilonLUM;
this->max_dist_match2_LUM = sqr(max_dist_match);
ctime = 0;
this->my_icp = new icp6D(my_icp6Dminimizer, mdm, max_num_iterations,
quiet, meta, rnd, eP, anim, epsilonICP, nns_method);
}
graphSlam6D::~graphSlam6D()
{
cout << "Time spent in the SLAM backend:" << ctime << endl;
}
/**
* 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 allScans Contains all laser scans
* @param nrIt The number of iterations the LUM-algorithm will run
* @param clpairs minimal number of points aximal distance for closing loops
* @param loopsize minimal loop size
*/
void graphSlam6D::matchGraph6Dautomatic(vector <Scan *> allScans, int nrIt, int clpairs, int loopsize)
{
// the IdentityMatrix to transform some Scans with
double id[16];
M4identity(id);
Graph *gr = 0;
int i = 0;
do {
cout << "Generate graph ... " << flush;
i++;
if (gr) delete gr;
gr = new Graph(0, false);
int j, maxj = (int)allScans.size();
#ifdef _OPENMP
omp_set_num_threads(OPENMP_NUM_THREADS);
#pragma omp parallel for schedule(dynamic)
#endif
for (j = 0; j < maxj; j++) {
#ifdef _OPENMP
int thread_num = omp_get_thread_num();
#else
int thread_num = 0;
#endif
for (int k = 0; k < (int)allScans.size(); k++) {
if (j == k) continue;
Scan * FirstScan = allScans[j];
Scan * SecondScan = allScans[k];
double centroid_d[3] = {0.0, 0.0, 0.0};
double centroid_m[3] = {0.0, 0.0, 0.0};
vPtPair temp;
double sum_dummy;
Scan::getPtPairs(&temp, FirstScan, SecondScan, thread_num,
my_icp->get_rnd(), (int)max_dist_match2_LUM, sum_dummy,
centroid_m, centroid_d);
if ((int)temp.size() > clpairs) {
#ifdef _OPENMP
#pragma omp critical
#endif
gr->addLink(j, k);
}
}
}
cout << "done" << endl;
} while ((doGraphSlam6D(*gr, allScans, 1) > 0.001) && (i < nrIt));
return;
}
Graph *graphSlam6D::computeGraph6Dautomatic(vector <Scan *> allScans, int clpairs)
{
// the IdentityMatrix to transform some Scans with
double id[16];
M4identity(id);
int i = 0;
cout << "Generate graph ... " << flush;
i++;
Graph *gr = new Graph(0, false);
int j, maxj = (int)allScans.size();
#ifdef _OPENMP
omp_set_num_threads(OPENMP_NUM_THREADS);
#pragma omp parallel for schedule(dynamic)
#endif
for (j = 0; j < maxj; j++) {
#ifdef _OPENMP
int thread_num = omp_get_thread_num();
#else
int thread_num = 0;
#endif
for (int k = 0; k < (int)allScans.size(); k++) {
if (j == k) continue;
Scan * FirstScan = allScans[j];
Scan * SecondScan = allScans[k];
double centroid_d[3] = {0.0, 0.0, 0.0};
double centroid_m[3] = {0.0, 0.0, 0.0};
vPtPair temp;
double sum_dummy;
Scan::getPtPairs(&temp, FirstScan, SecondScan, thread_num,
my_icp->get_rnd(), (int)max_dist_match2_LUM, sum_dummy,
centroid_m, centroid_d);
if ((int)temp.size() > clpairs) {
#ifdef _OPENMP
#pragma omp critical
#endif
gr->addLink(j, k);
}
}
}
cout << "done" << endl;
return gr;
}
/**
* This function is used to solve the system of linear eq.
*
* @param G symmetric, positive definite Matrix, thus invertable
* @param B column vector
*/
void graphSlam6D::writeMatrixPGM(const Matrix &G)
{
int n = G.Ncols();
static int matrixnum = 0;
string mf = "matrix" + to_string(matrixnum,4) + ".pgm";
ofstream matrixout(mf.c_str());
matrixout << "P2" << endl
<< "# CREATOR slam6D (c) Andreas Nuechter, 05/2007" << endl
<< n << " " << n << endl
<< 255 << endl;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (G.element(i, j) > 0.001) {
matrixout << 0 << " ";
} else {
matrixout << 255 << " ";
}
}
matrixout << endl;
}
// matrixout << G << endl;
matrixout.close();
matrixout.clear();
matrixnum++;
}
/**
* This function is used to solve the system of linear eq.
*
* @param G Matrix, invertable
* @param B column vector
*/
ColumnVector graphSlam6D::solve(const Matrix &G, const ColumnVector &B)
{
#ifdef WRITE_MATRIX_PGM
writeMatrixPGM(G);
#endif
// ----------------------------------
// solve eqn via inverting the matrix
// ----------------------------------
return (ColumnVector)(G.i() * B);
}
/**
* This function is used to solve the system of linear eq.
* The implementation from the numerical recepies are used
*
* @param G symmetric, positive definite Matrix, thus invertable
* @param B column vector
*/
ColumnVector graphSlam6D::solveCholesky(const Matrix &G, const ColumnVector &B)
{
#ifdef WRITE_MATRIX_PGM
writeMatrixPGM(G);
#endif
// We copy the newmat matrices and use our own
// Cholesky decomposition code here. The Cholesky
// decomosition is based on the Numerical Recipes in C.
// This speed ups computation time
// copy values
int n = G.Ncols();
double **A = new double*[n];
double *C = new double[n];
double *diag = new double[n];
double *x = new double[n];
ColumnVector X(n);
for (int i = 0; i < n; i++) {
A[i] = new double[n];
for (int j = 0; j < n; j++) {
A[i][j] = G.element(i, j);
}
C[i] = B.element(i);
}
// --------------------------------------------------
// make cholesky dekomposition with numerical recipes
// --------------------------------------------------
if (!choldc(n, A, diag)) {
cout << "cannot perfom cholesky decomposition" << endl;
}
// solve A x = C
cholsl(n, A, diag, C, x);
// copy values back
for (int i = 0; i < n; i++) {
X.element(i) = x[i];
}
// clean up
for (int i = 0; i < n; i++) {
delete [] A[i];
}
delete [] x;
delete [] diag;
delete [] C;
delete [] A;
return X;
}
/**
* This function is used to solve the system of linear eq.
*
* @param G symmetric, positive definite Matrix, thus invertable
* @param B column vector
*/
ColumnVector graphSlam6D::solveSparseCholesky(const Matrix &G, const ColumnVector &B)
{
long starttime = GetCurrentTimeInMilliSec();
#ifdef WRITE_MATRIX_PGM
writeMatrixPGM(G);
#endif
int n = G.Ncols();
// ------------------------------
// Sparse Cholsekey decomposition
// ------------------------------
ColumnVector X(n);
cs *A, *T = cs_spalloc (0, 0, 1, 1, 1) ;
double *x = new double[n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (fabs(G.element(i, j)) > 0.00001) {
cs_entry (T, i, j, G.element(i, j));
}
}
x[i] = B.element(i);
}
A = cs_triplet (T);
cs_dropzeros (A) ; // drop zero entries
cs_cholsol (A, x, 1) ;
// copy values back
for (int i = 0; i < n; i++) {
X.element(i) = x[i];
}
cs_spfree(A);
cs_spfree(T);
delete [] x;
ctime += GetCurrentTimeInMilliSec() - starttime;
return X;
}
ColumnVector graphSlam6D::solveSparseCholesky(GraphMatrix *G, const ColumnVector &B)
{
long starttime = GetCurrentTimeInMilliSec();
int n = B.Nrows();
ColumnVector X(n);
// ------------------------------
// Sparse Cholsekey decomposition
// ------------------------------
cs *A, *T = cs_spalloc (0, 0, 1, 1, 1) ;
double *x = new double[n];
for (int i = 0; i < n; i++) {
x[i] = B.element(i);
}
G->convertToCS(T);
A = cs_triplet (T);
cs_dropzeros (A) ; // drop zero entries
// cs_print(T, 0);
cs_cholsol (A, x, 1) ;
// copy values back
for (int i = 0; i < n; i++) {
X.element(i) = x[i];
}
cs_spfree(A);
cs_spfree(T);
delete [] x;
ctime += GetCurrentTimeInMilliSec() - starttime;
return X;
}
/**
* This function is used to solve the system of linear eq.
*
* @param G invertable Matrix
* @param B column vector
*/
ColumnVector graphSlam6D::solveSparseQR(const Matrix &G, const ColumnVector &B)
{
#ifdef WRITE_MATRIX_PGM
writeMatrixPGM(G);
#endif
int n = B.Ncols();
ColumnVector X(n);
cs *A, *T = cs_spalloc (0, 0, 1, 1, 1) ;
double *x = new double[n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (fabs(G.element(i, j)) > 0.00001) {
cs_entry (T, i, j, G.element(i, j));
}
}
x[i] = B.element(i);
}
A = cs_triplet (T);
cs_dropzeros (A) ; // drop zero entries
int order = 3; // for qr-ordering
cs_qrsol ( A, x, order) ;
// copy values back
for (int i = 0; i < n; i++) {
X.element(i) = x[i];
}
cs_spfree(A);
cs_spfree(T);
delete [] x;
return X;
}
void graphSlam6D::set_mdmll(double mdmll) {
max_dist_match2_LUM = sqr(mdmll);
}
void GraphMatrix::add(const unsigned int i, const unsigned int j, Matrix &Cij) {
uipair ui(i,j);
it = matrix.find( ui );
if (it != matrix.end()) {
(*(it->second)) += Cij;
} else {
Matrix *C = new Matrix(6,6);
*C = Cij;
matrix.insert( uimpair( ui, C));
}
}
void GraphMatrix::subtract(const unsigned int i, const unsigned int j,Matrix &Cij) {
uipair ui(i,j);
it = matrix.find( ui );
if (it != matrix.end()) {
(*it->second) -= Cij;
} else {
Matrix *C = new Matrix(6,6);
*C = Cij;
*C *= -1.0;
matrix.insert( uimpair( ui, C));
}
}
void GraphMatrix::print() {
for ( it = matrix.begin() ; it != matrix.end(); it++ ) {
uimpair uim = *it;
uipair ui = uim.first;
cout << ui.first << " " << ui.second << " :" << endl << *uim.second << endl;
}
}
GraphMatrix::~GraphMatrix() {
for ( it = matrix.begin() ; it != matrix.end(); it++ ) {
uimpair uim = *it;
delete uim.second;
}
}
void GraphMatrix::convertToCS(cs *T) {
unsigned int a,b;
int imin,imax,jmin,jmax;
for ( it = matrix.begin() ; it != matrix.end(); it++ ) {
Matrix *C = it->second;
a = it->first.first;
b = it->first.second;
// cout << a << " " << b << " " << C << endl;
imin = a*6;
jmin = b*6;
imax = a*6 + 6;
jmax = b*6 + 6;
a = b = 0;
for (int i = imin; i < imax; i++, a++) {
b = 0;
for (int j = jmin; j < jmax; j++, b++) {
if (fabs(C->element(a, b)) > 0.00001) {
cs_entry (T, i, j, C->element(a, b));
}
}
}
}
// print();
// cs_print(T, 0);
}