00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <sys/time.h>
00019 #include <visualization_msgs/Marker.h>
00020 #include <geometry_msgs/Point.h>
00021
00022 #include "graph_manager.h"
00023 #include "misc.h"
00024
00025 #include <opencv2/features2d/features2d.hpp>
00026 #include <QThread>
00027 #include <qtconcurrentrun.h>
00028 #include <QtConcurrentMap>
00029 #include <utility>
00030 #include <fstream>
00031 #include <limits>
00032 #include <boost/foreach.hpp>
00033 #include <pcl_ros/point_cloud.h>
00034
00035 #include "g2o/types/slam3d/se3quat.h"
00036 #include "g2o/types/slam3d/edge_se3.h"
00037
00038 #include "g2o/core/block_solver.h"
00039 #include "g2o/core/optimizable_graph.h"
00040 #include "g2o/solvers/csparse/linear_solver_csparse.h"
00041 #include "g2o/solvers/cholmod/linear_solver_cholmod.h"
00042 #include "g2o/solvers/pcg/linear_solver_pcg.h"
00043 #include "g2o/solvers/dense/linear_solver_dense.h"
00044 #include "g2o/core/sparse_optimizer.h"
00045 #include "g2o/core/optimization_algorithm_dogleg.h"
00046 #include "g2o/core/optimization_algorithm_levenberg.h"
00047 #include "scoped_timer.h"
00048
00049
00050
00051 typedef g2o::BlockSolver< g2o::BlockSolverTraits<6, 3> > SlamBlockSolver;
00052 typedef g2o::LinearSolverCSparse<SlamBlockSolver::PoseMatrixType> SlamLinearCSparseSolver;
00053 typedef g2o::LinearSolverCholmod<SlamBlockSolver::PoseMatrixType> SlamLinearCholmodSolver;
00054 typedef g2o::LinearSolverPCG<SlamBlockSolver::PoseMatrixType> SlamLinearPCGSolver;
00055 typedef g2o::LinearSolverDense<SlamBlockSolver::PoseMatrixType> SlamLinearDenseSolver;
00056
00057 typedef std::tr1::unordered_map<int, g2o::HyperGraph::Vertex*> VertexIDMap;
00058 typedef std::pair<int, g2o::HyperGraph::Vertex*> VertexIDPair;
00059
00060 typedef std::set<g2o::HyperGraph::Edge*> EdgeSet;
00061
00062
00063 GraphManager::GraphManager() :
00064 optimizer_(NULL),
00065 reset_request_(false),
00066 marker_id_(0),
00067 batch_processing_runs_(false),
00068 process_node_runs_(false),
00069 localization_only_(false),
00070 loop_closures_edges(0), sequential_edges(0),
00071 next_seq_id(0), next_vertex_id(0),
00072 current_backend_("none"),
00073 last_odom_time_(0),
00074 calibration_vertex_(NULL)
00075 {
00076 ScopedTimer s(__FUNCTION__);
00077 #ifdef DO_FEATURE_OPTIMIZATION
00078 next_landmark_id = 0;
00079 #endif
00080
00081 ParameterServer* ps = ParameterServer::instance();
00082 createOptimizer(ps->get<std::string>("backend_solver"));
00083 ros::NodeHandle nh;
00084 batch_cloud_pub_ = nh.advertise<pointcloud_type>(ps->get<std::string>("individual_cloud_out_topic"),
00085 ps->get<int>("publisher_queue_size"));
00086 online_cloud_pub_ = nh.advertise<pointcloud_type>(ps->get<std::string>("online_cloud_out_topic"),
00087 ps->get<int>("publisher_queue_size"));
00088 whole_cloud_pub_ = nh.advertise<pointcloud_type>(ps->get<std::string>("aggregate_cloud_out_topic"),
00089 ps->get<int>("publisher_queue_size"));
00090 ransac_marker_pub_ = nh.advertise<visualization_msgs::Marker>("/rgbdslam/correspondence_marker",
00091 ps->get<int>("publisher_queue_size"));
00092 marker_pub_ = nh.advertise<visualization_msgs::Marker>("/rgbdslam/pose_graph_markers",
00093 ps->get<int>("publisher_queue_size"));
00094 computed_motion_ = tf::Transform::getIdentity();
00095 init_base_pose_ = tf::Transform::getIdentity();
00096 std::string fixed_frame = ParameterServer::instance()->get<std::string>("fixed_frame_name");
00097 std::string base_frame = ParameterServer::instance()->get<std::string>("base_frame_name");
00098
00099 latest_transform_cache_ = tf::StampedTransform(tf::Transform::getIdentity(), ros::Time::now(), base_frame, fixed_frame);
00100 timer_ = nh.createTimer(ros::Duration(0.1), &GraphManager::broadcastLatestTransform, this);
00101 }
00102
00103
00104 void GraphManager::setOptimizerVerbose(bool verbose){
00105 optimizer_->setVerbose(verbose);
00106 }
00107 void GraphManager::createOptimizer(std::string backend, g2o::SparseOptimizer* optimizer)
00108 {
00109 QMutexLocker locker(&optimizer_mutex_);
00110 QMutexLocker locker2(&optimization_mutex_);
00111
00112 if(optimizer == NULL){
00113 if(optimizer_ != NULL){
00114 for(g2o::SparseOptimizer::VertexIDMap::iterator it = optimizer_->vertices().begin(); it != optimizer_->vertices().end(); it++)
00115 {
00116 it->second->edges().clear();
00117 }
00118 for(EdgeSet::iterator it = optimizer_->edges().begin(); it != optimizer_->edges().end(); it++)
00119 {
00120
00121 }
00122 optimizer_->edges().clear();
00123 optimizer_->vertices().clear();
00124 }
00125 delete optimizer_;
00126 optimizer_ = new g2o::SparseOptimizer();
00127 optimizer_->setVerbose(false);
00128 } else if (optimizer_ != optimizer){
00129 delete optimizer_;
00130 optimizer_ = new g2o::SparseOptimizer();
00131 optimizer_->setVerbose(false);
00132 }
00133
00134
00135
00136 ParameterServer* ps = ParameterServer::instance();
00137 if(ps->get<bool>("optimize_landmarks")){
00138 g2o::BlockSolverX::LinearSolverType *linearSolver = new g2o::LinearSolverCSparse<g2o::BlockSolverX::PoseMatrixType>();
00139 g2o::BlockSolverX *solver_ptr = new g2o::BlockSolverX(linearSolver);
00140
00141 g2o::OptimizationAlgorithmDogleg * algo = new g2o::OptimizationAlgorithmDogleg(solver_ptr);
00142 optimizer_->setAlgorithm(algo);
00143 }
00144 else
00145 {
00146 SlamBlockSolver* solver = NULL;
00147 if(backend == "cholmod" || backend == "auto"){
00148 SlamLinearCholmodSolver* linearSolver = new SlamLinearCholmodSolver();
00149 linearSolver->setBlockOrdering(false);
00150 solver = new SlamBlockSolver(linearSolver);
00151 current_backend_ = "cholmod";
00152 }
00153 else if(backend == "csparse"){
00154 SlamLinearCSparseSolver* linearSolver = new SlamLinearCSparseSolver();
00155 linearSolver->setBlockOrdering(false);
00156 solver = new SlamBlockSolver(linearSolver);
00157 current_backend_ = "csparse";
00158 }
00159 else if(backend == "dense"){
00160 SlamLinearDenseSolver* linearSolver = new SlamLinearDenseSolver();
00161 solver = new SlamBlockSolver(linearSolver);
00162 current_backend_ = "dense";
00163 }
00164 else if(backend == "pcg"){
00165 SlamLinearPCGSolver* linearSolver = new SlamLinearPCGSolver();
00166 solver = new SlamBlockSolver(linearSolver);
00167 current_backend_ = "pcg";
00168 }
00169 else {
00170 ROS_ERROR("Bad Parameter for g2o Solver backend: %s. User cholmod, csparse or pcg", backend.c_str());
00171 ROS_INFO("Falling Back to Cholmod Solver");
00172 SlamLinearCholmodSolver* linearSolver = new SlamLinearCholmodSolver();
00173 linearSolver->setBlockOrdering(false);
00174 solver = new SlamBlockSolver(linearSolver);
00175 current_backend_ = "cholmod";
00176 }
00177
00178 g2o::OptimizationAlgorithmLevenberg * algo = new g2o::OptimizationAlgorithmLevenberg(solver);
00179
00180 optimizer_->setAlgorithm(algo);
00181 }
00182
00183
00184
00185 optimizer_->setVerbose(false);
00186
00187
00188 #ifdef DO_FEATURE_OPTIMIZATION
00189 float fx = ps->get<double>("depth_camera_fx") > 0 ? ps->get<double>("depth_camera_fx") : 525;
00190 float fy = ps->get<double>("depth_camera_fy") > 0 ? ps->get<double>("depth_camera_fy") : 525;
00191 float cx = ps->get<double>("depth_camera_cx") > 0 ? ps->get<double>("depth_camera_cx") : 319.5;
00192 float cy = ps->get<double>("depth_camera_cy") > 0 ? ps->get<double>("depth_camera_cy") : 239.5;
00193
00194 g2o::ParameterCamera* cameraParams = new g2o::ParameterCamera();
00195 cameraParams->setKcam(fx, fy, cx, cy);
00196 g2o::SE3Quat offset;
00197 cameraParams->setOffset(offset);
00198 cameraParams->setId(0);
00199 optimizer_->addParameter(cameraParams);
00200 #endif
00201 }
00202
00203
00204 QList<int> GraphManager::getPotentialEdgeTargetsWithDijkstra(const Node* new_node, int sequential_targets, int geodesic_targets, int sampled_targets, int predecessor_id, bool include_predecessor)
00205 {
00206 QList<int> ids_to_link_to;
00207 if(predecessor_id < 0) predecessor_id = graph_.size()-1;
00208
00209 std::stringstream ss;
00210 ss << "Node ID's to compare with candidate for node " << graph_.size() << ". Sequential: ";
00211
00212 if((int)camera_vertices.size() <= sequential_targets+geodesic_targets+sampled_targets ||
00213 camera_vertices.size() <= 1)
00214 {
00215 sequential_targets = sequential_targets+geodesic_targets+sampled_targets;
00216 geodesic_targets = 0;
00217 sampled_targets = 0;
00218 predecessor_id = graph_.size()-1;
00219 }
00220
00221 if(sequential_targets > 0){
00222
00223 for (int i=1; i < sequential_targets+1 && predecessor_id-i >= 0; i++) {
00224 ids_to_link_to.push_back(predecessor_id-i);
00225 ss << ids_to_link_to.back() << ", " ;
00226 }
00227 }
00228
00229 if(geodesic_targets > 0){
00230 g2o::HyperDijkstra hypdij(optimizer_);
00231 g2o::UniformCostFunction cost_function;
00232 g2o::VertexSE3* prev_vertex = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(graph_[predecessor_id]->vertex_id_));
00233 hypdij.shortestPaths(prev_vertex,&cost_function,ParameterServer::instance()->get<int>("geodesic_depth"));
00234 g2o::HyperGraph::VertexSet& vs = hypdij.visited();
00235
00236
00237 std::map<int, int> vertex_id_to_node_id;
00238 for (graph_it it = graph_.begin(); it !=graph_.end(); ++it){
00239 Node *node = it->second;
00240 vertex_id_to_node_id[node->vertex_id_] = node->id_;
00241
00242 }
00243
00244
00245 std::map<int,int> neighbour_indices;
00246 int sum_of_weights=0;
00247 for (g2o::HyperGraph::VertexSet::iterator vit=vs.begin(); vit!=vs.end(); vit++) {
00248 int vid = (*vit)->id();
00249
00250 int id = 0;
00251 try{
00252 id = vertex_id_to_node_id.at(vid);
00253 }
00254 catch (std::exception e){
00255 ROS_ERROR("Vertex ID %d has no corresponding node", vid);
00256 ROS_ERROR("Map Content:");
00257 for(std::map<int,int>::const_iterator it = vertex_id_to_node_id.begin(); it != vertex_id_to_node_id.end(); it++){
00258 ROS_ERROR("Node ID %d: Vertex ID %d", it->first, it->second);
00259 }
00260 for(g2o::SparseOptimizer::VertexIDMap::iterator it = optimizer_->vertices().begin(); it != optimizer_->vertices().end(); it++)
00261 {
00262 ROS_ERROR("Vertex ID %d", it->first);
00263 }
00264 }
00265 if(!graph_.at(id)->matchable_) continue;
00266 if(id < predecessor_id-sequential_targets || (id > predecessor_id && id <= (int)graph_.size()-1)){
00267 int weight = abs(predecessor_id-id);
00268 neighbour_indices[id] = weight;
00269 sum_of_weights += weight;
00270 }
00271 }
00272
00273
00274 ss << "Dijkstra: ";
00275 while(ids_to_link_to.size() < sequential_targets+geodesic_targets && neighbour_indices.size() != 0){
00276 int random_pick = rand() % sum_of_weights;
00277 ROS_DEBUG("Pick: %d/%d", random_pick, sum_of_weights);
00278 int weight_so_far = 0;
00279 for(std::map<int,int>::iterator map_it = neighbour_indices.begin(); map_it != neighbour_indices.end(); map_it++ ){
00280 weight_so_far += map_it->second;
00281 ROS_DEBUG("Checking: %d, %d, %d", map_it->first, map_it-> second, weight_so_far);
00282 if(weight_so_far > random_pick){
00283 int sampled_id = map_it->first;
00284 ids_to_link_to.push_front(sampled_id);
00285 ss << ids_to_link_to.front() << ", " ;
00286 sum_of_weights -= map_it->second;
00287 ROS_DEBUG("Taking ID: %d, decreasing sum of weights to %d", map_it->first, sum_of_weights);
00288 neighbour_indices.erase(map_it);
00289 ROS_ERROR_COND(sum_of_weights<0, "Sum of weights should never be zero");
00290 break;
00291 }
00292 ROS_DEBUG("Skipping ID: %d", map_it->first);
00293 }
00294 }
00295 }
00296
00297 if(sampled_targets > 0){
00298 ss << "Random Sampling: ";
00299 std::vector<int> non_neighbour_indices;
00300 non_neighbour_indices.reserve(graph_.size());
00301 for (QList<int>::iterator it = keyframe_ids_.begin(); it != keyframe_ids_.end(); it++){
00302 if(ids_to_link_to.contains(*it) == 0 && graph_.at(*it)->matchable_){
00303 non_neighbour_indices.push_back(*it);
00304 }
00305 }
00306
00307
00308 while(ids_to_link_to.size() < geodesic_targets+sampled_targets+sequential_targets && non_neighbour_indices.size() != 0){
00309 int index_of_v_id = rand() % non_neighbour_indices.size();
00310 int sampled_id = non_neighbour_indices[index_of_v_id];
00311 non_neighbour_indices[index_of_v_id] = non_neighbour_indices.back();
00312 non_neighbour_indices.resize(non_neighbour_indices.size()-1);
00313 ids_to_link_to.push_front(sampled_id);
00314 ss << ids_to_link_to.front() << ", " ;
00315 }
00316 }
00317
00318 if(include_predecessor){
00319 ids_to_link_to.push_back(predecessor_id);
00320 ss << predecessor_id;
00321 }
00322 ROS_INFO("%s", ss.str().c_str());
00323 return ids_to_link_to;
00324 }
00325
00326 void GraphManager::resetGraph(){
00327 #ifdef DO_FEATURE_OPTIMIZATION
00328 next_landmark_id = 0;
00329 landmarks.clear();
00330 #endif
00331
00332 next_seq_id = next_vertex_id = 0;
00333 camera_vertices.clear();
00334 cam_cam_edges_.clear();
00335 odometry_edges_.clear();
00336 marker_id_ =0;
00337 ParameterServer* ps = ParameterServer::instance();
00338 createOptimizer(ps->get<std::string>("backend_solver"));
00339
00340
00341 BOOST_FOREACH(GraphNodeType entry, graph_){ delete entry.second; entry.second = NULL; }
00342
00343 graph_.clear();
00344 keyframe_ids_.clear();
00345 Q_EMIT resetGLViewer();
00346 curr_best_result_ = MatchingResult();
00347
00348 current_edges_.clear();
00349 reset_request_ = false;
00350 loop_closures_edges = 0;
00351 sequential_edges = 0;
00352 }
00353
00354
00355
00356
00357
00358
00359
00360 void GraphManager::firstNode(Node* new_node)
00361 {
00362 Q_EMIT renderableOctomap(&co_server_);
00363
00364
00365 new_node->id_ = graph_.size();
00366 new_node->seq_id_ = next_seq_id++;
00367 init_base_pose_ = new_node->getGroundTruthTransform();
00368 printTransform("Ground Truth Transform for First Node", init_base_pose_);
00369
00370 g2o::VertexSE3* reference_pose = new g2o::VertexSE3;
00371
00372 new_node->vertex_id_ = next_vertex_id++;
00373 graph_[new_node->id_] = new_node;
00374 reference_pose->setId(new_node->vertex_id_);
00375
00376 camera_vertices.insert(reference_pose);
00377
00378 ROS_INFO("Adding initial node with id %i and seq %i, v_id: %i", new_node->id_, new_node->seq_id_, new_node->vertex_id_);
00379 g2o::SE3Quat g2o_ref_se3 = tf2G2O(init_base_pose_);
00380 reference_pose->setEstimate(g2o_ref_se3);
00381 reference_pose->setFixed(true);
00382 optimizer_mutex_.lock();
00383 optimizer_->addVertex(reference_pose);
00384 optimizer_mutex_.unlock();
00385 QString message;
00386 Q_EMIT setGUIInfo(message.sprintf("Added first node with %i keypoints to the graph", (int)new_node->feature_locations_2d_.size()));
00387
00388 QMatrix4x4 latest_transform = g2o2QMatrix(g2o_ref_se3);
00389 if(!ParameterServer::instance()->get<bool>("glwidget_without_clouds")) {
00390 Q_EMIT setPointCloud(new_node->pc_col.get(), latest_transform);
00391 Q_EMIT setFeatures(&(new_node->feature_locations_3d_));
00392 }
00393
00394 this->addKeyframe(new_node->id_);
00395 if(ParameterServer::instance()->get<bool>("octomap_online_creation")) {
00396 optimizeGraph();
00397
00398
00399 }
00400
00401 process_node_runs_ = false;
00402 }
00403
00404
00405
00406
00407
00408
00409 void updateInlierFeatures(const MatchingResult& mr, Node* new_node, Node* old_node)
00410 {
00411 BOOST_FOREACH(const cv::DMatch& match, mr.inlier_matches){
00412 assert(new_node->feature_matching_stats_.size() > match.queryIdx);
00413 assert(old_node->feature_matching_stats_.size() > match.trainIdx);
00414 unsigned char& new_flag = new_node->feature_matching_stats_[match.queryIdx];
00415 if(new_flag < 255) ++new_flag;
00416 unsigned char& old_flag = old_node->feature_matching_stats_[match.trainIdx];
00417 if(old_flag < 255) ++old_flag;
00418 }
00419 }
00420
00421 bool GraphManager::nodeComparisons(Node* new_node,
00422 QMatrix4x4& curr_motion_estimate,
00423 bool& edge_to_keyframe)
00424 {
00426 ScopedTimer s(__FUNCTION__);
00427 process_node_runs_ = true;
00428
00429 ParameterServer* ps = ParameterServer::instance();
00430 int num_keypoints = (int)new_node->feature_locations_2d_.size();
00431 if (num_keypoints < ps->get<int>("min_matches") &&
00432 ! ps->get<bool>("keep_all_nodes"))
00433 {
00434 ROS_INFO("Found only %i features on image, node is not included", num_keypoints);
00435 process_node_runs_ = false;
00436 return false;
00437 }
00438
00439
00440 new_node->id_ = graph_.size();
00441 new_node->seq_id_ = next_seq_id++;
00442
00443
00444 earliest_loop_closure_node_ = new_node->id_;
00445 unsigned int num_edges_before = cam_cam_edges_.size();
00446 edge_to_keyframe = false;
00447 marker_id_ = 0;
00448 ROS_DEBUG("Graphsize: %d Nodes", (int) graph_.size());
00449
00450 int sequentially_previous_id = graph_.rbegin()->second->id_;
00451
00452 MatchingResult mr;
00453 int prev_best = mr.edge.id1;
00454 curr_best_result_ = mr;
00455
00456
00457 bool predecessor_matched = false;
00458 if(ps->get<double>("min_translation_meter") > 0.0 ||
00459 ps->get<double>("min_rotation_degree") > 0.0)
00460 {
00461
00462
00463 Node* prev_frame = graph_[graph_.size()-1];
00464 if(localization_only_ && curr_best_result_.edge.id1 > 0){ prev_frame = graph_[curr_best_result_.edge.id1]; }
00465 ROS_INFO("Comparing new node (%i) with previous node %i", new_node->id_, prev_frame->id_);
00466 mr = new_node->matchNodePair(prev_frame);
00467 ROS_INFO("Node comparison result: %s", mr.toString());
00468 if(mr.edge.id1 >= 0 && mr.edge.id2 >= 0) {
00469 ros::Time time1 = prev_frame->header_.stamp;
00470 ros::Time time2 = new_node->header_.stamp;
00471 ros::Duration delta_time = time2 - time1;
00472 if(!isBigTrafo(mr.edge.transform) || !isSmallTrafo(mr.edge.transform, delta_time.toSec())){
00473 ROS_WARN("Transformation not within bounds. Did not add as Node");
00474
00475 tf::Transform incremental = eigenTransf2TF(mr.edge.transform);
00476 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(graph_[prev_frame->id_]->vertex_id_));
00477 tf::Transform previous = eigenTransf2TF(v->estimate());
00478 tf::Transform combined = previous*incremental;
00479 latest_transform_cache_ = stampedTransformInWorldFrame(new_node, combined);
00480 printTransform("Computed new transform", latest_transform_cache_);
00481 broadcastTransform(latest_transform_cache_);
00482 process_node_runs_ = false;
00483 curr_best_result_ = mr;
00484 return false;
00485 } else {
00486 ROS_DEBUG_STREAM("Information Matrix for Edge (" << mr.edge.id1 << "<->" << mr.edge.id2 << ") \n" << mr.edge.informationMatrix);
00487 if (addEdgeToG2O(mr.edge, prev_frame, new_node, true, true, curr_motion_estimate))
00488 {
00489 graph_[new_node->id_] = new_node;
00490 if(keyframe_ids_.contains(mr.edge.id1)) edge_to_keyframe = true;
00491 #ifdef DO_FEATURE_OPTIMIZATION
00492 updateLandmarks(mr, prev_frame,new_node);
00493 #endif
00494 updateInlierFeatures(mr, new_node, prev_frame);
00495 graph_[mr.edge.id1]->valid_tf_estimate_ = true;
00496 ROS_INFO("Added Edge between %i and %i. Inliers: %i",mr.edge.id1,mr.edge.id2,(int) mr.inlier_matches.size());
00497 curr_best_result_ = mr;
00498
00499
00500 } else {
00501 ROS_INFO("Edge not added");
00502 process_node_runs_ = false;
00503 return false;
00504 }
00505 }
00506 predecessor_matched = true;
00507 }
00508 else {
00509 ROS_WARN("Found no transformation to predecessor (edge ids are negative)");
00510
00511 }
00512 }
00513
00514
00515 QList<int> vertices_to_comp;
00516 int seq_cand = localization_only_ ? 0 : ps->get<int>("predecessor_candidates") - 1;
00517 int geod_cand = ps->get<int>("neighbor_candidates");
00518 int samp_cand = ps->get<int>("min_sampled_candidates");
00519 if(predecessor_matched){
00520 vertices_to_comp = getPotentialEdgeTargetsWithDijkstra(new_node, seq_cand, geod_cand, samp_cand, curr_best_result_.edge.id1);
00521 } else {
00522 vertices_to_comp = getPotentialEdgeTargetsWithDijkstra(new_node, seq_cand, geod_cand, samp_cand, sequentially_previous_id, true);
00523 }
00524 if(prev_best >= 0 && !vertices_to_comp.contains(prev_best)){
00525 vertices_to_comp.append(prev_best);
00526 }
00527
00528 QList<const Node* > nodes_to_comp;
00529
00530
00531 if (ps->get<bool>("concurrent_edge_construction"))
00532 {
00533 std::stringstream ss;
00534 for (int id_of_id = (int) vertices_to_comp.size() - 1; id_of_id >= 0; id_of_id--) {
00535
00536
00537 nodes_to_comp.push_front(graph_[vertices_to_comp[id_of_id]]);
00538 ss << vertices_to_comp[id_of_id] << ", ";
00539 }
00540 ROS_INFO_STREAM("Nodes to compare: " << ss);
00541 QThreadPool* qtp = QThreadPool::globalInstance();
00542 ROS_INFO("Running node comparisons in parallel in %i (of %i) available threads", qtp->maxThreadCount() - qtp->activeThreadCount(), qtp->maxThreadCount());
00543 if (qtp->maxThreadCount() - qtp->activeThreadCount() == 1) {
00544
00545 ROS_WARN("Few Threads Remaining: Increasing maxThreadCount to %i", qtp->maxThreadCount()+1);
00546 qtp->setMaxThreadCount(qtp->maxThreadCount() + 1);
00547 }
00548 QList<MatchingResult> results = QtConcurrent::blockingMapped(nodes_to_comp, boost::bind(&Node::matchNodePair, new_node, _1));
00549
00550 for (int i = 0; i < results.size(); i++)
00551 {
00552 MatchingResult& mr = results[i];
00553 ROS_INFO("Result of comparison %d: %s", i, mr.toString());
00554 if (mr.edge.id1 >= 0 ) {
00555
00556 assert(graph_[mr.edge.id1]);
00557
00558 ros::Duration delta_time = new_node->header_.stamp - graph_[mr.edge.id1]->header_.stamp;
00559 if (isSmallTrafo(mr.edge.transform, delta_time.toSec()) &&
00560 addEdgeToG2O(mr.edge,graph_[mr.edge.id1],new_node, isBigTrafo(mr.edge.transform), mr.inlier_matches.size() > curr_best_result_.inlier_matches.size(), curr_motion_estimate))
00561 {
00562 graph_[new_node->id_] = new_node;
00563 if(mr.edge.id1 == mr.edge.id2-1 ) {
00564 predecessor_matched = true;
00565 }
00566
00567 #ifdef DO_FEATURE_OPTIMIZATION
00568 updateLandmarks(mr, graph_[mr.edge.id1],new_node);
00569 #endif
00570 updateInlierFeatures(mr, new_node, graph_[mr.edge.id1]);
00571 graph_[mr.edge.id1]->valid_tf_estimate_ = true;
00572 ROS_INFO("Added Edge between %i and %i. Inliers: %i",mr.edge.id1,mr.edge.id2,(int) mr.inlier_matches.size());
00573 if (mr.inlier_matches.size() > curr_best_result_.inlier_matches.size()) {
00574
00575 curr_best_result_ = mr;
00576 }
00577 if(keyframe_ids_.contains(mr.edge.id1)) edge_to_keyframe = true;
00578 }
00579 else{
00580 ROS_WARN("Rejected edge from %d to %d", mr.edge.id1, mr.edge.id2);
00581 }
00582 }
00583 }
00584 } else {
00585 for (int id_of_id = (int) vertices_to_comp.size() - 1; id_of_id >= 0; id_of_id--) {
00586 Node* node_to_compare = graph_[vertices_to_comp[id_of_id]];
00587 ROS_INFO("Comparing new node (%i) with node %i / %i", new_node->id_, vertices_to_comp[id_of_id], node_to_compare->id_);
00588 MatchingResult mr = new_node->matchNodePair(node_to_compare);
00589 ROS_INFO("Result of comparison: %s", mr.toString());
00590
00591 if (mr.edge.id1 >= 0) {
00592 ros::Duration delta_time = new_node->header_.stamp - graph_[mr.edge.id1]->header_.stamp;
00593 if (isSmallTrafo(mr.edge.transform, delta_time.toSec()) &&
00594 addEdgeToG2O(mr.edge, node_to_compare, new_node, isBigTrafo(mr.edge.transform), mr.inlier_matches.size() > curr_best_result_.inlier_matches.size(), curr_motion_estimate))
00595 {
00596 #ifdef DO_FEATURE_OPTIMIZATION
00597 updateLandmarks(mr, node_to_compare, new_node);
00598 #endif
00599 graph_[new_node->id_] = new_node;
00600 if(mr.edge.id1 == mr.edge.id2-1 ) {
00601 predecessor_matched = true;
00602 }
00603 updateInlierFeatures(mr, new_node, node_to_compare);
00604 graph_[mr.edge.id1]->valid_tf_estimate_ = true;
00605 ROS_INFO("Added Edge between %i and %i. Inliers: %i",mr.edge.id1,mr.edge.id2,(int) mr.inlier_matches.size());
00606 if (mr.inlier_matches.size() > curr_best_result_.inlier_matches.size()) {
00607
00608 curr_best_result_ = mr;
00609 }
00610 if(keyframe_ids_.contains(mr.edge.id1)) edge_to_keyframe = true;
00611 }
00612 else {
00613 ROS_INFO("Matching result rejected for being too big? Time Delta: %f", delta_time.toSec());
00614 }
00615 }
00616 else
00617 {
00618 ROS_INFO("Matching result rejected for edge.id1");
00619 }
00620 }
00621 }
00622
00623
00624 bool found_trafo = (cam_cam_edges_.size() != num_edges_before);
00625 bool valid_odometry = !ps->get<std::string>("odom_frame_name").empty();
00626
00627 bool keep_anyway = (ps->get<bool>("keep_all_nodes") ||
00628 (((int)new_node->feature_locations_3d_.size() > ps->get<int>("min_matches"))
00629 && ps->get<bool>("keep_good_nodes")));
00630 ros::Duration delta_time = new_node->header_.stamp - graph_[sequentially_previous_id]->header_.stamp;
00631 float time_delta_sec = fabs(delta_time.toSec());
00632 ROS_WARN_COND(time_delta_sec >= 0.1, "Time jump (time delta: %.2f)", time_delta_sec);
00633
00634
00635
00636 if((!found_trafo && valid_odometry) ||
00637 ((!found_trafo && keep_anyway) ||
00638 (!predecessor_matched && time_delta_sec < 0.1)))
00639 {
00640 LoadedEdge3D odom_edge;
00641
00642 odom_edge.id1 = sequentially_previous_id;
00643 odom_edge.id2 = new_node->id_;
00644 odom_edge.transform.setIdentity();
00645 curr_motion_estimate = eigenTF2QMatrix(odom_edge.transform);
00646 odom_edge.informationMatrix = Eigen::Matrix<double,6,6>::Zero();
00647 ROS_WARN("No valid (sequential) transformation between %d and %d: Using constant position assumption.", odom_edge.id1, odom_edge.id2);
00648 odom_edge.informationMatrix = Eigen::Matrix<double,6,6>::Identity() / time_delta_sec;
00649 addEdgeToG2O(odom_edge,graph_[sequentially_previous_id],new_node, true,true, curr_motion_estimate);
00650 graph_[new_node->id_] = new_node;
00651 new_node->valid_tf_estimate_ = false;
00652 MatchingResult mr;
00653 mr.edge = odom_edge;
00654 curr_best_result_ = mr;
00655 }
00656
00657 return cam_cam_edges_.size() > num_edges_before;
00658 }
00659
00660 void GraphManager::localizationUpdate(Node* new_node, QMatrix4x4 motion_estimate)
00661 {
00662
00663
00664 pointcloud_type* cloud_to_visualize = new_node->pc_col.get();
00665 std_vector_of_eigen_vector4f * features_to_visualize = &(new_node->feature_locations_3d_);
00666 if(!new_node->valid_tf_estimate_){
00667 cloud_to_visualize = new pointcloud_type();
00668 features_to_visualize = new std_vector_of_eigen_vector4f();
00669 }
00670
00671 Q_EMIT setPointCloud(cloud_to_visualize, motion_estimate);
00672 Q_EMIT setFeatures(features_to_visualize);
00673
00674 optimizeGraph();
00675 g2o::VertexSE3* new_v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(graph_[new_node->id_]->vertex_id_));
00676 QMutexLocker locker(&optimizer_mutex_);
00677 QMutexLocker locker2(&optimization_mutex_);
00678 optimizer_->removeVertex(new_v);
00679 }
00680
00681 bool GraphManager::addNode(Node* new_node)
00682 {
00683 ScopedTimer s(__FUNCTION__, false);
00684
00685 if(reset_request_) resetGraph();
00686 ParameterServer* ps = ParameterServer::instance();
00687 if (new_node->feature_locations_2d_.size() < ps->get<int>("min_matches")) {
00688 ROS_WARN("Skipping node because it has only %zu features (minimum is %d)",new_node->feature_locations_2d_.size(), ps->get<int>("min_matches"));
00689 return false;
00690 }
00691
00692
00693
00694 if (graph_.size()==0){
00695 firstNode(new_node);
00696 return true;
00697 }
00698
00699
00700 QMatrix4x4 motion_estimate;
00701 bool edge_to_last_keyframe_found = false;
00702 bool found_match = nodeComparisons(new_node, motion_estimate, edge_to_last_keyframe_found);
00703
00704 if (found_match)
00705 {
00706 if(localization_only_)
00707 {
00708 ROS_INFO("Localizing (only)");
00709 localizationUpdate(new_node, motion_estimate);
00710 }
00711 else
00712 {
00713
00714 graph_[new_node->id_] = new_node;
00715
00716
00717 pointcloud_type* cloud_to_visualize = new_node->pc_col.get();
00718 std_vector_of_eigen_vector4f * features_to_visualize = &(new_node->feature_locations_3d_);
00719 if(!new_node->valid_tf_estimate_) {
00720 cloud_to_visualize = new pointcloud_type();
00721 features_to_visualize = new std_vector_of_eigen_vector4f();
00722 }
00723 ROS_INFO("Adding node with id %i and seq id %i to the graph", new_node->id_, new_node->seq_id_);
00724
00725
00726 ROS_INFO_STREAM("create cloud " << new_node->id_ << " " << ps->get<int>("create_cloud_every_nth_node") << " " << new_node->id_%ps->get<int>("create_cloud_every_nth_node")) ;
00727 if((new_node->id_%ps->get<int>("create_cloud_every_nth_node"))!=0){
00728 new_node->clearPointCloud();
00729 }
00730
00731 if(!edge_to_last_keyframe_found && earliest_loop_closure_node_ > keyframe_ids_.back()) {
00732 this->addKeyframe(new_node->id_-1);
00733 } else {
00734 if(ps->get<bool>("visualize_keyframes_only")){
00735 cloud_to_visualize = new pointcloud_type();
00736 features_to_visualize = new std_vector_of_eigen_vector4f();
00737 }
00738 }
00739 if(ps->get<bool>("glwidget_without_clouds")){
00740 cloud_to_visualize = new pointcloud_type();
00741 features_to_visualize = new std_vector_of_eigen_vector4f();
00742 }
00743
00744 Q_EMIT setPointCloud(cloud_to_visualize, motion_estimate);
00745 Q_EMIT setFeatures(features_to_visualize);
00746 ROS_INFO("Added Node, new graphsize: %i nodes", (int) graph_.size());
00747 if(ps->get<int>("optimizer_skip_step") > 0 &&
00748 (camera_vertices.size() % ps->get<int>("optimizer_skip_step")) == 0)
00749 {
00750 optimizeGraph();
00751 }
00752
00753
00754 visualizeGraphEdges();
00755 visualizeGraphNodes();
00756 visualizeFeatureFlow3D(marker_id_++);
00757
00758 }
00759 }
00760 else
00761 {
00762 if(graph_.size() == 1){
00763 ROS_WARN("Choosing new initial node, because it has more features");
00764 if(new_node->feature_locations_2d_.size() > graph_[0]->feature_locations_2d_.size()){
00765 this->resetGraph();
00766 process_node_runs_ = false;
00767 firstNode(new_node);
00768 return true;
00769 }
00770 } else {
00771 ROS_WARN("Did not add as Node");
00772 }
00773 }
00774
00775
00776 QString message;
00777 Q_EMIT setGUIInfo(message.sprintf("%s, Camera Pose Graph Size: %iN/%iE, Duration: %f, Inliers:%5i",
00778 found_match ? "Added" : "Ignored", (int)camera_vertices.size(), (int)cam_cam_edges_.size(), s.elapsed(), (int)curr_best_result_.inlier_matches.size()));
00779 process_node_runs_ = false;
00780 ROS_INFO("%s", qPrintable(message));
00781 return found_match;
00782 }
00783
00784 void GraphManager::addKeyframe(int id)
00785 {
00786 ScopedTimer s(__FUNCTION__);
00787
00788 if(ParameterServer::instance()->get<bool>("clear_non_keyframes")){
00789 if(keyframe_ids_.size() >= 2){
00790 int most_recent= keyframe_ids_.back();
00791 int second_most_recent= keyframe_ids_.at(keyframe_ids_.size() - 2);
00792 ROS_INFO("Clearing out data for nodes between keyframes %d and %d", second_most_recent, most_recent);
00793 for (std::map<int, Node*>::iterator it=graph_.begin(); it!=graph_.end(); ++it){
00794 Node* mynode = it->second;
00795 if(mynode->id_ > second_most_recent && mynode->id_ < most_recent){
00796
00797 mynode->clearPointCloud();
00798 mynode->clearFeatureInformation();
00799 }
00800 }
00801 }
00802 }
00803
00804 keyframe_ids_.push_back(id);
00805
00806 std::stringstream ss; ss << keyframe_ids_.size() << " Keyframes: ";
00807 BOOST_FOREACH(int i, keyframe_ids_){ ss << i << ", "; }
00808 ROS_INFO("%s", ss.str().c_str());
00809 }
00810
00811 bool GraphManager::addEdgeToG2O(const LoadedEdge3D& edge,
00812 Node* n1, Node* n2,
00813 bool largeEdge, bool set_estimate,
00814 QMatrix4x4& motion_estimate)
00815 {
00816 ScopedTimer s(__FUNCTION__);
00817 assert(n1);
00818 assert(n2);
00819 assert(n1->id_ == edge.id1);
00820 assert(n2->id_ == edge.id2);
00821
00822 QMutexLocker locker(&optimizer_mutex_);
00823 g2o::VertexSE3* v1 = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(n1->vertex_id_));
00824 g2o::VertexSE3* v2 = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(n2->vertex_id_));
00825
00826
00827
00828 if (!v1 || !v2){
00829 if (!largeEdge) {
00830 ROS_INFO("Edge to new vertex is too short, vertex will not be inserted");
00831 return false;
00832 }
00833 }
00834
00835 if(!v1 && !v2){
00836 ROS_ERROR("Missing both vertices: %i, %i, cannot create edge", edge.id1, edge.id2);
00837 return false;
00838 }
00839 else if (!v1 && v2) {
00840 v1 = new g2o::VertexSE3;
00841 assert(v1);
00842 int v_id = next_vertex_id++;
00843 v1->setId(v_id);
00844
00845 n1->vertex_id_ = v_id;
00846 v1->setEstimate(v2->estimate() * edge.transform.inverse());
00847 camera_vertices.insert(v1);
00848 optimizer_->addVertex(v1);
00849 motion_estimate = eigenTF2QMatrix(v1->estimate());
00850 ROS_WARN("Creating previous id. This is unexpected by the programmer");
00851 }
00852 else if (!v2 && v1) {
00853 v2 = new g2o::VertexSE3;
00854 assert(v2);
00855 int v_id = next_vertex_id++;
00856 v2->setId(v_id);
00857 n2->vertex_id_ = v_id;
00858 v2->setEstimate(v1->estimate() * edge.transform);
00859 camera_vertices.insert(v2);
00860 optimizer_->addVertex(v2);
00861 motion_estimate = eigenTF2QMatrix(v2->estimate());
00862 }
00863 else if(set_estimate){
00864 v2->setEstimate(v1->estimate() * edge.transform);
00865 motion_estimate = eigenTF2QMatrix(v2->estimate());
00866 }
00867 g2o::EdgeSE3* g2o_edge = new g2o::EdgeSE3;
00868 g2o_edge->vertices()[0] = v1;
00869 g2o_edge->vertices()[1] = v2;
00870 Eigen::Isometry3d meancopy(edge.transform);
00871 g2o_edge->setMeasurement(meancopy);
00872
00873 g2o_edge->setRobustKernel(&robust_kernel_);
00874
00875 g2o_edge->setInformation(edge.informationMatrix);
00876 optimizer_->addEdge(g2o_edge);
00877
00878 cam_cam_edges_.insert(g2o_edge);
00879 current_match_edges_.insert(g2o_edge);
00880
00881
00882 if(abs(edge.id1 - edge.id2) > ParameterServer::instance()->get<int>("predecessor_candidates")){
00883 loop_closures_edges++;
00884 } else {
00885 sequential_edges++;
00886 }
00887 current_edges_.append( qMakePair(n1->id_, n2->id_));
00888
00889 if (ParameterServer::instance()->get<std::string>("pose_relative_to") == "inaffected") {
00890 v1->setFixed(false);
00891 v2->setFixed(false);
00892 }
00893 else if(ParameterServer::instance()->get<std::string>("pose_relative_to") == "largest_loop") {
00894 earliest_loop_closure_node_ = std::min(earliest_loop_closure_node_, edge.id1);
00895 earliest_loop_closure_node_ = std::min(earliest_loop_closure_node_, edge.id2);
00896 }
00897 return true;
00898 }
00899
00900 double GraphManager::optimizeGraph(double break_criterion, bool nonthreaded){
00901 if(ParameterServer::instance()->get<bool>("concurrent_optimization") && !nonthreaded) {
00902 ROS_DEBUG("Optimization done in Thread");
00903 QtConcurrent::run(this, &GraphManager::optimizeGraphImpl, break_criterion);
00904 return -1.0;
00905 }
00906 else {
00907 return optimizeGraphImpl(break_criterion);
00908 }
00909 }
00910
00911 void fixationOfVertices(std::string strategy,
00912 g2o::SparseOptimizer* optimizer,
00913 std::map<int, Node* >& graph,
00914 g2o::HyperGraph::VertexSet& camera_vertices,
00915 int earliest_loop_closure_node
00916 ){
00917
00918 if (strategy == "previous" && graph.size() > 2) {
00919 optimizer->setFixed(camera_vertices, false);
00920 optimizer->vertex(graph[graph.size() - 2]->vertex_id_)->setFixed(true);
00921 }
00922 else if (strategy == "largest_loop"){
00923
00924 for (std::map<int, Node*>::iterator it=graph.begin(); it!=graph.end(); ++it){
00925 Node* mynode = it->second;
00926
00927 bool is_outside_largest_loop = mynode->id_ < earliest_loop_closure_node;
00928
00929 optimizer->vertex(mynode->vertex_id_)->setFixed(is_outside_largest_loop);
00930 }
00931
00932 }
00933 else if (strategy == "first") {
00934 optimizer->setFixed(camera_vertices, false);
00935 optimizer->vertex(graph[0]->vertex_id_)->setFixed(true);
00936 }
00937 }
00938 double GraphManager::optimizeGraphImpl(double break_criterion)
00939 {
00940 ScopedTimer s(__FUNCTION__, false, true);
00941 ParameterServer* ps = ParameterServer::instance();
00942 double stop_cond = break_criterion > 0.0 ? break_criterion : ps->get<double>("optimizer_iterations");
00943 ROS_WARN_NAMED("eval", "Loop Closures: %u, Sequential Edges: %u", loop_closures_edges, sequential_edges);
00944 ROS_WARN("Starting Optimization");
00945 double chi2 = std::numeric_limits<double>::max();
00946 if(!optimization_mutex_.tryLock(2))
00947 {
00948 ROS_INFO("Attempted Graph Optimization, but it is already running. Skipping.");
00949 return -1.0;
00950 }
00951 else
00952 {
00953 optimizer_mutex_.lock();
00954 if(optimizer_->vertices().size() == 0){
00955 ROS_ERROR("Attempted Graph Optimization on an empty graph!");
00956 return -1.0;
00957 }
00958
00959 fixationOfVertices(ps->get<std::string>("pose_relative_to"),
00960 optimizer_, graph_, camera_vertices, earliest_loop_closure_node_);
00961
00962 #ifdef DO_FEATURE_OPTIMIZATION
00963 printLandmarkStatistic();
00964 if (ps->get<bool>("optimize_landmarks")){
00965 updateProjectionEdges();
00966 optimizer_->initializeOptimization(cam_lm_edges);
00967 } else
00968 #endif
00969 if (ps->get<std::string>("pose_relative_to") == "inaffected") {
00970 ScopedTimer s2("Optimizer Initialization (inaffected)");
00971 g2o::HyperDijkstra hypdij(optimizer_);
00972 g2o::UniformCostFunction cost_function;
00973 g2o::VertexSE3* new_vertex = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(graph_[graph_.size()-1]->vertex_id_));
00974 hypdij.shortestPaths(new_vertex,&cost_function,4);
00975 g2o::HyperGraph::VertexSet& vs = hypdij.visited();
00976 optimizer_->initializeOptimization(vs);
00977 }
00978 else {
00979 g2o::HyperGraph::EdgeSet edges;
00980 if (!ps->get<bool>("use_robot_odom_only")){
00981 edges.insert(cam_cam_edges_.begin(), cam_cam_edges_.end());
00982 }
00983 if (ps->get<bool>("use_robot_odom")){
00984 edges.insert(odometry_edges_.begin(), odometry_edges_.end());
00985 }
00986 optimizer_->initializeOptimization(edges);
00987 }
00988
00989 {
00990 ScopedTimer s2("Optimizer Initialization");
00991 optimizer_->initializeOptimization(cam_cam_edges_);
00992 }
00993
00994 ROS_WARN_NAMED("eval", "Optimization with %zu cams, %zu nodes and %zu edges in the graph", graph_.size(), optimizer_->vertices().size(), optimizer_->edges().size());
00995 Q_EMIT iamBusy(1, "Optimizing Graph", 0);
00996 int currentIt = 0;
00997
00998 if(stop_cond >= 1.0){
00999 do {
01000 currentIt += optimizer_->optimize(ceil(stop_cond / 10));
01001 } while(ros::ok() && currentIt < stop_cond && currentIt > 0);
01002 optimizer_->computeActiveErrors();
01003 chi2 = optimizer_->chi2();
01004 }
01005
01006 else {
01007 double prev_chi2;
01008 do {
01009 prev_chi2 = chi2;
01010 currentIt += optimizer_->optimize(5);
01011 optimizer_->computeActiveErrors();
01012 chi2 = optimizer_->chi2();
01013 } while(chi2/prev_chi2 < (1.0 - stop_cond));
01014 }
01015
01016 ROS_WARN_STREAM_NAMED("eval", "G2O Statistics: " << std::setprecision(15) << camera_vertices.size()
01017 << " cameras, " << optimizer_->edges().size() << " edges. " << chi2
01018 << " ; chi2 "<< ", Iterations: " << currentIt);
01019 optimizer_mutex_.unlock();
01020 optimization_mutex_.unlock();
01021 }
01022
01023 Q_EMIT progress(1, "Optimizing Graph", 1);
01024
01025
01026
01027
01028 ROS_INFO("A: last cam: %i", last_added_cam_vertex_id());
01029
01030 QMutexLocker locker(&optimizer_mutex_);
01031 if (ps->get<std::string>("pose_relative_to") == "inaffected") {
01032 optimizer_->setFixed(camera_vertices, true);
01033 }
01034 else {
01035 optimizer_->setFixed(camera_vertices, false);
01036 }
01037 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(last_added_cam_vertex_id()));
01038
01039 computed_motion_ = eigenTransf2TF(v->estimate());
01040 Node* newest_node = graph_[graph_.size()-1];
01041 latest_transform_cache_ = stampedTransformInWorldFrame(newest_node, computed_motion_);
01042
01043 broadcastTransform(latest_transform_cache_);
01044 if(ps->get<bool>("octomap_online_creation")) {
01045 if(updateCloudOrigin(newest_node)){
01046 renderToOctomap(newest_node);
01047
01048 }
01049 }
01050
01051 current_match_edges_.clear();
01052
01053 Q_EMIT setGraphEdges(getGraphEdges());
01054 Q_EMIT updateTransforms(getAllPosesAsMatrixList());
01055
01056 ROS_WARN_STREAM_NAMED("eval", "Optimizer Runtime; "<< s.elapsed() <<" s");
01057 #ifdef DO_LOOP_CLOSING
01058 loopClosingTest();
01059
01060
01061
01062
01063 #endif
01064 Q_EMIT setGraph(optimizer_);
01065 return chi2;
01066 }
01067
01068 bool containsVertex(g2o::HyperGraph::Edge* myedge, g2o::HyperGraph::Vertex* v_to_del)
01069 {
01070 std::vector<g2o::HyperGraph::Vertex*>& myvertices = myedge->vertices();
01071 g2o::VertexSE3 *v1, *v2;
01072 v1 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(1));
01073 v2 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(0));
01074 return (v1->id() == v_to_del->id() || v2->id() == v_to_del->id());
01075 }
01076
01077 void GraphManager::deleteCameraFrame(int id)
01078 {
01079 QMutexLocker locker(&optimizer_mutex_);
01080 QMutexLocker locker2(&optimization_mutex_);
01081
01082 g2o::VertexSE3* v_to_del = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(graph_[id]->vertex_id_));
01083
01084 #ifdef DO_FEATURE_OPTIMIZATION
01085
01086 EdgeSet::iterator edge_iter = cam_cam_edges_.begin();
01087 for(;edge_iter != cam_cam_edges_.end(); edge_iter++) {
01088 if(containsVertex(*edge_iter, v_to_del))
01089 cam_cam_edges_.erase(edge_iter);
01090 }
01091
01092 edge_iter = cam_lm_edges.begin();
01093 for(;edge_iter != cam_lm_edges.end(); edge_iter++) {
01094 if(containsVertex(*edge_iter, v_to_del))
01095 cam_cam_edges_.erase(edge_iter);
01096 }
01097 #endif
01098
01099 optimizer_->removeVertex(v_to_del);
01100 camera_vertices.erase(v_to_del);
01101 graph_.erase(id);
01102 }
01103
01106 unsigned int GraphManager::pruneEdgesWithErrorAbove(float thresh){
01107 QMutexLocker locker(&optimizer_mutex_);
01108 QMutexLocker locker2(&optimization_mutex_);
01109
01110 optimizer_->computeActiveErrors();
01111 unsigned int counter = 0;
01112
01113 #ifdef DO_FEATURE_OPTIMIZATION
01114
01115 if (ParameterServer::instance()->get<bool>("optimize_landmarks")){
01116 EdgeSet::iterator edge_iter = cam_lm_edges.begin();
01117 for(;edge_iter != cam_lm_edges.end(); edge_iter++)
01118 {
01119 g2o::EdgeSE3PointXYZDepth* myedge = dynamic_cast<g2o::EdgeSE3PointXYZDepth*>(*edge_iter);
01120 Eigen::Vector3d ev = myedge->error();
01121 if(ev.squaredNorm() > thresh){
01122 optimizer_->removeEdge(myedge);
01123 cam_lm_edges.erase(edge_iter);
01124 }
01125 }
01126 }
01127 #endif
01128
01129
01130 g2o::VertexSE3 *v1, *v2;
01131 std::map<int, int> vertex_id_to_node_id;
01132 for (graph_it it = graph_.begin(); it !=graph_.end(); ++it){
01133 Node *node = it->second;
01134 vertex_id_to_node_id[node->vertex_id_] = node->id_;
01135 }
01136
01137
01138 g2o::HyperGraph::EdgeSet remaining_cam_cam_edges, edges_to_remove;
01139 EdgeSet::iterator edge_iter = cam_cam_edges_.begin();
01140 for(;edge_iter != cam_cam_edges_.end(); edge_iter++) {
01141 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
01142 g2o::EdgeSE3::ErrorVector ev = myedge->error();
01143
01144
01145 std::vector<g2o::HyperGraph::Vertex*>& myvertices = myedge->vertices();
01146 v1 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(1));
01147 v2 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(0));
01148 int n_id1 = vertex_id_to_node_id[v1->id()];
01149 int n_id2 = vertex_id_to_node_id[v2->id()];
01150
01151 ROS_DEBUG("Mahalanobis Distance for edge from node %d to %d is %f", n_id1, n_id2, myedge->chi2());
01152 if(myedge->chi2() > thresh){
01153 counter++;
01154
01155
01156 Eigen::Quaterniond eigen_quat(1,0,0,0);
01157 Eigen::Vector3d translation(0,0,0);
01158 g2o::SE3Quat unit_tf(eigen_quat, translation);
01159 myedge->setMeasurement(unit_tf);
01160
01161 if(abs(n_id1 - n_id2) != 1)
01162 {
01163
01164 if(v1->edges().size() > 1 && v2->edges().size() > 1){
01165 ROS_INFO("Removing edge from node %d to %d, because error is %f", n_id1, n_id2, ev.squaredNorm());
01166 edges_to_remove.insert(myedge);
01167 }
01168 else {
01169 ROS_WARN("Setting edge from node %d to %d to Identity and Information to diag(1e-100), because error is %f", n_id1, n_id2, ev.squaredNorm());
01170
01171 Eigen::Matrix<double,6,6> new_info = Eigen::Matrix<double,6,6>::Identity()* 1e-100;
01172 new_info(3,3) = 1e-100;
01173 new_info(4,4) = 1e-100;
01174 new_info(5,5) = 1e-100;
01175 myedge->setInformation(new_info);
01176 }
01177 } else {
01178
01179 ROS_WARN("Setting edge from node %d to %d and Information to Identity because error is %f", n_id1, n_id2, ev.squaredNorm());
01180 Eigen::Matrix<double,6,6> new_info = Eigen::Matrix<double,6,6>::Identity();
01181 myedge->setInformation(new_info);
01182 }
01183
01184
01185
01186
01187
01188
01189
01190
01191
01192 }
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218 }
01219 EdgeSet::iterator remove_edge_iter = edges_to_remove.begin();
01220 for(;remove_edge_iter!= edges_to_remove.end();remove_edge_iter++) {
01221 cam_cam_edges_.erase(*remove_edge_iter);
01222 }
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243
01244
01245 return counter;
01246 }
01247
01248 QList<QPair<int, int> >* GraphManager::getGraphEdges()
01249 {
01250 ScopedTimer s(__FUNCTION__);
01251
01252 g2o::VertexSE3 *v1, *v2;
01253 std::map<int, int> vertex_id_to_node_id;
01254 for (graph_it it = graph_.begin(); it !=graph_.end(); ++it){
01255 Node *node = it->second;
01256 vertex_id_to_node_id[node->vertex_id_] = node->id_;
01257 }
01258 QList<QPair<int, int> >* current_edges = new QList<QPair<int, int> >();
01259 EdgeSet odom_and_feature_edges(odometry_edges_);
01260 odometry_edges_.insert(cam_cam_edges_.begin(), cam_cam_edges_.end());
01261 EdgeSet::iterator edge_iter = odom_and_feature_edges.begin();
01262 for(;edge_iter != odom_and_feature_edges.end(); edge_iter++) {
01263 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
01264 if(myedge){
01265 std::vector<g2o::HyperGraph::Vertex*>& myvertices = myedge->vertices();
01266 v1 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(1));
01267 v2 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(0));
01268 } else {
01269 ROS_WARN("Unexpected edge type in getGraphEdges()");
01270 continue;
01271 }
01272
01273 if(vertex_id_to_node_id.find(v1->id()) == vertex_id_to_node_id.end()){
01274 ROS_WARN("Vertex ID %d does not match any Node ", v1->id());
01275 }
01276 if(vertex_id_to_node_id.find(v2->id()) == vertex_id_to_node_id.end()){
01277 ROS_WARN("Vertex ID %d does not match any Node ", v2->id());
01278 }
01279 std::map<int, int>::iterator node_id1_it = vertex_id_to_node_id.find(v1->id());
01280 if (node_id1_it == vertex_id_to_node_id.end()) continue;
01281 std::map<int, int>::iterator node_id2_it = vertex_id_to_node_id.find(v2->id());
01282 if (node_id2_it == vertex_id_to_node_id.end()) continue;
01283 current_edges->append( qMakePair(node_id1_it->second, node_id2_it->second));
01284 }
01285 return current_edges;
01286 }
01287
01288 QList<QMatrix4x4>* GraphManager::getAllPosesAsMatrixList() const{
01289 ScopedTimer s(__FUNCTION__);
01290
01291 ROS_DEBUG("Retrieving all transformations from optimizer");
01292 QList<QMatrix4x4>* current_poses = new QList<QMatrix4x4>();
01293
01294 #if defined(QT_VERSION) && QT_VERSION >= 0x040700
01295 current_poses->reserve(camera_vertices.size()+10);
01296 #endif
01297
01298 for (std::map<int, Node* >::const_iterator it = graph_.begin(); it !=graph_.end(); ++it){
01299 const Node *node = it->second;
01300 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex( node->vertex_id_));
01301 if(v){
01302 current_poses->push_back(eigenTF2QMatrix(v->estimate()));
01303 } else {
01304 ROS_ERROR("Nullpointer in graph at position %i!", it->first);
01305 }
01306 }
01307 return current_poses;
01308 }
01309
01310 void GraphManager::reducePointCloud(pointcloud_type const * pc) {
01311 double vfs = ParameterServer::instance()->get<double>("voxelfilter_size");
01312 BOOST_REVERSE_FOREACH(GraphNodeType entry, graph_){
01313 if(entry.second->pc_col.get() == pc){
01314 entry.second->reducePointCloud(vfs);
01315 ROS_INFO("Reduced PointCloud after rendering to openGL list.");
01316 return;
01317 }
01318 }
01319 }
01320
01321 void GraphManager::printEdgeErrors(QString filename){
01322 QMutexLocker locker(&optimizer_mutex_);
01323 std::fstream filestr;
01324 filestr.open (qPrintable(filename), std::fstream::out );
01325
01326 EdgeSet::iterator edge_iter = cam_cam_edges_.begin();
01327 for(int i =0;edge_iter != cam_cam_edges_.end(); edge_iter++, i++) {
01328 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
01329 g2o::EdgeSE3::ErrorVector ev = myedge->error();
01330 ROS_INFO_STREAM("Error Norm for edge " << i << ": " << ev.squaredNorm());
01331 filestr << "Error for edge " << i << ": " << ev.squaredNorm() << std::endl;
01332 }
01333 filestr.close();
01334 }
01335
01336 double GraphManager::geodesicDiscount(g2o::HyperDijkstra& hypdij, const MatchingResult& mr){
01337
01338 const g2o::HyperDijkstra::AdjacencyMap am = hypdij.adjacencyMap();
01339
01340 g2o::VertexSE3* older_vertex = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex( nodeId2VertexId(mr.edge.id1) ));
01341 double discount_factor = am.at(older_vertex).distance();
01342 discount_factor = discount_factor > 0.0? 1.0/discount_factor : 1.0;
01343 ROS_INFO("Discount weight for connection to Node %i = %f", mr.edge.id1, discount_factor);
01344 return discount_factor;
01345 }
01346
01347 void GraphManager::sanityCheck(float thresh){
01348 thresh *=thresh;
01349 QMutexLocker locker(&optimizer_mutex_);
01350 QMutexLocker locker2(&optimization_mutex_);
01351 EdgeSet::iterator edge_iter = cam_cam_edges_.begin();
01352 for(int i =0;edge_iter != cam_cam_edges_.end(); edge_iter++, i++) {
01353 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
01354 Eigen::Vector3d ev = myedge->measurement().translation();
01355 if(ev.squaredNorm() > thresh){
01356
01357 myedge->setInformation(Eigen::Matrix<double,6,6>::Identity()* 0.000001);
01358 }
01359 }
01360 }
01361
01362 void GraphManager::filterNodesByPosition(float x, float y, float z)
01363 {
01364 ROS_WARN("filtering Nodes");
01365 Eigen::Vector3f center(x,y,z);
01366 float radius = 0.5;
01367 for (graph_it it = graph_.begin(); it !=graph_.end(); ++it){
01368 Node *node = it->second;
01369 Node* clone = node->copy_filtered(center, radius);
01370 }
01371 }
01372 void GraphManager::occupancyFilterClouds(){
01373 Q_EMIT iamBusy(0, "Filtering Clouds by Occupancy", graph_.size());
01374 BOOST_FOREACH(GraphNodeType entry, graph_){
01375 ROS_INFO("Filtering points of Node %d", entry.first);
01376 co_server_.occupancyFilter(entry.second->pc_col, entry.second->pc_col, ParameterServer::instance()->get<double>("occupancy_filter_threshold"));
01377 Q_EMIT progress(0, "Filtering Clouds by Occupancy", entry.first);
01378 }
01379 Q_EMIT progress(0, "Filtering Clouds by Occupancy", 1e6);
01380 ROS_INFO("Done Filtering, display will not be updated!");
01381 }