79 float min_distance = 0.0f;
81 float max_distance = std::numeric_limits<float>::infinity();
83 float sensor_angular_resolution =
radians(0.0f);
85 float translation_error = 0.0f;
86 float orientation_error =
radians(0.0f);
90 : sensor_error_f_(sensor_error_f)
92 angularResolution(
radians(1.0f));
95 template <
class Map,
class T,
class... Rest>
105 std::enable_if_t<execution::is_execution_policy_v<ExecutionPolicy>,
bool> =
true>
109 std::future<void>
const& finish_before_touch = {})
const
113 if constexpr (execution::is_seq_v<ExecutionPolicy> ||
114 execution::is_unseq_v<ExecutionPolicy>) {
115 return operator()(map, cloud,
transform);
118 auto const t0 = std::chrono::high_resolution_clock::now();
120 fillDistances(policy, cloud.template view<0>(), sensor_origin);
122 removeNanAndMinDistance(cloud);
124 auto const t1 = std::chrono::high_resolution_clock::now();
128 sensor_origin = frame_origin(sensor_origin);
131 auto const t2 = std::chrono::high_resolution_clock::now();
133 fillData(policy, cloud.template view<0>(), sensor_origin);
135 auto const t3 = std::chrono::high_resolution_clock::now();
137 fillMisses(policy, map, cloud.template view<0>(), sensor_origin);
139 auto const t4 = std::chrono::high_resolution_clock::now();
141 if (finish_before_touch.valid()) {
142 finish_before_touch.wait();
145 auto const t5 = std::chrono::high_resolution_clock::now();
147 this->insertMisses(policy, map, cached_misses_);
149 auto const t6 = std::chrono::high_resolution_clock::now();
153 auto const t7 = std::chrono::high_resolution_clock::now();
155 filterHits(policy, cloud, sensor_origin);
157 auto const t8 = std::chrono::high_resolution_clock::now();
159 this->insertHits(policy, map, cloud, sensor_origin);
161 auto const t9 = std::chrono::high_resolution_clock::now();
165 if (this->propagate) {
170 auto const t10 = std::chrono::high_resolution_clock::now();
172 std::chrono::duration<double, std::milli>
const remove_nan_ms = t1 - t0;
173 std::chrono::duration<double, std::milli>
const transform_ms = t2 - t1;
174 std::chrono::duration<double, std::milli>
const fill_data_ms = t3 - t2;
175 std::chrono::duration<double, std::milli>
const fill_misses_ms = t4 - t3;
176 std::chrono::duration<double, std::milli>
const wait_to_touch_ms = t5 - t4;
177 std::chrono::duration<double, std::milli>
const insert_misses_ms = t6 - t5;
178 std::chrono::duration<double, std::milli>
const reset_data_ms = t7 - t6;
179 std::chrono::duration<double, std::milli>
const filter_hits_ms = t8 - t7;
180 std::chrono::duration<double, std::milli>
const insert_hits_ms = t9 - t8;
181 std::chrono::duration<double, std::milli>
const propagate_ms = t10 - t9;
182 std::chrono::duration<double, std::milli>
const total_ms = t10 - t0;
184 std::cout <<
"Remove NaN: " << remove_nan_ms.count() <<
" ms\n";
185 std::cout <<
"Transform: " << transform_ms.count() <<
" ms\n";
186 std::cout <<
"Fill data: " << fill_data_ms.count() <<
" ms\n";
187 std::cout <<
"Fill misses: " << fill_misses_ms.count() <<
" ms\n";
188 std::cout <<
"Wait to touch: " << wait_to_touch_ms.count() <<
" ms\n";
189 std::cout <<
"Insert misses: " << insert_misses_ms.count() <<
" ms\n";
190 std::cout <<
"Reset data: " << reset_data_ms.count() <<
" ms\n";
191 std::cout <<
"Filter hits: " << filter_hits_ms.count() <<
" ms\n";
192 std::cout <<
"Insert hits: " << insert_hits_ms.count() <<
" ms\n";
193 std::cout <<
"Propagate: " << propagate_ms.count() <<
" ms\n";
194 std::cout <<
"Total: " << total_ms.count() <<
" ms\n\n";
197 [[nodiscard]]
constexpr float angularResolution()
const noexcept
199 return 1.0f / angular_resolution_factor_;
202 constexpr void angularResolution(
float resolution)
noexcept
204 angular_resolution_factor_ = 1.0f / resolution;
205 auto const arf = angular_resolution_factor_;
207 assert(0.0f < resolution && 0.0f < angular_resolution_factor_);
209 columns_ =
static_cast<std::uint_fast32_t
>(2.0f * numbers::pi_v<float> * arf) + 1;
211 if constexpr (2 == Dim) {
213 }
else if constexpr (3 == Dim) {
214 rows_ =
static_cast<std::uint_fast32_t
>(numbers::pi_v<float> * arf) + 1;
217 data_.resize(rows_ * columns_);
219 data_2_.resize(rows_ * columns_ / (ds_ * ds_));
222 [[nodiscard]] SensorErrorFun& sensorErrorFunction() {
return sensor_error_f_; }
224 [[nodiscard]] SensorErrorFun
const& sensorErrorFunction()
const
226 return sensor_error_f_;
239 cached_distances_.resize(points.size());
240 ufo::transform(points.begin(), points.end(), cached_distances_.begin(),
241 [&origin](
auto const& p) { return distance(origin, p); });
246 std::enable_if_t<execution::is_execution_policy_v<ExecutionPolicy>,
bool> =
true>
250 cached_distances_.resize(points.size());
251 ufo::transform(std::forward<ExecutionPolicy>(policy), points.begin(), points.end(),
252 cached_distances_.begin(),
253 [&origin](
auto const& p) { return distance(origin, p); });
256 template <
class T,
class... Rest>
298 for (std::size_t i{}; points.size() > i; ++i) {
300 auto dir = p - origin;
301 T dist =
static_cast<T
>(cached_distances_[i]);
303 dist = std::min(
static_cast<T
>(this->max_distance), dist);
305 dist += sensor_error_f_(dist);
306 p = origin + dir * dist;
315 [[nodiscard]] std::int_fast32_t polarIndex(T polar_angle)
const
317 if constexpr (2u == Dim) {
319 }
else if constexpr (3u == Dim) {
320 return static_cast<std::int_fast32_t
>(
321 std::floor(polar_angle * angular_resolution_factor_));
326 [[nodiscard]] std::int_fast32_t azimuthalIndex(T azimuthal_angle)
const
328 azimuthal_angle += numbers::pi_v<T>;
329 return static_cast<std::int_fast32_t
>(
330 std::floor(azimuthal_angle * angular_resolution_factor_));
347 std::enable_if_t<execution::is_execution_policy_v<ExecutionPolicy>,
bool> =
true>
353 assert(0.0f <= sensor_angular_resolution);
354 auto const hsar = sensor_angular_resolution / 2.0f;
356 ufo::for_each(std::forward<ExecutionPolicy>(policy), std::size_t(0), points.size(),
357 [
this, points, &origin, hsar](std::size_t i) {
360 float const radial_distance = cached_distances_[i];
361 float const azimuthal_angle = azimuthalAngle(Vec<2, T>(p));
363 if constexpr (2 == Dim) {
365 }
else if constexpr (3 == Dim) {
366 polar_angle = polarAngle(p.z, radial_distance);
371 auto const min_ai = azimuthalIndex(azimuthal_angle - hsar) + columns_;
372 auto const max_ai = azimuthalIndex(azimuthal_angle + hsar) + columns_;
373 auto const min_pi = polarIndex(polar_angle - hsar) + rows_;
374 auto const max_pi = polarIndex(polar_angle + hsar) + rows_;
376 for (
auto ai = min_ai; max_ai >= ai; ++ai) {
377 auto const first_ai = (ai % columns_) * rows_;
378 for (
auto pi = min_pi; max_pi >= pi; ++pi) {
379 auto index = first_ai + (pi % rows_);
380 assert(data_.size() >
index);
382 std::scoped_lock lock(data.lock);
383 if (0u == data.count || data.distance > radial_distance) {
384 data.distance = radial_distance;
385 data.distance_error = sensor_error_f_(radial_distance);
392 for (std::uint_fast32_t ai{}; columns_ > ai; ++ai) {
393 auto ai_2 = ai / ds_;
394 auto const first_ai = ai * rows_;
395 auto const first_ai_2 = ai_2 * (rows_ / ds_);
396 for (std::uint_fast32_t pi{}; rows_ > pi; ++pi) {
397 auto const pi_2 = pi / ds_;
398 auto const index = first_ai + pi;
399 auto const index_2 = first_ai_2 + pi_2;
400 assert(data_.size() >
index);
402 assert(data_2_.size() > index_2);
403 auto& data = data_[
index];
404 auto& data_2 = data_2_[index_2];
406 if (data.distance < data_2.min_distance) {
407 data_2.min_distance = data.distance;
408 data_2.min_distance_error = data.distance_error;
410 if (data.distance > data_2.max_distance) {
411 data_2.max_distance = data.distance;
412 data_2.max_distance_error = data.distance_error;
414 data_2.count += data.count;
419 void resetData()
const
422 [](
auto const&) { return Data{}; });
424 [](
auto const&) { return Data2{}; });
428 class ExecutionPolicy,
429 std::enable_if_t<execution::is_execution_policy_v<ExecutionPolicy>,
bool> =
true>
430 void resetData(ExecutionPolicy&& policy)
const
433 [](
auto const&) { return Data{}; });
434 ufo::transform(std::forward<ExecutionPolicy>(policy), data_2_.begin(), data_2_.end(),
435 data_2_.begin(), [](
auto const&) { return Data2{}; });
444 template <
class Map,
class T>
445 std::vector<TreeKey<Dim>> missesKeys(
Map const& map, SoAView<Vec<Dim, T>> points,
446 Vec<Dim, T>
const& origin)
const
448 auto const bb = bounds(points, origin);
451 auto const depth = this->miss_depth + 5;
453 auto const min = map.key(TreeCoord<Dim>(
ufo::min(bb), depth));
454 auto const max = map.key(TreeCoord<Dim>(
ufo::max(bb), depth));
456 using key_t =
typename TreeKey<Dim>::Key;
457 auto const diff = (
static_cast<key_t
>(
max) -
static_cast<key_t
>(min)) + 1u;
459 std::vector<TreeKey<Dim>> keys;
460 keys.reserve(std::accumulate(begin(diff), end(diff), 0u, std::multiplies<>()));
462 if constexpr (2 == Dim) {
463 for (
auto k = min;
max.x >= k.x; ++k.x) {
464 for (k.y =
min.y;
max.y >= k.y; ++k.y) {
468 }
else if constexpr (3 == Dim) {
469 for (
auto k = min;
max.x >= k.x; ++k.x) {
470 for (k.y =
min.y;
max.y >= k.y; ++k.y) {
471 for (k.z =
min.z;
max.z >= k.z; ++k.z) {
483 template <
class Map,
class T>
484 void fillMisses(
Map const& map, Vec<Dim, T>
const& origin)
const
490 class ExecutionPolicy,
class Map,
class T,
491 std::enable_if_t<execution::is_execution_policy_v<ExecutionPolicy>,
bool> =
true>
492 void fillMisses(ExecutionPolicy&& policy,
Map const& map, SoAView<Vec<Dim, T>> points,
493 Vec<Dim, T>
const& origin)
const
495 auto const keys = missesKeys(map, points, origin);
501 [
this, &mutex, &size, &map, &origin](
auto const& key) {
502 thread_local std::size_t idx;
503 auto const id = std::this_thread::get_id();
504 if (size <= idx || id != thread_misses_[idx].id) {
505 std::scoped_lock lock(mutex);
507 if (thread_misses_.size() < size) {
508 thread_misses_.emplace_back();
509 assert(thread_misses_.size() == size);
511 thread_misses_[idx].id = id;
514 auto& misses = thread_misses_[idx].misses;
516 auto const code = map.code(key);
517 auto const center = cast<T>(map.center(key)) - origin;
519 fillMissesRecurs(misses, map, code, center);
522 std::vector<std::size_t> offset(size);
523 std::size_t total_size{};
524 for (std::size_t i{}; size > i; ++i) {
525 offset[i] = total_size;
526 total_size += thread_misses_[i].misses.size();
529 std::cout << total_size <<
'\n';
531 cached_misses_.resize(total_size);
532 ufo::for_each(policy, std::size_t(0), size, [
this, &offset](std::size_t i) {
533 auto& data = thread_misses_[i];
534 auto const& m = data.misses;
535 auto const o = offset[i];
537 std::copy(m.begin(), m.end(), cached_misses_.begin() + o);
543 template <
class Map,
class T>
544 std::pair<bool, bool> fillMissesRecurs(std::vector<detail::Miss<Dim>>& misses,
545 Map const& map, TreeCode<Dim>
const& code,
546 Vec<Dim, T>
const& center)
const
548 auto const ca =
abs(center);
549 auto const hl = cast<T>(map.halfLength(code));
551 auto const return_distance =
norm(ca + hl);
552 auto const inner_distance =
norm(ca - hl);
554 auto const [min_polar, max_polar] = polarAngles(center, hl);
555 auto const [min_azimuthal, max_azimuthal] = azimuthalAngles(center, hl);
560 auto const min_ai = azimuthalIndex(min_azimuthal) + columns_;
561 auto const max_ai = azimuthalIndex(max_azimuthal) + columns_;
563 auto const min_pi = polarIndex(min_polar) + rows_;
564 auto const max_pi = polarIndex(max_polar) + rows_;
566 bool valid_return =
false;
567 bool valid_parent =
false;
568 bool valid_void_region =
false;
569 std::uint_fast32_t count{};
571 if (this->miss_depth == code.depth()) {
572 valid_return = validReturn(return_distance, min_ai, max_ai, min_pi, max_pi);
573 valid_void_region = valid_return && validVoidRegion(return_distance, min_ai, max_ai,
577 misses.emplace_back(code, center, count, valid_void_region);
580 valid_parent = validParent(inner_distance, min_ai, max_ai, min_pi, max_pi);
582 valid_parent && validReturn(return_distance, min_ai, max_ai, min_pi, max_pi);
583 valid_void_region = valid_return && validVoidRegion(return_distance, min_ai, max_ai,
586 if (valid_return && valid_void_region) {
587 misses.emplace_back(code, center, count, valid_void_region);
588 }
else if (valid_parent) {
589 auto size_before = misses.size();
591 auto child_code = code.firstborn();
593 valid_void_region =
true;
594 bool any_void_region =
false;
595 for (std::uint_fast32_t i{}; map.branchingFactor() > i; ++i) {
597 fillMissesRecurs(misses, map, child_code.firstbornSibling(i),
598 map.child(TreeCoord<Dim, T>(center, code.depth()), i));
599 valid_return = valid_return && vr;
600 valid_void_region = valid_void_region && vvr;
601 any_void_region = any_void_region || vvr;
604 if (valid_return && (valid_void_region || !any_void_region)) {
605 misses.resize(size_before);
606 misses.emplace_back(code, center, count, valid_void_region);
611 return std::pair{valid_return, valid_void_region};
614 [[nodiscard]]
bool validReturn(
float const distance, std::int_fast32_t min_ai,
615 std::int_fast32_t max_ai, std::int_fast32_t min_pi,
616 std::int_fast32_t max_pi)
const
620 for (
auto ai = min_ai; max_ai >= ai; ++ai) {
621 auto const first_ai = (ai % columns_) * rows_;
622 for (
auto pi = min_pi; max_pi >= pi; ++pi) {
623 auto const& data = data_[first_ai + (pi % rows_)];
624 auto const d = data.distance + data.distance_error;
633 [[nodiscard]]
bool validVoidRegion(
float const distance, std::int_fast32_t min_ai,
634 std::int_fast32_t max_ai, std::int_fast32_t min_pi,
635 std::int_fast32_t max_pi)
const
639 for (
auto ai = min_ai; max_ai >= ai; ++ai) {
640 auto const first_ai = (ai % columns_) * rows_;
641 for (
auto pi = min_pi; max_pi >= pi; ++pi) {
642 auto const& data = data_[first_ai + (pi % rows_)];
643 auto const d = data.distance - data.distance_error;
652 [[nodiscard]]
bool validParent(
float const distance, std::int_fast32_t min_ai,
653 std::int_fast32_t max_ai, std::int_fast32_t min_pi,
654 std::int_fast32_t max_pi)
const
658 for (
auto ai = min_ai; max_ai >= ai; ++ai) {
659 auto const first_ai = (ai % columns_) * rows_;
660 for (
auto pi = min_pi; max_pi >= pi; ++pi) {
661 auto const& data = data_[first_ai + (pi % rows_)];
662 auto const d = data.distance + data.distance_error;
733 [[nodiscard]] T polarAngle(T
const& z, T radial_distance)
const
735 return std::acos(z / radial_distance);
739 [[nodiscard]] T polarAngle(Vec<Dim, T>
const& point)
const
741 if constexpr (2u == Dim) {
743 }
else if constexpr (3u == Dim) {
744 return polarAngle(point.z,
norm(point));
751 [[nodiscard]] T azimuthalAngle(Vec<2, T>
const& point)
const
753 return std::atan2(point.y, point.x);
812 [[nodiscard]] std::pair<T, T> polarAngles(Vec<Dim, T>
const& center,
813 Vec<Dim, T>
const& half_length)
const
815 if constexpr (2 == Dim) {
816 return std::pair{T(0), T(0)};
818 auto const ca =
abs(Vec<2, T>(center));
819 auto const min_z = center.z - half_length.z;
820 auto const max_z = center.z + half_length.z;
825 max = polarAngle(Vec<3, T>(ca + Vec2<T>(half_length), min_z));
826 }
else if (ca.x >= half_length.x || ca.y >= half_length.y) {
827 max = polarAngle(Vec<3, T>(std::max(T(0), ca.x - half_length.x),
828 std::max(T(0), ca.y - half_length.y), min_z));
830 max = numbers::pi_v<float>;
833 min = polarAngle(Vec<3, T>(ca + Vec2<T>(half_length), max_z));
834 }
else if (ca.x >= half_length.x || ca.y >= half_length.y) {
835 min = polarAngle(Vec<3, T>(std::max(T(0), ca.x - half_length.x),
836 std::max(T(0), ca.y - half_length.y), max_z));
841 return std::pair{
min,
max};
855 [[nodiscard]] std::pair<T, T> azimuthalAngles(Vec<Dim, T>
const& center,
856 Vec<Dim, T>
const& half_length)
const
858 auto const ca =
abs(Vec<2, T>(center));
860 Vec<2, int>
const offset{ca.x <= half_length.x ? 0 : (T(0) < center.x ? 1 : -1),
861 ca.y <= half_length.y ? 0 : (T(0) < center.y ? 1 : -1)};
863 if (0 == offset.x && 0 == offset.y) {
864 return std::pair(-numbers::pi_v<T>, numbers::pi_v<T>);
871 if (-1 == offset.x) {
872 min_p.y = center.y + half_length.y;
873 max_p.y = center.y - half_length.y;
874 }
else if (1 == offset.x) {
875 min_p.y = center.y - half_length.y;
876 max_p.y = center.y + half_length.y;
877 }
else if (-1 == offset.y) {
878 min_p.y = center.y + half_length.y;
879 max_p.y = center.y + half_length.y;
881 min_p.y = center.y - half_length.y;
882 max_p.y = center.y - half_length.y;
885 if (-1 == offset.y) {
886 min_p.x = center.x - half_length.x;
887 max_p.x = center.x + half_length.x;
888 }
else if (1 == offset.y) {
889 min_p.x = center.x + half_length.x;
890 max_p.x = center.x - half_length.x;
891 }
else if (-1 == offset.x) {
892 min_p.x = center.x + half_length.x;
893 max_p.x = center.x + half_length.x;
894 a = T(2) * numbers::pi_v<T>;
896 min_p.x = center.x - half_length.x;
897 max_p.x = center.x - half_length.x;
900 return std::pair{azimuthalAngle(min_p), azimuthalAngle(max_p) +
a};
909 template <
class T,
class... Rest>
910 void filterHits(PointCloud<Dim, T, Rest...>& cloud, Vec<Dim, T>
const& origin)
const
913 [&origin, max_sq = max_distance * max_distance](Vec<Dim, T>
const& p)
mutable {
919 class ExecutionPolicy,
class T,
class... Rest,
920 std::enable_if_t<execution::is_execution_policy_v<ExecutionPolicy>,
bool> =
true>
921 void filterHits(ExecutionPolicy&& policy, PointCloud<Dim, T, Rest...>& cloud,
922 Vec<Dim, T>
const& origin)
const
924 filterHits(cloud, origin);
930 float distance_error{};
931 std::uint_fast32_t count{};
938 , distance_error(other.distance_error)
946 distance_error = rhs.distance_error;
953 float min_distance = std::numeric_limits<float>::max();
954 float min_distance_error{};
956 float max_distance = std::numeric_limits<float>::lowest();
957 float max_distance_error{};
959 std::uint_fast32_t count{};
964 std::vector<detail::Miss<Dim>> misses;
973 SensorErrorFun sensor_error_f_;
975 float angular_resolution_factor_{};
977 std::uint_fast32_t rows_{};
978 std::uint_fast32_t columns_{};
979 mutable __block std::vector<Data> data_;
980 std::uint_fast32_t ds_ = 10u;
981 mutable __block std::vector<Data2> data_2_;
983 mutable __block std::vector<float> cached_distances_;
985 mutable __block std::vector<detail::Miss<Dim>> cached_misses_;
987 mutable __block std::deque<ThreadMisses> thread_misses_;