Swarm-SLAM  1.0.0
C-SLAM Framework
logger.cpp
Go to the documentation of this file.
2 
3 namespace cslam
4 {
5 
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)
7  {
8  node_ = node;
9  auto t = std::time(nullptr);
10  auto tm = std::localtime(&t);
11  char time_str[100];
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());
16  total_pgo_time_ = 0;
17  max_nb_robots_ = max_nb_robots;
18 
19  logger_subscriber_ = node_->create_subscription<diagnostic_msgs::msg::KeyValue>(
20  "cslam/log_info", 10, std::bind(&Logger::log_callback, this, std::placeholders::_1));
21 
22  inter_robot_matches_subscriber_ = node_->create_subscription<cslam_common_interfaces::msg::InterRobotMatches>(
23  "cslam/log_matches", 10, std::bind(&Logger::log_matches_callback, this, std::placeholders::_1));
24 
25  log_nb_matches_ = 0;
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;
32  }
33 
34  void Logger::log_pose_timestamp(const gtsam::LabeledSymbol &symbol, const int &sec, const int &nanosec)
35  {
36  pose_time_map_.insert({symbol, {sec, nanosec}});
37  }
38 
39  void Logger::add_pose_graph_log_info(const cslam_common_interfaces::msg::PoseGraph &msg)
40  {
41  pose_graphs_log_info_.push_back(msg);
42  }
43 
44  void Logger::log_initial_global_pose_graph(const gtsam::NonlinearFactorGraph::shared_ptr &graph,
45  const gtsam::Values::shared_ptr &initial)
46  {
47  initial_global_pose_graph_ = std::make_pair(graph, initial);
48  }
49 
50  void Logger::log_optimized_global_pose_graph(const gtsam::NonlinearFactorGraph::shared_ptr &graph,
51  const gtsam::Values &result,
52  const unsigned int &origin_robot_id)
53  {
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;
57  }
58 
60  {
61  start_time_ = std::chrono::steady_clock::now();
62  }
63 
65  {
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_;
69  }
70 
72  {
73  // Create folder
74  auto t = std::time(nullptr);
75  auto tm = std::localtime(&t);
76  char time_str[100];
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());
81 
82  // Write pgo logs (.g2o)
83  try
84  {
85  if (initial_global_pose_graph_.first != nullptr && initial_global_pose_graph_.second != nullptr)
86  {
87  if (initial_global_pose_graph_.second->size() > 0)
88  {
89  gtsam::writeG2o(*initial_global_pose_graph_.first, *initial_global_pose_graph_.second, result_folder + "/initial_global_pose_graph.g2o");
90  }
91  }
92  if (optimized_global_pose_graph_.first != nullptr && optimized_global_pose_graph_.second != nullptr)
93  {
94  if (optimized_global_pose_graph_.second->size() > 0)
95  {
96  gtsam::writeG2o(*optimized_global_pose_graph_.first, *optimized_global_pose_graph_.second, result_folder + "/optimized_global_pose_graph.g2o");
97  }
98  }
99  }
100  catch (std::exception &e)
101  {
102  RCLCPP_ERROR(node_->get_logger(), "Logging: Error while writing g2o files: %s", e.what());
103  }
104 
105  // Write other logs (.csv)
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_)
118  {
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;
125  }
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;
134 
135  if (optimized_global_pose_graph_.first != nullptr && optimized_global_pose_graph_.second != nullptr)
136  {
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);
144 
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)
147  {
148  optimization_log_file << "error"
149  << "," << std::to_string(error.second) << std::endl;
150  }
151  }
152 
153  optimization_log_file.close();
154 
155  // Write gps logs (.csv)
156  for (const auto &info : pose_graphs_log_info_)
157  {
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;
161 
162  for (unsigned int i = 0; i < info.gps_values_idx.size(); i++)
163  {
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;
169  }
170 
171  gps_log_file.close();
172  }
173 
174  // Write matches logs (.csv)
175  for (const auto &info : pose_graphs_log_info_)
176  {
177  if (info.robot_id == robot_id_)
178  {
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++)
183  {
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;
189  }
190  }
191  }
192 
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;
196 
197  for (const auto &key_value : pose_time_map_)
198  {
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;
202  }
203 
204  pose_time_map_file.close();
205 
206  // Clear logs
207  pose_graphs_log_info_.clear();
208  gps_values_.clear();
209  if (optimized_global_pose_graph_.first != nullptr && optimized_global_pose_graph_.second != nullptr)
210  {
211  optimized_global_pose_graph_.first.reset();
212  optimized_global_pose_graph_.second.reset();
213  }
214  if (initial_global_pose_graph_.first != nullptr && initial_global_pose_graph_.second != nullptr)
215  {
216  initial_global_pose_graph_.first.reset();
217  initial_global_pose_graph_.second.reset();
218  }
219  }
220 
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)
223  {
224  std::vector<std::pair<std::pair<gtsam::LabeledSymbol, gtsam::LabeledSymbol>, double>> loop_closure_errors;
225  try
226  {
227  if (result->size() > 0)
228  {
229  for (const auto &factor_ : *graph)
230  {
231  auto factor =
232  boost::dynamic_pointer_cast<gtsam::BetweenFactor<gtsam::Pose3>>(
233  factor_);
234  if (factor)
235  {
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)
241  {
242  if (result->exists(key1) && result->exists(key2))
243  {
244  auto error = factor->error(*result);
245  loop_closure_errors.push_back(std::make_pair(std::make_pair(key1, key2), error));
246  }
247  }
248  }
249  }
250  }
251  }
252  catch (std::exception &e)
253  {
254  RCLCPP_ERROR(node_->get_logger(), "Logging: Error while computing inter-robot loop closure errors connectivity: %s", e.what());
255  }
256  return loop_closure_errors;
257  }
258 
259  double Logger::compute_error(const gtsam::NonlinearFactorGraph::shared_ptr &graph,
260  const gtsam::Values::shared_ptr &result)
261  {
262  double error = 0.0;
263  try
264  {
265  error = graph->error(*result);
266  }
267  catch (std::exception &e)
268  {
269  RCLCPP_ERROR(node_->get_logger(), "Logging: Error while computing graph error: %s", e.what());
270  }
271  return error;
272  }
273 
274  void Logger::log_callback(const diagnostic_msgs::msg::KeyValue::ConstSharedPtr msg)
275  {
276  if (msg->key == "nb_matches")
277  {
278  log_nb_matches_ = std::stoul(msg->value);
279  }
280  else if (msg->key == "nb_failed_matches")
281  {
282  log_nb_failed_matches_ = std::stoul(msg->value);
283  }
284  else if (msg->key == "nb_vertices_transmitted")
285  {
286  log_nb_vertices_transmitted_ = std::stoul(msg->value);
287  }
288  else if (msg->key == "nb_matches_selected")
289  {
290  log_nb_matches_selected_ = std::stoul(msg->value);
291  }
292  else if (msg->key == "detection_cumulative_communication")
293  {
294  log_detection_cumulative_communication_ = std::stoul(msg->value);
295  }
296  else if (msg->key == "local_descriptors_cumulative_communication")
297  {
298  log_local_descriptors_cumulative_communication_ = std::stoul(msg->value);
299  }
300  else if (msg->key == "sparsification_cumulative_computation_time")
301  {
302  log_sparsification_cumulative_computation_time_ = std::stof(msg->value);
303  }
304  else
305  {
306  RCLCPP_ERROR(node_->get_logger(), "Unknown log key: %s %s", msg->key.c_str(), msg->value.c_str());
307  }
308  }
309 
310  void Logger::log_matches_callback(const cslam_common_interfaces::msg::InterRobotMatches::ConstSharedPtr msg)
311  {
312  if (msg->robot_id == 0)
313  {
314  for (size_t i = 0; i < msg->matches.size(); i++)
315  {
316  // Check if match is already in the vector
317  if (std::find(spectral_matches_.matches.begin(), spectral_matches_.matches.end(), msg->matches[i]) == spectral_matches_.matches.end())
318  {
319  spectral_matches_.matches.push_back(msg->matches[i]);
320  }
321  }
322  }
323  }
324 
325  void Logger::fill_msg(cslam_common_interfaces::msg::PoseGraph &msg)
326  {
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_;
334  }
335 
336 } // namespace cslam
void stop_timer()
Definition: logger.cpp:64
void log_initial_global_pose_graph(const gtsam::NonlinearFactorGraph::shared_ptr &graph, const gtsam::Values::shared_ptr &initial)
Definition: logger.cpp:44
void add_pose_graph_log_info(const cslam_common_interfaces::msg::PoseGraph &msg)
Definition: logger.cpp:39
void log_matches_callback(const cslam_common_interfaces::msg::InterRobotMatches::ConstSharedPtr msg)
Receive log messages.
Definition: logger.cpp:310
void fill_msg(cslam_common_interfaces::msg::PoseGraph &msg)
Definition: logger.cpp:325
void log_optimized_global_pose_graph(const gtsam::NonlinearFactorGraph::shared_ptr &graph, const gtsam::Values &result, const unsigned int &origin_robot_id)
Definition: logger.cpp:50
void start_timer()
Definition: logger.cpp:59
void write_logs()
Definition: logger.cpp:71
void log_pose_timestamp(const gtsam::LabeledSymbol &symbol, const int &sec, const int &nanosec)
Definition: logger.cpp:34
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.
Definition: logger.cpp:6
void log_callback(const diagnostic_msgs::msg::KeyValue::ConstSharedPtr msg)
Receive log messages.
Definition: logger.cpp:274
Definition: __init__.py:1