From 961afb68175e64a57d7acbb809da9053d0586b33 Mon Sep 17 00:00:00 2001 From: Wolfgang Hess Date: Wed, 3 Aug 2016 14:54:45 +0200 Subject: [PATCH] Add code to allow offline processing of 2D Cartographer bags. (#1) * Add code to allow offline processing of 2D Cartographer bags. Fixes waiting for transforms to be published, adds support for multi-echo laser scans, sets default values for the submap topic and query for the RViz plugin. --- cartographer_ros/CMakeLists.txt | 3 +- .../configuration_files/backpack_2d.lua | 23 ++++ cartographer_ros/launch/mapping_2d.launch | 41 ++++++ .../src/cartographer_node_main.cc | 130 +++++++++--------- cartographer_ros/src/drawable_submap.cc | 6 +- cartographer_ros/src/msg_conversion.cc | 26 ++++ cartographer_ros/src/msg_conversion.h | 3 + cartographer_ros/src/submaps_display.cc | 23 ++-- cartographer_ros/urdf/backpack_2d.urdf | 22 +-- 9 files changed, 184 insertions(+), 93 deletions(-) create mode 100644 cartographer_ros/configuration_files/backpack_2d.lua create mode 100644 cartographer_ros/launch/mapping_2d.launch diff --git a/cartographer_ros/CMakeLists.txt b/cartographer_ros/CMakeLists.txt index 89dde17..178bd90 100644 --- a/cartographer_ros/CMakeLists.txt +++ b/cartographer_ros/CMakeLists.txt @@ -97,10 +97,11 @@ target_link_libraries(cartographer_node add_library(cartographer_rviz_submaps_visualization src/drawable_submap.cc src/drawable_submap.h + src/node_constants.h src/submaps_display.cc src/submaps_display.h - src/trajectory.h src/trajectory.cc + src/trajectory.h ) target_link_libraries(cartographer_rviz_submaps_visualization ${Boost_LIBRARIES} diff --git a/cartographer_ros/configuration_files/backpack_2d.lua b/cartographer_ros/configuration_files/backpack_2d.lua new file mode 100644 index 0000000..75b035f --- /dev/null +++ b/cartographer_ros/configuration_files/backpack_2d.lua @@ -0,0 +1,23 @@ +-- 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 "trajectory_builder.lua" +include "sparse_pose_graph.lua" + +options = { + sparse_pose_graph = SPARSE_POSE_GRAPH, + trajectory_builder = TRAJECTORY_BUILDER, +} + +return options diff --git a/cartographer_ros/launch/mapping_2d.launch b/cartographer_ros/launch/mapping_2d.launch new file mode 100644 index 0000000..f8561ae --- /dev/null +++ b/cartographer_ros/launch/mapping_2d.launch @@ -0,0 +1,41 @@ + + + + + + + + + + map_frame: "map" + tracking_frame: "base_link" + configuration_files_directories: [ + "$(find cartographer_ros)/configuration_files" + ] + mapping_configuration_basename: "backpack_2d.lua" + imu_topic: "/imu" + multi_echo_laser_scan_2d_topic: "/horizontal_2d_laser" + laser_min_range_m: 0. + laser_max_range_m: 30. + laser_missing_echo_ray_length_m: 5. + + + + diff --git a/cartographer_ros/src/cartographer_node_main.cc b/cartographer_ros/src/cartographer_node_main.cc index 7612edb..c6fda22 100644 --- a/cartographer_ros/src/cartographer_node_main.cc +++ b/cartographer_ros/src/cartographer_node_main.cc @@ -42,14 +42,14 @@ #include "cartographer/sensor/proto/sensor.pb.h" #include "cartographer/transform/rigid_transform.h" #include "cartographer/transform/transform.h" -#include "geometry_msgs/Transform.h" -#include "geometry_msgs/TransformStamped.h" -#include "glog/log_severity.h" -#include "glog/logging.h" #include "cartographer_ros_msgs/SubmapEntry.h" #include "cartographer_ros_msgs/SubmapList.h" #include "cartographer_ros_msgs/SubmapQuery.h" #include "cartographer_ros_msgs/TrajectorySubmapList.h" +#include "geometry_msgs/Transform.h" +#include "geometry_msgs/TransformStamped.h" +#include "glog/log_severity.h" +#include "glog/logging.h" #include "pcl/point_cloud.h" #include "pcl/point_types.h" #include "pcl_conversions/pcl_conversions.h" @@ -78,6 +78,7 @@ constexpr int64 kTrajectoryId = 0; constexpr int kSubscriberQueueSize = 150; constexpr int kSubmapPublishPeriodInUts = 300 * 10000ll; // 300 milliseconds constexpr int kPosePublishPeriodInUts = 5 * 10000ll; // 5 milliseconds +constexpr double kMaxTransformDelaySeconds = 1.; Rigid3d ToRidig3d(const geometry_msgs::TransformStamped& transform) { return Rigid3d(Eigen::Vector3d(transform.transform.translation.x, @@ -165,12 +166,10 @@ class Node { template const T GetParamOrDie(const string& name); - // Returns true if a transform for 'frame_id' to 'tracking_frame_' exists at - // 'time'. - bool CanTransform(ros::Time time, const string& frame_id); - - Rigid3d LookupToTrackingTransformOrDie(ros::Time time, - const string& frame_id); + // Returns a transform for 'frame_id' to 'tracking_frame_' if it exists at + // 'time' or throws tf2::TransformException if it does not exist. + Rigid3d LookupToTrackingTransformOrThrow(::cartographer::common::Time time, + const string& frame_id); bool HandleSubmapQuery( ::cartographer_ros_msgs::SubmapQuery::Request& request, @@ -217,21 +216,11 @@ Node::Node() last_submap_list_publish_timestamp_(0), last_pose_publish_timestamp_(0) {} -bool Node::CanTransform(ros::Time time, const string& frame_id) { - return tf_buffer_.canTransform(tracking_frame_, frame_id, time); -} - -Rigid3d Node::LookupToTrackingTransformOrDie(ros::Time time, - const string& frame_id) { - geometry_msgs::TransformStamped stamped_transform; - try { - stamped_transform = tf_buffer_.lookupTransform(tracking_frame_, frame_id, - time, ros::Duration(1.)); - } catch (tf2::TransformException& ex) { - LOG(FATAL) << "Timed out while waiting for transform: " << frame_id - << " -> " << tracking_frame_ << ": " << ex.what(); - } - return ToRidig3d(stamped_transform); +Rigid3d Node::LookupToTrackingTransformOrThrow( + const ::cartographer::common::Time time, const string& frame_id) { + return ToRidig3d( + tf_buffer_.lookupTransform(tracking_frame_, frame_id, ToRos(time), + ros::Duration(kMaxTransformDelaySeconds))); } void Node::ImuMessageCallback(const sensor_msgs::Imu::ConstPtr& msg) { @@ -247,22 +236,23 @@ void Node::AddImu(const int64 timestamp, const string& frame_id, const proto::Imu& imu) { const ::cartographer::common::Time time = ::cartographer::common::FromUniversal(timestamp); - - if (!CanTransform(ToRos(time), frame_id)) { - LOG(WARNING) << "Cannot transform to " << frame_id; - return; + try { + const Rigid3d sensor_to_tracking = + LookupToTrackingTransformOrThrow(time, frame_id); + CHECK(sensor_to_tracking.translation().norm() < 1e-5) + << "The IMU is not colocated with the tracking frame. This makes it " + "hard " + "and inprecise to transform its linear accelaration into the " + "tracking_frame and will decrease the quality of the SLAM."; + trajectory_builder_->AddImuData( + time, sensor_to_tracking.rotation() * + ::cartographer::transform::ToEigen(imu.linear_acceleration()), + sensor_to_tracking.rotation() * + ::cartographer::transform::ToEigen(imu.angular_velocity())); + } catch (tf2::TransformException& ex) { + LOG(WARNING) << "Cannot transform " << frame_id << " -> " << tracking_frame_ + << ": " << ex.what(); } - const Rigid3d sensor_to_tracking = - LookupToTrackingTransformOrDie(ToRos(time), frame_id); - CHECK(sensor_to_tracking.translation().norm() < 1e-5) - << "The IMU is not colocated with the tracking frame. This makes it hard " - "and inprecise to transform its linear accelaration into the " - "tracking_frame and will decrease the quality of the SLAM."; - trajectory_builder_->AddImuData( - time, sensor_to_tracking.rotation() * - ::cartographer::transform::ToEigen(imu.linear_acceleration()), - sensor_to_tracking.rotation() * - ::cartographer::transform::ToEigen(imu.angular_velocity())); } void Node::LaserScanMessageCallback( @@ -279,29 +269,33 @@ void Node::AddHorizontalLaserFan(const int64 timestamp, const string& frame_id, const proto::LaserScan& laser_scan) { const ::cartographer::common::Time time = ::cartographer::common::FromUniversal(timestamp); - if (!CanTransform(ToRos(time), frame_id)) { - LOG(WARNING) << "Cannot transform to " << frame_id; - return; + try { + const Rigid3d sensor_to_tracking = + LookupToTrackingTransformOrThrow(time, frame_id); + // TODO(hrapp): Make things configurable? Through Lua? Through ROS params? + const ::cartographer::sensor::LaserFan laser_fan = + ::cartographer::sensor::ToLaserFan(laser_scan, laser_min_range_m_, + laser_max_range_m_, + laser_missing_echo_ray_length_m_); + + const auto laser_fan_3d = ::cartographer::sensor::TransformLaserFan3D( + ::cartographer::sensor::ToLaserFan3D(laser_fan), + sensor_to_tracking.cast()); + trajectory_builder_->AddHorizontalLaserFan(time, laser_fan_3d); + } catch (tf2::TransformException& ex) { + LOG(WARNING) << "Cannot transform " << frame_id << " -> " << tracking_frame_ + << ": " << ex.what(); } - const Rigid3d sensor_to_tracking = - LookupToTrackingTransformOrDie(ToRos(time), frame_id); - - // TODO(hrapp): Make things configurable? Through Lua? Through ROS params? - const ::cartographer::sensor::LaserFan laser_fan = - ::cartographer::sensor::ToLaserFan(laser_scan, laser_min_range_m_, - laser_max_range_m_, - laser_missing_echo_ray_length_m_); - - const auto laser_fan_3d = ::cartographer::sensor::TransformLaserFan3D( - ::cartographer::sensor::ToLaserFan3D(laser_fan), - sensor_to_tracking.cast()); - trajectory_builder_->AddHorizontalLaserFan(time, laser_fan_3d); } void Node::MultiEchoLaserScanCallback( const sensor_msgs::MultiEchoLaserScan::ConstPtr& msg) { - // TODO(hrapp): Do something useful. - LOG(INFO) << "LaserScan message: " << msg->header.stamp; + auto sensor_data = ::cartographer::common::make_unique( + msg->header.frame_id, ToCartographer(*msg)); + sensor_collator_.AddSensorData( + kTrajectoryId, + ::cartographer::common::ToUniversal(FromRos(msg->header.stamp)), + laser_2d_subscriber_.getTopic(), std::move(sensor_data)); } void Node::PointCloud2MessageCallback( @@ -321,17 +315,17 @@ void Node::AddLaserFan3D(const int64 timestamp, const string& frame_id, const proto::LaserFan3D& laser_fan_3d) { const ::cartographer::common::Time time = ::cartographer::common::FromUniversal(timestamp); - if (!CanTransform(ToRos(time), frame_id)) { - LOG(WARNING) << "Cannot transform to " << frame_id; - return; + try { + const Rigid3d sensor_to_tracking = + LookupToTrackingTransformOrThrow(time, frame_id); + trajectory_builder_->AddLaserFan3D( + time, ::cartographer::sensor::TransformLaserFan3D( + ::cartographer::sensor::FromProto(laser_fan_3d), + sensor_to_tracking.cast())); + } catch (tf2::TransformException& ex) { + LOG(WARNING) << "Cannot transform " << frame_id << " -> " << tracking_frame_ + << ": " << ex.what(); } - const Rigid3d sensor_to_tracking = - LookupToTrackingTransformOrDie(ToRos(time), frame_id); - - trajectory_builder_->AddLaserFan3D( - time, ::cartographer::sensor::TransformLaserFan3D( - ::cartographer::sensor::FromProto(laser_fan_3d), - sensor_to_tracking.cast())); } template diff --git a/cartographer_ros/src/drawable_submap.cc b/cartographer_ros/src/drawable_submap.cc index c76f63a..c3276eb 100644 --- a/cartographer_ros/src/drawable_submap.cc +++ b/cartographer_ros/src/drawable_submap.cc @@ -19,8 +19,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -117,8 +117,8 @@ void DrawableSubmap::QuerySubmap(const int submap_id, const int trajectory_id, srv.request.submap_id = submap_id; srv.request.trajectory_id = trajectory_id; if (client->call(srv)) { - response_.reset(new ::cartographer_ros_msgs::SubmapQuery::Response( - srv.response)); + response_.reset( + new ::cartographer_ros_msgs::SubmapQuery::Response(srv.response)); Q_EMIT RequestSucceeded(); } else { OnRequestFailure(); diff --git a/cartographer_ros/src/msg_conversion.cc b/cartographer_ros/src/msg_conversion.cc index 31a18e4..e766847 100644 --- a/cartographer_ros/src/msg_conversion.cc +++ b/cartographer_ros/src/msg_conversion.cc @@ -240,6 +240,32 @@ sensor_msgs::PointCloud2 ToPointCloud2Message( return proto; } +::cartographer::sensor::proto::LaserScan ToCartographer( + const sensor_msgs::MultiEchoLaserScan& msg) { + ::cartographer::sensor::proto::LaserScan proto; + proto.set_angle_min(msg.angle_min); + proto.set_angle_max(msg.angle_max); + proto.set_angle_increment(msg.angle_increment); + proto.set_time_increment(msg.time_increment); + proto.set_scan_time(msg.scan_time); + proto.set_range_min(msg.range_min); + proto.set_range_max(msg.range_max); + + for (const auto& range : msg.ranges) { + auto* proto_echoes = proto.add_range()->mutable_value(); + for (const auto& echo : range.echoes) { + proto_echoes->Add(echo); + } + } + for (const auto& intensity : msg.intensities) { + auto* proto_echoes = proto.add_intensity()->mutable_value(); + for (const auto& echo : intensity.echoes) { + proto_echoes->Add(echo); + } + } + return proto; +} + ::cartographer::sensor::proto::LaserFan3D ToCartographer( const pcl::PointCloud& pcl_points) { ::cartographer::sensor::proto::LaserFan3D proto; diff --git a/cartographer_ros/src/msg_conversion.h b/cartographer_ros/src/msg_conversion.h index f553d04..b069e4d 100644 --- a/cartographer_ros/src/msg_conversion.h +++ b/cartographer_ros/src/msg_conversion.h @@ -49,6 +49,9 @@ sensor_msgs::PointCloud2 ToPointCloud2Message( ::cartographer::sensor::proto::LaserScan ToCartographer( const sensor_msgs::LaserScan& msg); +::cartographer::sensor::proto::LaserScan ToCartographer( + const sensor_msgs::MultiEchoLaserScan& msg); + ::cartographer::sensor::proto::LaserFan3D ToCartographer( const pcl::PointCloud& pcl_points); diff --git a/cartographer_ros/src/submaps_display.cc b/cartographer_ros/src/submaps_display.cc index 5b14dca..d062a6f 100644 --- a/cartographer_ros/src/submaps_display.cc +++ b/cartographer_ros/src/submaps_display.cc @@ -34,9 +34,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -45,6 +45,8 @@ #include #include +#include "node_constants.h" + namespace cartographer_ros { namespace rviz { @@ -55,8 +57,7 @@ constexpr char kMaterialsDirectory[] = "/ogre_media/materials"; constexpr char kGlsl120Directory[] = "/glsl120"; constexpr char kScriptsDirectory[] = "/scripts"; constexpr char kScreenBlitMaterialName[] = "ScreenBlitMaterial"; -constexpr char kScreenBlitSourceMaterialName[] = - "cartographer_ros/ScreenBlit"; +constexpr char kScreenBlitSourceMaterialName[] = "cartographer_ros/ScreenBlit"; constexpr char kSubmapsRttPrefix[] = "SubmapsRtt"; constexpr char kMapTextureName[] = "MapTexture"; constexpr char kMapOverlayName[] = "MapOverlay"; @@ -74,13 +75,15 @@ SubmapsDisplay::SubmapsDisplay() tf_listener_(tf_buffer_) { connect(this, SIGNAL(SubmapListUpdated()), this, SLOT(RequestNewSubmaps())); topic_property_ = new ::rviz::RosTopicProperty( - "Topic", "", - QString::fromStdString(ros::message_traits::datatype< - ::cartographer_ros_msgs::SubmapList>()), + "Topic", QString("/cartographer/") + kSubmapListTopic, + QString::fromStdString( + ros::message_traits::datatype<::cartographer_ros_msgs::SubmapList>()), "cartographer_ros_msgs::SubmapList topic to subscribe to.", this, SLOT(UpdateTopic())); submap_query_service_property_ = new ::rviz::StringProperty( - "Submap query service", "", "Submap query service to connect to.", this, + "Submap query service", + QString("/cartographer/") + kSubmapQueryServiceName, + "Submap query service to connect to.", this, SLOT(UpdateSubmapQueryServiceName())); map_frame_property_ = new ::rviz::StringProperty( "Map frame", kDefaultMapFrame, "Map frame, used for fading out submaps.", @@ -88,8 +91,7 @@ SubmapsDisplay::SubmapsDisplay() tracking_frame_property_ = new ::rviz::StringProperty( "Tracking frame", kDefaultTrackingFrame, "Tracking frame, used for fading out submaps.", this); - client_ = - update_nh_.serviceClient<::cartographer_ros_msgs::SubmapQuery>(""); + client_ = update_nh_.serviceClient<::cartographer_ros_msgs::SubmapQuery>(""); const std::string package_path = ::ros::package::getPath(ROS_PACKAGE_NAME); Ogre::ResourceGroupManager::getSingleton().addResourceLocation( package_path + kMaterialsDirectory, "FileSystem", ROS_PACKAGE_NAME); @@ -136,7 +138,8 @@ void SubmapsDisplay::onInitialize() { kSubmapTexturesGroup); scene_manager_->addListener(&scene_manager_listener_); - UpdateTopic(); + // TODO(whess): Combine UpdateTopic()/UpdateSubmapQueryServiceName(). + UpdateSubmapQueryServiceName(); } void SubmapsDisplay::UpdateTopic() { diff --git a/cartographer_ros/urdf/backpack_2d.urdf b/cartographer_ros/urdf/backpack_2d.urdf index 3bfdfd9..6c88906 100644 --- a/cartographer_ros/urdf/backpack_2d.urdf +++ b/cartographer_ros/urdf/backpack_2d.urdf @@ -25,7 +25,7 @@ - + @@ -35,7 +35,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -55,7 +55,7 @@ - + @@ -65,21 +65,21 @@ - - - + + + - - + + - - + +