00001
00008 #ifndef VSLAM_GRAPH_OPT_H_INCLUDED
00009 #define VSLAM_GRAPH_OPT_H_INCLUDED
00010 #include "utils.h"
00011 #include "pose_graph.h"
00012
00013 #include "g2o_types/vertex_pose.h"
00014 #include "g2o_types/vertex_landmark.h"
00015 #include "g2o_types/edge_pose_landmark.h"
00016
00017 #include <g2o/core/sparse_optimizer.h>
00018 #include <g2o/core/block_solver.h>
00019 #include <g2o/core/factory.h>
00020 #include <g2o/core/optimization_algorithm_factory.h>
00021 #include <g2o/core/optimization_algorithm_gauss_newton.h>
00022 #include <g2o/core/optimization_algorithm_levenberg.h>
00023 #include <g2o/solvers/csparse/linear_solver_csparse.h>
00024 #include <g2o/solvers/pcg/linear_solver_pcg.h>
00025 #include "g2o/solvers/dense/linear_solver_dense.h"
00026 #if defined G2O_HAVE_CHOLMOD
00027 #include "g2o/solvers/cholmod/linear_solver_cholmod.h"
00028 #elif defined G2O_HAVE_CSPARSE
00029 #include "g2o/solvers/csparse/linear_solver_csparse.h"
00030 #endif
00031
00032
00033 #include <boost/graph/dijkstra_shortest_paths.hpp>
00034 #include <iostream>
00035 #include <cmath>
00036 #include <tr1/functional>
00037
00038
00039
00040 namespace vslam{
00041
00054
00055
00062 namespace g2o_types{}
00063
00064
00070 template<class G> class G2OGraphBuilder{
00071 public:
00072 typedef typename graph_traits<G>::vertex_descriptor VDT;
00073
00074 const int first_landmark_id;
00075 const double max_error;
00076
00085 G2OGraphBuilder(int _first_landmark_id, double _max_error) :
00086 first_landmark_id(_first_landmark_id), max_error(_max_error) {
00087
00088 }
00089
00096 void operator()(g2o::SparseOptimizer& optimizer, G& g, Points<G>& points, VDT startv){
00097 buildGraphImpl(optimizer, g, points, startv);
00098 }
00099
00100 private:
00101 virtual void buildGraphImpl(
00102 g2o::SparseOptimizer& optimizer, G& g, Points<G>& points, VDT startv) = 0;
00103 };
00104
00105
00106
00107
00108
00117 template<class G> class G2OConstActiveDistanceBuilder: public G2OGraphBuilder<G>{
00118 protected:
00119 typedef typename graph_traits<G>::vertex_iterator VertexItT;
00120 typedef typename graph_traits<G>::vertex_descriptor VertexD;
00121
00122 const int active_window;
00123 public:
00125 G2OConstActiveDistanceBuilder(int _first_landmark_id, double _max_error, int _active_window) :
00126 G2OGraphBuilder<G>(_first_landmark_id, _max_error), active_window(_active_window) { }
00127
00128 private:
00129
00131 struct Instance{
00132 g2o::SparseOptimizer& optimizer;
00133 G& g;
00134 Points<G>& points;
00135 vector<bool> point_added;
00136 std::vector<VertexD> added_vertices;
00137 vector<int> distances;
00138
00139 Instance(g2o::SparseOptimizer& _opt, G& _g, Points<G>& _points, VertexD current_pose) :
00140 optimizer(_opt), g(_g), points(_points), point_added(_points.size(), false) {
00141 typedef boost::iterator_property_map<
00142 vector<int>::iterator,
00143 typename boost::property_map<
00144 G,
00145 boost::vertex_index_t>::type> DistancePMap;
00146 vector<int> _distances(num_vertices(g));
00147 DistancePMap dmap(_distances.begin(), get(boost::vertex_index, g));
00148 boost::dijkstra_shortest_paths(g, current_pose,
00149 boost::weight_map(AllWeightsOneWeightMap<G>()).distance_map(dmap));
00150 distances=_distances;
00151 }
00152 };
00153
00155 class PoseAgeCompare {
00156 private:
00157 const G& g;
00158 public:
00159 PoseAgeCompare(const G& _g) : g(_g) {}
00160
00162 bool operator()(VertexD a, VertexD b)
00163 {
00164 return get(vertex_timestamp, g, a) > get(vertex_timestamp, g, b);
00165 }
00166 };
00167
00175 void addLandmarkObservations(int lm_idx, Instance& instance){
00176 using namespace g2o_types;
00177 typedef std::pair<VertexD, int> FT;
00178 const Point<G> & lm = instance.points.getPoint(lm_idx);
00179 int added_fixed=0;
00180 for(int i=0; i< static_cast<int>(lm.seen_from.size()); i++){
00181 FT fid = lm.seen_from[i];
00182 double dist = instance.distances[get(boost::vertex_index, instance.g, fid.first)];
00183 bool add=false;
00184 if(dist <= active_window){
00185 add=true;
00186 } else{
00187 if(dist <= 20){
00188 if(added_fixed%1 == 0) add=true;
00189 added_fixed++;
00190 }
00191 }
00192 if(add){
00193 vector<cv::KeyPoint> & keypoints = get(vertex_kp, instance.g, fid.first);
00194 EdgeSE3PointXYZ<G>* observation = new EdgeSE3PointXYZ<G>;
00195 addPose(fid.first, instance, false);
00196 observation->setVertex(
00197 0, instance.optimizer.vertex(get(vertex_seq, instance.g, fid.first)));
00198 observation->setVertex(
00199 1, instance.optimizer.vertex(this->first_landmark_id+lm_idx));
00200 observation->setMeasurement(
00201 Vector2d(keypoints[fid.second].pt.x, keypoints[fid.second].pt.y));
00202 observation->setInformation(Eigen::Matrix2d::Identity());
00203 observation->computeError();
00204 if((observation->error()).norm() <= this->max_error){
00205 instance.optimizer.addEdge(observation);
00206 } else{
00207 ROS_WARN_STREAM_NAMED("Optimization", "encountered high error constraint("
00208 << (observation->error()).norm() << ")");
00209 break;
00210 }
00211 }
00212 }
00213 }
00214
00220 void addVisibleLandmarks(VertexD vertex, Instance& instance){
00221 using namespace g2o_types;
00222 vector<cv::KeyPoint> & keypoints = get(vertex_kp, instance.g, vertex);
00223 vector<int> & fixed_points = get(vertex_fix_idx, instance.g, vertex);
00224 for(size_t i = 0; i<keypoints.size(); i++){
00225 if(fixed_points[i] != BAD_INDEX &&
00226 instance.points.getPoint(fixed_points[i]).initialized){
00227 if(!instance.point_added[fixed_points[i]]){
00228 VertexPointXYZ<G>* feature_landmark =
00229 new VertexPointXYZ<G>(fixed_points[i], instance.points);
00230 feature_landmark->setId(this->first_landmark_id+fixed_points[i]);
00231 feature_landmark->setEstimate(
00232 instance.points.getPoint(fixed_points[i]).global);
00233 feature_landmark->setFixed(false);
00234 instance.optimizer.addVertex(feature_landmark);
00235 instance.point_added[fixed_points[i]]=true;
00236 addLandmarkObservations(fixed_points[i], instance);
00237 }
00238 }
00239 }
00240 }
00241
00247 void addPose(VertexD vertex, Instance& instance, bool add_lms){
00248 using namespace g2o_types;
00249 if(!instance.optimizer.vertex(get(vertex_seq, instance.g, vertex))){
00250 VertexSE3<G>* camera_pose=
00251 new VertexSE3<G>(instance.g, vertex);
00252 camera_pose->setId(get(vertex_seq, instance.g, vertex));
00253 camera_pose->setEstimate(get(vertex_transform, instance.g, vertex));
00254 camera_pose->setFixed(true);
00255 instance.optimizer.addVertex(camera_pose);
00256 if(add_lms) addVisibleLandmarks(vertex, instance);
00257 ROS_INFO_STREAM_NAMED("Optimization", "Added vertex " <<
00258 get(vertex_seq, instance.g, vertex) << " keyframe flag: " <<
00259 get(vertex_keyframe_flag, instance.g, vertex));
00260 instance.added_vertices.push_back(vertex);
00261 }
00262 }
00263
00265 void setPoseFixed(VertexD vertex, Instance& instance, bool fixed){
00266 instance.optimizer.vertex(get(vertex_seq, instance.g, vertex))->setFixed(fixed);
00267 }
00268
00269 virtual void buildGraphImpl(
00270 g2o::SparseOptimizer& optimizer, G& g, Points<G>& points, VertexD startv){
00271 Instance instance(optimizer, g, points, startv);
00272
00273 VertexItT vertex_it, vertex_it_end;
00274 tie(vertex_it, vertex_it_end) = vertices(instance.g);
00275 while(vertex_it != vertex_it_end){
00276 int dist = instance.distances[get(boost::vertex_index, instance.g, *vertex_it)];
00277 if(dist <= active_window){
00278 addPose(*vertex_it, instance, true);
00279 setPoseFixed(*vertex_it, instance, false);
00280 instance.added_vertices.push_back(*vertex_it);
00281 ROS_INFO_STREAM_NAMED("Optimization", "Marked vertex " <<
00282 get(vertex_seq, instance.g, *vertex_it) << " as optimizable.");
00283 }
00284 ++vertex_it;
00285 }
00286 std::sort(instance.added_vertices.begin(), instance.added_vertices.end(),
00287 PoseAgeCompare(instance.g));
00288 int tmp = static_cast<int>(instance.added_vertices.size());
00289 for(int i=tmp-2; i< tmp ; i++){
00290 setPoseFixed(instance.added_vertices[i], instance, true);
00291 }
00292 }
00293 };
00294
00295
00297 template<class G> class G2ORunner2{
00298 protected:
00299 typedef typename graph_traits<G>::vertex_descriptor VertexD;
00300 g2o::SparseOptimizer optimizer;
00301
00302 public:
00303 G2ORunner2(){
00304 using namespace g2o;
00305 typedef BlockSolver< BlockSolverTraits<Eigen::Dynamic, Eigen::Dynamic> > SlamBlockSolver;
00306 typedef LinearSolverCSparse<SlamBlockSolver::PoseMatrixType> SlamLinearSolver;
00307
00308
00309
00310
00311 SlamLinearSolver* linearSolver = new SlamLinearSolver();
00312 linearSolver->setBlockOrdering(false);
00313 SlamBlockSolver* blockSolver = new SlamBlockSolver(linearSolver);
00314 blockSolver->setSchur(false);
00315
00316
00317 OptimizationAlgorithmLevenberg* solver =
00318 new OptimizationAlgorithmLevenberg(blockSolver);
00319
00320 optimizer.setAlgorithm(solver);
00321 optimizer.setComputeBatchStatistics(false);
00322 }
00323
00324 public:
00325
00333 virtual void doOptimalization(
00334 G& _g,
00335 Points<G>& _points,
00336 G2OGraphBuilder<G> & builder,
00337 VertexD startv){
00338 using namespace g2o_types;
00339 PerformanceLogger<G> pl(_g, startv, "opt");
00340
00341 ROS_INFO_NAMED("Optimization", "Building pose graph ... ");
00342 builder(optimizer,_g,_points,startv);
00343
00344 optimizer.setVerbose(false);
00345 ROS_INFO_NAMED("Optimization",
00346 "Running 15 iterations of the optimalization algorithm ... ");
00347 optimizer.initializeOptimization();
00348
00349 optimizer.optimize(15);
00350
00351 g2o::OptimizableGraph::EdgeContainer all_edges = optimizer.activeEdges();
00352 double max_error=0;
00353 BOOST_FOREACH(g2o::OptimizableGraph::Edge* edge_tmp, all_edges){
00354 EdgeSE3PointXYZ<G>* edge =
00355 dynamic_cast<EdgeSE3PointXYZ<G>*>(edge_tmp);
00356 edge->computeError();
00357 if(edge->error().norm() > max_error) max_error=edge->error().norm();
00358 double err_tolerance = builder.max_error;
00359 if((edge->error()).norm() >= err_tolerance){
00360 ROS_WARN_STREAM_NAMED("Optimization", "high error edge detecded, error norm: "
00361 << (edge->error()).norm() << " errror tolerance: " << err_tolerance);
00362 }
00363 }
00364 ROS_INFO_STREAM_NAMED("Optimization", "Optimization done. Final chi2 is " <<
00365 optimizer.chi2() << "; Maximal error is "<<max_error << "; Number of edges: " <<
00366 optimizer.activeEdges().size());
00367 get(vertex_debug_info, _g, startv).double_propmap["opt_chi2"]= optimizer.chi2();
00368 get(vertex_debug_info, _g, startv).double_propmap["max_opt_error"]= max_error;
00369 optimizer.clear();
00370 }
00371 };
00372
00373
00374
00376 }
00377
00378
00379
00380 #endif