Skip to content

Commit c300cb0

Browse files
committed
Forcibly split infinite clusters (360° obstacles; e.g. tunnels or wall of room)
Previously the clustering crashed because the ring buffer became full
1 parent b487b09 commit c300cb0

File tree

1 file changed

+69
-12
lines changed

1 file changed

+69
-12
lines changed

src/clustering/continuous_clustering.cpp

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -723,12 +723,22 @@ void ContinuousClustering::associatePointsInColumn(AssociationJob&& job)
723723
{
724724
if (point.tree_root_.column_index == -1)
725725
{
726-
// this point is not associated to any tree -> associate it if the resulting cluster will be
727-
// not wider than one full rotation
726+
// this point is not associated to any point tree -> associate it
727+
728+
// Furthermore, we also have to check that clusters are forcibly published when they span a
729+
// full rotation. For this we take two measures: (1) don't associate this point to the point
730+
// tree when it would cover more than one rotation; (2) don't associate this point to the
731+
// point tree, when its cluster is considered to be finished. Normally, the latter can not
732+
// happen because a cluster is only considered to be finished when no more point can be
733+
// associated (LiDAR rotated far enough away from a cluster's point trees). It can only
734+
// happen when a cluster is forcibly considered to be finished because it already spans a
735+
// full rotation. Then we do not want to associate more points to it.
728736
Point& point_root = range_image_[point_other.tree_root_.column_index * num_rows_ +
729737
point_other.tree_root_.row_index];
730-
uint32_t new_cluster_width = point.global_column_index - point_root.global_column_index;
731-
if (new_cluster_width < num_columns_)
738+
uint32_t new_cluster_width = point.global_column_index - point_root.global_column_index + 1;
739+
bool point_tree_is_smaller_than_one_rotation = new_cluster_width <= num_columns_; // (1)
740+
bool point_tree_is_finished_forcibly = point_root.belongs_to_finished_cluster; // (2)
741+
if (point_tree_is_smaller_than_one_rotation && !point_tree_is_finished_forcibly)
732742
{
733743
point.tree_root_ = point_other.tree_root_;
734744
point.tree_id = point_root.global_column_index * num_rows_ + point_root.row_index;
@@ -755,9 +765,17 @@ void ContinuousClustering::associatePointsInColumn(AssociationJob&& job)
755765
Point& point_root_other = range_image_[point_other.tree_root_.column_index * num_rows_ +
756766
point_other.tree_root_.row_index];
757767

758-
// add mutual link
759-
point_root.associated_trees.insert(point_other.tree_root_);
760-
point_root_other.associated_trees.insert(point.tree_root_);
768+
// similar to above (unassociated point is added to point tree) we also don't want to
769+
// introduce links between point trees when either of them was forcibly finished because its
770+
// corresponding cluster already covers a full rotation
771+
bool neither_cluster_was_forcibly_finished = !point_root.belongs_to_finished_cluster &&
772+
!point_root_other.belongs_to_finished_cluster;
773+
if (neither_cluster_was_forcibly_finished)
774+
{
775+
// add mutual link
776+
point_root.associated_trees.insert(point_other.tree_root_);
777+
point_root_other.associated_trees.insert(point.tree_root_);
778+
}
761779
}
762780
}
763781

@@ -825,16 +843,20 @@ void ContinuousClustering::findFinishedTreesAndAssignSameId(TreeCombinationJob&&
825843
std::list<std::list<RangeImageIndex>> trees_per_finished_cluster;
826844
std::list<uint64_t> finished_cluster_ids;
827845

846+
// run breath-first search (BFS) on each unpublished point tree. If a BFS from a specific starting node completes
847+
// without finding an unfinished point tree, then these point trees form a finished cluster.
828848
std::list<RangeImageIndex> trees_collected_for_current_cluster;
829849
std::list<RangeImageIndex> trees_to_visit_for_current_cluster;
830850
for (RangeImageIndex& tree_root_index : sc_unfinished_point_trees_)
831851
{
832852
Point& point_root = range_image_[tree_root_index.column_index * num_rows_ + tree_root_index.row_index];
833853
if (point_root.visited_at_continuous_azimuth_angle == job.current_minimum_continuous_azimuth_angle)
834-
continue; // this tree was already visited
854+
continue; // this point tree was already visited, no need to use it as start node for BFS
835855
trees_collected_for_current_cluster.clear();
836856
trees_to_visit_for_current_cluster.clear();
837857
trees_to_visit_for_current_cluster.emplace_back(tree_root_index);
858+
int64_t min_column_index = std::numeric_limits<int64_t>::max();
859+
int64_t max_column_index = 0;
838860
uint32_t cluster_num_points = 0;
839861
bool cluster_has_at_least_one_unfinished_tree = false;
840862
while (!trees_to_visit_for_current_cluster.empty())
@@ -843,12 +865,34 @@ void ContinuousClustering::findFinishedTreesAndAssignSameId(TreeCombinationJob&&
843865
trees_to_visit_for_current_cluster.pop_front();
844866
Point& cur_point_root =
845867
range_image_[cur_tree_root_index.column_index * num_rows_ + cur_tree_root_index.row_index];
868+
869+
// The following condition is always false under normal circumstances. It can only be true when a cluster in
870+
// the current graph was forcibly finished (exceeds full rotation) and when there was a race condition with
871+
// the association. In this case we simply ignore that a link between current point tree and the previous
872+
// (forcibly finished) point tree was found
873+
if (cur_point_root.belongs_to_finished_cluster)
874+
continue;
875+
876+
// keep track of the column range occupied by this cluster (to detect clusters larger than a full
877+
// rotation)
878+
min_column_index = std::min(min_column_index, cur_point_root.global_column_index);
879+
max_column_index =
880+
std::max(max_column_index, cur_point_root.global_column_index + cur_point_root.cluster_width);
881+
882+
// check if cluster is finished because LiDAR rotated far enough away
846883
if (cur_point_root.finished_at_continuous_azimuth_angle > job.current_minimum_continuous_azimuth_angle)
847884
cluster_has_at_least_one_unfinished_tree = true;
885+
886+
// check if this point tree was already visited. This can happen because the undirected graph (where each
887+
// node represents a point tree) is traversed by breath-first search.
848888
if (cur_point_root.visited_at_continuous_azimuth_angle == job.current_minimum_continuous_azimuth_angle)
849-
continue; // cluster was already visited
850-
// cur_point_root.id = cluster_counter_;
889+
continue;
890+
891+
// mark that this node (point tree) was already traversed. Instead of a boolean flag we use a monotonically
892+
// rising value that does not need to be cleared
851893
cur_point_root.visited_at_continuous_azimuth_angle = job.current_minimum_continuous_azimuth_angle;
894+
895+
// add this point tree to the current cluster
852896
trees_collected_for_current_cluster.push_back(cur_tree_root_index);
853897
cluster_num_points += cur_point_root.tree_num_points;
854898

@@ -857,16 +901,29 @@ void ContinuousClustering::findFinishedTreesAndAssignSameId(TreeCombinationJob&&
857901
{
858902
Point& other_point_root =
859903
range_image_[other_tree_root_index.column_index * num_rows_ + other_tree_root_index.row_index];
904+
// if neighbor point tree was not already visited then traverse it too
860905
if (other_point_root.visited_at_continuous_azimuth_angle !=
861906
job.current_minimum_continuous_azimuth_angle)
862907
trees_to_visit_for_current_cluster.push_back(other_tree_root_index);
863908
}
864909
}
865910

866-
if (trees_collected_for_current_cluster.empty() || cluster_has_at_least_one_unfinished_tree)
911+
// check if finished point trees together already exceed one rotation
912+
bool finished_point_trees_exceed_one_rotation = false;
913+
if (max_column_index - min_column_index >= num_columns_)
914+
{
915+
std::cout << "Found a cluster exceeding one rotation: " << (max_column_index - min_column_index) << " ("
916+
<< min_column_index << ", " << max_column_index << ")" << std::endl;
917+
finished_point_trees_exceed_one_rotation = true;
918+
}
919+
920+
// skip cluster if at least one point tree is not finished (but not if we already have a full rotation)
921+
if ((trees_collected_for_current_cluster.empty() || cluster_has_at_least_one_unfinished_tree) &&
922+
!finished_point_trees_exceed_one_rotation)
867923
continue;
868924

869-
// we found a completely finished cluster; now we mark its trees finished and prepare them for publishing
925+
// we found an actually (or forcibly) finished cluster; now we mark its trees finished and prepare them for
926+
// publishing
870927
for (const RangeImageIndex& cur_tree_root_index : trees_collected_for_current_cluster)
871928
{
872929
// mark current tree root as finished

0 commit comments

Comments
 (0)