odometer ok
							parent
							
								
									f47143f01b
								
							
						
					
					
						commit
						42eaae9acd
					
				|  | @ -12,7 +12,7 @@ struct Color { | |||
| #define RED Color(255, 0, 0) | ||||
| #define GREEN Color(0, 255, 0) | ||||
| #define BLUE Color(0, 0, 255) | ||||
| #define DRAK_GRAY Color(60, 60, 60) | ||||
| #define DRAK_GRAY Color(50, 50, 50) | ||||
| #define GRAY Color(128, 128, 128) | ||||
| #define CYAN Color(0, 255, 255) | ||||
| #define PURPLE Color(160, 32, 240) | ||||
|  |  | |||
|  | @ -5,15 +5,15 @@ | |||
| 
 | ||||
| namespace common { | ||||
| 
 | ||||
| // Distance squred of a point to origin
 | ||||
| // Distance squared of a point to origin
 | ||||
| template <typename PT> | ||||
| inline double DistanceSqure(const PT &pt) { | ||||
| inline double DistanceSquare(const PT &pt) { | ||||
|   return pt.x * pt.x + pt.y * pt.y + pt.z * pt.z; | ||||
| } | ||||
| 
 | ||||
| // Distance squred of two points
 | ||||
| // Distance squared of two points
 | ||||
| template <typename PT> | ||||
| inline double DistanceSqure(const PT &pt1, const PT &pt2) { | ||||
| inline double DistanceSquare(const PT &pt1, const PT &pt2) { | ||||
|   return (pt1.x - pt2.x) * (pt1.x - pt2.x) + (pt1.y - pt2.y) * (pt1.y - pt2.y) + | ||||
|          (pt1.z - pt2.z) * (pt1.z - pt2.z); | ||||
| } | ||||
|  | @ -21,13 +21,13 @@ inline double DistanceSqure(const PT &pt1, const PT &pt2) { | |||
| // Distance of a point to origin
 | ||||
| template <typename PT> | ||||
| inline double Distance(const PT &pt) { | ||||
|   return std::sqrt(DistanceSqure(pt)); | ||||
|   return std::sqrt(DistanceSquare(pt)); | ||||
| } | ||||
| 
 | ||||
| // Distance squred of two points
 | ||||
| template <typename PT> | ||||
| inline double Distance(const PT &pt1, const PT &pt2) { | ||||
|   return std::sqrt(DistanceSqure(pt1, pt2)); | ||||
|   return std::sqrt(DistanceSquare(pt1, pt2)); | ||||
| } | ||||
| 
 | ||||
| // Check whether is a finite point: neither infinite nor nan
 | ||||
|  |  | |||
|  | @ -1,16 +1,3 @@ | |||
| #include "lidar_visualizer_utils.h" | ||||
| 
 | ||||
| namespace common { | ||||
| 
 | ||||
| void AddLine(const pcl::PointXYZ &pt1, const pcl::PointXYZ &pt2, | ||||
|              const Color &color, const std::string &id, | ||||
|              PCLVisualizer *const viewer) { | ||||
|   viewer->addLine(pt1, pt2, color.r, color.g, color.b, id); | ||||
| } | ||||
| 
 | ||||
| void AddSphere(const pcl::PointXYZ ¢er, double radius, const Color &color, | ||||
|                const std::string &id, PCLVisualizer *const viewer) { | ||||
|   viewer->addSphere(center, radius, color.r, color.g, color.b, id); | ||||
| } | ||||
| 
 | ||||
| }  // namespace common
 | ||||
| namespace common {}  // namespace common
 | ||||
|  | @ -37,11 +37,16 @@ void AddPointCloud(const typename pcl::PointCloud<PT>::ConstPtr &cloud, | |||
|       pcl::visualization::PCL_VISUALIZER_POINT_SIZE, point_size, id); | ||||
| } | ||||
| 
 | ||||
| void AddLine(const pcl::PointXYZ &pt1, const pcl::PointXYZ &pt2, | ||||
|              const Color &color, const std::string &id, | ||||
|              PCLVisualizer *const viewer); | ||||
| template <typename PT> | ||||
| void AddLine(const PT &pt1, const PT &pt2, const Color &color, | ||||
|              const std::string &id, PCLVisualizer *const viewer) { | ||||
|   viewer->addLine(pt1, pt2, color.r, color.g, color.b, id); | ||||
| } | ||||
| 
 | ||||
| void AddSphere(const pcl::PointXYZ ¢er, double radius, const Color &color, | ||||
|                const std::string &id, PCLVisualizer *const viewer); | ||||
| template <typename PT> | ||||
| void AddSphere(const PT ¢er, double radius, const Color &color, | ||||
|                const std::string &id, PCLVisualizer *const viewer) { | ||||
|   viewer->addSphere(center, radius, color.r, color.g, color.b, id); | ||||
| } | ||||
| 
 | ||||
| }  // namespace common
 | ||||
							
								
								
									
										4
									
								
								main.cc
								
								
								
								
							
							
						
						
									
										4
									
								
								main.cc
								
								
								
								
							|  | @ -48,6 +48,8 @@ void PointCloudHandler(const sensor_msgs::PointCloud2ConstPtr &msg, | |||
|   PointCloudPtr cloud(new PointCloud); | ||||
|   pcl::fromROSMsg(*msg, *cloud); | ||||
|   double timestamp = msg->header.stamp.toSec(); | ||||
|   AUSER << "Timestamp: " << FMT_TIMESTAMP(timestamp); | ||||
|   static size_t frame_id = 0; | ||||
|   AINFO << "Ohmyloam: frame_id = " << ++frame_id | ||||
|         << ", timestamp = " << FMT_TIMESTAMP(timestamp); | ||||
|   slam->Run(timestamp, cloud); | ||||
| } | ||||
|  | @ -9,22 +9,25 @@ extractor_config: | |||
|   vis: false | ||||
|   verbose: false | ||||
|   min_point_num: 66 | ||||
|   sharp_corner_point_num: 2 | ||||
|   corner_point_num: 10 | ||||
|   flat_surf_point_num: 4 | ||||
|   surf_point_num: 20 | ||||
|   scan_seg_num: 36 | ||||
|   sharp_corner_point_num: 1 | ||||
|   corner_point_num: 4 | ||||
|   flat_surf_point_num: 1 | ||||
|   corner_point_curvature_th: 0.5 | ||||
|   surf_point_curvature_th: 0.5 | ||||
|   neighbor_point_dist_sq_th: 0.1 | ||||
|   downsample_voxel_size: 0.6 | ||||
| 
 | ||||
| # configs for odometer | ||||
| odometer_config: | ||||
|   vis: true | ||||
|   verbose: true | ||||
|   verbose: false | ||||
|   nearby_scan_num: 1 | ||||
|   min_correspondence_num: 10 | ||||
|   icp_iter_num: 2 | ||||
|   solve_iter_num: 4 | ||||
|   corn_match_dist_sq_th: 1.0 | ||||
|   surf_match_dist_sq_th: 1.0 | ||||
|   surf_match_dist_sq_th: 16.0 | ||||
| 
 | ||||
| # configs for mapper | ||||
| mapper_config: | ||||
|  |  | |||
|  | @ -7,7 +7,6 @@ | |||
| namespace oh_my_loam { | ||||
| 
 | ||||
| namespace { | ||||
| const int kScanSegNum = 6; | ||||
| const double kTwoPi = 2 * M_PI; | ||||
| const double kEps = 1e-6; | ||||
| }  // namespace
 | ||||
|  | @ -58,18 +57,23 @@ void Extractor::Process(double timestamp, | |||
| void Extractor::SplitScan(const common::PointCloud &cloud, | ||||
|                           std::vector<TCTPointCloud> *const scans) const { | ||||
|   scans->resize(num_scans_); | ||||
|   double yaw_start = -std::atan2(cloud.points[0].y, cloud.points[0].x); | ||||
|   auto it = std::min_element(cloud.begin(), cloud.begin() + num_scans(), | ||||
|                              [](const auto &p1, const auto &p2) { | ||||
|                                return -std::atan2(p1.y, p1.x) < | ||||
|                                       -std::atan2(p2.y, p2.x); | ||||
|                              }); | ||||
|   double yaw_start = -std::atan2(it->y, it->x); | ||||
|   bool half_passed = false; | ||||
|   for (const auto &pt : cloud) { | ||||
|     int scan_id = GetScanID(pt); | ||||
|     if (scan_id >= num_scans_ || scan_id < 0) continue; | ||||
|     double yaw = -std::atan2(pt.y, pt.x); | ||||
|     double yaw_diff = common::NormalizeAngle(yaw - yaw_start); | ||||
|     if (yaw_diff > 0) { | ||||
|       if (half_passed) yaw_start += kTwoPi; | ||||
|     if (yaw_diff >= -kEps) { | ||||
|       if (half_passed) yaw_diff += kTwoPi; | ||||
|     } else { | ||||
|       half_passed = true; | ||||
|       yaw_start += kTwoPi; | ||||
|       yaw_diff += kTwoPi; | ||||
|     } | ||||
|     double time = std::min(yaw_diff / kTwoPi, 1 - kEps) + scan_id; | ||||
|     scans->at(scan_id).push_back( | ||||
|  | @ -78,7 +82,8 @@ void Extractor::SplitScan(const common::PointCloud &cloud, | |||
| } | ||||
| 
 | ||||
| void Extractor::ComputeCurvature(TCTPointCloud *const scan) const { | ||||
|   if (scan->size() <= 10 + kScanSegNum) return; | ||||
|   size_t seg_num = config_["scan_seg_num"].as<int>(); | ||||
|   if (scan->size() <= 10 + seg_num) return; | ||||
|   auto &pts = scan->points; | ||||
|   for (size_t i = 5; i < pts.size() - 5; ++i) { | ||||
|     float dx = pts[i - 5].x + pts[i - 4].x + pts[i - 3].x + pts[i - 2].x + | ||||
|  | @ -99,19 +104,21 @@ void Extractor::ComputeCurvature(TCTPointCloud *const scan) const { | |||
| 
 | ||||
| void Extractor::AssignType(TCTPointCloud *const scan) const { | ||||
|   int pt_num = scan->size(); | ||||
|   if (pt_num < kScanSegNum) return; | ||||
|   int seg_pt_num = (pt_num - 1) / kScanSegNum + 1; | ||||
|   int seg_num = config_["scan_seg_num"].as<int>(); | ||||
|   if (pt_num < seg_num) return; | ||||
|   int seg_pt_num = (pt_num - 1) / seg_num + 1; | ||||
|   AERROR << pt_num << " " << seg_num << " " << seg_pt_num; | ||||
| 
 | ||||
|   std::vector<bool> picked(pt_num, false); | ||||
|   std::vector<int> indices = common::Range(pt_num); | ||||
|   int sharp_corner_point_num = config_["sharp_corner_point_num"].as<int>(); | ||||
|   int corner_point_num = config_["corner_point_num"].as<int>(); | ||||
|   int flat_surf_point_num = config_["flat_surf_point_num"].as<int>(); | ||||
|   int surf_point_num = config_["surf_point_num"].as<int>(); | ||||
|   float corner_point_curvature_th = | ||||
|       config_["corner_point_curvature_th"].as<float>(); | ||||
|   float surf_point_curvature_th = | ||||
|       config_["surf_point_curvature_th"].as<float>(); | ||||
|   for (int seg = 0; seg < kScanSegNum; ++seg) { | ||||
|   for (int seg = 0; seg < seg_num; ++seg) { | ||||
|     int b = seg * seg_pt_num; | ||||
|     int e = std::min((seg + 1) * seg_pt_num, pt_num); | ||||
|     // sort by curvature for each segment: large -> small
 | ||||
|  | @ -144,8 +151,6 @@ void Extractor::AssignType(TCTPointCloud *const scan) const { | |||
|         ++surf_point_picked_num; | ||||
|         if (surf_point_picked_num <= flat_surf_point_num) { | ||||
|           scan->at(ix).type = PointType::FLAT_SURF; | ||||
|         } else if (surf_point_picked_num <= surf_point_num) { | ||||
|           scan->at(ix).type = PointType::SURF; | ||||
|         } else { | ||||
|           break; | ||||
|         } | ||||
|  | @ -174,10 +179,15 @@ void Extractor::GenerateFeature(const TCTPointCloud &scan, | |||
|         feature->cloud_corner->push_back(point); | ||||
|         break; | ||||
|       default: | ||||
|         // feature->cloud_surf->push_back(point);
 | ||||
|         feature->cloud_surf->push_back(point); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|   TPointCloudPtr dowm_sampled(new TPointCloud); | ||||
|   common::VoxelDownSample<TPoint>( | ||||
|       feature->cloud_surf, dowm_sampled.get(), | ||||
|       config_["downsample_voxel_size"].as<double>()); | ||||
|   feature->cloud_surf = dowm_sampled; | ||||
| } | ||||
| 
 | ||||
| void Extractor::Visualize(const common::PointCloudConstPtr &cloud, | ||||
|  | @ -193,12 +203,12 @@ void Extractor::Visualize(const common::PointCloudConstPtr &cloud, | |||
| void Extractor::UpdateNeighborsPicked(const TCTPointCloud &scan, size_t ix, | ||||
|                                       std::vector<bool> *const picked) const { | ||||
|   auto dist_sq = [&](size_t i, size_t j) -> double { | ||||
|     return common::DistanceSqure<TCTPoint>(scan[i], scan[j]); | ||||
|     return common::DistanceSquare<TCTPoint>(scan[i], scan[j]); | ||||
|   }; | ||||
|   double neighbor_point_dist_sq_th = | ||||
|       config_["neighbor_point_dist_sq_th"].as<float>(); | ||||
|   for (size_t i = 1; i <= 5; ++i) { | ||||
|     if (ix < i) break; | ||||
|     if (ix - i < 0) break; | ||||
|     if (picked->at(ix - i)) continue; | ||||
|     if (dist_sq(ix - i, ix - i + 1) > neighbor_point_dist_sq_th) break; | ||||
|     picked->at(ix - i) = true; | ||||
|  |  | |||
|  | @ -10,11 +10,6 @@ | |||
| 
 | ||||
| namespace oh_my_loam { | ||||
| 
 | ||||
| namespace { | ||||
| int kNearScanN = 2; | ||||
| size_t kMinMatchNum = 10; | ||||
| }  // namespace
 | ||||
| 
 | ||||
| bool Odometer::Init() { | ||||
|   const auto &config = common::YAMLConfig::Instance()->config(); | ||||
|   config_ = config["odometer_config"]; | ||||
|  | @ -33,17 +28,19 @@ void Odometer::Process(double timestamp, const std::vector<Feature> &features, | |||
|     pose_curr2last_.SetIdentity(); | ||||
|     pose_curr2world_.SetIdentity(); | ||||
|     pose_out->SetIdentity(); | ||||
|     AWARN << "Odometer initialized...."; | ||||
|     AINFO << "Odometer initialized...."; | ||||
|     return; | ||||
|   } | ||||
|   BLOCK_TIMER_START; | ||||
|   AINFO << "Pose before: " << pose_curr2world_.ToString(); | ||||
|   std::vector<PointLinePair> pl_pairs; | ||||
|   const auto &cloud_corn = kdtree_corn_.getInputCloud(); | ||||
|   const auto &cloud_surf = kdtree_surf_.getInputCloud(); | ||||
|   AINFO_IF(verbose_) << "Points to be matched: " << cloud_corn->size() << " + " | ||||
|                      << cloud_surf->size(); | ||||
|   std::vector<PointPlanePair> pp_pairs; | ||||
|   AINFO_IF(verbose_) << "Points to be matched: " | ||||
|                      << kdtree_corn_.getInputCloud()->size() << " + " | ||||
|                      << kdtree_surf_.getInputCloud()->size(); | ||||
|   std::vector<PointLinePair> pl_pairs; | ||||
|   for (int i = 0; i < config_["icp_iter_num"].as<int>(); ++i) { | ||||
|     std::vector<PointLinePair>().swap(pl_pairs); | ||||
|     std::vector<PointPlanePair>().swap(pp_pairs); | ||||
|     for (const auto &feature : features) { | ||||
|       MatchCorn(*feature.cloud_sharp_corner, &pl_pairs); | ||||
|       MatchSurf(*feature.cloud_flat_surf, &pp_pairs); | ||||
|  | @ -51,7 +48,8 @@ void Odometer::Process(double timestamp, const std::vector<Feature> &features, | |||
|     AINFO_IF(verbose_) << "Iter " << i | ||||
|                        << ": matched num: point2line = " << pl_pairs.size() | ||||
|                        << ", point2plane = " << pp_pairs.size(); | ||||
|     if (pl_pairs.size() + pp_pairs.size() < kMinMatchNum) { | ||||
|     if (pl_pairs.size() + pp_pairs.size() < | ||||
|         config_["min_correspondence_num"].as<size_t>()) { | ||||
|       AWARN << "Too less correspondence: " << pl_pairs.size() << " + " | ||||
|             << pp_pairs.size(); | ||||
|       continue; | ||||
|  | @ -63,17 +61,19 @@ void Odometer::Process(double timestamp, const std::vector<Feature> &features, | |||
|     for (const auto &pair : pp_pairs) { | ||||
|       solver.AddPointPlanePair(pair, GetTime(pair.pt)); | ||||
|     } | ||||
|     solver.Solve(config_["solve_iter_num"].as<int>(), verbose_, | ||||
|                  &pose_curr2last_); | ||||
|     AINFO << "Odometer::ICP: iter_" << i << ": " << BLOCK_TIMER_STOP_FMT; | ||||
|     bool is_converge = solver.Solve(config_["solve_iter_num"].as<int>(), | ||||
|                                     verbose_, &pose_curr2last_); | ||||
|     AWARN_IF(!is_converge) << "Solve: no_convergence"; | ||||
|     AINFO_IF(verbose_) << "Odometer::ICP: iter_" << i << ": " | ||||
|                        << BLOCK_TIMER_STOP_FMT; | ||||
|   } | ||||
|   pose_curr2world_ = pose_curr2world_ * pose_curr2last_; | ||||
|   *pose_out = pose_curr2world_; | ||||
|   AINFO << "Pose increase: " << pose_curr2last_.ToString(); | ||||
|   AINFO << "Pose after: " << pose_curr2world_.ToString(); | ||||
|   AINFO_IF(verbose_) << "Pose increase: " << pose_curr2last_.ToString(); | ||||
|   AINFO_IF(verbose_) << "Pose after: " << pose_curr2world_.ToString(); | ||||
|   UpdatePre(features); | ||||
|   AINFO << "Odometer::Porcess: " << BLOCK_TIMER_STOP_FMT; | ||||
|   if (is_vis_) Visualize(features, pl_pairs, pp_pairs); | ||||
|   if (is_vis_) Visualize(cloud_corn, cloud_surf, pl_pairs, pp_pairs); | ||||
| } | ||||
| 
 | ||||
| void Odometer::MatchCorn(const TPointCloud &src, | ||||
|  | @ -90,12 +90,13 @@ void Odometer::MatchCorn(const TPointCloud &src, | |||
|     TPoint pt1 = kdtree_corn_.getInputCloud()->at(indices[0]); | ||||
|     int pt1_scan_id = GetScanId(pt1); | ||||
| 
 | ||||
|     int nearby_scan_num = config_["nearby_scan_num"].as<int>(); | ||||
|     TPoint pt2; | ||||
|     bool pt2_fount = false; | ||||
|     float min_pt2_dist_sq = dist_sq_thresh; | ||||
|     int i_begin = std::max<int>(0, pt1_scan_id - kNearScanN); | ||||
|     int i_end = | ||||
|         std::min<int>(kdtrees_scan_corn_.size(), pt1_scan_id + kNearScanN + 1); | ||||
|     int i_begin = std::max<int>(0, pt1_scan_id - nearby_scan_num); | ||||
|     int i_end = std::min<int>(kdtrees_scan_corn_.size(), | ||||
|                               pt1_scan_id + nearby_scan_num + 1); | ||||
|     for (int i = i_begin; i < i_end; ++i) { | ||||
|       if (i == pt1_scan_id) continue; | ||||
|       const auto &kdtree = kdtrees_scan_corn_[i]; | ||||
|  | @ -128,12 +129,13 @@ void Odometer::MatchSurf(const TPointCloud &src, | |||
|     TPoint pt1 = kdtree_surf_.getInputCloud()->at(indices[0]); | ||||
|     int pt1_scan_id = GetScanId(pt1); | ||||
| 
 | ||||
|     int nearby_scan_num = config_["nearby_scan_num"].as<int>(); | ||||
|     TPoint pt2; | ||||
|     bool pt2_fount = false; | ||||
|     float min_pt2_dist_sq = dist_sq_thresh; | ||||
|     int i_begin = std::max<int>(0, pt1_scan_id - kNearScanN); | ||||
|     int i_end = | ||||
|         std::min<int>(kdtrees_scan_surf_.size(), pt1_scan_id + kNearScanN + 1); | ||||
|     int i_begin = std::max<int>(0, pt1_scan_id - nearby_scan_num); | ||||
|     int i_end = std::min<int>(kdtrees_scan_surf_.size(), | ||||
|                               pt1_scan_id + nearby_scan_num + 1); | ||||
|     for (int i = i_begin; i < i_end; ++i) { | ||||
|       if (i == pt1_scan_id) continue; | ||||
|       const auto &kdtree = kdtrees_scan_surf_[i]; | ||||
|  | @ -185,12 +187,15 @@ void Odometer::UpdatePre(const std::vector<Feature> &features) { | |||
|   kdtree_surf_.setInputCloud(surf_pre); | ||||
| } | ||||
| 
 | ||||
| void Odometer::Visualize(const std::vector<Feature> &features, | ||||
| void Odometer::Visualize(const TPointCloudConstPtr &cloud_corn, | ||||
|                          const TPointCloudConstPtr &cloud_surf, | ||||
|                          const std::vector<PointLinePair> &pl_pairs, | ||||
|                          const std::vector<PointPlanePair> &pp_pairs, | ||||
|                          double timestamp) const { | ||||
|   std::shared_ptr<OdometerVisFrame> frame(new OdometerVisFrame); | ||||
|   frame->timestamp = timestamp; | ||||
|   frame->cloud_corn = cloud_corn; | ||||
|   frame->cloud_surf = cloud_surf; | ||||
|   frame->pl_pairs = pl_pairs; | ||||
|   frame->pp_pairs = pp_pairs; | ||||
|   frame->pose_curr2last = pose_curr2last_; | ||||
|  |  | |||
|  | @ -27,7 +27,8 @@ class Odometer { | |||
|   void MatchSurf(const TPointCloud &src, | ||||
|                  std::vector<PointPlanePair> *const pairs) const; | ||||
| 
 | ||||
|   void Visualize(const std::vector<Feature> &features, | ||||
|   void Visualize(const TPointCloudConstPtr &cloud_corn, | ||||
|                  const TPointCloudConstPtr &cloud_surf, | ||||
|                  const std::vector<PointLinePair> &pl_pairs, | ||||
|                  const std::vector<PointPlanePair> &pp_pairs, | ||||
|                  double timestamp = 0.0) const; | ||||
|  |  | |||
|  | @ -7,11 +7,10 @@ namespace oh_my_loam { | |||
| 
 | ||||
| namespace { | ||||
| const double kPointMinDist = 0.1; | ||||
| using namespace common; | ||||
| }  // namespace
 | ||||
| 
 | ||||
| bool OhMyLoam::Init() { | ||||
|   YAML::Node config = YAMLConfig::Instance()->config(); | ||||
|   YAML::Node config = common::YAMLConfig::Instance()->config(); | ||||
|   extractor_.reset(new ExtractorVLP16); | ||||
|   if (!extractor_->Init()) { | ||||
|     AERROR << "Failed to initialize extractor"; | ||||
|  | @ -30,8 +29,9 @@ bool OhMyLoam::Init() { | |||
|   return true; | ||||
| } | ||||
| 
 | ||||
| void OhMyLoam::Run(double timestamp, const PointCloudConstPtr &cloud_in) { | ||||
|   PointCloudPtr cloud(new PointCloud); | ||||
| void OhMyLoam::Run(double timestamp, | ||||
|                    const common::PointCloudConstPtr &cloud_in) { | ||||
|   common::PointCloudPtr cloud(new common::PointCloud); | ||||
|   RemoveOutliers(*cloud_in, cloud.get()); | ||||
|   std::vector<Feature> features; | ||||
|   extractor_->Process(timestamp, cloud, &features); | ||||
|  | @ -40,11 +40,12 @@ void OhMyLoam::Run(double timestamp, const PointCloudConstPtr &cloud_in) { | |||
|   poses_.emplace_back(pose); | ||||
| } | ||||
| 
 | ||||
| void OhMyLoam::RemoveOutliers(const PointCloud &cloud_in, | ||||
|                               PointCloud *const cloud_out) const { | ||||
|   RemovePoints<Point>(cloud_in, cloud_out, [&](const Point &pt) { | ||||
|     return !IsFinite<Point>(pt) || | ||||
|            DistanceSqure<Point>(pt) < kPointMinDist * kPointMinDist; | ||||
| void OhMyLoam::RemoveOutliers(const common::PointCloud &cloud_in, | ||||
|                               common::PointCloud *const cloud_out) const { | ||||
|   common::RemovePoints<common::Point>(cloud_in, cloud_out, [&](const auto &pt) { | ||||
|     return !common::IsFinite<common::Point>(pt) || | ||||
|            common::DistanceSquare<common::Point>(pt) < | ||||
|                kPointMinDist * kPointMinDist; | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -59,10 +59,22 @@ bool PointLineCostFunction::operator()(const T *const r_quat, | |||
|   Eigen::Matrix<T, 3, 1> p(T(pt.x), T(pt.y), T(pt.z)); | ||||
|   Eigen::Matrix<T, 3, 1> p1(T(pt1.x), T(pt1.y), T(pt1.z)); | ||||
|   Eigen::Matrix<T, 3, 1> p2(T(pt2.x), T(pt2.y), T(pt2.z)); | ||||
|   // if constexpr (!std::is_arithmetic<T>::value) {
 | ||||
|   //   AERROR << p.x().a << ", " << p.y().a << ", " << p.z().a;
 | ||||
|   //   AERROR << p1.x().a << ", " << p1.y().a << ", " << p1.z().a;
 | ||||
|   //   AERROR << p2.x().a << ", " << p2.y().a << ", " << p2.z().a;
 | ||||
|   // }
 | ||||
| 
 | ||||
|   Eigen::Quaternion<T> r(r_quat[3], r_quat[0], r_quat[1], r_quat[2]); | ||||
|   Eigen::Quaternion<T> r_interp = | ||||
|       Eigen::Quaternion<T>::Identity().slerp(T(time_), r); | ||||
|   // if constexpr (!std::is_arithmetic<T>::value) {
 | ||||
|   //   AERROR << time_;
 | ||||
|   //   AERROR << r.w().a << " " << r.x().a << ", " << r.y().a << ", " <<
 | ||||
|   //   r.z().a; AERROR << r_interp.w().a << " " << r_interp.x().a << ", " <<
 | ||||
|   //   r_interp.y().a
 | ||||
|   //          << ", " << r_interp.z().a;
 | ||||
|   // }
 | ||||
|   Eigen::Matrix<T, 3, 1> t(T(time_) * t_vec[0], T(time_) * t_vec[1], | ||||
|                            T(time_) * t_vec[2]); | ||||
|   Eigen::Matrix<T, 3, 1> p0 = r_interp * p + t; | ||||
|  | @ -73,9 +85,9 @@ bool PointLineCostFunction::operator()(const T *const r_quat, | |||
|   residual[0] = area[0] / base_length; | ||||
|   residual[1] = area[1] / base_length; | ||||
|   residual[2] = area[2] / base_length; | ||||
|   if constexpr (!std::is_arithmetic<T>::value) { | ||||
|     AERROR << p.transpose() << ", "; | ||||
|   } | ||||
|   // if constexpr (!std::is_arithmetic<T>::value) {
 | ||||
|   //   AERROR << base_length.a;
 | ||||
|   // }
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
|  | @ -99,9 +111,6 @@ bool PointPlaneCostFunction::operator()(const T *const r_quat, | |||
| 
 | ||||
|   Eigen::Matrix<T, 3, 1> normal = (p2 - p1).cross(p3 - p1).normalized(); | ||||
|   residual[0] = (p0 - p1).dot(normal); | ||||
|   if constexpr (!std::is_arithmetic<T>::value) { | ||||
|     AERROR << "ppres" << residual[0].a; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,8 +30,6 @@ bool PoseSolver::Solve(int max_iter_num, bool verbose, | |||
|   ceres::Solver::Summary summary; | ||||
|   ceres::Solve(options, &problem_, &summary); | ||||
|   AINFO_IF(verbose) << summary.BriefReport(); | ||||
|   AWARN_IF(summary.termination_type != ceres::CONVERGENCE) | ||||
|       << "Solve: no convergence"; | ||||
|   if (pose) *pose = common::Pose3d(r_quat_, t_vec_); | ||||
|   return summary.termination_type == ceres::CONVERGENCE; | ||||
| } | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ using namespace common; | |||
| 
 | ||||
| void ExtractorVisualizer::Draw() { | ||||
|   auto frame = GetCurrentFrame<ExtractorVisFrame>(); | ||||
|   DrawPointCloud<Point>(frame.cloud, GRAY, "cloud_raw"); | ||||
|   DrawPointCloud<Point>(frame.cloud, DRAK_GRAY, "cloud_raw"); | ||||
|   for (size_t i = 0; i < frame.features.size(); ++i) { | ||||
|     const auto &feature = frame.features[i]; | ||||
|     std::string id_suffix = std::to_string(i); | ||||
|  |  | |||
|  | @ -10,49 +10,116 @@ using namespace common; | |||
| 
 | ||||
| void OdometerVisualizer::Draw() { | ||||
|   auto frame = GetCurrentFrame<OdometerVisFrame>(); | ||||
|   TPointCloudPtr src_corn(new TPointCloud); | ||||
|   TPointCloudPtr tgt_corn(new TPointCloud); | ||||
|   src_corn->resize(frame.pl_pairs.size()); | ||||
|   tgt_corn->resize(frame.pl_pairs.size() * 2); | ||||
|   for (size_t i = 0; i < frame.pl_pairs.size(); ++i) { | ||||
|     const auto &pair = frame.pl_pairs[i]; | ||||
|     TransformToStart(frame.pose_curr2last, pair.pt, &src_corn->points[i]); | ||||
|     tgt_corn->points[2 * i] = pair.line.pt1; | ||||
|     tgt_corn->points[2 * i + 1] = pair.line.pt2; | ||||
|   DrawPointCloud<TPoint>(frame.cloud_corn, GRAY, "cloud_corn"); | ||||
|   DrawPointCloud<TPoint>(frame.cloud_surf, GRAY, "cloud_surf"); | ||||
| 
 | ||||
|   DrawCorn(frame.pose_curr2last, frame.pl_pairs); | ||||
|   DrawSurf(frame.pose_curr2last, frame.pp_pairs); | ||||
| 
 | ||||
|   poses_.push_back(frame.pose_curr2world); | ||||
|   DrawTrajectory(); | ||||
| } | ||||
| 
 | ||||
| void OdometerVisualizer::DrawCorn(const Pose3d &pose, | ||||
|                                   const std::vector<PointLinePair> &pairs) { | ||||
|   TPointCloudPtr src(new TPointCloud); | ||||
|   TPointCloudPtr tgt(new TPointCloud); | ||||
|   src->resize(pairs.size()); | ||||
|   tgt->resize(pairs.size() * 2); | ||||
|   for (size_t i = 0; i < pairs.size(); ++i) { | ||||
|     const auto &pair = pairs[i]; | ||||
|     src->at(i) = pair.pt; | ||||
|     if (trans_) TransformToStart(pose, pair.pt, &src->points[i]); | ||||
|     tgt->at(2 * i) = pair.line.pt1; | ||||
|     tgt->at(2 * i + 1) = pair.line.pt2; | ||||
|   } | ||||
|   TPointCloudPtr src_surf(new TPointCloud); | ||||
|   TPointCloudPtr tgt_surf(new TPointCloud); | ||||
|   src_surf->resize(frame.pp_pairs.size()); | ||||
|   tgt_surf->resize(frame.pp_pairs.size() * 3); | ||||
|   for (size_t i = 0; i < frame.pp_pairs.size(); ++i) { | ||||
|     const auto &pair = frame.pp_pairs[i]; | ||||
|     TransformToStart(frame.pose_curr2last, pair.pt, &src_surf->points[i]); | ||||
|     tgt_surf->points[3 * i] = pair.plane.pt1; | ||||
|     tgt_surf->points[3 * i + 1] = pair.plane.pt2; | ||||
|     tgt_surf->points[3 * i + 2] = pair.plane.pt3; | ||||
|   DrawPointCloud<TPoint>(tgt, YELLOW, "tgt_corn", 4); | ||||
|   DrawPointCloud<TPoint>(src, RED, "src_corn", 4); | ||||
|   if (!corn_connect_) return; | ||||
|   for (size_t i = 0; i < src->size(); ++i) { | ||||
|     const auto &p = src->at(i), &p1 = tgt->at(2 * i), &p2 = tgt->at(2 * i + 1); | ||||
|     common::AddLine<TPoint>(p, p1, WHITE, "corn_line1_" + std::to_string(i), | ||||
|                             viewer_.get()); | ||||
|     common::AddLine<TPoint>(p, p2, WHITE, "corn_line2_" + std::to_string(i), | ||||
|                             viewer_.get()); | ||||
|   } | ||||
|   DrawPointCloud<TPoint>(tgt_corn, YELLOW, "tgt_corn", 4); | ||||
|   DrawPointCloud<TPoint>(src_corn, RED, "src_corn", 4); | ||||
|   DrawPointCloud<TPoint>(tgt_surf, BLUE, "tgt_surf", 4); | ||||
|   DrawPointCloud<TPoint>(src_surf, CYAN, "src_surf", 4); | ||||
| } | ||||
| 
 | ||||
| void OdometerVisualizer::DrawSurf(const Pose3d &pose, | ||||
|                                   const std::vector<PointPlanePair> &pairs) { | ||||
|   TPointCloudPtr src(new TPointCloud); | ||||
|   TPointCloudPtr tgt(new TPointCloud); | ||||
|   src->resize(pairs.size()); | ||||
|   tgt->resize(pairs.size() * 3); | ||||
|   for (size_t i = 0; i < pairs.size(); ++i) { | ||||
|     const auto &pair = pairs[i]; | ||||
|     src->at(i) = pair.pt; | ||||
|     if (trans_) TransformToStart(pose, pair.pt, &src->points[i]); | ||||
|     tgt->at(3 * i) = pair.plane.pt1; | ||||
|     tgt->at(3 * i + 1) = pair.plane.pt2; | ||||
|     tgt->at(3 * i + 2) = pair.plane.pt3; | ||||
|   } | ||||
|   DrawPointCloud<TPoint>(tgt, BLUE, "tgt_surf", 4); | ||||
|   DrawPointCloud<TPoint>(src, CYAN, "src_surf", 4); | ||||
|   if (!surf_connect_) return; | ||||
|   for (size_t i = 0; i < src->size(); ++i) { | ||||
|     const auto &p = src->at(i), &p1 = tgt->at(3 * i), &p2 = tgt->at(3 * i + 1), | ||||
|                &p3 = tgt->at(3 * i + 2); | ||||
|     AddLine<TPoint>(p, p1, WHITE, "surf_line1_" + std::to_string(i), | ||||
|                     viewer_.get()); | ||||
|     AddLine<TPoint>(p, p2, WHITE, "surf_line2_" + std::to_string(i), | ||||
|                     viewer_.get()); | ||||
|     AddLine<TPoint>(p, p3, WHITE, "surf_line3_" + std::to_string(i), | ||||
|                     viewer_.get()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void OdometerVisualizer::DrawTrajectory() { | ||||
|   std::vector<Pose3d> poses_n; | ||||
|   poses_n.reserve((poses_.size())); | ||||
|   Pose3d pose_inv = frame.pose_curr2world.Inv(); | ||||
|   Pose3d pose_inv = poses_.back().Inv(); | ||||
|   for (const auto &pose : poses_) { | ||||
|     poses_n.emplace_back(pose_inv * pose); | ||||
|   }; | ||||
|   for (size_t i = 0; i < poses_n.size(); ++i) { | ||||
|   for (size_t i = 0; i < poses_n.size() - 1; ++i) { | ||||
|     Eigen::Vector3f p1 = poses_n[i].t_vec().cast<float>(); | ||||
|     if (i < poses_n.size() - 1) { | ||||
|       Eigen::Vector3f p2 = poses_n[i + 1].t_vec().cast<float>(); | ||||
|       AddLine(Point(p1.x(), p1.y(), p1.z()), Point(p2.x(), p2.y(), p2.z()), | ||||
|               WHITE, "line" + std::to_string(i), viewer_.get()); | ||||
|     } else { | ||||
|       AddLine(Point(p1.x(), p1.y(), p1.z()), Point(0, 0, 0), WHITE, | ||||
|               "line" + std::to_string(i), viewer_.get()); | ||||
|     } | ||||
|     Eigen::Vector3f p2 = poses_n[i + 1].t_vec().cast<float>(); | ||||
|     AddLine<Point>({p1.x(), p1.y(), p1.z()}, {p2.x(), p2.y(), p2.z()}, PINK, | ||||
|                    "trajectory" + std::to_string(i), viewer_.get()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void OdometerVisualizer::KeyboardEventCallback( | ||||
|     const pcl::visualization::KeyboardEvent &event) { | ||||
|   if (event.getKeySym() == "p" && event.keyDown()) { | ||||
|     std::lock_guard<std::mutex> lock(mutex_); | ||||
|     ++curr_frame_iter_; | ||||
|     if (curr_frame_iter_ == history_frames_.end()) { | ||||
|       --curr_frame_iter_; | ||||
|     } | ||||
|     is_updated_ = true; | ||||
|   } else if (event.getKeySym() == "n" && event.keyDown()) { | ||||
|     std::lock_guard<std::mutex> lock(mutex_); | ||||
|     if (curr_frame_iter_ != history_frames_.begin()) { | ||||
|       --curr_frame_iter_; | ||||
|     } | ||||
|     is_updated_ = true; | ||||
|   } else if (event.getKeySym() == "t" && event.keyDown()) { | ||||
|     trans_ = !trans_; | ||||
|     is_updated_ = true; | ||||
|   } else if (event.getKeySym() == "c" && event.keyDown()) { | ||||
|     corn_connect_ = !corn_connect_; | ||||
|     is_updated_ = true; | ||||
|   } else if (event.getKeySym() == "s" && event.keyDown()) { | ||||
|     surf_connect_ = !surf_connect_; | ||||
|     is_updated_ = true; | ||||
|   } else if (event.getKeySym() == "r" && event.keyDown()) { | ||||
|     viewer_->setCameraPosition(0, 0, 200, 0, 0, 0, 1, 0, 0, 0); | ||||
|     viewer_->setSize(2500, 1500); | ||||
|     trans_ = true; | ||||
|     corn_connect_ = surf_connect_ = false; | ||||
|     is_updated_ = true; | ||||
|   } | ||||
|   poses_.push_back(frame.pose_curr2world); | ||||
| } | ||||
| 
 | ||||
| }  // namespace oh_my_loam
 | ||||
|  |  | |||
|  | @ -6,8 +6,8 @@ | |||
| namespace oh_my_loam { | ||||
| 
 | ||||
| struct OdometerVisFrame : public common::LidarVisFrame { | ||||
|   TPointCloudPtr cloud_surf; | ||||
|   TPointCloudPtr cloud_corner; | ||||
|   TPointCloudConstPtr cloud_surf; | ||||
|   TPointCloudConstPtr cloud_corn; | ||||
|   std::vector<PointLinePair> pl_pairs; | ||||
|   std::vector<PointPlanePair> pp_pairs; | ||||
|   common::Pose3d pose_curr2last; | ||||
|  | @ -23,6 +23,19 @@ class OdometerVisualizer : public common::LidarVisualizer { | |||
|  private: | ||||
|   void Draw() override; | ||||
| 
 | ||||
|   void DrawCorn(const Pose3d &pose, const std::vector<PointLinePair> &pairs); | ||||
| 
 | ||||
|   void DrawSurf(const Pose3d &pose, const std::vector<PointPlanePair> &pairs); | ||||
| 
 | ||||
|   void DrawTrajectory(); | ||||
| 
 | ||||
|   void KeyboardEventCallback( | ||||
|       const pcl::visualization::KeyboardEvent &event) override; | ||||
| 
 | ||||
|   bool trans_ = true; | ||||
|   bool corn_connect_ = false; | ||||
|   bool surf_connect_ = false; | ||||
| 
 | ||||
|   std::deque<common::Pose3d> poses_; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue