@@ -723,12 +723,22 @@ void ContinuousClustering::associatePointsInColumn(AssociationJob&& job)
723
723
{
724
724
if (point.tree_root_ .column_index == -1 )
725
725
{
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.
728
736
Point& point_root = range_image_[point_other.tree_root_ .column_index * num_rows_ +
729
737
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)
732
742
{
733
743
point.tree_root_ = point_other.tree_root_ ;
734
744
point.tree_id = point_root.global_column_index * num_rows_ + point_root.row_index ;
@@ -755,9 +765,17 @@ void ContinuousClustering::associatePointsInColumn(AssociationJob&& job)
755
765
Point& point_root_other = range_image_[point_other.tree_root_ .column_index * num_rows_ +
756
766
point_other.tree_root_ .row_index ];
757
767
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
+ }
761
779
}
762
780
}
763
781
@@ -825,16 +843,20 @@ void ContinuousClustering::findFinishedTreesAndAssignSameId(TreeCombinationJob&&
825
843
std::list<std::list<RangeImageIndex>> trees_per_finished_cluster;
826
844
std::list<uint64_t > finished_cluster_ids;
827
845
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.
828
848
std::list<RangeImageIndex> trees_collected_for_current_cluster;
829
849
std::list<RangeImageIndex> trees_to_visit_for_current_cluster;
830
850
for (RangeImageIndex& tree_root_index : sc_unfinished_point_trees_)
831
851
{
832
852
Point& point_root = range_image_[tree_root_index.column_index * num_rows_ + tree_root_index.row_index ];
833
853
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
835
855
trees_collected_for_current_cluster.clear ();
836
856
trees_to_visit_for_current_cluster.clear ();
837
857
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 ;
838
860
uint32_t cluster_num_points = 0 ;
839
861
bool cluster_has_at_least_one_unfinished_tree = false ;
840
862
while (!trees_to_visit_for_current_cluster.empty ())
@@ -843,12 +865,34 @@ void ContinuousClustering::findFinishedTreesAndAssignSameId(TreeCombinationJob&&
843
865
trees_to_visit_for_current_cluster.pop_front ();
844
866
Point& cur_point_root =
845
867
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
846
883
if (cur_point_root.finished_at_continuous_azimuth_angle > job.current_minimum_continuous_azimuth_angle )
847
884
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.
848
888
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
851
893
cur_point_root.visited_at_continuous_azimuth_angle = job.current_minimum_continuous_azimuth_angle ;
894
+
895
+ // add this point tree to the current cluster
852
896
trees_collected_for_current_cluster.push_back (cur_tree_root_index);
853
897
cluster_num_points += cur_point_root.tree_num_points ;
854
898
@@ -857,16 +901,29 @@ void ContinuousClustering::findFinishedTreesAndAssignSameId(TreeCombinationJob&&
857
901
{
858
902
Point& other_point_root =
859
903
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
860
905
if (other_point_root.visited_at_continuous_azimuth_angle !=
861
906
job.current_minimum_continuous_azimuth_angle )
862
907
trees_to_visit_for_current_cluster.push_back (other_tree_root_index);
863
908
}
864
909
}
865
910
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)
867
923
continue ;
868
924
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
870
927
for (const RangeImageIndex& cur_tree_root_index : trees_collected_for_current_cluster)
871
928
{
872
929
// mark current tree root as finished
0 commit comments