assets_writer_main.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 
17 #include <algorithm>
18 #include <fstream>
19 #include <iostream>
20 #include <string>
21 #include <vector>
22 
29 #include "cartographer/mapping/proto/sparse_pose_graph.pb.h"
36 #include "gflags/gflags.h"
37 #include "glog/logging.h"
38 #include "ros/ros.h"
39 #include "ros/time.h"
40 #include "rosbag/bag.h"
41 #include "rosbag/view.h"
42 #include "tf2_eigen/tf2_eigen.h"
43 #include "tf2_msgs/TFMessage.h"
44 #include "tf2_ros/buffer.h"
45 #include "urdf/model.h"
46 
47 DEFINE_string(configuration_directory, "",
48  "First directory in which configuration files are searched, "
49  "second is always the Cartographer installation to allow "
50  "including files from there.");
51 DEFINE_string(configuration_basename, "",
52  "Basename, i.e. not containing any directory prefix, of the "
53  "configuration file.");
55  urdf_filename, "",
56  "URDF file that contains static links for your sensor configuration.");
57 DEFINE_string(bag_filename, "", "Bag to process.");
59  pose_graph_filename, "",
60  "Proto containing the pose graph written by /write_assets service.");
61 DEFINE_bool(use_bag_transforms, true,
62  "Whether to read and use the transforms from the bag.");
63 
64 namespace cartographer_ros {
65 namespace {
66 
67 constexpr char kTfStaticTopic[] = "/tf_static";
68 namespace carto = ::cartographer;
69 
70 // TODO(hrapp): We discovered that using tf_buffer with a large CACHE
71 // is very inefficient. Switch asset writer to use our own
72 // TransformInterpolationBuffer.
73 void ReadTransformsFromBag(const string& bag_filename,
74  tf2_ros::Buffer* const tf_buffer) {
75  rosbag::Bag bag;
76  bag.open(bag_filename, rosbag::bagmode::Read);
77  rosbag::View view(bag);
78 
79  const ::ros::Time begin_time = view.getBeginTime();
80  const double duration_in_seconds = (view.getEndTime() - begin_time).toSec();
81  for (const rosbag::MessageInstance& msg : view) {
82  if (msg.isType<tf2_msgs::TFMessage>()) {
83  const auto tf_msg = msg.instantiate<tf2_msgs::TFMessage>();
84  for (const auto& transform : tf_msg->transforms) {
85  try {
86  // TODO(damonkohler): Handle topic remapping.
87  tf_buffer->setTransform(transform, "unused_authority",
88  msg.getTopic() == kTfStaticTopic);
89  } catch (const tf2::TransformException& ex) {
90  LOG(WARNING) << ex.what();
91  }
92  }
93  }
94  LOG_EVERY_N(INFO, 100000)
95  << "Processed " << (msg.getTime() - begin_time).toSec() << " of "
96  << duration_in_seconds << " bag time seconds...";
97  }
98 
99  bag.close();
100 }
101 
102 template <typename T>
103 void HandleMessage(
104  const T& message, const string& tracking_frame,
105  const tf2_ros::Buffer& tf_buffer,
106  const carto::transform::TransformInterpolationBuffer&
107  transform_interpolation_buffer,
108  const std::vector<std::unique_ptr<carto::io::PointsProcessor>>& pipeline) {
109  const carto::common::Time time = FromRos(message.header.stamp);
110  if (!transform_interpolation_buffer.Has(time)) {
111  return;
112  }
113 
114  const carto::transform::Rigid3d tracking_to_map =
115  transform_interpolation_buffer.Lookup(time);
116  const carto::transform::Rigid3d sensor_to_tracking =
117  ToRigid3d(tf_buffer.lookupTransform(
118  tracking_frame, message.header.frame_id, message.header.stamp));
119  const carto::transform::Rigid3f sensor_to_map =
120  (tracking_to_map * sensor_to_tracking).cast<float>();
121 
122  auto batch = carto::common::make_unique<carto::io::PointsBatch>();
123  batch->time = time;
124  batch->origin = sensor_to_map * Eigen::Vector3f::Zero();
125  batch->frame_id = message.header.frame_id;
126 
127  carto::sensor::PointCloudWithIntensities point_cloud =
129  CHECK(point_cloud.intensities.size() == point_cloud.points.size());
130 
131  for (size_t i = 0; i < point_cloud.points.size(); ++i) {
132  batch->points.push_back(sensor_to_map * point_cloud.points[i]);
133  batch->intensities.push_back(point_cloud.intensities[i]);
134  }
135  pipeline.back()->Process(std::move(batch));
136 }
137 
138 void Run(const string& pose_graph_filename, const string& bag_filename,
139  const string& configuration_directory,
140  const string& configuration_basename, const string& urdf_filename) {
141  auto file_resolver =
142  carto::common::make_unique<carto::common::ConfigurationFileResolver>(
143  std::vector<string>{configuration_directory});
144  const string code =
145  file_resolver->GetFileContentOrDie(configuration_basename);
146  carto::common::LuaParameterDictionary lua_parameter_dictionary(
147  code, std::move(file_resolver));
148 
149  std::ifstream stream(pose_graph_filename.c_str());
150  carto::mapping::proto::SparsePoseGraph pose_graph_proto;
151  CHECK(pose_graph_proto.ParseFromIstream(&stream));
152  CHECK_EQ(pose_graph_proto.trajectory_size(), 1)
153  << "Only pose graphs containing a single trajectory are supported.";
154  const carto::mapping::proto::Trajectory& trajectory_proto =
155  pose_graph_proto.trajectory(0);
156  CHECK_GT(trajectory_proto.node_size(), 0)
157  << "Trajectory does not contain any nodes.";
158 
159  const auto file_writer_factory = [](const string& filename) {
160  return carto::common::make_unique<carto::io::StreamFileWriter>(filename);
161  };
162 
163  carto::io::PointsProcessorPipelineBuilder builder;
164  carto::io::RegisterBuiltInPointsProcessors(trajectory_proto,
165  file_writer_factory, &builder);
166  std::vector<std::unique_ptr<carto::io::PointsProcessor>> pipeline =
167  builder.CreatePipeline(
168  lua_parameter_dictionary.GetDictionary("pipeline").get());
169 
170  tf2_ros::Buffer tf_buffer(::ros::DURATION_MAX);
171 
172  if (FLAGS_use_bag_transforms) {
173  LOG(INFO) << "Pre-loading transforms from bag...";
174  ReadTransformsFromBag(bag_filename, &tf_buffer);
175  }
176 
177  if (!urdf_filename.empty()) {
178  ReadStaticTransformsFromUrdf(urdf_filename, &tf_buffer);
179  }
180 
181  const string tracking_frame =
182  lua_parameter_dictionary.GetString("tracking_frame");
183  const auto transform_interpolation_buffer =
184  carto::transform::TransformInterpolationBuffer::FromTrajectory(
185  trajectory_proto);
186  LOG(INFO) << "Processing pipeline...";
187  do {
188  rosbag::Bag bag;
189  bag.open(bag_filename, rosbag::bagmode::Read);
190  rosbag::View view(bag);
191  const ::ros::Time begin_time = view.getBeginTime();
192  const double duration_in_seconds = (view.getEndTime() - begin_time).toSec();
193 
194  for (const rosbag::MessageInstance& message : view) {
195  if (message.isType<sensor_msgs::PointCloud2>()) {
196  HandleMessage(*message.instantiate<sensor_msgs::PointCloud2>(),
197  tracking_frame, tf_buffer,
198  *transform_interpolation_buffer, pipeline);
199  }
200  if (message.isType<sensor_msgs::MultiEchoLaserScan>()) {
201  HandleMessage(*message.instantiate<sensor_msgs::MultiEchoLaserScan>(),
202  tracking_frame, tf_buffer,
203  *transform_interpolation_buffer, pipeline);
204  }
205  if (message.isType<sensor_msgs::LaserScan>()) {
206  HandleMessage(*message.instantiate<sensor_msgs::LaserScan>(),
207  tracking_frame, tf_buffer,
208  *transform_interpolation_buffer, pipeline);
209  }
210  LOG_EVERY_N(INFO, 100000)
211  << "Processed " << (message.getTime() - begin_time).toSec() << " of "
212  << duration_in_seconds << " bag time seconds...";
213  }
214  bag.close();
215  } while (pipeline.back()->Flush() ==
216  carto::io::PointsProcessor::FlushResult::kRestartStream);
217 }
218 
219 } // namespace
220 } // namespace cartographer_ros
221 
222 int main(int argc, char** argv) {
223  FLAGS_alsologtostderr = true;
224  google::InitGoogleLogging(argv[0]);
225  google::ParseCommandLineFlags(&argc, &argv, true);
226 
227  CHECK(!FLAGS_configuration_directory.empty())
228  << "-configuration_directory is missing.";
229  CHECK(!FLAGS_configuration_basename.empty())
230  << "-configuration_basename is missing.";
231  CHECK(!FLAGS_bag_filename.empty()) << "-bag_filename is missing.";
232  CHECK(!FLAGS_pose_graph_filename.empty())
233  << "-pose_graph_filename is missing.";
234 
235  ::cartographer_ros::Run(FLAGS_pose_graph_filename, FLAGS_bag_filename,
236  FLAGS_configuration_directory,
237  FLAGS_configuration_basename, FLAGS_urdf_filename);
238 }
filename
void open(std::string const &filename, uint32_t mode=bagmode::Read)
bool setTransform(const geometry_msgs::TransformStamped &transform, const std::string &authority, bool is_static=false)
Rigid3d ToRigid3d(const geometry_msgs::TransformStamped &transform)
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
DEFINE_string(configuration_directory,"","First directory in which configuration files are searched, ""second is always the Cartographer installation to allow ""including files from there.")
int main(int argc, char **argv)
PointCloudWithIntensities ToPointCloudWithIntensities(const sensor_msgs::LaserScan &msg)
const common::Time time
void close()
DEFINE_bool(use_bag_transforms, true,"Whether to read and use the transforms from the bag.")
::cartographer::common::Time FromRos(const ::ros::Time &time)
void Run(const std::string &configuration_directory, const std::string &configuration_basename)
ROSTIME_DECL const Duration DURATION_MAX
std::vector< geometry_msgs::TransformStamped > ReadStaticTransformsFromUrdf(const string &urdf_filename, tf2_ros::Buffer *const tf_buffer)
Definition: urdf_reader.cc:27


cartographer_ros
Author(s):
autogenerated on Mon Jun 10 2019 12:59:40