parent
4cd812d806
commit
8b239ded86
|
@ -29,7 +29,6 @@
|
||||||
#include "cartographer/sensor/point_cloud.h"
|
#include "cartographer/sensor/point_cloud.h"
|
||||||
#include "cartographer/sensor/range_data.h"
|
#include "cartographer/sensor/range_data.h"
|
||||||
#include "cartographer/transform/transform_interpolation_buffer.h"
|
#include "cartographer/transform/transform_interpolation_buffer.h"
|
||||||
#include "cartographer_ros/bag_reader.h"
|
|
||||||
#include "cartographer_ros/msg_conversion.h"
|
#include "cartographer_ros/msg_conversion.h"
|
||||||
#include "cartographer_ros/time_conversion.h"
|
#include "cartographer_ros/time_conversion.h"
|
||||||
#include "cartographer_ros/urdf_reader.h"
|
#include "cartographer_ros/urdf_reader.h"
|
||||||
|
@ -64,8 +63,41 @@ DEFINE_bool(use_bag_transforms, true,
|
||||||
namespace cartographer_ros {
|
namespace cartographer_ros {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr char kTfStaticTopic[] = "/tf_static";
|
||||||
namespace carto = ::cartographer;
|
namespace carto = ::cartographer;
|
||||||
|
|
||||||
|
// TODO(hrapp): We discovered that using tf_buffer with a large CACHE
|
||||||
|
// is very inefficient. Switch asset writer to use our own
|
||||||
|
// TransformInterpolationBuffer.
|
||||||
|
void ReadTransformsFromBag(const string& bag_filename,
|
||||||
|
tf2_ros::Buffer* const tf_buffer) {
|
||||||
|
rosbag::Bag bag;
|
||||||
|
bag.open(bag_filename, rosbag::bagmode::Read);
|
||||||
|
rosbag::View view(bag);
|
||||||
|
|
||||||
|
const ::ros::Time begin_time = view.getBeginTime();
|
||||||
|
const double duration_in_seconds = (view.getEndTime() - begin_time).toSec();
|
||||||
|
for (const rosbag::MessageInstance& msg : view) {
|
||||||
|
if (msg.isType<tf2_msgs::TFMessage>()) {
|
||||||
|
const auto tf_msg = msg.instantiate<tf2_msgs::TFMessage>();
|
||||||
|
for (const auto& transform : tf_msg->transforms) {
|
||||||
|
try {
|
||||||
|
// TODO(damonkohler): Handle topic remapping.
|
||||||
|
tf_buffer->setTransform(transform, "unused_authority",
|
||||||
|
msg.getTopic() == kTfStaticTopic);
|
||||||
|
} catch (const tf2::TransformException& ex) {
|
||||||
|
LOG(WARNING) << ex.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_EVERY_N(INFO, 100000)
|
||||||
|
<< "Processed " << (msg.getTime() - begin_time).toSec() << " of "
|
||||||
|
<< duration_in_seconds << " bag time seconds...";
|
||||||
|
}
|
||||||
|
|
||||||
|
bag.close();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void HandleMessage(
|
void HandleMessage(
|
||||||
const T& message, const string& tracking_frame,
|
const T& message, const string& tracking_frame,
|
||||||
|
@ -128,16 +160,15 @@ void Run(const string& trajectory_filename, const string& bag_filename,
|
||||||
builder.CreatePipeline(
|
builder.CreatePipeline(
|
||||||
lua_parameter_dictionary.GetDictionary("pipeline").get());
|
lua_parameter_dictionary.GetDictionary("pipeline").get());
|
||||||
|
|
||||||
auto tf_buffer =
|
tf2_ros::Buffer tf_buffer(::ros::DURATION_MAX);
|
||||||
::cartographer::common::make_unique<tf2_ros::Buffer>(::ros::DURATION_MAX);
|
|
||||||
|
|
||||||
if (FLAGS_use_bag_transforms) {
|
if (FLAGS_use_bag_transforms) {
|
||||||
LOG(INFO) << "Pre-loading transforms from bag...";
|
LOG(INFO) << "Pre-loading transforms from bag...";
|
||||||
ReadTransformsFromBag(bag_filename, tf_buffer.get());
|
ReadTransformsFromBag(bag_filename, &tf_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!urdf_filename.empty()) {
|
if (!urdf_filename.empty()) {
|
||||||
ReadStaticTransformsFromUrdf(urdf_filename, tf_buffer.get());
|
ReadStaticTransformsFromUrdf(urdf_filename, &tf_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const string tracking_frame =
|
const string tracking_frame =
|
||||||
|
@ -156,17 +187,17 @@ void Run(const string& trajectory_filename, const string& bag_filename,
|
||||||
for (const rosbag::MessageInstance& message : view) {
|
for (const rosbag::MessageInstance& message : view) {
|
||||||
if (message.isType<sensor_msgs::PointCloud2>()) {
|
if (message.isType<sensor_msgs::PointCloud2>()) {
|
||||||
HandleMessage(*message.instantiate<sensor_msgs::PointCloud2>(),
|
HandleMessage(*message.instantiate<sensor_msgs::PointCloud2>(),
|
||||||
tracking_frame, *tf_buffer,
|
tracking_frame, tf_buffer,
|
||||||
*transform_interpolation_buffer, pipeline);
|
*transform_interpolation_buffer, pipeline);
|
||||||
}
|
}
|
||||||
if (message.isType<sensor_msgs::MultiEchoLaserScan>()) {
|
if (message.isType<sensor_msgs::MultiEchoLaserScan>()) {
|
||||||
HandleMessage(*message.instantiate<sensor_msgs::MultiEchoLaserScan>(),
|
HandleMessage(*message.instantiate<sensor_msgs::MultiEchoLaserScan>(),
|
||||||
tracking_frame, *tf_buffer,
|
tracking_frame, tf_buffer,
|
||||||
*transform_interpolation_buffer, pipeline);
|
*transform_interpolation_buffer, pipeline);
|
||||||
}
|
}
|
||||||
if (message.isType<sensor_msgs::LaserScan>()) {
|
if (message.isType<sensor_msgs::LaserScan>()) {
|
||||||
HandleMessage(*message.instantiate<sensor_msgs::LaserScan>(),
|
HandleMessage(*message.instantiate<sensor_msgs::LaserScan>(),
|
||||||
tracking_frame, *tf_buffer,
|
tracking_frame, tf_buffer,
|
||||||
*transform_interpolation_buffer, pipeline);
|
*transform_interpolation_buffer, pipeline);
|
||||||
}
|
}
|
||||||
LOG_EVERY_N(INFO, 100000)
|
LOG_EVERY_N(INFO, 100000)
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 The Cartographer Authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "cartographer_ros/bag_reader.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "cartographer/common/make_unique.h"
|
|
||||||
#include "glog/logging.h"
|
|
||||||
#include "rosbag/bag.h"
|
|
||||||
#include "rosbag/view.h"
|
|
||||||
#include "tf2_msgs/TFMessage.h"
|
|
||||||
|
|
||||||
namespace cartographer_ros {
|
|
||||||
|
|
||||||
constexpr char kTfStaticTopic[] = "/tf_static";
|
|
||||||
|
|
||||||
void ReadTransformsFromBag(const string& bag_filename,
|
|
||||||
tf2_ros::Buffer* const tf_buffer) {
|
|
||||||
rosbag::Bag bag;
|
|
||||||
bag.open(bag_filename, rosbag::bagmode::Read);
|
|
||||||
rosbag::View view(bag);
|
|
||||||
|
|
||||||
const ::ros::Time begin_time = view.getBeginTime();
|
|
||||||
const double duration_in_seconds = (view.getEndTime() - begin_time).toSec();
|
|
||||||
for (const rosbag::MessageInstance& msg : view) {
|
|
||||||
if (msg.isType<tf2_msgs::TFMessage>()) {
|
|
||||||
const auto tf_msg = msg.instantiate<tf2_msgs::TFMessage>();
|
|
||||||
for (const auto& transform : tf_msg->transforms) {
|
|
||||||
try {
|
|
||||||
// TODO(damonkohler): Handle topic remapping.
|
|
||||||
tf_buffer->setTransform(transform, "unused_authority",
|
|
||||||
msg.getTopic() == kTfStaticTopic);
|
|
||||||
} catch (const tf2::TransformException& ex) {
|
|
||||||
LOG(WARNING) << ex.what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG_EVERY_N(INFO, 100000)
|
|
||||||
<< "Processed " << (msg.getTime() - begin_time).toSec() << " of "
|
|
||||||
<< duration_in_seconds << " bag time seconds...";
|
|
||||||
}
|
|
||||||
|
|
||||||
bag.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace cartographer_ros
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 The Cartographer Authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CARTOGRAPHER_ROS_BAG_READER_H_
|
|
||||||
#define CARTOGRAPHER_ROS_BAG_READER_H_
|
|
||||||
|
|
||||||
#include "cartographer/common/port.h"
|
|
||||||
#include "tf2_ros/buffer.h"
|
|
||||||
|
|
||||||
namespace cartographer_ros {
|
|
||||||
|
|
||||||
void ReadTransformsFromBag(const string& bag_filename,
|
|
||||||
tf2_ros::Buffer* const tf_buffer);
|
|
||||||
|
|
||||||
} // namespace cartographer_ros
|
|
||||||
|
|
||||||
#endif // CARTOGRAPHER_ROS_BAG_READER_H_
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include "cartographer/common/lua_parameter_dictionary.h"
|
#include "cartographer/common/lua_parameter_dictionary.h"
|
||||||
#include "cartographer/common/make_unique.h"
|
#include "cartographer/common/make_unique.h"
|
||||||
#include "cartographer/common/port.h"
|
#include "cartographer/common/port.h"
|
||||||
#include "cartographer_ros/bag_reader.h"
|
|
||||||
#include "cartographer_ros/node.h"
|
#include "cartographer_ros/node.h"
|
||||||
#include "cartographer_ros/ros_log_sink.h"
|
#include "cartographer_ros/ros_log_sink.h"
|
||||||
#include "cartographer_ros/urdf_reader.h"
|
#include "cartographer_ros/urdf_reader.h"
|
||||||
|
@ -53,9 +52,10 @@ DEFINE_bool(use_bag_transforms, true,
|
||||||
namespace cartographer_ros {
|
namespace cartographer_ros {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr int kLatestOnlyPublisherQueueSize = 1;
|
|
||||||
constexpr char kClockTopic[] = "clock";
|
constexpr char kClockTopic[] = "clock";
|
||||||
|
constexpr char kTfStaticTopic[] = "/tf_static";
|
||||||
constexpr char kTfTopic[] = "tf";
|
constexpr char kTfTopic[] = "tf";
|
||||||
|
constexpr int kLatestOnlyPublisherQueueSize = 1;
|
||||||
|
|
||||||
volatile std::sig_atomic_t sigint_triggered = 0;
|
volatile std::sig_atomic_t sigint_triggered = 0;
|
||||||
|
|
||||||
|
@ -86,29 +86,21 @@ NodeOptions LoadOptions() {
|
||||||
void Run(const std::vector<string>& bag_filenames) {
|
void Run(const std::vector<string>& bag_filenames) {
|
||||||
auto options = LoadOptions();
|
auto options = LoadOptions();
|
||||||
|
|
||||||
auto tf_buffer =
|
tf2_ros::Buffer tf_buffer;
|
||||||
::cartographer::common::make_unique<tf2_ros::Buffer>(::ros::DURATION_MAX);
|
|
||||||
|
|
||||||
if (FLAGS_use_bag_transforms) {
|
|
||||||
LOG(INFO) << "Pre-loading transforms from bag...";
|
|
||||||
// TODO(damonkohler): Support multi-trajectory.
|
|
||||||
CHECK_EQ(bag_filenames.size(), 1);
|
|
||||||
ReadTransformsFromBag(bag_filenames.back(), tf_buffer.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<geometry_msgs::TransformStamped> urdf_transforms;
|
std::vector<geometry_msgs::TransformStamped> urdf_transforms;
|
||||||
if (!FLAGS_urdf_filename.empty()) {
|
if (!FLAGS_urdf_filename.empty()) {
|
||||||
urdf_transforms =
|
urdf_transforms =
|
||||||
ReadStaticTransformsFromUrdf(FLAGS_urdf_filename, tf_buffer.get());
|
ReadStaticTransformsFromUrdf(FLAGS_urdf_filename, &tf_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
tf_buffer->setUsingDedicatedThread(true);
|
tf_buffer.setUsingDedicatedThread(true);
|
||||||
|
|
||||||
// Since we preload the transform buffer, we should never have to wait for a
|
// Since we preload the transform buffer, we should never have to wait for a
|
||||||
// transform. When we finish processing the bag, we will simply drop any
|
// transform. When we finish processing the bag, we will simply drop any
|
||||||
// remaining sensor data that cannot be transformed due to missing transforms.
|
// remaining sensor data that cannot be transformed due to missing transforms.
|
||||||
options.lookup_transform_timeout_sec = 0.;
|
options.lookup_transform_timeout_sec = 0.;
|
||||||
Node node(options, tf_buffer.get());
|
Node node(options, &tf_buffer);
|
||||||
node.Initialize();
|
node.Initialize();
|
||||||
|
|
||||||
std::unordered_set<string> expected_sensor_ids;
|
std::unordered_set<string> expected_sensor_ids;
|
||||||
|
@ -178,6 +170,10 @@ void Run(const std::vector<string>& bag_filenames) {
|
||||||
const ::ros::Time begin_time = view.getBeginTime();
|
const ::ros::Time begin_time = view.getBeginTime();
|
||||||
const double duration_in_seconds = (view.getEndTime() - begin_time).toSec();
|
const double duration_in_seconds = (view.getEndTime() - begin_time).toSec();
|
||||||
|
|
||||||
|
// We make sure that tf_messages are published before any data messages, so
|
||||||
|
// that tf lookups always work and that tf_buffer has a small cache size -
|
||||||
|
// because it gets very inefficient with a large one.
|
||||||
|
std::deque<rosbag::MessageInstance> delayed_messages;
|
||||||
for (const rosbag::MessageInstance& msg : view) {
|
for (const rosbag::MessageInstance& msg : view) {
|
||||||
if (sigint_triggered) {
|
if (sigint_triggered) {
|
||||||
break;
|
break;
|
||||||
|
@ -186,6 +182,57 @@ void Run(const std::vector<string>& bag_filenames) {
|
||||||
if (FLAGS_use_bag_transforms && msg.isType<tf2_msgs::TFMessage>()) {
|
if (FLAGS_use_bag_transforms && msg.isType<tf2_msgs::TFMessage>()) {
|
||||||
auto tf_message = msg.instantiate<tf2_msgs::TFMessage>();
|
auto tf_message = msg.instantiate<tf2_msgs::TFMessage>();
|
||||||
tf_publisher.publish(tf_message);
|
tf_publisher.publish(tf_message);
|
||||||
|
|
||||||
|
if (FLAGS_use_bag_transforms) {
|
||||||
|
for (const auto& transform : tf_message->transforms) {
|
||||||
|
try {
|
||||||
|
tf_buffer.setTransform(transform, "unused_authority",
|
||||||
|
msg.getTopic() == kTfStaticTopic);
|
||||||
|
} catch (const tf2::TransformException& ex) {
|
||||||
|
LOG(WARNING) << ex.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!delayed_messages.empty() &&
|
||||||
|
delayed_messages.front().getTime() <
|
||||||
|
msg.getTime() + ::ros::Duration(1.)) {
|
||||||
|
const rosbag::MessageInstance& delayed_msg = delayed_messages.front();
|
||||||
|
const string topic = node.node_handle()->resolveName(
|
||||||
|
delayed_msg.getTopic(), false /* resolve */);
|
||||||
|
if (delayed_msg.isType<sensor_msgs::LaserScan>()) {
|
||||||
|
node.map_builder_bridge()
|
||||||
|
->sensor_bridge(trajectory_id)
|
||||||
|
->HandleLaserScanMessage(
|
||||||
|
topic, delayed_msg.instantiate<sensor_msgs::LaserScan>());
|
||||||
|
}
|
||||||
|
if (delayed_msg.isType<sensor_msgs::MultiEchoLaserScan>()) {
|
||||||
|
node.map_builder_bridge()
|
||||||
|
->sensor_bridge(trajectory_id)
|
||||||
|
->HandleMultiEchoLaserScanMessage(
|
||||||
|
topic,
|
||||||
|
delayed_msg.instantiate<sensor_msgs::MultiEchoLaserScan>());
|
||||||
|
}
|
||||||
|
if (delayed_msg.isType<sensor_msgs::PointCloud2>()) {
|
||||||
|
node.map_builder_bridge()
|
||||||
|
->sensor_bridge(trajectory_id)
|
||||||
|
->HandlePointCloud2Message(
|
||||||
|
topic, delayed_msg.instantiate<sensor_msgs::PointCloud2>());
|
||||||
|
}
|
||||||
|
if (delayed_msg.isType<sensor_msgs::Imu>()) {
|
||||||
|
node.map_builder_bridge()
|
||||||
|
->sensor_bridge(trajectory_id)
|
||||||
|
->HandleImuMessage(topic,
|
||||||
|
delayed_msg.instantiate<sensor_msgs::Imu>());
|
||||||
|
}
|
||||||
|
if (delayed_msg.isType<nav_msgs::Odometry>()) {
|
||||||
|
node.map_builder_bridge()
|
||||||
|
->sensor_bridge(trajectory_id)
|
||||||
|
->HandleOdometryMessage(
|
||||||
|
topic, delayed_msg.instantiate<nav_msgs::Odometry>());
|
||||||
|
}
|
||||||
|
delayed_messages.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
const string topic =
|
const string topic =
|
||||||
|
@ -193,35 +240,7 @@ void Run(const std::vector<string>& bag_filenames) {
|
||||||
if (expected_sensor_ids.count(topic) == 0) {
|
if (expected_sensor_ids.count(topic) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (msg.isType<sensor_msgs::LaserScan>()) {
|
delayed_messages.push_back(msg);
|
||||||
node.map_builder_bridge()
|
|
||||||
->sensor_bridge(trajectory_id)
|
|
||||||
->HandleLaserScanMessage(topic,
|
|
||||||
msg.instantiate<sensor_msgs::LaserScan>());
|
|
||||||
}
|
|
||||||
if (msg.isType<sensor_msgs::MultiEchoLaserScan>()) {
|
|
||||||
node.map_builder_bridge()
|
|
||||||
->sensor_bridge(trajectory_id)
|
|
||||||
->HandleMultiEchoLaserScanMessage(
|
|
||||||
topic, msg.instantiate<sensor_msgs::MultiEchoLaserScan>());
|
|
||||||
}
|
|
||||||
if (msg.isType<sensor_msgs::PointCloud2>()) {
|
|
||||||
node.map_builder_bridge()
|
|
||||||
->sensor_bridge(trajectory_id)
|
|
||||||
->HandlePointCloud2Message(
|
|
||||||
topic, msg.instantiate<sensor_msgs::PointCloud2>());
|
|
||||||
}
|
|
||||||
if (msg.isType<sensor_msgs::Imu>()) {
|
|
||||||
node.map_builder_bridge()
|
|
||||||
->sensor_bridge(trajectory_id)
|
|
||||||
->HandleImuMessage(topic, msg.instantiate<sensor_msgs::Imu>());
|
|
||||||
}
|
|
||||||
if (msg.isType<nav_msgs::Odometry>()) {
|
|
||||||
node.map_builder_bridge()
|
|
||||||
->sensor_bridge(trajectory_id)
|
|
||||||
->HandleOdometryMessage(topic,
|
|
||||||
msg.instantiate<nav_msgs::Odometry>());
|
|
||||||
}
|
|
||||||
|
|
||||||
rosgraph_msgs::Clock clock;
|
rosgraph_msgs::Clock clock;
|
||||||
clock.clock = msg.getTime();
|
clock.clock = msg.getTime();
|
||||||
|
|
Loading…
Reference in New Issue