ros1_serial_msckf.cpp
Go to the documentation of this file.
1 /*
2  * OpenVINS: An Open Platform for Visual-Inertial Research
3  * Copyright (C) 2018-2023 Patrick Geneva
4  * Copyright (C) 2018-2023 Guoquan Huang
5  * Copyright (C) 2018-2023 OpenVINS Contributors
6  * Copyright (C) 2018-2019 Kevin Eckenhoff
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <ros/ros.h>
23 #include <rosbag/bag.h>
24 #include <rosbag/view.h>
25 #include <sensor_msgs/Image.h>
26 #include <sensor_msgs/Imu.h>
27 
28 #include <memory>
29 
30 #include "core/VioManager.h"
31 #include "core/VioManagerOptions.h"
32 #include "ros/ROS1Visualizer.h"
33 #include "utils/dataset_reader.h"
34 
35 using namespace ov_msckf;
36 
37 std::shared_ptr<VioManager> sys;
38 std::shared_ptr<ROS1Visualizer> viz;
39 
40 // Main function
41 int main(int argc, char **argv) {
42 
43  // Ensure we have a path, if the user passes it then we should use it
44  std::string config_path = "unset_path_to_config.yaml";
45  if (argc > 1) {
46  config_path = argv[1];
47  }
48 
49  // Launch our ros node
50  ros::init(argc, argv, "ros1_serial_msckf");
51  auto nh = std::make_shared<ros::NodeHandle>("~");
52  nh->param<std::string>("config_path", config_path, config_path);
53 
54  // Load the config
55  auto parser = std::make_shared<ov_core::YamlParser>(config_path);
56  parser->set_node_handler(nh);
57 
58  // Verbosity
59  std::string verbosity = "INFO";
60  parser->parse_config("verbosity", verbosity);
62 
63  // Create our VIO system
64  VioManagerOptions params;
65  params.print_and_load(parser);
66  // params.num_opencv_threads = 0; // uncomment if you want repeatability
67  // params.use_multi_threading_pubs = 0; // uncomment if you want repeatability
68  params.use_multi_threading_subs = false;
69  sys = std::make_shared<VioManager>(params);
70  viz = std::make_shared<ROS1Visualizer>(nh, sys);
71 
72  // Ensure we read in all parameters required
73  if (!parser->successful()) {
74  PRINT_ERROR(RED "[SERIAL]: unable to parse all parameters, please fix\n" RESET);
75  std::exit(EXIT_FAILURE);
76  }
77 
78  //===================================================================================
79  //===================================================================================
80  //===================================================================================
81 
82  // Our imu topic
83  std::string topic_imu;
84  nh->param<std::string>("topic_imu", topic_imu, "/imu0");
85  parser->parse_external("relative_config_imu", "imu0", "rostopic", topic_imu);
86  PRINT_DEBUG("[SERIAL]: imu: %s\n", topic_imu.c_str());
87 
88  // Our camera topics
89  std::vector<std::string> topic_cameras;
90  for (int i = 0; i < params.state_options.num_cameras; i++) {
91  std::string cam_topic;
92  nh->param<std::string>("topic_camera" + std::to_string(i), cam_topic, "/cam" + std::to_string(i) + "/image_raw");
93  parser->parse_external("relative_config_imucam", "cam" + std::to_string(i), "rostopic", cam_topic);
94  topic_cameras.emplace_back(cam_topic);
95  PRINT_DEBUG("[SERIAL]: cam: %s\n", cam_topic.c_str());
96  }
97 
98  // Location of the ROS bag we want to read in
99  std::string path_to_bag;
100  nh->param<std::string>("path_bag", path_to_bag, "/home/patrick/datasets/eth/V1_01_easy.bag");
101  PRINT_DEBUG("[SERIAL]: ros bag path is: %s\n", path_to_bag.c_str());
102 
103  // Load groundtruth if we have it
104  // NOTE: needs to be a csv ASL format file
105  std::map<double, Eigen::Matrix<double, 17, 1>> gt_states;
106  if (nh->hasParam("path_gt")) {
107  std::string path_to_gt;
108  nh->param<std::string>("path_gt", path_to_gt, "");
109  if (!path_to_gt.empty()) {
110  ov_core::DatasetReader::load_gt_file(path_to_gt, gt_states);
111  PRINT_DEBUG("[SERIAL]: gt file path is: %s\n", path_to_gt.c_str());
112  }
113  }
114 
115  // Get our start location and how much of the bag we want to play
116  // Make the bag duration < 0 to just process to the end of the bag
117  double bag_start, bag_durr;
118  nh->param<double>("bag_start", bag_start, 0);
119  nh->param<double>("bag_durr", bag_durr, -1);
120  PRINT_DEBUG("[SERIAL]: bag start: %.1f\n", bag_start);
121  PRINT_DEBUG("[SERIAL]: bag duration: %.1f\n", bag_durr);
122 
123  //===================================================================================
124  //===================================================================================
125  //===================================================================================
126 
127  // Load rosbag here, and find messages we can play
128  rosbag::Bag bag;
129  bag.open(path_to_bag, rosbag::bagmode::Read);
130 
131  // We should load the bag as a view
132  // Here we go from beginning of the bag to the end of the bag
133  rosbag::View view_full;
134  rosbag::View view;
135 
136  // Start a few seconds in from the full view time
137  // If we have a negative duration then use the full bag length
138  view_full.addQuery(bag);
139  ros::Time time_init = view_full.getBeginTime();
140  time_init += ros::Duration(bag_start);
141  ros::Time time_finish = (bag_durr < 0) ? view_full.getEndTime() : time_init + ros::Duration(bag_durr);
142  PRINT_DEBUG("time start = %.6f\n", time_init.toSec());
143  PRINT_DEBUG("time end = %.6f\n", time_finish.toSec());
144  view.addQuery(bag, time_init, time_finish);
145 
146  // Check to make sure we have data to play
147  if (view.size() == 0) {
148  PRINT_ERROR(RED "[SERIAL]: No messages to play on specified topics. Exiting.\n" RESET);
149  ros::shutdown();
150  return EXIT_FAILURE;
151  }
152 
153  // We going to loop through and collect a list of all messages
154  // This is done so we can access arbitrary points in the bag
155  // NOTE: if we instantiate messages here, this requires the whole bag to be read
156  // NOTE: thus we just check the topic which allows us to quickly loop through the index
157  // NOTE: see this PR https://github.com/ros/ros_comm/issues/117
158  double max_camera_time = -1;
159  std::vector<rosbag::MessageInstance> msgs;
160  for (const rosbag::MessageInstance &msg : view) {
161  if (!ros::ok())
162  break;
163  if (msg.getTopic() == topic_imu) {
164  // if (msg.instantiate<sensor_msgs::Imu>() == nullptr) {
165  // PRINT_ERROR(RED "[SERIAL]: IMU topic has unmatched message types!!\n" RESET);
166  // PRINT_ERROR(RED "[SERIAL]: Supports: sensor_msgs::Imu\n" RESET);
167  // return EXIT_FAILURE;
168  // }
169  msgs.push_back(msg);
170  }
171  for (int i = 0; i < params.state_options.num_cameras; i++) {
172  if (msg.getTopic() == topic_cameras.at(i)) {
173  // sensor_msgs::CompressedImage::ConstPtr img_c = msg.instantiate<sensor_msgs::CompressedImage>();
174  // sensor_msgs::Image::ConstPtr img_i = msg.instantiate<sensor_msgs::Image>();
175  // if (img_c == nullptr && img_i == nullptr) {
176  // PRINT_ERROR(RED "[SERIAL]: Image topic has unmatched message types!!\n" RESET);
177  // PRINT_ERROR(RED "[SERIAL]: Supports: sensor_msgs::Image and sensor_msgs::CompressedImage\n" RESET);
178  // return EXIT_FAILURE;
179  // }
180  msgs.push_back(msg);
181  max_camera_time = std::max(max_camera_time, msg.getTime().toSec());
182  }
183  }
184  }
185  PRINT_DEBUG("[SERIAL]: total of %zu messages!\n", msgs.size());
186 
187  //===================================================================================
188  //===================================================================================
189  //===================================================================================
190 
191  // Loop through our message array, and lets process them
192  std::set<int> used_index;
193  for (int m = 0; m < (int)msgs.size(); m++) {
194 
195  // End once we reach the last time, or skip if before beginning time (shouldn't happen)
196  if (!ros::ok() || msgs.at(m).getTime() > time_finish || msgs.at(m).getTime().toSec() > max_camera_time)
197  break;
198  if (msgs.at(m).getTime() < time_init)
199  continue;
200 
201  // Skip messages that we have already used
202  if (used_index.find(m) != used_index.end()) {
203  used_index.erase(m);
204  continue;
205  }
206 
207  // IMU processing
208  if (msgs.at(m).getTopic() == topic_imu) {
209  // PRINT_DEBUG("processing imu = %.3f sec\n", msgs.at(m).getTime().toSec() - time_init.toSec());
210  viz->callback_inertial(msgs.at(m).instantiate<sensor_msgs::Imu>());
211  }
212 
213  // Camera processing
214  for (int cam_id = 0; cam_id < params.state_options.num_cameras; cam_id++) {
215 
216  // Skip if this message is not a camera topic
217  if (msgs.at(m).getTopic() != topic_cameras.at(cam_id))
218  continue;
219 
220  // We have a matching camera topic here, now find the other cameras for this time
221  // For each camera, we will find the nearest timestamp (within 0.02sec) that is greater than the current
222  // If we are unable, then this message should just be skipped since it isn't a sync'ed pair!
223  std::map<int, int> camid_to_msg_index;
224  double meas_time = msgs.at(m).getTime().toSec();
225  for (int cam_idt = 0; cam_idt < params.state_options.num_cameras; cam_idt++) {
226  if (cam_idt == cam_id) {
227  camid_to_msg_index.insert({cam_id, m});
228  continue;
229  }
230  int cam_idt_idx = -1;
231  for (int mt = m; mt < (int)msgs.size(); mt++) {
232  if (msgs.at(mt).getTopic() != topic_cameras.at(cam_idt))
233  continue;
234  if (std::abs(msgs.at(mt).getTime().toSec() - meas_time) < 0.02)
235  cam_idt_idx = mt;
236  break;
237  }
238  if (cam_idt_idx != -1) {
239  camid_to_msg_index.insert({cam_idt, cam_idt_idx});
240  }
241  }
242 
243  // Skip processing if we were unable to find any messages
244  if ((int)camid_to_msg_index.size() != params.state_options.num_cameras) {
245  PRINT_DEBUG(YELLOW "[SERIAL]: Unable to find stereo pair for message %d at %.2f into bag (will skip!)\n" RESET, m,
246  meas_time - time_init.toSec());
247  continue;
248  }
249 
250  // Check if we should initialize using the groundtruth
251  Eigen::Matrix<double, 17, 1> imustate;
252  if (!gt_states.empty() && !sys->initialized() && ov_core::DatasetReader::get_gt_state(meas_time, imustate, gt_states)) {
253  // biases are pretty bad normally, so zero them
254  // imustate.block(11,0,6,1).setZero();
255  sys->initialize_with_gt(imustate);
256  }
257 
258  // Pass our data into our visualizer callbacks!
259  // PRINT_DEBUG("processing cam = %.3f sec\n", msgs.at(m).getTime().toSec() - time_init.toSec());
260  if (params.state_options.num_cameras == 1) {
261  viz->callback_monocular(msgs.at(camid_to_msg_index.at(0)).instantiate<sensor_msgs::Image>(), 0);
262  } else if (params.state_options.num_cameras == 2) {
263  auto msg0 = msgs.at(camid_to_msg_index.at(0));
264  auto msg1 = msgs.at(camid_to_msg_index.at(1));
265  used_index.insert(camid_to_msg_index.at(0)); // skip this message
266  used_index.insert(camid_to_msg_index.at(1)); // skip this message
267  viz->callback_stereo(msg0.instantiate<sensor_msgs::Image>(), msg1.instantiate<sensor_msgs::Image>(), 0, 1);
268  } else {
269  PRINT_ERROR(RED "[SERIAL]: We currently only support 1 or 2 camera serial input....\n" RESET);
270  return EXIT_FAILURE;
271  }
272 
273  break;
274  }
275  }
276 
277  // Final visualization
278  viz->visualize_final();
279 
280  // Done!
281  return EXIT_SUCCESS;
282 }
ov_core::DatasetReader::load_gt_file
static void load_gt_file(std::string path, std::map< double, Eigen::Matrix< double, 17, 1 >> &gt_states)
rosbag::Bag
ROS1Visualizer.h
rosbag::View::size
uint32_t size()
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
rosbag::MessageInstance
YELLOW
YELLOW
ros.h
PRINT_DEBUG
#define PRINT_DEBUG(x...)
ov_msckf
Extended Kalman Filter estimator.
Definition: VioManager.h:46
ov_msckf::VioManagerOptions::print_and_load
void print_and_load(const std::shared_ptr< ov_core::YamlParser > &parser=nullptr)
This function will load the non-simulation parameters of the system and print.
Definition: VioManagerOptions.h:62
TimeBase< Time, Duration >::toSec
double toSec() const
ros::shutdown
ROSCPP_DECL void shutdown()
bag.h
PRINT_ERROR
#define PRINT_ERROR(x...)
ros::ok
ROSCPP_DECL bool ok()
rosbag::bagmode::Read
Read
sys
std::shared_ptr< VioManager > sys
Definition: ros1_serial_msckf.cpp:37
viz
std::shared_ptr< ROS1Visualizer > viz
Definition: ros1_serial_msckf.cpp:38
ov_core::Printer::setPrintLevel
static void setPrintLevel(const std::string &level)
VioManager.h
ov_core::DatasetReader::get_gt_state
static bool get_gt_state(double timestep, Eigen::Matrix< double, 17, 1 > &imustate, std::map< double, Eigen::Matrix< double, 17, 1 >> &gt_states)
VioManagerOptions.h
rosbag::View::addQuery
void addQuery(Bag const &bag, boost::function< bool(ConnectionInfo const *)> query, ros::Time const &start_time=ros::TIME_MIN, ros::Time const &end_time=ros::TIME_MAX)
ov_msckf::VioManagerOptions::use_multi_threading_subs
bool use_multi_threading_subs
If our ROS subscriber callbacks should be async (if sim and serial then this should be no!...
Definition: VioManagerOptions.h:418
view.h
rosbag::View
rosbag::View::getBeginTime
ros::Time getBeginTime()
ros::Time
main
int main(int argc, char **argv)
Definition: ros1_serial_msckf.cpp:41
RESET
#define RESET
dataset_reader.h
ov_msckf::VioManagerOptions::state_options
StateOptions state_options
Core state options (e.g. number of cameras, use fej, stereo, what calibration to enable etc)
Definition: VioManagerOptions.h:74
ov_msckf::VioManagerOptions
Struct which stores all options needed for state estimation.
Definition: VioManagerOptions.h:56
ov_msckf::StateOptions::num_cameras
int num_cameras
Number of distinct cameras that we will observe features in.
Definition: StateOptions.h:83
rosbag::View::getEndTime
ros::Time getEndTime()
rosbag::Bag::open
void open(std::string const &filename, uint32_t mode=bagmode::Read)
ros::Duration
RED
RED


ov_msckf
Author(s): Patrick Geneva , Kevin Eckenhoff , Guoquan Huang
autogenerated on Mon Dec 16 2024 03:06:54