21 #include <sys/resource.h> 29 #include "gflags/gflags.h" 31 #include "rosgraph_msgs/Clock.h" 36 "First directory in which configuration files are searched, " 37 "second is always the Cartographer installation to allow " 38 "including files from there.");
40 configuration_basenames,
"",
41 "Comma-separated list of basenames, i.e. not containing any " 42 "directory prefix, of the configuration files for each trajectory. " 43 "The first configuration file will be used for node options. " 44 "If less configuration files are specified than trajectories, the " 45 "first file will be used the remaining trajectories.");
48 "Comma-separated list of bags to process. One bag per trajectory. " 49 "Any combination of simultaneous and sequential bags is supported.");
51 "Comma-separated list of one or more URDF files that contain " 52 "static links for the sensor configuration(s).");
54 "Whether to read, use and republish transforms from bags.");
56 "If non-empty, filename of a .pbstream file to load, containing " 57 "a saved SLAM state.");
59 "Load the saved state as frozen (non-optimized) trajectories.");
61 "Keep running the offline node after all messages from the bag " 62 "have been processed.");
64 "Optional amount of seconds to skip from the beginning " 65 "(i.e. when the earliest bag starts.). ");
80 CHECK(!FLAGS_configuration_directory.empty())
81 <<
"-configuration_directory is missing.";
82 CHECK(!FLAGS_configuration_basenames.empty())
83 <<
"-configuration_basenames is missing.";
84 CHECK(!(FLAGS_bag_filenames.empty() && FLAGS_load_state_filename.empty()))
85 <<
"-bag_filenames and -load_state_filename cannot both be unspecified.";
86 const auto bag_filenames =
89 const auto configuration_basenames =
91 std::vector<TrajectoryOptions> bag_trajectory_options(1);
92 std::tie(node_options, bag_trajectory_options.at(0)) =
93 LoadOptions(FLAGS_configuration_directory, configuration_basenames.at(0));
95 for (
size_t bag_index = 1; bag_index < bag_filenames.size(); ++bag_index) {
97 if (bag_index < configuration_basenames.size()) {
98 std::tie(std::ignore, current_trajectory_options) =
LoadOptions(
99 FLAGS_configuration_directory, configuration_basenames.at(bag_index));
101 current_trajectory_options = bag_trajectory_options.at(0);
103 bag_trajectory_options.push_back(current_trajectory_options);
105 if (bag_filenames.size() > 0) {
106 CHECK_EQ(bag_trajectory_options.size(), bag_filenames.size());
116 const std::chrono::time_point<std::chrono::steady_clock> start_time =
117 std::chrono::steady_clock::now();
121 std::vector<geometry_msgs::TransformStamped> urdf_transforms;
122 for (
const std::string& urdf_filename :
124 const auto current_urdf_transforms =
126 urdf_transforms.insert(urdf_transforms.end(),
127 current_urdf_transforms.begin(),
128 current_urdf_transforms.end());
133 Node node(node_options, std::move(map_builder), &tf_buffer);
134 if (!FLAGS_load_state_filename.empty()) {
135 node.
LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);
148 if (urdf_transforms.size() > 0) {
149 static_tf_broadcaster.sendTransform(urdf_transforms);
153 async_spinner.
start();
154 rosgraph_msgs::Clock clock;
157 [&clock_publisher, &clock](const ::ros::WallTimerEvent&) {
158 clock_publisher.
publish(clock);
163 std::set<cartographer::mapping::TrajectoryBuilderInterface::SensorId>>
164 bag_expected_sensor_ids;
165 if (configuration_basenames.size() == 1) {
166 const auto current_bag_expected_sensor_ids =
168 {bag_trajectory_options.front()});
169 bag_expected_sensor_ids = {bag_filenames.size(),
170 current_bag_expected_sensor_ids.front()};
172 bag_expected_sensor_ids =
175 CHECK_EQ(bag_expected_sensor_ids.size(), bag_filenames.size());
177 std::map<std::pair<
int , std::string>,
179 bag_topic_to_sensor_id;
181 for (
size_t current_bag_index = 0; current_bag_index < bag_filenames.size();
182 ++current_bag_index) {
183 const std::string& bag_filename = bag_filenames.at(current_bag_index);
187 for (
const auto& expected_sensor_id :
188 bag_expected_sensor_ids.at(current_bag_index)) {
189 const auto bag_resolved_topic = std::make_pair(
190 static_cast<int>(current_bag_index),
192 if (bag_topic_to_sensor_id.count(bag_resolved_topic) != 0) {
193 LOG(ERROR) <<
"Sensor " << expected_sensor_id.id <<
" of bag " 194 << current_bag_index <<
" resolves to topic " 195 << bag_resolved_topic.second <<
" which is already used by " 197 << bag_topic_to_sensor_id.at(bag_resolved_topic).id;
199 bag_topic_to_sensor_id[bag_resolved_topic] = expected_sensor_id;
210 if (msg.
isType<tf2_msgs::TFMessage>()) {
211 if (FLAGS_use_bag_transforms) {
212 const auto tf_message = msg.instantiate<tf2_msgs::TFMessage>();
213 tf_publisher.publish(tf_message);
215 for (const auto& transform : tf_message->transforms) {
221 tf_buffer.setTransform(transform,
"unused_authority",
222 msg.getTopic() == kTfStaticTopic);
223 } catch (const tf2::TransformException& ex) {
224 LOG(WARNING) << ex.what();
238 std::unordered_map<int, int> bag_index_to_trajectory_id;
241 playable_bag_multiplexer.IsMessageAvailable()
242 ? playable_bag_multiplexer.PeekMessageTime()
244 while (playable_bag_multiplexer.IsMessageAvailable()) {
249 const auto next_msg_tuple = playable_bag_multiplexer.GetNextMessage();
251 const int bag_index = std::get<1>(next_msg_tuple);
252 const bool is_last_message_in_bag = std::get<2>(next_msg_tuple);
261 if (bag_index_to_trajectory_id.count(bag_index) == 0) {
263 node.AddOfflineTrajectory(bag_expected_sensor_ids.at(bag_index),
264 bag_trajectory_options.at(bag_index));
265 CHECK(bag_index_to_trajectory_id
266 .emplace(std::piecewise_construct,
267 std::forward_as_tuple(bag_index),
268 std::forward_as_tuple(trajectory_id))
270 LOG(INFO) <<
"Assigned trajectory " << trajectory_id <<
" to bag " 271 << bag_filenames.at(bag_index);
273 trajectory_id = bag_index_to_trajectory_id.at(bag_index);
276 const auto bag_topic = std::make_pair(
278 node.node_handle()->resolveName(msg.
getTopic(),
false ));
279 auto it = bag_topic_to_sensor_id.find(bag_topic);
280 if (it != bag_topic_to_sensor_id.end()) {
281 const std::string& sensor_id = it->second.id;
282 if (msg.
isType<sensor_msgs::LaserScan>()) {
283 node.HandleLaserScanMessage(trajectory_id, sensor_id,
286 if (msg.
isType<sensor_msgs::MultiEchoLaserScan>()) {
287 node.HandleMultiEchoLaserScanMessage(
288 trajectory_id, sensor_id,
289 msg.
instantiate<sensor_msgs::MultiEchoLaserScan>());
291 if (msg.
isType<sensor_msgs::PointCloud2>()) {
292 node.HandlePointCloud2Message(
293 trajectory_id, sensor_id,
296 if (msg.
isType<sensor_msgs::Imu>()) {
297 node.HandleImuMessage(trajectory_id, sensor_id,
300 if (msg.
isType<nav_msgs::Odometry>()) {
301 node.HandleOdometryMessage(trajectory_id, sensor_id,
304 if (msg.
isType<sensor_msgs::NavSatFix>()) {
305 node.HandleNavSatFixMessage(trajectory_id, sensor_id,
308 if (msg.
isType<cartographer_ros_msgs::LandmarkList>()) {
309 node.HandleLandmarkMessage(
310 trajectory_id, sensor_id,
311 msg.
instantiate<cartographer_ros_msgs::LandmarkList>());
315 clock_publisher.publish(clock);
317 if (is_last_message_in_bag) {
318 node.FinishTrajectory(trajectory_id);
325 clock_republish_timer.start();
326 node.RunFinalOptimization();
328 const std::chrono::time_point<std::chrono::steady_clock> end_time =
329 std::chrono::steady_clock::now();
330 const double wall_clock_seconds =
331 std::chrono::duration_cast<std::chrono::duration<double>>(end_time -
335 LOG(INFO) <<
"Elapsed wall clock time: " << wall_clock_seconds <<
" s";
337 timespec cpu_timespec = {};
338 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_timespec);
339 LOG(INFO) <<
"Elapsed CPU time: " 340 << (cpu_timespec.tv_sec + 1e-9 * cpu_timespec.tv_nsec) <<
" s";
342 CHECK_EQ(getrusage(RUSAGE_SELF, &usage), 0) << strerror(errno);
343 LOG(INFO) <<
"Peak memory usage: " << usage.ru_maxrss <<
" KiB";
346 if (::
ros::ok() && bag_filenames.size() > 0) {
347 const std::string output_filename = bag_filenames.front();
348 const std::string suffix =
".pbstream";
349 const std::string state_output_filename = output_filename + suffix;
350 LOG(INFO) <<
"Writing state to '" << state_output_filename <<
"'...";
351 node.SerializeState(state_output_filename);
353 if (FLAGS_keep_running) {
354 ::ros::waitForShutdown();
DEFINE_double(skip_seconds, 0, "Optional amount of seconds to skip from the beginning " "(i.e. when the earliest bag starts.). ")
constexpr char kClockTopic[]
void RunOfflineNode(const MapBuilderFactory &map_builder_factory)
std::function< std::unique_ptr<::cartographer::mapping::MapBuilderInterface >(const ::cartographer::mapping::proto::MapBuilderOptions &)> MapBuilderFactory
const ::ros::Duration kDelay
::cartographer::mapping::proto::MapBuilderOptions map_builder_options
void setUsingDedicatedThread(bool value)
constexpr char kTfTopic[]
constexpr double kClockPublishFrequencySec
std::vector< std::set<::cartographer::mapping::TrajectoryBuilderInterface::SensorId > > ComputeDefaultSensorIdsForMultipleBags(const std::vector< TrajectoryOptions > &bags_options) const
ros::Time const & getTime() const
const Time TIME_MIN(0, 1)
constexpr int kLatestOnlyPublisherQueueSize
constexpr int kSingleThreaded
DEFINE_string(configuration_directory, "", "First directory in which configuration files are searched, " "second is always the Cartographer installation to allow " "including files from there.")
constexpr char kTfStaticTopic[]
WallTimer createWallTimer(WallDuration period, void(T::*callback)(const WallTimerEvent &), T *obj, bool oneshot=false, bool autostart=true) const
std::tuple< NodeOptions, TrajectoryOptions > LoadOptions(const std::string &configuration_directory, const std::string &configuration_basename)
void publish(const boost::shared_ptr< M > &message) const
void AddPlayableBag(PlayableBag playable_bag)
std::vector< std::string > SplitString(const std::string &input, const char delimiter)
std::string resolveName(const std::string &name, bool remap=true) const
ROSTIME_DECL const Time TIME_MAX
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void LoadState(const std::string &state_filename, bool load_frozen_state)
boost::shared_ptr< T > instantiate() const
::ros::NodeHandle * node_handle()
DEFINE_bool(use_bag_transforms, true, "Whether to read, use and republish transforms from bags.")
double lookup_transform_timeout_sec
std::string const & getTopic() const
tf2_ros::Buffer * tf_buffer
std::vector< geometry_msgs::TransformStamped > ReadStaticTransformsFromUrdf(const std::string &urdf_filename, tf2_ros::Buffer *const tf_buffer)