mcl_3dl.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016-2020, the mcl_3dl authors
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <algorithm>
31 #include <cassert>
32 #include <cmath>
33 #include <limits>
34 #include <map>
35 #include <memory>
36 #include <string>
37 #include <utility>
38 #include <vector>
39 
40 #include <Eigen/Core>
41 
42 #include <boost/chrono.hpp>
43 #include <boost/shared_ptr.hpp>
44 
45 #include <ros/ros.h>
46 
47 #include <sensor_msgs/PointCloud2.h>
49 #include <nav_msgs/Odometry.h>
50 #include <sensor_msgs/Imu.h>
51 #include <geometry_msgs/PoseArray.h>
52 #include <geometry_msgs/PoseWithCovarianceStamped.h>
53 #include <geometry_msgs/TransformStamped.h>
54 #include <visualization_msgs/MarkerArray.h>
55 #include <mcl_3dl_msgs/ResizeParticle.h>
56 #include <mcl_3dl_msgs/Status.h>
57 #include <std_srvs/Trigger.h>
59 
64 
66 #include <pcl/point_types.h>
67 #include <pcl_ros/point_cloud.h>
68 #include <pcl_ros/transforms.h>
69 #include <pcl/conversions.h>
70 #include <pcl/filters/voxel_grid.h>
71 #include <pcl/kdtree/kdtree.h>
72 #include <pcl/kdtree/kdtree_flann.h>
73 
75 
76 #include <mcl_3dl/chunked_kdtree.h>
77 #include <mcl_3dl/cloud_accum.h>
78 #include <mcl_3dl/filter.h>
79 #include <mcl_3dl/filter_vec3.h>
87 #include <mcl_3dl/nd.h>
89 #include <mcl_3dl/parameters.h>
90 #include <mcl_3dl/pf.h>
94 #include <mcl_3dl/point_types.h>
95 #include <mcl_3dl/quat.h>
96 #include <mcl_3dl/raycast.h>
97 #include <mcl_3dl/state_6dof.h>
98 #include <mcl_3dl/vec3.h>
99 
101 
102 namespace mcl_3dl
103 {
105 {
106 protected:
108  std::shared_ptr<pf::ParticleFilter<State6DOF, float, ParticleWeightedMeanQuat, std::default_random_engine>> pf_;
109 
110  class MyPointRepresentation : public pcl::PointRepresentation<PointType>
111  {
112  using pcl::PointRepresentation<PointType>::nr_dimensions_;
113 
114  public:
116  {
117  nr_dimensions_ = 3;
118  }
119 
120  virtual void copyToFloatArray(const PointType& p, float* out) const
121  {
122  out[0] = p.x;
123  out[1] = p.y;
124  out[2] = p.z;
125  }
126  };
127  void cbMapcloud(const sensor_msgs::PointCloud2::ConstPtr& msg)
128  {
129  ROS_INFO("map received");
130  pcl::PointCloud<PointType>::Ptr pc_tmp(new pcl::PointCloud<PointType>);
131  if (!mcl_3dl::fromROSMsg(*msg, *pc_tmp))
132  {
133  has_map_ = false;
134  return;
135  }
136  const ros::Time map_stamp = (msg->header.stamp != ros::Time()) ? msg->header.stamp : ros::Time::now();
137  pcl_conversions::toPCL(map_stamp, pc_tmp->header.stamp);
138 
139  pc_map_.reset(new pcl::PointCloud<PointType>);
140  pc_map2_.reset();
141  pc_update_.reset();
143  ds.setInputCloud(pc_tmp);
145  ds.filter(*pc_map_);
146  pc_all_accum_.reset(new pcl::PointCloud<PointType>);
147  has_map_ = true;
148 
149  accumClear();
150  accum_->reset();
151 
152  ROS_INFO("map original: %d points", (int)pc_tmp->points.size());
153  ROS_INFO("map reduced: %d points", (int)pc_map_->points.size());
154 
156  }
157  void cbMapcloudUpdate(const sensor_msgs::PointCloud2::ConstPtr& msg)
158  {
159  ROS_INFO("map_update received");
160  pcl::PointCloud<PointType>::Ptr pc_tmp(new pcl::PointCloud<PointType>);
161  if (!mcl_3dl::fromROSMsg(*msg, *pc_tmp))
162  return;
163 
164  pc_update_.reset(new pcl::PointCloud<PointType>);
166  ds.setInputCloud(pc_tmp);
168  ds.filter(*pc_update_);
169  }
170 
171  void cbPosition(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& msg)
172  {
173  const double len2 =
174  msg->pose.pose.orientation.x * msg->pose.pose.orientation.x +
175  msg->pose.pose.orientation.y * msg->pose.pose.orientation.y +
176  msg->pose.pose.orientation.z * msg->pose.pose.orientation.z +
177  msg->pose.pose.orientation.w * msg->pose.pose.orientation.w;
178  if (std::abs(len2 - 1.0) > 0.1)
179  {
180  ROS_ERROR("Discarded invalid initialpose. The orientation must be unit quaternion.");
181  return;
182  }
183 
184  geometry_msgs::PoseStamped pose_in, pose;
185  pose_in.header = msg->header;
186  pose_in.pose = msg->pose.pose;
187  try
188  {
189  const geometry_msgs::TransformStamped trans = tfbuf_.lookupTransform(
190  params_.frame_ids_["map"], pose_in.header.frame_id, pose_in.header.stamp, ros::Duration(1.0));
191  tf2::doTransform(pose_in, pose, trans);
192  }
193  catch (tf2::TransformException& e)
194  {
195  return;
196  }
197  const State6DOF mean(Vec3(pose.pose.position.x, pose.pose.position.y, pose.pose.position.z),
198  Quat(pose.pose.orientation.x,
199  pose.pose.orientation.y,
200  pose.pose.orientation.z,
201  pose.pose.orientation.w));
202  const MultivariateNoiseGenerator<float> noise_gen(mean, msg->pose.covariance);
203  pf_->initUsingNoiseGenerator(noise_gen);
204 
205  pc_update_.reset();
206  auto integ_reset_func = [](State6DOF& s)
207  {
208  s.odom_err_integ_lin_ = Vec3();
209  s.odom_err_integ_ang_ = Vec3();
210  };
211  pf_->predict(integ_reset_func);
212 
214  }
215 
216  void cbOdom(const nav_msgs::Odometry::ConstPtr& msg)
217  {
218  odom_ =
219  State6DOF(
220  Vec3(msg->pose.pose.position.x,
221  msg->pose.pose.position.y,
222  msg->pose.pose.position.z),
223  Quat(msg->pose.pose.orientation.x,
224  msg->pose.pose.orientation.y,
225  msg->pose.pose.orientation.z,
226  msg->pose.pose.orientation.w));
227  if (!has_odom_)
228  {
229  odom_prev_ = odom_;
230  odom_last_ = msg->header.stamp;
231  has_odom_ = true;
232  return;
233  }
234  const float dt = (msg->header.stamp - odom_last_).toSec();
235  if (dt < 0.0 || dt > 5.0)
236  {
237  ROS_WARN("Detected time jump in odometry. Resetting.");
238  has_odom_ = false;
239  return;
240  }
241  else if (dt > 0.05)
242  {
243  motion_prediction_model_->setOdoms(odom_prev_, odom_, dt);
244  auto prediction_func = [this](State6DOF& s)
245  {
246  motion_prediction_model_->predict(s);
247  };
248  pf_->predict(prediction_func);
249  odom_last_ = msg->header.stamp;
250  odom_prev_ = odom_;
251  }
252  if (params_.fake_imu_)
253  {
254  const Vec3 accel = odom_.rot_ * Vec3(0.0, 0.0, 1.0);
255  sensor_msgs::Imu::Ptr imu(new sensor_msgs::Imu);
256  imu->header = msg->header;
257  imu->linear_acceleration.x = accel.x_;
258  imu->linear_acceleration.y = accel.y_;
259  imu->linear_acceleration.z = accel.z_;
260  imu->orientation = msg->pose.pose.orientation;
261  cbImu(imu);
262  }
263  }
264  void cbCloud(const sensor_msgs::PointCloud2::ConstPtr& msg)
265  {
266  status_ = mcl_3dl_msgs::Status();
267  status_.header.stamp = ros::Time::now();
268  status_.status = mcl_3dl_msgs::Status::NORMAL;
269  status_.error = mcl_3dl_msgs::Status::ERROR_NORMAL;
270  status_.convergence_status = mcl_3dl_msgs::Status::CONVERGENCE_STATUS_NORMAL;
271 
272  if (!has_map_)
273  return;
274 
275  accum_->push(
276  msg->header.frame_id,
277  msg,
278  std::bind(&MCL3dlNode::measure, this),
279  std::bind(&MCL3dlNode::accumCloud, this, std::placeholders::_1),
280  std::bind(&MCL3dlNode::accumClear, this));
281  }
282 
283  void accumClear()
284  {
285  pc_local_accum_.reset(new pcl::PointCloud<PointType>);
286  pc_local_accum_->header.frame_id = params_.frame_ids_["odom"];
287  pc_accum_header_.clear();
288  }
289 
290  bool accumCloud(const sensor_msgs::PointCloud2::ConstPtr& msg)
291  {
292  sensor_msgs::PointCloud2 pc_bl;
293  try
294  {
295  const geometry_msgs::TransformStamped trans = tfbuf_.lookupTransform(
296  params_.frame_ids_["odom"], msg->header.frame_id, msg->header.stamp, ros::Duration(0.1));
297  tf2::doTransform(*msg, pc_bl, trans);
298  }
299  catch (tf2::TransformException& e)
300  {
301  ROS_INFO("Failed to transform pointcloud: %s", e.what());
302  return false;
303  }
304  pcl::PointCloud<PointType>::Ptr pc_tmp(new pcl::PointCloud<PointType>);
305  if (!mcl_3dl::fromROSMsg(pc_bl, *pc_tmp))
306  {
307  ROS_INFO("Failed to convert pointcloud");
308  return false;
309  }
310 
311  for (auto& p : pc_tmp->points)
312  {
313  p.label = pc_accum_header_.size();
314  }
315  *pc_local_accum_ += *pc_tmp;
316  pc_accum_header_.push_back(msg->header);
317  return true;
318  }
319 
320  void measure()
321  {
322  cnt_measure_++;
323  if (cnt_measure_ % static_cast<size_t>(params_.skip_measure_) != 0)
324  {
325  return;
326  }
327 
328  if (pc_accum_header_.empty())
329  {
330  ROS_ERROR("MCL measure function is called without available pointcloud");
331  return;
332  }
333  const std_msgs::Header& header = pc_accum_header_.back();
334 
335  try
336  {
337  const geometry_msgs::TransformStamped trans = tfbuf_.lookupTransform(
338  params_.frame_ids_["base_link"],
339  pc_local_accum_->header.frame_id,
340  header.stamp, ros::Duration(0.1));
341 
342  const Eigen::Affine3f trans_eigen =
343  Eigen::Translation3f(
344  trans.transform.translation.x,
345  trans.transform.translation.y,
346  trans.transform.translation.z) *
347  Eigen::Quaternionf(
348  trans.transform.rotation.w,
349  trans.transform.rotation.x,
350  trans.transform.rotation.y,
351  trans.transform.rotation.z);
352  pcl::transformPointCloud(*pc_local_accum_, *pc_local_accum_, trans_eigen);
353  }
354  catch (tf2::TransformException& e)
355  {
356  ROS_INFO("Failed to transform pointcloud: %s", e.what());
357  return;
358  }
359  std::vector<Vec3> origins;
360  for (auto& h : pc_accum_header_)
361  {
362  try
363  {
364  const geometry_msgs::TransformStamped trans = tfbuf_.lookupTransform(
365  params_.frame_ids_["base_link"], header.stamp, h.frame_id, h.stamp, params_.frame_ids_["odom"]);
366  origins.push_back(Vec3(trans.transform.translation.x,
367  trans.transform.translation.y,
368  trans.transform.translation.z));
369  }
370  catch (tf2::TransformException& e)
371  {
372  ROS_INFO("Failed to transform pointcloud: %s", e.what());
373  return;
374  }
375  }
376 
377  const auto ts = boost::chrono::high_resolution_clock::now();
378 
379  pcl::PointCloud<PointType>::Ptr pc_local_full(new pcl::PointCloud<PointType>);
381  ds.setInputCloud(pc_local_accum_);
383  ds.filter(*pc_local_full);
384 
385  std::map<std::string, pcl::PointCloud<PointType>::Ptr> pc_locals;
386  for (auto& lm : lidar_measurements_)
387  {
388  lm.second->setGlobalLocalizationStatus(
389  params_.num_particles_, pf_->getParticleSize());
390 
392  {
393  const State6DOF prev_mean = pf_->expectation();
394  const float cov_ratio = std::max(0.1f, static_cast<float>(params_.num_particles_) / pf_->getParticleSize());
395  const std::vector<State6DOF> prev_cov = pf_->covariance(1.0, cov_ratio);
396  auto sampler = std::dynamic_pointer_cast<PointCloudSamplerWithNormal<PointType>>(lm.second->getRandomSampler());
397  sampler->setParticleStatistics(prev_mean, prev_cov);
398  }
399  pc_locals[lm.first] = lm.second->filter(pc_local_full);
400  }
401 
402  if (pc_locals["likelihood"]->size() == 0)
403  {
404  ROS_ERROR("All points are filtered out. Failed to localize.");
405  status_.error = mcl_3dl_msgs::Status::ERROR_POINTS_NOT_FOUND;
407  return;
408  }
409 
410  if (pc_locals["beam"] && pc_locals["beam"]->size() == 0)
411  {
412  ROS_DEBUG("All beam points are filtered out. Skipping beam model.");
413  }
414 
415  float match_ratio_min = 1.0;
416  float match_ratio_max = 0.0;
419  auto measure_func = [this, &pc_locals,
420  &origins,
421  &odom_error_lin_nd,
422  &match_ratio_min, &match_ratio_max](const State6DOF& s) -> float
423  {
424  float likelihood = 1;
425  std::map<std::string, float> qualities;
426  for (auto& lm : lidar_measurements_)
427  {
428  const LidarMeasurementResult result = lm.second->measure(
429  kdtree_, pc_locals[lm.first], origins, s);
430  likelihood *= result.likelihood;
431  qualities[lm.first] = result.quality;
432  }
433  if (match_ratio_min > qualities["likelihood"])
434  match_ratio_min = qualities["likelihood"];
435  if (match_ratio_max < qualities["likelihood"])
436  match_ratio_max = qualities["likelihood"];
437 
438  // odometry error integration
439  const float odom_error =
440  odom_error_lin_nd(s.odom_err_integ_lin_.norm());
441  return likelihood * odom_error;
442  };
443  pf_->measure(measure_func);
444 
445  if (static_cast<int>(pf_->getParticleSize()) > params_.num_particles_)
446  {
447  auto bias_func = [](const State6DOF& s, float& p_bias) -> void
448  {
449  p_bias = 1.0;
450  };
451  pf_->bias(bias_func);
452  }
453  else
454  {
457  auto bias_func = [this, &nl_lin, &nl_ang](const State6DOF& s, float& p_bias) -> void
458  {
459  const float lin_diff = (s.pos_ - state_prev_.pos_).norm();
460  Vec3 axis;
461  float ang_diff;
462  (s.rot_ * state_prev_.rot_.inv()).getAxisAng(axis, ang_diff);
463  p_bias = nl_lin(lin_diff) * nl_ang(ang_diff) + 1e-6;
464  assert(std::isfinite(p_bias));
465  };
466  pf_->bias(bias_func);
467  }
468  auto e = pf_->expectationBiased();
469  const auto e_max = pf_->max();
470 
471  assert(std::isfinite(e.pos_.x_));
472  assert(std::isfinite(e.pos_.y_));
473  assert(std::isfinite(e.pos_.z_));
474  assert(std::isfinite(e.rot_.x_));
475  assert(std::isfinite(e.rot_.y_));
476  assert(std::isfinite(e.rot_.z_));
477  assert(std::isfinite(e.rot_.w_));
478 
479  e.rot_.normalize();
480 
481  if (lidar_measurements_["beam"])
482  {
483  visualization_msgs::MarkerArray markers;
484 
485  pcl::PointCloud<PointType>::Ptr pc_particle_beam(new pcl::PointCloud<PointType>);
486  *pc_particle_beam = *pc_locals["beam"];
487  e.transform(*pc_particle_beam);
488  const auto beam_model = std::dynamic_pointer_cast<LidarMeasurementModelBeam>(lidar_measurements_["beam"]);
489  for (auto& p : pc_particle_beam->points)
490  {
491  const int beam_header_id = p.label;
492  const Vec3 pos = e.pos_ + e.rot_ * origins[beam_header_id];
493  const Vec3 end(p.x, p.y, p.z);
495  const LidarMeasurementModelBeam::BeamStatus beam_status = beam_model->getBeamStatus(kdtree_, pos, end, point);
496 
498  {
499  visualization_msgs::Marker marker;
500  marker.header.frame_id = params_.frame_ids_["map"];
501  marker.header.stamp = header.stamp;
502  marker.ns = "Ray collisions";
503  marker.id = markers.markers.size();
504  marker.type = visualization_msgs::Marker::CUBE;
505  marker.action = 0;
506  marker.pose.position.x = point.pos_.x_;
507  marker.pose.position.y = point.pos_.y_;
508  marker.pose.position.z = point.pos_.z_;
509  marker.pose.orientation.x = 0.0;
510  marker.pose.orientation.y = 0.0;
511  marker.pose.orientation.z = 0.0;
512  marker.pose.orientation.w = 1.0;
513  marker.scale.x = marker.scale.y = marker.scale.z = 0.2;
514  marker.lifetime = ros::Duration(0.2);
515  marker.frame_locked = true;
516  switch (beam_status)
517  {
519  marker.color.a = 0.8;
520  marker.color.r = 0.0;
521  marker.color.g = 1.0;
522  marker.color.b = 0.0;
523  break;
525  marker.color.a = 0.8;
526  marker.color.r = 1.0;
527  marker.color.g = 0.0;
528  marker.color.b = 0.0;
529  break;
531  marker.color.a = 0.2;
532  marker.color.r = 0.0;
533  marker.color.g = 1.0;
534  marker.color.b = 0.0;
535  break;
536  default:
537  break;
538  }
539  markers.markers.push_back(marker);
540  }
541 
542  visualization_msgs::Marker marker;
543  marker.header.frame_id = params_.frame_ids_["map"];
544  marker.header.stamp = header.stamp;
545  marker.ns = "Rays";
546  marker.id = markers.markers.size();
547  marker.type = visualization_msgs::Marker::LINE_STRIP;
548  marker.action = 0;
549  marker.pose.position.x = 0.0;
550  marker.pose.position.y = 0.0;
551  marker.pose.position.z = 0.0;
552  marker.pose.orientation.x = 0.0;
553  marker.pose.orientation.y = 0.0;
554  marker.pose.orientation.z = 0.0;
555  marker.pose.orientation.w = 1.0;
556  marker.scale.x = marker.scale.y = marker.scale.z = 0.04;
557  marker.lifetime = ros::Duration(0.2);
558  marker.frame_locked = true;
559  marker.points.resize(2);
560  marker.points[0].x = pos.x_;
561  marker.points[0].y = pos.y_;
562  marker.points[0].z = pos.z_;
563  marker.points[1].x = end.x_;
564  marker.points[1].y = end.y_;
565  marker.points[1].z = end.z_;
566  marker.colors.resize(2);
567 
568  switch (beam_status)
569  {
571  marker.colors[0].a = 0.5;
572  marker.colors[0].r = 0.0;
573  marker.colors[0].g = 1.0;
574  marker.colors[0].b = 0.0;
575  marker.colors[1].a = 0.8;
576  marker.colors[1].r = 0.0;
577  marker.colors[1].g = 1.0;
578  marker.colors[1].b = 0.0;
579  break;
581  marker.colors[0].a = 0.5;
582  marker.colors[0].r = 1.0;
583  marker.colors[0].g = 0.0;
584  marker.colors[0].b = 0.0;
585  marker.colors[1].a = 0.8;
586  marker.colors[1].r = 1.0;
587  marker.colors[1].g = 0.0;
588  marker.colors[1].b = 0.0;
589  break;
591  marker.colors[0].a = 0.5;
592  marker.colors[0].r = 0.0;
593  marker.colors[0].g = 0.0;
594  marker.colors[0].b = 1.0;
595  marker.colors[1].a = 0.8;
596  marker.colors[1].r = 0.0;
597  marker.colors[1].g = 0.0;
598  marker.colors[1].b = 1.0;
599  break;
601  marker.colors[0].a = 0.2;
602  marker.colors[0].r = 0.0;
603  marker.colors[0].g = 1.0;
604  marker.colors[0].b = 0.0;
605  marker.colors[1].a = 0.2;
606  marker.colors[1].r = 0.0;
607  marker.colors[1].g = 1.0;
608  marker.colors[1].b = 0.0;
609  break;
610  }
611  markers.markers.push_back(marker);
612  }
613 
614  pcl::PointCloud<PointType>::Ptr pc_particle(new pcl::PointCloud<PointType>);
615  *pc_particle = *pc_locals["likelihood"];
616  e.transform(*pc_particle);
617  for (auto& p : pc_particle->points)
618  {
619  visualization_msgs::Marker marker;
620  marker.header.frame_id = params_.frame_ids_["map"];
621  marker.header.stamp = header.stamp;
622  marker.ns = "Sample points";
623  marker.id = markers.markers.size();
624  marker.type = visualization_msgs::Marker::SPHERE;
625  marker.action = 0;
626  marker.pose.position.x = p.x;
627  marker.pose.position.y = p.y;
628  marker.pose.position.z = p.z;
629  marker.pose.orientation.x = 0.0;
630  marker.pose.orientation.y = 0.0;
631  marker.pose.orientation.z = 0.0;
632  marker.pose.orientation.w = 1.0;
633  marker.scale.x = marker.scale.y = marker.scale.z = 0.2;
634  marker.lifetime = ros::Duration(0.2);
635  marker.frame_locked = true;
636  marker.color.a = 1.0;
637  marker.color.r = 1.0;
638  marker.color.g = 0.0;
639  marker.color.b = 1.0;
640 
641  markers.markers.push_back(marker);
642  }
643 
644  pub_debug_marker_.publish(markers);
645  }
646 
647  Vec3 map_pos;
648  Quat map_rot;
649  map_pos = e.pos_ - e.rot_ * odom_.rot_.inv() * odom_.pos_;
650  map_rot = e.rot_ * odom_.rot_.inv();
651 
652  bool jump = false;
653  if (static_cast<int>(pf_->getParticleSize()) > params_.num_particles_)
654  {
655  jump = true;
656  state_prev_ = e;
657  }
658  else
659  {
660  Vec3 jump_axis;
661  float jump_ang;
662  float jump_dist = (e.pos_ - state_prev_.pos_).norm();
663  (e.rot_.inv() * state_prev_.rot_).getAxisAng(jump_axis, jump_ang);
664  if (jump_dist > params_.jump_dist_ ||
665  fabs(jump_ang) > params_.jump_ang_)
666  {
667  ROS_INFO("Pose jumped pos:%0.3f, ang:%0.3f", jump_dist, jump_ang);
668  jump = true;
669 
670  auto integ_reset_func = [](State6DOF& s)
671  {
672  s.odom_err_integ_lin_ = Vec3();
673  s.odom_err_integ_ang_ = Vec3();
674  };
675  pf_->predict(integ_reset_func);
676  }
677  state_prev_ = e;
678  }
679  geometry_msgs::TransformStamped trans;
680  if (has_odom_)
681  trans.header.stamp = odom_last_ + tf_tolerance_base_ + *params_.tf_tolerance_;
682  else
683  trans.header.stamp = ros::Time::now() + tf_tolerance_base_ + *params_.tf_tolerance_;
684  trans.header.frame_id = params_.frame_ids_["map"];
685  trans.child_frame_id = params_.frame_ids_["odom"];
686  const auto rpy = map_rot.getRPY();
687  if (jump)
688  {
689  f_ang_->set(rpy);
690  f_pos_->set(map_pos);
691  }
692  map_rot.setRPY(f_ang_->in(rpy));
693  map_pos = f_pos_->in(map_pos);
694  trans.transform.translation = tf2::toMsg(tf2::Vector3(map_pos.x_, map_pos.y_, map_pos.z_));
695  trans.transform.rotation = tf2::toMsg(tf2::Quaternion(map_rot.x_, map_rot.y_, map_rot.z_, map_rot.w_));
696 
697  std::vector<geometry_msgs::TransformStamped> transforms;
698  transforms.push_back(trans);
699 
700  e.rot_ = map_rot * odom_.rot_;
701  e.pos_ = map_pos + map_rot * odom_.pos_;
702 
703  assert(std::isfinite(e.pos_.x_));
704  assert(std::isfinite(e.pos_.y_));
705  assert(std::isfinite(e.pos_.z_));
706  assert(std::isfinite(e.rot_.x_));
707  assert(std::isfinite(e.rot_.y_));
708  assert(std::isfinite(e.rot_.z_));
709  assert(std::isfinite(e.rot_.w_));
710 
711  trans.header.frame_id = params_.frame_ids_["map"];
712  trans.child_frame_id = params_.frame_ids_["floor"];
713  trans.transform.translation = tf2::toMsg(tf2::Vector3(0.0, 0.0, e.pos_.z_));
714  trans.transform.rotation = tf2::toMsg(tf2::Quaternion(0.0, 0.0, 0.0, 1.0));
715 
716  transforms.push_back(trans);
717 
718  if (params_.publish_tf_)
719  tfb_.sendTransform(transforms);
720 
721  // Calculate covariance from sampled particles to reduce calculation cost on global localization.
722  // Use the number of original particles or at least 10% of full particles.
723  auto cov = pf_->covariance(
724  1.0,
725  std::max(
726  0.1f, static_cast<float>(params_.num_particles_) / pf_->getParticleSize()));
727 
728  geometry_msgs::PoseWithCovarianceStamped pose;
729  pose.header.stamp = header.stamp;
730  pose.header.frame_id = trans.header.frame_id;
731  pose.pose.pose.position.x = e.pos_.x_;
732  pose.pose.pose.position.y = e.pos_.y_;
733  pose.pose.pose.position.z = e.pos_.z_;
734  pose.pose.pose.orientation.x = e.rot_.x_;
735  pose.pose.pose.orientation.y = e.rot_.y_;
736  pose.pose.pose.orientation.z = e.rot_.z_;
737  pose.pose.pose.orientation.w = e.rot_.w_;
738  for (size_t i = 0; i < 36; i++)
739  {
740  pose.pose.covariance[i] = cov[i / 6][i % 6];
741  }
742  pub_pose_.publish(pose);
743 
745  {
746  if (std::sqrt(cov[0][0] + cov[1][1]) > params_.std_warn_thresh_[0] ||
747  std::sqrt(cov[2][2]) > params_.std_warn_thresh_[1] ||
748  std::sqrt(cov[5][5]) > params_.std_warn_thresh_[2])
749  {
750  status_.convergence_status = mcl_3dl_msgs::Status::CONVERGENCE_STATUS_LARGE_STD_VALUE;
751  }
752  }
753 
754  if (status_.convergence_status != mcl_3dl_msgs::Status::CONVERGENCE_STATUS_LARGE_STD_VALUE)
755  {
756  Vec3 fix_axis;
757  const float fix_ang = std::sqrt(cov[3][3] + cov[4][4] + cov[5][5]);
758  const float fix_dist = std::sqrt(cov[0][0] + cov[1][1] + cov[2][2]);
759  ROS_DEBUG("cov: lin %0.3f ang %0.3f", fix_dist, fix_ang);
760  if (fix_dist < params_.fix_dist_ &&
761  fabs(fix_ang) < params_.fix_ang_)
762  {
763  ROS_DEBUG("Localization fixed");
764  status_.convergence_status = mcl_3dl_msgs::Status::CONVERGENCE_STATUS_CONVERGED;
765  }
766  }
767 
768  if (params_.output_pcd_)
769  {
770  pcl::PointCloud<PointType>::Ptr pc_particle(new pcl::PointCloud<PointType>);
771  *pc_particle = *pc_locals["likelihood"];
772  e.transform(*pc_particle);
773  *pc_all_accum_ += *pc_particle;
774  }
775 
776  if ((header.stamp > match_output_last_ + *params_.match_output_interval_ ||
777  header.stamp + ros::Duration(1.0) < match_output_last_) &&
779  {
780  match_output_last_ = header.stamp;
781 
782  pcl::PointCloud<pcl::PointXYZ>::Ptr pc_match(new pcl::PointCloud<pcl::PointXYZ>);
783  pcl::PointCloud<pcl::PointXYZ>::Ptr pc_unmatch(new pcl::PointCloud<pcl::PointXYZ>);
784 
785  pcl::PointCloud<PointType>::Ptr pc_local(new pcl::PointCloud<PointType>);
786  *pc_local = *pc_local_full;
787 
788  e.transform(*pc_local);
789 
790  std::vector<int> id(1);
791  std::vector<float> sqdist(1);
792  const double match_dist_sq = params_.match_output_dist_ * params_.match_output_dist_;
793  for (auto& p : pc_local->points)
794  {
795  if (!kdtree_->radiusSearch(p, params_.unmatch_output_dist_, id, sqdist, 1))
796  {
797  pc_unmatch->points.emplace_back(p.x, p.y, p.z);
798  }
799  else if (sqdist[0] < match_dist_sq)
800  {
801  pc_match->points.emplace_back(p.x, p.y, p.z);
802  }
803  }
805  {
806  sensor_msgs::PointCloud2 pc2;
807  pcl::toROSMsg(*pc_match, pc2);
808  pc2.header.stamp = header.stamp;
809  pc2.header.frame_id = params_.frame_ids_["map"];
810  pub_matched_.publish(pc2);
811  }
813  {
814  sensor_msgs::PointCloud2 pc2;
815  pcl::toROSMsg(*pc_unmatch, pc2);
816  pc2.header.stamp = header.stamp;
817  pc2.header.frame_id = params_.frame_ids_["map"];
818  pub_unmatched_.publish(pc2);
819  }
820  }
821 
823 
824  pf_->resample(State6DOF(
831 
832  std::normal_distribution<float> noise(0.0, 1.0);
833  auto update_noise_func = [this, &noise](State6DOF& s)
834  {
835  s.noise_ll_ = noise(engine_) * params_.odom_err_lin_lin_;
836  s.noise_la_ = noise(engine_) * params_.odom_err_lin_ang_;
837  s.noise_aa_ = noise(engine_) * params_.odom_err_ang_ang_;
838  s.noise_al_ = noise(engine_) * params_.odom_err_ang_lin_;
839  };
840  pf_->predict(update_noise_func);
841 
842  const auto tnow = boost::chrono::high_resolution_clock::now();
843  ROS_DEBUG("MCL (%0.3f sec.)",
844  boost::chrono::duration<float>(tnow - ts).count());
845  const auto err_integ_map = e_max.rot_ * e_max.odom_err_integ_lin_;
846  ROS_DEBUG("odom error integral lin: %0.3f, %0.3f, %0.3f, "
847  "ang: %0.3f, %0.3f, %0.3f, "
848  "pos: %0.3f, %0.3f, %0.3f, "
849  "err on map: %0.3f, %0.3f, %0.3f",
850  e_max.odom_err_integ_lin_.x_,
851  e_max.odom_err_integ_lin_.y_,
852  e_max.odom_err_integ_lin_.z_,
853  e_max.odom_err_integ_ang_.x_,
854  e_max.odom_err_integ_ang_.y_,
855  e_max.odom_err_integ_ang_.z_,
856  e_max.pos_.x_,
857  e_max.pos_.y_,
858  e_max.pos_.z_,
859  err_integ_map.x_,
860  err_integ_map.y_,
861  err_integ_map.z_);
862  ROS_DEBUG("match ratio min: %0.3f, max: %0.3f, pos: %0.3f, %0.3f, %0.3f",
863  match_ratio_min,
864  match_ratio_max,
865  e.pos_.x_,
866  e.pos_.y_,
867  e.pos_.z_);
868  if (match_ratio_max < params_.match_ratio_thresh_)
869  {
870  ROS_WARN_THROTTLE(3.0, "Low match_ratio. Expansion resetting.");
871  pf_->noise(State6DOF(
878  status_.status = mcl_3dl_msgs::Status::EXPANSION_RESETTING;
879  }
880 
881  ros::Time localized_current = ros::Time::now();
882  float dt = (localized_current - localized_last_).toSec();
883  if (dt > 1.0)
884  dt = 1.0;
885  else if (dt < 0.0)
886  dt = 0.0;
888  localized_last_ = localized_current;
889 
890  if (static_cast<int>(pf_->getParticleSize()) > params_.num_particles_)
891  {
892  const int reduced = pf_->getParticleSize() * 0.75;
893  if (reduced > params_.num_particles_)
894  {
895  pf_->resizeParticle(reduced);
896  }
897  else
898  {
899  pf_->resizeParticle(params_.num_particles_);
900  }
901  // wait 99.7% fix (three-sigma)
902  global_localization_fix_cnt_ = 1 + std::ceil(params_.lpf_step_) * 3.0;
903  }
905  {
907  status_.status = mcl_3dl_msgs::Status::GLOBAL_LOCALIZATION;
908  }
909 
910  status_.match_ratio = match_ratio_max;
911  status_.particle_size = pf_->getParticleSize();
913  }
914  void cbLandmark(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& msg)
915  {
917  Eigen::Matrix<double, 6, 6>(
918  msg->pose.covariance.data())
919  .cast<float>());
920  const State6DOF measured(
921  Vec3(msg->pose.pose.position.x,
922  msg->pose.pose.position.y,
923  msg->pose.pose.position.z),
924  Quat(msg->pose.pose.orientation.x,
925  msg->pose.pose.orientation.y,
926  msg->pose.pose.orientation.z,
927  msg->pose.pose.orientation.w));
928  auto measure_func = [this, &measured, &nd](const State6DOF& s) -> float
929  {
930  State6DOF diff = s - measured;
931  const Vec3 rot_rpy = diff.rot_.getRPY();
932  const Eigen::Matrix<float, 6, 1> diff_vec =
933  (Eigen::MatrixXf(6, 1) << diff.pos_.x_,
934  diff.pos_.y_,
935  diff.pos_.z_,
936  rot_rpy.x_,
937  rot_rpy.y_,
938  rot_rpy.z_)
939  .finished();
940 
941  const auto n = nd(diff_vec);
942  return n;
943  };
944  pf_->measure(measure_func);
945 
946  pf_->resample(State6DOF(
953 
955  }
956  void cbImu(const sensor_msgs::Imu::ConstPtr& msg)
957  {
958  const Vec3 acc = f_acc_->in(Vec3(
959  msg->linear_acceleration.x,
960  msg->linear_acceleration.y,
961  msg->linear_acceleration.z));
962 
963  if (!has_imu_)
964  {
965  f_acc_->set(Vec3());
966  imu_last_ = msg->header.stamp;
967  has_imu_ = true;
968  return;
969  }
970 
971  float dt = (msg->header.stamp - imu_last_).toSec();
972  if (dt < 0.0 || dt > 5.0)
973  {
974  ROS_WARN("Detected time jump in imu. Resetting.");
975  has_imu_ = false;
976  return;
977  }
978  else if (dt > 0.05)
979  {
980  Vec3 acc_measure = acc.normalized();
981  try
982  {
983  geometry_msgs::Vector3 in, out;
984  in.x = acc_measure.x_;
985  in.y = acc_measure.y_;
986  in.z = acc_measure.z_;
987  // assuming imu frame is regit on base_link
988  const geometry_msgs::TransformStamped trans = tfbuf_.lookupTransform(
989  params_.frame_ids_["base_link"], msg->header.frame_id, ros::Time(0));
990  tf2::doTransform(in, out, trans);
991  acc_measure = Vec3(out.x, out.y, out.z);
992 
993  imu_quat_.x_ = msg->orientation.x;
994  imu_quat_.y_ = msg->orientation.y;
995  imu_quat_.z_ = msg->orientation.z;
996  imu_quat_.w_ = msg->orientation.w;
997  Vec3 axis;
998  float angle;
999  imu_quat_.getAxisAng(axis, angle);
1000  axis = Quat(trans.transform.rotation.x,
1001  trans.transform.rotation.y,
1002  trans.transform.rotation.z,
1003  trans.transform.rotation.w) *
1004  axis;
1005  imu_quat_.setAxisAng(axis, angle);
1006  }
1007  catch (tf2::TransformException& e)
1008  {
1009  return;
1010  }
1011 
1012  imu_measurement_model_->setAccMeasure(acc_measure);
1013  auto imu_measure_func = [this](const State6DOF& s) -> float
1014  {
1015  return imu_measurement_model_->measure(s);
1016  };
1017  pf_->measure(imu_measure_func);
1018 
1019  imu_last_ = msg->header.stamp;
1020 
1021  if (params_.fake_odom_)
1022  {
1023  nav_msgs::Odometry::Ptr odom(new nav_msgs::Odometry);
1024  odom->header.frame_id = params_.frame_ids_["base_link"];
1025  odom->header.stamp = msg->header.stamp;
1026  odom->pose.pose.orientation.x = imu_quat_.x_;
1027  odom->pose.pose.orientation.y = imu_quat_.y_;
1028  odom->pose.pose.orientation.z = imu_quat_.z_;
1029  odom->pose.pose.orientation.w = imu_quat_.w_;
1030  cbOdom(odom);
1031  }
1032  }
1033  }
1034  bool cbResizeParticle(mcl_3dl_msgs::ResizeParticleRequest& request,
1035  mcl_3dl_msgs::ResizeParticleResponse& response)
1036  {
1037  pf_->resizeParticle(request.size);
1038  publishParticles();
1039  return true;
1040  }
1041  bool cbExpansionReset(std_srvs::TriggerRequest& request,
1042  std_srvs::TriggerResponse& response)
1043  {
1044  pf_->noise(State6DOF(
1051  publishParticles();
1052  return true;
1053  }
1054  bool cbGlobalLocalization(std_srvs::TriggerRequest& request,
1055  std_srvs::TriggerResponse& response)
1056  {
1057  if (!has_map_)
1058  {
1059  response.success = false;
1060  response.message = "No map received.";
1061  return true;
1062  }
1063  pcl::PointCloud<PointType>::Ptr points(new pcl::PointCloud<PointType>);
1064 
1066  ds.setInputCloud(pc_map_);
1067  ds.setLeafSize(
1071  ds.filter(*points);
1072 
1073  pcl::KdTreeFLANN<PointType>::Ptr kdtree(new pcl::KdTreeFLANN<PointType>);
1074  kdtree->setPointRepresentation(
1075  boost::dynamic_pointer_cast<
1076  pcl::PointRepresentation<PointType>,
1077  MyPointRepresentation>(boost::make_shared<MyPointRepresentation>(point_rep_)));
1078  kdtree->setInputCloud(points);
1079 
1080  auto pc_filter = [this, kdtree](const PointType& p)
1081  {
1082  std::vector<int> id(1);
1083  std::vector<float> sqdist(1);
1084  auto p2 = p;
1085  p2.z += 0.01 + params_.global_localization_grid_;
1086 
1087  return kdtree->radiusSearch(
1088  p2, params_.global_localization_grid_, id, sqdist, 1);
1089  };
1090  points->erase(
1091  std::remove_if(points->begin(), points->end(), pc_filter),
1092  points->end());
1093 
1094  const int dir = params_.global_localization_div_yaw_;
1095  pf_->resizeParticle(points->size() * dir);
1096  auto pit = points->begin();
1097 
1098  const float prob = 1.0 / static_cast<float>(points->size());
1099  int cnt = 0;
1100  for (auto& particle : *pf_)
1101  {
1102  assert(pit != points->end());
1103  particle.probability_ = prob;
1104  particle.probability_bias_ = 1.0;
1105  particle.state_ = State6DOF(
1106  Vec3(pit->x, pit->y, pit->z),
1107  (Quat(Vec3(0.0, 0.0, 2.0 * M_PI * cnt / dir)) * imu_quat_).normalized());
1108  if (++cnt >= dir)
1109  {
1110  cnt = 0;
1111  ++pit;
1112  }
1113  }
1114  response.success = true;
1115  response.message = std::to_string(pf_->getParticleSize()) + " particles";
1116  return true;
1117  }
1118 
1120  {
1121  geometry_msgs::PoseArray pa;
1122  if (has_odom_)
1123  pa.header.stamp = odom_last_ + tf_tolerance_base_ + *params_.tf_tolerance_;
1124  else
1125  pa.header.stamp = ros::Time::now() + tf_tolerance_base_ + *params_.tf_tolerance_;
1126  pa.header.frame_id = params_.frame_ids_["map"];
1127  for (size_t i = 0; i < pf_->getParticleSize(); i++)
1128  {
1129  geometry_msgs::Pose pm;
1130  auto p = pf_->getParticle(i);
1131  p.rot_.normalize();
1132  pm.position.x = p.pos_.x_;
1133  pm.position.y = p.pos_.y_;
1134  pm.position.z = p.pos_.z_;
1135  pm.orientation.x = p.rot_.x_;
1136  pm.orientation.y = p.rot_.y_;
1137  pm.orientation.z = p.rot_.z_;
1138  pm.orientation.w = p.rot_.w_;
1139  pa.poses.push_back(pm);
1140  }
1141  pub_particle_.publish(pa);
1142  }
1143 
1144  float getEntropy()
1145  {
1146  float sum = 0.0f;
1147  for (auto& particle : *pf_)
1148  {
1149  sum += particle.probability_;
1150  }
1151 
1152  float entropy = 0.0f;
1153  for (auto& particle : *pf_)
1154  {
1155  if (particle.probability_ / sum > 0.0)
1156  entropy += particle.probability_ / sum * std::log(particle.probability_ / sum);
1157  }
1158 
1159  return -entropy;
1160  }
1161 
1163  {
1164  if (status_.error == mcl_3dl_msgs::Status::ERROR_POINTS_NOT_FOUND)
1165  {
1166  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Valid points does not found.");
1167  }
1168  else if (status_.convergence_status == mcl_3dl_msgs::Status::CONVERGENCE_STATUS_LARGE_STD_VALUE)
1169  {
1170  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Too Large Standard Deviation.");
1171  }
1172  else
1173  {
1174  stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "OK");
1175  }
1176 
1177  stat.add("Map Availability", has_map_ ? "true" : "false");
1178  stat.add("Odometry Availability", has_odom_ ? "true" : "false");
1179  stat.add("IMU Availability", has_imu_ ? "true" : "false");
1180 
1181  status_.entropy = getEntropy();
1183  }
1184 
1185 public:
1187  : pnh_("~")
1188  , tfl_(tfbuf_)
1189  , cnt_measure_(0)
1191  , engine_(seed_gen_())
1192  {
1193  }
1194  bool configure()
1195  {
1197 
1198  if (!params_.load(pnh_))
1199  {
1200  ROS_ERROR("Failed to load parameters");
1201  return false;
1202  }
1203 
1204  if (!params_.fake_odom_)
1205  {
1206  int odom_queue_size;
1207  pnh_.param("odom_queue_size", odom_queue_size, 200);
1209  nh_, "odom",
1210  pnh_, "odom", odom_queue_size, &MCL3dlNode::cbOdom, this);
1211  }
1212  if (!params_.fake_imu_)
1213  {
1214  int imu_queue_size;
1215  pnh_.param("imu_queue_size", imu_queue_size, 200);
1217  nh_, "imu/data",
1218  pnh_, "imu", imu_queue_size, &MCL3dlNode::cbImu, this);
1219  }
1220 
1221  int cloud_queue_size;
1222  pnh_.param("cloud_queue_size", cloud_queue_size, 100);
1224  nh_, "cloud",
1225  pnh_, "cloud", cloud_queue_size, &MCL3dlNode::cbCloud, this);
1227  nh_, "mapcloud",
1228  pnh_, "mapcloud", 1, &MCL3dlNode::cbMapcloud, this);
1230  nh_, "mapcloud_update",
1231  pnh_, "mapcloud_update", 1, &MCL3dlNode::cbMapcloudUpdate, this);
1233  nh_, "initialpose",
1234  pnh_, "initialpose", 1, &MCL3dlNode::cbPosition, this);
1236  nh_, "mcl_measurement",
1237  pnh_, "landmark", 1, &MCL3dlNode::cbLandmark, this);
1238 
1239  pub_pose_ = nh_.advertise<geometry_msgs::PoseWithCovarianceStamped>("amcl_pose", 5, false);
1240  pub_particle_ = pnh_.advertise<geometry_msgs::PoseArray>("particles", 1, true);
1241  pub_mapcloud_ = pnh_.advertise<sensor_msgs::PointCloud2>("updated_map", 1, true);
1242  pub_debug_marker_ = pnh_.advertise<visualization_msgs::MarkerArray>("debug_marker", 1, true);
1243  pub_status_ = pnh_.advertise<mcl_3dl_msgs::Status>("status", 1, true);
1244  pub_matched_ = pnh_.advertise<sensor_msgs::PointCloud2>("matched", 2, true);
1245  pub_unmatched_ = pnh_.advertise<sensor_msgs::PointCloud2>("unmatched", 2, true);
1246 
1248  nh_, "resize_mcl_particle",
1249  pnh_, "resize_particle", &MCL3dlNode::cbResizeParticle, this);
1251  nh_, "global_localization",
1252  pnh_, "global_localization", &MCL3dlNode::cbGlobalLocalization, this);
1254  nh_, "expansion_resetting",
1255  pnh_, "expansion_resetting", &MCL3dlNode::cbExpansionReset, this);
1256 
1257  point_rep_.setRescaleValues(params_.dist_weight_.data());
1258 
1259  pf_.reset(new pf::ParticleFilter<State6DOF,
1260  float,
1262  std::default_random_engine>(params_.num_particles_));
1264 
1265  f_pos_.reset(new FilterVec3(
1268  Vec3()));
1269  f_ang_.reset(new FilterVec3(
1272  Vec3(), true));
1273  f_acc_.reset(new FilterVec3(
1276  Vec3()));
1277 
1278  if (params_.accum_cloud_ == 0)
1280  else
1281  accum_.reset(
1283 
1284  imu_quat_ = Quat(0.0, 0.0, 0.0, 1.0);
1285 
1286  has_odom_ = has_map_ = has_imu_ = false;
1287  localize_rate_.reset(new Filter(Filter::FILTER_LPF, 5.0, 0.0));
1288 
1289  lidar_measurements_["likelihood"] =
1292  lidar_measurements_["beam"] =
1300 
1301  float max_search_radius = 0;
1302  for (auto& lm : lidar_measurements_)
1303  {
1304  lm.second->loadConfig(pnh_, lm.first);
1305  max_search_radius = std::max(max_search_radius, lm.second->getMaxSearchRange());
1306 
1308  {
1309  auto sampler = std::make_shared<PointCloudSamplerWithNormal<PointType>>();
1310  sampler->loadConfig(pnh_);
1311  lm.second->setRandomSampler(sampler);
1312  }
1313  else
1314  {
1315  auto sampler = std::make_shared<PointCloudUniformSampler<PointType>>();
1316  lm.second->setRandomSampler(sampler);
1317  }
1318  }
1319 
1320  ROS_DEBUG("max_search_radius: %0.3f", max_search_radius);
1321  kdtree_.reset(new ChunkedKdtree<PointType>(params_.map_chunk_, max_search_radius));
1322  kdtree_->setEpsilon(params_.map_grid_min_ / 16);
1323  kdtree_->setPointRepresentation(
1324  boost::dynamic_pointer_cast<
1325  pcl::PointRepresentation<PointType>,
1326  MyPointRepresentation>(boost::make_shared<MyPointRepresentation>(point_rep_)));
1327 
1331 
1332  diag_updater_.setHardwareID("none");
1333  diag_updater_.add("Status", this, &MCL3dlNode::diagnoseStatus);
1334 
1335  return true;
1336  }
1338  {
1340  {
1341  std::cerr << "mcl_3dl: saving pcd file.";
1342  std::cerr << " (" << pc_all_accum_->points.size() << " points)" << std::endl;
1343  pcl::io::savePCDFileBinary("mcl_3dl.pcd", *pc_all_accum_);
1344  }
1345  }
1346 
1348  {
1349  if (has_map_)
1350  {
1351  const auto ts = boost::chrono::high_resolution_clock::now();
1352  if (pc_update_)
1353  {
1354  if (!pc_map2_)
1355  pc_map2_.reset(new pcl::PointCloud<PointType>);
1356  *pc_map2_ = *pc_map_ + *pc_update_;
1357  pc_update_.reset();
1359  }
1360  else
1361  {
1362  if (pc_map2_)
1363  return;
1364  pc_map2_ = pc_map_;
1365  }
1366  kdtree_->setInputCloud(pc_map2_);
1367 
1368  sensor_msgs::PointCloud2 out;
1369  pcl::toROSMsg(*pc_map2_, out);
1370  pub_mapcloud_.publish(out);
1371  const auto tnow = boost::chrono::high_resolution_clock::now();
1372  ROS_DEBUG("Map update (%0.3f sec.)",
1373  boost::chrono::duration<float>(tnow - ts).count());
1374  }
1375  }
1376 
1377 protected:
1380 
1399 
1403 
1404  std::shared_ptr<FilterVec3> f_pos_;
1405  std::shared_ptr<FilterVec3> f_ang_;
1406  std::shared_ptr<FilterVec3> f_acc_;
1407  std::shared_ptr<Filter> localize_rate_;
1410 
1412 
1415  bool has_map_;
1417  bool has_imu_;
1426  mcl_3dl_msgs::Status status_;
1427 
1429 
1430  pcl::PointCloud<PointType>::Ptr pc_map_;
1431  pcl::PointCloud<PointType>::Ptr pc_map2_;
1432  pcl::PointCloud<PointType>::Ptr pc_update_;
1433  pcl::PointCloud<PointType>::Ptr pc_all_accum_;
1435 
1437  pcl::PointCloud<PointType>::Ptr pc_local_accum_;
1438  std::vector<std_msgs::Header> pc_accum_header_;
1439 
1440  std::map<
1441  std::string,
1445 
1446  std::random_device seed_gen_;
1447  std::default_random_engine engine_;
1448 };
1449 } // namespace mcl_3dl
1450 
1451 int main(int argc, char* argv[])
1452 {
1453  ros::init(argc, argv, "mcl_3dl");
1454 
1455  mcl_3dl::MCL3dlNode mcl;
1456  if (!mcl.configure())
1457  {
1458  return 1;
1459  }
1460  ros::spin();
1461 
1462  return 0;
1463 }
virtual void filter(const PointCloud2::ConstPtr &input, const IndicesPtr &indices, PointCloud2 &output)
float z_
Definition: vec3.h:42
std::default_random_engine engine_
Definition: mcl_3dl.cpp:1447
ros::Time match_output_last_
Definition: mcl_3dl.cpp:1413
void setAxisAng(const Vec3 &axis, const float &ang)
Definition: quat.h:216
ros::NodeHandle pnh_
Definition: mcl_3dl.cpp:1379
ros::NodeHandle nh_
Definition: mcl_3dl.cpp:1378
State6DOF state_prev_
Definition: mcl_3dl.cpp:1420
std::shared_ptr< pf::ParticleFilter< State6DOF, float, ParticleWeightedMeanQuat, std::default_random_engine > > pf_
Definition: mcl_3dl.cpp:108
bool load(ros::NodeHandle &nh)
Definition: parameters.cpp:43
double update_downsample_x_
Definition: parameters.h:53
void cbOdom(const nav_msgs::Odometry::ConstPtr &msg)
Definition: mcl_3dl.cpp:216
double expansion_var_z_
Definition: parameters.h:71
#define ROS_WARN_THROTTLE(rate,...)
double resample_var_roll_
Definition: parameters.h:66
void publish(const boost::shared_ptr< M > &message) const
f
void summary(unsigned char lvl, const std::string s)
double odom_err_lin_ang_
Definition: parameters.h:81
float y_
Definition: quat.h:44
ros::Publisher pub_debug_marker_
Definition: mcl_3dl.cpp:1393
double unmatch_output_dist_
Definition: parameters.h:90
void setHardwareID(const std::string &hwid)
ros::Subscriber sub_mapcloud_
Definition: mcl_3dl.cpp:1382
XmlRpcServer s
std::shared_ptr< ChunkedKdtree > Ptr
mcl_3dl::Vec3 pos_
Definition: state_6dof.h:52
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
IMETHOD Vector diff(const Vector &p_w_a, const Vector &p_w_b, double dt=1)
double match_output_dist_
Definition: parameters.h:89
void add(const std::string &name, TaskFunction f)
std::random_device seed_gen_
Definition: mcl_3dl.cpp:1446
double update_downsample_z_
Definition: parameters.h:55
tf2_ros::TransformBroadcaster tfb_
Definition: mcl_3dl.cpp:1402
double expansion_var_roll_
Definition: parameters.h:72
ros::ServiceServer srv_global_localization_
Definition: mcl_3dl.cpp:1397
ros::Subscriber sub_cloud_
Definition: mcl_3dl.cpp:1381
void doTransform(const T &data_in, T &data_out, const geometry_msgs::TransformStamped &transform)
#define ROS_WARN(...)
ros::Subscriber sub_odom_
Definition: mcl_3dl.cpp:1384
double map_downsample_x_
Definition: parameters.h:50
ros::Publisher pub_status_
Definition: mcl_3dl.cpp:1394
pcl::PointCloud< PointType >::Ptr pc_update_
Definition: mcl_3dl.cpp:1432
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
ROSCPP_DECL void spin(Spinner &spinner)
std::shared_ptr< FilterVec3 > f_ang_
Definition: mcl_3dl.cpp:1405
std::map< std::string, LidarMeasurementModelBase::Ptr > lidar_measurements_
Definition: mcl_3dl.cpp:1442
ChunkedKdtree< PointType >::Ptr kdtree_
Definition: mcl_3dl.cpp:1434
pcl::PointCloud< PointType >::Ptr pc_map2_
Definition: mcl_3dl.cpp:1431
std::shared_ptr< ros::Duration > map_update_interval_
Definition: parameters.h:84
TFSIMD_FORCE_INLINE Vector3 normalized() const
ros::ServiceServer advertiseService(ros::NodeHandle &nh_new, const std::string &service_new, ros::NodeHandle &nh_old, const std::string &service_old, bool(T::*srv_func)(MReq &, MRes &), T *obj)
tf2_ros::Buffer tfbuf_
Definition: mcl_3dl.cpp:1400
ros::Publisher pub_particle_
Definition: mcl_3dl.cpp:1388
ros::Subscriber sub_mapcloud_update_
Definition: mcl_3dl.cpp:1383
Parameters params_
Definition: mcl_3dl.cpp:1411
std::shared_ptr< Filter > localize_rate_
Definition: mcl_3dl.cpp:1407
double global_localization_grid_
Definition: parameters.h:58
double odom_err_ang_ang_
Definition: parameters.h:83
void cbMapUpdateTimer(const ros::TimerEvent &event)
Definition: mcl_3dl.cpp:1347
double expansion_var_y_
Definition: parameters.h:70
constexpr Vec3 getRPY() const
Definition: quat.h:191
bool accumCloud(const sensor_msgs::PointCloud2::ConstPtr &msg)
Definition: mcl_3dl.cpp:290
void diagnoseStatus(diagnostic_updater::DiagnosticStatusWrapper &stat)
Definition: mcl_3dl.cpp:1162
bool fromROSMsg(const sensor_msgs::PointCloud2 &msg, pcl::PointCloud< PointT > &pc)
ros::ServiceServer srv_expansion_reset_
Definition: mcl_3dl.cpp:1398
ros::Time localized_last_
Definition: mcl_3dl.cpp:1408
void cbLandmark(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr &msg)
Definition: mcl_3dl.cpp:914
State6DOF initial_pose_std_
Definition: parameters.h:109
double map_downsample_y_
Definition: parameters.h:51
#define ROS_INFO(...)
CloudAccumulationLogicBase::Ptr accum_
Definition: mcl_3dl.cpp:1436
float w_
Definition: quat.h:46
bool param(const std::string &param_name, T &param_val, const T &default_val) const
float y_
Definition: vec3.h:41
constexpr Quat inv() const
Definition: quat.h:187
ros::Publisher pub_unmatched_
Definition: mcl_3dl.cpp:1392
void getAxisAng(Vec3 &axis, float &ang) const
Definition: quat.h:226
ros::Subscriber sub_landmark_
Definition: mcl_3dl.cpp:1387
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
MotionPredictionModelBase::Ptr motion_prediction_model_
Definition: mcl_3dl.cpp:1444
ros::Publisher pub_matched_
Definition: mcl_3dl.cpp:1391
std::shared_ptr< LidarMeasurementModelBase > Ptr
std::array< float, 3 > std_warn_thresh_
Definition: parameters.h:107
pcl::PointCloud< PointType >::Ptr pc_local_accum_
Definition: mcl_3dl.cpp:1437
bool cbExpansionReset(std_srvs::TriggerRequest &request, std_srvs::TriggerResponse &response)
Definition: mcl_3dl.cpp:1041
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
double odom_err_integ_lin_tc_
Definition: parameters.h:94
double resample_var_yaw_
Definition: parameters.h:68
tf2_ros::TransformListener tfl_
Definition: mcl_3dl.cpp:1401
double odom_err_lin_lin_
Definition: parameters.h:80
void sendTransform(const geometry_msgs::TransformStamped &transform)
virtual void copyToFloatArray(const PointType &p, float *out) const
Definition: mcl_3dl.cpp:120
double expansion_var_yaw_
Definition: parameters.h:74
mcl_3dl_msgs::Status status_
Definition: mcl_3dl.cpp:1426
std::shared_ptr< FilterVec3 > f_acc_
Definition: mcl_3dl.cpp:1406
State6DOF odom_prev_
Definition: mcl_3dl.cpp:1419
std::shared_ptr< FilterVec3 > f_pos_
Definition: mcl_3dl.cpp:1404
mcl_3dl::Quat rot_
Definition: state_6dof.h:53
void cbMapcloud(const sensor_msgs::PointCloud2::ConstPtr &msg)
Definition: mcl_3dl.cpp:127
B toMsg(const A &a)
double map_downsample_z_
Definition: parameters.h:52
ros::Subscriber sub_imu_
Definition: mcl_3dl.cpp:1385
ImuMeasurementModelBase::Ptr imu_measurement_model_
Definition: mcl_3dl.cpp:1443
double resample_var_z_
Definition: parameters.h:65
double odom_err_ang_lin_
Definition: parameters.h:82
void checkCompatMode()
Definition: compatibility.h:62
std::shared_ptr< ImuMeasurementModelBase > Ptr
int main(int argc, char *argv[])
Definition: mcl_3dl.cpp:1451
float x_
Definition: vec3.h:40
ros::Subscriber subscribe(ros::NodeHandle &nh_new, const std::string &topic_new, ros::NodeHandle &nh_old, const std::string &topic_old, uint32_t queue_size, void(T::*fp)(M) const, T *obj, const ros::TransportHints &transport_hints=ros::TransportHints())
uint32_t getNumSubscribers() const
Vec3 normalized() const
Definition: vec3.h:157
std::map< std::string, std::string > frame_ids_
Definition: parameters.h:106
double resample_var_y_
Definition: parameters.h:64
void setRPY(const Vec3 &rpy)
Definition: quat.h:202
void setParticleStatistics(const State6DOF &mean, const std::vector< State6DOF > &covariances)
ros::Time odom_last_
Definition: mcl_3dl.cpp:1414
double expansion_var_x_
Definition: parameters.h:69
std::vector< std_msgs::Header > pc_accum_header_
Definition: mcl_3dl.cpp:1438
MyPointRepresentation point_rep_
Definition: mcl_3dl.cpp:1428
void toROSMsg(const sensor_msgs::PointCloud2 &cloud, sensor_msgs::Image &image)
static Time now()
double expansion_var_pitch_
Definition: parameters.h:73
double odom_err_integ_ang_tc_
Definition: parameters.h:96
ros::Publisher pub_mapcloud_
Definition: mcl_3dl.cpp:1389
double match_ratio_thresh_
Definition: parameters.h:75
ros::Timer map_update_timer_
Definition: mcl_3dl.cpp:1395
void cbPosition(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr &msg)
Definition: mcl_3dl.cpp:171
ros::Time imu_last_
Definition: mcl_3dl.cpp:1421
ros::Duration tf_tolerance_base_
Definition: mcl_3dl.cpp:1409
void cbImu(const sensor_msgs::Imu::ConstPtr &msg)
Definition: mcl_3dl.cpp:956
void add(const std::string &key, const T &val)
diagnostic_updater::Updater diag_updater_
Definition: mcl_3dl.cpp:1425
bool use_random_sampler_with_normal_
Definition: parameters.h:110
pcl::PointCloud< PointType >::Ptr pc_map_
Definition: mcl_3dl.cpp:1430
std::shared_ptr< ros::Duration > match_output_interval_
Definition: parameters.h:98
bool cbResizeParticle(mcl_3dl_msgs::ResizeParticleRequest &request, mcl_3dl_msgs::ResizeParticleResponse &response)
Definition: mcl_3dl.cpp:1034
#define ROS_ERROR(...)
std::shared_ptr< ros::Duration > tf_tolerance_
Definition: parameters.h:99
ros::Publisher pub_pose_
Definition: mcl_3dl.cpp:1390
double odom_err_integ_lin_sigma_
Definition: parameters.h:95
float z_
Definition: quat.h:45
void cbMapcloudUpdate(const sensor_msgs::PointCloud2::ConstPtr &msg)
Definition: mcl_3dl.cpp:157
State6DOF initial_pose_
Definition: parameters.h:108
double update_downsample_y_
Definition: parameters.h:54
int global_localization_div_yaw_
Definition: parameters.h:59
ros::ServiceServer srv_particle_size_
Definition: mcl_3dl.cpp:1396
std::shared_ptr< CloudAccumulationLogicBase > Ptr
Definition: cloud_accum.h:45
size_t global_localization_fix_cnt_
Definition: mcl_3dl.cpp:1424
double odom_err_integ_ang_sigma_
Definition: parameters.h:97
double resample_var_x_
Definition: parameters.h:63
ros::Subscriber sub_position_
Definition: mcl_3dl.cpp:1386
void toPCL(const ros::Time &stamp, pcl::uint64_t &pcl_stamp)
pcl::PointCloud< PointType >::Ptr pc_all_accum_
Definition: mcl_3dl.cpp:1433
float x_
Definition: quat.h:43
bool cbGlobalLocalization(std_srvs::TriggerRequest &request, std_srvs::TriggerResponse &response)
Definition: mcl_3dl.cpp:1054
double resample_var_pitch_
Definition: parameters.h:67
#define ROS_DEBUG(...)
std::shared_ptr< MotionPredictionModelBase > Ptr
void cbCloud(const sensor_msgs::PointCloud2::ConstPtr &msg)
Definition: mcl_3dl.cpp:264
std::array< float, 4 > dist_weight_
Definition: parameters.h:102


mcl_3dl
Author(s): Atsushi Watanabe
autogenerated on Wed May 12 2021 02:16:29