msg_conversion.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright 2016 The Cartographer Authors
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *      http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "cartographer_ros/msg_conversion.h"
00018 
00019 #include <cmath>
00020 
00021 #include "cartographer/common/math.h"
00022 #include "cartographer/common/port.h"
00023 #include "cartographer/common/time.h"
00024 #include "cartographer/io/submap_painter.h"
00025 #include "cartographer/transform/proto/transform.pb.h"
00026 #include "cartographer/transform/transform.h"
00027 #include "cartographer_ros/time_conversion.h"
00028 #include "geometry_msgs/Pose.h"
00029 #include "geometry_msgs/Quaternion.h"
00030 #include "geometry_msgs/Transform.h"
00031 #include "geometry_msgs/TransformStamped.h"
00032 #include "geometry_msgs/Vector3.h"
00033 #include "glog/logging.h"
00034 #include "nav_msgs/OccupancyGrid.h"
00035 #include "pcl/point_cloud.h"
00036 #include "pcl/point_types.h"
00037 #include "pcl_conversions/pcl_conversions.h"
00038 #include "ros/ros.h"
00039 #include "ros/serialization.h"
00040 #include "sensor_msgs/Imu.h"
00041 #include "sensor_msgs/LaserScan.h"
00042 #include "sensor_msgs/MultiEchoLaserScan.h"
00043 #include "sensor_msgs/PointCloud2.h"
00044 
00045 namespace {
00046 
00047 // Sizes of PCL point types have to be 4n floats for alignment, as described in
00048 // http://pointclouds.org/documentation/tutorials/adding_custom_ptype.php
00049 struct PointXYZT {
00050   float x;
00051   float y;
00052   float z;
00053   float time;
00054 };
00055 
00056 struct PointXYZIT {
00057   PCL_ADD_POINT4D;
00058   float intensity;
00059   float time;
00060   float unused_padding[2];
00061 };
00062 
00063 }  // namespace
00064 
00065 POINT_CLOUD_REGISTER_POINT_STRUCT(
00066     PointXYZT, (float, x, x)(float, y, y)(float, z, z)(float, time, time))
00067 
00068 POINT_CLOUD_REGISTER_POINT_STRUCT(
00069     PointXYZIT,
00070     (float, x, x)(float, y, y)(float, z, z)(float, intensity,
00071                                             intensity)(float, time, time))
00072 
00073 namespace cartographer_ros {
00074 namespace {
00075 
00076 // The ros::sensor_msgs::PointCloud2 binary data contains 4 floats for each
00077 // point. The last one must be this value or RViz is not showing the point cloud
00078 // properly.
00079 constexpr float kPointCloudComponentFourMagic = 1.;
00080 
00081 using ::cartographer::sensor::LandmarkData;
00082 using ::cartographer::sensor::LandmarkObservation;
00083 using ::cartographer::sensor::PointCloudWithIntensities;
00084 using ::cartographer::transform::Rigid3d;
00085 using ::cartographer_ros_msgs::LandmarkEntry;
00086 using ::cartographer_ros_msgs::LandmarkList;
00087 
00088 sensor_msgs::PointCloud2 PreparePointCloud2Message(const int64_t timestamp,
00089                                                    const std::string& frame_id,
00090                                                    const int num_points) {
00091   sensor_msgs::PointCloud2 msg;
00092   msg.header.stamp = ToRos(::cartographer::common::FromUniversal(timestamp));
00093   msg.header.frame_id = frame_id;
00094   msg.height = 1;
00095   msg.width = num_points;
00096   msg.fields.resize(3);
00097   msg.fields[0].name = "x";
00098   msg.fields[0].offset = 0;
00099   msg.fields[0].datatype = sensor_msgs::PointField::FLOAT32;
00100   msg.fields[0].count = 1;
00101   msg.fields[1].name = "y";
00102   msg.fields[1].offset = 4;
00103   msg.fields[1].datatype = sensor_msgs::PointField::FLOAT32;
00104   msg.fields[1].count = 1;
00105   msg.fields[2].name = "z";
00106   msg.fields[2].offset = 8;
00107   msg.fields[2].datatype = sensor_msgs::PointField::FLOAT32;
00108   msg.fields[2].count = 1;
00109   msg.is_bigendian = false;
00110   msg.point_step = 16;
00111   msg.row_step = 16 * msg.width;
00112   msg.is_dense = true;
00113   msg.data.resize(16 * num_points);
00114   return msg;
00115 }
00116 
00117 // For sensor_msgs::LaserScan.
00118 bool HasEcho(float) { return true; }
00119 
00120 float GetFirstEcho(float range) { return range; }
00121 
00122 // For sensor_msgs::MultiEchoLaserScan.
00123 bool HasEcho(const sensor_msgs::LaserEcho& echo) {
00124   return !echo.echoes.empty();
00125 }
00126 
00127 float GetFirstEcho(const sensor_msgs::LaserEcho& echo) {
00128   return echo.echoes[0];
00129 }
00130 
00131 // For sensor_msgs::LaserScan and sensor_msgs::MultiEchoLaserScan.
00132 template <typename LaserMessageType>
00133 std::tuple<PointCloudWithIntensities, ::cartographer::common::Time>
00134 LaserScanToPointCloudWithIntensities(const LaserMessageType& msg) {
00135   CHECK_GE(msg.range_min, 0.f);
00136   CHECK_GE(msg.range_max, msg.range_min);
00137   if (msg.angle_increment > 0.f) {
00138     CHECK_GT(msg.angle_max, msg.angle_min);
00139   } else {
00140     CHECK_GT(msg.angle_min, msg.angle_max);
00141   }
00142   PointCloudWithIntensities point_cloud;
00143   float angle = msg.angle_min;
00144   for (size_t i = 0; i < msg.ranges.size(); ++i) {
00145     const auto& echoes = msg.ranges[i];
00146     if (HasEcho(echoes)) {
00147       const float first_echo = GetFirstEcho(echoes);
00148       if (msg.range_min <= first_echo && first_echo <= msg.range_max) {
00149         const Eigen::AngleAxisf rotation(angle, Eigen::Vector3f::UnitZ());
00150         const cartographer::sensor::TimedRangefinderPoint point{
00151             rotation * (first_echo * Eigen::Vector3f::UnitX()),
00152             i * msg.time_increment};
00153         point_cloud.points.push_back(point);
00154         if (msg.intensities.size() > 0) {
00155           CHECK_EQ(msg.intensities.size(), msg.ranges.size());
00156           const auto& echo_intensities = msg.intensities[i];
00157           CHECK(HasEcho(echo_intensities));
00158           point_cloud.intensities.push_back(GetFirstEcho(echo_intensities));
00159         } else {
00160           point_cloud.intensities.push_back(0.f);
00161         }
00162       }
00163     }
00164     angle += msg.angle_increment;
00165   }
00166   ::cartographer::common::Time timestamp = FromRos(msg.header.stamp);
00167   if (!point_cloud.points.empty()) {
00168     const double duration = point_cloud.points.back().time;
00169     timestamp += cartographer::common::FromSeconds(duration);
00170     for (auto& point : point_cloud.points) {
00171       point.time -= duration;
00172     }
00173   }
00174   return std::make_tuple(point_cloud, timestamp);
00175 }
00176 
00177 bool PointCloud2HasField(const sensor_msgs::PointCloud2& pc2,
00178                          const std::string& field_name) {
00179   for (const auto& field : pc2.fields) {
00180     if (field.name == field_name) {
00181       return true;
00182     }
00183   }
00184   return false;
00185 }
00186 
00187 }  // namespace
00188 
00189 sensor_msgs::PointCloud2 ToPointCloud2Message(
00190     const int64_t timestamp, const std::string& frame_id,
00191     const ::cartographer::sensor::TimedPointCloud& point_cloud) {
00192   auto msg = PreparePointCloud2Message(timestamp, frame_id, point_cloud.size());
00193   ::ros::serialization::OStream stream(msg.data.data(), msg.data.size());
00194   for (const cartographer::sensor::TimedRangefinderPoint& point : point_cloud) {
00195     stream.next(point.position.x());
00196     stream.next(point.position.y());
00197     stream.next(point.position.z());
00198     stream.next(kPointCloudComponentFourMagic);
00199   }
00200   return msg;
00201 }
00202 
00203 std::tuple<::cartographer::sensor::PointCloudWithIntensities,
00204            ::cartographer::common::Time>
00205 ToPointCloudWithIntensities(const sensor_msgs::LaserScan& msg) {
00206   return LaserScanToPointCloudWithIntensities(msg);
00207 }
00208 
00209 std::tuple<::cartographer::sensor::PointCloudWithIntensities,
00210            ::cartographer::common::Time>
00211 ToPointCloudWithIntensities(const sensor_msgs::MultiEchoLaserScan& msg) {
00212   return LaserScanToPointCloudWithIntensities(msg);
00213 }
00214 
00215 std::tuple<::cartographer::sensor::PointCloudWithIntensities,
00216            ::cartographer::common::Time>
00217 ToPointCloudWithIntensities(const sensor_msgs::PointCloud2& msg) {
00218   PointCloudWithIntensities point_cloud;
00219   // We check for intensity field here to avoid run-time warnings if we pass in
00220   // a PointCloud2 without intensity.
00221   if (PointCloud2HasField(msg, "intensity")) {
00222     if (PointCloud2HasField(msg, "time")) {
00223       pcl::PointCloud<PointXYZIT> pcl_point_cloud;
00224       pcl::fromROSMsg(msg, pcl_point_cloud);
00225       point_cloud.points.reserve(pcl_point_cloud.size());
00226       point_cloud.intensities.reserve(pcl_point_cloud.size());
00227       for (const auto& point : pcl_point_cloud) {
00228         point_cloud.points.push_back(
00229             {Eigen::Vector3f{point.x, point.y, point.z}, point.time});
00230         point_cloud.intensities.push_back(point.intensity);
00231       }
00232     } else {
00233       pcl::PointCloud<pcl::PointXYZI> pcl_point_cloud;
00234       pcl::fromROSMsg(msg, pcl_point_cloud);
00235       point_cloud.points.reserve(pcl_point_cloud.size());
00236       point_cloud.intensities.reserve(pcl_point_cloud.size());
00237       for (const auto& point : pcl_point_cloud) {
00238         point_cloud.points.push_back(
00239             {Eigen::Vector3f{point.x, point.y, point.z}, 0.f});
00240         point_cloud.intensities.push_back(point.intensity);
00241       }
00242     }
00243   } else {
00244     // If we don't have an intensity field, just copy XYZ and fill in 1.0f.
00245     if (PointCloud2HasField(msg, "time")) {
00246       pcl::PointCloud<PointXYZT> pcl_point_cloud;
00247       pcl::fromROSMsg(msg, pcl_point_cloud);
00248       point_cloud.points.reserve(pcl_point_cloud.size());
00249       point_cloud.intensities.reserve(pcl_point_cloud.size());
00250       for (const auto& point : pcl_point_cloud) {
00251         point_cloud.points.push_back(
00252             {Eigen::Vector3f{point.x, point.y, point.z}, point.time});
00253         point_cloud.intensities.push_back(1.0f);
00254       }
00255     } else {
00256       pcl::PointCloud<pcl::PointXYZ> pcl_point_cloud;
00257       pcl::fromROSMsg(msg, pcl_point_cloud);
00258       point_cloud.points.reserve(pcl_point_cloud.size());
00259       point_cloud.intensities.reserve(pcl_point_cloud.size());
00260       for (const auto& point : pcl_point_cloud) {
00261         point_cloud.points.push_back(
00262             {Eigen::Vector3f{point.x, point.y, point.z}, 0.f});
00263         point_cloud.intensities.push_back(1.0f);
00264       }
00265     }
00266   }
00267   ::cartographer::common::Time timestamp = FromRos(msg.header.stamp);
00268   if (!point_cloud.points.empty()) {
00269     const double duration = point_cloud.points.back().time;
00270     timestamp += cartographer::common::FromSeconds(duration);
00271     for (auto& point : point_cloud.points) {
00272       point.time -= duration;
00273       CHECK_LE(point.time, 0.f)
00274           << "Encountered a point with a larger stamp than "
00275              "the last point in the cloud.";
00276     }
00277   }
00278   return std::make_tuple(point_cloud, timestamp);
00279 }
00280 
00281 LandmarkData ToLandmarkData(const LandmarkList& landmark_list) {
00282   LandmarkData landmark_data;
00283   landmark_data.time = FromRos(landmark_list.header.stamp);
00284   for (const LandmarkEntry& entry : landmark_list.landmarks) {
00285     landmark_data.landmark_observations.push_back(
00286         {entry.id, ToRigid3d(entry.tracking_from_landmark_transform),
00287          entry.translation_weight, entry.rotation_weight});
00288   }
00289   return landmark_data;
00290 }
00291 
00292 Rigid3d ToRigid3d(const geometry_msgs::TransformStamped& transform) {
00293   return Rigid3d(ToEigen(transform.transform.translation),
00294                  ToEigen(transform.transform.rotation));
00295 }
00296 
00297 Rigid3d ToRigid3d(const geometry_msgs::Pose& pose) {
00298   return Rigid3d({pose.position.x, pose.position.y, pose.position.z},
00299                  ToEigen(pose.orientation));
00300 }
00301 
00302 Eigen::Vector3d ToEigen(const geometry_msgs::Vector3& vector3) {
00303   return Eigen::Vector3d(vector3.x, vector3.y, vector3.z);
00304 }
00305 
00306 Eigen::Quaterniond ToEigen(const geometry_msgs::Quaternion& quaternion) {
00307   return Eigen::Quaterniond(quaternion.w, quaternion.x, quaternion.y,
00308                             quaternion.z);
00309 }
00310 
00311 geometry_msgs::Transform ToGeometryMsgTransform(const Rigid3d& rigid3d) {
00312   geometry_msgs::Transform transform;
00313   transform.translation.x = rigid3d.translation().x();
00314   transform.translation.y = rigid3d.translation().y();
00315   transform.translation.z = rigid3d.translation().z();
00316   transform.rotation.w = rigid3d.rotation().w();
00317   transform.rotation.x = rigid3d.rotation().x();
00318   transform.rotation.y = rigid3d.rotation().y();
00319   transform.rotation.z = rigid3d.rotation().z();
00320   return transform;
00321 }
00322 
00323 geometry_msgs::Pose ToGeometryMsgPose(const Rigid3d& rigid3d) {
00324   geometry_msgs::Pose pose;
00325   pose.position = ToGeometryMsgPoint(rigid3d.translation());
00326   pose.orientation.w = rigid3d.rotation().w();
00327   pose.orientation.x = rigid3d.rotation().x();
00328   pose.orientation.y = rigid3d.rotation().y();
00329   pose.orientation.z = rigid3d.rotation().z();
00330   return pose;
00331 }
00332 
00333 geometry_msgs::Point ToGeometryMsgPoint(const Eigen::Vector3d& vector3d) {
00334   geometry_msgs::Point point;
00335   point.x = vector3d.x();
00336   point.y = vector3d.y();
00337   point.z = vector3d.z();
00338   return point;
00339 }
00340 
00341 Eigen::Vector3d LatLongAltToEcef(const double latitude, const double longitude,
00342                                  const double altitude) {
00343   // https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates
00344   constexpr double a = 6378137.;  // semi-major axis, equator to center.
00345   constexpr double f = 1. / 298.257223563;
00346   constexpr double b = a * (1. - f);  // semi-minor axis, pole to center.
00347   constexpr double a_squared = a * a;
00348   constexpr double b_squared = b * b;
00349   constexpr double e_squared = (a_squared - b_squared) / a_squared;
00350   const double sin_phi = std::sin(cartographer::common::DegToRad(latitude));
00351   const double cos_phi = std::cos(cartographer::common::DegToRad(latitude));
00352   const double sin_lambda = std::sin(cartographer::common::DegToRad(longitude));
00353   const double cos_lambda = std::cos(cartographer::common::DegToRad(longitude));
00354   const double N = a / std::sqrt(1 - e_squared * sin_phi * sin_phi);
00355   const double x = (N + altitude) * cos_phi * cos_lambda;
00356   const double y = (N + altitude) * cos_phi * sin_lambda;
00357   const double z = (b_squared / a_squared * N + altitude) * sin_phi;
00358 
00359   return Eigen::Vector3d(x, y, z);
00360 }
00361 
00362 cartographer::transform::Rigid3d ComputeLocalFrameFromLatLong(
00363     const double latitude, const double longitude) {
00364   const Eigen::Vector3d translation = LatLongAltToEcef(latitude, longitude, 0.);
00365   const Eigen::Quaterniond rotation =
00366       Eigen::AngleAxisd(cartographer::common::DegToRad(latitude - 90.),
00367                         Eigen::Vector3d::UnitY()) *
00368       Eigen::AngleAxisd(cartographer::common::DegToRad(-longitude),
00369                         Eigen::Vector3d::UnitZ());
00370   return cartographer::transform::Rigid3d(rotation * -translation, rotation);
00371 }
00372 
00373 std::unique_ptr<nav_msgs::OccupancyGrid> CreateOccupancyGridMsg(
00374     const cartographer::io::PaintSubmapSlicesResult& painted_slices,
00375     const double resolution, const std::string& frame_id,
00376     const ros::Time& time) {
00377   auto occupancy_grid = absl::make_unique<nav_msgs::OccupancyGrid>();
00378 
00379   const int width = cairo_image_surface_get_width(painted_slices.surface.get());
00380   const int height =
00381       cairo_image_surface_get_height(painted_slices.surface.get());
00382 
00383   occupancy_grid->header.stamp = time;
00384   occupancy_grid->header.frame_id = frame_id;
00385   occupancy_grid->info.map_load_time = time;
00386   occupancy_grid->info.resolution = resolution;
00387   occupancy_grid->info.width = width;
00388   occupancy_grid->info.height = height;
00389   occupancy_grid->info.origin.position.x =
00390       -painted_slices.origin.x() * resolution;
00391   occupancy_grid->info.origin.position.y =
00392       (-height + painted_slices.origin.y()) * resolution;
00393   occupancy_grid->info.origin.position.z = 0.;
00394   occupancy_grid->info.origin.orientation.w = 1.;
00395   occupancy_grid->info.origin.orientation.x = 0.;
00396   occupancy_grid->info.origin.orientation.y = 0.;
00397   occupancy_grid->info.origin.orientation.z = 0.;
00398 
00399   const uint32_t* pixel_data = reinterpret_cast<uint32_t*>(
00400       cairo_image_surface_get_data(painted_slices.surface.get()));
00401   occupancy_grid->data.reserve(width * height);
00402   for (int y = height - 1; y >= 0; --y) {
00403     for (int x = 0; x < width; ++x) {
00404       const uint32_t packed = pixel_data[y * width + x];
00405       const unsigned char color = packed >> 16;
00406       const unsigned char observed = packed >> 8;
00407       const int value =
00408           observed == 0
00409               ? -1
00410               : ::cartographer::common::RoundToInt((1. - color / 255.) * 100.);
00411       CHECK_LE(-1, value);
00412       CHECK_GE(100, value);
00413       occupancy_grid->data.push_back(value);
00414     }
00415   }
00416 
00417   return occupancy_grid;
00418 }
00419 
00420 }  // namespace cartographer_ros


cartographer_ros
Author(s): The Cartographer Authors
autogenerated on Wed Jul 10 2019 04:10:28