ivox3d.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. //
  2. // Created by xiang on 2021/9/16.
  3. //
  4. #ifndef FASTER_LIO_IVOX3D_H
  5. #define FASTER_LIO_IVOX3D_H
  6. #include <glog/logging.h>
  7. #include <execution>
  8. #include <list>
  9. #include <thread>
  10. #include "eigen_types.h"
  11. #include "ivox3d_node.hpp"
  12. namespace faster_lio {
  13. enum class IVoxNodeType {
  14. DEFAULT, // linear ivox
  15. PHC, // phc ivox
  16. };
  17. /// traits for NodeType
  18. template <IVoxNodeType node_type, typename PointT, int dim>
  19. struct IVoxNodeTypeTraits {};
  20. template <typename PointT, int dim>
  21. struct IVoxNodeTypeTraits<IVoxNodeType::DEFAULT, PointT, dim> {
  22. using NodeType = IVoxNode<PointT, dim>;
  23. };
  24. template <typename PointT, int dim>
  25. struct IVoxNodeTypeTraits<IVoxNodeType::PHC, PointT, dim> {
  26. using NodeType = IVoxNodePhc<PointT, dim>;
  27. };
  28. template <int dim = 3, IVoxNodeType node_type = IVoxNodeType::DEFAULT, typename PointType = pcl::PointXYZ>
  29. class IVox {
  30. public:
  31. using KeyType = Eigen::Matrix<int, dim, 1>;
  32. using PtType = Eigen::Matrix<float, dim, 1>;
  33. using NodeType = typename IVoxNodeTypeTraits<node_type, PointType, dim>::NodeType;
  34. using PointVector = std::vector<PointType, Eigen::aligned_allocator<PointType>>;
  35. using DistPoint = typename NodeType::DistPoint;
  36. enum class NearbyType {
  37. CENTER, // center only
  38. NEARBY6,
  39. NEARBY18,
  40. NEARBY26,
  41. };
  42. struct Options {
  43. float resolution_ = 0.2; // ivox resolution
  44. float inv_resolution_ = 10.0; // inverse resolution
  45. NearbyType nearby_type_ = NearbyType::NEARBY6; // nearby range
  46. std::size_t capacity_ = 1000000; // capacity
  47. };
  48. /**
  49. * constructor
  50. * @param options ivox options
  51. */
  52. explicit IVox(Options options) : options_(options) {
  53. options_.inv_resolution_ = 1.0 / options_.resolution_;
  54. GenerateNearbyGrids();
  55. }
  56. /**
  57. * add points
  58. * @param points_to_add
  59. */
  60. void AddPoints(const PointVector& points_to_add);
  61. void GetPoints(PointVector& points);
  62. void DownSample(double leafsize=0.02);
  63. /// get nn
  64. bool GetClosestPoint(const PointType& pt, PointType& closest_pt);
  65. /// get nn with condition
  66. bool GetClosestPoint(const PointType& pt, PointVector& closest_pt, int max_num = 5, double max_range = 5.0);
  67. /// get nn in cloud
  68. bool GetClosestPoint(const PointVector& cloud, PointVector& closest_cloud);
  69. /// get number of points
  70. size_t NumPoints() const;
  71. /// get number of valid grids
  72. size_t NumValidGrids() const;
  73. /// get statistics of the points
  74. std::vector<float> StatGridPoints() const;
  75. public:
  76. /// generate the nearby grids according to the given options
  77. void GenerateNearbyGrids();
  78. /// position to grid
  79. KeyType Pos2Grid(const PtType& pt) const;
  80. Options options_;
  81. std::unordered_map<KeyType, typename std::list<std::pair<KeyType, NodeType>>::iterator, hash_vec<dim>>
  82. grids_map_; // voxel hash map
  83. std::list<std::pair<KeyType, NodeType>> grids_cache_; // voxel cache
  84. std::vector<KeyType> nearby_grids_; // nearbys
  85. };
  86. template <int dim, IVoxNodeType node_type, typename PointType>
  87. bool IVox<dim, node_type, PointType>::GetClosestPoint(const PointType& pt, PointType& closest_pt) {
  88. std::vector<DistPoint> candidates;
  89. auto key = Pos2Grid(ToEigen<float, dim>(pt));
  90. std::for_each(nearby_grids_.begin(), nearby_grids_.end(), [&key, &candidates, &pt, this](const KeyType& delta) {
  91. auto dkey = key + delta;
  92. auto iter = grids_map_.find(dkey);
  93. if (iter != grids_map_.end()) {
  94. DistPoint dist_point;
  95. bool found = iter->second->second.NNPoint(pt, dist_point);
  96. if (found) {
  97. candidates.emplace_back(dist_point);
  98. }
  99. }
  100. });
  101. if (candidates.empty()) {
  102. return false;
  103. }
  104. auto iter = std::min_element(candidates.begin(), candidates.end());
  105. closest_pt = iter->Get();
  106. return true;
  107. }
  108. template <int dim, IVoxNodeType node_type, typename PointType>
  109. bool IVox<dim, node_type, PointType>::GetClosestPoint(const PointType& pt, PointVector& closest_pt, int max_num,
  110. double max_range) {
  111. std::vector<DistPoint> candidates;
  112. candidates.reserve(max_num * nearby_grids_.size());
  113. auto key = Pos2Grid(ToEigen<float, dim>(pt));
  114. // #define INNER_TIMER
  115. #ifdef INNER_TIMER
  116. static std::unordered_map<std::string, std::vector<int64_t>> stats;
  117. if (stats.empty()) {
  118. stats["knn"] = std::vector<int64_t>();
  119. stats["nth"] = std::vector<int64_t>();
  120. }
  121. #endif
  122. for (const KeyType& delta : nearby_grids_) {
  123. auto dkey = key + delta;
  124. auto iter = grids_map_.find(dkey);
  125. if (iter != grids_map_.end()) {
  126. #ifdef INNER_TIMER
  127. auto t1 = std::chrono::high_resolution_clock::now();
  128. #endif
  129. auto tmp = iter->second->second.KNNPointByCondition(candidates, pt, max_num, max_range);
  130. #ifdef INNER_TIMER
  131. auto t2 = std::chrono::high_resolution_clock::now();
  132. auto knn = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
  133. stats["knn"].emplace_back(knn);
  134. #endif
  135. }
  136. }
  137. if (candidates.empty()) {
  138. return false;
  139. }
  140. #ifdef INNER_TIMER
  141. auto t1 = std::chrono::high_resolution_clock::now();
  142. #endif
  143. if (candidates.size() <= max_num) {
  144. } else {
  145. std::nth_element(candidates.begin(), candidates.begin() + max_num - 1, candidates.end());
  146. candidates.resize(max_num);
  147. }
  148. std::nth_element(candidates.begin(), candidates.begin(), candidates.end());
  149. #ifdef INNER_TIMER
  150. auto t2 = std::chrono::high_resolution_clock::now();
  151. auto nth = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
  152. stats["nth"].emplace_back(nth);
  153. constexpr int STAT_PERIOD = 100000;
  154. if (!stats["nth"].empty() && stats["nth"].size() % STAT_PERIOD == 0) {
  155. for (auto& it : stats) {
  156. const std::string& key = it.first;
  157. std::vector<int64_t>& stat = it.second;
  158. int64_t sum_ = std::accumulate(stat.begin(), stat.end(), 0);
  159. int64_t num_ = stat.size();
  160. stat.clear();
  161. std::cout << "inner_" << key << "(ns): sum=" << sum_ << " num=" << num_ << " ave=" << 1.0 * sum_ / num_
  162. << " ave*n=" << 1.0 * sum_ / STAT_PERIOD << std::endl;
  163. }
  164. }
  165. #endif
  166. closest_pt.clear();
  167. for (auto& it : candidates) {
  168. closest_pt.emplace_back(it.Get());
  169. }
  170. return closest_pt.empty() == false;
  171. }
  172. template <int dim, IVoxNodeType node_type, typename PointType>
  173. size_t IVox<dim, node_type, PointType>::NumPoints() const
  174. {
  175. int num=0;
  176. for( auto it=grids_map_.begin();it!=grids_map_.end();++it)
  177. {
  178. num+=it->second->second.Size();
  179. }
  180. return num;
  181. }
  182. template <int dim, IVoxNodeType node_type, typename PointType>
  183. size_t IVox<dim, node_type, PointType>::NumValidGrids() const {
  184. return grids_map_.size();
  185. }
  186. template <int dim, IVoxNodeType node_type, typename PointType>
  187. void IVox<dim, node_type, PointType>::DownSample(double leafsize)
  188. {
  189. for( auto it=grids_map_.begin();it!=grids_map_.end();++it)
  190. {
  191. it->second->second.DownSample(leafsize);
  192. }
  193. }
  194. template <int dim, IVoxNodeType node_type, typename PointType>
  195. void IVox<dim, node_type, PointType>::GenerateNearbyGrids() {
  196. if (options_.nearby_type_ == NearbyType::CENTER) {
  197. nearby_grids_.emplace_back(KeyType::Zero());
  198. } else if (options_.nearby_type_ == NearbyType::NEARBY6) {
  199. nearby_grids_ = {KeyType(0, 0, 0), KeyType(-1, 0, 0), KeyType(1, 0, 0), KeyType(0, 1, 0),
  200. KeyType(0, -1, 0), KeyType(0, 0, -1), KeyType(0, 0, 1)};
  201. } else if (options_.nearby_type_ == NearbyType::NEARBY18) {
  202. nearby_grids_ = {KeyType(0, 0, 0), KeyType(-1, 0, 0), KeyType(1, 0, 0), KeyType(0, 1, 0),
  203. KeyType(0, -1, 0), KeyType(0, 0, -1), KeyType(0, 0, 1), KeyType(1, 1, 0),
  204. KeyType(-1, 1, 0), KeyType(1, -1, 0), KeyType(-1, -1, 0), KeyType(1, 0, 1),
  205. KeyType(-1, 0, 1), KeyType(1, 0, -1), KeyType(-1, 0, -1), KeyType(0, 1, 1),
  206. KeyType(0, -1, 1), KeyType(0, 1, -1), KeyType(0, -1, -1)};
  207. } else if (options_.nearby_type_ == NearbyType::NEARBY26) {
  208. nearby_grids_ = {KeyType(0, 0, 0), KeyType(-1, 0, 0), KeyType(1, 0, 0), KeyType(0, 1, 0),
  209. KeyType(0, -1, 0), KeyType(0, 0, -1), KeyType(0, 0, 1), KeyType(1, 1, 0),
  210. KeyType(-1, 1, 0), KeyType(1, -1, 0), KeyType(-1, -1, 0), KeyType(1, 0, 1),
  211. KeyType(-1, 0, 1), KeyType(1, 0, -1), KeyType(-1, 0, -1), KeyType(0, 1, 1),
  212. KeyType(0, -1, 1), KeyType(0, 1, -1), KeyType(0, -1, -1), KeyType(1, 1, 1),
  213. KeyType(-1, 1, 1), KeyType(1, -1, 1), KeyType(1, 1, -1), KeyType(-1, -1, 1),
  214. KeyType(-1, 1, -1), KeyType(1, -1, -1), KeyType(-1, -1, -1)};
  215. } else {
  216. LOG(ERROR) << "Unknown nearby_type!";
  217. }
  218. }
  219. template <int dim, IVoxNodeType node_type, typename PointType>
  220. bool IVox<dim, node_type, PointType>::GetClosestPoint(const PointVector& cloud, PointVector& closest_cloud) {
  221. std::vector<size_t> index(cloud.size());
  222. for (int i = 0; i < cloud.size(); ++i) {
  223. index[i] = i;
  224. }
  225. closest_cloud.resize(cloud.size());
  226. std::for_each(std::execution::par_unseq, index.begin(), index.end(), [&cloud, &closest_cloud, this](size_t idx) {
  227. PointType pt;
  228. if (GetClosestPoint(cloud[idx], pt)) {
  229. closest_cloud[idx] = pt;
  230. } else {
  231. closest_cloud[idx] = PointType();
  232. }
  233. });
  234. return true;
  235. }
  236. template <int dim, IVoxNodeType node_type, typename PointType>
  237. void IVox<dim, node_type, PointType>::GetPoints(PointVector& points)
  238. {
  239. points.clear();
  240. for(auto it=grids_cache_.begin();it!=grids_cache_.end();it++)
  241. {
  242. std::vector<PointType> node_points=it->second.Points();
  243. points.insert(points.end(),node_points.begin(),node_points.end());
  244. }
  245. }
  246. template <int dim, IVoxNodeType node_type, typename PointType>
  247. void IVox<dim, node_type, PointType>::AddPoints(const PointVector& points_to_add)
  248. {
  249. for (int i = 0; i < points_to_add.size(); ++i)
  250. {
  251. PointType pt = points_to_add[i];
  252. auto key = Pos2Grid(ToEigen<float, dim>(pt));
  253. auto iter = grids_map_.find(key);
  254. if (iter == grids_map_.end()) {
  255. PointType center;
  256. center.getVector3fMap() = key.template cast<float>() * options_.resolution_;
  257. grids_cache_.push_front({key, NodeType(center, options_.resolution_)});
  258. grids_map_.insert({key, grids_cache_.begin()});
  259. grids_cache_.front().second.InsertPoint(pt);
  260. if (grids_map_.size() >= options_.capacity_) {
  261. grids_map_.erase(grids_cache_.back().first);
  262. grids_cache_.pop_back();
  263. }
  264. } else {
  265. iter->second->second.InsertPoint(pt);
  266. grids_cache_.splice(grids_cache_.begin(), grids_cache_, iter->second);
  267. grids_map_[key] = grids_cache_.begin();
  268. }
  269. }
  270. }
  271. template <int dim, IVoxNodeType node_type, typename PointType>
  272. Eigen::Matrix<int, dim, 1> IVox<dim, node_type, PointType>::Pos2Grid(const IVox::PtType& pt) const {
  273. return (pt * options_.inv_resolution_).array().round().template cast<int>();
  274. }
  275. template <int dim, IVoxNodeType node_type, typename PointType>
  276. std::vector<float> IVox<dim, node_type, PointType>::StatGridPoints() const {
  277. int num = grids_cache_.size(), valid_num = 0, max = 0, min = 100000000;
  278. int sum = 0, sum_square = 0;
  279. for (auto& it : grids_cache_) {
  280. int s = it.second.Size();
  281. valid_num += s > 0;
  282. max = s > max ? s : max;
  283. min = s < min ? s : min;
  284. sum += s;
  285. sum_square += s * s;
  286. }
  287. float ave = float(sum) / num;
  288. float stddev = num > 1 ? sqrt((float(sum_square) - num * ave * ave) / (num - 1)) : 0;
  289. return std::vector<float>{valid_num, ave, max, min, stddev};
  290. }
  291. } // namespace faster_lio
  292. #endif