To be sure there is a recent loop closure constraint, you can query PoseGraphInterface::constraints(), loop over all constraints and check if there is a recent one of type INTER that spans some time.
A simpler solution is to subscribe to the global slam callback GlobalSlamOptimizationCallback by setting PoseGraphInterface::SetGlobalSlamOptimizationCallback. This will call you back after Cartographer has searched for loop closures. It does not tell you if loop closures were found, it mostly tells you if the background loop closure search is keeping up or falls behind.
if (FLAGS_is_handsome) FLAGS_is_handsome = false; std::cout << FLAGS_hobby << std::endl;
DEFINE_string(configuration_directory, "", "First directory in which configuration files are searched, " "second is always the Cartographer installation to allow " "including files from there.");
Google glog是一个基于程序级记录日志信息的c++库,编程使用方式与c++的stream操作类似。 每个级别的日志除了输出到对应日志文件中,还输出到每个低级别日志文件中。默认ERROR和FATAL消息除了输出到日志文件中之外,还会输出到标准错误中 每个级别都对应有相应的日志文件,文件默认存放在临时文件中,Linux是 /tmp。运行cartographer之后,可以在/tmp里看到
intmain(int argc, char** argv) { google::InitGoogleLogging(argv[0]); // INFO std::string str_info; // 文件名最后只能是 等级+下划线 str_info.append("/home/user/log/INFO_"); google::SetLogDestination(google::INFO, str_info.c_str()); LOG(INFO)<< "The is a info!"; // WARNING std::string str_warn; str_warn.append("/home/user/log/WARNING_"); google::SetLogDestination(google::WARNING, str_warn.c_str()); LOG(WARNING) << "The is a warning!";
structConstraint { structPose { transform::Rigid3d zbar_ij; double translation_weight; double rotation_weight; }; SubmapId submap_id; // 'i' in the paper. NodeId node_id; // 'j' in the paper. // Pose of the node 'j' relative to submap 'i'. Pose pose; // Differentiates between intra-submap (where node 'j' was inserted into // submap 'i') and inter-submap constraints (where node 'j' was not // inserted into submap 'i'). enumTag { INTRA_SUBMAP, INTER_SUBMAP } tag; };
for (size_t i = 0; i < insertion_submaps.size(); ++i) { const SubmapId submap_id = submap_ids[i]; // Even if this was the last node added to 'submap_id', the submap will // only be marked as finished in 'data_.submap_data' further below. CHECK( data_.submap_data.at(submap_id).state == SubmapState::kNoConstraintSearch ); data_.submap_data.at(submap_id).node_ids.emplace(node_id); // node_id的 scan pose 在子图中的 相对位置,就是后面的 intra constrait const transform::Rigid2d constraint_transform = constraints::ComputeSubmapPose(*insertion_submaps[i]).inverse() * local_pose_2d; /* 遍历matching结果的所有submap,其新增加的轨迹节点必定组成了其submap, 故其约束为 INTRA_SUBMAP 类型约束,可直接将相对位置加入约束队列中 */ data_.constraints.push_back( Constraint{ submap_id, node_id, { transform::Embed3D(constraint_transform), options_.matcher_translation_weight(), options_.matcher_rotation_weight()}, Constraint::INTRA_SUBMAP} ); }
// 新加入的节点与之前所有完成约束计算的子图,均计算一次约束 for (constauto& submap_id : finished_submap_ids) ComputeConstraint(node_id, submap_id); /* 每次新的子图完成,不再更新即不再添加新的轨迹节点时, 则需要计算此子图与所有优化位姿图node之间的约束 */ if (newly_finished_submap) { const SubmapId newly_finished_submap_id = submap_ids.front(); // We have a new completed submap, so we look into // adding constraints for old nodes for (constauto& node_id_data : optimization_problem_->node_data() ) { const NodeId& node_id = node_id_data.id; if (newly_finished_submap_node_ids.count(node_id) == 0) { ComputeConstraint(node_id, newly_finished_submap_id); } } }