assets_writer.cc
Go to the documentation of this file.
1 /*
2  * Copyright 2016 The Cartographer Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
18 
19 #include <algorithm>
20 #include <fstream>
21 #include <iostream>
22 
31 #include "cartographer/mapping/proto/pose_graph.pb.h"
32 #include "cartographer/mapping/proto/trajectory_builder_options.pb.h"
41 #include "gflags/gflags.h"
42 #include "glog/logging.h"
43 #include "ros/ros.h"
44 #include "ros/time.h"
45 #include "rosbag/bag.h"
46 #include "rosbag/view.h"
47 #include "tf2_eigen/tf2_eigen.h"
48 #include "tf2_msgs/TFMessage.h"
49 #include "tf2_ros/buffer.h"
50 #include "urdf/model.h"
51 
52 namespace cartographer_ros {
53 namespace {
54 
55 constexpr char kTfStaticTopic[] = "/tf_static";
56 namespace carto = ::cartographer;
57 
58 std::unique_ptr<carto::io::PointsProcessorPipelineBuilder>
59 CreatePipelineBuilder(
60  const std::vector<carto::mapping::proto::Trajectory>& trajectories,
61  const std::string file_prefix) {
62  const auto file_writer_factory =
64  auto builder =
65  carto::common::make_unique<carto::io::PointsProcessorPipelineBuilder>();
66  carto::io::RegisterBuiltInPointsProcessors(trajectories, file_writer_factory,
67  builder.get());
69  [file_writer_factory](
70  carto::common::LuaParameterDictionary* const dictionary,
71  carto::io::PointsProcessor* const next)
72  -> std::unique_ptr<carto::io::PointsProcessor> {
74  file_writer_factory, dictionary, next);
75  });
76  return builder;
77 }
78 
79 std::unique_ptr<carto::common::LuaParameterDictionary> LoadLuaDictionary(
80  const std::string& configuration_directory,
81  const std::string& configuration_basename) {
82  auto file_resolver =
83  carto::common::make_unique<carto::common::ConfigurationFileResolver>(
84  std::vector<std::string>{configuration_directory});
85 
86  const std::string code =
87  file_resolver->GetFileContentOrDie(configuration_basename);
88  auto lua_parameter_dictionary =
89  carto::common::make_unique<carto::common::LuaParameterDictionary>(
90  code, std::move(file_resolver));
91  return lua_parameter_dictionary;
92 }
93 
94 template <typename T>
95 std::unique_ptr<carto::io::PointsBatch> HandleMessage(
96  const T& message, const std::string& tracking_frame,
98  const carto::transform::TransformInterpolationBuffer&
99  transform_interpolation_buffer) {
100  const carto::common::Time start_time = FromRos(message.header.stamp);
101 
102  auto points_batch = carto::common::make_unique<carto::io::PointsBatch>();
103  points_batch->start_time = start_time;
104  points_batch->frame_id = message.header.frame_id;
105 
106  carto::sensor::PointCloudWithIntensities point_cloud;
107  carto::common::Time point_cloud_time;
108  std::tie(point_cloud, point_cloud_time) =
110  CHECK_EQ(point_cloud.intensities.size(), point_cloud.points.size());
111 
112  for (size_t i = 0; i < point_cloud.points.size(); ++i) {
113  const carto::common::Time time =
114  point_cloud_time + carto::common::FromSeconds(point_cloud.points[i][3]);
115  if (!transform_interpolation_buffer.Has(time)) {
116  continue;
117  }
118  const carto::transform::Rigid3d tracking_to_map =
119  transform_interpolation_buffer.Lookup(time);
120  const carto::transform::Rigid3d sensor_to_tracking =
121  ToRigid3d(tf_buffer.lookupTransform(
122  tracking_frame, message.header.frame_id, ToRos(time)));
123  const carto::transform::Rigid3f sensor_to_map =
124  (tracking_to_map * sensor_to_tracking).cast<float>();
125  points_batch->points.push_back(sensor_to_map *
126  point_cloud.points[i].head<3>());
127  points_batch->intensities.push_back(point_cloud.intensities[i]);
128  // We use the last transform for the origin, which is approximately correct.
129  points_batch->origin = sensor_to_map * Eigen::Vector3f::Zero();
130  }
131  if (points_batch->points.empty()) {
132  return nullptr;
133  }
134  return points_batch;
135 }
136 
137 } // namespace
138 
139 AssetsWriter::AssetsWriter(const std::string& pose_graph_filename,
140  const std::vector<std::string>& bag_filenames,
141  const std::string& output_file_prefix)
142  : bag_filenames_(bag_filenames),
143  pose_graph_(
144  carto::io::DeserializePoseGraphFromFile(pose_graph_filename)) {
145  CHECK_EQ(pose_graph_.trajectory_size(), bag_filenames_.size())
146  << "Pose graphs contains " << pose_graph_.trajectory_size()
147  << " trajectories while " << bag_filenames_.size()
148  << " bags were provided. This tool requires one bag for each "
149  "trajectory in the same order as the correponding trajectories in the "
150  "pose graph proto.";
151 
152  // This vector must outlive the pipeline.
153  all_trajectories_ = std::vector<::cartographer::mapping::proto::Trajectory>(
154  pose_graph_.trajectory().begin(), pose_graph_.trajectory().end());
155 
156  const std::string file_prefix = !output_file_prefix.empty()
157  ? output_file_prefix
158  : bag_filenames_.front() + "_";
160  CreatePipelineBuilder(all_trajectories_, file_prefix);
161 }
162 
164  const std::string& name,
166  point_pipeline_builder_->Register(name, factory);
167 }
168 
169 void AssetsWriter::Run(const std::string& configuration_directory,
170  const std::string& configuration_basename,
171  const std::string& urdf_filename,
172  const bool use_bag_transforms) {
173  const auto lua_parameter_dictionary =
174  LoadLuaDictionary(configuration_directory, configuration_basename);
175 
176  std::vector<std::unique_ptr<carto::io::PointsProcessor>> pipeline =
177  point_pipeline_builder_->CreatePipeline(
178  lua_parameter_dictionary->GetDictionary("pipeline").get());
179  const std::string tracking_frame =
180  lua_parameter_dictionary->GetString("tracking_frame");
181 
182  do {
183  for (size_t trajectory_id = 0; trajectory_id < bag_filenames_.size();
184  ++trajectory_id) {
185  const carto::mapping::proto::Trajectory& trajectory_proto =
186  pose_graph_.trajectory(trajectory_id);
187  const std::string& bag_filename = bag_filenames_[trajectory_id];
188  LOG(INFO) << "Processing " << bag_filename << "...";
189  if (trajectory_proto.node_size() == 0) {
190  continue;
191  }
193  if (!urdf_filename.empty()) {
194  ReadStaticTransformsFromUrdf(urdf_filename, &tf_buffer);
195  }
196 
197  const carto::transform::TransformInterpolationBuffer
198  transform_interpolation_buffer(trajectory_proto);
199  rosbag::Bag bag;
200  bag.open(bag_filename, rosbag::bagmode::Read);
201  rosbag::View view(bag);
202  const ::ros::Time begin_time = view.getBeginTime();
203  const double duration_in_seconds =
204  (view.getEndTime() - begin_time).toSec();
205 
206  // We need to keep 'tf_buffer' small because it becomes very inefficient
207  // otherwise. We make sure that tf_messages are published before any data
208  // messages, so that tf lookups always work.
209  std::deque<rosbag::MessageInstance> delayed_messages;
210  // We publish tf messages one second earlier than other messages. Under
211  // the assumption of higher frequency tf this should ensure that tf can
212  // always interpolate.
213  const ::ros::Duration kDelay(1.);
214  for (const rosbag::MessageInstance& message : view) {
215  if (use_bag_transforms && message.isType<tf2_msgs::TFMessage>()) {
216  auto tf_message = message.instantiate<tf2_msgs::TFMessage>();
217  for (const auto& transform : tf_message->transforms) {
218  try {
219  tf_buffer.setTransform(transform, "unused_authority",
220  message.getTopic() == kTfStaticTopic);
221  } catch (const tf2::TransformException& ex) {
222  LOG(WARNING) << ex.what();
223  }
224  }
225  }
226 
227  while (!delayed_messages.empty() && delayed_messages.front().getTime() <
228  message.getTime() - kDelay) {
229  const rosbag::MessageInstance& delayed_message =
230  delayed_messages.front();
231 
232  std::unique_ptr<carto::io::PointsBatch> points_batch;
233  if (delayed_message.isType<sensor_msgs::PointCloud2>()) {
234  points_batch = HandleMessage(
235  *delayed_message.instantiate<sensor_msgs::PointCloud2>(),
236  tracking_frame, tf_buffer, transform_interpolation_buffer);
237  } else if (delayed_message
238  .isType<sensor_msgs::MultiEchoLaserScan>()) {
239  points_batch = HandleMessage(
240  *delayed_message.instantiate<sensor_msgs::MultiEchoLaserScan>(),
241  tracking_frame, tf_buffer, transform_interpolation_buffer);
242  } else if (delayed_message.isType<sensor_msgs::LaserScan>()) {
243  points_batch = HandleMessage(
244  *delayed_message.instantiate<sensor_msgs::LaserScan>(),
245  tracking_frame, tf_buffer, transform_interpolation_buffer);
246  }
247  if (points_batch != nullptr) {
248  points_batch->trajectory_id = trajectory_id;
249  pipeline.back()->Process(std::move(points_batch));
250  }
251  delayed_messages.pop_front();
252  }
253  delayed_messages.push_back(message);
254  LOG_EVERY_N(INFO, 100000)
255  << "Processed " << (message.getTime() - begin_time).toSec()
256  << " of " << duration_in_seconds << " bag time seconds...";
257  }
258  bag.close();
259  }
260  } while (pipeline.back()->Flush() ==
261  carto::io::PointsProcessor::FlushResult::kRestartStream);
262 }
263 
265  const std::string& file_path) {
266  const auto file_writer_factory = [file_path](const std::string& filename) {
267  return carto::common::make_unique<carto::io::StreamFileWriter>(file_path +
268  filename);
269  };
270  return file_writer_factory;
271 }
272 
273 } // namespace cartographer_ros
void Run(const std::string &configuration_directory, const std::string &configuration_basename, const std::string &urdf_filename, bool use_bag_transforms)
std::function< std::unique_ptr< PointsProcessor >(common::LuaParameterDictionary *, PointsProcessor *next)> FactoryFunction
std::function< std::unique_ptr< FileWriter >(const std::string &filename)> FileWriterFactory
filename
::cartographer::io::FileWriterFactory CreateFileWriterFactory(const std::string &file_path)
const ::ros::Duration kDelay
Definition: offline_node.cc:77
void open(std::string const &filename, uint32_t mode=bagmode::Read)
AssetsWriter(const std::string &pose_graph_filename, const std::vector< std::string > &bag_filenames, const std::string &output_file_prefix)
bool setTransform(const geometry_msgs::TransformStamped &transform, const std::string &authority, bool is_static=false)
Rigid3d ToRigid3d(const geometry_msgs::TransformStamped &transform)
std::vector<::cartographer::mapping::proto::Trajectory > all_trajectories_
Definition: assets_writer.h:55
constexpr char kTfStaticTopic[]
Definition: offline_node.cc:70
const common::Time time
void close()
static std::unique_ptr< RosMapWritingPointsProcessor > FromDictionary(::cartographer::io::FileWriterFactory file_writer_factory, ::cartographer::common::LuaParameterDictionary *dictionary, PointsProcessor *next)
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
std::vector< std::string > bag_filenames_
Definition: assets_writer.h:54
ros::Time getBeginTime()
std::unique_ptr<::cartographer::io::PointsProcessorPipelineBuilder > point_pipeline_builder_
Definition: assets_writer.h:58
void RegisterPointsProcessor(const std::string &name, cartographer::io::PointsProcessorPipelineBuilder::FactoryFunction factory)
boost::shared_ptr< T > instantiate() const
::cartographer::mapping::proto::PoseGraph pose_graph_
Definition: assets_writer.h:56
mapping::proto::PoseGraph DeserializePoseGraphFromFile(const std::string &file_name)
::cartographer::common::Time FromRos(const ::ros::Time &time)
ros::Time getEndTime()
PoseGraph *const pose_graph_
std::tuple<::cartographer::sensor::PointCloudWithIntensities, ::cartographer::common::Time > ToPointCloudWithIntensities(const sensor_msgs::LaserScan &msg)
::ros::Time ToRos(::cartographer::common::Time time)
tf2_ros::Buffer * tf_buffer
std::vector< geometry_msgs::TransformStamped > ReadStaticTransformsFromUrdf(const std::string &urdf_filename, tf2_ros::Buffer *const tf_buffer)
Definition: urdf_reader.cc:27


cartographer_ros
Author(s): The Cartographer Authors
autogenerated on Mon Feb 28 2022 22:06:05