00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
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
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
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
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);
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 }
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 }