LCOV - code coverage report
Current view: top level - src/perception/filters/ray_ground_classifier_nodes/src - ray_ground_classifier_cloud_node.cpp (source / functions) Hit Total Coverage
Test: lcov.total.filtered Lines: 72 108 66.7 %
Date: 2023-03-03 05:44:19 Functions: 3 3 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 147 662 22.2 %

           Branch data     Line data    Source code
       1                 :            : // Copyright 2018-2020 the Autoware Foundation, Arm Limited
       2                 :            : //
       3                 :            : // Licensed under the Apache License, Version 2.0 (the "License");
       4                 :            : // you may not use this file except in compliance with the License.
       5                 :            : // You may obtain a copy of the License at
       6                 :            : //
       7                 :            : //     http://www.apache.org/licenses/LICENSE-2.0
       8                 :            : //
       9                 :            : // Unless required by applicable law or agreed to in writing, software
      10                 :            : // distributed under the License is distributed on an "AS IS" BASIS,
      11                 :            : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12                 :            : // See the License for the specific language governing permissions and
      13                 :            : // limitations under the License.
      14                 :            : //
      15                 :            : // Co-developed by Tier IV, Inc. and Apex.AI, Inc.
      16                 :            : #include <common/types.hpp>
      17                 :            : #include <lidar_utils/point_cloud_utils.hpp>
      18                 :            : #include <ray_ground_classifier_nodes/ray_ground_classifier_cloud_node.hpp>
      19                 :            : #include <rclcpp/rclcpp.hpp>
      20                 :            : #include <rclcpp_components/register_node_macro.hpp>
      21                 :            : #include <stdlib.h>
      22                 :            : 
      23                 :            : #include <limits>
      24                 :            : #include <string>
      25                 :            : 
      26                 :            : namespace autoware
      27                 :            : {
      28                 :            : namespace perception
      29                 :            : {
      30                 :            : namespace filters
      31                 :            : {
      32                 :            : namespace ray_ground_classifier_nodes
      33                 :            : {
      34                 :            : ////////////////////////////////////////////////////////////////////////////////
      35                 :            : using autoware::common::types::PointXYZI;
      36                 :            : using autoware::common::types::float32_t;
      37                 :            : using autoware::perception::filters::ray_ground_classifier::PointPtrBlock;
      38                 :            : 
      39                 :            : using std::placeholders::_1;
      40                 :            : 
      41                 :            : using autoware::common::lidar_utils::has_intensity_and_throw_if_no_xyz;
      42                 :            : 
      43                 :            : using autoware::common::lidar_utils::CloudModifier;
      44                 :            : 
      45                 :          5 : RayGroundClassifierCloudNode::RayGroundClassifierCloudNode(
      46                 :          5 :   const rclcpp::NodeOptions & node_options)
      47                 :            : : Node("ray_ground_classifier", node_options),
      48   [ +  -  -  - ]:          5 :   m_classifier(ray_ground_classifier::Config{
      49   [ +  -  +  -  :         10 :           static_cast<float32_t>(declare_parameter("classifier.sensor_height_m").get<float32_t>()),
                   +  - ]
      50                 :            :           static_cast<float32_t>(declare_parameter(
      51   [ +  -  +  -  :         10 :             "classifier.max_local_slope_deg").get<float32_t>()),
          +  -  +  -  +  
                -  -  - ]
      52                 :            :           static_cast<float32_t>(declare_parameter(
      53   [ +  -  +  -  :         10 :             "classifier.max_global_slope_deg").get<float32_t>()),
          +  -  +  -  +  
                -  -  - ]
      54                 :            :           static_cast<float32_t>(declare_parameter(
      55   [ +  -  +  -  :         10 :             "classifier.nonground_retro_thresh_deg").get<float32_t>()),
          +  -  +  -  +  
                -  -  - ]
      56                 :            :           static_cast<float32_t>(declare_parameter(
      57   [ +  -  +  -  :         10 :             "classifier.min_height_thresh_m").get<float32_t>()),
          +  -  +  -  +  
                -  -  - ]
      58                 :            :           static_cast<float32_t>(declare_parameter(
      59   [ +  -  +  -  :         10 :             "classifier.max_global_height_thresh_m").get<float32_t>()),
          +  -  +  -  +  
                -  -  - ]
      60                 :            :           static_cast<float32_t>(declare_parameter(
      61   [ +  -  +  -  :         10 :             "classifier.max_last_local_ground_thresh_m").get<float32_t>()),
          +  -  +  -  +  
                -  -  - ]
      62                 :            :           static_cast<float32_t>(declare_parameter(
      63   [ +  -  +  -  :         15 :             "classifier.max_provisional_ground_distance_m").get<float32_t>())
          +  -  +  -  +  
             -  +  -  -  
                      - ]
      64                 :            :         }),
      65   [ +  -  -  - ]:          5 :   m_aggregator(ray_ground_classifier::RayAggregator::Config{
      66                 :            :           static_cast<float32_t>(declare_parameter(
      67   [ +  -  +  -  :         10 :             "aggregator.min_ray_angle_rad").get<float32_t>()),
                   +  - ]
      68                 :            :           static_cast<float32_t>(declare_parameter(
      69   [ +  -  +  -  :         10 :             "aggregator.max_ray_angle_rad").get<float32_t>()),
          +  -  +  -  +  
                -  -  - ]
      70   [ +  -  +  -  :         10 :           static_cast<float32_t>(declare_parameter("aggregator.ray_width_rad").get<float32_t>()),
          +  -  +  -  +  
                -  -  - ]
      71                 :            :           static_cast<std::size_t>(
      72   [ +  -  +  -  :         15 :             declare_parameter("aggregator.max_ray_points").get<std::size_t>())
          +  -  +  -  +  
             -  +  -  -  
                      - ]
      73                 :            :         }),
      74   [ +  -  +  -  :         10 :   m_pcl_size(static_cast<uint32_t>(declare_parameter("pcl_size").get<uint32_t>())),
             +  -  +  - ]
      75   [ +  -  +  -  :         10 :   m_frame_id(declare_parameter("frame_id").get<std::string>().c_str()),
                   +  - ]
      76                 :            :   m_has_failed(false),
      77   [ +  -  +  -  :         15 :   m_timeout(std::chrono::milliseconds{declare_parameter("cloud_timeout_ms").get<uint16_t>()}),
             +  -  +  - ]
      78                 :            :   m_raw_sub_ptr(create_subscription<PointCloud2>(
      79                 :            :       "points_in",
      80         [ +  - ]:          5 :       rclcpp::QoS(10), std::bind(&RayGroundClassifierCloudNode::callback, this, _1))),
      81                 :            :   m_ground_pub_ptr(create_publisher<PointCloud2>(
      82         [ +  - ]:          5 :       "points_ground", rclcpp::QoS(10))),
      83                 :            :   m_nonground_pub_ptr(create_publisher<PointCloud2>(
      84   [ +  -  +  -  :         80 :       "points_nonground", rclcpp::QoS(10)))
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          -  -  -  -  -  
                -  -  - ]
      85                 :            : {
      86                 :            :   // initialize messages
      87         [ +  - ]:          5 :   CloudModifier{
      88         [ +  - ]:          5 :     m_ground_msg, m_frame_id}.reserve(m_pcl_size);
      89   [ +  -  -  - ]:          5 :   CloudModifier{
      90         [ +  - ]:          5 :     m_nonground_msg, m_frame_id}.reserve(m_pcl_size);
      91                 :          5 : }
      92                 :            : ////////////////////////////////////////////////////////////////////////////////
      93                 :            : void
      94                 :         97 : RayGroundClassifierCloudNode::callback(const PointCloud2::SharedPtr msg)
      95                 :            : {
      96                 :         97 :   PointXYZIF pt_tmp;
      97                 :         97 :   pt_tmp.id = static_cast<uint16_t>(PointXYZIF::END_OF_SCAN_ID);
      98                 :         97 :   const ray_ground_classifier::PointXYZIFR eos_pt{&pt_tmp};
      99                 :            : 
     100                 :            :   try {
     101         [ +  - ]:         97 :     CloudModifier ground_msg_modifier{m_ground_msg};
     102                 :            :     CloudModifier nonground_msg_modifier{
     103         [ +  - ]:         97 :       m_nonground_msg};
     104                 :            : 
     105                 :            :     // Reset messages and aggregator to ensure they are in a good state
     106         [ +  - ]:         97 :     reset();
     107                 :            :     // Verify header
     108         [ +  + ]:         97 :     if (msg->header.frame_id != m_ground_msg.header.frame_id) {
     109                 :            :       throw std::runtime_error(
     110                 :            :               "RayGroundClassifierCloudNode: raw topic from unexpected "
     111   [ +  -  -  +  :        190 :               "frame (expected '" + m_ground_msg.header.frame_id +
                   -  - ]
     112   [ +  -  +  -  :        380 :               "', got '" + msg->header.frame_id + "')");
          +  -  +  -  -  
          +  -  +  -  -  
                   -  - ]
     113                 :            :     }
     114                 :            :     // Verify the consistency of PointCloud msg
     115                 :          2 :     const auto data_length = msg->width * msg->height * msg->point_step;
     116   [ +  -  -  + ]:          2 :     if ((msg->data.size() != msg->row_step) || (data_length != msg->row_step)) {
     117         [ #  # ]:          0 :       throw std::runtime_error("RayGroundClassifierCloudNode: Malformed PointCloud2");
     118                 :            :     }
     119                 :            :     // Verify the point cloud format and assign correct point_step
     120   [ +  -  -  + ]:          2 :     if (!has_intensity_and_throw_if_no_xyz(msg)) {
     121   [ #  #  #  #  :          0 :       RCLCPP_WARN(
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                      # ]
     122                 :            :         this->get_logger(),
     123                 :            :         "RayGroundClassifierNode Warning: PointCloud doesn't have intensity field");
     124                 :            :     }
     125                 :            :     // Harvest timestamp
     126                 :          2 :     m_nonground_msg.header.stamp = msg->header.stamp;
     127                 :          2 :     m_ground_msg.header.stamp = msg->header.stamp;
     128                 :            :     // Add all points to aggregator
     129                 :            :     // Iterate through the data, but skip intensity in case the point cloud does not have it.
     130                 :            :     // For example:
     131                 :            :     //
     132                 :            :     // point_step = 4
     133                 :            :     // x y z i a b c x y z i a b c
     134                 :            :     // ^------       ^------
     135                 :            :     size_t num_ready = 0;
     136                 :            :     bool8_t has_encountered_unknown_exception = false;
     137                 :            :     bool8_t abort = false;
     138                 :            : 
     139                 :          2 :     std::size_t point_step = msg->point_step;
     140         [ +  + ]:         22 :     for (std::size_t idx = 0U; idx < msg->data.size(); idx += point_step) {
     141         [ -  + ]:         20 :       if (abort) {
     142                 :          0 :         continue;
     143                 :            :       }
     144                 :            :       try {
     145                 :            :         PointXYZIF * pt;
     146                 :            :         // TODO(c.ho) Fix below deviation after #2131 is in
     147                 :            :         //lint -e{925, 9110} Need to convert pointers and use bit for external API NOLINT
     148                 :            :         pt = reinterpret_cast<PointXYZIF *>(&msg->data[idx]);
     149                 :            :         // don't bother inserting the points almost (0,0).
     150                 :            :         // Too many of those makes the bin 0 overflow
     151         [ -  + ]:         20 :         if ((fabsf(pt->x) > std::numeric_limits<decltype(pt->x)>::epsilon()) ||
     152         [ #  # ]:          0 :           (fabsf(pt->y) > std::numeric_limits<decltype(pt->y)>::epsilon()))
     153                 :            :         {
     154   [ +  -  -  + ]:         20 :           if (!m_aggregator.insert(pt)) {
     155         [ #  # ]:          0 :             m_aggregator.end_of_scan();
     156                 :            :           }
     157                 :            :         } else {
     158   [ #  #  #  #  :          0 :           nonground_msg_modifier.push_back(PointXYZI{pt->x, pt->y, pt->z, pt->intensity});
                      # ]
     159                 :            :         }
     160                 :          0 :       } catch (const std::runtime_error & e) {
     161                 :          0 :         m_has_failed = true;
     162   [ -  -  -  -  :          0 :         RCLCPP_INFO(this->get_logger(), e.what());
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                      - ]
     163                 :            :         abort = true;
     164                 :          0 :       } catch (const std::exception & e) {
     165                 :          0 :         m_has_failed = true;
     166   [ -  -  -  -  :          0 :         RCLCPP_INFO(this->get_logger(), e.what());
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                      - ]
     167                 :            :         abort = true;
     168         [ -  - ]:          0 :       } catch (...) {
     169   [ -  -  -  -  :          0 :         RCLCPP_INFO(
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                      - ]
     170                 :            :           this->get_logger(),
     171                 :            :           "RayGroundClassifierCloudNode has encountered an unknown failure");
     172                 :            :         abort = true;
     173                 :            :         has_encountered_unknown_exception = true;
     174                 :            :       }
     175                 :            :     }
     176                 :            : 
     177                 :            :     // if abort, we skip all remaining the parallel work to be able to return/throw
     178         [ +  - ]:          2 :     if (!abort) {
     179         [ +  - ]:          2 :       m_aggregator.end_of_scan();
     180         [ +  - ]:          2 :       num_ready = m_aggregator.get_ready_ray_count();
     181                 :            : 
     182                 :            :       // Partition each ray
     183         [ +  + ]:         22 :       for (size_t i = 0; i < num_ready; i++) {
     184         [ -  + ]:         20 :         if (abort) {
     185                 :          0 :           continue;
     186                 :            :         }
     187                 :            :         // Note: if an exception occurs in this loop, the aggregator can get into a bad state
     188                 :            :         // (e.g. overrun capacity)
     189                 :            :         PointPtrBlock ground_blk;
     190                 :            :         PointPtrBlock nonground_blk;
     191                 :            :         try {
     192   [ +  -  +  - ]:         20 :           auto ray = m_aggregator.get_next_ray();
     193                 :            :           // partition: should never fail, guaranteed to have capacity via other checks
     194         [ +  - ]:         20 :           m_classifier.partition(ray, ground_blk, nonground_blk);
     195                 :            : 
     196                 :            :           // Add ray to point clouds
     197         [ +  + ]:         40 :           for (auto & ground_point : ground_blk) {
     198                 :         20 :             ground_msg_modifier.push_back(
     199                 :         20 :               PointXYZI{
     200         [ +  - ]:         20 :                       ground_point->x, ground_point->y, ground_point->z, ground_point->intensity});
     201                 :            :           }
     202         [ -  + ]:         20 :           for (auto & nonground_point : nonground_blk) {
     203                 :          0 :             nonground_msg_modifier.push_back(
     204         [ #  # ]:          0 :               PointXYZI{
     205                 :          0 :                       nonground_point->x, nonground_point->y, nonground_point->z,
     206         [ #  # ]:          0 :                       nonground_point->intensity});
     207                 :            :           }
     208                 :          0 :         } catch (const std::runtime_error & e) {
     209                 :          0 :           m_has_failed = true;
     210   [ -  -  -  -  :          0 :           RCLCPP_INFO(this->get_logger(), e.what());
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                      - ]
     211                 :            :           abort = true;
     212                 :          0 :         } catch (const std::exception & e) {
     213                 :          0 :           m_has_failed = true;
     214   [ -  -  -  -  :          0 :           RCLCPP_INFO(this->get_logger(), e.what());
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                      - ]
     215                 :            :           abort = true;
     216         [ -  - ]:          0 :         } catch (...) {
     217   [ -  -  -  -  :          0 :           RCLCPP_INFO(
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                      - ]
     218                 :            :             this->get_logger(),
     219                 :            :             "RayGroundClassifierCloudNode has encountered an unknown failure");
     220                 :            :           abort = true;
     221                 :            :           has_encountered_unknown_exception = true;
     222                 :            :         }
     223                 :            :       }
     224                 :            :     }
     225                 :            : 
     226         [ -  + ]:          2 :     if (has_encountered_unknown_exception) {
     227                 :          0 :       m_has_failed = true;
     228   [ -  -  -  -  :         95 :       RCLCPP_INFO(
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
             -  +  -  - ]
     229                 :            :         this->get_logger(),
     230                 :            :         "RayGroundClassifierCloudNode has encountered an unknown failure");
     231                 :          0 :       throw;
     232                 :            :     }
     233                 :            : 
     234         [ -  + ]:          2 :     if (abort) {
     235                 :          0 :       return;
     236                 :            :     }
     237                 :            : 
     238                 :            :     // publish: nonground first for the possible microseconds of latency
     239         [ +  - ]:          2 :     m_nonground_pub_ptr->publish(m_nonground_msg);
     240         [ +  - ]:          2 :     m_ground_pub_ptr->publish(m_ground_msg);
     241                 :        190 :   } catch (const std::runtime_error & e) {
     242                 :         95 :     m_has_failed = true;
     243   [ -  +  -  -  :        475 :     RCLCPP_INFO(this->get_logger(), e.what());
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     244                 :          0 :   } catch (const std::exception & e) {
     245                 :          0 :     m_has_failed = true;
     246   [ -  -  -  -  :          0 :     RCLCPP_INFO(this->get_logger(), e.what());
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                      - ]
     247                 :          0 :   } catch (...) {
     248   [ -  -  -  -  :          0 :     RCLCPP_INFO(
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                      - ]
     249                 :            :       this->get_logger(),
     250                 :            :       "RayGroundClassifierCloudNode has encountered an unknown failure");
     251                 :          0 :     throw;
     252                 :            :   }
     253                 :            : }
     254                 :            : ////////////////////////////////////////////////////////////////////////////////
     255                 :         97 : void RayGroundClassifierCloudNode::reset()
     256                 :            : {
     257                 :            :   // reset aggregator: Needed in case an error is thrown during partitioning of cloud
     258                 :            :   //                   which would lead to filled rays and overflow during next callback
     259                 :         97 :   m_aggregator.reset();
     260                 :            :   // reset messages
     261                 :         97 :   CloudModifier modifier1{m_ground_msg};
     262                 :            :   modifier1.clear();
     263                 :            : 
     264                 :         97 :   CloudModifier modifier2{m_nonground_msg};
     265                 :            :   modifier2.clear();
     266                 :         97 : }
     267                 :            : }  // namespace ray_ground_classifier_nodes
     268                 :            : }  // namespace filters
     269                 :            : }  // namespace perception
     270                 :            : }  // namespace autoware
     271                 :            : 
     272                 :            : RCLCPP_COMPONENTS_REGISTER_NODE(
     273                 :            :   autoware::perception::filters::ray_ground_classifier_nodes::RayGroundClassifierCloudNode)

Generated by: LCOV version 1.14