6 Logger::Logger(std::shared_ptr<rclcpp::Node> &
node,
const unsigned int &robot_id,
const unsigned int &max_nb_robots,
const std::string &log_folder) : robot_id_(robot_id)
9 auto t = std::time(
nullptr);
10 auto tm = std::localtime(&t);
12 std::strftime(time_str,
sizeof(time_str),
"%d-%m-%Y_%H-%M-%S", tm);
13 std::string experiment_id = std::string(time_str) +
"_experiment_robot_" + std::to_string(robot_id);
14 log_folder_ = log_folder +
"/" + experiment_id;
15 system((
"mkdir -p " + log_folder_).c_str());
17 max_nb_robots_ = max_nb_robots;
19 logger_subscriber_ = node_->create_subscription<diagnostic_msgs::msg::KeyValue>(
22 inter_robot_matches_subscriber_ = node_->create_subscription<cslam_common_interfaces::msg::InterRobotMatches>(
26 log_nb_failed_matches_ = 0;
27 log_nb_vertices_transmitted_ = 0;
28 log_nb_matches_selected_ = 0;
29 log_detection_cumulative_communication_ = 0;
30 log_local_descriptors_cumulative_communication_ = 0;
31 log_sparsification_cumulative_computation_time_ = 0.0;
36 pose_time_map_.insert({symbol, {sec, nanosec}});
41 pose_graphs_log_info_.push_back(msg);
45 const gtsam::Values::shared_ptr &initial)
47 initial_global_pose_graph_ = std::make_pair(graph, initial);
51 const gtsam::Values &result,
52 const unsigned int &origin_robot_id)
54 auto values = gtsam::Values::shared_ptr(
new gtsam::Values(result));
55 optimized_global_pose_graph_ = std::make_pair(graph, values);
56 origin_robot_id_ = origin_robot_id;
61 start_time_ = std::chrono::steady_clock::now();
66 auto end_time = std::chrono::steady_clock::now();
67 elapsed_time_ = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time_).count();
68 total_pgo_time_ += elapsed_time_;
74 auto t = std::time(
nullptr);
75 auto tm = std::localtime(&t);
77 std::strftime(time_str,
sizeof(time_str),
"%d-%m-%Y_%H-%M-%S", tm);
78 std::string timestamp = std::string(time_str);
79 std::string result_folder = log_folder_ +
"/" + timestamp;
80 system((
"mkdir -p " + result_folder).c_str());
85 if (initial_global_pose_graph_.first !=
nullptr && initial_global_pose_graph_.second !=
nullptr)
87 if (initial_global_pose_graph_.second->size() > 0)
89 gtsam::writeG2o(*initial_global_pose_graph_.first, *initial_global_pose_graph_.second, result_folder +
"/initial_global_pose_graph.g2o");
92 if (optimized_global_pose_graph_.first !=
nullptr && optimized_global_pose_graph_.second !=
nullptr)
94 if (optimized_global_pose_graph_.second->size() > 0)
96 gtsam::writeG2o(*optimized_global_pose_graph_.first, *optimized_global_pose_graph_.second, result_folder +
"/optimized_global_pose_graph.g2o");
100 catch (std::exception &e)
102 RCLCPP_ERROR(node_->get_logger(),
"Logging: Error while writing g2o files: %s", e.what());
106 std::ofstream optimization_log_file;
107 optimization_log_file.open(result_folder +
"/log.csv");
108 optimization_log_file <<
"robot_id," << std::to_string(robot_id_) << std::endl;
109 optimization_log_file <<
"origin_robot_id," << std::to_string(origin_robot_id_) << std::endl;
110 optimization_log_file <<
"max_nb_robots," << std::to_string(max_nb_robots_) << std::endl;
111 unsigned int total_nb_matches = 0;
112 unsigned int total_nb_failed_matches = 0;
113 unsigned int total_nb_vertices_transmitted = 0;
114 unsigned int total_front_end_cumulative_communication_bytes = 0;
115 float total_sparsification_cumulative_computation_time = 0.0;
116 unsigned int total_nb_matches_selected = 0;
117 for (
const auto &info : pose_graphs_log_info_)
119 total_nb_matches += info.nb_matches;
120 total_nb_failed_matches += info.nb_failed_matches;
121 total_nb_vertices_transmitted += info.nb_vertices_transmitted;
122 total_front_end_cumulative_communication_bytes += info.front_end_cumulative_communication_bytes;
123 total_sparsification_cumulative_computation_time += info.sparsification_cumulative_computation_time;
124 total_nb_matches_selected += info.nb_matches_selected;
126 optimization_log_file <<
"total_nb_successful_matches," << std::to_string(total_nb_matches) << std::endl;
127 optimization_log_file <<
"total_nb_failed_matches," << std::to_string(total_nb_failed_matches) << std::endl;
128 optimization_log_file <<
"total_nb_vertices_transmitted," << std::to_string(total_nb_vertices_transmitted) << std::endl;
129 optimization_log_file <<
"total_nb_matches_selected," << std::to_string(total_nb_matches_selected) << std::endl;
130 optimization_log_file <<
"total_front_end_cumulative_communication_bytes," << std::to_string(total_front_end_cumulative_communication_bytes) << std::endl;
131 optimization_log_file <<
"total_sparsification_cumulative_computation_time," << std::to_string(total_sparsification_cumulative_computation_time) << std::endl;
132 optimization_log_file <<
"latest_pgo_time," << std::to_string(elapsed_time_) << std::endl;
133 optimization_log_file <<
"total_pgo_time," << std::to_string(total_pgo_time_) << std::endl;
135 if (optimized_global_pose_graph_.first !=
nullptr && optimized_global_pose_graph_.second !=
nullptr)
137 optimization_log_file <<
"nb_edges," << std::to_string(optimized_global_pose_graph_.first->size()) << std::endl;
138 optimization_log_file <<
"nb_vertices," << std::to_string(optimized_global_pose_graph_.second->size()) << std::endl;
139 float total_error = compute_error(optimized_global_pose_graph_.first,
140 optimized_global_pose_graph_.second);
141 optimization_log_file <<
"total_error," << std::to_string(total_error) << std::endl;
142 auto loop_closure_errors = compute_inter_robot_loop_closure_errors(optimized_global_pose_graph_.first,
143 optimized_global_pose_graph_.second);
145 optimization_log_file <<
"inter_robot_loop_closures," << std::to_string(loop_closure_errors.size()) << std::endl;
146 for (
const auto &error : loop_closure_errors)
148 optimization_log_file <<
"error"
149 <<
"," << std::to_string(error.second) << std::endl;
153 optimization_log_file.close();
156 for (
const auto &info : pose_graphs_log_info_)
158 std::ofstream gps_log_file;
159 gps_log_file.open(result_folder +
"/gps_robot_" + std::to_string(info.robot_id) +
".csv");
160 gps_log_file <<
"vertice_id,latitude,longitude,altitude" << std::endl;
162 for (
unsigned int i = 0; i < info.gps_values_idx.size(); i++)
164 gps_log_file << std::fixed << std::setprecision(10)
165 << std::to_string(info.gps_values_idx[i]) <<
","
166 << std::to_string(info.gps_values[i].latitude) <<
","
167 << std::to_string(info.gps_values[i].longitude) <<
","
168 << std::to_string(info.gps_values[i].altitude) << std::endl;
171 gps_log_file.close();
175 for (
const auto &info : pose_graphs_log_info_)
177 if (info.robot_id == robot_id_)
179 std::ofstream matches_log_file;
180 matches_log_file.open(result_folder +
"/spectral_matches.csv");
181 matches_log_file <<
"robot0_id, robot0_keyframe_id, robot1_id, robot1_keyframe_id, weight" << std::endl;
182 for (
size_t i = 0; i < info.spectral_matches.matches.size(); i++)
184 matches_log_file << std::to_string(info.spectral_matches.matches[i].robot0_id) <<
","
185 << std::to_string(info.spectral_matches.matches[i].robot0_keyframe_id) <<
","
186 << std::to_string(info.spectral_matches.matches[i].robot1_id) <<
","
187 << std::to_string(info.spectral_matches.matches[i].robot1_keyframe_id) <<
","
188 << std::to_string(info.spectral_matches.matches[i].weight) << std::endl;
193 std::ofstream pose_time_map_file;
194 pose_time_map_file.open(result_folder +
"/pose_timestamps" + std::to_string(robot_id_) +
".csv");
195 pose_time_map_file <<
"vertice_id,sec,nanosec" << std::endl;
197 for (
const auto &key_value : pose_time_map_)
199 pose_time_map_file << std::to_string(key_value.first) <<
","
200 << std::to_string(key_value.second.first) <<
","
201 << std::to_string(key_value.second.second) << std::endl;
204 pose_time_map_file.close();
207 pose_graphs_log_info_.clear();
209 if (optimized_global_pose_graph_.first !=
nullptr && optimized_global_pose_graph_.second !=
nullptr)
211 optimized_global_pose_graph_.first.reset();
212 optimized_global_pose_graph_.second.reset();
214 if (initial_global_pose_graph_.first !=
nullptr && initial_global_pose_graph_.second !=
nullptr)
216 initial_global_pose_graph_.first.reset();
217 initial_global_pose_graph_.second.reset();
221 std::vector<std::pair<std::pair<gtsam::LabeledSymbol, gtsam::LabeledSymbol>,
double>> Logger::compute_inter_robot_loop_closure_errors(
const gtsam::NonlinearFactorGraph::shared_ptr &graph,
222 const gtsam::Values::shared_ptr &result)
224 std::vector<std::pair<std::pair<gtsam::LabeledSymbol, gtsam::LabeledSymbol>,
double>> loop_closure_errors;
227 if (result->size() > 0)
229 for (
const auto &factor_ : *graph)
232 boost::dynamic_pointer_cast<gtsam::BetweenFactor<gtsam::Pose3>>(
236 auto key1 = gtsam::LabeledSymbol(factor->key1());
237 auto key2 = gtsam::LabeledSymbol(factor->key2());
238 auto robot_id1 = key1.label();
239 auto robot_id2 = key2.label();
240 if (robot_id1 != robot_id2)
242 if (result->exists(key1) && result->exists(key2))
244 auto error = factor->error(*result);
245 loop_closure_errors.push_back(std::make_pair(std::make_pair(key1, key2), error));
252 catch (std::exception &e)
254 RCLCPP_ERROR(node_->get_logger(),
"Logging: Error while computing inter-robot loop closure errors connectivity: %s", e.what());
256 return loop_closure_errors;
259 double Logger::compute_error(
const gtsam::NonlinearFactorGraph::shared_ptr &graph,
260 const gtsam::Values::shared_ptr &result)
265 error = graph->error(*result);
267 catch (std::exception &e)
269 RCLCPP_ERROR(node_->get_logger(),
"Logging: Error while computing graph error: %s", e.what());
276 if (msg->key ==
"nb_matches")
278 log_nb_matches_ = std::stoul(msg->value);
280 else if (msg->key ==
"nb_failed_matches")
282 log_nb_failed_matches_ = std::stoul(msg->value);
284 else if (msg->key ==
"nb_vertices_transmitted")
286 log_nb_vertices_transmitted_ = std::stoul(msg->value);
288 else if (msg->key ==
"nb_matches_selected")
290 log_nb_matches_selected_ = std::stoul(msg->value);
292 else if (msg->key ==
"detection_cumulative_communication")
294 log_detection_cumulative_communication_ = std::stoul(msg->value);
296 else if (msg->key ==
"local_descriptors_cumulative_communication")
298 log_local_descriptors_cumulative_communication_ = std::stoul(msg->value);
300 else if (msg->key ==
"sparsification_cumulative_computation_time")
302 log_sparsification_cumulative_computation_time_ = std::stof(msg->value);
306 RCLCPP_ERROR(node_->get_logger(),
"Unknown log key: %s %s", msg->key.c_str(), msg->value.c_str());
312 if (msg->robot_id == 0)
314 for (
size_t i = 0; i < msg->matches.size(); i++)
317 if (std::find(spectral_matches_.matches.begin(), spectral_matches_.matches.end(), msg->matches[i]) == spectral_matches_.matches.end())
319 spectral_matches_.matches.push_back(msg->matches[i]);
327 msg.nb_matches = log_nb_matches_;
328 msg.nb_failed_matches = log_nb_failed_matches_;
329 msg.nb_vertices_transmitted = log_nb_vertices_transmitted_;
330 msg.nb_matches_selected = log_nb_matches_selected_;
331 msg.front_end_cumulative_communication_bytes = log_detection_cumulative_communication_ + log_local_descriptors_cumulative_communication_;
332 msg.sparsification_cumulative_computation_time = log_sparsification_cumulative_computation_time_;
333 msg.spectral_matches = spectral_matches_;
void log_initial_global_pose_graph(const gtsam::NonlinearFactorGraph::shared_ptr &graph, const gtsam::Values::shared_ptr &initial)
void add_pose_graph_log_info(const cslam_common_interfaces::msg::PoseGraph &msg)
void log_matches_callback(const cslam_common_interfaces::msg::InterRobotMatches::ConstSharedPtr msg)
Receive log messages.
void fill_msg(cslam_common_interfaces::msg::PoseGraph &msg)
void log_optimized_global_pose_graph(const gtsam::NonlinearFactorGraph::shared_ptr &graph, const gtsam::Values &result, const unsigned int &origin_robot_id)
void log_pose_timestamp(const gtsam::LabeledSymbol &symbol, const int &sec, const int &nanosec)
Logger(std::shared_ptr< rclcpp::Node > &node, const unsigned int &robot_id, const unsigned int &max_nb_robots, const std::string &log_folder)
Logger class to log various metrics.
void log_callback(const diagnostic_msgs::msg::KeyValue::ConstSharedPtr msg)
Receive log messages.