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 #include "pcl_ros/transforms.h"
00025 #include "pcl/io/pcd_io.h"
00026 #include <sensor_msgs/PointCloud2.h>
00027 #include <opencv2/features2d/features2d.hpp>
00028 #include <QThread>
00029 #include <qtconcurrentrun.h>
00030 #include <QtConcurrentMap>
00031 #include <QFile>
00032 #include <utility>
00033 #include <fstream>
00034 #include <limits>
00035 #include <boost/foreach.hpp>
00036 #include <pcl_ros/point_cloud.h>
00037
00038 #include "g2o/math_groups/se3quat.h"
00039 #include "g2o/types/slam3d/edge_se3_quat.h"
00040
00041 #include "g2o/core/block_solver.h"
00042 #include "g2o/solvers/csparse/linear_solver_csparse.h"
00043 #include "g2o/solvers/cholmod/linear_solver_cholmod.h"
00044 #include "g2o/solvers/pcg/linear_solver_pcg.h"
00045
00046
00047 typedef g2o::BlockSolver< g2o::BlockSolverTraits<6, 3> > SlamBlockSolver;
00048 typedef g2o::LinearSolverCSparse<SlamBlockSolver::PoseMatrixType> SlamLinearCSparseSolver;
00049 typedef g2o::LinearSolverCholmod<SlamBlockSolver::PoseMatrixType> SlamLinearCholmodSolver;
00050 typedef g2o::LinearSolverPCG<SlamBlockSolver::PoseMatrixType> SlamLinearPCGSolver;
00051
00052 typedef std::tr1::unordered_map<int, g2o::HyperGraph::Vertex*> VertexIDMap;
00053 typedef std::pair<int, g2o::HyperGraph::Vertex*> VertexIDPair;
00054
00055 typedef std::set<g2o::HyperGraph::Edge*> EdgeSet;
00056
00057
00058 GraphManager::GraphManager(ros::NodeHandle nh) :
00059 optimizer_(NULL),
00060 latest_transform_(),
00061 reset_request_(false),
00062 marker_id_(0),
00063 last_matching_node_(-1),
00064 batch_processing_runs_(false),
00065 process_node_runs_(false),
00066 localization_only_(false),
00067 someone_is_waiting_for_me_(false),
00068 loop_closures_edges(0), sequential_edges(0),
00069 current_backend_("none")
00070 {
00071 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
00072
00073 ParameterServer* ps = ParameterServer::instance();
00074 createOptimizer(ps->get<std::string>("backend_solver"));
00075
00076 batch_cloud_pub_ = nh.advertise<pointcloud_type>(ps->get<std::string>("individual_cloud_out_topic"),
00077 ps->get<int>("publisher_queue_size"));
00078 whole_cloud_pub_ = nh.advertise<pointcloud_type>(ps->get<std::string>("aggregate_cloud_out_topic"),
00079 ps->get<int>("publisher_queue_size"));
00080 ransac_marker_pub_ = nh.advertise<visualization_msgs::Marker>("/rgbdslam/correspondence_marker",
00081 ps->get<int>("publisher_queue_size"));
00082 marker_pub_ = nh.advertise<visualization_msgs::Marker>("/rgbdslam/pose_graph_markers",
00083 ps->get<int>("publisher_queue_size"));
00084 computed_motion_ = tf::Transform::getIdentity();
00085 init_base_pose_ = tf::Transform::getIdentity();
00086 base2points_ = tf::Transform::getIdentity();
00087
00088 Max_Depth = -1;
00089
00090 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
00091 }
00092
00093 void GraphManager::createOptimizer(std::string backend, g2o::SparseOptimizer* optimizer)
00094 {
00095 QMutexLocker locker(&optimizer_mutex);
00096
00097 if(optimizer == NULL){
00098 delete optimizer_;
00099 optimizer_ = new g2o::SparseOptimizer();
00100 optimizer_->setVerbose(true);
00101 } else if (optimizer_ != optimizer){
00102 delete optimizer_;
00103 optimizer_ = new g2o::SparseOptimizer();
00104 optimizer_->setVerbose(true);
00105 }
00106 SlamBlockSolver* solver = NULL;
00107 if(backend == "cholmod" || backend == "auto"){
00108 SlamLinearCholmodSolver* linearSolver = new SlamLinearCholmodSolver();
00109 linearSolver->setBlockOrdering(false);
00110 solver = new SlamBlockSolver(optimizer_, linearSolver);
00111 current_backend_ = "cholmod";
00112 }
00113 else if(backend == "csparse"){
00114 SlamLinearCSparseSolver* linearSolver = new SlamLinearCSparseSolver();
00115 linearSolver->setBlockOrdering(false);
00116 solver = new SlamBlockSolver(optimizer_, linearSolver);
00117 current_backend_ = "csparse";
00118 }
00119 else if(backend == "pcg"){
00120 SlamLinearPCGSolver* linearSolver = new SlamLinearPCGSolver();
00121 solver = new SlamBlockSolver(optimizer_, linearSolver);
00122 current_backend_ = "pcg";
00123 }
00124 else {
00125 ROS_ERROR("Bad Parameter for g2o Solver backend: %s. User cholmod, csparse or pcg", backend.c_str());
00126 ROS_INFO("Falling Back to Cholmod Solver");
00127 SlamLinearCholmodSolver* linearSolver = new SlamLinearCholmodSolver();
00128 linearSolver->setBlockOrdering(false);
00129 solver = new SlamBlockSolver(optimizer_, linearSolver);
00130 current_backend_ = "cholmod";
00131 }
00132 optimizer_->setSolver(solver);
00133 }
00134
00135
00136 void GraphManager::deleteFeatureInformation() {
00137 ROS_WARN("Clearing out Feature information from nodes");
00138
00139 BOOST_FOREACH(GraphNodeType entry, graph_){
00140 entry.second->clearFeatureInformation();
00141 }
00142 }
00143
00144 GraphManager::~GraphManager() {
00145
00146
00147
00148 BOOST_FOREACH(GraphNodeType entry, graph_) {
00149 delete entry.second;
00150 }
00151 graph_.clear();
00152 delete (optimizer_);
00153 ransac_marker_pub_.shutdown();
00154 whole_cloud_pub_.shutdown();
00155 marker_pub_.shutdown();
00156 batch_cloud_pub_.shutdown();
00157
00158 }
00159
00160 void GraphManager::drawFeatureFlow(cv::Mat& canvas, cv::Scalar line_color,
00161 cv::Scalar circle_color){
00162 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
00163 if(!ParameterServer::instance()->get<bool>("use_gui")){ return; }
00164 ROS_DEBUG("Number of features to draw: %d", (int)last_inlier_matches_.size());
00165
00166 const double pi_fourth = 3.14159265358979323846 / 4.0;
00167 const int line_thickness = 1;
00168 const int circle_radius = 6;
00169 const int CV_AA = 16;
00170 if(graph_.empty()) {
00171 ROS_WARN("Feature Flow for empty graph requested. Bug?");
00172 return;
00173 } else if(graph_.size() == 1 || last_matching_node_ == -1 ) {
00174 Node* newernode = graph_[graph_.size()-1];
00175 cv::drawKeypoints(canvas, newernode->feature_locations_2d_, canvas, cv::Scalar(255), 5);
00176 return;
00177 }
00178
00179 Node* earliernode = graph_[last_matching_node_];
00180 Node* newernode = graph_[graph_.size()-1];
00181 if(earliernode == NULL){
00182 if(newernode == NULL ){ ROS_ERROR("Nullpointer for Node %u", (unsigned int)graph_.size()-1); }
00183 ROS_ERROR("Nullpointer for Node %d", last_matching_node_);
00184 last_matching_node_ = 0;
00185 return;
00186 } else if(newernode == NULL ){
00187 ROS_ERROR("Nullpointer for Node %u", (unsigned int)graph_.size()-1);
00188 return;
00189 }
00190
00191
00192
00193
00194
00195
00196
00197 cv::Mat tmpimage = cv::Mat::zeros(canvas.rows, canvas.cols, canvas.type());
00198 cv::drawKeypoints(canvas, newernode->feature_locations_2d_, tmpimage, circle_color, 5);
00199 canvas+=tmpimage;
00200 for(unsigned int mtch = 0; mtch < last_inlier_matches_.size(); mtch++) {
00201 cv::Point2f p,q;
00202 unsigned int newer_idx = last_inlier_matches_[mtch].queryIdx;
00203 unsigned int earlier_idx = last_inlier_matches_[mtch].trainIdx;
00204 q = newernode->feature_locations_2d_[newer_idx].pt;
00205 p = earliernode->feature_locations_2d_[earlier_idx].pt;
00206
00207 double angle; angle = atan2( (double) p.y - q.y, (double) p.x - q.x );
00208 double hypotenuse = cv::norm(p-q);
00209 cv::line(canvas, p, q, line_color, line_thickness, CV_AA);
00210 if(hypotenuse > 1.5){
00211 cv::line( canvas, p, q, line_color, line_thickness, CV_AA );
00212
00213 p.x = (q.x + 4 * cos(angle + pi_fourth));
00214 p.y = (q.y + 4 * sin(angle + pi_fourth));
00215 cv::line( canvas, p, q, line_color, line_thickness, CV_AA );
00216 p.x = (q.x + 4 * cos(angle - pi_fourth));
00217 p.y = (q.y + 4 * sin(angle - pi_fourth));
00218 cv::line( canvas, p, q, line_color, line_thickness, CV_AA );
00219 } else {
00220 cv::circle(canvas, p, circle_radius-2, circle_color, line_thickness, CV_AA);
00221 }
00222 }
00223 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
00224 }
00225
00226
00227 QList<int> GraphManager::getPotentialEdgeTargetsWithDijkstra(const Node* new_node, int sequential_targets, int geodesic_targets, int sampled_targets, int predecessor_id)
00228 {
00229 QList<int> ids_to_link_to;
00230 if(predecessor_id < 0) predecessor_id = graph_.size()-1;
00231
00232 std::stringstream ss;
00233 ss << "Node ID's to compare with candidate for node " << graph_.size() << ". Sequential: ";
00234
00235 if((int)optimizer_->vertices().size() <= sequential_targets+geodesic_targets+sampled_targets ||
00236 optimizer_->vertices().size() <= 1)
00237 {
00238 sequential_targets = sequential_targets+geodesic_targets+sampled_targets;
00239 geodesic_targets = 0;
00240 sampled_targets = 0;
00241 predecessor_id = graph_.size()-1;
00242 }
00243
00244 if(sequential_targets > 0){
00245
00246 for (int i=1; i < sequential_targets+1 && predecessor_id-i >= 0; i++) {
00247 ids_to_link_to.push_back(predecessor_id-i);
00248 ss << ids_to_link_to.back() << ", " ;
00249 }
00250 }
00251
00252 if(geodesic_targets > 0){
00253 g2o::HyperDijkstra hypdij(optimizer_);
00254 g2o::UniformCostFunction cost_function;
00255 g2o::VertexSE3* prev_vertex = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(predecessor_id));
00256 hypdij.shortestPaths(prev_vertex,&cost_function,3);
00257 g2o::HyperGraph::VertexSet& vs = hypdij.visited();
00258
00259
00260
00261 std::map<int,int> neighbour_indices;
00262 int sum_of_weights=0;
00263 for (g2o::HyperGraph::VertexSet::iterator vit=vs.begin(); vit!=vs.end(); vit++) {
00264 int id = (*vit)->id();
00265 if(id < predecessor_id-sequential_targets || (id > predecessor_id && id <= (int)graph_.size()-1)){
00266 int weight = abs(predecessor_id-id);
00267 neighbour_indices[id] = weight;
00268 sum_of_weights += weight;
00269 }
00270 }
00271
00272
00273 ss << "Dijkstra: ";
00274 while(ids_to_link_to.size() < sequential_targets+geodesic_targets && neighbour_indices.size() != 0){
00275 int random_pick = rand() % sum_of_weights;
00276 ROS_DEBUG("Pick: %d/%d", random_pick, sum_of_weights);
00277 int weight_so_far = 0;
00278 for(std::map<int,int>::iterator map_it = neighbour_indices.begin(); map_it != neighbour_indices.end(); map_it++ ){
00279 weight_so_far += map_it->second;
00280 ROS_DEBUG("Checking: %d, %d, %d", map_it->first, map_it-> second, weight_so_far);
00281 if(weight_so_far > random_pick){
00282 int sampled_id = map_it->first;
00283 ids_to_link_to.push_front(sampled_id);
00284 ss << ids_to_link_to.front() << ", " ;
00285 sum_of_weights -= map_it->second;
00286 ROS_DEBUG("Taking ID: %d, decreasing sum of weights to %d", map_it->first, sum_of_weights);
00287 neighbour_indices.erase(map_it);
00288 ROS_ERROR_COND(sum_of_weights<0, "Sum of weights should never be zero");
00289 break;
00290 }
00291 ROS_DEBUG("Skipping ID: %d", map_it->first);
00292 }
00293 }
00294 }
00295
00296 if(sampled_targets > 0){
00297 ss << "Random Sampling: ";
00298 std::vector<int> non_neighbour_indices;
00299 non_neighbour_indices.reserve(graph_.size());
00300 for (QList<int>::iterator it = keyframe_ids_.begin(); it != keyframe_ids_.end(); it++){
00301 if(ids_to_link_to.contains(*it) == 0){
00302 non_neighbour_indices.push_back(*it);
00303 }
00304 }
00305
00306
00307 while(ids_to_link_to.size() < geodesic_targets+sampled_targets+sequential_targets && non_neighbour_indices.size() != 0){
00308 int index_of_v_id = rand() % non_neighbour_indices.size();
00309 int sampled_id = non_neighbour_indices[index_of_v_id];
00310 non_neighbour_indices[index_of_v_id] = non_neighbour_indices.back();
00311 non_neighbour_indices.resize(non_neighbour_indices.size()-1);
00312 ids_to_link_to.push_front(sampled_id);
00313 ss << ids_to_link_to.front() << ", " ;
00314 }
00315 }
00316
00317 ROS_INFO("%s", ss.str().c_str());
00318 return ids_to_link_to;
00319 }
00320
00321 QList<int> GraphManager::getPotentialEdgeTargets(const Node* new_node, int last_targets, int sample_targets)
00322 {
00323 int gsize = graph_.size();
00324 ROS_ERROR_COND(gsize == 0, "Do not call this function as long as the graph is empty");
00325 QList<int> ids_to_link_to;
00326
00327
00328 if(last_targets + sample_targets <= 0) {
00329 return ids_to_link_to;
00330 }
00331
00332
00333 if(gsize <= sample_targets+last_targets){
00334 last_targets = gsize;
00335 sample_targets = 0;
00336 }
00337 for(int i = 2; i <= gsize && i <= last_targets; i++){
00338 ids_to_link_to.push_back(gsize-i);
00339 }
00340
00341
00342 while(ids_to_link_to.size() < sample_targets+last_targets && ids_to_link_to.size() < gsize-1){
00343 int sample_id = rand() % (gsize - 1);
00344 ROS_DEBUG_STREAM("Sample: " << sample_id << " Graph size: " << gsize << " ids_to_link_to.size: " << ids_to_link_to.size());
00345 QList<int>::const_iterator i1 = qFind(ids_to_link_to, sample_id);
00346 if(i1 != ids_to_link_to.end())
00347 continue;
00348 ids_to_link_to.push_back(sample_id);
00349 }
00350
00351
00352
00353 std::stringstream ss;
00354 ss << "Node ID's to compare with candidate for node " << graph_.size() << ":";
00355 for(int i = 0; i < (int)ids_to_link_to.size(); i++){
00356 ss << ids_to_link_to[i] << ", " ;
00357 }
00358 ROS_INFO("%s", ss.str().c_str());
00359 return ids_to_link_to;
00360 }
00361
00362 void GraphManager::resetGraph(){
00363 marker_id_ =0;
00364 ParameterServer* ps = ParameterServer::instance();
00365 createOptimizer(ps->get<std::string>("backend_solver"));
00366
00367
00368 BOOST_FOREACH(GraphNodeType entry, graph_){ delete entry.second; }
00369
00370 graph_.clear();
00371 keyframe_ids_.clear();
00372 Q_EMIT resetGLViewer();
00373 last_matching_node_ = -1;
00374 latest_transform_.setToIdentity();
00375 current_poses_.clear();
00376 current_edges_.clear();
00377 reset_request_ = false;
00378 loop_closures_edges = 0;
00379 sequential_edges = 0;
00380 }
00381
00382
00383
00384
00385
00386
00387
00388
00389 void GraphManager::firstNode(Node* new_node)
00390 {
00391 init_base_pose_ = new_node->getGroundTruthTransform();
00392 printTransform("Ground Truth Transform for First Node", init_base_pose_);
00393 new_node->buildFlannIndex();
00394 graph_[new_node->id_] = new_node;
00395 g2o::VertexSE3* reference_pose = new g2o::VertexSE3;
00396 reference_pose->setId(0);
00397 g2o::SE3Quat g2o_ref_se3 = tf2G2O(init_base_pose_);
00398 reference_pose->setEstimate(g2o_ref_se3);
00399 reference_pose->setFixed(true);
00400 optimizer_mutex.lock();
00401 optimizer_->addVertex(reference_pose);
00402 optimizer_mutex.unlock();
00403 QString message;
00404 Q_EMIT setGUIInfo(message.sprintf("Added first node with %i keypoints to the graph", (int)new_node->feature_locations_2d_.size()));
00405
00406 latest_transform_ = g2o2QMatrix(g2o_ref_se3);
00407 printQMatrix4x4("Latest Transform", latest_transform_);
00408 Q_EMIT setPointCloud(new_node->pc_col.get(), latest_transform_);
00409 current_poses_.append(latest_transform_);
00410 ROS_DEBUG("GraphManager is thread %d, New Node is at (%p, %p)", (unsigned int)QThread::currentThreadId(), new_node, graph_[0]);
00411 keyframe_ids_.push_back(new_node->id_);
00412
00413 process_node_runs_ = false;
00414 }
00415
00416
00417 bool GraphManager::addNode(Node* new_node) {
00419 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
00420 process_node_runs_ = true;
00421
00422 last_inlier_matches_.clear();
00423 if(reset_request_) resetGraph();
00424 ParameterServer* ps = ParameterServer::instance();
00425 if ((int)new_node->feature_locations_2d_.size() < ps->get<int>("min_matches") &&
00426 ! ps->get<bool>("keep_all_nodes"))
00427 {
00428 ROS_DEBUG("found only %i features on image, node is not included",(int)new_node->feature_locations_2d_.size());
00429 process_node_runs_ = false;
00430 return false;
00431 }
00432
00433
00434
00435 new_node->id_ = graph_.size();
00436
00437
00438
00439 if (graph_.size()==0){
00440 firstNode(new_node);
00441 return true;
00442 }
00443
00444 unsigned int num_edges_before = optimizer_->edges().size();
00445 bool edge_to_keyframe = false;
00446
00447 ROS_DEBUG("Graphsize: %d Nodes", (int) graph_.size());
00448 marker_id_ = 0;
00449
00450
00451
00452
00453
00454
00455
00456 int sequentially_previous_id = graph_.rbegin()->second->id_;
00457 tf::StampedTransform odom_tf_new = new_node->getOdomTransform();
00458 tf::StampedTransform odom_tf_old = graph_[sequentially_previous_id]->getOdomTransform();
00459 tf::Transform odom_delta_tf = odom_tf_new * odom_tf_old.inverse();
00460 printTransform("Odometry Delta", odom_delta_tf);
00461 if(odom_tf_old.frame_id_ == "missing_odometry" || odom_tf_new.frame_id_ == "missing_odometry"){
00462 ROS_WARN("No Valid Odometry, using identity");
00463 odom_delta_tf = tf::Transform::getIdentity();
00464 }
00465
00466
00467
00468
00469
00470 Node* prev_frame = graph_[graph_.size()-1];
00471 if(localization_only_ && last_matching_node_ > 0){ prev_frame = graph_[last_matching_node_]; }
00472 last_matching_node_ = -1;
00473 ROS_INFO("Comparing new node (%i) with previous node %i", new_node->id_, prev_frame->id_);
00474 MatchingResult mr = new_node->matchNodePair(prev_frame);
00475 if(mr.edge.id1 >= 0 && !isBigTrafo(mr.edge.mean)){
00476 ROS_WARN("Transformation not relevant. Did not add as Node");
00477
00478 tf::Transform incremental = g2o2TF(mr.edge.mean);
00479 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(prev_frame->id_));
00480 tf::Transform previous = g2o2TF(v->estimate());
00481 tf::Transform combined = previous*incremental;
00482 broadcastTransform(new_node, combined);
00483 process_node_runs_ = false;
00484 return false;
00485 } else if(mr.edge.id1 >= 0){
00486
00487
00488 ROS_DEBUG_STREAM("Information Matrix for Edge (" << mr.edge.id1 << "<->" << mr.edge.id2 << "\n" << mr.edge.informationMatrix);
00489
00490 if (addEdgeToG2O(mr.edge, true, true)) {
00491 if(keyframe_ids_.contains(mr.edge.id1)) edge_to_keyframe = true;
00492 ROS_INFO("Added Edge between %i and %i. Inliers: %i",mr.edge.id1,mr.edge.id2,(int) mr.inlier_matches.size());
00493 last_matching_node_ = mr.edge.id1;
00494 last_inlier_matches_ = mr.inlier_matches;
00495 last_matches_ = mr.all_matches;
00496 edge_to_previous_node_ = mr.edge.mean;
00497
00498
00499 } else {
00500 process_node_runs_ = false;
00501 return false;
00502 }
00503 }
00504
00505 QList<int> vertices_to_comp;
00506 int seq_cand = localization_only_ ? 0 : ps->get<int>("predecessor_candidates");
00507 int geod_cand = ps->get<int>("neighbor_candidates");
00508 int samp_cand = ps->get<int>("min_sampled_candidates");
00509 vertices_to_comp = getPotentialEdgeTargetsWithDijkstra(new_node, seq_cand, geod_cand, samp_cand, last_matching_node_);
00510 QList<const Node* > nodes_to_comp;
00511
00512
00513 if (ps->get<bool>("concurrent_edge_construction")) {
00514 for (int id_of_id = (int) vertices_to_comp.size() - 1; id_of_id >= 0; id_of_id--) {
00515
00516
00517 nodes_to_comp.push_front(graph_[vertices_to_comp[id_of_id]]);
00518 }
00519 QThreadPool* qtp = QThreadPool::globalInstance();
00520 ROS_INFO("Running node comparisons in parallel in %i (of %i) available threads", qtp->maxThreadCount() - qtp->activeThreadCount(), qtp->maxThreadCount());
00521 if (qtp->maxThreadCount() - qtp->activeThreadCount() == 1) {
00522 ROS_WARN("Few Threads Remaining: Increasing maxThreadCount to %i", qtp->maxThreadCount()+1);
00523 qtp->setMaxThreadCount(qtp->maxThreadCount() + 1);
00524 }
00525 QList<MatchingResult> results = QtConcurrent::blockingMapped(nodes_to_comp, boost::bind(&Node::matchNodePair, new_node, _1));
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539 for (int i = 0; i < results.size(); i++) {
00540 MatchingResult& mr = results[i];
00541
00542 if (mr.edge.id1 >= 0) {
00543
00544 ROS_INFO_STREAM("Information Matrix for Edge (" << mr.edge.id1 << "<->" << mr.edge.id2 << "\n" << mr.edge.informationMatrix);
00545
00546 if (addEdgeToG2O(mr.edge, isBigTrafo(mr.edge.mean), mr.inlier_matches.size() > last_inlier_matches_.size()))
00547 {
00548 ROS_INFO("Added Edge between %i and %i. Inliers: %i",mr.edge.id1,mr.edge.id2,(int) mr.inlier_matches.size());
00549 if (mr.inlier_matches.size() > last_inlier_matches_.size()) {
00550 last_matching_node_ = mr.edge.id1;
00551 last_inlier_matches_ = mr.inlier_matches;
00552 last_matches_ = mr.all_matches;
00553
00554 }
00555 if(keyframe_ids_.contains(mr.edge.id1)) edge_to_keyframe = true;
00556 }
00557 }
00558 }
00559 } else {
00560 for (int id_of_id = (int) vertices_to_comp.size() - 1; id_of_id >= 0; id_of_id--) {
00561 Node* node_to_compare = graph_[vertices_to_comp[id_of_id]];
00562 ROS_INFO("Comparing new node (%i) with node %i / %i", new_node->id_, vertices_to_comp[id_of_id], node_to_compare->id_);
00563 MatchingResult mr = new_node->matchNodePair(node_to_compare);
00564
00565 if (mr.edge.id1 >= 0) {
00566
00567 ROS_INFO_STREAM("Information Matrix for Edge (" << mr.edge.id1 << "<->" << mr.edge.id2 << "\n" << mr.edge.informationMatrix);
00568
00569 if (addEdgeToG2O(mr.edge, isBigTrafo(mr.edge.mean), mr.inlier_matches.size() > last_inlier_matches_.size()))
00570 {
00571 ROS_INFO("Added Edge between %i and %i. Inliers: %i",mr.edge.id1,mr.edge.id2,(int) mr.inlier_matches.size());
00572 if (mr.inlier_matches.size() > last_inlier_matches_.size()) {
00573 last_matching_node_ = mr.edge.id1;
00574 last_inlier_matches_ = mr.inlier_matches;
00575 last_matches_ = mr.all_matches;
00576
00577 }
00578 if(keyframe_ids_.contains(mr.edge.id1)) edge_to_keyframe = true;
00579 }
00580 }
00581 }
00582 }
00583
00584
00585 bool found_trafo = (optimizer_->edges().size() != num_edges_before);
00586
00587 if((found_trafo
00588 && !ps->get<std::string>("odom_frame_name").empty()
00589 && !(odom_tf_old.frame_id_ == "missing_odometry"
00590 || odom_tf_new.frame_id_ == "missing_odometry"))
00591 || (!found_trafo && ps->get<bool>("keep_all_nodes")))
00592 {
00593 ROS_INFO("Adding odometry motion edge for Node %i (if available, otherwise using identity)", (int)graph_.rbegin()->second->id_);
00594 LoadedEdge3D odom_edge;
00595 odom_edge.id2 = sequentially_previous_id;
00596 odom_edge.id1 = new_node->id_;
00597 odom_edge.mean = tf2G2O(odom_delta_tf);
00598 if(!found_trafo){
00599 latest_transform_ = g2o2QMatrix(odom_edge.mean);
00600 last_matching_node_ = odom_edge.id1;
00601 odom_edge.informationMatrix = Eigen::Matrix<double,6,6>::Identity()*(1);
00602 }
00603 else {
00604
00605 odom_edge.informationMatrix = Eigen::Matrix<double,6,6>::Identity()*(10);
00606 odom_edge.informationMatrix(1,1) = 100000000000000;
00607 odom_edge.informationMatrix(3,3) = 100000000000000;
00608 odom_edge.informationMatrix(5,5) = 100000000000000;
00609 }
00610 addEdgeToG2O(odom_edge, true,true) ;
00611 }
00612
00613
00614
00615 if (optimizer_->edges().size() > num_edges_before) {
00616 if(localization_only_)
00617 {
00618 optimizeGraph();
00619 broadcastTransform(new_node, computed_motion_);
00620 g2o::VertexSE3* new_v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(new_node->id_));
00621 optimizer_->removeVertex(new_v);
00622 }
00623 else
00624 {
00625 new_node->buildFlannIndex();
00626 graph_[new_node->id_] = new_node;
00627 if(!edge_to_keyframe) {
00628 std::stringstream ss;
00629 ss << "Keyframes: ";
00630 BOOST_FOREACH(int id, keyframe_ids_){ ss << id << ", "; }
00631 ROS_INFO("%s", ss.str().c_str());
00632 keyframe_ids_.push_back(new_node->id_-1);
00633 }
00634 ROS_INFO("Added Node, new graphsize: %i nodes", (int) graph_.size());
00635 if((optimizer_->vertices().size() % ps->get<int>("optimizer_skip_step")) == 0){
00636 optimizeGraph();
00637 } else {
00638 current_poses_.append(latest_transform_);
00639 current_edges_.append( qMakePair((int)new_node->id_, last_matching_node_));
00640
00641
00642 }
00643
00644
00645
00646 broadcastTransform(new_node, computed_motion_);
00647 visualizeGraphEdges();
00648 visualizeGraphNodes();
00649 visualizeFeatureFlow3D(marker_id_++);
00650
00651
00652
00653
00654 pointcloud_type* cloud_to_visualize = new_node->pc_col.get();
00655
00656 Q_EMIT setPointCloud(cloud_to_visualize, latest_transform_);
00657 }
00658 }else{
00659 if(graph_.size() == 1){
00660 ROS_WARN("Choosing new initial node, because it has more features");
00661 if(new_node->feature_locations_2d_.size() > graph_[0]->feature_locations_2d_.size()){
00662 this->resetGraph();
00663 process_node_runs_ = false;
00664 return this->addNode(new_node);
00665 }
00666 } else {
00667 ROS_WARN("Did not add as Node");
00668 }
00669 }
00670 QString message;
00671 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
00672 Q_EMIT setGUIInfo(message.sprintf("%s, Graph Size: %iN/%iE, Duration: %f, Inliers: %i, χ<sup>2</sup>: %f",
00673 (optimizer_->edges().size() > num_edges_before) ? "Added" : "Ignored",
00674 (int)optimizer_->vertices().size(), (int)optimizer_->edges().size(),
00675 elapsed, (int)last_inlier_matches_.size(), optimizer_->chi2()));
00676 process_node_runs_ = false;
00677 someone_is_waiting_for_me_ = false;
00678 return (optimizer_->edges().size() > num_edges_before);
00679 }
00680
00681
00682 void GraphManager::visualizeFeatureFlow3D(unsigned int marker_id, bool draw_outlier) const
00683 {
00684 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
00685 if (ransac_marker_pub_.getNumSubscribers() > 0){
00686
00687 visualization_msgs::Marker marker_lines;
00688
00689 marker_lines.header.frame_id = "/camera_rgb_optical_frame";
00690 marker_lines.ns = "ransac_markers";
00691 marker_lines.header.stamp = ros::Time::now();
00692 marker_lines.action = visualization_msgs::Marker::ADD;
00693 marker_lines.pose.orientation.w = 1.0;
00694 marker_lines.id = marker_id;
00695 marker_lines.type = visualization_msgs::Marker::LINE_LIST;
00696 marker_lines.scale.x = 0.002;
00697
00698 std_msgs::ColorRGBA color_red ;
00699 color_red.r = 1.0;
00700 color_red.a = 1.0;
00701 std_msgs::ColorRGBA color_green;
00702 color_green.g = 1.0;
00703 color_green.a = 1.0;
00704 std_msgs::ColorRGBA color_yellow;
00705 color_yellow.r = 1.0;
00706 color_yellow.g = 1.0;
00707 color_yellow.a = 1.0;
00708 std_msgs::ColorRGBA color_blue ;
00709 color_blue.b = 1.0;
00710 color_blue.a = 1.0;
00711
00712 marker_lines.color = color_green;
00713 const g2o::VertexSE3* earlier_v;
00714 const g2o::VertexSE3* newer_v;
00715 VertexIDMap v_idmap = optimizer_->vertices();
00716
00717 ROS_DEBUG("Matches Visualization start: %zu Matches, %zu Inliers", last_matches_.size(), last_inlier_matches_.size());
00718
00719
00720 marker_lines.points.clear();
00721
00722 if (draw_outlier)
00723 {
00724 for (unsigned int i=0;i<last_matches_.size(); i++){
00725 int newer_id = last_matches_.at(i).queryIdx;
00726 int earlier_id = last_matches_.at(i).trainIdx;
00727
00728 earlier_v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(last_matching_node_));
00729 newer_v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(graph_.size()-1));
00730
00731
00732 marker_lines.colors.push_back(color_red);
00733 marker_lines.colors.push_back(color_blue);
00734
00735 Node* last = graph_.find(graph_.size()-1)->second;
00736 marker_lines.points.push_back(
00737 pointInWorldFrame(last->feature_locations_3d_[newer_id], newer_v->estimate()));
00738 Node* prev = graph_.find(last_matching_node_)->second;
00739 marker_lines.points.push_back(
00740 pointInWorldFrame(prev->feature_locations_3d_[earlier_id], earlier_v->estimate()));
00741 }
00742 }
00743
00744 for (unsigned int i=0;i<last_inlier_matches_.size(); i++){
00745 int newer_id = last_inlier_matches_.at(i).queryIdx;
00746 int earlier_id = last_inlier_matches_.at(i).trainIdx;
00747
00748 earlier_v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(last_matching_node_));
00749 newer_v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(graph_.size()-1));
00750
00751
00752
00753 marker_lines.colors.push_back(color_green);
00754 marker_lines.colors.push_back(color_blue);
00755
00756 Node* last = graph_.find(graph_.size()-1)->second;
00757 marker_lines.points.push_back(
00758 pointInWorldFrame(last->feature_locations_3d_[newer_id], newer_v->estimate()));
00759 Node* prev = graph_.find(last_matching_node_)->second;
00760 marker_lines.points.push_back(
00761 pointInWorldFrame(prev->feature_locations_3d_[earlier_id], earlier_v->estimate()));
00762 }
00763
00764 ransac_marker_pub_.publish(marker_lines);
00765 ROS_DEBUG_STREAM("Published " << marker_lines.points.size()/2 << " lines");
00766 }
00767 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
00768 }
00769
00770
00771 void GraphManager::visualizeGraphEdges() const {
00772 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
00773
00774 if (marker_pub_.getNumSubscribers() > 0){
00775 visualization_msgs::Marker edges_marker;
00776 edges_marker.header.frame_id = "/camera_rgb_optical_frame";
00777 edges_marker.header.stamp = ros::Time::now();
00778 edges_marker.ns = "camera_pose_graph";
00779 edges_marker.id = 0;
00780
00781 edges_marker.type = visualization_msgs::Marker::LINE_LIST;
00782 edges_marker.action = visualization_msgs::Marker::ADD;
00783 edges_marker.frame_locked = true;
00784
00785 edges_marker.scale.x = 0.005;
00786
00787 edges_marker.pose.position.x = 0;
00788 edges_marker.pose.position.y = 0;
00789 edges_marker.pose.position.z = 0;
00790 edges_marker.pose.orientation.x = 0.0;
00791 edges_marker.pose.orientation.y = 0.0;
00792 edges_marker.pose.orientation.z = 0.0;
00793 edges_marker.pose.orientation.w = 1.0;
00794
00795 edges_marker.color.r = 1.0f;
00796 edges_marker.color.g = 1.0f;
00797 edges_marker.color.b = 1.0f;
00798 edges_marker.color.a = 0.5f;
00799 geometry_msgs::Point point;
00800 g2o::VertexSE3* v1,* v2;
00801 EdgeSet::iterator edge_iter = optimizer_->edges().begin();
00802 int counter = 0;
00803 for(;edge_iter != optimizer_->edges().end(); edge_iter++, counter++) {
00804 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
00805 std::vector<g2o::HyperGraph::Vertex*>& myvertices = myedge->vertices();
00806 v1 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(1));
00807 v2 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(0));
00808
00809 point.x = v1->estimate().translation().x();
00810 point.y = v1->estimate().translation().y();
00811 point.z = v1->estimate().translation().z();
00812 edges_marker.points.push_back(point);
00813
00814 point.x = v2->estimate().translation().x();
00815 point.y = v2->estimate().translation().y();
00816 point.z = v2->estimate().translation().z();
00817 edges_marker.points.push_back(point);
00818 }
00819
00820 marker_pub_.publish (edges_marker);
00821 ROS_INFO("published %d graph edges", counter);
00822 }
00823
00824 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
00825 }
00826
00827 void GraphManager::visualizeGraphNodes() const {
00828 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
00829
00830 if (marker_pub_.getNumSubscribers() > 0){
00831 visualization_msgs::Marker nodes_marker;
00832 nodes_marker.header.frame_id = "/camera_rgb_optical_frame";
00833 nodes_marker.header.stamp = ros::Time::now();
00834 nodes_marker.ns = "camera_pose_graph";
00835 nodes_marker.id = 1;
00836
00837
00838 nodes_marker.type = visualization_msgs::Marker::LINE_LIST;
00839 nodes_marker.action = visualization_msgs::Marker::ADD;
00840 nodes_marker.frame_locked = true;
00841
00842 nodes_marker.scale.x = 0.002;
00843
00844 nodes_marker.pose.position.x = 0;
00845 nodes_marker.pose.position.y = 0;
00846 nodes_marker.pose.position.z = 0;
00847 nodes_marker.pose.orientation.x = 0.0;
00848 nodes_marker.pose.orientation.y = 0.0;
00849 nodes_marker.pose.orientation.z = 0.0;
00850 nodes_marker.pose.orientation.w = 1.0;
00851
00852 nodes_marker.color.r = 1.0f;
00853 nodes_marker.color.g = 0.0f;
00854 nodes_marker.color.b = 0.0f;
00855 nodes_marker.color.a = 1.0f;
00856
00857
00858 geometry_msgs::Point tail;
00859 geometry_msgs::Point tip;
00860 std_msgs::ColorRGBA arrow_color_red ;
00861 arrow_color_red.r = 1.0;
00862 arrow_color_red.a = 1.0;
00863 std_msgs::ColorRGBA arrow_color_green;
00864 arrow_color_green.g = 1.0;
00865 arrow_color_green.a = 1.0;
00866 std_msgs::ColorRGBA arrow_color_blue ;
00867 arrow_color_blue.b = 1.0;
00868 arrow_color_blue.a = 1.0;
00869 Eigen::Vector3d origin(0.0,0.0,0.0);
00870 Eigen::Vector3d x_axis(0.2,0.0,0.0);
00871 Eigen::Vector3d y_axis(0.0,0.2,0.0);
00872 Eigen::Vector3d z_axis(0.0,0.0,0.2);
00873 Eigen::Vector3d tmp;
00874 int counter = 0;
00875 g2o::VertexSE3* v;
00876 VertexIDMap::iterator vertex_iter = optimizer_->vertices().begin();
00877 for(; vertex_iter != optimizer_->vertices().end(); vertex_iter++, counter++) {
00878 v = dynamic_cast<g2o::VertexSE3* >((*vertex_iter).second);
00879
00880 tmp = v->estimate() * origin;
00881 tail.x = tmp.x();
00882 tail.y = tmp.y();
00883 tail.z = tmp.z();
00884
00885 nodes_marker.points.push_back(tail);
00886 nodes_marker.colors.push_back(arrow_color_red);
00887 tmp = v->estimate() * x_axis;
00888 tip.x = tmp.x();
00889 tip.y = tmp.y();
00890 tip.z = tmp.z();
00891 nodes_marker.points.push_back(tip);
00892 nodes_marker.colors.push_back(arrow_color_red);
00893
00894 nodes_marker.points.push_back(tail);
00895 nodes_marker.colors.push_back(arrow_color_green);
00896 tmp = v->estimate() * y_axis;
00897 tip.x = tmp.x();
00898 tip.y = tmp.y();
00899 tip.z = tmp.z();
00900 nodes_marker.points.push_back(tip);
00901 nodes_marker.colors.push_back(arrow_color_green);
00902
00903 nodes_marker.points.push_back(tail);
00904 nodes_marker.colors.push_back(arrow_color_blue);
00905 tmp = v->estimate() * z_axis;
00906 tip.x = tmp.x();
00907 tip.y = tmp.y();
00908 tip.z = tmp.z();
00909 nodes_marker.points.push_back(tip);
00910 nodes_marker.colors.push_back(arrow_color_blue);
00911
00912 x_axis.x() = 0.1;
00913 y_axis.y() = 0.1;
00914 z_axis.z() = 0.1;
00915 }
00916
00917 marker_pub_.publish (nodes_marker);
00918 ROS_INFO("published %d graph nodes", counter);
00919 }
00920
00921 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
00922 }
00923
00924 bool GraphManager::addEdgeToG2O(const LoadedEdge3D& edge, bool largeEdge, bool set_estimate) {
00925 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
00926
00927 QMutexLocker locker(&optimizer_mutex);
00928 g2o::VertexSE3* v1 = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(edge.id1));
00929 g2o::VertexSE3* v2 = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(edge.id2));
00930
00931
00932
00933 if (!v1 || !v2){
00934 if (!largeEdge) {
00935 ROS_INFO("Edge to new vertex is to short, vertex will not be inserted");
00936 return false;
00937 }
00938 }
00939
00940 if(!v1 && !v2){
00941 ROS_ERROR("Missing both vertices: %i, %i, cannot create edge", edge.id1, edge.id2);
00942 return false;
00943 }
00944 else if (!v1 && v2) {
00945 v1 = new g2o::VertexSE3;
00946 assert(v1);
00947 v1->setId(edge.id1);
00948 v1->setEstimate(v2->estimate() * edge.mean.inverse());
00949 optimizer_->addVertex(v1);
00950 latest_transform_ = g2o2QMatrix(v1->estimate());
00951 }
00952 else if (!v2 && v1) {
00953 v2 = new g2o::VertexSE3;
00954 assert(v2);
00955 v2->setId(edge.id2);
00956 v2->setEstimate(v1->estimate() * edge.mean);
00957 optimizer_->addVertex(v2);
00958 latest_transform_ = g2o2QMatrix(v2->estimate());
00959 }
00960 else if(set_estimate){
00961 v2->setEstimate(v1->estimate() * edge.mean);
00962 }
00963 g2o::EdgeSE3* g2o_edge = new g2o::EdgeSE3;
00964 g2o_edge->vertices()[0] = v1;
00965 g2o_edge->vertices()[1] = v2;
00966 g2o_edge->setMeasurement(edge.mean);
00967 g2o_edge->setInverseMeasurement(edge.mean.inverse());
00968 g2o_edge->setInformation(edge.informationMatrix);
00969 optimizer_->addEdge(g2o_edge);
00970
00971 if(abs(edge.id1 - edge.id2) > ParameterServer::instance()->get<int>("predecessor_candidates")){
00972 loop_closures_edges++;
00973 } else {
00974 sequential_edges++;
00975 }
00976
00977 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
00978 return true;
00979 }
00980
00981 void GraphManager::optimizeGraph(int max_iter, bool nonthreaded){
00982 if(ParameterServer::instance()->get<bool>("concurrent_optimization") && !nonthreaded) {
00983 ROS_DEBUG("Optimization done in Thread");
00984 QtConcurrent::run(this, &GraphManager::optimizeGraphImpl, max_iter);
00985 }
00986 else {
00987 optimizeGraphImpl(max_iter);
00988 }
00989 }
00990
00991 void GraphManager::optimizeGraphImpl(int max_iter)
00992 {
00993 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
00994 int iterations = max_iter >= 0 ? max_iter : ParameterServer::instance()->get<int>("optimizer_iterations");
00995 QMutexLocker locker(&optimizer_mutex);
00996
00997 ROS_WARN_NAMED("eval", "Loop Closures: %u, Sequential Edges: %u", loop_closures_edges, sequential_edges);
00998 if(ParameterServer::instance()->get<std::string>("backend_solver") == "auto"){
00999 if(static_cast<float>(sequential_edges)/static_cast<float>(loop_closures_edges) < 1.0){
01000 ROS_WARN_NAMED("eval", "Using PCG as solver");
01001 if(current_backend_ != "pcg") createOptimizer("pcg", optimizer_);
01002 } else {
01003 ROS_WARN_NAMED("eval", "Using CHOLMOD as solver");
01004 if(current_backend_ != "cholmod") createOptimizer("cholmod", optimizer_);
01005 }
01006 }
01007
01008 ROS_WARN("Starting Optimization");
01009 std::string bagfile_name = ParameterServer::instance()->get<std::string>("bagfile_name");
01010 optimizer_->save((bagfile_name + "_g2o-optimizer-save-file-before").c_str());
01011 optimizer_->initializeOptimization();
01012 double prev_chi2 = std::numeric_limits<double>::max();
01013 int i = 0;
01014 for(; i < iterations; i++){
01015 int currentIt = optimizer_->optimize(1);
01016 optimizer_->computeActiveErrors();
01017 ROS_WARN_STREAM_NAMED("eval", "G2O Statistics: " << std::setprecision(15) << optimizer_->vertices().size() << " nodes, "
01018 << optimizer_->edges().size() << " edges. "
01019 << optimizer_->chi2() << " ; chi2 "<< ", Iterations: " << currentIt);
01020 if(!(prev_chi2 - optimizer_->chi2() > optimizer_->chi2()*0.0001)){
01021 ROS_INFO("G2O Converged after %d iterations", i);
01022 break;
01023 }
01024 prev_chi2 = optimizer_->chi2();
01025 }
01026 ROS_INFO("Finished G2O optimization after %d iterations", i);
01027 optimizer_->save((bagfile_name + "_g2o-optimizer-save-file-after").c_str());
01028
01029 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(optimizer_->vertices().size()-1));
01030
01031 computed_motion_ = g2o2TF(v->estimate());
01032 latest_transform_ = g2o2QMatrix(v->estimate());
01033 Q_EMIT setGraphEdges(getGraphEdges());
01034 Q_EMIT updateTransforms(getAllPosesAsMatrixList());
01035
01036 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
01037 ROS_WARN_STREAM_NAMED("eval", "Optimizer Runtime; "<< elapsed <<" s");
01038 }
01039
01040
01063 void GraphManager::reset(){
01064 reset_request_ = true;
01065 }
01066
01067 void GraphManager::deleteLastFrame(){
01068 if(graph_.size() <= 1) {
01069 ROS_INFO("Resetting, as the only node is to be deleted");
01070 reset_request_ = true;
01071 Q_EMIT deleteLastNode();
01072 return;
01073 }
01074 optimizer_mutex.lock();
01075 g2o::VertexSE3* v_to_del = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(optimizer_->vertices().size()-1));
01076 g2o::VertexSE3 *v1, *v2;
01077 EdgeSet::iterator edge_iter = optimizer_->edges().begin();
01078 for(;edge_iter != optimizer_->edges().end(); edge_iter++) {
01079 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
01080 std::vector<g2o::HyperGraph::Vertex*>& myvertices = myedge->vertices();
01081 v1 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(1));
01082 v2 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(0));
01083 if(v1->id() == v_to_del->id() || v2->id() == v_to_del->id())
01084 optimizer_->removeEdge((*edge_iter));
01085 }
01086
01087 optimizer_->removeVertex(v_to_del);
01088 graph_.erase(graph_.size()-1);
01089 optimizer_mutex.unlock();
01090 Q_EMIT deleteLastNode();
01091 optimizeGraph();
01092 ROS_INFO("Removed most recent node");
01093 Q_EMIT setGUIInfo("Removed most recent node");
01094
01095
01096
01097 }
01098
01099 QList<QPair<int, int> >* GraphManager::getGraphEdges()
01100 {
01101 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
01102
01103 current_edges_.clear();
01104 g2o::VertexSE3 *v1, *v2;
01105 EdgeSet::iterator edge_iter = optimizer_->edges().begin();
01106 for(;edge_iter != optimizer_->edges().end(); edge_iter++) {
01107 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
01108 std::vector<g2o::HyperGraph::Vertex*>& myvertices = myedge->vertices();
01109 v1 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(1));
01110 v2 = dynamic_cast<g2o::VertexSE3*>(myvertices.at(0));
01111 current_edges_.append( qMakePair(v1->id(), v2->id()));
01112 }
01113 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
01114 return new QList<QPair<int, int> >(current_edges_);
01115 }
01116
01117 QList<QMatrix4x4>* GraphManager::getAllPosesAsMatrixList(){
01118 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
01119 ROS_DEBUG("Retrieving all transformations from optimizer");
01120
01121 current_poses_.clear();
01122 #if defined(QT_VERSION) && QT_VERSION >= 0x040700
01123 current_poses_.reserve(optimizer_->vertices().size());
01124 #endif
01125
01126 for (unsigned int i = 0; i < optimizer_->vertices().size(); ++i) {
01127 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(i));
01128 if(v){
01129 current_poses_.push_back(g2o2QMatrix(v->estimate()));
01130 } else {
01131 ROS_ERROR("Nullpointer in graph at position %i!", i);
01132 }
01133 }
01134 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
01135 return new QList<QMatrix4x4>(current_poses_);
01136 }
01137
01138 void GraphManager::saveAllClouds(QString filename, bool threaded){
01139 if (ParameterServer::instance()->get<bool>("concurrent_edge_construction") && threaded) {
01140 QFuture<void> f1 = QtConcurrent::run(this, &GraphManager::saveAllCloudsToFile, filename);
01141
01142 }
01143 else {
01144 saveAllCloudsToFile(filename);
01145 }
01146 }
01147
01148 void GraphManager::saveIndividualClouds(QString filename, bool threaded){
01149 if (ParameterServer::instance()->get<bool>("concurrent_edge_construction") && threaded) {
01150 QFuture<void> f1 = QtConcurrent::run(this, &GraphManager::saveIndividualCloudsToFile, filename);
01151
01152 }
01153 else {
01154 saveIndividualCloudsToFile(filename);
01155 }
01156 }
01157
01158 void GraphManager::saveIndividualCloudsToFile(QString file_basename)
01159 {
01160 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
01161 ROS_INFO("Saving all clouds to %sxxxx.pcd", qPrintable(file_basename));
01162 std::string gt = ParameterServer::instance()->get<std::string>("ground_truth_frame_name");
01163 ROS_INFO_COND(!gt.empty(), "Saving all clouds with ground truth sensor position to gt_%sxxxx.pcd", qPrintable(file_basename));
01164
01165 batch_processing_runs_ = true;
01166 tf::Transform world2base;
01167 QString message, filename;
01168 std::string fixed_frame_id = ParameterServer::instance()->get<std::string>("fixed_frame_name");
01169 for (unsigned int i = 0; i < optimizer_->vertices().size(); ++i) {
01170 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(i));
01171 if(!v){
01172 ROS_ERROR("Nullpointer in graph at position %i!", i);
01173 continue;
01174 }
01175 if(graph_[i]->pc_col->size() == 0){
01176 ROS_INFO("Skipping Node %i, point cloud data is empty!", i);
01177 continue;
01178 }
01179
01180
01181
01182
01183
01184
01185
01186 tf::Transform pose = g2o2TF(v->estimate());
01187 tf::StampedTransform base2points = graph_[i]->getBase2PointsTransform();
01188 world2base = init_base_pose_*base2points*pose*base2points.inverse();
01189
01190 Eigen::Vector4f sensor_origin(world2base.getOrigin().x(),world2base.getOrigin().y(),world2base.getOrigin().z(),world2base.getOrigin().w());
01191 Eigen::Quaternionf sensor_orientation(world2base.getRotation().w(),world2base.getRotation().x(),world2base.getRotation().y(),world2base.getRotation().z());
01192
01193 graph_[i]->pc_col->sensor_origin_ = sensor_origin;
01194 graph_[i]->pc_col->sensor_orientation_ = sensor_orientation;
01195 graph_[i]->pc_col->header.frame_id = fixed_frame_id;
01196
01197 filename.sprintf("%s_%04d.pcd", qPrintable(file_basename), i);
01198 Q_EMIT setGUIStatus(message.sprintf("Saving to %s: Transformed Node %i/%i", qPrintable(filename), i, (int)optimizer_->vertices().size()));
01199 pcl::io::savePCDFile(qPrintable(filename), *(graph_[i]->pc_col), true);
01200
01201 if(!gt.empty()){
01202 tf::StampedTransform gt_world2base = graph_[i ]->getGroundTruthTransform();
01203 if( gt_world2base.frame_id_ == "/missing_ground_truth" ){
01204 ROS_WARN_STREAM("Skipping ground truth: " << gt_world2base.child_frame_id_ << " child/parent " << gt_world2base.frame_id_);
01205 continue;
01206 }
01207 Eigen::Vector4f sensor_origin(gt_world2base.getOrigin().x(),gt_world2base.getOrigin().y(),gt_world2base.getOrigin().z(),gt_world2base.getOrigin().w());
01208 Eigen::Quaternionf sensor_orientation(gt_world2base.getRotation().w(),gt_world2base.getRotation().x(),gt_world2base.getRotation().y(),gt_world2base.getRotation().z());
01209
01210 graph_[i]->pc_col->sensor_origin_ = sensor_origin;
01211 graph_[i]->pc_col->sensor_orientation_ = sensor_orientation;
01212 graph_[i]->pc_col->header.frame_id = fixed_frame_id;
01213
01214 filename.sprintf("%s_%04d_gt.pcd", qPrintable(file_basename), i);
01215 Q_EMIT setGUIStatus(message.sprintf("Saving to %s: Transformed Node %i/%i", qPrintable(filename), i, (int)optimizer_->vertices().size()));
01216 pcl::io::savePCDFile(qPrintable(filename), *(graph_[i]->pc_col), true);
01217 }
01218
01219 }
01220 Q_EMIT setGUIStatus("Saved all point clouds");
01221 ROS_INFO ("Saved all points clouds to %sxxxx.pcd", qPrintable(file_basename));
01222 batch_processing_runs_ = false;
01223 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
01224 }
01225
01226 void GraphManager::saveAllFeatures(QString filename, bool threaded)
01227 {
01228 if (ParameterServer::instance()->get<bool>("concurrent_edge_construction") && threaded) {
01229 QFuture<void> f1 = QtConcurrent::run(this, &GraphManager::saveAllFeaturesToFile, filename);
01230
01231 }
01232 else {
01233 saveAllFeaturesToFile(filename);
01234 }
01235 }
01236 void GraphManager::saveAllFeaturesToFile(QString filename)
01237 {
01238 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
01239 cv::FileStorage fs(qPrintable(filename), cv::FileStorage::WRITE);
01240 fs << "Feature_Locations" << "[";
01241
01242 ROS_INFO("Saving all features to %s transformed to a common coordinate frame.", qPrintable(filename));
01243 batch_processing_runs_ = true;
01244 tf::Transform world2rgb, cam2rgb;
01245 QString message;
01246 cam2rgb.setRotation(tf::createQuaternionFromRPY(-1.57,0,-1.57));
01247 cam2rgb.setOrigin(tf::Point(0,-0.04,0));
01248 int feat_count = 0;
01249 for (unsigned int i = 0; i < optimizer_->vertices().size(); ++i) {
01250 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(i));
01251 tf::Transform world2cam = g2o2TF(v->estimate());
01252 world2rgb = cam2rgb*world2cam;
01253 Eigen::Matrix4f world2rgbMat;
01254 pcl_ros::transformAsMatrix(world2rgb, world2rgbMat);
01255 BOOST_FOREACH(Eigen::Vector4f loc, graph_[i]->feature_locations_3d_)
01256 {
01257 loc.w() = 1.0;
01258 Eigen::Vector4f new_loc = world2rgbMat * loc;
01259 fs << "{:" << "x" << new_loc.x() << "y" << new_loc.y() << "z" << new_loc.z() << "}";
01260 feat_count++;
01261 }
01262 Q_EMIT setGUIStatus(message.sprintf("Saving to %s: Features of Node %i/%i", qPrintable(filename), i, (int)optimizer_->vertices().size()));
01263 }
01264 fs << "]";
01265
01266 int descriptor_size = graph_[0]->feature_descriptors_.cols;
01267 int descriptor_type = graph_[0]->feature_descriptors_.type();
01268 cv::Mat alldescriptors(0, descriptor_size, descriptor_type);
01269 alldescriptors.reserve(feat_count);
01270 for (unsigned int i = 0; i < graph_.size(); ++i) {
01271 alldescriptors.push_back(graph_[i]->feature_descriptors_);
01272 }
01273 fs << "Feature_Descriptors" << alldescriptors;
01274 fs.release();
01275
01276 Q_EMIT setGUIStatus(message.sprintf("Saved %d features points to %s", feat_count, qPrintable(filename)));
01277 ROS_INFO ("Saved %d feature points to %s", feat_count, qPrintable(filename));
01278 batch_processing_runs_ = false;
01279 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
01280 }
01281
01282
01283
01284
01285 void GraphManager::saveAllCloudsToFile(QString filename){
01286 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
01287 pointcloud_type aggregate_cloud;
01288 ROS_INFO("Saving all clouds to %s, this may take a while as they need to be transformed to a common coordinate frame.", qPrintable(filename));
01289 batch_processing_runs_ = true;
01290 tf::Transform world2cam;
01291
01292
01293 QString message;
01294
01295 tf::StampedTransform base2points = graph_[0]->getBase2PointsTransform();
01296 for (unsigned int i = 0; i < optimizer_->vertices().size(); ++i) {
01297 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(i));
01298 if(!v){
01299 ROS_ERROR("Nullpointer in graph at position %i!", i);
01300 continue;
01301 }
01302 tf::Transform transform = g2o2TF(v->estimate());
01303 world2cam = base2points*transform;
01304 transformAndAppendPointCloud (*(graph_[i]->pc_col), aggregate_cloud, world2cam, Max_Depth);
01305
01306 if(ParameterServer::instance()->get<bool>("batch_processing"))
01307 graph_[i]->clearPointCloud();
01308 Q_EMIT setGUIStatus(message.sprintf("Saving to %s: Transformed Node %i/%i", qPrintable(filename), i, (int)optimizer_->vertices().size()));
01309 }
01310
01311 aggregate_cloud.header.frame_id = base2points.frame_id_;
01312
01313 if(filename.endsWith(".ply", Qt::CaseInsensitive))
01314 pointCloud2MeshFile(filename, aggregate_cloud);
01315 if(filename.endsWith(".pcd", Qt::CaseInsensitive))
01316 pcl::io::savePCDFile(qPrintable(filename), aggregate_cloud, true);
01317 else {
01318 ROS_WARN("Filename misses correct extension (.pcd or .ply) using .pcd");
01319 filename.append(".pcd");
01320 pcl::io::savePCDFile(qPrintable(filename), aggregate_cloud, true);
01321 }
01322 Q_EMIT setGUIStatus(message.sprintf("Saved %d data points to %s", (int)aggregate_cloud.points.size(), qPrintable(filename)));
01323 ROS_INFO ("Saved %d data points to %s", (int)aggregate_cloud.points.size(), qPrintable(filename));
01324
01325 if (whole_cloud_pub_.getNumSubscribers() > 0){
01326 aggregate_cloud.header.stamp = ros::Time::now();
01327 whole_cloud_pub_.publish(aggregate_cloud);
01328 ROS_INFO("Aggregate pointcloud sent");
01329 }
01330 batch_processing_runs_ = false;
01331 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
01332 }
01333
01334 void GraphManager::pointCloud2MeshFile(QString filename, pointcloud_type full_cloud){
01335 QFile file(filename);
01336 if(!file.open(QIODevice::WriteOnly|QIODevice::Text)){
01337 ROS_ERROR("Could not open file %s", qPrintable(filename));
01338 return;
01339 }
01340 QTextStream out(&file);
01341 out << "ply\n";
01342 out << "format ascii 1.0\n";
01343 out << "element vertex " << (int)full_cloud.points.size() << "\n";
01344 out << "property float x\n";
01345 out << "property float y\n";
01346 out << "property float z\n";
01347 out << "property uchar red\n";
01348 out << "property uchar green\n";
01349 out << "property uchar blue\n";
01350 out << "end_header\n";
01351 unsigned char r,g,b;
01352 float x, y, z ;
01353 for(unsigned int i = 0; i < full_cloud.points.size() ; i++){
01354 b = *( (unsigned char*)&(full_cloud.points[i].rgb));
01355 g = *(1+(unsigned char*)&(full_cloud.points[i].rgb));
01356 r = *(2+(unsigned char*)&(full_cloud.points[i].rgb));
01357 x = full_cloud.points[i].x;
01358 y = full_cloud.points[i].y;
01359 z = full_cloud.points[i].z;
01360 out << qSetFieldWidth(8) << x << " " << y << " " << z << " ";
01361 out << qSetFieldWidth(3) << r << " " << g << " " << b << "\n";
01362 }
01363 }
01364
01365
01366 void GraphManager::saveTrajectory(QString filebasename, bool with_ground_truth){
01367 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
01368 if(graph_.size() == 0){
01369 ROS_ERROR("Graph is empty, no trajectory can be saved");
01370 return;
01371 }
01372 ROS_INFO("Logging Trajectory");
01373 QMutexLocker locker(&optimizer_mutex);
01374
01375
01376 std::string gt = ParameterServer::instance()->get<std::string>("ground_truth_frame_name");
01377
01378 ROS_INFO("Comparison of relative motion with ground truth");
01379 QString gtt_fname("_ground_truth.txt");
01380 QFile gtt_file(gtt_fname.prepend(filebasename));
01381 if(!gtt_file.open(QIODevice::WriteOnly|QIODevice::Text)) return;
01382 QTextStream gtt_out(>t_file);
01383 tf::StampedTransform b2p = graph_[0]->getGroundTruthTransform();
01384 gtt_out.setRealNumberNotation(QTextStream::FixedNotation);
01385 gtt_out << "# TF Coordinate Frame ID: " << b2p.frame_id_.c_str() << "(data: " << b2p.child_frame_id_.c_str() << ")\n";
01386
01387
01388 QString et_fname("_estimate.txt");
01389 QFile et_file (et_fname.prepend(filebasename));
01390 if(!et_file.open(QIODevice::WriteOnly|QIODevice::Text)) return;
01391 QTextStream et_out(&et_file);
01392 et_out.setRealNumberNotation(QTextStream::FixedNotation);
01393 b2p = graph_[0]->getBase2PointsTransform();
01394 et_out << "# TF Coordinate Frame ID: " << b2p.frame_id_.c_str() << "(data: " << b2p.child_frame_id_.c_str() << ")\n";
01395
01396 for (unsigned int i = 0; i < optimizer_->vertices().size(); ++i) {
01397 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(i));
01398 ROS_ERROR_COND(!v, "Nullpointer in graph at position %i!", i);
01399
01400 tf::Transform pose = g2o2TF(v->estimate());
01401
01402 tf::StampedTransform base2points = graph_[i]->getBase2PointsTransform();
01403 tf::Transform world2base = init_base_pose_*base2points*pose*base2points.inverse();
01404
01405 logTransform(et_out, world2base, graph_[i]->pc_col->header.stamp.toSec());
01406
01407
01408 if(with_ground_truth && !gt.empty()){
01409 tf::StampedTransform gt_world2base = graph_[i ]->getGroundTruthTransform();
01410 if( gt_world2base.frame_id_ == "/missing_ground_truth" ){
01411 ROS_WARN_STREAM("Skipping ground truth: " << gt_world2base.child_frame_id_ << " child/parent " << gt_world2base.frame_id_);
01412 continue;
01413 }
01414 logTransform(gtt_out, gt_world2base, gt_world2base.stamp_.toSec());
01415
01416 }
01417 }
01418 ROS_INFO_COND(!gt.empty() && with_ground_truth, "Written logfiles ground_truth_trajectory.txt and estimated_trajectory.txt");
01419 ROS_INFO_COND(gt.empty(), "Written logfile estimated_trajectory.txt");
01420 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
01421 }
01422
01423 void GraphManager::broadcastTransform(Node* node, tf::Transform& computed_motion){
01424 std::string fixed_frame = ParameterServer::instance()->get<std::string>("fixed_frame_name");
01425 std::string base_frame = ParameterServer::instance()->get<std::string>("base_frame_name");
01426
01427
01428
01429
01430
01431
01432
01433
01434 const tf::StampedTransform& base2points = node->getBase2PointsTransform();
01435
01436
01437 tf::Transform world2base = init_base_pose_*base2points*computed_motion*base2points.inverse();
01438 printTransform("World->Base", world2base);
01439
01440 ROS_DEBUG("Broadcasting transform");
01441
01442 br_.sendTransform(tf::StampedTransform(world2base.inverse(), base2points.stamp_, base_frame, fixed_frame));
01443 }
01444
01445
01446 void GraphManager::sendAllClouds(bool threaded){
01447 if (ParameterServer::instance()->get<bool>("concurrent_edge_construction") && threaded) {
01448 QFuture<void> f1 = QtConcurrent::run(this, &GraphManager::sendAllCloudsImpl);
01449
01450 }
01451 else {
01452 sendAllCloudsImpl();
01453 }
01454 }
01455
01456
01457 void GraphManager::sendAllCloudsImpl(){
01458 struct timespec starttime, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &starttime);
01459 if (batch_cloud_pub_.getNumSubscribers() == 0){
01460 ROS_WARN("No Subscribers: Sending of clouds cancelled");
01461 return;
01462 }
01463
01464 ROS_INFO("Sending out all clouds");
01465 batch_processing_runs_ = true;
01466 ros::Rate r(ParameterServer::instance()->get<double>("send_clouds_rate"));
01467
01468 for (unsigned int i = 0; i < optimizer_->vertices().size(); ++i) {
01469 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(i));
01470 if(!v){
01471 ROS_ERROR("Nullpointer in graph at position %i!", i);
01472 continue;
01473 }
01474
01475 tf::StampedTransform base2points = graph_[i]->getBase2PointsTransform();
01476 printTransform("base2points", base2points);
01477 tf::Transform computed_motion = g2o2TF(v->estimate());
01478 printTransform("computed_motion", computed_motion);
01479 printTransform("init_base_pose_", init_base_pose_);
01480
01481 tf::Transform world2base = init_base_pose_*base2points*computed_motion*base2points.inverse();
01482 tf::Transform gt_world2base = graph_[i]->getGroundTruthTransform();
01483 tf::Transform err = gt_world2base.inverseTimes(world2base);
01484
01485
01486 ros::Time now = ros::Time::now();
01487 ROS_DEBUG("Sending out transform %i", i);
01488 printTransform("World->Base", world2base);
01489 std::string fixed_frame = ParameterServer::instance()->get<std::string>("fixed_frame_name");
01490 br_.sendTransform(tf::StampedTransform(world2base, now, fixed_frame, base2points.frame_id_));
01491 br_.sendTransform(tf::StampedTransform(err, now, fixed_frame, "/where_mocap_should_be"));
01492 ROS_DEBUG("Sending out cloud %i", i);
01493
01494 graph_[i]->publish(now, batch_cloud_pub_);
01495
01496 QString message;
01497 Q_EMIT setGUIInfo(message.sprintf("Sending pointcloud and map transform (%i/%i) on topics %s and /tf", (int)i+1, (int)optimizer_->vertices().size(), ParameterServer::instance()->get<std::string>("individual_cloud_out_topic").c_str()) );
01498 r.sleep();
01499 }
01500
01501 batch_processing_runs_ = false;
01502 Q_EMIT sendFinished();
01503 clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - starttime.tv_sec); elapsed += (finish.tv_nsec - starttime.tv_nsec) / 1000000000.0; ROS_INFO_STREAM_COND_NAMED(elapsed > ParameterServer::instance()->get<double>("min_time_reported"), "timings", __FUNCTION__ << " runtime: "<< elapsed <<" s");
01504 }
01505
01506 void GraphManager::setMaxDepth(float max_depth){
01507 Max_Depth = max_depth;
01508 ROS_INFO("Max Depth set to: %f", max_depth);
01509 }
01510
01511 void GraphManager::cloudRendered(pointcloud_type const * pc) {
01512 BOOST_REVERSE_FOREACH(GraphNodeType entry, graph_){
01513 if(entry.second->pc_col.get() == pc){
01514 entry.second->clearPointCloud();
01515 ROS_WARN("Cleared PointCloud after rendering to openGL list. It will not be available for save/send.");
01516 return;
01517 }
01518 }
01519 }
01520
01521
01522 void GraphManager::sanityCheck(float thresh){
01523 thresh *=thresh;
01524 QMutexLocker locker(&optimizer_mutex);
01525 EdgeSet::iterator edge_iter = optimizer_->edges().begin();
01526 for(int i =0;edge_iter != optimizer_->edges().end(); edge_iter++, i++) {
01527 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
01528 Eigen::Vector3d ev = myedge->measurement().translation();
01529 if(ev.squaredNorm() > thresh){
01530 optimizer_->removeEdge(myedge);
01531 }
01532 }
01533 }
01534 void GraphManager::pruneEdgesWithErrorAbove(float thresh){
01535 QMutexLocker locker(&optimizer_mutex);
01536 EdgeSet::iterator edge_iter = optimizer_->edges().begin();
01537 for(int i =0;edge_iter != optimizer_->edges().end(); edge_iter++, i++) {
01538 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
01539 g2o::EdgeSE3::ErrorVector ev = myedge->error();
01540 if(ev.squaredNorm() > thresh){
01541 optimizer_->removeEdge(myedge);
01542 }
01543 }
01544 }
01545 void GraphManager::printEdgeErrors(QString filename){
01546 QMutexLocker locker(&optimizer_mutex);
01547 std::fstream filestr;
01548 filestr.open (qPrintable(filename), std::fstream::out );
01549
01550 EdgeSet::iterator edge_iter = optimizer_->edges().begin();
01551 for(int i =0;edge_iter != optimizer_->edges().end(); edge_iter++, i++) {
01552 g2o::EdgeSE3* myedge = dynamic_cast<g2o::EdgeSE3*>(*edge_iter);
01553 g2o::EdgeSE3::ErrorVector ev = myedge->error();
01554 ROS_INFO_STREAM("Error Norm for edge " << i << ": " << ev.squaredNorm());
01555 filestr << "Error for edge " << i << ": " << ev.squaredNorm() << std::endl;
01556 }
01557 filestr.close();
01558 }
01559
01560 void GraphManager::finishUp(){
01561 someone_is_waiting_for_me_ = true;
01562 }
01563 bool GraphManager::isBusy(){
01564 return (batch_processing_runs_ || process_node_runs_ );
01565 }
01566
01567 double GraphManager::geodesicDiscount(g2o::HyperDijkstra& hypdij, const MatchingResult& mr){
01568
01569 const g2o::HyperDijkstra::AdjacencyMap am = hypdij.adjacencyMap();
01570 g2o::VertexSE3* older_vertex = dynamic_cast<g2o::VertexSE3*>(optimizer_->vertex(mr.edge.id1));
01571 double discount_factor = am.at(older_vertex).distance();
01572 discount_factor = discount_factor > 0.0? 1.0/discount_factor : 1.0;
01573 ROS_INFO("Discount weight for connection to Node %i = %f", mr.edge.id1, discount_factor);
01574 return discount_factor;
01575 }
01576
01577 void GraphManager::toggleMapping(bool mappingOn){
01578 QMutexLocker locker(&optimizer_mutex);
01579 ROS_INFO_COND(mappingOn, "Switching mapping back on");
01580 ROS_INFO_COND(!mappingOn, "Switching mapping off: Localization continues");
01581 localization_only_ = !mappingOn;
01582 BOOST_FOREACH(VertexIDPair pair, optimizer_->vertices()) {
01583 g2o::VertexSE3* v = dynamic_cast<g2o::VertexSE3* >(pair.second);
01584 v->setFixed(localization_only_);
01585 }
01586 }
01587