00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
00048
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 }
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
00077
00078
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
00118 bool HasEcho(float) { return true; }
00119
00120 float GetFirstEcho(float range) { return range; }
00121
00122
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
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 }
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
00220
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
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
00344 constexpr double a = 6378137.;
00345 constexpr double f = 1. / 298.257223563;
00346 constexpr double b = a * (1. - f);
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 }