mcl_3dl.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2016-2018, the mcl_3dl authors
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the copyright holder nor the names of its 
00014  *       contributors may be used to endorse or promote products derived from 
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include <algorithm>
00031 #include <map>
00032 #include <memory>
00033 #include <string>
00034 #include <utility>
00035 #include <vector>
00036 
00037 #include <boost/chrono.hpp>
00038 #include <boost/shared_ptr.hpp>
00039 
00040 #include <ros/ros.h>
00041 #include <sensor_msgs/PointCloud.h>
00042 #include <sensor_msgs/PointCloud2.h>
00043 #include <sensor_msgs/point_cloud_conversion.h>
00044 #include <nav_msgs/Odometry.h>
00045 #include <sensor_msgs/Imu.h>
00046 #include <geometry_msgs/PoseArray.h>
00047 #include <geometry_msgs/PoseWithCovarianceStamped.h>
00048 #include <geometry_msgs/TransformStamped.h>
00049 #include <visualization_msgs/MarkerArray.h>
00050 #include <mcl_3dl_msgs/ResizeParticle.h>
00051 #include <mcl_3dl_msgs/Status.h>
00052 #include <std_srvs/Trigger.h>
00053 
00054 #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
00055 #include <tf2_ros/transform_broadcaster.h>
00056 #include <tf2_ros/transform_listener.h>
00057 #include <tf2_sensor_msgs/tf2_sensor_msgs.h>
00058 
00059 #include <pcl_conversions/pcl_conversions.h>
00060 #include <pcl/point_types.h>
00061 #include <pcl_ros/point_cloud.h>
00062 #include <pcl_ros/transforms.h>
00063 #include <pcl/conversions.h>
00064 #include <pcl/filters/voxel_grid.h>
00065 #include <pcl/kdtree/kdtree.h>
00066 #include <pcl/kdtree/kdtree_flann.h>
00067 
00068 #include <Eigen/Core>
00069 
00070 #include <pcl18_backports/voxel_grid.h>
00071 
00072 #include <mcl_3dl/chunked_kdtree.h>
00073 #include <mcl_3dl/filter.h>
00074 #include <mcl_3dl/imu_measurement_model_base.h>
00075 #include <mcl_3dl/imu_measurement_models/imu_measurement_model_gravity.h>
00076 #include <mcl_3dl/lidar_measurement_model_base.h>
00077 #include <mcl_3dl/lidar_measurement_models/lidar_measurement_model_beam.h>
00078 #include <mcl_3dl/lidar_measurement_models/lidar_measurement_model_likelihood.h>
00079 #include <mcl_3dl/motion_prediction_model_base.h>
00080 #include <mcl_3dl/motion_prediction_models/motion_prediction_model_differential_drive.h>
00081 #include <mcl_3dl/nd.h>
00082 #include <mcl_3dl/parameters.h>
00083 #include <mcl_3dl/pf.h>
00084 #include <mcl_3dl/point_conversion.h>
00085 #include <mcl_3dl/point_types.h>
00086 #include <mcl_3dl/quat.h>
00087 #include <mcl_3dl/raycast.h>
00088 #include <mcl_3dl/state_6dof.h>
00089 #include <mcl_3dl/vec3.h>
00090 
00091 #include <mcl_3dl_compat/compatibility.h>
00092 
00093 namespace mcl_3dl
00094 {
00095 class MCL3dlNode
00096 {
00097 protected:
00098   using PointType = mcl_3dl::PointXYZIL;
00099   std::shared_ptr<pf::ParticleFilter<State6DOF, float, ParticleWeightedMeanQuat>> pf_;
00100 
00101   class MyPointRepresentation : public pcl::PointRepresentation<PointType>
00102   {
00103     using pcl::PointRepresentation<PointType>::nr_dimensions_;
00104 
00105   public:
00106     MyPointRepresentation()
00107     {
00108       nr_dimensions_ = 3;
00109     }
00110 
00111     virtual void copyToFloatArray(const PointType& p, float* out) const
00112     {
00113       out[0] = p.x;
00114       out[1] = p.y;
00115       out[2] = p.z;
00116     }
00117   };
00118   void cbMapcloud(const sensor_msgs::PointCloud2::ConstPtr& msg)
00119   {
00120     ROS_INFO("map received");
00121     pcl::PointCloud<PointType>::Ptr pc_tmp(new pcl::PointCloud<PointType>);
00122     if (!mcl_3dl::fromROSMsg(*msg, *pc_tmp))
00123     {
00124       has_map_ = false;
00125       return;
00126     }
00127 
00128     pc_map_.reset(new pcl::PointCloud<PointType>);
00129     pc_map2_.reset();
00130     pc_update_.reset();
00131     pcl::VoxelGrid18<PointType> ds;
00132     ds.setInputCloud(pc_tmp);
00133     ds.setLeafSize(params_.map_downsample_x_, params_.map_downsample_y_, params_.map_downsample_z_);
00134     ds.filter(*pc_map_);
00135     pc_local_accum_.reset(new pcl::PointCloud<PointType>);
00136     pc_all_accum_.reset(new pcl::PointCloud<PointType>);
00137     frame_num_ = 0;
00138     has_map_ = true;
00139 
00140     ROS_INFO("map original: %d points", (int)pc_tmp->points.size());
00141     ROS_INFO("map reduced: %d points", (int)pc_map_->points.size());
00142 
00143     cbMapUpdateTimer(ros::TimerEvent());
00144   }
00145   void cbMapcloudUpdate(const sensor_msgs::PointCloud2::ConstPtr& msg)
00146   {
00147     ROS_INFO("map_update received");
00148     pcl::PointCloud<PointType>::Ptr pc_tmp(new pcl::PointCloud<PointType>);
00149     if (!mcl_3dl::fromROSMsg(*msg, *pc_tmp))
00150       return;
00151 
00152     pc_update_.reset(new pcl::PointCloud<PointType>);
00153     pcl::VoxelGrid18<PointType> ds;
00154     ds.setInputCloud(pc_tmp);
00155     ds.setLeafSize(params_.update_downsample_x_, params_.update_downsample_y_, params_.update_downsample_z_);
00156     ds.filter(*pc_update_);
00157   }
00158 
00159   void cbPosition(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& msg)
00160   {
00161     geometry_msgs::PoseStamped pose_in, pose;
00162     pose_in.header = msg->header;
00163     pose_in.pose = msg->pose.pose;
00164     try
00165     {
00166       const geometry_msgs::TransformStamped trans = tfbuf_.lookupTransform(
00167           frame_ids_["map"], pose_in.header.frame_id, pose_in.header.stamp, ros::Duration(1.0));
00168       tf2::doTransform(pose_in, pose, trans);
00169     }
00170     catch (tf2::TransformException& e)
00171     {
00172       return;
00173     }
00174     pf_->init(
00175         State6DOF(
00176             Vec3(pose.pose.position.x, pose.pose.position.y, pose.pose.position.z),
00177             Quat(pose.pose.orientation.x,
00178                  pose.pose.orientation.y,
00179                  pose.pose.orientation.z,
00180                  pose.pose.orientation.w)),
00181         State6DOF(
00182             Vec3(msg->pose.covariance[0],
00183                  msg->pose.covariance[6 * 1 + 1],
00184                  msg->pose.covariance[6 * 2 + 2]),
00185             Vec3(msg->pose.covariance[6 * 3 + 3],
00186                  msg->pose.covariance[6 * 4 + 4],
00187                  msg->pose.covariance[6 * 5 + 5])));
00188     pc_update_.reset();
00189     auto integ_reset_func = [](State6DOF& s)
00190     {
00191       s.odom_err_integ_lin_ = Vec3();
00192       s.odom_err_integ_ang_ = Vec3();
00193     };
00194     pf_->predict(integ_reset_func);
00195   }
00196 
00197   void cbOdom(const nav_msgs::Odometry::ConstPtr& msg)
00198   {
00199     odom_ =
00200         State6DOF(
00201             Vec3(msg->pose.pose.position.x,
00202                  msg->pose.pose.position.y,
00203                  msg->pose.pose.position.z),
00204             Quat(msg->pose.pose.orientation.x,
00205                  msg->pose.pose.orientation.y,
00206                  msg->pose.pose.orientation.z,
00207                  msg->pose.pose.orientation.w));
00208     if (!has_odom_)
00209     {
00210       odom_prev_ = odom_;
00211       odom_last_ = msg->header.stamp;
00212       has_odom_ = true;
00213       return;
00214     }
00215     const float dt = (msg->header.stamp - odom_last_).toSec();
00216     if (dt < 0.0 || dt > 5.0)
00217     {
00218       ROS_WARN("Detected time jump in odometry. Resetting.");
00219       has_odom_ = false;
00220       return;
00221     }
00222     else if (dt > 0.05)
00223     {
00224       motion_prediction_model_->setOdoms(odom_prev_, odom_, dt);
00225       auto prediction_func = [this](State6DOF& s)
00226       {
00227         motion_prediction_model_->predict(s);
00228       };
00229       pf_->predict(prediction_func);
00230       odom_last_ = msg->header.stamp;
00231       odom_prev_ = odom_;
00232     }
00233   }
00234   void cbCloud(const sensor_msgs::PointCloud2::ConstPtr& msg)
00235   {
00236     mcl_3dl_msgs::Status status;
00237     status.header.stamp = ros::Time::now();
00238     status.status = mcl_3dl_msgs::Status::NORMAL;
00239 
00240     if (!has_map_)
00241       return;
00242     if (frames_.find(msg->header.frame_id) == frames_.end())
00243     {
00244       frames_[msg->header.frame_id] = true;
00245       frames_v_.push_back(msg->header.frame_id);
00246     }
00247 
00248     sensor_msgs::PointCloud2 pc_bl;
00249     try
00250     {
00251       const geometry_msgs::TransformStamped trans = tfbuf_.lookupTransform(
00252           frame_ids_["odom"], msg->header.frame_id, msg->header.stamp, ros::Duration(0.1));
00253       tf2::doTransform(*msg, pc_bl, trans);
00254     }
00255     catch (tf2::TransformException& e)
00256     {
00257       ROS_INFO("Failed to transform pointcloud: %s", e.what());
00258       pc_local_accum_.reset(new pcl::PointCloud<PointType>);
00259       pc_accum_header_.clear();
00260       return;
00261     }
00262     pcl::PointCloud<PointType>::Ptr pc_tmp(new pcl::PointCloud<PointType>);
00263     if (!mcl_3dl::fromROSMsg(pc_bl, *pc_tmp))
00264       return;
00265 
00266     for (auto& p : pc_tmp->points)
00267     {
00268       p.label = pc_accum_header_.size();
00269     }
00270     *pc_local_accum_ += *pc_tmp;
00271     pc_local_accum_->header.frame_id = frame_ids_["odom"];
00272     pc_accum_header_.push_back(msg->header);
00273 
00274     if (frames_v_[frame_num_].compare(msg->header.frame_id) != 0)
00275       return;
00276     frame_num_++;
00277     if (frame_num_ >= frames_v_.size())
00278       frame_num_ = 0;
00279 
00280     if (frame_num_ != 0)
00281       return;
00282 
00283     cnt_accum_++;
00284     if (cnt_accum_ % params_.accum_cloud_ != 0)
00285       return;
00286 
00287     cnt_measure_++;
00288     if (cnt_measure_ % params_.skip_measure_ != 0)
00289     {
00290       pc_local_accum_.reset(new pcl::PointCloud<PointType>);
00291       pc_accum_header_.clear();
00292       return;
00293     }
00294 
00295     try
00296     {
00297       const geometry_msgs::TransformStamped trans = tfbuf_.lookupTransform(
00298           frame_ids_["base_link"],
00299           pc_local_accum_->header.frame_id,
00300           pcl_conversions::fromPCL(pc_local_accum_->header.stamp), ros::Duration(0.1));
00301 
00302       const Eigen::Affine3f trans_eigen =
00303           Eigen::Translation3f(
00304               trans.transform.translation.x,
00305               trans.transform.translation.y,
00306               trans.transform.translation.z) *
00307           Eigen::Quaternionf(
00308               trans.transform.rotation.w,
00309               trans.transform.rotation.x,
00310               trans.transform.rotation.y,
00311               trans.transform.rotation.z);
00312       pcl::transformPointCloud(*pc_local_accum_, *pc_local_accum_, trans_eigen);
00313     }
00314     catch (tf2::TransformException& e)
00315     {
00316       ROS_INFO("Failed to transform pointcloud: %s", e.what());
00317       pc_local_accum_.reset(new pcl::PointCloud<PointType>);
00318       pc_accum_header_.clear();
00319       return;
00320     }
00321     std::vector<Vec3> origins;
00322     for (auto& h : pc_accum_header_)
00323     {
00324       try
00325       {
00326         const geometry_msgs::TransformStamped trans = tfbuf_.lookupTransform(
00327             frame_ids_["base_link"], msg->header.stamp, h.frame_id, h.stamp, frame_ids_["odom"]);
00328         origins.push_back(Vec3(trans.transform.translation.x,
00329                                trans.transform.translation.y,
00330                                trans.transform.translation.z));
00331       }
00332       catch (tf2::TransformException& e)
00333       {
00334         ROS_INFO("Failed to transform pointcloud: %s", e.what());
00335         pc_local_accum_.reset(new pcl::PointCloud<PointType>);
00336         pc_accum_header_.clear();
00337         return;
00338       }
00339     }
00340 
00341     const auto ts = boost::chrono::high_resolution_clock::now();
00342 
00343     pcl::PointCloud<PointType>::Ptr pc_local_full(new pcl::PointCloud<PointType>);
00344     pcl::VoxelGrid18<PointType> ds;
00345     ds.setInputCloud(pc_local_accum_);
00346     ds.setLeafSize(params_.downsample_x_, params_.downsample_y_, params_.downsample_z_);
00347     ds.filter(*pc_local_full);
00348 
00349     std::map<std::string, pcl::PointCloud<PointType>::Ptr> pc_locals;
00350     for (auto& lm : lidar_measurements_)
00351     {
00352       lm.second->setGlobalLocalizationStatus(
00353           params_.num_particles_, pf_->getParticleSize());
00354       pc_locals[lm.first] = lm.second->filter(pc_local_full);
00355     }
00356 
00357     if (pc_locals["likelihood"]->size() == 0)
00358     {
00359       ROS_ERROR("All points are filtered out. Failed to localize.");
00360       return;
00361     }
00362     if (pc_locals["beam"] && pc_locals["beam"]->size() == 0)
00363     {
00364       ROS_DEBUG("All beam points are filtered out. Skipping beam model.");
00365     }
00366 
00367     float match_ratio_min = 1.0;
00368     float match_ratio_max = 0.0;
00369     NormalLikelihood<float> odom_error_lin_nd(params_.odom_err_integ_lin_sigma_);
00370     NormalLikelihood<float> odom_error_ang_nd(params_.odom_err_integ_ang_sigma_);
00371     auto measure_func = [this, &pc_locals,
00372                          &origins,
00373                          &odom_error_lin_nd,
00374                          &match_ratio_min, &match_ratio_max](const State6DOF& s) -> float
00375     {
00376       float likelihood = 1;
00377       std::map<std::string, float> qualities;
00378       for (auto& lm : lidar_measurements_)
00379       {
00380         const LidarMeasurementResult result = lm.second->measure(
00381             kdtree_, pc_locals[lm.first], origins, s);
00382         likelihood *= result.likelihood;
00383         qualities[lm.first] = result.quality;
00384       }
00385       if (match_ratio_min > qualities["likelihood"])
00386         match_ratio_min = qualities["likelihood"];
00387       if (match_ratio_max < qualities["likelihood"])
00388         match_ratio_max = qualities["likelihood"];
00389 
00390       // odometry error integration
00391       const float odom_error =
00392           odom_error_lin_nd(s.odom_err_integ_lin_.norm());
00393       return likelihood * odom_error;
00394     };
00395     pf_->measure(measure_func);
00396 
00397     if (static_cast<int>(pf_->getParticleSize()) > params_.num_particles_)
00398     {
00399       auto bias_func = [](const State6DOF& s, float& p_bias) -> void
00400       {
00401         p_bias = 1.0;
00402       };
00403       pf_->bias(bias_func);
00404     }
00405     else
00406     {
00407       NormalLikelihood<float> nl_lin(params_.bias_var_dist_);
00408       NormalLikelihood<float> nl_ang(params_.bias_var_ang_);
00409       auto bias_func = [this, &nl_lin, &nl_ang](const State6DOF& s, float& p_bias) -> void
00410       {
00411         const float lin_diff = (s.pos_ - state_prev_.pos_).norm();
00412         Vec3 axis;
00413         float ang_diff;
00414         (s.rot_ * state_prev_.rot_.inv()).getAxisAng(axis, ang_diff);
00415         p_bias = nl_lin(lin_diff) * nl_ang(ang_diff) + 1e-6;
00416         assert(std::isfinite(p_bias));
00417       };
00418       pf_->bias(bias_func);
00419     }
00420     auto e = pf_->expectationBiased();
00421     const auto e_max = pf_->max();
00422 
00423     assert(std::isfinite(e.pos_.x_));
00424     assert(std::isfinite(e.pos_.y_));
00425     assert(std::isfinite(e.pos_.z_));
00426     assert(std::isfinite(e.rot_.x_));
00427     assert(std::isfinite(e.rot_.y_));
00428     assert(std::isfinite(e.rot_.z_));
00429     assert(std::isfinite(e.rot_.w_));
00430 
00431     e.rot_.normalize();
00432 
00433     if (lidar_measurements_["beam"])
00434     {
00435       visualization_msgs::MarkerArray markers;
00436 
00437       pcl::PointCloud<PointType>::Ptr pc_particle_beam(new pcl::PointCloud<PointType>);
00438       *pc_particle_beam = *pc_locals["beam"];
00439       e.transform(*pc_particle_beam);
00440       for (auto& p : pc_particle_beam->points)
00441       {
00442         const int beam_header_id = p.label;
00443         const Vec3 pos = e.pos_ + e.rot_ * origins[beam_header_id];
00444         const Vec3 end(p.x, p.y, p.z);
00445 
00446         visualization_msgs::Marker marker;
00447         marker.header.frame_id = frame_ids_["map"];
00448         marker.header.stamp = msg->header.stamp;
00449         marker.ns = "Rays";
00450         marker.id = markers.markers.size();
00451         marker.type = visualization_msgs::Marker::LINE_STRIP;
00452         marker.action = 0;
00453         marker.pose.position.x = 0.0;
00454         marker.pose.position.y = 0.0;
00455         marker.pose.position.z = 0.0;
00456         marker.pose.orientation.x = 0.0;
00457         marker.pose.orientation.y = 0.0;
00458         marker.pose.orientation.z = 0.0;
00459         marker.pose.orientation.w = 1.0;
00460         marker.scale.x = marker.scale.y = marker.scale.z = 0.04;
00461         marker.lifetime = ros::Duration(0.2);
00462         marker.frame_locked = true;
00463         marker.points.resize(2);
00464         marker.points[0].x = pos.x_;
00465         marker.points[0].y = pos.y_;
00466         marker.points[0].z = pos.z_;
00467         marker.points[1].x = end.x_;
00468         marker.points[1].y = end.y_;
00469         marker.points[1].z = end.z_;
00470         marker.colors.resize(2);
00471         marker.colors[0].a = 0.5;
00472         marker.colors[0].r = 1.0;
00473         marker.colors[0].g = 0.0;
00474         marker.colors[0].b = 0.0;
00475         marker.colors[1].a = 0.2;
00476         marker.colors[1].r = 1.0;
00477         marker.colors[1].g = 0.0;
00478         marker.colors[1].b = 0.0;
00479 
00480         markers.markers.push_back(marker);
00481       }
00482       const auto beam_model =
00483           std::dynamic_pointer_cast<LidarMeasurementModelBeam>(
00484               lidar_measurements_["beam"]);
00485       const float sin_total_ref = beam_model->getSinTotalRef();
00486       for (auto& p : pc_particle_beam->points)
00487       {
00488         const int beam_header_id = p.label;
00489         Raycast<PointType> ray(
00490             kdtree_,
00491             e.pos_ + e.rot_ * origins[beam_header_id],
00492             Vec3(p.x, p.y, p.z),
00493             params_.map_grid_min_, params_.map_grid_max_);
00494         for (auto point : ray)
00495         {
00496           if (point.collision_)
00497           {
00498             visualization_msgs::Marker marker;
00499             marker.header.frame_id = frame_ids_["map"];
00500             marker.header.stamp = msg->header.stamp;
00501             marker.ns = "Ray collisions";
00502             marker.id = markers.markers.size();
00503             marker.type = visualization_msgs::Marker::CUBE;
00504             marker.action = 0;
00505             marker.pose.position.x = point.pos_.x_;
00506             marker.pose.position.y = point.pos_.y_;
00507             marker.pose.position.z = point.pos_.z_;
00508             marker.pose.orientation.x = 0.0;
00509             marker.pose.orientation.y = 0.0;
00510             marker.pose.orientation.z = 0.0;
00511             marker.pose.orientation.w = 1.0;
00512             marker.scale.x = marker.scale.y = marker.scale.z = 0.4;
00513             marker.lifetime = ros::Duration(0.2);
00514             marker.frame_locked = true;
00515             marker.color.a = 0.8;
00516             marker.color.r = 1.0;
00517             marker.color.g = 0.0;
00518             marker.color.b = 0.0;
00519             if (point.sin_angle_ < sin_total_ref)
00520             {
00521               marker.color.a = 0.2;
00522             }
00523             markers.markers.push_back(marker);
00524             break;
00525           }
00526         }
00527       }
00528 
00529       pcl::PointCloud<PointType>::Ptr pc_particle(new pcl::PointCloud<PointType>);
00530       *pc_particle = *pc_locals["likelihood"];
00531       e.transform(*pc_particle);
00532       for (auto& p : pc_particle->points)
00533       {
00534         visualization_msgs::Marker marker;
00535         marker.header.frame_id = frame_ids_["map"];
00536         marker.header.stamp = msg->header.stamp;
00537         marker.ns = "Sample points";
00538         marker.id = markers.markers.size();
00539         marker.type = visualization_msgs::Marker::SPHERE;
00540         marker.action = 0;
00541         marker.pose.position.x = p.x;
00542         marker.pose.position.y = p.y;
00543         marker.pose.position.z = p.z;
00544         marker.pose.orientation.x = 0.0;
00545         marker.pose.orientation.y = 0.0;
00546         marker.pose.orientation.z = 0.0;
00547         marker.pose.orientation.w = 1.0;
00548         marker.scale.x = marker.scale.y = marker.scale.z = 0.2;
00549         marker.lifetime = ros::Duration(0.2);
00550         marker.frame_locked = true;
00551         marker.color.a = 1.0;
00552         marker.color.r = 1.0;
00553         marker.color.g = 0.0;
00554         marker.color.b = 1.0;
00555 
00556         markers.markers.push_back(marker);
00557       }
00558 
00559       pub_debug_marker_.publish(markers);
00560     }
00561 
00562     Vec3 map_pos;
00563     Quat map_rot;
00564     map_pos = e.pos_ - e.rot_ * odom_.rot_.inv() * odom_.pos_;
00565     map_rot = e.rot_ * odom_.rot_.inv();
00566 
00567     bool jump = false;
00568     if (static_cast<int>(pf_->getParticleSize()) > params_.num_particles_)
00569     {
00570       jump = true;
00571       state_prev_ = e;
00572     }
00573     else
00574     {
00575       Vec3 jump_axis;
00576       float jump_ang;
00577       float jump_dist = (e.pos_ - state_prev_.pos_).norm();
00578       (e.rot_.inv() * state_prev_.rot_).getAxisAng(jump_axis, jump_ang);
00579       if (jump_dist > params_.jump_dist_ ||
00580           fabs(jump_ang) > params_.jump_ang_)
00581       {
00582         ROS_INFO("Pose jumped pos:%0.3f, ang:%0.3f", jump_dist, jump_ang);
00583         jump = true;
00584 
00585         auto integ_reset_func = [](State6DOF& s)
00586         {
00587           s.odom_err_integ_lin_ = Vec3();
00588           s.odom_err_integ_ang_ = Vec3();
00589         };
00590         pf_->predict(integ_reset_func);
00591       }
00592       state_prev_ = e;
00593     }
00594     geometry_msgs::TransformStamped trans;
00595     if (has_odom_)
00596       trans.header.stamp = odom_last_ + tf_tolerance_base_ + *params_.tf_tolerance_;
00597     else
00598       trans.header.stamp = ros::Time::now() + tf_tolerance_base_ + *params_.tf_tolerance_;
00599     trans.header.frame_id = frame_ids_["map"];
00600     trans.child_frame_id = frame_ids_["odom"];
00601     auto rpy = map_rot.getRPY();
00602     if (jump)
00603     {
00604       f_ang_[0]->set(rpy.x_);
00605       f_ang_[1]->set(rpy.y_);
00606       f_ang_[2]->set(rpy.z_);
00607       f_pos_[0]->set(map_pos.x_);
00608       f_pos_[1]->set(map_pos.y_);
00609       f_pos_[2]->set(map_pos.z_);
00610     }
00611     rpy.x_ = f_ang_[0]->in(rpy.x_);
00612     rpy.y_ = f_ang_[1]->in(rpy.y_);
00613     rpy.z_ = f_ang_[2]->in(rpy.z_);
00614     map_rot.setRPY(rpy);
00615     map_pos.x_ = f_pos_[0]->in(map_pos.x_);
00616     map_pos.y_ = f_pos_[1]->in(map_pos.y_);
00617     map_pos.z_ = f_pos_[2]->in(map_pos.z_);
00618     trans.transform.translation = tf2::toMsg(tf2::Vector3(map_pos.x_, map_pos.y_, map_pos.z_));
00619     trans.transform.rotation = tf2::toMsg(tf2::Quaternion(map_rot.x_, map_rot.y_, map_rot.z_, map_rot.w_));
00620 
00621     std::vector<geometry_msgs::TransformStamped> transforms;
00622     transforms.push_back(trans);
00623 
00624     e.rot_ = map_rot * odom_.rot_;
00625     e.pos_ = map_pos + e.rot_ * odom_.rot_.inv() * odom_.pos_;
00626 
00627     assert(std::isfinite(e.pos_.x_));
00628     assert(std::isfinite(e.pos_.y_));
00629     assert(std::isfinite(e.pos_.z_));
00630     assert(std::isfinite(e.rot_.x_));
00631     assert(std::isfinite(e.rot_.y_));
00632     assert(std::isfinite(e.rot_.z_));
00633     assert(std::isfinite(e.rot_.w_));
00634 
00635     trans.header.frame_id = frame_ids_["map"];
00636     trans.child_frame_id = frame_ids_["floor"];
00637     trans.transform.translation = tf2::toMsg(tf2::Vector3(0.0, 0.0, e.pos_.z_));
00638     trans.transform.rotation = tf2::toMsg(tf2::Quaternion(0.0, 0.0, 0.0, 1.0));
00639 
00640     transforms.push_back(trans);
00641 
00642     if (publish_tf_)
00643       tfb_.sendTransform(transforms);
00644 
00645     auto cov = pf_->covariance();
00646 
00647     geometry_msgs::PoseWithCovarianceStamped pose;
00648     pose.header.stamp = msg->header.stamp;
00649     pose.header.frame_id = trans.header.frame_id;
00650     pose.pose.pose.position.x = e.pos_.x_;
00651     pose.pose.pose.position.y = e.pos_.y_;
00652     pose.pose.pose.position.z = e.pos_.z_;
00653     pose.pose.pose.orientation.x = e.rot_.x_;
00654     pose.pose.pose.orientation.y = e.rot_.y_;
00655     pose.pose.pose.orientation.z = e.rot_.z_;
00656     pose.pose.pose.orientation.w = e.rot_.w_;
00657     for (size_t i = 0; i < 36; i++)
00658     {
00659       pose.pose.covariance[i] = cov[i / 6][i % 6];
00660     }
00661     pub_pose_.publish(pose);
00662 
00663     {
00664       bool fix = false;
00665       Vec3 fix_axis;
00666       const float fix_ang = sqrtf(cov[3][3] + cov[4][4] + cov[5][5]);
00667       const float fix_dist = sqrtf(cov[0][0] + cov[1][1] + cov[2][2]);
00668       ROS_DEBUG("cov: lin %0.3f ang %0.3f", fix_dist, fix_ang);
00669       if (fix_dist < params_.fix_dist_ &&
00670           fabs(fix_ang) < params_.fix_ang_)
00671       {
00672         fix = true;
00673       }
00674 
00675       if (fix)
00676         ROS_DEBUG("Localization fixed");
00677     }
00678 
00679     if (output_pcd_)
00680     {
00681       pcl::PointCloud<PointType>::Ptr pc_particle(new pcl::PointCloud<PointType>);
00682       *pc_particle = *pc_locals["likelihood"];
00683       e.transform(*pc_particle);
00684       *pc_all_accum_ += *pc_particle;
00685     }
00686 
00687     if ((msg->header.stamp - match_output_last_ > *params_.match_output_interval_ ||
00688          msg->header.stamp < match_output_last_ - ros::Duration(1.0)) &&
00689         (pub_matched_.getNumSubscribers() > 0 || pub_unmatched_.getNumSubscribers() > 0))
00690     {
00691       match_output_last_ = msg->header.stamp;
00692 
00693       sensor_msgs::PointCloud pc_match;
00694       pc_match.header.stamp = msg->header.stamp;
00695       pc_match.header.frame_id = frame_ids_["map"];
00696       sensor_msgs::PointCloud pc_unmatch;
00697       pc_unmatch.header.stamp = msg->header.stamp;
00698       pc_unmatch.header.frame_id = frame_ids_["map"];
00699 
00700       pcl::PointCloud<PointType>::Ptr pc_local(new pcl::PointCloud<PointType>);
00701       *pc_local = *pc_local_full;
00702 
00703       e.transform(*pc_local);
00704 
00705       std::vector<int> id(1);
00706       std::vector<float> sqdist(1);
00707       const double match_dist_sq = params_.match_output_dist_ * params_.match_output_dist_;
00708       for (auto& p : pc_local->points)
00709       {
00710         geometry_msgs::Point32 pp;
00711         pp.x = p.x;
00712         pp.y = p.y;
00713         pp.z = p.z;
00714 
00715         if (!kdtree_->radiusSearch(p, params_.unmatch_output_dist_, id, sqdist, 1))
00716         {
00717           pc_unmatch.points.push_back(pp);
00718         }
00719         else if (sqdist[0] < match_dist_sq)
00720         {
00721           pc_match.points.push_back(pp);
00722         }
00723       }
00724       if (pub_matched_.getNumSubscribers() > 0)
00725       {
00726         sensor_msgs::PointCloud2 pc2;
00727         sensor_msgs::convertPointCloudToPointCloud2(pc_match, pc2);
00728         pub_matched_.publish(pc2);
00729       }
00730       if (pub_unmatched_.getNumSubscribers() > 0)
00731       {
00732         sensor_msgs::PointCloud2 pc2;
00733         sensor_msgs::convertPointCloudToPointCloud2(pc_unmatch, pc2);
00734         pub_unmatched_.publish(pc2);
00735       }
00736     }
00737 
00738     geometry_msgs::PoseArray pa;
00739     if (has_odom_)
00740       pa.header.stamp = odom_last_ + tf_tolerance_base_ + *params_.tf_tolerance_;
00741     else
00742       pa.header.stamp = ros::Time::now() + tf_tolerance_base_ + *params_.tf_tolerance_;
00743     pa.header.frame_id = frame_ids_["map"];
00744     for (size_t i = 0; i < pf_->getParticleSize(); i++)
00745     {
00746       geometry_msgs::Pose pm;
00747       auto p = pf_->getParticle(i);
00748       p.rot_.normalize();
00749       pm.position.x = p.pos_.x_;
00750       pm.position.y = p.pos_.y_;
00751       pm.position.z = p.pos_.z_;
00752       pm.orientation.x = p.rot_.x_;
00753       pm.orientation.y = p.rot_.y_;
00754       pm.orientation.z = p.rot_.z_;
00755       pm.orientation.w = p.rot_.w_;
00756       pa.poses.push_back(pm);
00757     }
00758     pub_particle_.publish(pa);
00759 
00760     pf_->resample(State6DOF(
00761         Vec3(params_.resample_var_x_,
00762              params_.resample_var_y_,
00763              params_.resample_var_z_),
00764         Vec3(params_.resample_var_roll_,
00765              params_.resample_var_pitch_,
00766              params_.resample_var_yaw_)));
00767 
00768     std::normal_distribution<float> noise(0.0, 1.0);
00769     auto update_noise_func = [this, &noise](State6DOF& s)
00770     {
00771       s.noise_ll_ = noise(engine_) * params_.odom_err_lin_lin_;
00772       s.noise_la_ = noise(engine_) * params_.odom_err_lin_ang_;
00773       s.noise_aa_ = noise(engine_) * params_.odom_err_ang_ang_;
00774       s.noise_al_ = noise(engine_) * params_.odom_err_ang_lin_;
00775     };
00776     pf_->predict(update_noise_func);
00777 
00778     const auto tnow = boost::chrono::high_resolution_clock::now();
00779     ROS_DEBUG("MCL (%0.3f sec.)",
00780               boost::chrono::duration<float>(tnow - ts).count());
00781     const auto err_integ_map = e_max.rot_ * e_max.odom_err_integ_lin_;
00782     ROS_DEBUG("odom error integral lin: %0.3f, %0.3f, %0.3f, "
00783               "ang: %0.3f, %0.3f, %0.3f, "
00784               "pos: %0.3f, %0.3f, %0.3f, "
00785               "err on map: %0.3f, %0.3f, %0.3f",
00786               e_max.odom_err_integ_lin_.x_,
00787               e_max.odom_err_integ_lin_.y_,
00788               e_max.odom_err_integ_lin_.z_,
00789               e_max.odom_err_integ_ang_.x_,
00790               e_max.odom_err_integ_ang_.y_,
00791               e_max.odom_err_integ_ang_.z_,
00792               e_max.pos_.x_,
00793               e_max.pos_.y_,
00794               e_max.pos_.z_,
00795               err_integ_map.x_,
00796               err_integ_map.y_,
00797               err_integ_map.z_);
00798     ROS_DEBUG("match ratio min: %0.3f, max: %0.3f, pos: %0.3f, %0.3f, %0.3f",
00799               match_ratio_min,
00800               match_ratio_max,
00801               e.pos_.x_,
00802               e.pos_.y_,
00803               e.pos_.z_);
00804     if (match_ratio_max < params_.match_ratio_thresh_)
00805     {
00806       ROS_WARN_THROTTLE(3.0, "Low match_ratio. Expansion resetting.");
00807       pf_->noise(State6DOF(
00808           Vec3(params_.expansion_var_x_,
00809                params_.expansion_var_y_,
00810                params_.expansion_var_z_),
00811           Vec3(params_.expansion_var_roll_,
00812                params_.expansion_var_pitch_,
00813                params_.expansion_var_yaw_)));
00814       status.status = mcl_3dl_msgs::Status::EXPANSION_RESETTING;
00815     }
00816     pc_local_accum_.reset(new pcl::PointCloud<PointType>);
00817     pc_accum_header_.clear();
00818 
00819     ros::Time localized_current = ros::Time::now();
00820     float dt = (localized_current - localized_last_).toSec();
00821     if (dt > 1.0)
00822       dt = 1.0;
00823     else if (dt < 0.0)
00824       dt = 0.0;
00825     tf_tolerance_base_ = ros::Duration(localize_rate_->in(dt));
00826     localized_last_ = localized_current;
00827 
00828     if (static_cast<int>(pf_->getParticleSize()) > params_.num_particles_)
00829     {
00830       const int reduced = pf_->getParticleSize() * 0.75;
00831       if (reduced > params_.num_particles_)
00832       {
00833         pf_->resizeParticle(reduced);
00834       }
00835       else
00836       {
00837         pf_->resizeParticle(params_.num_particles_);
00838       }
00839       // wait 99.7% fix (three-sigma)
00840       global_localization_fix_cnt_ = 1 + ceil(params_.lpf_step_) * 3.0;
00841     }
00842     if (global_localization_fix_cnt_)
00843     {
00844       global_localization_fix_cnt_--;
00845       status.status = mcl_3dl_msgs::Status::GLOBAL_LOCALIZATION;
00846     }
00847 
00848     status.match_ratio = match_ratio_max;
00849     status.particle_size = pf_->getParticleSize();
00850     pub_status_.publish(status);
00851   }
00852   void cbLandmark(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& msg)
00853   {
00854     NormalLikelihoodNd<float, 6> nd(
00855         Eigen::Matrix<double, 6, 6>(
00856             msg->pose.covariance.data())
00857             .cast<float>());
00858     const State6DOF measured(
00859         Vec3(msg->pose.pose.position.x,
00860              msg->pose.pose.position.y,
00861              msg->pose.pose.position.z),
00862         Quat(msg->pose.pose.orientation.x,
00863              msg->pose.pose.orientation.y,
00864              msg->pose.pose.orientation.z,
00865              msg->pose.pose.orientation.w));
00866     auto measure_func = [this, &measured, &nd](const State6DOF& s) -> float
00867     {
00868       State6DOF diff = s - measured;
00869       const Vec3 rot_rpy = diff.rot_.getRPY();
00870       const Eigen::Matrix<float, 6, 1> diff_vec =
00871           (Eigen::MatrixXf(6, 1) << diff.pos_.x_,
00872            diff.pos_.y_,
00873            diff.pos_.z_,
00874            rot_rpy.x_,
00875            rot_rpy.y_,
00876            rot_rpy.z_)
00877               .finished();
00878 
00879       return nd(diff_vec);
00880     };
00881     pf_->measure(measure_func);
00882   }
00883   void cbImu(const sensor_msgs::Imu::ConstPtr& msg)
00884   {
00885     Vec3 acc;
00886     acc.x_ = f_acc_[0]->in(msg->linear_acceleration.x);
00887     acc.y_ = f_acc_[1]->in(msg->linear_acceleration.y);
00888     acc.z_ = f_acc_[2]->in(msg->linear_acceleration.z);
00889 
00890     if (!has_imu_)
00891     {
00892       f_acc_[0]->set(0.0);
00893       f_acc_[1]->set(0.0);
00894       f_acc_[2]->set(0.0);
00895       imu_last_ = msg->header.stamp;
00896       has_imu_ = true;
00897       return;
00898     }
00899 
00900     float dt = (msg->header.stamp - imu_last_).toSec();
00901     if (dt < 0.0 || dt > 5.0)
00902     {
00903       ROS_WARN("Detected time jump in imu. Resetting.");
00904       has_imu_ = false;
00905       return;
00906     }
00907     else if (dt > 0.05)
00908     {
00909       Vec3 acc_measure = acc / acc.norm();
00910       try
00911       {
00912         geometry_msgs::Vector3 in, out;
00913         in.x = acc_measure.x_;
00914         in.y = acc_measure.y_;
00915         in.z = acc_measure.z_;
00916         // assuming imu frame is regit on base_link
00917         const geometry_msgs::TransformStamped trans = tfbuf_.lookupTransform(
00918             frame_ids_["base_link"], msg->header.frame_id, ros::Time(0));
00919         tf2::doTransform(in, out, trans);
00920         acc_measure = Vec3(out.x, out.y, out.z);
00921 
00922         imu_quat_.x_ = msg->orientation.x;
00923         imu_quat_.y_ = msg->orientation.y;
00924         imu_quat_.z_ = msg->orientation.z;
00925         imu_quat_.w_ = msg->orientation.w;
00926         Vec3 axis;
00927         float angle;
00928         imu_quat_.getAxisAng(axis, angle);
00929         axis = Quat(trans.transform.rotation.x,
00930                     trans.transform.rotation.y,
00931                     trans.transform.rotation.z,
00932                     trans.transform.rotation.w) *
00933                axis;
00934         imu_quat_.setAxisAng(axis, angle);
00935       }
00936       catch (tf2::TransformException& e)
00937       {
00938         return;
00939       }
00940 
00941       imu_measurement_model_->setAccMeasure(acc_measure);
00942       auto imu_measure_func = [this](const State6DOF& s) -> float
00943       {
00944         return imu_measurement_model_->measure(s);
00945       };
00946       pf_->measure(imu_measure_func);
00947 
00948       imu_last_ = msg->header.stamp;
00949     }
00950   }
00951   bool cbResizeParticle(mcl_3dl_msgs::ResizeParticleRequest& request,
00952                         mcl_3dl_msgs::ResizeParticleResponse& response)
00953   {
00954     pf_->resizeParticle(request.size);
00955     return true;
00956   }
00957   bool cbExpansionReset(std_srvs::TriggerRequest& request,
00958                         std_srvs::TriggerResponse& response)
00959   {
00960     pf_->noise(State6DOF(
00961         Vec3(params_.expansion_var_x_,
00962              params_.expansion_var_y_,
00963              params_.expansion_var_z_),
00964         Vec3(params_.expansion_var_roll_,
00965              params_.expansion_var_pitch_,
00966              params_.expansion_var_yaw_)));
00967     return true;
00968   }
00969   bool cbGlobalLocalization(std_srvs::TriggerRequest& request,
00970                             std_srvs::TriggerResponse& response)
00971   {
00972     if (!has_map_)
00973     {
00974       response.success = false;
00975       response.message = "No map received.";
00976       return true;
00977     }
00978     pcl::PointCloud<PointType>::Ptr points(new pcl::PointCloud<PointType>);
00979 
00980     pcl::VoxelGrid18<PointType> ds;
00981     ds.setInputCloud(pc_map_);
00982     ds.setLeafSize(
00983         params_.global_localization_grid_,
00984         params_.global_localization_grid_,
00985         params_.global_localization_grid_);
00986     ds.filter(*points);
00987 
00988     pcl::KdTreeFLANN<PointType>::Ptr kdtree(new pcl::KdTreeFLANN<PointType>);
00989     kdtree->setPointRepresentation(
00990         boost::dynamic_pointer_cast<
00991             pcl::PointRepresentation<PointType>,
00992             MyPointRepresentation>(boost::make_shared<MyPointRepresentation>(point_rep_)));
00993     kdtree->setInputCloud(points);
00994 
00995     auto pc_filter = [this, kdtree](const PointType& p)
00996     {
00997       std::vector<int> id(1);
00998       std::vector<float> sqdist(1);
00999       auto p2 = p;
01000       p2.z += 0.01 + params_.global_localization_grid_;
01001 
01002       return kdtree->radiusSearch(
01003           p2, params_.global_localization_grid_, id, sqdist, 1);
01004     };
01005     points->erase(
01006         std::remove_if(points->begin(), points->end(), pc_filter),
01007         points->end());
01008 
01009     const int dir = params_.global_localization_div_yaw_;
01010     pf_->resizeParticle(points->size() * dir);
01011     auto pit = points->begin();
01012 
01013     const float prob = 1.0 / static_cast<float>(points->size());
01014     int cnt = 0;
01015     for (auto& particle : *pf_)
01016     {
01017       assert(pit != points->end());
01018       particle.probability_ = prob;
01019       particle.probability_bias_ = 1.0;
01020       particle.state_.pos_.x_ = pit->x;
01021       particle.state_.pos_.y_ = pit->y;
01022       particle.state_.pos_.z_ = pit->z;
01023       particle.state_.rot_ = Quat(Vec3(0.0, 0.0, 2.0 * M_PI * cnt / dir)) * imu_quat_;
01024       particle.state_.rot_.normalize();
01025       if (++cnt >= dir)
01026       {
01027         cnt = 0;
01028         ++pit;
01029       }
01030     }
01031     response.success = true;
01032     response.message = std::to_string(points->size()) + " particles";
01033     return true;
01034   }
01035 
01036 public:
01037   MCL3dlNode(int argc, char* argv[])
01038     : nh_("")
01039     , pnh_("~")
01040     , tfl_(tfbuf_)
01041     , global_localization_fix_cnt_(0)
01042     , engine_(seed_gen_())
01043   {
01044     mcl_3dl_compat::checkCompatMode();
01045     sub_cloud_ = mcl_3dl_compat::subscribe(
01046         nh_, "cloud",
01047         pnh_, "cloud", 100, &MCL3dlNode::cbCloud, this);
01048     sub_odom_ = mcl_3dl_compat::subscribe(
01049         nh_, "odom",
01050         pnh_, "odom", 200, &MCL3dlNode::cbOdom, this);
01051     sub_imu_ = mcl_3dl_compat::subscribe(
01052         nh_, "imu/data",
01053         pnh_, "imu", 200, &MCL3dlNode::cbImu, this);
01054     sub_mapcloud_ = mcl_3dl_compat::subscribe(
01055         nh_, "mapcloud",
01056         pnh_, "mapcloud", 1, &MCL3dlNode::cbMapcloud, this);
01057     sub_mapcloud_update_ = mcl_3dl_compat::subscribe(
01058         nh_, "mapcloud_update",
01059         pnh_, "mapcloud_update", 1, &MCL3dlNode::cbMapcloudUpdate, this);
01060     sub_position_ = mcl_3dl_compat::subscribe(
01061         nh_, "initialpose",
01062         pnh_, "initialpose", 1, &MCL3dlNode::cbPosition, this);
01063     sub_landmark_ = mcl_3dl_compat::subscribe(
01064         nh_, "mcl_measurement",
01065         pnh_, "landmark", 1, &MCL3dlNode::cbLandmark, this);
01066 
01067     pub_pose_ = nh_.advertise<geometry_msgs::PoseWithCovarianceStamped>("amcl_pose", 5, false);
01068     pub_particle_ = pnh_.advertise<geometry_msgs::PoseArray>("particles", 1, true);
01069     pub_mapcloud_ = pnh_.advertise<sensor_msgs::PointCloud2>("updated_map", 1, true);
01070     pub_debug_marker_ = pnh_.advertise<visualization_msgs::MarkerArray>("debug_marker", 1, true);
01071     pub_status_ = pnh_.advertise<mcl_3dl_msgs::Status>("status", 1, true);
01072     pub_matched_ = pnh_.advertise<sensor_msgs::PointCloud2>("matched", 2, true);
01073     pub_unmatched_ = pnh_.advertise<sensor_msgs::PointCloud2>("unmatched", 2, true);
01074 
01075     srv_particle_size_ = mcl_3dl_compat::advertiseService(
01076         nh_, "resize_mcl_particle",
01077         pnh_, "resize_particle", &MCL3dlNode::cbResizeParticle, this);
01078     srv_global_localization_ = mcl_3dl_compat::advertiseService(
01079         nh_, "global_localization",
01080         pnh_, "global_localization", &MCL3dlNode::cbGlobalLocalization, this);
01081     srv_expansion_reset_ = mcl_3dl_compat::advertiseService(
01082         nh_, "expansion_resetting",
01083         pnh_, "expansion_resetting", &MCL3dlNode::cbExpansionReset, this);
01084 
01085     pnh_.param("map_frame", frame_ids_["map"], std::string("map"));
01086     pnh_.param("robot_frame", frame_ids_["base_link"], std::string("base_link"));
01087     pnh_.param("odom_frame", frame_ids_["odom"], std::string("odom"));
01088     pnh_.param("floor_frame", frame_ids_["floor"], std::string("floor"));
01089 
01090     mcl_3dl_compat::paramRename<double>(pnh_, "likelihood/clip_near", "clip_near");
01091     mcl_3dl_compat::paramRename<double>(pnh_, "likelihood/clip_far", "clip_far");
01092     mcl_3dl_compat::paramRename<double>(pnh_, "likelihood/clip_z_min", "clip_z_min");
01093     mcl_3dl_compat::paramRename<double>(pnh_, "likelihood/clip_z_max", "clip_z_max");
01094     mcl_3dl_compat::paramRename<double>(pnh_, "likelihood/match_dist_min", "match_dist_min");
01095     mcl_3dl_compat::paramRename<double>(pnh_, "likelihood/match_dist_flat", "match_dist_flat");
01096     mcl_3dl_compat::paramRename<double>(pnh_, "likelihood/match_weight", "match_weight");
01097     mcl_3dl_compat::paramRename<double>(pnh_, "likelihood/num_points", "num_points");
01098     mcl_3dl_compat::paramRename<double>(pnh_, "likelihood/num_points_global", "num_points_global");
01099 
01100     mcl_3dl_compat::paramRename<double>(pnh_, "beam/clip_near", "clip_beam_near");
01101     mcl_3dl_compat::paramRename<double>(pnh_, "beam/clip_far", "clip_beam_far");
01102     mcl_3dl_compat::paramRename<double>(pnh_, "beam/clip_z_min", "clip_beam_z_min");
01103     mcl_3dl_compat::paramRename<double>(pnh_, "beam/clip_z_max", "clip_beam_z_max");
01104     mcl_3dl_compat::paramRename<double>(pnh_, "beam/num_points", "num_points_beam");
01105     mcl_3dl_compat::paramRename<double>(pnh_, "beam/beam_likelihood", "beam_likelihood");
01106     mcl_3dl_compat::paramRename<double>(pnh_, "beam/ang_total_ref", "ang_total_ref");
01107 
01108     pnh_.param("map_downsample_x", params_.map_downsample_x_, 0.1);
01109     pnh_.param("map_downsample_y", params_.map_downsample_y_, 0.1);
01110     pnh_.param("map_downsample_z", params_.map_downsample_z_, 0.1);
01111     pnh_.param("downsample_x", params_.downsample_x_, 0.1);
01112     pnh_.param("downsample_y", params_.downsample_y_, 0.1);
01113     pnh_.param("downsample_z", params_.downsample_z_, 0.05);
01114     params_.map_grid_min_ = std::min(std::min(params_.map_downsample_x_, params_.map_downsample_y_),
01115                                      params_.map_downsample_z_);
01116     params_.map_grid_max_ = std::max(std::max(params_.map_downsample_x_, params_.map_downsample_y_),
01117                                      params_.map_downsample_z_);
01118     pnh_.param("update_downsample_x", params_.update_downsample_x_, 0.3);
01119     pnh_.param("update_downsample_y", params_.update_downsample_y_, 0.3);
01120     pnh_.param("update_downsample_z", params_.update_downsample_z_, 0.3);
01121     double map_update_interval_t;
01122     pnh_.param("map_update_interval_interval", map_update_interval_t, 2.0);
01123     params_.map_update_interval_.reset(new ros::Duration(map_update_interval_t));
01124 
01125     double weight[3];
01126     float weight_f[4];
01127     pnh_.param("dist_weight_x", weight[0], 1.0);
01128     pnh_.param("dist_weight_y", weight[1], 1.0);
01129     pnh_.param("dist_weight_z", weight[2], 5.0);
01130     for (size_t i = 0; i < 3; i++)
01131       weight_f[i] = weight[i];
01132     weight_f[3] = 0.0;
01133     point_rep_.setRescaleValues(weight_f);
01134 
01135     pnh_.param("global_localization_grid_lin", params_.global_localization_grid_, 0.3);
01136     double grid_ang;
01137     pnh_.param("global_localization_grid_ang", grid_ang, 0.524);
01138     params_.global_localization_div_yaw_ = lroundf(2 * M_PI / grid_ang);
01139 
01140     pnh_.param("num_particles", params_.num_particles_, 64);
01141     pf_.reset(new pf::ParticleFilter<State6DOF, float, ParticleWeightedMeanQuat>(params_.num_particles_));
01142 
01143     pnh_.param("resample_var_x", params_.resample_var_x_, 0.05);
01144     pnh_.param("resample_var_y", params_.resample_var_y_, 0.05);
01145     pnh_.param("resample_var_z", params_.resample_var_z_, 0.05);
01146     pnh_.param("resample_var_roll", params_.resample_var_roll_, 0.05);
01147     pnh_.param("resample_var_pitch", params_.resample_var_pitch_, 0.05);
01148     pnh_.param("resample_var_yaw", params_.resample_var_yaw_, 0.05);
01149     pnh_.param("expansion_var_x", params_.expansion_var_x_, 0.2);
01150     pnh_.param("expansion_var_y", params_.expansion_var_y_, 0.2);
01151     pnh_.param("expansion_var_z", params_.expansion_var_z_, 0.2);
01152     pnh_.param("expansion_var_roll", params_.expansion_var_roll_, 0.05);
01153     pnh_.param("expansion_var_pitch", params_.expansion_var_pitch_, 0.05);
01154     pnh_.param("expansion_var_yaw", params_.expansion_var_yaw_, 0.05);
01155     pnh_.param("match_ratio_thresh", params_.match_ratio_thresh_, 0.0);
01156 
01157     pnh_.param("odom_err_lin_lin", params_.odom_err_lin_lin_, 0.10);
01158     pnh_.param("odom_err_lin_ang", params_.odom_err_lin_ang_, 0.05);
01159     pnh_.param("odom_err_ang_lin", params_.odom_err_ang_lin_, 0.05);
01160     pnh_.param("odom_err_ang_ang", params_.odom_err_ang_ang_, 0.05);
01161 
01162     pnh_.param("odom_err_integ_lin_tc", params_.odom_err_integ_lin_tc_, 10.0);
01163     pnh_.param("odom_err_integ_lin_sigma", params_.odom_err_integ_lin_sigma_, 100.0);
01164     pnh_.param("odom_err_integ_ang_tc", params_.odom_err_integ_ang_tc_, 10.0);
01165     pnh_.param("odom_err_integ_ang_sigma", params_.odom_err_integ_ang_sigma_, 100.0);
01166 
01167     double x, y, z;
01168     double roll, pitch, yaw;
01169     double v_x, v_y, v_z;
01170     double v_roll, v_pitch, v_yaw;
01171     pnh_.param("init_x", x, 0.0);
01172     pnh_.param("init_y", y, 0.0);
01173     pnh_.param("init_z", z, 0.0);
01174     pnh_.param("init_roll", roll, 0.0);
01175     pnh_.param("init_pitch", pitch, 0.0);
01176     pnh_.param("init_yaw", yaw, 0.0);
01177     pnh_.param("init_var_x", v_x, 2.0);
01178     pnh_.param("init_var_y", v_y, 2.0);
01179     pnh_.param("init_var_z", v_z, 0.5);
01180     pnh_.param("init_var_roll", v_roll, 0.1);
01181     pnh_.param("init_var_pitch", v_pitch, 0.1);
01182     pnh_.param("init_var_yaw", v_yaw, 0.5);
01183     pf_->init(
01184         State6DOF(
01185             Vec3(x, y, z),
01186             Quat(Vec3(roll, pitch, yaw))),
01187         State6DOF(
01188             Vec3(v_x, v_y, v_z),
01189             Vec3(v_roll, v_pitch, v_yaw)));
01190 
01191     pnh_.param("lpf_step", params_.lpf_step_, 16.0);
01192     f_pos_[0].reset(new Filter(Filter::FILTER_LPF, params_.lpf_step_, 0.0));
01193     f_pos_[1].reset(new Filter(Filter::FILTER_LPF, params_.lpf_step_, 0.0));
01194     f_pos_[2].reset(new Filter(Filter::FILTER_LPF, params_.lpf_step_, 0.0));
01195     f_ang_[0].reset(new Filter(Filter::FILTER_LPF, params_.lpf_step_, 0.0, true));
01196     f_ang_[1].reset(new Filter(Filter::FILTER_LPF, params_.lpf_step_, 0.0, true));
01197     f_ang_[2].reset(new Filter(Filter::FILTER_LPF, params_.lpf_step_, 0.0, true));
01198 
01199     double acc_lpf_step;
01200     pnh_.param("acc_lpf_step", acc_lpf_step, 128.0);
01201     f_acc_[0].reset(new Filter(Filter::FILTER_LPF, acc_lpf_step, 0.0));
01202     f_acc_[1].reset(new Filter(Filter::FILTER_LPF, acc_lpf_step, 0.0));
01203     f_acc_[2].reset(new Filter(Filter::FILTER_LPF, acc_lpf_step, 0.0));
01204     pnh_.param("acc_var", params_.acc_var_, M_PI / 4.0);  // 45 deg
01205 
01206     pnh_.param("jump_dist", params_.jump_dist_, 1.0);
01207     pnh_.param("jump_ang", params_.jump_ang_, 1.57);
01208     pnh_.param("fix_dist", params_.fix_dist_, 0.2);
01209     pnh_.param("fix_ang", params_.fix_ang_, 0.1);
01210     pnh_.param("bias_var_dist", params_.bias_var_dist_, 2.0);
01211     pnh_.param("bias_var_ang", params_.bias_var_ang_, 1.57);
01212 
01213     pnh_.param("skip_measure", params_.skip_measure_, 1);
01214     cnt_measure_ = 0;
01215     pnh_.param("accum_cloud", params_.accum_cloud_, 1);
01216     cnt_accum_ = 0;
01217 
01218     pnh_.param("match_output_dist", params_.match_output_dist_, 0.1);
01219     pnh_.param("unmatch_output_dist", params_.unmatch_output_dist_, 0.5);
01220     double match_output_interval_t;
01221     pnh_.param("match_output_interval_interval", match_output_interval_t, 0.2);
01222     params_.match_output_interval_.reset(new ros::Duration(match_output_interval_t));
01223 
01224     double tf_tolerance_t;
01225     pnh_.param("tf_tolerance", tf_tolerance_t, 0.05);
01226     params_.tf_tolerance_.reset(new ros::Duration(tf_tolerance_t));
01227 
01228     pnh_.param("publish_tf", publish_tf_, true);
01229     pnh_.param("output_pcd", output_pcd_, false);
01230 
01231     imu_quat_ = Quat(0.0, 0.0, 0.0, 1.0);
01232 
01233     has_odom_ = has_map_ = has_imu_ = false;
01234     localize_rate_.reset(new Filter(Filter::FILTER_LPF, 5.0, 0.0));
01235 
01236     lidar_measurements_["likelihood"] =
01237         LidarMeasurementModelBase::Ptr(
01238             new LidarMeasurementModelLikelihood());
01239     lidar_measurements_["beam"] =
01240         LidarMeasurementModelBase::Ptr(
01241             new LidarMeasurementModelBeam(
01242                 params_.map_downsample_x_, params_.map_downsample_y_, params_.map_downsample_z_));
01243     imu_measurement_model_ = ImuMeasurementModelBase::Ptr(new ImuMeasurementModelGravity(params_.acc_var_));
01244     motion_prediction_model_ = MotionPredictionModelBase::Ptr(
01245         new MotionPredictionModelDifferentialDrive(params_.odom_err_integ_lin_tc_,
01246                                                    params_.odom_err_integ_ang_tc_));
01247 
01248     float max_search_radius = 0;
01249     for (auto& lm : lidar_measurements_)
01250     {
01251       lm.second->loadConfig(pnh_, lm.first);
01252       max_search_radius = std::max(max_search_radius, lm.second->getMaxSearchRange());
01253     }
01254 
01255     double map_chunk;
01256     pnh_.param("map_chunk", map_chunk, 20.0);
01257     ROS_DEBUG("max_search_radius: %0.3f", max_search_radius);
01258     kdtree_.reset(new ChunkedKdtree<PointType>(map_chunk, max_search_radius));
01259     kdtree_->setEpsilon(params_.map_grid_min_ / 16);
01260     kdtree_->setPointRepresentation(
01261         boost::dynamic_pointer_cast<
01262             pcl::PointRepresentation<PointType>,
01263             MyPointRepresentation>(boost::make_shared<MyPointRepresentation>(point_rep_)));
01264 
01265     map_update_timer_ = nh_.createTimer(
01266         *params_.map_update_interval_,
01267         &MCL3dlNode::cbMapUpdateTimer, this);
01268   }
01269   ~MCL3dlNode()
01270   {
01271     if (output_pcd_ && pc_all_accum_)
01272     {
01273       std::cerr << "mcl_3dl: saving pcd file.";
01274       std::cerr << " (" << pc_all_accum_->points.size() << " points)" << std::endl;
01275       pcl::io::savePCDFileBinary("mcl_3dl.pcd", *pc_all_accum_);
01276     }
01277   }
01278 
01279   void cbMapUpdateTimer(const ros::TimerEvent& event)
01280   {
01281     if (has_map_)
01282     {
01283       const auto ts = boost::chrono::high_resolution_clock::now();
01284       if (pc_update_)
01285       {
01286         if (!pc_map2_)
01287           pc_map2_.reset(new pcl::PointCloud<PointType>);
01288         *pc_map2_ = *pc_map_ + *pc_update_;
01289         pc_update_.reset();
01290       }
01291       else
01292       {
01293         if (pc_map2_)
01294           return;
01295         pc_map2_ = pc_map_;
01296       }
01297       kdtree_->setInputCloud(pc_map2_);
01298 
01299       sensor_msgs::PointCloud2 out;
01300       pcl::toROSMsg(*pc_map2_, out);
01301       pub_mapcloud_.publish(out);
01302       const auto tnow = boost::chrono::high_resolution_clock::now();
01303       ROS_DEBUG("Map update (%0.3f sec.)",
01304                 boost::chrono::duration<float>(tnow - ts).count());
01305     }
01306   }
01307 
01308 protected:
01309   ros::NodeHandle nh_;
01310   ros::NodeHandle pnh_;
01311 
01312   ros::Subscriber sub_cloud_;
01313   ros::Subscriber sub_mapcloud_;
01314   ros::Subscriber sub_mapcloud_update_;
01315   ros::Subscriber sub_odom_;
01316   ros::Subscriber sub_imu_;
01317   ros::Subscriber sub_position_;
01318   ros::Subscriber sub_landmark_;
01319   ros::Publisher pub_particle_;
01320   ros::Publisher pub_mapcloud_;
01321   ros::Publisher pub_pose_;
01322   ros::Publisher pub_matched_;
01323   ros::Publisher pub_unmatched_;
01324   ros::Publisher pub_debug_marker_;
01325   ros::Publisher pub_status_;
01326   ros::Timer map_update_timer_;
01327   ros::ServiceServer srv_particle_size_;
01328   ros::ServiceServer srv_global_localization_;
01329   ros::ServiceServer srv_expansion_reset_;
01330 
01331   tf2_ros::Buffer tfbuf_;
01332   tf2_ros::TransformListener tfl_;
01333   tf2_ros::TransformBroadcaster tfb_;
01334 
01335   std::shared_ptr<Filter> f_pos_[3];
01336   std::shared_ptr<Filter> f_ang_[3];
01337   std::shared_ptr<Filter> f_acc_[3];
01338   std::shared_ptr<Filter> localize_rate_;
01339   ros::Time localized_last_;
01340   ros::Duration tf_tolerance_base_;
01341 
01342   Parameters params_;
01343   std::map<std::string, std::string> frame_ids_;
01344   bool output_pcd_;
01345   bool publish_tf_;
01346 
01347   ros::Time match_output_last_;
01348   ros::Time odom_last_;
01349   bool has_map_;
01350   bool has_odom_;
01351   bool has_imu_;
01352   State6DOF odom_;
01353   State6DOF odom_prev_;
01354   std::map<std::string, bool> frames_;
01355   std::vector<std::string> frames_v_;
01356   size_t frame_num_;
01357   State6DOF state_prev_;
01358   ros::Time imu_last_;
01359   int cnt_measure_;
01360   int cnt_accum_;
01361   Quat imu_quat_;
01362   size_t global_localization_fix_cnt_;
01363 
01364   MyPointRepresentation point_rep_;
01365 
01366   pcl::PointCloud<PointType>::Ptr pc_map_;
01367   pcl::PointCloud<PointType>::Ptr pc_map2_;
01368   pcl::PointCloud<PointType>::Ptr pc_update_;
01369   pcl::PointCloud<PointType>::Ptr pc_all_accum_;
01370   pcl::PointCloud<PointType>::Ptr pc_local_accum_;
01371   ChunkedKdtree<PointType>::Ptr kdtree_;
01372   std::vector<std_msgs::Header> pc_accum_header_;
01373 
01374   std::map<
01375       std::string,
01376       LidarMeasurementModelBase::Ptr> lidar_measurements_;
01377   ImuMeasurementModelBase::Ptr imu_measurement_model_;
01378   MotionPredictionModelBase::Ptr motion_prediction_model_;
01379 
01380   std::random_device seed_gen_;
01381   std::default_random_engine engine_;
01382 };
01383 }  // namespace mcl_3dl
01384 
01385 int main(int argc, char* argv[])
01386 {
01387   ros::init(argc, argv, "mcl_3dl");
01388 
01389   mcl_3dl::MCL3dlNode mcl(argc, argv);
01390   ros::spin();
01391 
01392   return 0;
01393 }


mcl_3dl
Author(s): Atsushi Watanabe
autogenerated on Thu Jun 20 2019 20:04:43