From 073127eca184889755d2fedc5ae77172adf4bc94 Mon Sep 17 00:00:00 2001 From: jvallve <jvallve@224674b8-e365-4e73-a4a8-558dbbfec58c> Date: Tue, 23 Jun 2015 16:25:11 +0000 Subject: [PATCH] I'm working on solver, uooo! --- src/constraint_base.h | 14 + src/constraint_corner_2D_theta.h | 14 +- src/constraint_gps_2D.h | 12 +- src/constraint_odom_2D_complex_angle.h | 12 +- src/constraint_odom_2D_theta.h | 12 +- src/constraint_sparse.h | 141 +++- src/examples/CMakeLists.txt | 12 + src/examples/test_ceres_2_lasers.cpp | 8 +- src/examples/test_iQR_wolf.cpp | 564 ++++++++++++++ src/examples/test_iQR_wolf2.cpp | 989 +++++++++++++++++++++++++ src/solver/CMakeLists.txt | 4 + src/solver/solver_QR.h | 33 + src/solver/solver_manager.cpp | 228 ++++++ src/solver/solver_manager.h | 49 ++ 14 files changed, 2023 insertions(+), 69 deletions(-) create mode 100644 src/examples/test_iQR_wolf.cpp create mode 100644 src/examples/test_iQR_wolf2.cpp create mode 100644 src/solver/CMakeLists.txt create mode 100644 src/solver/solver_QR.h create mode 100644 src/solver/solver_manager.cpp create mode 100644 src/solver/solver_manager.h diff --git a/src/constraint_base.h b/src/constraint_base.h index 4bf7666b5..fc12182e0 100644 --- a/src/constraint_base.h +++ b/src/constraint_base.h @@ -52,6 +52,13 @@ class ConstraintBase : public NodeLinked<FeatureBase, NodeTerminus> **/ virtual const std::vector<WolfScalar*> getStateBlockPtrVector() = 0; + /** \brief Returns a vector of pointers to the states + * + * Returns a vector of pointers to the state in which this constraint depends + * + **/ + virtual const std::vector<StateBase*> getStatePtrVector() const = 0; + /** \brief Returns a pointer to the feature measurement * * Returns a pointer to the feature measurement @@ -73,5 +80,12 @@ class ConstraintBase : public NodeLinked<FeatureBase, NodeTerminus> **/ CaptureBase* getCapturePtr() const; + /** \brief Returns the constraint residual size + * + * Returns the constraint residual size + * + **/ + virtual unsigned int getSize() const = 0; + }; #endif diff --git a/src/constraint_corner_2D_theta.h b/src/constraint_corner_2D_theta.h index d3a0e6bbf..47e4babe0 100644 --- a/src/constraint_corner_2D_theta.h +++ b/src/constraint_corner_2D_theta.h @@ -14,15 +14,15 @@ class ConstraintCorner2DTheta: public ConstraintSparse<3,2,1,2,1> public: static const unsigned int N_BLOCKS = 4; - ConstraintCorner2DTheta(FeatureBase* _ftr_ptr, LandmarkCorner2D* _lmk_ptr, WolfScalar* _robotPPtr, WolfScalar* _robotOPtr, WolfScalar* _landmarkPPtr, WolfScalar* _landmarkOPtr) : - ConstraintSparse<3,2,1,2,1>(_ftr_ptr,CTR_CORNER_2D_THETA, _robotPPtr, _robotOPtr, _landmarkPPtr, _landmarkOPtr), - lmk_ptr_(_lmk_ptr) - { - // - } +// ConstraintCorner2DTheta(FeatureBase* _ftr_ptr, LandmarkCorner2D* _lmk_ptr, WolfScalar* _robotPPtr, WolfScalar* _robotOPtr, WolfScalar* _landmarkPPtr, WolfScalar* _landmarkOPtr) : +// ConstraintSparse<3,2,1,2,1>(_ftr_ptr,CTR_CORNER_2D_THETA, _robotPPtr, _robotOPtr, _landmarkPPtr, _landmarkOPtr), +// lmk_ptr_(_lmk_ptr) +// { +// // +// } ConstraintCorner2DTheta(FeatureBase* _ftr_ptr, LandmarkCorner2D* _lmk_ptr, StateBase* _robotPPtr, StateBase* _robotOPtr, StateBase* _landmarkPPtr, StateBase* _landmarkOPtr) : - ConstraintSparse<3,2,1,2,1>(_ftr_ptr,CTR_CORNER_2D_THETA, _robotPPtr->getPtr(), _robotOPtr->getPtr(),_landmarkPPtr->getPtr(), _landmarkOPtr->getPtr()), + ConstraintSparse<3,2,1,2,1>(_ftr_ptr,CTR_CORNER_2D_THETA, _robotPPtr, _robotOPtr,_landmarkPPtr, _landmarkOPtr), lmk_ptr_(_lmk_ptr) { // diff --git a/src/constraint_gps_2D.h b/src/constraint_gps_2D.h index 9b94e4a09..ad57d9ce4 100644 --- a/src/constraint_gps_2D.h +++ b/src/constraint_gps_2D.h @@ -11,14 +11,14 @@ class ConstraintGPS2D: public ConstraintSparse<2,2> public: static const unsigned int N_BLOCKS = 1; - ConstraintGPS2D(FeatureBase* _ftr_ptr, WolfScalar* _statePtr) : - ConstraintSparse<2,2>(_ftr_ptr,CTR_GPS_FIX_2D, _statePtr) - { - // - } +// ConstraintGPS2D(FeatureBase* _ftr_ptr, WolfScalar* _statePtr) : +// ConstraintSparse<2,2>(_ftr_ptr,CTR_GPS_FIX_2D, _statePtr) +// { +// // +// } ConstraintGPS2D(FeatureBase* _ftr_ptr, StateBase* _statePtr): - ConstraintSparse<2,2>(_ftr_ptr,CTR_GPS_FIX_2D, _statePtr->getPtr()) + ConstraintSparse<2,2>(_ftr_ptr,CTR_GPS_FIX_2D, _statePtr) { // } diff --git a/src/constraint_odom_2D_complex_angle.h b/src/constraint_odom_2D_complex_angle.h index 4597709e0..a2ed5d503 100644 --- a/src/constraint_odom_2D_complex_angle.h +++ b/src/constraint_odom_2D_complex_angle.h @@ -10,14 +10,14 @@ class ConstraintOdom2DComplexAngle : public ConstraintSparse<3, 2, 2, 2, 2> public: static const unsigned int N_BLOCKS = 4; - ConstraintOdom2DComplexAngle(FeatureBase* _ftr_ptr, WolfScalar* _block0Ptr, WolfScalar* _block1Ptr, WolfScalar* _block2Ptr, WolfScalar* _block3Ptr) : - ConstraintSparse<3, 2, 2, 2, 2>(_ftr_ptr, CTR_ODOM_2D_COMPLEX_ANGLE, _block0Ptr, _block1Ptr, _block2Ptr, _block3Ptr) - { - // - } +// ConstraintOdom2DComplexAngle(FeatureBase* _ftr_ptr, WolfScalar* _block0Ptr, WolfScalar* _block1Ptr, WolfScalar* _block2Ptr, WolfScalar* _block3Ptr) : +// ConstraintSparse<3, 2, 2, 2, 2>(_ftr_ptr, CTR_ODOM_2D_COMPLEX_ANGLE, _block0Ptr, _block1Ptr, _block2Ptr, _block3Ptr) +// { +// // +// } ConstraintOdom2DComplexAngle(FeatureBase* _ftr_ptr, StateBase* _state0Ptr, StateBase* _state1Ptr, StateBase* _state2Ptr, StateBase* _state3Ptr) : - ConstraintSparse<3, 2, 2, 2, 2>(_ftr_ptr, CTR_ODOM_2D_COMPLEX_ANGLE, _state0Ptr->getPtr(), _state1Ptr->getPtr(), _state2Ptr->getPtr(), _state3Ptr->getPtr()) + ConstraintSparse<3, 2, 2, 2, 2>(_ftr_ptr, CTR_ODOM_2D_COMPLEX_ANGLE, _state0Ptr, _state1Ptr, _state2Ptr, _state3Ptr) { // } diff --git a/src/constraint_odom_2D_theta.h b/src/constraint_odom_2D_theta.h index 9e2024207..c4d8d91ce 100644 --- a/src/constraint_odom_2D_theta.h +++ b/src/constraint_odom_2D_theta.h @@ -10,14 +10,14 @@ class ConstraintOdom2DTheta : public ConstraintSparse<3, 2, 1, 2, 1> public: static const unsigned int N_BLOCKS = 4; - ConstraintOdom2DTheta(FeatureBase*_ftr_ptr, WolfScalar* _block0Ptr, WolfScalar* _block1Ptr, WolfScalar* _block2Ptr, WolfScalar* _block3Ptr) : - ConstraintSparse<3, 2, 1, 2, 1>(_ftr_ptr, CTR_ODOM_2D_THETA, _block0Ptr, _block1Ptr, _block2Ptr, _block3Ptr) - { - // - } +// ConstraintOdom2DTheta(FeatureBase*_ftr_ptr, WolfScalar* _block0Ptr, WolfScalar* _block1Ptr, WolfScalar* _block2Ptr, WolfScalar* _block3Ptr) : +// ConstraintSparse<3, 2, 1, 2, 1>(_ftr_ptr, CTR_ODOM_2D_THETA, _block0Ptr, _block1Ptr, _block2Ptr, _block3Ptr) +// { +// // +// } ConstraintOdom2DTheta(FeatureBase* _ftr_ptr, StateBase* _state0Ptr, StateBase* _state1Ptr, StateBase* _state2Ptr, StateBase* _state3Ptr) : - ConstraintSparse<3, 2, 1, 2, 1>(_ftr_ptr, CTR_ODOM_2D_THETA, _state0Ptr->getPtr(), _state1Ptr->getPtr(), _state2Ptr->getPtr(), _state3Ptr->getPtr()) + ConstraintSparse<3, 2, 1, 2, 1>(_ftr_ptr, CTR_ODOM_2D_THETA, _state0Ptr, _state1Ptr, _state2Ptr, _state3Ptr) { // } diff --git a/src/constraint_sparse.h b/src/constraint_sparse.h index 9ee533e3b..94bc5fe41 100644 --- a/src/constraint_sparse.h +++ b/src/constraint_sparse.h @@ -28,6 +28,7 @@ template <const unsigned int MEASUREMENT_SIZE, class ConstraintSparse: public ConstraintBase { protected: + std::vector<StateBase*> state_ptr_vector_; std::vector<WolfScalar*> state_block_ptr_vector_; std::vector<unsigned int> state_block_sizes_vector_; @@ -50,59 +51,99 @@ class ConstraintSparse: public ConstraintBase * JVN: Potser aquest constructor no l'utilitzarem mai.. no? * **/ - ConstraintSparse(FeatureBase* _ftr_ptr, ConstraintType _tp, WolfScalar** _blockPtrArray) : - ConstraintBase(_ftr_ptr,_tp), - state_block_ptr_vector_(10), - state_block_sizes_vector_({BLOCK_0_SIZE,BLOCK_1_SIZE,BLOCK_2_SIZE,BLOCK_3_SIZE,BLOCK_4_SIZE,BLOCK_5_SIZE,BLOCK_6_SIZE,BLOCK_7_SIZE,BLOCK_8_SIZE,BLOCK_9_SIZE}) - { - for (unsigned int ii = 0; ii<state_block_sizes_vector_.size(); ii++) - { - if (state_block_sizes_vector_.at(ii) != 0) - { - state_block_ptr_vector_.at(ii) = _blockPtrArray[ii]; - } - else //at the end the vector is cropped to just relevant components - { - state_block_ptr_vector_.resize(ii); - break; - } - } - } +// ConstraintSparse(FeatureBase* _ftr_ptr, ConstraintType _tp, WolfScalar** _blockPtrArray) : +// ConstraintBase(_ftr_ptr,_tp), +// state_block_ptr_vector_(10), +// state_block_sizes_vector_({BLOCK_0_SIZE,BLOCK_1_SIZE,BLOCK_2_SIZE,BLOCK_3_SIZE,BLOCK_4_SIZE,BLOCK_5_SIZE,BLOCK_6_SIZE,BLOCK_7_SIZE,BLOCK_8_SIZE,BLOCK_9_SIZE}) +// { +// for (unsigned int ii = 0; ii<state_block_sizes_vector_.size(); ii++) +// { +// if (state_block_sizes_vector_.at(ii) != 0) +// { +// state_block_ptr_vector_.at(ii) = _blockPtrArray[ii]; +// } +// else //at the end the vector is cropped to just relevant components +// { +// state_block_ptr_vector_.resize(ii); +// break; +// } +// } +// } +// +// /** \brief Contructor with state pointer separated +// * +// * Constructor with state pointers separated +// * +// **/ +// ConstraintSparse(FeatureBase* _ftr_ptr, +// ConstraintType _tp, +// WolfScalar* _state0Ptr, +// WolfScalar* _state1Ptr = nullptr, +// WolfScalar* _state2Ptr = nullptr, +// WolfScalar* _state3Ptr = nullptr, +// WolfScalar* _state4Ptr = nullptr, +// WolfScalar* _state5Ptr = nullptr, +// WolfScalar* _state6Ptr = nullptr, +// WolfScalar* _state7Ptr = nullptr, +// WolfScalar* _state8Ptr = nullptr, +// WolfScalar* _state9Ptr = nullptr ) : +// ConstraintBase(_ftr_ptr,_tp), +// state_block_ptr_vector_({_state0Ptr,_state1Ptr,_state2Ptr,_state3Ptr,_state4Ptr,_state5Ptr,_state6Ptr,_state7Ptr,_state8Ptr,_state9Ptr}), +// state_block_sizes_vector_({BLOCK_0_SIZE,BLOCK_1_SIZE,BLOCK_2_SIZE,BLOCK_3_SIZE,BLOCK_4_SIZE,BLOCK_5_SIZE,BLOCK_6_SIZE,BLOCK_7_SIZE,BLOCK_8_SIZE,BLOCK_9_SIZE}) +// { +// for (unsigned int ii = 0; ii<state_block_sizes_vector_.size(); ii++) +// { +// if ( (state_block_ptr_vector_.at(ii) == nullptr) && (state_block_sizes_vector_.at(ii) == 0) ) +// { +// state_block_sizes_vector_.resize(ii); +// state_block_ptr_vector_.resize(ii); +// break; +// } +// else // check error cases +// { +// assert(state_block_ptr_vector_.at(ii) != nullptr); +// assert(state_block_sizes_vector_.at(ii) != 0); +// } +// } +// } /** \brief Contructor with state pointer separated - * + * * Constructor with state pointers separated - * - **/ + * + **/ ConstraintSparse(FeatureBase* _ftr_ptr, - ConstraintType _tp, - WolfScalar* _state0Ptr, - WolfScalar* _state1Ptr = nullptr, - WolfScalar* _state2Ptr = nullptr, - WolfScalar* _state3Ptr = nullptr, - WolfScalar* _state4Ptr = nullptr, - WolfScalar* _state5Ptr = nullptr, - WolfScalar* _state6Ptr = nullptr, - WolfScalar* _state7Ptr = nullptr, - WolfScalar* _state8Ptr = nullptr, - WolfScalar* _state9Ptr = nullptr ) : + ConstraintType _tp, + StateBase* _state0Ptr, + StateBase* _state1Ptr = nullptr, + StateBase* _state2Ptr = nullptr, + StateBase* _state3Ptr = nullptr, + StateBase* _state4Ptr = nullptr, + StateBase* _state5Ptr = nullptr, + StateBase* _state6Ptr = nullptr, + StateBase* _state7Ptr = nullptr, + StateBase* _state8Ptr = nullptr, + StateBase* _state9Ptr = nullptr ) : ConstraintBase(_ftr_ptr,_tp), - state_block_ptr_vector_({_state0Ptr,_state1Ptr,_state2Ptr,_state3Ptr,_state4Ptr,_state5Ptr,_state6Ptr,_state7Ptr,_state8Ptr,_state9Ptr}), + state_ptr_vector_({_state0Ptr,_state1Ptr,_state2Ptr,_state3Ptr,_state4Ptr,_state5Ptr,_state6Ptr,_state7Ptr,_state8Ptr,_state9Ptr}), + state_block_ptr_vector_({_state0Ptr->getPtr(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}), state_block_sizes_vector_({BLOCK_0_SIZE,BLOCK_1_SIZE,BLOCK_2_SIZE,BLOCK_3_SIZE,BLOCK_4_SIZE,BLOCK_5_SIZE,BLOCK_6_SIZE,BLOCK_7_SIZE,BLOCK_8_SIZE,BLOCK_9_SIZE}) { - for (unsigned int ii = 0; ii<state_block_sizes_vector_.size(); ii++) + for (unsigned int ii = 1; ii<state_block_sizes_vector_.size(); ii++) { - if ( (state_block_ptr_vector_.at(ii) == nullptr) && (state_block_sizes_vector_.at(ii) == 0) ) + if (state_ptr_vector_.at(ii) != nullptr) + { + assert(state_block_sizes_vector_.at(ii) != 0 && "Too many non-null state pointers in ConstraintSparse constructor"); + state_block_ptr_vector_.at(ii) = state_ptr_vector_.at(ii)->getPtr(); + } + else { + assert(state_block_sizes_vector_.at(ii) == 0 && "No non-null state pointers enough in ConstraintSparse constructor"); + state_ptr_vector_.resize(ii); state_block_sizes_vector_.resize(ii); state_block_ptr_vector_.resize(ii); break; } - else // check error cases - { - assert(state_block_ptr_vector_.at(ii) != nullptr); - assert(state_block_sizes_vector_.at(ii) != 0); - } } } @@ -126,6 +167,26 @@ class ConstraintSparse: public ConstraintBase return state_block_ptr_vector_; } + /** \brief Returns a vector of pointers to the states + * + * Returns a vector of pointers to the state in which this constraint depends + * + **/ + virtual const std::vector<StateBase*> getStatePtrVector() const + { + return state_ptr_vector_; + } + + /** \brief Returns the constraint residual size + * + * Returns the constraint residual size + * + **/ + virtual unsigned int getSize() const + { + return MEASUREMENT_SIZE; + } + virtual void print(unsigned int _ntabs = 0, std::ostream& _ost = std::cout) const { NodeLinked::printSelf(_ntabs, _ost); diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index adb268674..cc05783d0 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -52,6 +52,10 @@ IF(Suitesparse_FOUND) ADD_EXECUTABLE(test_iQR test_iQR.cpp) TARGET_LINK_LIBRARIES(test_iQR ${PROJECT_NAME}) + # Testing QR solver test tending to wolf + ADD_EXECUTABLE(test_iQR_wolf test_iQR_wolf.cpp) + TARGET_LINK_LIBRARIES(test_iQR_wolf ${PROJECT_NAME}) + # Testing SPQR simple test ADD_EXECUTABLE(test_SPQR test_SPQR.cpp) TARGET_LINK_LIBRARIES(test_SPQR ${PROJECT_NAME}) @@ -81,6 +85,14 @@ IF(faramotics_FOUND) ${pose_state_time_LIBRARIES} ${faramotics_LIBRARIES} ${PROJECT_NAME}) + + IF(Suitesparse_FOUND) + ADD_EXECUTABLE(test_iQR_wolf2 test_iQR_wolf2.cpp) + TARGET_LINK_LIBRARIES(test_iQR_wolf2 + ${pose_state_time_LIBRARIES} + ${faramotics_LIBRARIES} + ${PROJECT_NAME}) + ENDIF(Suitesparse_FOUND) ENDIF (laser_scan_utils_FOUND) ENDIF(faramotics_FOUND) diff --git a/src/examples/test_ceres_2_lasers.cpp b/src/examples/test_ceres_2_lasers.cpp index 4acd945f3..92c06eb36 100644 --- a/src/examples/test_ceres_2_lasers.cpp +++ b/src/examples/test_ceres_2_lasers.cpp @@ -135,8 +135,8 @@ int main(int argc, char** argv) string modelFileName; //model and initial view point - //modelFileName = "/home/jvallve/iri-lab/faramotics/models/campusNordUPC.obj"; - modelFileName = "/home/acoromin/dev/br/faramotics/models/campusNordUPC.obj"; + modelFileName = "/home/jvallve/iri-lab/faramotics/models/campusNordUPC.obj"; + //modelFileName = "/home/acoromin/dev/br/faramotics/models/campusNordUPC.obj"; //modelFileName = "/home/andreu/dev/faramotics/models/campusNordUPC.obj"; devicePose.setPose(2, 8, 0.2, 0, 0, 0); viewPoint.setPose(devicePose); @@ -168,8 +168,8 @@ int main(int argc, char** argv) Eigen::Vector4s laser_1_pose, laser_2_pose; //xyz + theta laser_1_pose << 1.2, 0, 0, 0; //laser 1 laser_2_pose << -1.2, 0, 0, M_PI; //laser 2 - SensorOdom2D odom_sensor(new StatePoint3D(odom_pose.data()), new StateTheta(&odom_pose(3)), odom_std_factor, odom_std_factor); - SensorGPSFix gps_sensor(new StatePoint3D(gps_pose.data()), new StateTheta(&gps_pose(3)), gps_std); + SensorOdom2D odom_sensor(new StatePoint3D(odom_pose.data()), new StateTheta(&odom_pose(2)), odom_std_factor, odom_std_factor); + SensorGPSFix gps_sensor(new StatePoint3D(gps_pose.data()), new StateTheta(&gps_pose(2)), gps_std); SensorLaser2D laser_1_sensor(new StatePoint3D(laser_1_pose.data()), new StateTheta(&laser_1_pose(3))); SensorLaser2D laser_2_sensor(new StatePoint3D(laser_2_pose.data()), new StateTheta(&laser_2_pose(3))); diff --git a/src/examples/test_iQR_wolf.cpp b/src/examples/test_iQR_wolf.cpp new file mode 100644 index 000000000..4b06f370a --- /dev/null +++ b/src/examples/test_iQR_wolf.cpp @@ -0,0 +1,564 @@ +/* + * test_iQR_wolf.cpp + * + * Created on: Jun 17, 2015 + * Author: jvallve + */ + + +//std includes +#include <cstdlib> +#include <string> +#include <iostream> +#include <fstream> +#include <memory> +#include <random> +#include <typeinfo> +#include <ctime> +#include <queue> + +// eigen includes +#include <eigen3/Eigen/OrderingMethods> +#include <eigen3/Eigen/SparseQR> +#include <Eigen/SPQRSupport> + +// ccolamd +#include "solver/ccolamd_ordering.h" + +using namespace Eigen; + +class block_pruning +{ + public: + int col, row, Nrows, Ncols; + block_pruning(int _col, int _row, int _Nrows, int _Ncols) : + col(_col), + row(_row), + Nrows(_Nrows), + Ncols(_Ncols) + { + // + } + bool operator()(int i, int j, double) const + { + return (i < row || i > row + Nrows-1) || (j < col || j > col + Ncols-1); + } +}; + +void erase_sparse_block(SparseMatrix<double>& original, const unsigned int& row, const unsigned int& col, const unsigned int& Nrows, const unsigned int& Ncols) +{ + // prune all non-zero elements that not satisfy the 'keep' operand + // elements that are not in the block rows or are not in the block columns should be kept + //original.prune([](int i, int j, double) { return (i < row || i > row + Nrows-1) || (j < col || j > col + Ncols-1); }); + + block_pruning bp(row, col, Nrows, Ncols); + original.prune(bp); + +// for (uint i = row; i < row + Nrows; i++) +// for (uint j = col; j < row + Ncols; j++) +// original.coeffRef(i,j) = 0.0; +// +// original.prune(0); +} + +void add_sparse_block(const MatrixXd& ins, SparseMatrix<double>& original, const unsigned int& row, const unsigned int& col) +{ + for (uint r=0; r<ins.rows(); ++r) + for (uint c = 0; c < ins.cols(); c++) + if (ins(r,c) != 0) + original.coeffRef(r + row, c + col) += ins(r,c); +} + +struct node +{ + public: + int id; + int dim; + int location; + int order; + + node(const int _id, const int _dim, const int _location, const int _order) : + id(_id), + dim(_dim), + location(_location), + order(_order) + { + + } +}; + +struct measurement +{ + public: + std::vector<MatrixXd> jacobians; + std::vector<int> nodes_idx; + VectorXd error; + int dim; + bool odometry_type; + int location; + + measurement(const MatrixXd & _jacobian1, const int _idx1, const VectorXd &_error, const int _meas_dim, bool _odometry_type=false) : + jacobians({_jacobian1}), + nodes_idx({_idx1}), + error(_error), + dim(_meas_dim), + odometry_type(_odometry_type), + location(0) + { + //jacobians.push_back(_jacobian1); + } + + measurement(const MatrixXd & _jacobian1, const int _idx1, const MatrixXd & _jacobian2, const int _idx2, const VectorXd &_error, const int _meas_dim, bool _odometry_type=false) : + jacobians({_jacobian1, _jacobian2}), + nodes_idx({_idx1, _idx2}), + error(_error), + dim(_meas_dim), + odometry_type(_odometry_type), + location(0) + { + + } +}; + +class SolverQR +{ + protected: + std::string name_; + SparseQR < SparseMatrix<double>, NaturalOrdering<int>> solver_; + SparseMatrix<double> A_, R_; + VectorXd b_, x_incr_; + int n_measurements; + int n_nodes_; + std::vector<node> nodes_; + std::vector<measurement> measurements_; + + // ordering + SparseMatrix<int> A_nodes_; + PermutationMatrix<Dynamic, Dynamic, int> acc_permutation_nodes_; + + CCOLAMDOrdering<int> ordering_; + VectorXi nodes_ordering_constraints_; + int first_ordered_node_; + + // time + clock_t t_ordering_, t_solving_, t_managing_; + double time_ordering_, time_solving_, time_managing_; + + public: + SolverQR(const std::string &_name) : + name_(_name), + A_(0,0), + R_(0,0), +// b_(0), +// x_(0), + n_measurements(0), + n_nodes_(0), + A_nodes_(0,0), + acc_permutation_nodes_(0), +// nodes_(0), +// measurements_(0), + time_ordering_(0), + time_solving_(0), + time_managing_(0) + { + // + } + + virtual ~SolverQR() + { + + } + + void add_state_unit(const int node_dim, const int node_idx) + { + t_managing_ = clock(); + + n_nodes_++; + nodes_.push_back(node(node_idx, node_dim, x_incr_.size(), n_nodes_-1)); + + // Resize accumulated permutations + augment_permutation(acc_permutation_nodes_, n_nodes_); + + // Resize state + x_incr_.conservativeResize(x_incr_.size() + node_dim); + + // Resize problem + A_.conservativeResize(A_.rows(), A_.cols() + node_dim); + R_.conservativeResize(R_.cols() + node_dim, R_.cols() + node_dim); + //A_nodes_.conservativeResize(n_measurements, n_nodes); // not necessary + + time_managing_ += ((double) clock() - t_managing_) / CLOCKS_PER_SEC; + } + + void addConstraint(const measurement& _meas) + { + t_managing_ = clock(); + + assert(_meas.jacobians.size() == _meas.nodes_idx.size()); + assert(_meas.error.size() == _meas.dim); + + n_measurements++; + measurements_.push_back(_meas); + measurements_.back().location = A_.rows(); + + // Resize problem + A_.conservativeResize(A_.rows() + _meas.dim, A_.cols()); + b_.conservativeResize(b_.size() + _meas.dim); + A_nodes_.conservativeResize(n_measurements,n_nodes_); + + // ADD MEASUREMENTS + first_ordered_node_ = n_nodes_; + for (unsigned int j = 0; j < _meas.nodes_idx.size(); j++) + { + assert(acc_permutation_nodes_.indices()(_meas.nodes_idx.at(j)) == nodes_.at(_meas.nodes_idx.at(j)).order); + + int ordered_node = nodes_.at(_meas.nodes_idx.at(j)).order;//acc_permutation_nodes_.indices()(_nodes_idx.at(j)); + + add_sparse_block(_meas.jacobians.at(j), A_, A_.rows()-_meas.dim, nodes_.at(_meas.nodes_idx.at(j)).location); + + A_nodes_.coeffRef(A_nodes_.rows()-1, ordered_node) = 1; + + + assert(_meas.jacobians.at(j).cols() == nodes_.at(_meas.nodes_idx.at(j)).dim); + assert(_meas.jacobians.at(j).rows() == _meas.dim); + + // store minimum ordered node + if (first_ordered_node_ > ordered_node) + first_ordered_node_ = ordered_node; + } + + // error + b_.tail(_meas.dim) = _meas.error; + + time_managing_ += ((double) clock() - t_managing_) / CLOCKS_PER_SEC; + } + + void ordering(const int & _first_ordered_node) + { + t_ordering_ = clock(); + + // full problem ordering + if (_first_ordered_node == 0) + { + // ordering ordering constraints + nodes_ordering_constraints_.resize(n_nodes_); + nodes_ordering_constraints_ = A_nodes_.bottomRows(1).transpose(); + + // computing nodes partial ordering_ + A_nodes_.makeCompressed(); + PermutationMatrix<Dynamic, Dynamic, int> incr_permutation_nodes(n_nodes_); + ordering_(A_nodes_, incr_permutation_nodes, nodes_ordering_constraints_.data()); + + // node ordering to variable ordering + PermutationMatrix<Dynamic, Dynamic, int> incr_permutation(A_.cols()); + permutation_2_block_permutation(incr_permutation_nodes, incr_permutation); + + // apply partial_ordering orderings + A_nodes_ = (A_nodes_ * incr_permutation_nodes.transpose()).sparseView(); + A_ = (A_ * incr_permutation.transpose()).sparseView(); + + // ACCUMULATING PERMUTATIONS + accumulate_permutation(incr_permutation_nodes); + } + + // partial ordering + else + { + int ordered_nodes = n_nodes_ - _first_ordered_node; + int unordered_nodes = n_nodes_ - ordered_nodes; + if (ordered_nodes > 2) // only reordering when involved nodes in the measurement are not the two last ones + { + // SUBPROBLEM ORDERING (from first node variable to last one) + //std::cout << "ordering partial_ordering problem: " << _first_ordered_node << " to "<< n_nodes_ - 1 << std::endl; + SparseMatrix<int> sub_A_nodes_ = A_nodes_.rightCols(ordered_nodes); + + // _partial_ordering ordering_ constraints + nodes_ordering_constraints_.resize(ordered_nodes); + nodes_ordering_constraints_ = sub_A_nodes_.bottomRows(1).transpose(); + + // computing nodes partial ordering_ + sub_A_nodes_.makeCompressed(); + PermutationMatrix<Dynamic, Dynamic, int> partial_permutation_nodes(ordered_nodes); + ordering_(sub_A_nodes_, partial_permutation_nodes, nodes_ordering_constraints_.data()); + + // node ordering to variable ordering + PermutationMatrix<Dynamic, Dynamic, int> partial_permutation(A_.cols()); + permutation_2_block_permutation(partial_permutation_nodes, partial_permutation); + + // apply partial_ordering orderings + int ordered_variables = A_.cols() - nodes_.at(_first_ordered_node).location; + A_nodes_.rightCols(ordered_nodes) = (A_nodes_.rightCols(ordered_nodes) * partial_permutation_nodes.transpose()).sparseView(); + A_.rightCols(ordered_variables) = (A_.rightCols(ordered_variables) * partial_permutation.transpose()).sparseView(); + R_.rightCols(ordered_variables) = (R_.rightCols(ordered_variables) * partial_permutation.transpose()).sparseView(); + + // ACCUMULATING PERMUTATIONS + accumulate_permutation(partial_permutation_nodes); + } + } + time_ordering_ += ((double) clock() - t_ordering_) / CLOCKS_PER_SEC; + } + + bool solve(const int mode) + { + bool batch = (mode !=2 || first_ordered_node_ == 0); + bool order = (mode !=0 && n_nodes_ > 1); + + // BATCH + if (batch) + { + // REORDER + if (order) + ordering(0); + + //print_problem(); + + // SOLVE + t_solving_ = clock(); + A_.makeCompressed(); + solver_.compute(A_); + if (solver_.info() != Success) + { + std::cout << "decomposition failed" << std::endl; + return 0; + } + x_incr_ = solver_.solve(b_); + R_ = solver_.matrixR(); + //std::cout << "R" << std::endl << MatrixXd::Identity(R_.cols(), R_.cols()) * R_ << std::endl; + time_solving_ += ((double) clock() - t_solving_) / CLOCKS_PER_SEC; + } + // INCREMENTAL + else + { + // REORDER SUBPROBLEM + ordering(first_ordered_node_); + //print_problem(); + + // SOLVE ORDERED SUBPROBLEM + t_solving_= clock(); + A_nodes_.makeCompressed(); + A_.makeCompressed(); + + // finding measurements block + SparseMatrix<int> measurements_to_initial = A_nodes_.col(first_ordered_node_); + // std::cout << "measurements_to_initial " << measurements_to_initial << std::endl; + // std::cout << "measurements_to_initial.innerIndexPtr()[measurements_to_initial.outerIndexPtr()[0]] " << measurements_to_initial.innerIndexPtr()[measurements_to_initial.outerIndexPtr()[0]] << std::endl; + int first_ordered_measurement = measurements_to_initial.innerIndexPtr()[measurements_to_initial.outerIndexPtr()[0]]; + int ordered_measurements = A_.rows() - measurements_.at(first_ordered_measurement).location; + int ordered_variables = A_.cols() - nodes_.at(first_ordered_node_).location; + int unordered_variables = nodes_.at(first_ordered_node_).location; + + SparseMatrix<double> A_partial = A_.bottomRightCorner(ordered_measurements, ordered_variables); + solver_.compute(A_partial); + if (solver_.info() != Success) + { + std::cout << "decomposition failed" << std::endl; + return 0; + } + //std::cout << "R new" << std::endl << MatrixXd::Identity(A_partial.cols(), A_partial.cols()) * solver_.matrixR() << std::endl; + x_incr_.tail(ordered_variables) = solver_.solve(b_.tail(ordered_measurements)); + + // store new part of R + erase_sparse_block(R_, unordered_variables, unordered_variables, ordered_variables, ordered_variables); + //std::cout << "R" << std::endl << MatrixXd::Identity(R_.rows(), R_.rows()) * R_ << std::endl; + add_sparse_block(solver_.matrixR(), R_, unordered_variables, unordered_variables); + //std::cout << "R" << std::endl << MatrixXd::Identity(R_.rows(), R_.rows()) * R_ << std::endl; + R_.makeCompressed(); + + // solving not ordered subproblem + if (unordered_variables > 0) + { + //std::cout << "--------------------- solving unordered part" << std::endl; + SparseMatrix<double> R1 = R_.topLeftCorner(unordered_variables, unordered_variables); + //std::cout << "R1" << std::endl << MatrixXd::Identity(R1.rows(), R1.rows()) * R1 << std::endl; + SparseMatrix<double> R2 = R_.topRightCorner(unordered_variables, ordered_variables); + //std::cout << "R2" << std::endl << MatrixXd::Identity(R2.rows(), R2.rows()) * R2 << std::endl; + solver_.compute(R1); + if (solver_.info() != Success) + { + std::cout << "decomposition failed" << std::endl; + return 0; + } + x_incr_.head(unordered_variables) = solver_.solve(b_.head(unordered_variables) - R2 * x_incr_.tail(ordered_variables)); + } + + } + // UNDO ORDERING FOR RESULT + PermutationMatrix<Dynamic, Dynamic, int> acc_permutation(A_.cols()); + permutation_2_block_permutation(acc_permutation_nodes_, acc_permutation); // TODO via pointers + x_incr_ = acc_permutation.inverse() * x_incr_; + + time_solving_ += ((double) clock() - t_solving_) / CLOCKS_PER_SEC; + + return 1; + } + + + void permutation_2_block_permutation(const PermutationMatrix<Dynamic, Dynamic, int> &_perm_nodes, PermutationMatrix<Dynamic, Dynamic, int> &perm_variables) + { + ArrayXi locations = perm_nodes_2_locations(_perm_nodes); + + int last_idx = 0; + for (unsigned int i = 0; i<locations.size(); i++) + { + perm_variables.indices().segment(last_idx, nodes_.at(i).dim) = VectorXi::LinSpaced(nodes_.at(i).dim, locations(i), locations(i)+nodes_.at(i).dim-1); + last_idx += nodes_.at(i).dim; + } + } + + ArrayXi perm_nodes_2_locations(const PermutationMatrix<Dynamic, Dynamic, int> &_perm_nodes) + { + ArrayXi indices = _perm_nodes.indices().array(); + + for (unsigned int i = 0; i<indices.size(); i++) + indices = (indices > indices(i)).select(indices + nodes_.at(i).dim-1, indices); + + return indices; + } + + void augment_permutation(PermutationMatrix<Dynamic, Dynamic, int> &perm, const int new_size) + { + int old_size = perm.indices().size(); + int dim = new_size - old_size; + VectorXi new_indices(new_size); + new_indices.head(old_size)= perm.indices(); + new_indices.tail(dim) = ArrayXi::LinSpaced(dim, old_size, new_size-1); + perm.resize(new_size); + perm.indices() = new_indices; + } + + void accumulate_permutation(const PermutationMatrix<Dynamic, Dynamic, int> &perm) + { + print_name(); + //std::cout << std::endl << "old acc_permutation_nodes_ " << acc_permutation_nodes_.indices().transpose() << std::endl; + //std::cout << "incr perm " << perm.indices().transpose() << std::endl; + + // acumulate permutation + if (perm.size() == acc_permutation_nodes_.size()) //full permutation + acc_permutation_nodes_ = perm * acc_permutation_nodes_; + else //partial permutation + { + PermutationMatrix<Dynamic, Dynamic, int> incr_permutation_nodes(VectorXi::LinSpaced(n_nodes_, 0, n_nodes_ - 1)); // identity permutation + incr_permutation_nodes.indices().tail(perm.size()) = perm.indices() + VectorXi::Constant(perm.size(), n_nodes_ - perm.size()); + //std::cout << "incr perm " << incr_permutation_nodes.indices().transpose() << std::endl; + acc_permutation_nodes_ = incr_permutation_nodes * acc_permutation_nodes_; + } + //std::cout << "new acc_permutation_nodes_ " << acc_permutation_nodes_.indices().transpose() << std::endl; + + // update nodes orders and locations + ArrayXi locations = perm_nodes_2_locations(acc_permutation_nodes_); + for (int i = 0; i < nodes_.size(); i++) + { + nodes_.at(i).order = acc_permutation_nodes_.indices()(i); + nodes_.at(i).location = locations(i); + } + } + + void print_name() + { + std::cout << name_; + } + + void print_results() + { + print_name(); + std::cout << " solved in " << time_solving_*1e3 << " ms | " << R_.nonZeros() << " nonzeros in R"<< std::endl; + std::cout << "x = " << x_incr_.transpose() << std::endl; + } + + void print_problem() + { + print_name(); + std::cout << std::endl << "A_nodes_: " << std::endl << MatrixXi::Identity(A_nodes_.rows(), A_nodes_.rows()) * A_nodes_ << std::endl << std::endl; + std::cout << "A_: " << std::endl << MatrixXd::Identity(A_.rows(), A_.rows()) * A_ << std::endl << std::endl; + std::cout << "b_: " << std::endl << b_.transpose() << std::endl << std::endl; + } +}; + +//main +int main(int argc, char *argv[]) +{ + if (argc != 3 || atoi(argv[1]) < 1|| atoi(argv[2]) < 1) + { + std::cout << "Please call me with: [./test_iQR SIZE DIM], where:" << std::endl; + std::cout << " - SIZE: integer size of the problem" << std::endl; + std::cout << " - DIM: integer dimension of the nodes" << std::endl; + std::cout << "EXIT due to bad user input" << std::endl << std::endl; + return -1; + } + int size = atoi(argv[1]); + int dim = atoi(argv[2]); + + // Problems + SolverQR solver_ordered("FULL ORDERED"); + SolverQR solver_unordered("UNORDERED"); + SolverQR solver_ordered_partial("PARTIALLY ORDERED"); + + MatrixXd omega = MatrixXd::Constant(dim, dim, 0.1) + MatrixXd::Identity(dim, dim); + + // results variables + clock_t t_ordering, t_solving_ordered_full, t_solving_unordered, t_solving_ordered_partial, t4; + double time_ordering=0, time_solving_unordered=0, time_solving_ordered=0, time_solving_ordered_partial=0; + + std::cout << "STARTING INCREMENTAL QR TEST" << std::endl << std::endl; + + // GENERATING MEASUREMENTS + std::vector<measurement> measurements; + for (unsigned int i = 0; i < size; i++) + { + std::vector<int> meas(0); + if (i == 0) //prior + measurements.push_back(measurement(omega, 0, VectorXd::LinSpaced(dim, 0, dim-1), dim)); + + else //odometry + measurements.push_back(measurement(2*omega, i-1, 2*omega, i, VectorXd::LinSpaced(dim, dim * i, dim * (i+1)-1), dim, true)); + + if (i > size / 2) //loop closures + measurements.push_back(measurement(4*omega, 0, 4*omega, i, VectorXd::LinSpaced(dim, dim * i, dim * (i+1)-1), dim)); + } + + // INCREMENTAL LOOP + for (unsigned int i = 0; i < measurements.size(); i++) + { + std::cout << "========================= MEASUREMENT " << i << ":" << std::endl; + + // AUGMENT THE PROBLEM ---------------------------- + if (measurements.at(i).odometry_type || i == 0) // if odometry, augment the problem + { + solver_unordered.add_state_unit(dim, i); + solver_ordered.add_state_unit(dim, i); + solver_ordered_partial.add_state_unit(dim,i); + } + + // ADD MEASUREMENTS + solver_unordered.addConstraint(measurements.at(i)); + solver_ordered.addConstraint(measurements.at(i)); + solver_ordered_partial.addConstraint(measurements.at(i)); + + // PRINT PROBLEM + solver_unordered.print_problem(); + solver_ordered.print_problem(); + solver_ordered_partial.print_problem(); + + // SOLVING + solver_unordered.solve(0); + solver_ordered.solve(1); + solver_ordered_partial.solve(2); + + // RESULTS ------------------------------------ + std::cout << "========================= RESULTS " << i << ":" << std::endl; + solver_unordered.print_results(); + solver_ordered.print_results(); + solver_ordered_partial.print_results(); + +// if ((x_ordered_partial-x_ordered).maxCoeff() < 1e-10) +// std::cout << "Both solutions are equals (tolerance " << (x_ordered_partial-x_ordered).maxCoeff() << ")" << std::endl; +// else +// std::cout << "DIFFERENT SOLUTIONS!!!!!!!! max difference " << (x_ordered_partial-x_ordered).maxCoeff() << std::endl; + } +} + + + + + + diff --git a/src/examples/test_iQR_wolf2.cpp b/src/examples/test_iQR_wolf2.cpp new file mode 100644 index 000000000..d2d9bfdd9 --- /dev/null +++ b/src/examples/test_iQR_wolf2.cpp @@ -0,0 +1,989 @@ +/* + * test_iQR_wolf.cpp + * + * Created on: Jun 17, 2015 + * Author: jvallve + */ + +//std includes +#include <cstdlib> +#include <string> +#include <iostream> +#include <fstream> +#include <memory> +#include <random> +#include <typeinfo> +#include <ctime> +#include <queue> + +// eigen includes +#include <eigen3/Eigen/OrderingMethods> +#include <eigen3/Eigen/SparseQR> +#include <Eigen/SPQRSupport> + +//Wolf includes +#include "state_base.h" +#include "constraint_base.h" +#include "wolf_manager.h" + +// ccolamd +#include "solver/ccolamd_ordering.h" + +//C includes for sleep, time and main args +#include "unistd.h" + +//faramotics includes +#include "faramotics/dynamicSceneRender.h" +#include "faramotics/rangeScan2D.h" +#include "btr-headers/pose3d.h" + +//laser_scan_utils +#include "iri-algorithms/laser_scan_utils/corner_detector.h" +#include "iri-algorithms/laser_scan_utils/entities.h" + +using namespace Eigen; + +//function travel around +void motionCampus(unsigned int ii, Cpose3d & pose, double& displacement_, double& rotation_) +{ + if (ii <= 120) + { + displacement_ = 0.1; + rotation_ = 0; + } + else if ((ii > 120) && (ii <= 170)) + { + displacement_ = 0.2; + rotation_ = 1.8 * M_PI / 180; + } + else if ((ii > 170) && (ii <= 220)) + { + displacement_ = 0; + rotation_ = -1.8 * M_PI / 180; + } + else if ((ii > 220) && (ii <= 310)) + { + displacement_ = 0.1; + rotation_ = 0; + } + else if ((ii > 310) && (ii <= 487)) + { + displacement_ = 0.1; + rotation_ = -1. * M_PI / 180; + } + else if ((ii > 487) && (ii <= 600)) + { + displacement_ = 0.2; + rotation_ = 0; + } + else if ((ii > 600) && (ii <= 700)) + { + displacement_ = 0.1; + rotation_ = -1. * M_PI / 180; + } + else if ((ii > 700) && (ii <= 780)) + { + displacement_ = 0; + rotation_ = -1. * M_PI / 180; + } + else + { + displacement_ = 0.3; + rotation_ = 0.0 * M_PI / 180; + } + + pose.moveForward(displacement_); + pose.rt.setEuler(pose.rt.head() + rotation_, pose.rt.pitch(), pose.rt.roll()); +} + +class block_pruning +{ + public: + int col, row, Nrows, Ncols; + block_pruning(int _col, int _row, int _Nrows, int _Ncols) : + col(_col), + row(_row), + Nrows(_Nrows), + Ncols(_Ncols) + { + // + } + bool operator()(int i, int j, double) const + { + return (i < row || i > row + Nrows-1) || (j < col || j > col + Ncols-1); + } +}; + +void erase_sparse_block(SparseMatrix<double>& original, const unsigned int& row, const unsigned int& col, const unsigned int& Nrows, const unsigned int& Ncols) +{ + // prune all non-zero elements that not satisfy the 'keep' operand + // elements that are not in the block rows or are not in the block columns should be kept + //original.prune([](int i, int j, double) { return (i < row || i > row + Nrows-1) || (j < col || j > col + Ncols-1); }); + + block_pruning bp(row, col, Nrows, Ncols); + original.prune(bp); +} + +void add_sparse_block(const MatrixXd& ins, SparseMatrix<double>& original, const unsigned int& row, const unsigned int& col) +{ + for (uint r=0; r<ins.rows(); ++r) + for (uint c = 0; c < ins.cols(); c++) + if (ins(r,c) != 0) + original.coeffRef(r + row, c + col) += ins(r,c); +} + +struct node +{ + public: + int id; + int dim; + int location; + int order; + + node(const int _id, const int _dim, const int _location, const int _order) : + id(_id), + dim(_dim), + location(_location), + order(_order) + { + + } +}; + +struct measurement +{ + public: + std::vector<int> nodes_idx; + VectorXd error; + int dim; + int location; + + measurement(const int _idx1, const VectorXd &_error, const int _meas_dim) : + nodes_idx({_idx1}), + error(_error), + dim(_meas_dim), + location(0) + { + //jacobians.push_back(_jacobian1); + } + + measurement(const int _idx1, const int _idx2, const VectorXd &_error, const int _meas_dim) : + nodes_idx({_idx1, _idx2}), + error(_error), + dim(_meas_dim), + location(0) + { + + } + + measurement(const std::vector<int> & _idxs, const VectorXd &_error, const int _meas_dim) : + nodes_idx(_idxs), + error(_error), + dim(_meas_dim), + location(0) + { + + } +}; + +class SolverQR +{ + protected: + std::string name_; + SparseQR < SparseMatrix<double>, NaturalOrdering<int>> solver_; + SparseMatrix<double> A_, R_; + VectorXd b_, x_incr_; + int n_measurements; + int n_nodes_; + std::vector<node> nodes_; + std::vector<StateBase*> states_; + std::vector<measurement> measurements_; + std::vector<ConstraintBase*> constraints_; + + // ordering + SparseMatrix<int> A_nodes_; + PermutationMatrix<Dynamic, Dynamic, int> acc_permutation_nodes_; + std::map<int, int> id_2_idx; + + CCOLAMDOrdering<int> ordering_; + VectorXi nodes_ordering_constraints_; + int n_new_measurements_; + + // time + clock_t t_ordering_, t_solving_, t_managing_; + double time_ordering_, time_solving_, time_managing_; + + + + public: + SolverQR(const std::string &_name) : + name_(_name), + A_(0,0), + R_(0,0), + n_measurements(0), + n_nodes_(0), + A_nodes_(0,0), + acc_permutation_nodes_(0), + time_ordering_(0), + time_solving_(0), + time_managing_(0) + { + // + } + + virtual ~SolverQR() + { + + } + + void update(WolfProblem* _problem_ptr) + { + // IF REALLOCATION OF STATE, REMOVE EVERYTHING AND BUILD THE PROBLEM AGAIN + if (_problem_ptr->isReallocated()) + { + // TODO +// // Remove all parameter blocks (residual blocks will be also removed) +// removeAllStateUnits(); +// +// // Add all parameter blocks +// for(auto state_unit_it = _problem_ptr->getStateListPtr()->begin(); state_unit_it!=_problem_ptr->getStateListPtr()->end(); state_unit_it++) +// addStateUnit(*state_unit_it); +// +// // Add all residual blocks +// ConstraintBaseList ctr_list; +// _problem_ptr->getTrajectoryPtr()->getConstraintList(ctr_list); +// for(auto ctr_it = ctr_list.begin(); ctr_it!=ctr_list.end(); ctr_it++) +// addConstraint(*ctr_it); +// +// // set the wolf problem reallocation flag to false +// _problem_ptr->reallocationDone(); + } + else + { + // ADD/UPDATE STATE UNITS + for(auto state_unit_it = _problem_ptr->getStateListPtr()->begin(); state_unit_it!=_problem_ptr->getStateListPtr()->end(); state_unit_it++) + { + if ((*state_unit_it)->getPendingStatus() == ADD_PENDING) + addStateUnit(*state_unit_it); + + else if((*state_unit_it)->getPendingStatus() == UPDATE_PENDING) + updateStateUnitStatus(*state_unit_it); + } + //std::cout << "state units updated!" << std::endl; + + // REMOVE STATE UNITS + while (!_problem_ptr->getRemovedStateListPtr()->empty()) + { + // TODO + _problem_ptr->getRemovedStateListPtr()->pop_front(); + } + //std::cout << "state units removed!" << std::endl; + + // ADD CONSTRAINTS + ConstraintBaseList ctr_list; + _problem_ptr->getTrajectoryPtr()->getConstraintList(ctr_list); + //std::cout << "ctr_list.size() = " << ctr_list.size() << std::endl; + for(auto ctr_it = ctr_list.begin(); ctr_it!=ctr_list.end(); ctr_it++) + if ((*ctr_it)->getPendingStatus() == ADD_PENDING) + addConstraint(*ctr_it); + + //std::cout << "constraints updated!" << std::endl; + } + } + + void addStateUnit(StateBase* _state_ptr) + { + std::cout << "adding state unit " << _state_ptr->nodeId() << std::endl; + if (_state_ptr->getStateStatus() == ST_ESTIMATED) + { + states_.push_back(_state_ptr); + id_2_idx[_state_ptr->nodeId()] = states_.size()-1; + + std::cout << "idx " << id_2_idx[_state_ptr->nodeId()] << std::endl; + + t_managing_ = clock(); + unsigned int node_dim = _state_ptr->getStateSize(); + int node_idx= _state_ptr->nodeId(); + + n_nodes_++; + nodes_.push_back(node(node_idx, node_dim, x_incr_.size(), n_nodes_-1)); + + // Resize accumulated permutations + augment_permutation(acc_permutation_nodes_, n_nodes_); + + // Resize state + x_incr_.conservativeResize(x_incr_.size() + node_dim); + + // Resize problem + A_.conservativeResize(A_.rows(), A_.cols() + node_dim); + R_.conservativeResize(R_.cols() + node_dim, R_.cols() + node_dim); + //A_nodes_.conservativeResize(n_measurements, n_nodes); // not necessary + + time_managing_ += ((double) clock() - t_managing_) / CLOCKS_PER_SEC; + } + _state_ptr->setPendingStatus(NOT_PENDING); + } + + void updateStateUnitStatus(StateBase* _state_ptr) + { + //TODO + } + + void getResidualJacobians(const ConstraintBase* _constraint_ptr, VectorXs &error, std::vector<MatrixXs>& jacobians) + { + assert(error.size() == _constraint_ptr->getSize()); + + if (!jacobians.empty()) + jacobians.clear(); + + //TODO get the jacobian calling functor operator() + for (int i = 0; i < _constraint_ptr->getStatePtrVector().size(); i++) + if (_constraint_ptr->getStatePtrVector().at(i)->getStateStatus() == ST_ESTIMATED) + jacobians.push_back(MatrixXs::Constant(_constraint_ptr->getSize(), _constraint_ptr->getStatePtrVector().at(i)->getStateSize(), 0.1) + MatrixXs::Identity(_constraint_ptr->getSize(), _constraint_ptr->getStatePtrVector().at(i)->getStateSize())); + + // TODO get the error calling functor operator() + error = VectorXs::LinSpaced(_constraint_ptr->getSize(), _constraint_ptr->getSize() * _constraint_ptr->nodeId(), _constraint_ptr->getSize() * _constraint_ptr->nodeId()+_constraint_ptr->getSize()-1); + } + + void addConstraint(ConstraintBase* _constraint_ptr) + { + std::cout << "adding constraint " << _constraint_ptr->nodeId() << std::endl; + t_managing_ = clock(); + + constraints_.push_back(_constraint_ptr); + + int meas_dim = _constraint_ptr->getSize(); + + std::vector<MatrixXs> jacobians(_constraint_ptr->getStatePtrVector().size()); + VectorXs error(_constraint_ptr->getSize()); + + getResidualJacobians(_constraint_ptr, error, jacobians); + + std::vector<int> idxs; + for (int i = 0; i < _constraint_ptr->getStatePtrVector().size(); i++) + if (_constraint_ptr->getStatePtrVector().at(i)->getStateStatus() == ST_ESTIMATED) + idxs.push_back(id_2_idx[_constraint_ptr->getStatePtrVector().at(i)->nodeId()]); + + measurement _meas(idxs, error, meas_dim); + + n_measurements++; + n_new_measurements_++; + measurements_.push_back(_meas); + measurements_.back().location = A_.rows(); + + // Resize problem + A_.conservativeResize(A_.rows() + meas_dim, A_.cols()); + b_.conservativeResize(b_.size() + meas_dim); + A_nodes_.conservativeResize(n_measurements,n_nodes_); + + // ADD MEASUREMENTS + for (unsigned int j = 0; j < idxs.size(); j++) + { + assert(acc_permutation_nodes_.indices()(idxs.at(j)) == nodes_.at(idxs.at(j)).order); + + int ordered_node = nodes_.at(idxs.at(j)).order;//acc_permutation_nodes_.indices()(_nodes_idx.at(j)); + + add_sparse_block(jacobians.at(j), A_, A_.rows()-meas_dim, nodes_.at(idxs.at(j)).location); + + A_nodes_.coeffRef(A_nodes_.rows()-1, ordered_node) = 1; + + assert(jacobians.at(j).cols() == nodes_.at(idxs.at(j)).dim); + assert(jacobians.at(j).rows() == meas_dim); + } + + // error + b_.tail(meas_dim) = error; + + _constraint_ptr->setPendingStatus(NOT_PENDING); + + time_managing_ += ((double) clock() - t_managing_) / CLOCKS_PER_SEC; + } + + void ordering(const int & _first_ordered_idx) + { + std::cout << "ordering from idx " << _first_ordered_idx << std::endl; + t_ordering_ = clock(); + + // full problem ordering + if (_first_ordered_idx == -1) + { + // ordering ordering constraints + nodes_ordering_constraints_.resize(n_nodes_); + nodes_ordering_constraints_ = A_nodes_.bottomRows(1).transpose(); + + // computing nodes partial ordering_ + A_nodes_.makeCompressed(); + PermutationMatrix<Dynamic, Dynamic, int> incr_permutation_nodes(n_nodes_); + ordering_(A_nodes_, incr_permutation_nodes, nodes_ordering_constraints_.data()); + + // node ordering to variable ordering + PermutationMatrix<Dynamic, Dynamic, int> incr_permutation(A_.cols()); + permutation_2_block_permutation(incr_permutation_nodes, incr_permutation); + + // apply partial_ordering orderings + A_nodes_ = (A_nodes_ * incr_permutation_nodes.transpose()).sparseView(); + A_ = (A_ * incr_permutation.transpose()).sparseView(); + + // ACCUMULATING PERMUTATIONS + accumulate_permutation(incr_permutation_nodes); + } + + // partial ordering + else + { + int first_ordered_node = nodes_.at(_first_ordered_idx).order; + int ordered_nodes = n_nodes_ - first_ordered_node; + int unordered_nodes = n_nodes_ - ordered_nodes; + if (ordered_nodes > 2) // only reordering when involved nodes in the measurement are not the two last ones + { + // SUBPROBLEM ORDERING (from first node variable to last one) + //std::cout << "ordering partial_ordering problem: " << _first_ordered_node << " to "<< n_nodes_ - 1 << std::endl; + SparseMatrix<int> sub_A_nodes_ = A_nodes_.rightCols(ordered_nodes); + + // _partial_ordering ordering_ constraints + nodes_ordering_constraints_.resize(ordered_nodes); + nodes_ordering_constraints_ = sub_A_nodes_.bottomRows(1).transpose(); + + // computing nodes partial ordering_ + sub_A_nodes_.makeCompressed(); + PermutationMatrix<Dynamic, Dynamic, int> partial_permutation_nodes(ordered_nodes); + ordering_(sub_A_nodes_, partial_permutation_nodes, nodes_ordering_constraints_.data()); + + // node ordering to variable ordering + int ordered_variables = A_.cols() - nodes_.at(_first_ordered_idx).location; +// std::cout << "first_ordered_node " << first_ordered_node << std::endl; +// std::cout << "A_.cols() " << A_.cols() << std::endl; +// std::cout << "nodes_.at(_first_ordered_idx).location " << nodes_.at(_first_ordered_idx).location << std::endl; +// std::cout << "ordered_variables " << ordered_variables << std::endl; + PermutationMatrix<Dynamic, Dynamic, int> partial_permutation(ordered_variables); + permutation_2_block_permutation(partial_permutation_nodes, partial_permutation); + + // apply partial_ordering orderings + A_nodes_.rightCols(ordered_nodes) = (A_nodes_.rightCols(ordered_nodes) * partial_permutation_nodes.transpose()).sparseView(); + A_.rightCols(ordered_variables) = (A_.rightCols(ordered_variables) * partial_permutation.transpose()).sparseView(); + R_.rightCols(ordered_variables) = (R_.rightCols(ordered_variables) * partial_permutation.transpose()).sparseView(); + + // ACCUMULATING PERMUTATIONS + accumulate_permutation(partial_permutation_nodes); + } + } + time_ordering_ += ((double) clock() - t_ordering_) / CLOCKS_PER_SEC; + } + + bool solve(const int mode) + { + if (n_new_measurements_ == 0) + return 1; + + std::cout << "solving mode " << mode << std::endl; + + int first_ordered_node = n_nodes_; + int first_ordered_idx; + for (int i = 0; i < n_new_measurements_; i++) + { + ConstraintBase* ct_ptr = constraints_.at(constraints_.size()-1-i); + for (int j = 0; j < ct_ptr->getStatePtrVector().size(); j++) + { + int idx = id_2_idx[ct_ptr->getStatePtrVector().at(j)->nodeId()]; + if (first_ordered_node > nodes_.at(idx).order) + { + first_ordered_idx = idx; + first_ordered_node = nodes_.at(idx).order; + } + } + } +// std::cout << "first_ordered_node " << first_ordered_node << std::endl; +// std::cout << "first_ordered_idx " << first_ordered_idx << std::endl; + + bool batch = (mode !=2 || first_ordered_node == 0); + bool order = (mode !=0 && n_nodes_ > 1); + + // BATCH + if (batch) + { + // REORDER + if (order) + ordering(-1); + + //print_problem(); + + // SOLVE + t_solving_ = clock(); + A_.makeCompressed(); + solver_.compute(A_); + if (solver_.info() != Success) + { + std::cout << "decomposition failed" << std::endl; + return 0; + } + x_incr_ = solver_.solve(b_); + R_ = solver_.matrixR(); + //std::cout << "R" << std::endl << MatrixXd::Identity(R_.cols(), R_.cols()) * R_ << std::endl; + time_solving_ += ((double) clock() - t_solving_) / CLOCKS_PER_SEC; + } + // INCREMENTAL + else + { + // REORDER SUBPROBLEM + ordering(first_ordered_idx); + //print_problem(); + + // SOLVE ORDERED SUBPROBLEM + t_solving_= clock(); + A_nodes_.makeCompressed(); + A_.makeCompressed(); + + // finding measurements block + SparseMatrix<int> measurements_to_initial = A_nodes_.col(first_ordered_node); + // std::cout << "measurements_to_initial " << measurements_to_initial << std::endl; + // std::cout << "measurements_to_initial.innerIndexPtr()[measurements_to_initial.outerIndexPtr()[0]] " << measurements_to_initial.innerIndexPtr()[measurements_to_initial.outerIndexPtr()[0]] << std::endl; + int first_ordered_measurement = measurements_to_initial.innerIndexPtr()[measurements_to_initial.outerIndexPtr()[0]]; + int ordered_measurements = A_.rows() - measurements_.at(first_ordered_measurement).location; + int ordered_variables = A_.cols() - nodes_.at(first_ordered_idx).location; + int unordered_variables = nodes_.at(first_ordered_idx).location; + + SparseMatrix<double> A_partial = A_.bottomRightCorner(ordered_measurements, ordered_variables); + solver_.compute(A_partial); + if (solver_.info() != Success) + { + std::cout << "decomposition failed" << std::endl; + return 0; + } + //std::cout << "R new" << std::endl << MatrixXd::Identity(A_partial.cols(), A_partial.cols()) * solver_.matrixR() << std::endl; + x_incr_.tail(ordered_variables) = solver_.solve(b_.tail(ordered_measurements)); + + // store new part of R + erase_sparse_block(R_, unordered_variables, unordered_variables, ordered_variables, ordered_variables); + //std::cout << "R" << std::endl << MatrixXd::Identity(R_.rows(), R_.rows()) * R_ << std::endl; + add_sparse_block(solver_.matrixR(), R_, unordered_variables, unordered_variables); + //std::cout << "R" << std::endl << MatrixXd::Identity(R_.rows(), R_.rows()) * R_ << std::endl; + R_.makeCompressed(); + + // solving not ordered subproblem + if (unordered_variables > 0) + { + //std::cout << "--------------------- solving unordered part" << std::endl; + SparseMatrix<double> R1 = R_.topLeftCorner(unordered_variables, unordered_variables); + //std::cout << "R1" << std::endl << MatrixXd::Identity(R1.rows(), R1.rows()) * R1 << std::endl; + SparseMatrix<double> R2 = R_.topRightCorner(unordered_variables, ordered_variables); + //std::cout << "R2" << std::endl << MatrixXd::Identity(R2.rows(), R2.rows()) * R2 << std::endl; + solver_.compute(R1); + if (solver_.info() != Success) + { + std::cout << "decomposition failed" << std::endl; + return 0; + } + x_incr_.head(unordered_variables) = solver_.solve(b_.head(unordered_variables) - R2 * x_incr_.tail(ordered_variables)); + } + + } + // UNDO ORDERING FOR RESULT + PermutationMatrix<Dynamic, Dynamic, int> acc_permutation(A_.cols()); + permutation_2_block_permutation(acc_permutation_nodes_, acc_permutation); // TODO via pointers + x_incr_ = acc_permutation.inverse() * x_incr_; + + time_solving_ += ((double) clock() - t_solving_) / CLOCKS_PER_SEC; + n_new_measurements_ = 0; + return 1; + } + + + void permutation_2_block_permutation(const PermutationMatrix<Dynamic, Dynamic, int> &_perm_nodes, PermutationMatrix<Dynamic, Dynamic, int> &perm_variables) + { + //std::cout << "perm_nodes: " << _perm_nodes.indices().transpose() << std::endl; + ArrayXi locations = perm_nodes_2_locations(_perm_nodes); + //std::cout << "locations: " << locations.transpose() << std::endl; + //std::cout << "perm_variables: " << perm_variables.indices().transpose() << std::endl; + + int last_idx = 0; + for (unsigned int i = 0; i<locations.size(); i++) + { + perm_variables.indices().segment(last_idx, nodes_.at(i).dim) = VectorXi::LinSpaced(nodes_.at(i).dim, locations(i), locations(i)+nodes_.at(i).dim-1); + last_idx += nodes_.at(i).dim; + //std::cout << i << " perm_variables: " << perm_variables.indices().transpose() << std::endl; + } + //std::cout << "perm_variables: " << perm_variables.indices().transpose() << std::endl; + } + + ArrayXi perm_nodes_2_locations(const PermutationMatrix<Dynamic, Dynamic, int> &_perm_nodes) + { + ArrayXi indices = _perm_nodes.indices().array(); + + for (unsigned int i = 0; i<indices.size(); i++) + indices = (indices > indices(i)).select(indices + nodes_.at(i).dim-1, indices); + + return indices; + } + + void augment_permutation(PermutationMatrix<Dynamic, Dynamic, int> &perm, const int new_size) + { + int old_size = perm.indices().size(); + int dim = new_size - old_size; + VectorXi new_indices(new_size); + new_indices.head(old_size)= perm.indices(); + new_indices.tail(dim) = ArrayXi::LinSpaced(dim, old_size, new_size-1); + perm.resize(new_size); + perm.indices() = new_indices; + } + + void accumulate_permutation(const PermutationMatrix<Dynamic, Dynamic, int> &perm) + { + print_name(); + //std::cout << std::endl << "old acc_permutation_nodes_ " << acc_permutation_nodes_.indices().transpose() << std::endl; + //std::cout << "incr perm " << perm.indices().transpose() << std::endl; + + // acumulate permutation + if (perm.size() == acc_permutation_nodes_.size()) //full permutation + acc_permutation_nodes_ = perm * acc_permutation_nodes_; + else //partial permutation + { + PermutationMatrix<Dynamic, Dynamic, int> incr_permutation_nodes(VectorXi::LinSpaced(n_nodes_, 0, n_nodes_ - 1)); // identity permutation + incr_permutation_nodes.indices().tail(perm.size()) = perm.indices() + VectorXi::Constant(perm.size(), n_nodes_ - perm.size()); + //std::cout << "incr perm " << incr_permutation_nodes.indices().transpose() << std::endl; + acc_permutation_nodes_ = incr_permutation_nodes * acc_permutation_nodes_; + } + //std::cout << "new acc_permutation_nodes_ " << acc_permutation_nodes_.indices().transpose() << std::endl; + + // update nodes orders and locations + ArrayXi locations = perm_nodes_2_locations(acc_permutation_nodes_); + for (int i = 0; i < nodes_.size(); i++) + { + nodes_.at(i).order = acc_permutation_nodes_.indices()(i); + nodes_.at(i).location = locations(i); + } + } + + void print_name() + { + std::cout << name_; + } + + void print_results() + { + print_name(); + std::cout << " solved in " << time_solving_*1e3 << " ms | " << R_.nonZeros() << " nonzeros in R"<< std::endl; + std::cout << "x = " << x_incr_.transpose() << std::endl; + } + + void print_problem() + { + print_name(); + std::cout << std::endl << "A_nodes_: " << std::endl << MatrixXi::Identity(A_nodes_.rows(), A_nodes_.rows()) * A_nodes_ << std::endl << std::endl; + //std::cout << "A_: " << std::endl << MatrixXd::Identity(A_.rows(), A_.rows()) * A_ << std::endl << std::endl; + std::cout << "b_: " << std::endl << b_.transpose() << std::endl << std::endl; + } +}; + +//main +int main(int argc, char *argv[]) +{ + // USER INPUT ============================================================================================ + if (argc != 2 || atoi(argv[1]) < 1 || atoi(argv[1]) > 1100) + { + std::cout << "Please call me with: [./test_ceres_manager NI], where:" << std::endl; + std::cout << " - NI is the number of iterations (0 < NI < 1100)" << std::endl; + std::cout << "EXIT due to bad user input" << std::endl << std::endl; + return -1; + } + bool complex_angle = false; + unsigned int n_execution = (unsigned int) atoi(argv[1]); //number of iterations of the whole execution + + // INITIALIZATION ============================================================================================ + // Problems + SolverQR solver_ordered("FULL ORDERED"); + SolverQR solver_unordered("UNORDERED"); + SolverQR solver_ordered_partial("PARTIALLY ORDERED"); + + //init random generators + WolfScalar odom_std_factor = 0.1; + WolfScalar gps_std = 1; + std::default_random_engine generator(1); + std::normal_distribution<WolfScalar> distribution_odom(0.0, odom_std_factor); //odometry noise + std::normal_distribution<WolfScalar> distribution_gps(0.0, gps_std); //GPS noise + + std::ofstream log_file, landmark_file; //output file + + // Faramotics stuff + Cpose3d viewPoint, devicePose, laser1Pose, laser2Pose, estimated_vehicle_pose, estimated_laser_1_pose, estimated_laser_2_pose; + vector < Cpose3d > devicePoses; + vector<float> scan1, scan2; + string modelFileName; + + //model and initial view point + modelFileName = "/home/jvallve/iri-lab/faramotics/models/campusNordUPC.obj"; + //modelFileName = "/home/acoromin/dev/br/faramotics/models/campusNordUPC.obj"; + //modelFileName = "/home/andreu/dev/faramotics/models/campusNordUPC.obj"; + devicePose.setPose(2, 8, 0.2, 0, 0, 0); + viewPoint.setPose(devicePose); + viewPoint.moveForward(10); + viewPoint.rt.setEuler(viewPoint.rt.head() + M_PI / 2, viewPoint.rt.pitch() + 30. * M_PI / 180., viewPoint.rt.roll()); + viewPoint.moveForward(-15); + //glut initialization + faramotics::initGLUT(argc, argv); + + //create a viewer for the 3D model and scan points + CdynamicSceneRender* myRender = new CdynamicSceneRender(1200, 700, 90 * M_PI / 180, 90 * 700.0 * M_PI / (1200.0 * 180.0), 0.2, 100); + myRender->loadAssimpModel(modelFileName, true); //with wireframe + //create scanner and load 3D model + CrangeScan2D* myScanner = new CrangeScan2D(HOKUYO_UTM30LX_180DEG); //HOKUYO_UTM30LX_180DEG or LEUZE_RS4 + myScanner->loadAssimpModel(modelFileName); + + //variables + Eigen::Vector3s odom_reading; + Eigen::Vector2s gps_fix_reading; + Eigen::VectorXs pose_odom(3); //current odometry integred pose + Eigen::VectorXs ground_truth(n_execution * 3); //all true poses + Eigen::VectorXs odom_trajectory(n_execution * 3); //open loop trajectory + Eigen::VectorXs mean_times = Eigen::VectorXs::Zero(7); + clock_t t1, t2; + + // Wolf manager initialization + Eigen::Vector3s odom_pose = Eigen::Vector3s::Zero(); + Eigen::Vector3s gps_pose = Eigen::Vector3s::Zero(); + Eigen::Vector4s laser_1_pose, laser_2_pose; //xyz + theta + laser_1_pose << 1.2, 0, 0, 0; //laser 1 + laser_2_pose << -1.2, 0, 0, M_PI; //laser 2 + SensorOdom2D odom_sensor(new StatePoint3D(odom_pose.data()), new StateTheta(&odom_pose(2)), odom_std_factor, odom_std_factor); + SensorGPSFix gps_sensor(new StatePoint3D(gps_pose.data()), new StateTheta(&gps_pose(2)), gps_std); + SensorLaser2D laser_1_sensor(new StatePoint3D(laser_1_pose.data()), new StateTheta(&laser_1_pose(3))); + SensorLaser2D laser_2_sensor(new StatePoint3D(laser_2_pose.data()), new StateTheta(&laser_2_pose(3))); + + // Initial pose + pose_odom << 2, 8, 0; + ground_truth.head(3) = pose_odom; + odom_trajectory.head(3) = pose_odom; + + WolfManager* wolf_manager = new WolfManager(1e3, &odom_sensor, pose_odom, Eigen::Matrix3s::Identity() * 0.01, n_execution*10); + + std::cout << "STARTING INCREMENTAL QR TEST" << std::endl << std::endl; + std::cout << "\n ========= 2D Robot with odometry and 2 LIDARs ===========\n"; + // START TRAJECTORY ============================================================================================ + for (unsigned int step = 1; step < n_execution; step++) + { + //get init time + t2 = clock(); + + // ROBOT MOVEMENT --------------------------- + //std::cout << "ROBOT MOVEMENT..." << std::endl; + // moves the device position + t1 = clock(); + motionCampus(step, devicePose, odom_reading(0), odom_reading(2)); + odom_reading(1) = 0; + devicePoses.push_back(devicePose); + + // SENSOR DATA --------------------------- + //std::cout << "SENSOR DATA..." << std::endl; + // store groundtruth + ground_truth.segment(step * 3, 3) << devicePose.pt(0), devicePose.pt(1), devicePose.rt.head(); + + // compute odometry + odom_reading(0) += distribution_odom(generator) * (odom_reading(0) == 0 ? 1e-6 : odom_reading(0)); + odom_reading(1) += distribution_odom(generator) * 1e-6; + odom_reading(2) += distribution_odom(generator) * (odom_reading(2) == 0 ? 1e-6 : odom_reading(2)); + + // odometry integration + pose_odom(0) = pose_odom(0) + odom_reading(0) * cos(pose_odom(2)) - odom_reading(1) * sin(pose_odom(2)); + pose_odom(1) = pose_odom(1) + odom_reading(0) * sin(pose_odom(2)) + odom_reading(1) * cos(pose_odom(2)); + pose_odom(2) = pose_odom(2) + odom_reading(1); + odom_trajectory.segment(step * 3, 3) = pose_odom; + + // compute GPS + gps_fix_reading << devicePose.pt(0), devicePose.pt(1); + gps_fix_reading(0) += distribution_gps(generator); + gps_fix_reading(1) += distribution_gps(generator); + + //compute scans + scan1.clear(); + scan2.clear(); + // scan 1 + laser1Pose.setPose(devicePose); + laser1Pose.moveForward(laser_1_pose(0)); + myScanner->computeScan(laser1Pose, scan1); + // scan 2 + laser2Pose.setPose(devicePose); + laser2Pose.moveForward(laser_2_pose(0)); + laser2Pose.rt.setEuler(laser2Pose.rt.head() + M_PI, laser2Pose.rt.pitch(), laser2Pose.rt.roll()); + myScanner->computeScan(laser2Pose, scan2); + + mean_times(0) += ((double) clock() - t1) / CLOCKS_PER_SEC; + + // ADD CAPTURES --------------------------- + //std::cout << "ADD CAPTURES..." << std::endl; + // adding new sensor captures + wolf_manager->addCapture(new CaptureOdom2D(TimeStamp(), &odom_sensor, odom_reading)); //, odom_std_factor * Eigen::MatrixXs::Identity(2,2))); +// wolf_manager->addCapture(new CaptureGPSFix(TimeStamp(), &gps_sensor, gps_fix_reading, gps_std * Eigen::MatrixXs::Identity(3,3))); + //wolf_manager->addCapture(new CaptureLaser2D(TimeStamp(), &laser_1_sensor, scan1)); + //wolf_manager->addCapture(new CaptureLaser2D(TimeStamp(), &laser_2_sensor, scan2)); + // updating problem + wolf_manager->update(); + + // UPDATING SOLVER --------------------------- + //std::cout << "UPDATING..." << std::endl; + // update state units and constraints in ceres + solver_unordered.update(wolf_manager->getProblemPtr()); + + // PRINT PROBLEM + solver_unordered.print_problem(); + + // SOLVE OPTIMIZATION --------------------------- + //std::cout << "SOLVING..." << std::endl; + solver_unordered.solve(2); + + std::cout << "========================= RESULTS " << step << ":" << std::endl; + solver_unordered.print_results(); + + // COMPUTE COVARIANCES --------------------------- + //std::cout << "COMPUTING COVARIANCES..." << std::endl; + // TODO + + // DRAWING STUFF --------------------------- + // draw detected corners + std::list < laserscanutils::Corner > corner_list; + std::vector<double> corner_vector; + CaptureLaser2D last_scan(TimeStamp(), &laser_1_sensor, scan1); + last_scan.extractCorners(corner_list); + for (std::list<laserscanutils::Corner>::iterator corner_it = corner_list.begin(); corner_it != corner_list.end(); corner_it++) + { + corner_vector.push_back(corner_it->pt_(0)); + corner_vector.push_back(corner_it->pt_(1)); + } + myRender->drawCorners(laser1Pose, corner_vector); + + // draw landmarks + std::vector<double> landmark_vector; + for (auto landmark_it = wolf_manager->getProblemPtr()->getMapPtr()->getLandmarkListPtr()->begin(); landmark_it != wolf_manager->getProblemPtr()->getMapPtr()->getLandmarkListPtr()->end(); landmark_it++) + { + WolfScalar* position_ptr = (*landmark_it)->getPPtr()->getPtr(); + landmark_vector.push_back(*position_ptr); //x + landmark_vector.push_back(*(position_ptr + 1)); //y + landmark_vector.push_back(0.2); //z + } + myRender->drawLandmarks(landmark_vector); + + // draw localization and sensors + estimated_vehicle_pose.setPose(wolf_manager->getVehiclePose()(0), wolf_manager->getVehiclePose()(1), 0.2, wolf_manager->getVehiclePose()(2), 0, 0); + estimated_laser_1_pose.setPose(estimated_vehicle_pose); + estimated_laser_1_pose.moveForward(laser_1_pose(0)); + estimated_laser_2_pose.setPose(estimated_vehicle_pose); + estimated_laser_2_pose.moveForward(laser_2_pose(0)); + estimated_laser_2_pose.rt.setEuler(estimated_laser_2_pose.rt.head() + M_PI, estimated_laser_2_pose.rt.pitch(), estimated_laser_2_pose.rt.roll()); + myRender->drawPoseAxisVector( { estimated_vehicle_pose, estimated_laser_1_pose, estimated_laser_2_pose }); + + //Set view point and render the scene + //locate visualization view point, somewhere behind the device + myRender->setViewPoint(viewPoint); + myRender->drawPoseAxis(devicePose); + myRender->drawScan(laser1Pose, scan1, 180. * M_PI / 180., 90. * M_PI / 180.); //draw scan + myRender->render(); + + // TIME MANAGEMENT --------------------------- + double dt = ((double) clock() - t2) / CLOCKS_PER_SEC; + mean_times(6) += dt; + if (dt < 0.1) + usleep(100000 - 1e6 * dt); + +// std::cout << "\nTree after step..." << std::endl; +// wolf_manager->getProblemPtr()->print(); + } + + // DISPLAY RESULTS ============================================================================================ + mean_times /= n_execution; + std::cout << "\nSIMULATION AVERAGE LOOP DURATION [s]" << std::endl; + std::cout << " data generation: " << mean_times(0) << std::endl; + std::cout << " wolf managing: " << mean_times(1) << std::endl; + std::cout << " ceres managing: " << mean_times(2) << std::endl; + std::cout << " ceres optimization: " << mean_times(3) << std::endl; + std::cout << " ceres covariance: " << mean_times(4) << std::endl; + std::cout << " results drawing: " << mean_times(5) << std::endl; + std::cout << " loop time: " << mean_times(6) << std::endl; + +// std::cout << "\nTree before deleting..." << std::endl; +// wolf_manager->getProblemPtr()->print(); + + // Draw Final result ------------------------- + std::vector<double> landmark_vector; + for (auto landmark_it = wolf_manager->getProblemPtr()->getMapPtr()->getLandmarkListPtr()->begin(); landmark_it != wolf_manager->getProblemPtr()->getMapPtr()->getLandmarkListPtr()->end(); landmark_it++) + { + WolfScalar* position_ptr = (*landmark_it)->getPPtr()->getPtr(); + landmark_vector.push_back(*position_ptr); //x + landmark_vector.push_back(*(position_ptr + 1)); //y + landmark_vector.push_back(0.2); //z + } + myRender->drawLandmarks(landmark_vector); +// viewPoint.setPose(devicePoses.front()); +// viewPoint.moveForward(10); +// viewPoint.rt.setEuler( viewPoint.rt.head()+M_PI/4, viewPoint.rt.pitch()+20.*M_PI/180., viewPoint.rt.roll() ); +// viewPoint.moveForward(-10); + myRender->setViewPoint(viewPoint); + myRender->render(); + + // Print Final result in a file ------------------------- + // Vehicle poses + int i = 0; + Eigen::VectorXs state_poses(n_execution * 3); + for (auto frame_it = wolf_manager->getProblemPtr()->getTrajectoryPtr()->getFrameListPtr()->begin(); frame_it != wolf_manager->getProblemPtr()->getTrajectoryPtr()->getFrameListPtr()->end(); frame_it++) + { + if (complex_angle) + state_poses.segment(i, 3) << *(*frame_it)->getPPtr()->getPtr(), *((*frame_it)->getPPtr()->getPtr() + 1), atan2(*(*frame_it)->getOPtr()->getPtr(), *((*frame_it)->getOPtr()->getPtr() + 1)); + else + state_poses.segment(i, 3) << *(*frame_it)->getPPtr()->getPtr(), *((*frame_it)->getPPtr()->getPtr() + 1), *(*frame_it)->getOPtr()->getPtr(); + i += 3; + } + + // Landmarks + i = 0; + Eigen::VectorXs landmarks(wolf_manager->getProblemPtr()->getMapPtr()->getLandmarkListPtr()->size() * 2); + for (auto landmark_it = wolf_manager->getProblemPtr()->getMapPtr()->getLandmarkListPtr()->begin(); landmark_it != wolf_manager->getProblemPtr()->getMapPtr()->getLandmarkListPtr()->end(); landmark_it++) + { + Eigen::Map<Eigen::Vector2s> landmark((*landmark_it)->getPPtr()->getPtr()); + landmarks.segment(i, 2) = landmark; + i += 2; + } + + // Print log files + std::string filepath = getenv("HOME") + (complex_angle ? std::string("/Desktop/log_file_3.txt") : std::string("/Desktop/log_file_2.txt")); + log_file.open(filepath, std::ofstream::out); //open log file + + if (log_file.is_open()) + { + log_file << 0 << std::endl; + for (unsigned int ii = 0; ii < n_execution; ii++) + log_file << state_poses.segment(ii * 3, 3).transpose() << "\t" << ground_truth.segment(ii * 3, 3).transpose() << "\t" << (state_poses.segment(ii * 3, 3) - ground_truth.segment(ii * 3, 3)).transpose() << "\t" << odom_trajectory.segment(ii * 3, 3).transpose() << std::endl; + log_file.close(); //close log file + std::cout << std::endl << "Result file " << filepath << std::endl; + } + else + std::cout << std::endl << "Failed to write the log file " << filepath << std::endl; + + std::string filepath2 = getenv("HOME") + (complex_angle ? std::string("/Desktop/landmarks_file_3.txt") : std::string("/Desktop/landmarks_file_2.txt")); + landmark_file.open(filepath2, std::ofstream::out); //open log file + + if (landmark_file.is_open()) + { + for (unsigned int ii = 0; ii < landmarks.size(); ii += 2) + landmark_file << landmarks.segment(ii, 2).transpose() << std::endl; + landmark_file.close(); //close log file + std::cout << std::endl << "Landmark file " << filepath << std::endl; + } + else + std::cout << std::endl << "Failed to write the landmark file " << filepath << std::endl; + + std::cout << "Press any key for ending... " << std::endl << std::endl; + std::getchar(); + + delete myRender; + delete myScanner; + delete wolf_manager; + std::cout << "wolf deleted" << std::endl; + + std::cout << " ========= END ===========" << std::endl << std::endl; + + //exit + return 0; +} + + + + + + diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt new file mode 100644 index 000000000..32da45f88 --- /dev/null +++ b/src/solver/CMakeLists.txt @@ -0,0 +1,4 @@ +SET(HDRS_SOLVER + ccolamd_ordering.h ) +SET(HDRS_SOLVER + ) \ No newline at end of file diff --git a/src/solver/solver_QR.h b/src/solver/solver_QR.h new file mode 100644 index 000000000..2fa0d11d1 --- /dev/null +++ b/src/solver/solver_QR.h @@ -0,0 +1,33 @@ +/* + * solver_QR.h + * + * Created on: Jun 22, 2015 + * Author: jvallve + */ + +#ifndef TRUNK_SRC_SOLVER_SOLVER_QR_H_ +#define TRUNK_SRC_SOLVER_SOLVER_QR_H_ + +using namespace Eigen; + +class SolverQR +{ + protected: + SparseQR < SparseMatrix<double>, NaturalOrdering<int>> solver_; + SparseMatrix<double> A, A_ordered, R; + VectorXd b, x, x_ordered, x_ordered_partial; + int n_measurements = 0; + int n_nodes = 0; + + // ordering variables + SparseMatrix<int> A_nodes_ordered; + PermutationMatrix<Dynamic, Dynamic, int> acc_permutation_nodes_matrix; + + CCOLAMDOrdering<int> ordering, partial_ordering; + VectorXi nodes_ordering_constraints; + + private: +}; + + +#endif /* TRUNK_SRC_SOLVER_SOLVER_QR_H_ */ diff --git a/src/solver/solver_manager.cpp b/src/solver/solver_manager.cpp new file mode 100644 index 000000000..16d7ab67a --- /dev/null +++ b/src/solver/solver_manager.cpp @@ -0,0 +1,228 @@ +#include "ceres_manager.h" + +SolverManager::SolverManager() +{ + +} + +SolverManager::~SolverManager() +{ + removeAllStateUnits(); +} + +void SolverManager::solve() +{ + +} + +//void SolverManager::computeCovariances(WolfProblem* _problem_ptr) +//{ +//} + +void SolverManager::update(WolfProblem* _problem_ptr) +{ + // IF REALLOCATION OF STATE, REMOVE EVERYTHING AND BUILD THE PROBLEM AGAIN + if (_problem_ptr->isReallocated()) + { + // todo: reallocate x + } + else + { + // ADD/UPDATE STATE UNITS + for(auto state_unit_it = _problem_ptr->getStateListPtr()->begin(); state_unit_it!=_problem_ptr->getStateListPtr()->end(); state_unit_it++) + { + if ((*state_unit_it)->getPendingStatus() == ADD_PENDING) + addStateUnit(*state_unit_it); + + else if((*state_unit_it)->getPendingStatus() == UPDATE_PENDING) + updateStateUnitStatus(*state_unit_it); + } + //std::cout << "state units updated!" << std::endl; + + // REMOVE STATE UNITS + while (!_problem_ptr->getRemovedStateListPtr()->empty()) + { + // TODO: remove state unit + //_problem_ptr->getRemovedStateListPtr()->pop_front(); + } + //std::cout << "state units removed!" << std::endl; + + // ADD CONSTRAINTS + ConstraintBaseList ctr_list; + _problem_ptr->getTrajectoryPtr()->getConstraintList(ctr_list); + //std::cout << "ctr_list.size() = " << ctr_list.size() << std::endl; + for(auto ctr_it = ctr_list.begin(); ctr_it!=ctr_list.end(); ctr_it++) + if ((*ctr_it)->getPendingStatus() == ADD_PENDING) + addConstraint(*ctr_it); + + //std::cout << "constraints updated!" << std::endl; + } +} + +void SolverManager::addConstraint(ConstraintBase* _corr_ptr) +{ + //TODO MatrixXs J; Vector e; + // getResidualsAndJacobian(_corr_ptr, J, e); + // solverQR->addConstraint(_corr_ptr, J, e); + +// constraint_map_[_corr_ptr->nodeId()] = blockIdx; + _corr_ptr->setPendingStatus(NOT_PENDING); +} + +void SolverManager::removeConstraint(const unsigned int& _corr_idx) +{ + // TODO +} + +void SolverManager::addStateUnit(StateBase* _st_ptr) +{ + //std::cout << "Adding State Unit " << _st_ptr->nodeId() << std::endl; + //_st_ptr->print(); + + switch (_st_ptr->getStateType()) + { + case ST_COMPLEX_ANGLE: + { + // TODO + //std::cout << "Adding Complex angle Local Parametrization to the List... " << std::endl; + //ceres_problem_->AddParameterBlock(_st_ptr->getPtr(), ((StateComplexAngle*)_st_ptr)->BLOCK_SIZE, new ComplexAngleParameterization); + break; + } + case ST_THETA: + { + //std::cout << "No Local Parametrization to be added" << std::endl; + ceres_problem_->AddParameterBlock(_st_ptr->getPtr(), ((StateTheta*)_st_ptr)->BLOCK_SIZE, nullptr); + break; + } + case ST_POINT_1D: + { + //std::cout << "No Local Parametrization to be added" << std::endl; + ceres_problem_->AddParameterBlock(_st_ptr->getPtr(), ((StatePoint1D*)_st_ptr)->BLOCK_SIZE, nullptr); + break; + } + case ST_POINT_2D: + { + //std::cout << "No Local Parametrization to be added" << std::endl; + ceres_problem_->AddParameterBlock(_st_ptr->getPtr(), ((StatePoint2D*)_st_ptr)->BLOCK_SIZE, nullptr); + break; + } + case ST_POINT_3D: + { + //std::cout << "No Local Parametrization to be added" << std::endl; + ceres_problem_->AddParameterBlock(_st_ptr->getPtr(), ((StatePoint3D*)_st_ptr)->BLOCK_SIZE, nullptr); + break; + } + default: + std::cout << "Unknown Local Parametrization type!" << std::endl; + } + if (_st_ptr->getStateStatus() != ST_ESTIMATED) + updateStateUnitStatus(_st_ptr); + + _st_ptr->setPendingStatus(NOT_PENDING); +} + +void SolverManager::removeAllStateUnits() +{ + std::vector<double*> parameter_blocks; + + ceres_problem_->GetParameterBlocks(¶meter_blocks); + + for (unsigned int i = 0; i< parameter_blocks.size(); i++) + ceres_problem_->RemoveParameterBlock(parameter_blocks[i]); +} + +void SolverManager::updateStateUnitStatus(StateBase* _st_ptr) +{ + // TODO + +// if (_st_ptr->getStateStatus() == ST_ESTIMATED) +// ceres_problem_->SetParameterBlockVariable(_st_ptr->getPtr()); +// else if (_st_ptr->getStateStatus() == ST_FIXED) +// ceres_problem_->SetParameterBlockConstant(_st_ptr->getPtr()); +// else +// printf("\nERROR: Update state unit status with unknown status"); +// +// _st_ptr->setPendingStatus(NOT_PENDING); +} + +ceres::CostFunction* SolverManager::createCostFunction(ConstraintBase* _corrPtr) +{ + //std::cout << "adding ctr " << _corrPtr->nodeId() << std::endl; + //_corrPtr->print(); + + switch (_corrPtr->getConstraintType()) + { + case CTR_GPS_FIX_2D: + { + ConstraintGPS2D* specific_ptr = (ConstraintGPS2D*)(_corrPtr); + return new ceres::AutoDiffCostFunction<ConstraintGPS2D, + specific_ptr->measurementSize, + specific_ptr->block0Size, + specific_ptr->block1Size, + specific_ptr->block2Size, + specific_ptr->block3Size, + specific_ptr->block4Size, + specific_ptr->block5Size, + specific_ptr->block6Size, + specific_ptr->block7Size, + specific_ptr->block8Size, + specific_ptr->block9Size>(specific_ptr); + break; + } + case CTR_ODOM_2D_COMPLEX_ANGLE: + { + ConstraintOdom2DComplexAngle* specific_ptr = (ConstraintOdom2DComplexAngle*)(_corrPtr); + return new ceres::AutoDiffCostFunction<ConstraintOdom2DComplexAngle, + specific_ptr->measurementSize, + specific_ptr->block0Size, + specific_ptr->block1Size, + specific_ptr->block2Size, + specific_ptr->block3Size, + specific_ptr->block4Size, + specific_ptr->block5Size, + specific_ptr->block6Size, + specific_ptr->block7Size, + specific_ptr->block8Size, + specific_ptr->block9Size>(specific_ptr); + break; + } + case CTR_ODOM_2D_THETA: + { + ConstraintOdom2DTheta* specific_ptr = (ConstraintOdom2DTheta*)(_corrPtr); + return new ceres::AutoDiffCostFunction<ConstraintOdom2DTheta, + specific_ptr->measurementSize, + specific_ptr->block0Size, + specific_ptr->block1Size, + specific_ptr->block2Size, + specific_ptr->block3Size, + specific_ptr->block4Size, + specific_ptr->block5Size, + specific_ptr->block6Size, + specific_ptr->block7Size, + specific_ptr->block8Size, + specific_ptr->block9Size>(specific_ptr); + break; + } + case CTR_CORNER_2D_THETA: + { + ConstraintCorner2DTheta* specific_ptr = (ConstraintCorner2DTheta*)(_corrPtr); + return new ceres::AutoDiffCostFunction<ConstraintCorner2DTheta, + specific_ptr->measurementSize, + specific_ptr->block0Size, + specific_ptr->block1Size, + specific_ptr->block2Size, + specific_ptr->block3Size, + specific_ptr->block4Size, + specific_ptr->block5Size, + specific_ptr->block6Size, + specific_ptr->block7Size, + specific_ptr->block8Size, + specific_ptr->block9Size>(specific_ptr); + break; + } + default: + std::cout << "Unknown constraint type! Please add it in the CeresWrapper::createCostFunction()" << std::endl; + + return nullptr; + } +} diff --git a/src/solver/solver_manager.h b/src/solver/solver_manager.h new file mode 100644 index 000000000..32604ed2a --- /dev/null +++ b/src/solver/solver_manager.h @@ -0,0 +1,49 @@ +#ifndef CERES_MANAGER_H_ +#define CERES_MANAGER_H_ + +//wolf includes +#include "../wolf.h" +#include "../state_base.h" +#include "../state_point.h" +#include "../state_complex_angle.h" +#include "../state_theta.h" +#include "../constraint_sparse.h" +#include "../constraint_gps_2D.h" +#include "../constraint_odom_2D_theta.h" +#include "../constraint_odom_2D_complex_angle.h" +#include "../constraint_corner_2D_theta.h" + +/** \brief solver manager for WOLF + * + */ + +class SolverManager +{ + protected: + + + public: + SolverManager(ceres::Problem::Options _options); + + ~SolverManager(); + + ceres::Solver::Summary solve(const ceres::Solver::Options& _ceres_options); + + //void computeCovariances(WolfProblem* _problem_ptr); + + void update(const WolfProblemPtr _problem_ptr); + + void addConstraint(ConstraintBase* _corr_ptr); + + void removeConstraint(const unsigned int& _corr_idx); + + void addStateUnit(StateBase* _st_ptr); + + void removeAllStateUnits(); + + void updateStateUnitStatus(StateBase* _st_ptr); + + ceres::CostFunction* createCostFunction(ConstraintBase* _corrPtr); +}; + +#endif -- GitLab