From 306d8a2e2ac6d305b13ebf5eaffedcf272d9161f Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 19 Sep 2024 11:40:38 +0900 Subject: [PATCH 001/115] chore: rebase main Signed-off-by: vividf --- .../CMakeLists.txt | 15 +- .../concatenate_and_time_sync_node.param.yaml | 18 + .../concatenate_data/cloud_collector.hpp | 117 +++ .../combine_cloud_handler.hpp | 134 +++ ...hpp => concatenate_and_time_sync_node.hpp} | 147 ++-- .../concatenate_and_time_sync_node.launch.xml | 11 + .../cocatenate_and_time_sync_node.schema.json | 98 +++ .../src/concatenate_data/cloud_collector.cpp | 157 ++++ .../combine_cloud_handler.cpp | 301 +++++++ .../concatenate_and_time_sync_node.cpp | 495 +++++++++++ .../concatenate_and_time_sync_nodelet.cpp | 717 ---------------- .../test/test_cloud_collector.cpp | 468 ++++++++++ .../test/test_concatenate_node.py | 797 ++++++++++++++++++ 13 files changed, 2670 insertions(+), 805 deletions(-) create mode 100644 sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml create mode 100644 sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp create mode 100644 sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp rename sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/{concatenate_and_time_sync_nodelet.hpp => concatenate_and_time_sync_node.hpp} (62%) create mode 100644 sensing/autoware_pointcloud_preprocessor/launch/concatenate_and_time_sync_node.launch.xml create mode 100644 sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json create mode 100644 sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp create mode 100644 sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp create mode 100644 sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp delete mode 100644 sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_nodelet.cpp create mode 100644 sensing/autoware_pointcloud_preprocessor/test/test_cloud_collector.cpp create mode 100644 sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node.py diff --git a/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt b/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt index 02ce2a0098220..8998c1aa38ad7 100644 --- a/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt +++ b/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt @@ -62,7 +62,9 @@ ament_target_dependencies(faster_voxel_grid_downsample_filter ) ament_auto_add_library(pointcloud_preprocessor_filter SHARED - src/concatenate_data/concatenate_and_time_sync_nodelet.cpp + src/concatenate_data/concatenate_and_time_sync_node.cpp + src/concatenate_data/combine_cloud_handler.cpp + src/concatenate_data/cloud_collector.cpp src/concatenate_data/concatenate_pointclouds.cpp src/time_synchronizer/time_synchronizer_node.cpp src/crop_box_filter/crop_box_filter_nodelet.cpp @@ -109,7 +111,7 @@ rclcpp_components_register_node(pointcloud_preprocessor_filter # ========== Concatenate and Sync data ========== rclcpp_components_register_node(pointcloud_preprocessor_filter PLUGIN "autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent" - EXECUTABLE concatenate_data_node) + EXECUTABLE concatenate_and_time_sync_node) # ========== CropBox ========== rclcpp_components_register_node(pointcloud_preprocessor_filter @@ -241,8 +243,17 @@ if(BUILD_TESTING) test/test_distortion_corrector_node.cpp ) + ament_add_gtest(test_cloud_collector + test/test_cloud_collector.cpp + ) + target_link_libraries(test_utilities pointcloud_preprocessor_filter) target_link_libraries(test_distortion_corrector_node pointcloud_preprocessor_filter) + target_link_libraries(test_cloud_collector pointcloud_preprocessor_filter) + add_ros_test( + test/test_concatenate_node.py + TIMEOUT "50" + ) endif() diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml new file mode 100644 index 0000000000000..d023479acf6e3 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml @@ -0,0 +1,18 @@ +/**: + ros__parameters: + maximum_queue_size: 5 + timeout_sec: 0.2 + is_motion_compensated: true + publish_synchronized_pointcloud: true + keep_input_frame_in_synchronized_pointcloud: true + publish_previous_but_late_pointcloud: false + synchronized_pointcloud_postfix: pointcloud + input_twist_topic_type: twist + input_topics: [ + "/sensing/lidar/left/pointcloud_before_sync", + "/sensing/lidar/right/pointcloud_before_sync", + "/sensing/lidar/top/pointcloud_before_sync" + ] + output_frame: base_link + lidar_timestamp_offsets: [0.0, 0.04, 0.08] + lidar_timestamp_noise_window: [0.01, 0.01, 0.01] diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp new file mode 100644 index 0000000000000..29085472579c0 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -0,0 +1,117 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ + * + */ + +#ifndef AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CLOUD_COLLECTOR_HPP_ +#define AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CLOUD_COLLECTOR_HPP_ + +#include "combine_cloud_handler.hpp" + +#include +#include +#include +#include +#include +#include + +namespace autoware::pointcloud_preprocessor +{ + +class PointCloudConcatenateDataSynchronizerComponent; +class CombineCloudHandler; + +class CloudCollector +{ +public: + CloudCollector( + std::shared_ptr concatenate_node, + std::list> & collectors, + std::shared_ptr combine_cloud_handler, int num_of_clouds, double time); + + void setReferenceTimeStamp(double timestamp, double noise_window); + std::tuple getReferenceTimeStampBoundary(); + void processCloud(std::string topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud); + void concatenateCallback(); + std::tuple< + sensor_msgs::msg::PointCloud2::SharedPtr, + std::unordered_map, + std::unordered_map> + concatenateClouds( + std::unordered_map topic_to_cloud_map); + + void publishClouds( + sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr, + std::unordered_map + topic_to_transformed_cloud_map, + std::unordered_map topic_to_original_stamp_map); + + void deleteCollector(); + + std::unordered_map + get_topic_to_cloud_map(); + +private: + std::shared_ptr concatenate_node_; + std::list> & collectors_; + std::shared_ptr combine_cloud_handler_; + rclcpp::TimerBase::SharedPtr timer_; + std::unordered_map topic_to_cloud_map_; + uint64_t num_of_clouds_; + double timeout_sec_; + double reference_timestamp_min_; + double reference_timestamp_max_; + std::mutex mutex_; +}; + +} // namespace autoware::pointcloud_preprocessor + +// clang-format off +#endif // AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CLOUD_COLLECTOR_HPP_ // NOLINT +// clang-format on diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp new file mode 100644 index 0000000000000..1e51e072928e3 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -0,0 +1,134 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ + * + */ + +#ifndef AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__COMBINE_CLOUD_HANDLER_HPP_ +#define AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__COMBINE_CLOUD_HANDLER_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include + +// ROS includes +#include "autoware_point_types/types.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace autoware::pointcloud_preprocessor +{ + +class CombineCloudHandler +{ +private: + rclcpp::Node * node_; + tf2_ros::Buffer tf_buffer_; + tf2_ros::TransformListener tf_listener_; + + std::vector input_topics_; + std::string output_frame_; + bool is_motion_compensated_; + bool keep_input_frame_in_synchronized_pointcloud_; + + struct RclcppTimeHash_ + { + std::size_t operator()(const rclcpp::Time & t) const + { + return std::hash()(t.nanoseconds()); + } + }; + +public: + std::deque twist_ptr_queue_; + + CombineCloudHandler( + rclcpp::Node * node, std::vector input_topics, std::string output_frame, + bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud); + void processTwist(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input); + void processOdometry(const nav_msgs::msg::Odometry::ConstSharedPtr & input); + + std::tuple< + sensor_msgs::msg::PointCloud2::SharedPtr, + std::unordered_map, + std::unordered_map> + combinePointClouds(std::unordered_map & + topic_to_cloud_map_); + + Eigen::Matrix4f computeTransformToAdjustForOldTimestamp( + const rclcpp::Time & old_stamp, const rclcpp::Time & new_stamp); +}; + +} // namespace autoware::pointcloud_preprocessor + +// clang-format off +#endif // AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__COMBINE_CLOUD_HANDLER_HPP_ // NOLINT +// clang-format on diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_nodelet.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp similarity index 62% rename from sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_nodelet.hpp rename to sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 977adc59cd7e5..1f653b19fdb6a 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_nodelet.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -49,19 +49,22 @@ * */ -#ifndef AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODELET_HPP_ -#define AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODELET_HPP_ +#ifndef AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODE_HPP_ +#define AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODE_HPP_ #include -#include +#include #include #include #include #include +#include #include // ROS includes #include "autoware_point_types/types.hpp" +#include "cloud_collector.hpp" +#include "combine_cloud_handler.hpp" #include #include @@ -83,115 +86,87 @@ #include #include #include -#include -#include namespace autoware::pointcloud_preprocessor { + using autoware_point_types::PointXYZIRC; using point_cloud_msg_wrapper::PointCloud2Modifier; -/** \brief @b PointCloudConcatenateDataSynchronizerComponent is a special form of data - * synchronizer: it listens for a set of input PointCloud messages on the same topic, - * checks their timestamps, and concatenates their fields together into a single - * PointCloud output message. - * \author Radu Bogdan Rusu - */ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node { public: - typedef sensor_msgs::msg::PointCloud2 PointCloud2; - - /** \brief constructor. */ explicit PointCloudConcatenateDataSynchronizerComponent(const rclcpp::NodeOptions & node_options); - - /** \brief constructor. - * \param queue_size the maximum queue size - */ - PointCloudConcatenateDataSynchronizerComponent( - const rclcpp::NodeOptions & node_options, int queue_size); - - /** \brief Empty destructor. */ virtual ~PointCloudConcatenateDataSynchronizerComponent() {} + void publishClouds( + sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr, + std::unordered_map & + topic_to_transformed_cloud_map, + std::unordered_map & topic_to_original_stamp_map, + double reference_timestamp_min, double reference_timestamp_max); private: - /** \brief The output PointCloud publisher. */ - rclcpp::Publisher::SharedPtr pub_output_; - /** \brief Delay Compensated PointCloud publisher*/ - std::map::SharedPtr> - transformed_raw_pc_publisher_map_; - - /** \brief The maximum number of messages that we can store in the queue. */ - int maximum_queue_size_ = 3; - - double timeout_sec_ = 0.1; - - bool publish_synchronized_pointcloud_; - bool keep_input_frame_in_synchronized_pointcloud_; - std::string synchronized_pointcloud_postfix_; - - std::set not_subscribed_topic_names_; - - /** \brief A vector of subscriber. */ - std::vector::SharedPtr> filters_; - - rclcpp::Subscription::SharedPtr sub_twist_; - rclcpp::Subscription::SharedPtr sub_odom_; - - rclcpp::TimerBase::SharedPtr timer_; - diagnostic_updater::Updater updater_{this}; - - const std::string input_twist_topic_type_; - - /** \brief Output TF frame the concatenated points should be transformed to. */ - std::string output_frame_; - - /** \brief The flag to indicate if only static TF are used. */ - bool has_static_tf_only_; - - /** \brief Input point cloud topics. */ - // XmlRpc::XmlRpcValue input_topics_; - std::vector input_topics_; - - std::unique_ptr managed_tf_buffer_{nullptr}; - - std::deque twist_ptr_queue_; - - std::map cloud_stdmap_; - std::map cloud_stdmap_tmp_; + struct Parameters + { + int maximum_queue_size; + double timeout_sec; + bool is_motion_compensated; + bool publish_synchronized_pointcloud; + bool keep_input_frame_in_synchronized_pointcloud; + bool publish_previous_but_late_pointcloud; + std::string synchronized_pointcloud_postfix; + std::string input_twist_topic_type; + std::vector input_topics; + std::string output_frame; + std::vector lidar_timestamp_offsets; + std::vector lidar_timestamp_noise_window; + } params_; + + double current_concat_cloud_timestamp_{0.0}; + double lastest_concat_cloud_timestamp_{0.0}; + bool drop_previous_but_late_pointcloud_{false}; + bool publish_pointcloud_{false}; + double diagnostic_reference_timestamp_min_{0.0}; + double diagnostic_reference_timestamp_max_{0.0}; + std::unordered_map diagnostic_topic_to_original_stamp_map_; + + std::shared_ptr combine_cloud_handler_; + std::shared_ptr cloud_collector_; + std::list> cloud_collectors_; std::mutex mutex_; + std::unordered_map topic_to_offset_map_; + std::unordered_map topic_to_noise_window_map_; + + // subscribers + std::vector::SharedPtr> pointcloud_subs; + rclcpp::Subscription::SharedPtr twist_sub_; + rclcpp::Subscription::SharedPtr odom_sub_; + + // publishers + rclcpp::Publisher::SharedPtr concatenate_cloud_publisher_; + std::unordered_map::SharedPtr> + topic_to_transformed_cloud_publisher_map_; + std::unique_ptr debug_publisher_; - std::vector input_offset_; - std::map offset_map_; - - Eigen::Matrix4f computeTransformToAdjustForOldTimestamp( - const rclcpp::Time & old_stamp, const rclcpp::Time & new_stamp); - std::map combineClouds( - sensor_msgs::msg::PointCloud2::SharedPtr & concat_cloud_ptr); - void publish(); + std::unique_ptr> stop_watch_ptr_; + diagnostic_updater::Updater updater_{this}; - void convertToXYZIRCCloud( - const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, - sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr); - void setPeriod(const int64_t new_period); void cloud_callback( - const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_ptr, - const std::string & topic_name); + const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, const std::string & topic_name); void twist_callback(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr input); void odom_callback(const nav_msgs::msg::Odometry::ConstSharedPtr input); - void timer_callback(); + std::string formatTimestamp(double timestamp); void checkConcatStatus(diagnostic_updater::DiagnosticStatusWrapper & stat); std::string replaceSyncTopicNamePostfix( const std::string & original_topic_name, const std::string & postfix); - - /** \brief processing time publisher. **/ - std::unique_ptr> stop_watch_ptr_; - std::unique_ptr debug_publisher_; + void convertToXYZIRCCloud( + const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, + sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr); }; } // namespace autoware::pointcloud_preprocessor // clang-format off -#endif // AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODELET_HPP_ // NOLINT +#endif // AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODE_HPP_ // NOLINT // clang-format on diff --git a/sensing/autoware_pointcloud_preprocessor/launch/concatenate_and_time_sync_node.launch.xml b/sensing/autoware_pointcloud_preprocessor/launch/concatenate_and_time_sync_node.launch.xml new file mode 100644 index 0000000000000..aa9579305dfb8 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/launch/concatenate_and_time_sync_node.launch.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json new file mode 100644 index 0000000000000..059411e02ab92 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json @@ -0,0 +1,98 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Parameters for Concatenate and Time Synchronize Node", + "type": "object", + "definitions": { + "concatenate_and_time_sync_node": { + "type": "object", + "properties": { + "maximum_queue_size": { + "type": "integer", + "default": 5, + "description": "Maximum size of the queue." + }, + "timeout_sec": { + "type": "number", + "default": 0.0, + "description": "Timeout in seconds." + }, + "offset_tolerance": { + "type": "number", + "default": 0.0, + "description": "Tolerance for offset." + }, + "is_motion_compensated": { + "type": "boolean", + "default": true, + "description": "Flag to indicate if motion compensation is enabled." + }, + "publish_synchronized_pointcloud": { + "type": "boolean", + "default": true, + "description": "Flag to indicate if synchronized point cloud should be published." + }, + "keep_input_frame_in_synchronized_pointcloud": { + "type": "boolean", + "default": true, + "description": "Flag to indicate if input frame should be kept in synchronized point cloud." + }, + "synchronized_pointcloud_postfix": { + "type": "string", + "default": "pointcloud", + "description": "Postfix for the synchronized point cloud." + }, + "input_twist_topic_type": { + "type": "string", + "default": "twist", + "description": "Type of the input twist topic." + }, + "input_topics": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "description": "List of input topics." + }, + "output_frame": { + "type": "string", + "default": "", + "description": "Output frame." + }, + "lidar_timestamp_offsets": { + "type": "array", + "items": { + "type": "number" + }, + "default": [], + "description": "List of LiDAR timestamp offsets." + } + }, + "required": [ + "maximum_queue_size", + "timeout_sec", + "offset_tolerance", + "is_motion_compensated", + "publish_synchronized_pointcloud", + "keep_input_frame_in_synchronized_pointcloud", + "synchronized_pointcloud_postfix", + "input_twist_topic_type", + "input_topics", + "output_frame", + "lidar_timestamp_offsets" + ] + } + }, + "properties": { + "/**": { + "type": "object", + "properties": { + "ros__parameters": { + "$ref": "#/definitions/concatenate_and_time_sync_node" + } + }, + "required": ["ros__parameters"] + } + }, + "required": ["/**"] +} diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp new file mode 100644 index 0000000000000..28ae3fc85dc12 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -0,0 +1,157 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ + * + */ + +#include "autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp" + +#include "autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp" +#include "autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp" + +#include + +#include + +namespace autoware::pointcloud_preprocessor +{ + +CloudCollector::CloudCollector( + std::shared_ptr concatenate_node, + std::list> & collectors, + std::shared_ptr combine_cloud_handler, int num_of_clouds, double timeout_sec) +: concatenate_node_(concatenate_node), + collectors_(collectors), + combine_cloud_handler_(combine_cloud_handler), + num_of_clouds_(num_of_clouds), + timeout_sec_(timeout_sec) +{ + const auto period_ns = std::chrono::duration_cast( + std::chrono::duration(timeout_sec_)); + + timer_ = rclcpp::create_timer( + concatenate_node_, concatenate_node_->get_clock(), period_ns, + std::bind(&CloudCollector::concatenateCallback, this)); +} + +void CloudCollector::setReferenceTimeStamp(double timestamp, double noise_window) +{ + reference_timestamp_max_ = timestamp + noise_window; + reference_timestamp_min_ = timestamp - noise_window; +} + +std::tuple CloudCollector::getReferenceTimeStampBoundary() +{ + return std::make_tuple(reference_timestamp_min_, reference_timestamp_max_); +} + +void CloudCollector::processCloud( + std::string topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud) +{ + // Check if the map already contains an entry for the same topic, shouldn't happened if the + // parameter set correctly. + if (topic_to_cloud_map_.find(topic_name) != topic_to_cloud_map_.end()) { + RCLCPP_WARN( + concatenate_node_->get_logger(), + "Topic '%s' already exists in the collector. Check the timestamp of the pointcloud.", + topic_name.c_str()); + } + topic_to_cloud_map_[topic_name] = cloud; + if (topic_to_cloud_map_.size() == num_of_clouds_) { + concatenateCallback(); + } +} + +void CloudCollector::concatenateCallback() +{ + // lock for protecting collector list and concatenated pointcloud + std::lock_guard lock(mutex_); + auto [concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map] = + concatenateClouds(topic_to_cloud_map_); + publishClouds(concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map); + deleteCollector(); +} + +std::tuple< + sensor_msgs::msg::PointCloud2::SharedPtr, + std::unordered_map, + std::unordered_map> +CloudCollector::concatenateClouds( + std::unordered_map topic_to_cloud_map) +{ + return combine_cloud_handler_->combinePointClouds(topic_to_cloud_map); +} + +void CloudCollector::publishClouds( + sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr, + std::unordered_map + topic_to_transformed_cloud_map, + std::unordered_map topic_to_original_stamp_map) +{ + concatenate_node_->publishClouds( + concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map, + reference_timestamp_min_, reference_timestamp_max_); +} + +void CloudCollector::deleteCollector() +{ + auto it = std::find_if( + collectors_.begin(), collectors_.end(), + [this](const std::shared_ptr & collector) { return collector.get() == this; }); + if (it != collectors_.end()) { + collectors_.erase(it); + } +} + +std::unordered_map +CloudCollector::get_topic_to_cloud_map() +{ + return topic_to_cloud_map_; +} + +} // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp new file mode 100644 index 0000000000000..11985a4c8720d --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -0,0 +1,301 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ + * + */ + +#include "autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp" + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace autoware::pointcloud_preprocessor +{ + +CombineCloudHandler::CombineCloudHandler( + rclcpp::Node * node, std::vector input_topics, std::string output_frame, + bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud) +: node_(node), + tf_buffer_(node_->get_clock()), + tf_listener_(tf_buffer_), + input_topics_(input_topics), + output_frame_(output_frame), + is_motion_compensated_(is_motion_compensated), + keep_input_frame_in_synchronized_pointcloud_(keep_input_frame_in_synchronized_pointcloud) +{ +} + +void CombineCloudHandler::processTwist( + const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input) +{ + // if rosbag restart, clear buffer + if (!twist_ptr_queue_.empty()) { + if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > rclcpp::Time(input->header.stamp)) { + twist_ptr_queue_.clear(); + } + } + + // pop old data + while (!twist_ptr_queue_.empty()) { + if ( + rclcpp::Time(twist_ptr_queue_.front()->header.stamp) + rclcpp::Duration::from_seconds(1.0) > + rclcpp::Time(input->header.stamp)) { + break; + } + twist_ptr_queue_.pop_front(); + } + + auto twist_ptr = std::make_shared(); + twist_ptr->header = input->header; + twist_ptr->twist = input->twist.twist; + twist_ptr_queue_.push_back(twist_ptr); +} + +void CombineCloudHandler::processOdometry(const nav_msgs::msg::Odometry::ConstSharedPtr & input) +{ + // if rosbag restart, clear buffer + if (!twist_ptr_queue_.empty()) { + if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > rclcpp::Time(input->header.stamp)) { + twist_ptr_queue_.clear(); + } + } + + // pop old data + while (!twist_ptr_queue_.empty()) { + if ( + rclcpp::Time(twist_ptr_queue_.front()->header.stamp) + rclcpp::Duration::from_seconds(1.0) > + rclcpp::Time(input->header.stamp)) { + break; + } + twist_ptr_queue_.pop_front(); + } + + auto twist_ptr = std::make_shared(); + twist_ptr->header = input->header; + twist_ptr->twist = input->twist.twist; + twist_ptr_queue_.push_back(twist_ptr); +} + +std::tuple< + sensor_msgs::msg::PointCloud2::SharedPtr, + std::unordered_map, + std::unordered_map> +CombineCloudHandler::combinePointClouds( + std::unordered_map & topic_to_cloud_map) +{ + sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr = nullptr; + std::unordered_map + topic_to_transformed_cloud_map; + std::unordered_map topic_to_original_stamp_map; + + std::vector pc_stamps; + for (const auto & pair : topic_to_cloud_map) { + pc_stamps.push_back(rclcpp::Time(pair.second->header.stamp)); + } + std::sort(pc_stamps.begin(), pc_stamps.end(), std::greater()); + const auto oldest_stamp = pc_stamps.back(); + + std::unordered_map transform_memo; + + for (const auto & pair : topic_to_cloud_map) { + std::string topic = pair.first; + sensor_msgs::msg::PointCloud2::SharedPtr cloud = pair.second; + + auto transformed_cloud_ptr = std::make_shared(); + if (output_frame_ != cloud->header.frame_id) { + if (!pcl_ros::transformPointCloud( + output_frame_, *cloud, *transformed_cloud_ptr, tf_buffer_)) { + RCLCPP_ERROR( + node_->get_logger(), + "Transform pointcloud from %s to %s failed, Please check the defined output frame.", + cloud->header.frame_id.c_str(), output_frame_.c_str()); + transformed_cloud_ptr = cloud; + } + } else { + transformed_cloud_ptr = cloud; + } + + topic_to_original_stamp_map[topic] = rclcpp::Time(cloud->header.stamp).seconds(); + + auto transformed_delay_compensated_cloud_ptr = + std::make_shared(); + + if (is_motion_compensated_) { + Eigen::Matrix4f adjust_to_old_data_transform = Eigen::Matrix4f::Identity(); + rclcpp::Time current_cloud_stamp = rclcpp::Time(cloud->header.stamp); + for (const auto & stamp : pc_stamps) { + if (stamp >= current_cloud_stamp) continue; + + Eigen::Matrix4f new_to_old_transform; + if (transform_memo.find(stamp) != transform_memo.end()) { + new_to_old_transform = transform_memo[stamp]; + } else { + new_to_old_transform = + computeTransformToAdjustForOldTimestamp(stamp, current_cloud_stamp); + transform_memo[stamp] = new_to_old_transform; + } + adjust_to_old_data_transform = new_to_old_transform * adjust_to_old_data_transform; + current_cloud_stamp = stamp; + } + pcl_ros::transformPointCloud( + adjust_to_old_data_transform, *transformed_cloud_ptr, + *transformed_delay_compensated_cloud_ptr); + + } else { + transformed_delay_compensated_cloud_ptr = transformed_cloud_ptr; + } + + // concatenate + if (concatenate_cloud_ptr == nullptr) { + concatenate_cloud_ptr = + std::make_shared(*transformed_delay_compensated_cloud_ptr); + } else { + pcl::concatenatePointCloud( + *concatenate_cloud_ptr, *transformed_delay_compensated_cloud_ptr, *concatenate_cloud_ptr); + } + + // convert to original sensor frame if necessary + bool need_transform_to_sensor_frame = (cloud->header.frame_id != output_frame_); + if (keep_input_frame_in_synchronized_pointcloud_ && need_transform_to_sensor_frame) { + sensor_msgs::msg::PointCloud2::SharedPtr transformed_cloud_ptr_in_sensor_frame( + new sensor_msgs::msg::PointCloud2()); + pcl_ros::transformPointCloud( + (std::string)cloud->header.frame_id, *transformed_delay_compensated_cloud_ptr, + *transformed_cloud_ptr_in_sensor_frame, tf_buffer_); + transformed_cloud_ptr_in_sensor_frame->header.stamp = oldest_stamp; + transformed_cloud_ptr_in_sensor_frame->header.frame_id = cloud->header.frame_id; + topic_to_transformed_cloud_map[topic] = transformed_cloud_ptr_in_sensor_frame; + } else { + transformed_delay_compensated_cloud_ptr->header.stamp = oldest_stamp; + transformed_delay_compensated_cloud_ptr->header.frame_id = output_frame_; + topic_to_transformed_cloud_map[topic] = transformed_delay_compensated_cloud_ptr; + } + } + concatenate_cloud_ptr->header.stamp = oldest_stamp; + + return std::make_tuple( + concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map); +} + +Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( + const rclcpp::Time & old_stamp, const rclcpp::Time & new_stamp) +{ + // return identity if no twist is available + if (twist_ptr_queue_.empty()) { + RCLCPP_WARN_STREAM_THROTTLE( + node_->get_logger(), *node_->get_clock(), std::chrono::milliseconds(10000).count(), + "No twist is available. Please confirm twist topic and timestamp"); + return Eigen::Matrix4f::Identity(); + } + + auto old_twist_ptr_it = std::lower_bound( + std::begin(twist_ptr_queue_), std::end(twist_ptr_queue_), old_stamp, + [](const geometry_msgs::msg::TwistStamped::ConstSharedPtr & x_ptr, const rclcpp::Time & t) { + return rclcpp::Time(x_ptr->header.stamp) < t; + }); + old_twist_ptr_it = + old_twist_ptr_it == twist_ptr_queue_.end() ? (twist_ptr_queue_.end() - 1) : old_twist_ptr_it; + + auto new_twist_ptr_it = std::lower_bound( + std::begin(twist_ptr_queue_), std::end(twist_ptr_queue_), new_stamp, + [](const geometry_msgs::msg::TwistStamped::ConstSharedPtr & x_ptr, const rclcpp::Time & t) { + return rclcpp::Time(x_ptr->header.stamp) < t; + }); + new_twist_ptr_it = + new_twist_ptr_it == twist_ptr_queue_.end() ? (twist_ptr_queue_.end() - 1) : new_twist_ptr_it; + + auto prev_time = old_stamp; + double x = 0.0; + double y = 0.0; + double yaw = 0.0; + tf2::Quaternion baselink_quat{}; + for (auto twist_ptr_it = old_twist_ptr_it; twist_ptr_it != new_twist_ptr_it + 1; ++twist_ptr_it) { + const double dt = + (twist_ptr_it != new_twist_ptr_it) + ? (rclcpp::Time((*twist_ptr_it)->header.stamp) - rclcpp::Time(prev_time)).seconds() + : (rclcpp::Time(new_stamp) - rclcpp::Time(prev_time)).seconds(); + + if (std::fabs(dt) > 0.1) { + RCLCPP_WARN_STREAM_THROTTLE( + node_->get_logger(), *node_->get_clock(), std::chrono::milliseconds(10000).count(), + "Time difference is too large. Cloud not interpolate. Please confirm twist topic and " + "timestamp"); + break; + } + + const double dis = (*twist_ptr_it)->twist.linear.x * dt; + yaw += (*twist_ptr_it)->twist.angular.z * dt; + x += dis * std::cos(yaw); + y += dis * std::sin(yaw); + prev_time = (*twist_ptr_it)->header.stamp; + } + + Eigen::Matrix4f transformation_matrix = Eigen::Matrix4f::Identity(); + + float cos_yaw = std::cos(yaw); + float sin_yaw = std::sin(yaw); + + transformation_matrix(0, 3) = x; + transformation_matrix(1, 3) = y; + transformation_matrix(0, 0) = cos_yaw; + transformation_matrix(0, 1) = -sin_yaw; + transformation_matrix(1, 0) = sin_yaw; + transformation_matrix(1, 1) = cos_yaw; + + return transformation_matrix; +} + +} // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp new file mode 100644 index 0000000000000..475a203319727 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -0,0 +1,495 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ + * + */ + +#include "autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp" + +#include "autoware/pointcloud_preprocessor/utility/memory.hpp" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_SYNC_TOPIC_POSTFIX \ + "_synchronized" // default postfix name for synchronized pointcloud + +namespace autoware::pointcloud_preprocessor +{ + +PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchronizerComponent( + const rclcpp::NodeOptions & node_options) +: Node("point_cloud_concatenator_component", node_options) +{ + // initialize debug tool + using autoware::universe_utils::DebugPublisher; + using autoware::universe_utils::StopWatch; + stop_watch_ptr_ = std::make_unique>(); + debug_publisher_ = std::make_unique(this, "concatenate_data_synchronizer"); + stop_watch_ptr_->tic("cyclic_time"); + stop_watch_ptr_->tic("processing_time"); + + // initialize parameters + params_.maximum_queue_size = declare_parameter("maximum_queue_size"); + params_.timeout_sec = declare_parameter("timeout_sec"); + params_.is_motion_compensated = declare_parameter("is_motion_compensated"); + params_.publish_synchronized_pointcloud = + declare_parameter("publish_synchronized_pointcloud"); + params_.keep_input_frame_in_synchronized_pointcloud = + declare_parameter("keep_input_frame_in_synchronized_pointcloud"); + params_.publish_previous_but_late_pointcloud = + declare_parameter("publish_previous_but_late_pointcloud"); + params_.synchronized_pointcloud_postfix = + declare_parameter("synchronized_pointcloud_postfix"); + params_.input_twist_topic_type = declare_parameter("input_twist_topic_type"); + params_.input_topics = declare_parameter>("input_topics"); + params_.output_frame = declare_parameter("output_frame"); + params_.lidar_timestamp_offsets = + declare_parameter>("lidar_timestamp_offsets"); + params_.lidar_timestamp_noise_window = + declare_parameter>("lidar_timestamp_noise_window"); + + if (params_.input_topics.empty()) { + RCLCPP_ERROR(get_logger(), "Need a 'input_topics' parameter to be set before continuing!"); + return; + } else if (params_.input_topics.size() == 1) { + RCLCPP_ERROR(get_logger(), "Only one topic given. Need at least two topics to continue."); + return; + } + + if (params_.output_frame.empty()) { + RCLCPP_ERROR(get_logger(), "Need an 'output_frame' parameter to be set before continuing!"); + return; + } + if (params_.lidar_timestamp_offsets.size() != params_.input_topics.size()) { + RCLCPP_ERROR( + get_logger(), "The number of topics does not match the number of timestamp offsets"); + return; + } + if (params_.lidar_timestamp_noise_window.size() != params_.input_topics.size()) { + RCLCPP_ERROR( + get_logger(), "The number of topics does not match the number of timestamp noise windwo"); + return; + } + + for (size_t i = 0; i < params_.input_topics.size(); i++) { + topic_to_offset_map_[params_.input_topics[i]] = params_.lidar_timestamp_offsets[i]; + topic_to_noise_window_map_[params_.input_topics[i]] = params_.lidar_timestamp_noise_window[i]; + } + + // Publishers + concatenate_cloud_publisher_ = this->create_publisher( + "output", rclcpp::SensorDataQoS().keep_last(params_.maximum_queue_size)); + + // Transformed Raw PointCloud2 Publisher to publish the transformed pointcloud + if (params_.publish_synchronized_pointcloud) { + for (auto & topic : params_.input_topics) { + std::string new_topic = + replaceSyncTopicNamePostfix(topic, params_.synchronized_pointcloud_postfix); + auto publisher = this->create_publisher( + new_topic, rclcpp::SensorDataQoS().keep_last(params_.maximum_queue_size)); + topic_to_transformed_cloud_publisher_map_.insert({topic, publisher}); + } + } + + // Subscribers + if (params_.input_twist_topic_type == "twist") { + twist_sub_ = this->create_subscription( + "~/input/twist", rclcpp::QoS{100}, + std::bind( + &PointCloudConcatenateDataSynchronizerComponent::twist_callback, this, + std::placeholders::_1)); + } else if (params_.input_twist_topic_type == "odom") { + odom_sub_ = this->create_subscription( + "~/input/odom", rclcpp::QoS{100}, + std::bind( + &PointCloudConcatenateDataSynchronizerComponent::odom_callback, this, + std::placeholders::_1)); + } else { + RCLCPP_ERROR_STREAM( + get_logger(), "input_twist_topic_type is invalid: " << params_.input_twist_topic_type); + throw std::runtime_error( + "input_twist_topic_type is invalid: " + params_.input_twist_topic_type); + } + + pointcloud_subs.resize(params_.input_topics.size()); + for (size_t topic_id = 0; topic_id < params_.input_topics.size(); ++topic_id) { + std::function callback = std::bind( + &PointCloudConcatenateDataSynchronizerComponent::cloud_callback, this, std::placeholders::_1, + params_.input_topics[topic_id]); + + pointcloud_subs[topic_id].reset(); + pointcloud_subs[topic_id] = this->create_subscription( + params_.input_topics[topic_id], rclcpp::SensorDataQoS().keep_last(params_.maximum_queue_size), + callback); + } + RCLCPP_DEBUG_STREAM( + get_logger(), + "Subscribing to " << params_.input_topics.size() << " user given topics as inputs:"); + for (auto & input_topic : params_.input_topics) { + RCLCPP_DEBUG_STREAM(get_logger(), " - " << input_topic); + } + + // Cloud handler + combine_cloud_handler_ = std::make_shared( + this, params_.input_topics, params_.output_frame, params_.is_motion_compensated, + params_.keep_input_frame_in_synchronized_pointcloud); + + // Diagnostic Updater + updater_.setHardwareID("concatenate_data_checker"); + updater_.add( + "concat_status", this, &PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus); +} + +std::string PointCloudConcatenateDataSynchronizerComponent::replaceSyncTopicNamePostfix( + const std::string & original_topic_name, const std::string & postfix) +{ + std::string replaced_topic_name; + // separate the topic name by '/' and replace the last element with the new postfix + size_t pos = original_topic_name.find_last_of("/"); + if (pos == std::string::npos) { + // not found '/': this is not a namespaced topic + RCLCPP_WARN_STREAM( + get_logger(), + "The topic name is not namespaced. The postfix will be added to the end of the topic name."); + return original_topic_name + postfix; + } else { + // replace the last element with the new postfix + replaced_topic_name = original_topic_name.substr(0, pos) + "/" + postfix; + } + + // if topic name is the same with original topic name, add postfix to the end of the topic name + if (replaced_topic_name == original_topic_name) { + RCLCPP_WARN_STREAM( + get_logger(), "The topic name " + << original_topic_name + << " have the same postfix with synchronized pointcloud. We use " + "the postfix " + "to the end of the topic name."); + replaced_topic_name = original_topic_name + DEFAULT_SYNC_TOPIC_POSTFIX; + } + return replaced_topic_name; +} + +void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( + const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, const std::string & topic_name) +{ + stop_watch_ptr_->toc("processing_time", true); + if (!utils::is_data_layout_compatible_with_point_xyzirc(*input_ptr)) { + RCLCPP_ERROR( + get_logger(), "The pointcloud layout is not compatible with PointXYZIRC. Aborting"); + + if (utils::is_data_layout_compatible_with_point_xyzi(*input_ptr)) { + RCLCPP_ERROR( + get_logger(), + "The pointcloud layout is compatible with PointXYZI. You may be using legacy code/data"); + } + + return; + } + + sensor_msgs::msg::PointCloud2::SharedPtr xyzirc_input_ptr(new sensor_msgs::msg::PointCloud2()); + auto input = std::make_shared(*input_ptr); + if (input->data.empty()) { + RCLCPP_WARN_STREAM_THROTTLE( + this->get_logger(), *this->get_clock(), 1000, "Empty sensor points!"); + return; + } else { + // convert to XYZIRC pointcloud if pointcloud is not empty + convertToXYZIRCCloud(input, xyzirc_input_ptr); + } + + // protect cloud collectors list + std::unique_lock lock(mutex_); + + // For each callback, check whether there is a exist collector that matches this cloud + bool collector_found = false; + + if (!cloud_collectors_.empty()) { + for (const auto & cloud_collector : cloud_collectors_) { + auto [reference_timestamp_min, reference_timestamp_max] = + cloud_collector->getReferenceTimeStampBoundary(); + + if ( + rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] < + reference_timestamp_max + topic_to_noise_window_map_[topic_name] && + rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] > + reference_timestamp_min - topic_to_noise_window_map_[topic_name]) { + lock.unlock(); + cloud_collector->processCloud(topic_name, input_ptr); + collector_found = true; + break; + } + } + } + + // if point cloud didn't find matched collector, create a new collector. + if (!collector_found) { + auto new_cloud_collector = std::make_shared( + std::dynamic_pointer_cast(shared_from_this()), + cloud_collectors_, combine_cloud_handler_, params_.input_topics.size(), params_.timeout_sec); + + cloud_collectors_.push_back(new_cloud_collector); + lock.unlock(); + new_cloud_collector->setReferenceTimeStamp( + rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name], + topic_to_noise_window_map_[topic_name]); + new_cloud_collector->processCloud(topic_name, input_ptr); + } +} + +void PointCloudConcatenateDataSynchronizerComponent::twist_callback( + const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr input) +{ + combine_cloud_handler_->processTwist(input); +} + +void PointCloudConcatenateDataSynchronizerComponent::odom_callback( + const nav_msgs::msg::Odometry::ConstSharedPtr input) +{ + combine_cloud_handler_->processOdometry(input); +} + +void PointCloudConcatenateDataSynchronizerComponent::publishClouds( + sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr, + std::unordered_map & + topic_to_transformed_cloud_map, + std::unordered_map & topic_to_original_stamp_map, + double reference_timestamp_min, double reference_timestamp_max) +{ + // should never come to this state. + if (concatenate_cloud_ptr == nullptr) return; + + current_concat_cloud_timestamp_ = rclcpp::Time(concatenate_cloud_ptr->header.stamp).seconds(); + + if ( + current_concat_cloud_timestamp_ < lastest_concat_cloud_timestamp_ && + !params_.publish_previous_but_late_pointcloud) { + drop_previous_but_late_pointcloud_ = true; + } else { + publish_pointcloud_ = true; + lastest_concat_cloud_timestamp_ = current_concat_cloud_timestamp_; + auto concat_output = std::make_unique(*concatenate_cloud_ptr); + concatenate_cloud_publisher_->publish(std::move(concat_output)); + // publish transformed raw pointclouds + for (const auto & pair : topic_to_transformed_cloud_map) { + if (pair.second) { + if (params_.publish_synchronized_pointcloud) { + auto transformed_cloud_output = + std::make_unique(*pair.second); + topic_to_transformed_cloud_publisher_map_[pair.first]->publish( + std::move(transformed_cloud_output)); + } + } else { + RCLCPP_WARN( + this->get_logger(), "transformed_raw_points[%s] is nullptr, skipping pointcloud publish.", + pair.first.c_str()); + } + } + } + + diagnostic_reference_timestamp_min_ = reference_timestamp_min; + diagnostic_reference_timestamp_max_ = reference_timestamp_max; + diagnostic_topic_to_original_stamp_map_ = topic_to_original_stamp_map; + updater_.force_update(); + + // add processing time for debug + if (debug_publisher_) { + const double cyclic_time_ms = stop_watch_ptr_->toc("cyclic_time", true); + const double processing_time_ms = stop_watch_ptr_->toc("processing_time", true); + debug_publisher_->publish( + "debug/cyclic_time_ms", cyclic_time_ms); + debug_publisher_->publish( + "debug/processing_time_ms", processing_time_ms); + + for (const auto & pair : topic_to_transformed_cloud_map) { + if (pair.second != nullptr) { + const auto pipeline_latency_ms = + std::chrono::duration( + std::chrono::nanoseconds( + (this->get_clock()->now() - pair.second->header.stamp).nanoseconds())) + .count(); + debug_publisher_->publish( + "debug" + pair.first + "/pipeline_latency_ms", pipeline_latency_ms); + } + } + } +} + +void PointCloudConcatenateDataSynchronizerComponent::convertToXYZIRCCloud( + const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, + sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr) +{ + output_ptr->header = input_ptr->header; + + PointCloud2Modifier output_modifier{ + *output_ptr, input_ptr->header.frame_id}; + output_modifier.reserve(input_ptr->width); + + bool has_valid_intensity = + std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](auto & field) { + return field.name == "intensity" && field.datatype == sensor_msgs::msg::PointField::UINT8; + }); + + bool has_valid_return_type = + std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](auto & field) { + return field.name == "return_type" && field.datatype == sensor_msgs::msg::PointField::UINT8; + }); + + bool has_valid_channel = + std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](auto & field) { + return field.name == "channel" && field.datatype == sensor_msgs::msg::PointField::UINT16; + }); + + sensor_msgs::PointCloud2Iterator it_x(*input_ptr, "x"); + sensor_msgs::PointCloud2Iterator it_y(*input_ptr, "y"); + sensor_msgs::PointCloud2Iterator it_z(*input_ptr, "z"); + + if (has_valid_intensity && has_valid_return_type && has_valid_channel) { + sensor_msgs::PointCloud2Iterator it_i(*input_ptr, "intensity"); + sensor_msgs::PointCloud2Iterator it_r(*input_ptr, "return_type"); + sensor_msgs::PointCloud2Iterator it_c(*input_ptr, "channel"); + + for (; it_x != it_x.end(); ++it_x, ++it_y, ++it_z, ++it_i, ++it_r, ++it_c) { + PointXYZIRC point; + point.x = *it_x; + point.y = *it_y; + point.z = *it_z; + point.intensity = *it_i; + point.return_type = *it_r; + point.channel = *it_c; + output_modifier.push_back(std::move(point)); + } + } else { + for (; it_x != it_x.end(); ++it_x, ++it_y, ++it_z) { + PointXYZIRC point; + point.x = *it_x; + point.y = *it_y; + point.z = *it_z; + output_modifier.push_back(std::move(point)); + } + } +} + +std::string PointCloudConcatenateDataSynchronizerComponent::formatTimestamp(double timestamp) +{ + std::ostringstream oss; + oss << std::fixed << std::setprecision(9) << timestamp; + return oss.str(); +} + +void PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus( + diagnostic_updater::DiagnosticStatusWrapper & stat) +{ + if (publish_pointcloud_ || drop_previous_but_late_pointcloud_) { + std::set missed_cloud; + + stat.add("concatenated cloud timestamp", formatTimestamp(current_concat_cloud_timestamp_)); + stat.add("reference timestamp min", formatTimestamp(diagnostic_reference_timestamp_min_)); + stat.add("reference timestamp max", formatTimestamp(diagnostic_reference_timestamp_max_)); + + bool topic_miss = false; + + int concatenate_status = 1; + for (auto topic : params_.input_topics) { + int cloud_status; // 1 for success, 0 for failure + if ( + diagnostic_topic_to_original_stamp_map_.find(topic) != + diagnostic_topic_to_original_stamp_map_.end()) { + cloud_status = 1; + stat.add( + topic + " timestamp", formatTimestamp(diagnostic_topic_to_original_stamp_map_[topic])); + } else { + topic_miss = true; + cloud_status = 0; + concatenate_status = 0; + } + stat.add(topic, cloud_status); + } + + stat.add("concatenate status", concatenate_status); + + int8_t level; + std::string message; + if (topic_miss) { + level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; + message = "Concatenated pointcloud is published but miss some topics"; + } else if (drop_previous_but_late_pointcloud_) { + level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; + message = "Concatenated pointcloud is not published as it is too late"; + } else { + level = diagnostic_msgs::msg::DiagnosticStatus::OK; + message = "Concatenated pointcloud is published and include all topics"; + } + + stat.summary(level, message); + + publish_pointcloud_ = false; + drop_previous_but_late_pointcloud_ = false; + } else { + const int8_t level = diagnostic_msgs::msg::DiagnosticStatus::OK; + const std::string message = + "Concatenate node launch successfully, but waiting for input pointcloud"; + stat.summary(level, message); + } +} + +} // namespace autoware::pointcloud_preprocessor + +#include +RCLCPP_COMPONENTS_REGISTER_NODE( + autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_nodelet.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_nodelet.cpp deleted file mode 100644 index d0911f769d3bd..0000000000000 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_nodelet.cpp +++ /dev/null @@ -1,717 +0,0 @@ -// Copyright 2020 Tier IV, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* - * Software License Agreement (BSD License) - * - * Copyright (c) 2009, Willow Garage, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Willow Garage, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ - * - */ - -#include "autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_nodelet.hpp" - -#include "autoware/pointcloud_preprocessor/utility/memory.hpp" - -#include - -#include - -#include -#include -#include -#include -#include - -#define DEFAULT_SYNC_TOPIC_POSTFIX \ - "_synchronized" // default postfix name for synchronized pointcloud - -////////////////////////////////////////////////////////////////////////////////////////////// - -namespace autoware::pointcloud_preprocessor -{ -PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchronizerComponent( - const rclcpp::NodeOptions & node_options) -: Node("point_cloud_concatenator_component", node_options), - input_twist_topic_type_(declare_parameter("input_twist_topic_type", "twist")) -{ - // initialize debug tool - { - using autoware::universe_utils::DebugPublisher; - using autoware::universe_utils::StopWatch; - stop_watch_ptr_ = std::make_unique>(); - debug_publisher_ = std::make_unique(this, "concatenate_data_synchronizer"); - stop_watch_ptr_->tic("cyclic_time"); - stop_watch_ptr_->tic("processing_time"); - } - - // Set parameters - { - output_frame_ = static_cast(declare_parameter("output_frame", "")); - if (output_frame_.empty()) { - RCLCPP_ERROR(get_logger(), "Need an 'output_frame' parameter to be set before continuing!"); - return; - } - has_static_tf_only_ = declare_parameter("has_static_tf_only", false); - declare_parameter("input_topics", std::vector()); - input_topics_ = get_parameter("input_topics").as_string_array(); - if (input_topics_.empty()) { - RCLCPP_ERROR(get_logger(), "Need a 'input_topics' parameter to be set before continuing!"); - return; - } - if (input_topics_.size() == 1) { - RCLCPP_ERROR(get_logger(), "Only one topic given. Need at least two topics to continue."); - return; - } - - // Optional parameters - maximum_queue_size_ = static_cast(declare_parameter("max_queue_size", 5)); - timeout_sec_ = static_cast(declare_parameter("timeout_sec", 0.1)); - - input_offset_ = declare_parameter("input_offset", std::vector{}); - if (!input_offset_.empty() && input_topics_.size() != input_offset_.size()) { - RCLCPP_ERROR(get_logger(), "The number of topics does not match the number of offsets."); - return; - } - - // Check if publish synchronized pointcloud - publish_synchronized_pointcloud_ = declare_parameter("publish_synchronized_pointcloud", true); - keep_input_frame_in_synchronized_pointcloud_ = - declare_parameter("keep_input_frame_in_synchronized_pointcloud", true); - synchronized_pointcloud_postfix_ = - declare_parameter("synchronized_pointcloud_postfix", "pointcloud"); - } - - // Initialize not_subscribed_topic_names_ - { - for (const std::string & e : input_topics_) { - not_subscribed_topic_names_.insert(e); - } - } - - // Initialize offset map - { - for (size_t i = 0; i < input_offset_.size(); ++i) { - offset_map_[input_topics_[i]] = input_offset_[i]; - } - } - - // tf2 listener - { - managed_tf_buffer_ = - std::make_unique(this, has_static_tf_only_); - } - - // Output Publishers - { - rclcpp::PublisherOptions pub_options; - pub_options.qos_overriding_options = rclcpp::QosOverridingOptions::with_default_policies(); - pub_output_ = this->create_publisher( - "output", rclcpp::SensorDataQoS().keep_last(maximum_queue_size_), pub_options); - } - - // Subscribers - { - RCLCPP_DEBUG_STREAM( - get_logger(), "Subscribing to " << input_topics_.size() << " user given topics as inputs:"); - for (const auto & input_topic : input_topics_) { - RCLCPP_DEBUG_STREAM(get_logger(), " - " << input_topic); - } - - // Subscribe to the filters - filters_.resize(input_topics_.size()); - - // First input_topics_.size () filters are valid - for (size_t d = 0; d < input_topics_.size(); ++d) { - cloud_stdmap_.insert(std::make_pair(input_topics_[d], nullptr)); - cloud_stdmap_tmp_ = cloud_stdmap_; - - // CAN'T use auto type here. - std::function cb = std::bind( - &PointCloudConcatenateDataSynchronizerComponent::cloud_callback, this, - std::placeholders::_1, input_topics_[d]); - - filters_[d].reset(); - filters_[d] = this->create_subscription( - input_topics_[d], rclcpp::SensorDataQoS().keep_last(maximum_queue_size_), cb); - } - - if (input_twist_topic_type_ == "twist") { - auto twist_cb = std::bind( - &PointCloudConcatenateDataSynchronizerComponent::twist_callback, this, - std::placeholders::_1); - sub_twist_ = this->create_subscription( - "~/input/twist", rclcpp::QoS{100}, twist_cb); - } else if (input_twist_topic_type_ == "odom") { - auto odom_cb = std::bind( - &PointCloudConcatenateDataSynchronizerComponent::odom_callback, this, - std::placeholders::_1); - sub_odom_ = this->create_subscription( - "~/input/odom", rclcpp::QoS{100}, odom_cb); - } else { - RCLCPP_ERROR_STREAM( - get_logger(), "input_twist_topic_type is invalid: " << input_twist_topic_type_); - throw std::runtime_error("input_twist_topic_type is invalid: " + input_twist_topic_type_); - } - } - - // Transformed Raw PointCloud2 Publisher to publish the transformed pointcloud - if (publish_synchronized_pointcloud_) { - rclcpp::PublisherOptions pub_options; - pub_options.qos_overriding_options = rclcpp::QosOverridingOptions::with_default_policies(); - - for (auto & topic : input_topics_) { - std::string new_topic = replaceSyncTopicNamePostfix(topic, synchronized_pointcloud_postfix_); - auto publisher = this->create_publisher( - new_topic, rclcpp::SensorDataQoS().keep_last(maximum_queue_size_), pub_options); - transformed_raw_pc_publisher_map_.insert({topic, publisher}); - } - } - - // Set timer - { - const auto period_ns = std::chrono::duration_cast( - std::chrono::duration(timeout_sec_)); - timer_ = rclcpp::create_timer( - this, get_clock(), period_ns, - std::bind(&PointCloudConcatenateDataSynchronizerComponent::timer_callback, this)); - } - - // Diagnostic Updater - { - updater_.setHardwareID("concatenate_data_checker"); - updater_.add( - "concat_status", this, &PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus); - } -} - -std::string PointCloudConcatenateDataSynchronizerComponent::replaceSyncTopicNamePostfix( - const std::string & original_topic_name, const std::string & postfix) -{ - std::string replaced_topic_name; - // separate the topic name by '/' and replace the last element with the new postfix - size_t pos = original_topic_name.find_last_of("/"); - if (pos == std::string::npos) { - // not found '/': this is not a namespaced topic - RCLCPP_WARN_STREAM( - get_logger(), - "The topic name is not namespaced. The postfix will be added to the end of the topic name."); - return original_topic_name + postfix; - } else { - // replace the last element with the new postfix - replaced_topic_name = original_topic_name.substr(0, pos) + "/" + postfix; - } - - // if topic name is the same with original topic name, add postfix to the end of the topic name - if (replaced_topic_name == original_topic_name) { - RCLCPP_WARN_STREAM( - get_logger(), "The topic name " - << original_topic_name - << " have the same postfix with synchronized pointcloud. We use the postfix " - "to the end of the topic name."); - replaced_topic_name = original_topic_name + DEFAULT_SYNC_TOPIC_POSTFIX; - } - return replaced_topic_name; -} - -/** - * @brief compute transform to adjust for old timestamp - * - * @param old_stamp - * @param new_stamp - * @return Eigen::Matrix4f: transformation matrix from new_stamp to old_stamp - */ -Eigen::Matrix4f -PointCloudConcatenateDataSynchronizerComponent::computeTransformToAdjustForOldTimestamp( - const rclcpp::Time & old_stamp, const rclcpp::Time & new_stamp) -{ - // return identity if no twist is available - if (twist_ptr_queue_.empty()) { - RCLCPP_WARN_STREAM_THROTTLE( - get_logger(), *get_clock(), std::chrono::milliseconds(10000).count(), - "No twist is available. Please confirm twist topic and timestamp"); - return Eigen::Matrix4f::Identity(); - } - - // return identity if old_stamp is newer than new_stamp - if (old_stamp > new_stamp) { - RCLCPP_DEBUG_STREAM_THROTTLE( - get_logger(), *get_clock(), std::chrono::milliseconds(10000).count(), - "old_stamp is newer than new_stamp,"); - return Eigen::Matrix4f::Identity(); - } - - auto old_twist_ptr_it = std::lower_bound( - std::begin(twist_ptr_queue_), std::end(twist_ptr_queue_), old_stamp, - [](const geometry_msgs::msg::TwistStamped::ConstSharedPtr & x_ptr, const rclcpp::Time & t) { - return rclcpp::Time(x_ptr->header.stamp) < t; - }); - old_twist_ptr_it = - old_twist_ptr_it == twist_ptr_queue_.end() ? (twist_ptr_queue_.end() - 1) : old_twist_ptr_it; - - auto new_twist_ptr_it = std::lower_bound( - std::begin(twist_ptr_queue_), std::end(twist_ptr_queue_), new_stamp, - [](const geometry_msgs::msg::TwistStamped::ConstSharedPtr & x_ptr, const rclcpp::Time & t) { - return rclcpp::Time(x_ptr->header.stamp) < t; - }); - new_twist_ptr_it = - new_twist_ptr_it == twist_ptr_queue_.end() ? (twist_ptr_queue_.end() - 1) : new_twist_ptr_it; - - auto prev_time = old_stamp; - double x = 0.0; - double y = 0.0; - double yaw = 0.0; - for (auto twist_ptr_it = old_twist_ptr_it; twist_ptr_it != new_twist_ptr_it + 1; ++twist_ptr_it) { - const double dt = - (twist_ptr_it != new_twist_ptr_it) - ? (rclcpp::Time((*twist_ptr_it)->header.stamp) - rclcpp::Time(prev_time)).seconds() - : (rclcpp::Time(new_stamp) - rclcpp::Time(prev_time)).seconds(); - - if (std::fabs(dt) > 0.1) { - RCLCPP_WARN_STREAM_THROTTLE( - get_logger(), *get_clock(), std::chrono::milliseconds(10000).count(), - "Time difference is too large. Cloud not interpolate. Please confirm twist topic and " - "timestamp"); - break; - } - - const double dis = (*twist_ptr_it)->twist.linear.x * dt; - yaw += (*twist_ptr_it)->twist.angular.z * dt; - x += dis * std::cos(yaw); - y += dis * std::sin(yaw); - prev_time = (*twist_ptr_it)->header.stamp; - } - Eigen::AngleAxisf rotation_x(0, Eigen::Vector3f::UnitX()); - Eigen::AngleAxisf rotation_y(0, Eigen::Vector3f::UnitY()); - Eigen::AngleAxisf rotation_z(yaw, Eigen::Vector3f::UnitZ()); - Eigen::Translation3f translation(x, y, 0); - Eigen::Matrix4f rotation_matrix = (translation * rotation_z * rotation_y * rotation_x).matrix(); - return rotation_matrix; -} - -std::map -PointCloudConcatenateDataSynchronizerComponent::combineClouds( - sensor_msgs::msg::PointCloud2::SharedPtr & concat_cloud_ptr) -{ - // map for storing the transformed point clouds - std::map transformed_clouds; - - // Step1. gather stamps and sort it - std::vector pc_stamps; - for (const auto & e : cloud_stdmap_) { - transformed_clouds[e.first] = nullptr; - if (e.second != nullptr) { - if (e.second->data.size() == 0) { - continue; - } - pc_stamps.push_back(rclcpp::Time(e.second->header.stamp)); - } - } - if (pc_stamps.empty()) { - return transformed_clouds; - } - // sort stamps and get oldest stamp - std::sort(pc_stamps.begin(), pc_stamps.end()); - std::reverse(pc_stamps.begin(), pc_stamps.end()); - const auto oldest_stamp = pc_stamps.back(); - - // Step2. Calculate compensation transform and concatenate with the oldest stamp - for (const auto & e : cloud_stdmap_) { - if (e.second != nullptr) { - if (e.second->data.size() == 0) { - continue; - } - sensor_msgs::msg::PointCloud2::SharedPtr transformed_cloud_ptr( - new sensor_msgs::msg::PointCloud2()); - managed_tf_buffer_->transformPointcloud(output_frame_, *e.second, *transformed_cloud_ptr); - - // calculate transforms to oldest stamp - Eigen::Matrix4f adjust_to_old_data_transform = Eigen::Matrix4f::Identity(); - rclcpp::Time transformed_stamp = rclcpp::Time(e.second->header.stamp); - for (const auto & stamp : pc_stamps) { - const auto new_to_old_transform = - computeTransformToAdjustForOldTimestamp(stamp, transformed_stamp); - adjust_to_old_data_transform = new_to_old_transform * adjust_to_old_data_transform; - transformed_stamp = std::min(transformed_stamp, stamp); - } - sensor_msgs::msg::PointCloud2::SharedPtr transformed_delay_compensated_cloud_ptr( - new sensor_msgs::msg::PointCloud2()); - pcl_ros::transformPointCloud( - adjust_to_old_data_transform, *transformed_cloud_ptr, - *transformed_delay_compensated_cloud_ptr); - - // concatenate - if (concat_cloud_ptr == nullptr) { - concat_cloud_ptr = - std::make_shared(*transformed_delay_compensated_cloud_ptr); - } else { - pcl::concatenatePointCloud( - *concat_cloud_ptr, *transformed_delay_compensated_cloud_ptr, *concat_cloud_ptr); - } - // convert to original sensor frame if necessary - bool need_transform_to_sensor_frame = (e.second->header.frame_id != output_frame_); - if (keep_input_frame_in_synchronized_pointcloud_ && need_transform_to_sensor_frame) { - sensor_msgs::msg::PointCloud2::SharedPtr transformed_cloud_ptr_in_sensor_frame( - new sensor_msgs::msg::PointCloud2()); - managed_tf_buffer_->transformPointcloud( - e.second->header.frame_id, *transformed_delay_compensated_cloud_ptr, - *transformed_cloud_ptr_in_sensor_frame); - transformed_cloud_ptr_in_sensor_frame->header.stamp = oldest_stamp; - transformed_cloud_ptr_in_sensor_frame->header.frame_id = e.second->header.frame_id; - transformed_clouds[e.first] = transformed_cloud_ptr_in_sensor_frame; - } else { - transformed_delay_compensated_cloud_ptr->header.stamp = oldest_stamp; - transformed_delay_compensated_cloud_ptr->header.frame_id = output_frame_; - transformed_clouds[e.first] = transformed_delay_compensated_cloud_ptr; - } - - } else { - not_subscribed_topic_names_.insert(e.first); - } - } - concat_cloud_ptr->header.stamp = oldest_stamp; - return transformed_clouds; -} - -void PointCloudConcatenateDataSynchronizerComponent::publish() -{ - stop_watch_ptr_->toc("processing_time", true); - sensor_msgs::msg::PointCloud2::SharedPtr concat_cloud_ptr = nullptr; - not_subscribed_topic_names_.clear(); - - const auto & transformed_raw_points = - PointCloudConcatenateDataSynchronizerComponent::combineClouds(concat_cloud_ptr); - - // publish concatenated pointcloud - if (concat_cloud_ptr) { - auto output = std::make_unique(*concat_cloud_ptr); - pub_output_->publish(std::move(output)); - } else { - RCLCPP_WARN(this->get_logger(), "concat_cloud_ptr is nullptr, skipping pointcloud publish."); - } - - // publish transformed raw pointclouds - if (publish_synchronized_pointcloud_) { - for (const auto & e : transformed_raw_points) { - if (e.second) { - auto output = std::make_unique(*e.second); - transformed_raw_pc_publisher_map_[e.first]->publish(std::move(output)); - } else { - RCLCPP_WARN( - this->get_logger(), "transformed_raw_points[%s] is nullptr, skipping pointcloud publish.", - e.first.c_str()); - } - } - } - - updater_.force_update(); - - cloud_stdmap_ = cloud_stdmap_tmp_; - std::for_each(std::begin(cloud_stdmap_tmp_), std::end(cloud_stdmap_tmp_), [](auto & e) { - e.second = nullptr; - }); - // add processing time for debug - if (debug_publisher_) { - const double cyclic_time_ms = stop_watch_ptr_->toc("cyclic_time", true); - const double processing_time_ms = stop_watch_ptr_->toc("processing_time", true); - debug_publisher_->publish( - "debug/cyclic_time_ms", cyclic_time_ms); - debug_publisher_->publish( - "debug/processing_time_ms", processing_time_ms); - } - for (const auto & e : cloud_stdmap_) { - if (e.second != nullptr) { - if (debug_publisher_) { - const auto pipeline_latency_ms = - std::chrono::duration( - std::chrono::nanoseconds( - (this->get_clock()->now() - e.second->header.stamp).nanoseconds())) - .count(); - debug_publisher_->publish( - "debug" + e.first + "/pipeline_latency_ms", pipeline_latency_ms); - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void PointCloudConcatenateDataSynchronizerComponent::convertToXYZIRCCloud( - const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, - sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr) -{ - output_ptr->header = input_ptr->header; - - PointCloud2Modifier output_modifier{ - *output_ptr, input_ptr->header.frame_id}; - output_modifier.reserve(input_ptr->width); - - bool has_valid_intensity = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { - return field.name == "intensity" && field.datatype == sensor_msgs::msg::PointField::UINT8; - }); - - bool has_valid_return_type = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { - return field.name == "return_type" && field.datatype == sensor_msgs::msg::PointField::UINT8; - }); - - bool has_valid_channel = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { - return field.name == "channel" && field.datatype == sensor_msgs::msg::PointField::UINT16; - }); - - sensor_msgs::PointCloud2Iterator it_x(*input_ptr, "x"); - sensor_msgs::PointCloud2Iterator it_y(*input_ptr, "y"); - sensor_msgs::PointCloud2Iterator it_z(*input_ptr, "z"); - - if (has_valid_intensity && has_valid_return_type && has_valid_channel) { - sensor_msgs::PointCloud2Iterator it_i(*input_ptr, "intensity"); - sensor_msgs::PointCloud2Iterator it_r(*input_ptr, "return_type"); - sensor_msgs::PointCloud2Iterator it_c(*input_ptr, "channel"); - - for (; it_x != it_x.end(); ++it_x, ++it_y, ++it_z, ++it_i, ++it_r, ++it_c) { - PointXYZIRC point; - point.x = *it_x; - point.y = *it_y; - point.z = *it_z; - point.intensity = *it_i; - point.return_type = *it_r; - point.channel = *it_c; - output_modifier.push_back(std::move(point)); - } - } else { - for (; it_x != it_x.end(); ++it_x, ++it_y, ++it_z) { - PointXYZIRC point; - point.x = *it_x; - point.y = *it_y; - point.z = *it_z; - output_modifier.push_back(std::move(point)); - } - } -} - -void PointCloudConcatenateDataSynchronizerComponent::setPeriod(const int64_t new_period) -{ - if (!timer_) { - return; - } - int64_t old_period = 0; - rcl_ret_t ret = rcl_timer_get_period(timer_->get_timer_handle().get(), &old_period); - if (ret != RCL_RET_OK) { - rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't get old period"); - } - ret = rcl_timer_exchange_period(timer_->get_timer_handle().get(), new_period, &old_period); - if (ret != RCL_RET_OK) { - rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't exchange_period"); - } -} - -void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( - const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_ptr, const std::string & topic_name) -{ - if (!utils::is_data_layout_compatible_with_point_xyzirc(*input_ptr)) { - RCLCPP_ERROR( - get_logger(), "The pointcloud layout is not compatible with PointXYZIRC. Aborting"); - - if (utils::is_data_layout_compatible_with_point_xyzi(*input_ptr)) { - RCLCPP_ERROR( - get_logger(), - "The pointcloud layout is compatible with PointXYZI. You may be using legacy code/data"); - } - - return; - } - - std::lock_guard lock(mutex_); - sensor_msgs::msg::PointCloud2::SharedPtr xyzirc_input_ptr(new sensor_msgs::msg::PointCloud2()); - auto input = std::make_shared(*input_ptr); - if (input->data.empty()) { - RCLCPP_WARN_STREAM_THROTTLE( - this->get_logger(), *this->get_clock(), 1000, "Empty sensor points!"); - } else { - // convert to XYZIRC pointcloud if pointcloud is not empty - convertToXYZIRCCloud(input, xyzirc_input_ptr); - } - - const bool is_already_subscribed_this = (cloud_stdmap_[topic_name] != nullptr); - const bool is_already_subscribed_tmp = std::any_of( - std::begin(cloud_stdmap_tmp_), std::end(cloud_stdmap_tmp_), - [](const auto & e) { return e.second != nullptr; }); - - if (is_already_subscribed_this) { - cloud_stdmap_tmp_[topic_name] = xyzirc_input_ptr; - - if (!is_already_subscribed_tmp) { - auto period = std::chrono::duration_cast( - std::chrono::duration(timeout_sec_)); - try { - setPeriod(period.count()); - } catch (rclcpp::exceptions::RCLError & ex) { - RCLCPP_WARN_THROTTLE(get_logger(), *get_clock(), 5000, "%s", ex.what()); - } - timer_->reset(); - } - } else { - cloud_stdmap_[topic_name] = xyzirc_input_ptr; - - const bool is_subscribed_all = std::all_of( - std::begin(cloud_stdmap_), std::end(cloud_stdmap_), - [](const auto & e) { return e.second != nullptr; }); - - if (is_subscribed_all) { - for (const auto & e : cloud_stdmap_tmp_) { - if (e.second != nullptr) { - cloud_stdmap_[e.first] = e.second; - } - } - std::for_each(std::begin(cloud_stdmap_tmp_), std::end(cloud_stdmap_tmp_), [](auto & e) { - e.second = nullptr; - }); - - timer_->cancel(); - publish(); - } else if (offset_map_.size() > 0) { - timer_->cancel(); - auto period = std::chrono::duration_cast( - std::chrono::duration(timeout_sec_ - offset_map_[topic_name])); - try { - setPeriod(period.count()); - } catch (rclcpp::exceptions::RCLError & ex) { - RCLCPP_WARN_THROTTLE(get_logger(), *get_clock(), 5000, "%s", ex.what()); - } - timer_->reset(); - } - } -} - -void PointCloudConcatenateDataSynchronizerComponent::timer_callback() -{ - using std::chrono_literals::operator""ms; - timer_->cancel(); - if (mutex_.try_lock()) { - publish(); - mutex_.unlock(); - } else { - try { - std::chrono::nanoseconds period = 10ms; - setPeriod(period.count()); - } catch (rclcpp::exceptions::RCLError & ex) { - RCLCPP_WARN_THROTTLE(get_logger(), *get_clock(), 5000, "%s", ex.what()); - } - timer_->reset(); - } -} - -void PointCloudConcatenateDataSynchronizerComponent::twist_callback( - const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr input) -{ - // if rosbag restart, clear buffer - if (!twist_ptr_queue_.empty()) { - if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > rclcpp::Time(input->header.stamp)) { - twist_ptr_queue_.clear(); - } - } - - // pop old data - while (!twist_ptr_queue_.empty()) { - if ( - rclcpp::Time(twist_ptr_queue_.front()->header.stamp) + rclcpp::Duration::from_seconds(1.0) > - rclcpp::Time(input->header.stamp)) { - break; - } - twist_ptr_queue_.pop_front(); - } - - auto twist_ptr = std::make_shared(); - twist_ptr->header = input->header; - twist_ptr->twist = input->twist.twist; - twist_ptr_queue_.push_back(twist_ptr); -} - -void PointCloudConcatenateDataSynchronizerComponent::odom_callback( - const nav_msgs::msg::Odometry::ConstSharedPtr input) -{ - // if rosbag restart, clear buffer - if (!twist_ptr_queue_.empty()) { - if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > rclcpp::Time(input->header.stamp)) { - twist_ptr_queue_.clear(); - } - } - - // pop old data - while (!twist_ptr_queue_.empty()) { - if ( - rclcpp::Time(twist_ptr_queue_.front()->header.stamp) + rclcpp::Duration::from_seconds(1.0) > - rclcpp::Time(input->header.stamp)) { - break; - } - twist_ptr_queue_.pop_front(); - } - - auto twist_ptr = std::make_shared(); - twist_ptr->header = input->header; - twist_ptr->twist = input->twist.twist; - twist_ptr_queue_.push_back(twist_ptr); -} - -void PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus( - diagnostic_updater::DiagnosticStatusWrapper & stat) -{ - for (const std::string & e : input_topics_) { - const std::string subscribe_status = not_subscribed_topic_names_.count(e) ? "NG" : "OK"; - stat.add(e, subscribe_status); - } - - const int8_t level = not_subscribed_topic_names_.empty() - ? diagnostic_msgs::msg::DiagnosticStatus::OK - : diagnostic_msgs::msg::DiagnosticStatus::WARN; - const std::string message = not_subscribed_topic_names_.empty() - ? "Concatenate all topics" - : "Some topics are not concatenated"; - stat.summary(level, message); -} -} // namespace autoware::pointcloud_preprocessor - -#include -RCLCPP_COMPONENTS_REGISTER_NODE( - autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent) diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_cloud_collector.cpp new file mode 100644 index 0000000000000..f3f0626364f88 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/test/test_cloud_collector.cpp @@ -0,0 +1,468 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// cloud_collector_test.cpp +// cloud_collector_test.cpp + +// cloud_collector_test.cpp + +#include "autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp" +#include "autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp" +#include "autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp" + +#include + +#include +#include + +#include +#include + +#include +#include +#include + +class ConcatenateCloudTest : public ::testing::Test +{ +protected: + void SetUp() override + { + rclcpp::NodeOptions node_options; + // Instead of "input_topics", other parameters are not unsed. + // They just helps to setup the concatenate node + node_options.parameter_overrides( + {{"maximum_queue_size", 5}, + {"timeout_sec", 0.2}, + {"is_motion_compensated", true}, + {"publish_synchronized_pointcloud", true}, + {"keep_input_frame_in_synchronized_pointcloud", true}, + {"publish_previous_but_late_pointcloud", false}, + {"synchronized_pointcloud_postfix", "pointcloud"}, + {"input_twist_topic_type", "twist"}, + {"input_topics", std::vector{"lidar_top", "lidar_left", "lidar_right"}}, + {"output_frame", "base_link"}, + {"lidar_timestamp_offsets", std::vector{0.0, 0.04, 0.08}}, + {"lidar_timestamp_noise_window", std::vector{0.01, 0.01, 0.01}}}); + + concatenate_node_ = std::make_shared< + autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent>( + node_options); + combine_cloud_handler_ = + std::make_shared( + concatenate_node_.get(), std::vector{"lidar_top", "lidar_left", "lidar_right"}, + "base_link", true, true); + + collector_ = std::make_shared( + std::dynamic_pointer_cast< + autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent>( + concatenate_node_->shared_from_this()), + collectors_, combine_cloud_handler_, number_of_pointcloud_, timeout_sec_); + + collectors_.push_back(collector_); + + // Setup TF + tf_broadcaster_ = std::make_shared(concatenate_node_); + tf_broadcaster_->sendTransform(generateStaticTransformMsg()); + + // Spin the node for a while to ensure transforms are published + auto start = std::chrono::steady_clock::now(); + auto timeout = std::chrono::milliseconds(100); + while (std::chrono::steady_clock::now() - start < timeout) { + rclcpp::spin_some(concatenate_node_); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + + geometry_msgs::msg::TransformStamped generateTransformMsg( + const std::string & parent_frame, const std::string & child_frame, double x, double y, double z, + double qx, double qy, double qz, double qw) + { + rclcpp::Time timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); + geometry_msgs::msg::TransformStamped tf_msg; + tf_msg.header.stamp = timestamp; + tf_msg.header.frame_id = parent_frame; + tf_msg.child_frame_id = child_frame; + tf_msg.transform.translation.x = x; + tf_msg.transform.translation.y = y; + tf_msg.transform.translation.z = z; + tf_msg.transform.rotation.x = qx; + tf_msg.transform.rotation.y = qy; + tf_msg.transform.rotation.z = qz; + tf_msg.transform.rotation.w = qw; + return tf_msg; + } + + sensor_msgs::msg::PointCloud2 generatePointCloudMsg( + bool generate_points, bool is_lidar_frame, std::string topic_name, rclcpp::Time stamp) + { + sensor_msgs::msg::PointCloud2 pointcloud_msg; + pointcloud_msg.header.stamp = stamp; + pointcloud_msg.header.frame_id = is_lidar_frame ? topic_name : "base_link"; + pointcloud_msg.height = 1; + pointcloud_msg.is_dense = true; + pointcloud_msg.is_bigendian = false; + + if (generate_points) { + std::array points = {{ + Eigen::Vector3f(10.0f, 0.0f, 0.0f), // point 1 + Eigen::Vector3f(0.0f, 10.0f, 0.0f), // point 2 + Eigen::Vector3f(0.0f, 0.0f, 10.0f), // point 3 + }}; + + sensor_msgs::PointCloud2Modifier modifier(pointcloud_msg); + modifier.setPointCloud2Fields( + 10, "x", 1, sensor_msgs::msg::PointField::FLOAT32, "y", 1, + sensor_msgs::msg::PointField::FLOAT32, "z", 1, sensor_msgs::msg::PointField::FLOAT32, + "intensity", 1, sensor_msgs::msg::PointField::UINT8, "return_type", 1, + sensor_msgs::msg::PointField::UINT8, "channel", 1, sensor_msgs::msg::PointField::UINT16, + "azimuth", 1, sensor_msgs::msg::PointField::FLOAT32, "elevation", 1, + sensor_msgs::msg::PointField::FLOAT32, "distance", 1, sensor_msgs::msg::PointField::FLOAT32, + "time_stamp", 1, sensor_msgs::msg::PointField::UINT32); + + modifier.resize(number_of_points_); + + sensor_msgs::PointCloud2Iterator iter_x(pointcloud_msg, "x"); + sensor_msgs::PointCloud2Iterator iter_y(pointcloud_msg, "y"); + sensor_msgs::PointCloud2Iterator iter_z(pointcloud_msg, "z"); + sensor_msgs::PointCloud2Iterator iter_t(pointcloud_msg, "time_stamp"); + + for (size_t i = 0; i < number_of_points_; ++i) { + *iter_x = points[i].x(); + *iter_y = points[i].y(); + *iter_z = points[i].z(); + *iter_t = 0; + ++iter_x; + ++iter_y; + ++iter_z; + ++iter_t; + } + } else { + pointcloud_msg.width = 0; + pointcloud_msg.row_step = 0; + } + + return pointcloud_msg; + } + + std::vector generateStaticTransformMsg() + { + // generate defined transformations + return { + generateTransformMsg("base_link", "lidar_top", 5.0, 5.0, 5.0, 0.683, 0.5, 0.183, 0.499), + generateTransformMsg("base_link", "lidar_left", 1.0, 1.0, 3.0, 0.278, 0.717, 0.441, 0.453)}; + generateTransformMsg("base_link", "lidar_right", 1.0, 1.0, 3.0, 0.278, 0.717, 0.441, 0.453); + } + + std::shared_ptr + concatenate_node_; + std::list> collectors_; + std::shared_ptr combine_cloud_handler_; + std::shared_ptr collector_; + std::shared_ptr tf_broadcaster_; + + static constexpr int32_t timestamp_seconds_{10}; + static constexpr uint32_t timestamp_nanoseconds_{100000000}; + static constexpr size_t number_of_points_{3}; + static constexpr float standard_tolerance_{1e-4}; + static constexpr int number_of_pointcloud_{3}; + static constexpr float timeout_sec_{1}; + bool debug_{true}; +}; + +//////////////////////////////// Test combine_cloud_handler //////////////////////////////// +TEST_F(ConcatenateCloudTest, ProcessTwist) +{ + auto twist_msg = std::make_shared(); + twist_msg->header.stamp = rclcpp::Time(10, 0); + twist_msg->twist.twist.linear.x = 1.0; + twist_msg->twist.twist.angular.z = 0.1; + + combine_cloud_handler_->processTwist(twist_msg); + + ASSERT_FALSE(combine_cloud_handler_->twist_ptr_queue_.empty()); + EXPECT_EQ(combine_cloud_handler_->twist_ptr_queue_.front()->twist.linear.x, 1.0); + EXPECT_EQ(combine_cloud_handler_->twist_ptr_queue_.front()->twist.angular.z, 0.1); +} + +TEST_F(ConcatenateCloudTest, ProcessOdometry) +{ + auto odom_msg = std::make_shared(); + odom_msg->header.stamp = rclcpp::Time(10, 0); + odom_msg->twist.twist.linear.x = 1.0; + odom_msg->twist.twist.angular.z = 0.1; + + combine_cloud_handler_->processOdometry(odom_msg); + + ASSERT_FALSE(combine_cloud_handler_->twist_ptr_queue_.empty()); + EXPECT_EQ(combine_cloud_handler_->twist_ptr_queue_.front()->twist.linear.x, 1.0); + EXPECT_EQ(combine_cloud_handler_->twist_ptr_queue_.front()->twist.angular.z, 0.1); +} + +TEST_F(ConcatenateCloudTest, ComputeTransformToAdjustForOldTimestamp) +{ + rclcpp::Time old_stamp(10, 100000000, RCL_ROS_TIME); + rclcpp::Time new_stamp(10, 150000000, RCL_ROS_TIME); + + // Time difference between twist msg is more than 100 miliseconds, won't calculate the difference + auto twist_msg1 = std::make_shared(); + twist_msg1->header.stamp = rclcpp::Time(10, 130000000, RCL_ROS_TIME); + twist_msg1->twist.linear.x = 1.0; + twist_msg1->twist.angular.z = 0.1; + combine_cloud_handler_->twist_ptr_queue_.push_back(twist_msg1); + + auto twist_msg2 = std::make_shared(); + twist_msg2->header.stamp = rclcpp::Time(10, 160000000, RCL_ROS_TIME); + twist_msg2->twist.linear.x = 1.0; + twist_msg2->twist.angular.z = 0.1; + combine_cloud_handler_->twist_ptr_queue_.push_back(twist_msg2); + + Eigen::Matrix4f transform = + combine_cloud_handler_->computeTransformToAdjustForOldTimestamp(old_stamp, new_stamp); + + // translation + EXPECT_NEAR(transform(0, 3), 0.0499996, standard_tolerance_); + EXPECT_NEAR(transform(1, 3), 0.000189999, standard_tolerance_); + + // rotation, yaw = 0.005 + EXPECT_NEAR(transform(0, 0), 0.999987, standard_tolerance_); + EXPECT_NEAR(transform(0, 1), -0.00499998, standard_tolerance_); + EXPECT_NEAR(transform(1, 0), 0.00499998, standard_tolerance_); + EXPECT_NEAR(transform(1, 1), 0.999987, standard_tolerance_); + + std::ostringstream oss; + oss << "Transformation matrix:\n" << transform; + + if (debug_) { + RCLCPP_INFO(concatenate_node_->get_logger(), "%s", oss.str().c_str()); + } +} + +//////////////////////////////// Test cloud_collector //////////////////////////////// + +TEST_F(ConcatenateCloudTest, SetAndGetReferenceTimeStampBoundary) +{ + double reference_timestamp = 10.0; + double noise_window = 0.1; + collector_->setReferenceTimeStamp(reference_timestamp, noise_window); + auto [min, max] = collector_->getReferenceTimeStampBoundary(); + EXPECT_DOUBLE_EQ(min, 9.9); + EXPECT_DOUBLE_EQ(max, 10.1); +} + +TEST_F(ConcatenateCloudTest, concatenateAndPublishClouds) +{ + rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); + rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40000000, RCL_ROS_TIME); + rclcpp::Time right_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 80000000, RCL_ROS_TIME); + sensor_msgs::msg::PointCloud2 top_pointcloud = + generatePointCloudMsg(true, false, "lidar_top", top_timestamp); + sensor_msgs::msg::PointCloud2 left_pointcloud = + generatePointCloudMsg(true, false, "lidar_left", left_timestamp); + sensor_msgs::msg::PointCloud2 right_pointcloud = + generatePointCloudMsg(true, false, "lidar_right", right_timestamp); + + sensor_msgs::msg::PointCloud2::SharedPtr top_pointcloud_ptr = + std::make_shared(top_pointcloud); + sensor_msgs::msg::PointCloud2::SharedPtr left_pointcloud_ptr = + std::make_shared(left_pointcloud); + sensor_msgs::msg::PointCloud2::SharedPtr right_pointcloud_ptr = + std::make_shared(right_pointcloud); + + std::unordered_map topic_to_cloud_map; + topic_to_cloud_map["lidar_top"] = top_pointcloud_ptr; + topic_to_cloud_map["lidar_left"] = left_pointcloud_ptr; + topic_to_cloud_map["lidar_right"] = right_pointcloud_ptr; + + auto [concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map] = + collector_->concatenateClouds(topic_to_cloud_map); + + // test output concatenate cloud + // No input twist, so it will not do the motion compensation + std::array expected_pointcloud = { + {Eigen::Vector3f(10.0f, 0.0f, 0.0f), Eigen::Vector3f(0.0f, 10.0f, 0.0f), + Eigen::Vector3f(0.0f, 0.0f, 10.0f), Eigen::Vector3f(10.0f, 0.0f, 0.0f), + Eigen::Vector3f(0.0f, 10.0f, 0.0f), Eigen::Vector3f(0.0f, 0.0f, 10.0f), + Eigen::Vector3f(10.0f, 0.0f, 0.0f), Eigen::Vector3f(0.0f, 10.0f, 0.0f), + Eigen::Vector3f(0.0f, 0.0f, 10.0f)}}; + + size_t i = 0; + std::ostringstream oss; + oss << "Concatenated pointcloud:\n"; + + sensor_msgs::PointCloud2Iterator iter_x(*concatenate_cloud_ptr, "x"); + sensor_msgs::PointCloud2Iterator iter_y(*concatenate_cloud_ptr, "y"); + sensor_msgs::PointCloud2Iterator iter_z(*concatenate_cloud_ptr, "z"); + + for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z, ++i) { + oss << "Concatenated point " << i << ": (" << *iter_x << ", " << *iter_y << ", " << *iter_z + << ")\n"; + EXPECT_FLOAT_EQ(*iter_x, expected_pointcloud[i].x()); + EXPECT_FLOAT_EQ(*iter_y, expected_pointcloud[i].y()); + EXPECT_FLOAT_EQ(*iter_z, expected_pointcloud[i].z()); + } + + // concatenate cloud should have the oldest pointcloud's timestamp + EXPECT_FLOAT_EQ( + top_timestamp.seconds(), rclcpp::Time(concatenate_cloud_ptr->header.stamp).seconds()); + + if (debug_) { + RCLCPP_INFO(concatenate_node_->get_logger(), "%s", oss.str().c_str()); + } + + // test seperated transformed cloud + std::array expected_top_pointcloud = { + {Eigen::Vector3f(10.0f, 0.0f, 0.0f), Eigen::Vector3f(0.0f, 10.0f, 0.0f), + Eigen::Vector3f(0.0f, 0.0f, 10.0f)}}; + std::array expected_left_pointcloud = { + {Eigen::Vector3f(10.0f, 0.0f, 0.0f), Eigen::Vector3f(0.0f, 10.0f, 0.0f), + Eigen::Vector3f(0.0f, 0.0f, 10.0f)}}; + std::array expected_right_pointcloud = { + {Eigen::Vector3f(10.0f, 0.0f, 0.0f), Eigen::Vector3f(0.0f, 10.0f, 0.0f), + Eigen::Vector3f(0.0f, 0.0f, 10.0f)}}; + + oss.clear(); + oss.str(""); + i = 0; + sensor_msgs::PointCloud2Iterator top_pc_iter_x( + *topic_to_transformed_cloud_map["lidar_top"], "x"); + sensor_msgs::PointCloud2Iterator top_pc_iter_y( + *topic_to_transformed_cloud_map["lidar_top"], "y"); + sensor_msgs::PointCloud2Iterator top_pc_iter_z( + *topic_to_transformed_cloud_map["lidar_top"], "z"); + + for (; top_pc_iter_x != top_pc_iter_x.end(); + ++top_pc_iter_x, ++top_pc_iter_y, ++top_pc_iter_z, ++i) { + oss << "Top point " << i << ": (" << *top_pc_iter_x << ", " << *top_pc_iter_y << ", " + << *top_pc_iter_z << ")\n"; + EXPECT_FLOAT_EQ(*top_pc_iter_x, expected_top_pointcloud[i].x()); + EXPECT_FLOAT_EQ(*top_pc_iter_y, expected_top_pointcloud[i].y()); + EXPECT_FLOAT_EQ(*top_pc_iter_z, expected_top_pointcloud[i].z()); + } + + if (debug_) { + RCLCPP_INFO(concatenate_node_->get_logger(), "%s", oss.str().c_str()); + } + + oss.clear(); + oss.str(""); + i = 0; + sensor_msgs::PointCloud2Iterator left_pc_iter_x( + *topic_to_transformed_cloud_map["lidar_left"], "x"); + sensor_msgs::PointCloud2Iterator left_pc_iter_y( + *topic_to_transformed_cloud_map["lidar_left"], "y"); + sensor_msgs::PointCloud2Iterator left_pc_iter_z( + *topic_to_transformed_cloud_map["lidar_left"], "z"); + + for (; left_pc_iter_x != left_pc_iter_x.end(); + ++left_pc_iter_x, ++left_pc_iter_y, ++left_pc_iter_z, ++i) { + oss << "Left point " << i << ": (" << *left_pc_iter_x << ", " << *left_pc_iter_y << ", " + << *left_pc_iter_z << ")\n"; + EXPECT_FLOAT_EQ(*left_pc_iter_x, expected_left_pointcloud[i].x()); + EXPECT_FLOAT_EQ(*left_pc_iter_y, expected_left_pointcloud[i].y()); + EXPECT_FLOAT_EQ(*left_pc_iter_z, expected_left_pointcloud[i].z()); + } + + if (debug_) { + RCLCPP_INFO(concatenate_node_->get_logger(), "%s", oss.str().c_str()); + } + + oss.clear(); + oss.str(""); + i = 0; + sensor_msgs::PointCloud2Iterator right_pc_iter_x( + *topic_to_transformed_cloud_map["lidar_right"], "x"); + sensor_msgs::PointCloud2Iterator right_pc_iter_y( + *topic_to_transformed_cloud_map["lidar_right"], "y"); + sensor_msgs::PointCloud2Iterator right_pc_iter_z( + *topic_to_transformed_cloud_map["lidar_right"], "z"); + + for (; right_pc_iter_x != right_pc_iter_x.end(); + ++right_pc_iter_x, ++right_pc_iter_y, ++right_pc_iter_z, ++i) { + oss << "Right point " << i << ": (" << *right_pc_iter_x << ", " << *right_pc_iter_y << ", " + << *right_pc_iter_z << ")\n"; + EXPECT_FLOAT_EQ(*right_pc_iter_x, expected_right_pointcloud[i].x()); + EXPECT_FLOAT_EQ(*right_pc_iter_y, expected_right_pointcloud[i].y()); + EXPECT_FLOAT_EQ(*right_pc_iter_z, expected_right_pointcloud[i].z()); + } + + if (debug_) { + RCLCPP_INFO(concatenate_node_->get_logger(), "%s", oss.str().c_str()); + } + + // test original cloud's timestamps + EXPECT_FLOAT_EQ(top_timestamp.seconds(), topic_to_original_stamp_map["lidar_top"]); + EXPECT_FLOAT_EQ(left_timestamp.seconds(), topic_to_original_stamp_map["lidar_left"]); + EXPECT_FLOAT_EQ(right_timestamp.seconds(), topic_to_original_stamp_map["lidar_right"]); +} + +TEST_F(ConcatenateCloudTest, DeleteCollector) +{ + collector_->deleteCollector(); + EXPECT_TRUE(collectors_.empty()); +} + +TEST_F(ConcatenateCloudTest, ProcessSingleCloud) +{ + rclcpp::Time timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); + sensor_msgs::msg::PointCloud2 top_pointcloud = + generatePointCloudMsg(true, false, "lidar_top", timestamp); + sensor_msgs::msg::PointCloud2::SharedPtr top_pointcloud_ptr = + std::make_shared(top_pointcloud); + collector_->processCloud("lidar_top", top_pointcloud_ptr); + + auto topic_to_cloud_map = collector_->get_topic_to_cloud_map(); + EXPECT_EQ(topic_to_cloud_map["lidar_top"], top_pointcloud_ptr); + EXPECT_FALSE(collectors_.empty()); + + // Sleep for 1.5 seconds + std::this_thread::sleep_for(std::chrono::milliseconds(1500)); + rclcpp::spin_some(concatenate_node_); + + // Collector should concatenate and publish the pointcloud, also delete itself. + EXPECT_TRUE(collectors_.empty()); +} + +TEST_F(ConcatenateCloudTest, ProcessMultipleCloud) +{ + rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); + rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40000000, RCL_ROS_TIME); + rclcpp::Time right_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 80000000, RCL_ROS_TIME); + sensor_msgs::msg::PointCloud2 top_pointcloud = + generatePointCloudMsg(true, false, "lidar_top", top_timestamp); + sensor_msgs::msg::PointCloud2 left_pointcloud = + generatePointCloudMsg(true, false, "lidar_left", left_timestamp); + sensor_msgs::msg::PointCloud2 right_pointcloud = + generatePointCloudMsg(true, false, "lidar_right", right_timestamp); + + sensor_msgs::msg::PointCloud2::SharedPtr top_pointcloud_ptr = + std::make_shared(top_pointcloud); + sensor_msgs::msg::PointCloud2::SharedPtr left_pointcloud_ptr = + std::make_shared(left_pointcloud); + sensor_msgs::msg::PointCloud2::SharedPtr right_pointcloud_ptr = + std::make_shared(right_pointcloud); + + collector_->processCloud("lidar_top", top_pointcloud_ptr); + collector_->processCloud("lidar_left", left_pointcloud_ptr); + collector_->processCloud("lidar_right", right_pointcloud_ptr); + + EXPECT_TRUE(collectors_.empty()); +} + +int main(int argc, char ** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + rclcpp::init(argc, argv); + int ret = RUN_ALL_TESTS(); + rclcpp::shutdown(); + return ret; +} diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node.py new file mode 100644 index 0000000000000..ac1a63b9ef07a --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node.py @@ -0,0 +1,797 @@ +#!/usr/bin/env python3 + +# Copyright 2024 TIER IV, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import random +import struct +import time +import unittest + +# TODO: remove unused later +from geometry_msgs.msg import TransformStamped +from geometry_msgs.msg import TwistWithCovarianceStamped +import launch +import launch.actions +from launch.logging import get_logger +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode +import launch_testing +import launch_testing.actions +import launch_testing.asserts +import launch_testing.markers +import launch_testing.tools +import numpy as np +import pytest +import rclpy +from rclpy.qos import QoSDurabilityPolicy +from rclpy.qos import QoSHistoryPolicy +from rclpy.qos import QoSProfile +from rclpy.qos import QoSReliabilityPolicy +from rclpy.time import Time +from sensor_msgs.msg import PointCloud2 +from sensor_msgs.msg import PointField +from sensor_msgs_py import point_cloud2 +from std_msgs.msg import Header +from tf2_ros.static_transform_broadcaster import StaticTransformBroadcaster + +logger = get_logger(__name__) + +INPUT_LIDAR_TOPICS = [ + "/test/sensing/lidar/left/pointcloud", + "/test/sensing/lidar/right/pointcloud", + "/test/sensing/lidar/top/pointcloud", +] +FRAME_ID_LISTS = [ + "left_lidar", + "right_lidar", + "top_lidar", +] + +TIMEOUT_SEC = 0.2 +NUM_OF_POINTS = 3 + +global_seconds = 10 +global_nanosceonds = 100000000 +milliseconds = 1000000 +global_timestamp = Time(seconds=global_seconds, nanoseconds=global_nanosceonds).to_msg() + + +@pytest.mark.launch_test +def generate_test_description(): + nodes = [] + + nodes.append( + ComposableNode( + package="autoware_pointcloud_preprocessor", + plugin="autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent", + name="test_concatenate_node", + remappings=[ + ("~/input/twist", "/test/sensing/vehicle_velocity_converter/twist_with_covariance"), + ("output", "/test/sensing/lidar/concatenated/pointcloud"), + ], + parameters=[ + { + "maximum_queue_size": 5, + "timeout_sec": TIMEOUT_SEC, + "is_motion_compensated": True, + "publish_synchronized_pointcloud": True, + "keep_input_frame_in_synchronized_pointcloud": True, + "publish_previous_but_late_pointcloud": True, + "synchronized_pointcloud_postfix": "pointcloud", + "input_twist_topic_type": "twist", + "input_topics": INPUT_LIDAR_TOPICS, + "output_frame": "base_link", + "lidar_timestamp_offsets": [0.0, 0.04, 0.08], + "lidar_timestamp_noise_window": [0.01, 0.01, 0.01], + } + ], + extra_arguments=[{"use_intra_process_comms": True}], + ) + ) + + container = ComposableNodeContainer( + name="test_concateante_data_container", + namespace="pointcloud_preprocessor", + package="rclcpp_components", + executable="component_container", + composable_node_descriptions=nodes, + output="screen", + ) + + return launch.LaunchDescription( + [ + container, + # Start tests right away - no need to wait for anything + launch_testing.actions.ReadyToTest(), + ] + ) + + +def create_header(timestamp: Time, frame_id_index: int, is_base_link: bool): + header = Header() + header.stamp = timestamp + + if is_base_link: + header.frame_id = "base_link" + else: + header.frame_id = FRAME_ID_LISTS[frame_id_index] + return header + + +def create_points(): + return [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)] + + +def create_fields(): + # The values of the fields do not influence the results. + intensities = [255] * NUM_OF_POINTS + return_types = [1] * NUM_OF_POINTS + channels = [1] * NUM_OF_POINTS + azimuths = [0.0] * NUM_OF_POINTS + elevations = [0.0] * NUM_OF_POINTS + distances = [1.0] * NUM_OF_POINTS + timestamps = [0] * NUM_OF_POINTS + return intensities, return_types, channels, azimuths, elevations, distances, timestamps + + +def get_pointcloud_msg( + timestamp: Time, is_generate_points: bool, frame_id_index: int, is_base_link: bool +): + header = create_header(timestamp, frame_id_index, is_base_link) + points = create_points() + intensities, return_types, channels, azimuths, elevations, distances, timestamps = ( + create_fields() + ) + + pointcloud_data = bytearray() + + if is_generate_points: + for i in range(NUM_OF_POINTS): + pointcloud_data += struct.pack("fff", points[i][0], points[i][1], points[i][2]) + pointcloud_data += struct.pack("B", intensities[i]) + pointcloud_data += struct.pack("B", return_types[i]) + pointcloud_data += struct.pack("H", channels[i]) + pointcloud_data += struct.pack("f", azimuths[i]) + pointcloud_data += struct.pack("f", elevations[i]) + pointcloud_data += struct.pack("f", distances[i]) + pointcloud_data += struct.pack("I", timestamps[i]) + + fields = [ + PointField(name="x", offset=0, datatype=PointField.FLOAT32, count=1), + PointField(name="y", offset=4, datatype=PointField.FLOAT32, count=1), + PointField(name="z", offset=8, datatype=PointField.FLOAT32, count=1), + PointField(name="intensity", offset=12, datatype=PointField.UINT8, count=1), + PointField(name="return_type", offset=13, datatype=PointField.UINT8, count=1), + PointField(name="channel", offset=14, datatype=PointField.UINT16, count=1), + PointField(name="azimuth", offset=16, datatype=PointField.FLOAT32, count=1), + PointField(name="elevation", offset=20, datatype=PointField.FLOAT32, count=1), + PointField(name="distance", offset=24, datatype=PointField.FLOAT32, count=1), + PointField(name="time_stamp", offset=28, datatype=PointField.UINT32, count=1), + ] + + pointcloud_msg = PointCloud2( + header=header, + height=1, + width=NUM_OF_POINTS, + is_dense=True, + is_bigendian=False, + point_step=32, # 3*4 + 1 + 1 + 2 + 4 + 4 + 4 + 4 = 32 bytes per point + row_step=32 * NUM_OF_POINTS, + fields=fields, + data=pointcloud_data, + ) + + return pointcloud_msg + + +def generate_transform_msg(parent_frame, child_frame, x, y, z, qx, qy, qz, qw): + tf_msg = TransformStamped() + tf_msg.header.stamp = global_timestamp + tf_msg.header.frame_id = parent_frame + tf_msg.child_frame_id = child_frame + tf_msg.transform.translation.x = x + tf_msg.transform.translation.y = y + tf_msg.transform.translation.z = z + tf_msg.transform.rotation.x = qx + tf_msg.transform.rotation.y = qy + tf_msg.transform.rotation.z = qz + tf_msg.transform.rotation.w = qw + return tf_msg + + +def generate_static_transform_msgs(): + tf_top_lidar_msg = generate_transform_msg( + parent_frame="base_link", + child_frame=FRAME_ID_LISTS[0], + x=0.0, + y=0.0, + z=5.0, + qx=0.0, + qy=0.0, + qz=0.0, + qw=1.0, + ) + + tf_right_lidar_msg = generate_transform_msg( + parent_frame="base_link", + child_frame=FRAME_ID_LISTS[1], + x=0.0, + y=5.0, + z=5.0, + qx=0.0, + qy=0.0, + qz=0.0, + qw=1.0, + ) + + tf_left_lidar_msg = generate_transform_msg( + parent_frame="base_link", + child_frame=FRAME_ID_LISTS[2], + x=0.0, + y=-5.0, + z=5.0, + qx=0.0, + qy=0.0, + qz=0.0, + qw=1.0, + ) + + return [tf_top_lidar_msg, tf_right_lidar_msg, tf_left_lidar_msg] + + +def generate_twist_msg(): + twist_header = Header() + twist_header.stamp = Time(seconds=global_seconds, nanoseconds=global_nanosceonds).to_msg() + twist_header.frame_id = "base_link" + twist_msg = TwistWithCovarianceStamped() + twist_msg.header = twist_header + twist_msg.twist.twist.linear.x = 1.0 + + return twist_msg + + +def get_output_points(cloud_msg): + points_list = [] + for point in point_cloud2.read_points(cloud_msg, field_names=("x", "y", "z"), skip_nans=True): + points_list.append([point[0], point[1], point[2]]) + points = np.array(points_list, dtype=np.float32) + return points + + +class TestConcatenateNode(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Init ROS at once + rclpy.init() + + @classmethod + def tearDownClass(cls): + # Shutdown ROS at once + rclpy.shutdown() + + def setUp(self): + self.node = rclpy.create_node("test_concat_node") + tf_msg = generate_static_transform_msgs() + self.tf_broadcaster = StaticTransformBroadcaster(self.node) + self.tf_broadcaster.sendTransform(tf_msg) + self.msg_buffer = [] + self.twist_publisher, self.pointcloud_publishers = self.create_pub_sub() + + def tearDown(self): + self.node.destroy_node() + + def callback(self, msg: PointCloud2): + self.msg_buffer.append(msg) + + def create_pub_sub(self): + # QoS profile for sensor data + sensor_qos = QoSProfile( + history=QoSHistoryPolicy.KEEP_LAST, + depth=10, + reliability=QoSReliabilityPolicy.BEST_EFFORT, + durability=QoSDurabilityPolicy.VOLATILE, + ) + # Publishers + twist_publisher = self.node.create_publisher( + TwistWithCovarianceStamped, + "/test/sensing/vehicle_velocity_converter/twist_with_covariance", + 10, + ) + + pointcloud_publishers = {} + for idx, input_lidar_topic in enumerate(INPUT_LIDAR_TOPICS): + pointcloud_publishers[idx] = self.node.create_publisher( + PointCloud2, + input_lidar_topic, + qos_profile=sensor_qos, + ) + + # create subscriber + self.msg_buffer = [] + self.node.create_subscription( + PointCloud2, + "/test/sensing/lidar/concatenated/pointcloud", + self.callback, + qos_profile=sensor_qos, + ) + + return twist_publisher, pointcloud_publishers + + def test_1_normal_inputs(self): + """Test the normal situation when no pointcloud is delayed or dropped. + + This can test that + 1. Concatenate works fine when all pointclouds are arrived in time. + 2. The motion compensation of concatenation works well. + """ + time.sleep(1) + global global_seconds + + twist_msg = generate_twist_msg() + self.twist_publisher.publish(twist_msg) + + for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): + pointcloud_seconds = global_seconds + pointcloud_nanoseconds = global_nanosceonds + frame_idx * milliseconds * 40 # add 40 ms + pointcloud_timestamp = Time( + seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds + ).to_msg() + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=True, + frame_id_index=frame_idx, + is_base_link=False, + ) + self.pointcloud_publishers[frame_idx].publish(pointcloud_msg) + time.sleep(0.01) + + rclpy.spin_once(self.node, timeout_sec=0.1) + + self.assertEqual( + len(self.msg_buffer), + 1, + "The number of concatenate pointcloud has different number as expected.", + ) + self.assertEqual( + len(get_output_points(self.msg_buffer[0])), + NUM_OF_POINTS * len(FRAME_ID_LISTS), + "The concatenate pointcloud has a different number of point as expected", + ) + + # test tf + self.assertEqual( + self.msg_buffer[0].header.frame_id, + "base_link", + "The concatenate pointcloud frame id is not base_link", + ) + + expected_pointcloud = np.array( + [ + [1.08, -5, 5], + [0.08, -4, 5], + [0.08, -5, 6], + [1.04, 5, 5], + [0.04, 6, 5], + [0.04, 5, 6], + [1, 0, 5], + [0, 1, 5], + [0, 0, 6], + ], + dtype=np.float32, + ) + + cloud_arr = get_output_points(self.msg_buffer[0]) + print("cloud_arr: ", cloud_arr) + self.assertTrue( + np.allclose(cloud_arr, expected_pointcloud, atol=1e-3), + "The concatenation node have wierd output", + ) + + global_seconds += 1 + + def test_2_normal_inputs_with_noise(self): + """Test the normal situation when no pointcloud is delayed or dropped. Additionally, the pointcloud's timestamp is not ideal which has some noise. + + This can test that + 1. Concatenate works fine when pointclouds' timestamp has noise. + """ + time.sleep(1) + global global_seconds + + twist_msg = generate_twist_msg() + self.twist_publisher.publish(twist_msg) + + for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): + noise = random.uniform(-10, 10) * milliseconds + pointcloud_seconds = global_seconds + pointcloud_nanoseconds = ( + global_nanosceonds + frame_idx * milliseconds * 40 + noise + ) # add 40 ms and noise (-10 to 10 ms) + pointcloud_timestamp = Time( + seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds + ).to_msg() + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=True, + frame_id_index=frame_idx, + is_base_link=False, + ) + self.pointcloud_publishers[frame_idx].publish(pointcloud_msg) + time.sleep(0.01) + + rclpy.spin_once(self.node, timeout_sec=0.1) + + self.assertEqual( + len(self.msg_buffer), + 1, + "The number of concatenate pointcloud has different number as expected.", + ) + self.assertEqual( + len(get_output_points(self.msg_buffer[0])), + NUM_OF_POINTS * len(FRAME_ID_LISTS), + "The concatenate pointcloud has a different number of point as expected", + ) + + # test tf + self.assertEqual( + self.msg_buffer[0].header.frame_id, + "base_link", + "The concatenate pointcloud frame id is not base_link", + ) + + # test transformed points + expected_pointcloud = np.array( + [ + [1.08, -5, 5], + [0.08, -4, 5], + [0.08, -5, 6], + [1.04, 5, 5], + [0.04, 6, 5], + [0.04, 5, 6], + [1, 0, 5], + [0, 1, 5], + [0, 0, 6], + ], + dtype=np.float32, + ) + + cloud_arr = get_output_points(self.msg_buffer[0]) + print("cloud_arr: ", cloud_arr) + self.assertTrue( + np.allclose(cloud_arr, expected_pointcloud, atol=1e-2), + "The concatenation node have wierd output", + ) + + def test_3_abnormal_null_pointcloud(self): + """Test the abnormal situation when a pointcloud is empty. + + This can test that + 1. The concatenate node ignore empty pointcloud and concatenate the remain pointcloud. + """ + time.sleep(1) + global global_seconds + + twist_msg = generate_twist_msg() + self.twist_publisher.publish(twist_msg) + + for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): + pointcloud_seconds = global_seconds + pointcloud_nanoseconds = global_nanosceonds + frame_idx * milliseconds * 40 # add 40 ms + pointcloud_timestamp = Time( + seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds + ).to_msg() + + if frame_idx == len(INPUT_LIDAR_TOPICS) - 1: + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=False, + frame_id_index=len(INPUT_LIDAR_TOPICS) - 1, + is_base_link=False, + ) + else: + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=True, + frame_id_index=frame_idx, + is_base_link=False, + ) + + self.pointcloud_publishers[frame_idx].publish(pointcloud_msg) + time.sleep(0.01) + + time.sleep(TIMEOUT_SEC) # timeout threshold + rclpy.spin_once(self.node, timeout_sec=0.1) + + self.assertEqual( + len(self.msg_buffer), + 1, + "The number of concatenate pointcloud has different number as expected.", + ) + self.assertEqual( + len(get_output_points(self.msg_buffer[0])), + NUM_OF_POINTS * (len(FRAME_ID_LISTS) - 1), + "The concatenate pointcloud has a different number of point as expected", + ) + + global_seconds += 1 + + def test_4_abnormal_null_pointcloud_and_drop(self): + """Test the abnormal situation when a pointcloud is empty and other pointclouds are dropped. + + This can test that + 1. The concatenate node ignore empty pointcloud and do not publish any pointcloud. + """ + time.sleep(1) + global global_seconds + + twist_msg = generate_twist_msg() + self.twist_publisher.publish(twist_msg) + + pointcloud_seconds = global_seconds + pointcloud_nanoseconds = global_nanosceonds + pointcloud_timestamp = Time( + seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds + ).to_msg() + + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=False, + frame_id_index=0, + is_base_link=False, + ) + + self.pointcloud_publishers[0].publish(pointcloud_msg) + time.sleep(0.01) + + time.sleep(TIMEOUT_SEC) # timeout threshold + rclpy.spin_once(self.node, timeout_sec=0.1) + + self.assertEqual( + len(self.msg_buffer), + 0, + "The number of concatenate pointcloud has different number as expected.", + ) + + global_seconds += 1 + + def test_5_abnormal_multiple_pointcloud_drop(self): + """Test the abnormal situation when a pointcloud was dropped. + + This can test that + 1. The concatenate node concatenates the remaining pointcloud after the timeout. + """ + time.sleep(1) + global global_seconds + + twist_msg = generate_twist_msg() + self.twist_publisher.publish(twist_msg) + + pointcloud_seconds = global_seconds + pointcloud_nanoseconds = global_nanosceonds + pointcloud_timestamp = Time( + seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds + ).to_msg() + + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=True, + frame_id_index=0, + is_base_link=False, + ) + + self.pointcloud_publishers[0].publish(pointcloud_msg) + time.sleep(0.01) + + time.sleep(TIMEOUT_SEC) # timeout threshold + rclpy.spin_once(self.node, timeout_sec=0.1) + + self.assertEqual( + len(self.msg_buffer), + 1, + "The number of concatenate pointcloud has different number as expected.", + ) + self.assertEqual( + len(get_output_points(self.msg_buffer[0])), + 3, + "The concatenate pointcloud has a different number of point as expected", + ) + + def test_6_abnormal_single_pointcloud_drop(self): + """Test the abnormal situation when a pointcloud was dropped. + + This can test that + 1. The concatenate node concatenate the remain pointcloud after the timeout. + """ + time.sleep(1) + global global_seconds + + twist_msg = generate_twist_msg() + self.twist_publisher.publish(twist_msg) + + for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): + pointcloud_seconds = global_seconds + pointcloud_nanoseconds = global_nanosceonds + frame_idx * milliseconds * 40 # add 40 ms + pointcloud_timestamp = Time( + seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds + ).to_msg() + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=True, + frame_id_index=frame_idx, + is_base_link=False, + ) + self.pointcloud_publishers[frame_idx].publish(pointcloud_msg) + time.sleep(0.02) + + time.sleep(TIMEOUT_SEC) # timeout threshold + rclpy.spin_once(self.node, timeout_sec=0.1) + + # Should receive only one concatenate pointcloud + self.assertEqual( + len(self.msg_buffer), + 1, + "The number of concatenate pointcloud has different number as expected.", + ) + + self.assertEqual( + len(get_output_points(self.msg_buffer[0])), + NUM_OF_POINTS * (len(FRAME_ID_LISTS) - 1), + "The concatenate pointcloud has a different number of point as expected", + ) + + global_seconds += 1 + + def test_7_abnormal_pointcloud_delay(self): + """Test the abnormal situation when a pointcloud was delayed after the timeout. + + This can test that + 1. The concatenate node concatenate the remain pointcloud after the timeout. + 2. The concatenate node will publish the delayed pointcloud after the timeout. + """ + time.sleep(1) + global global_seconds + + twist_msg = generate_twist_msg() + self.twist_publisher.publish(twist_msg) + + for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): + pointcloud_seconds = global_seconds + pointcloud_nanoseconds = global_nanosceonds + frame_idx * milliseconds * 40 # add 40 ms + pointcloud_timestamp = Time( + seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds + ).to_msg() + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=True, + frame_id_index=frame_idx, + is_base_link=False, + ) + self.pointcloud_publishers[frame_idx].publish(pointcloud_msg) + time.sleep(0.02) + + time.sleep(TIMEOUT_SEC) # timeout threshold + rclpy.spin_once(self.node, timeout_sec=0.1) + + pointcloud_seconds = global_seconds + pointcloud_nanoseconds = ( + global_nanosceonds + (len(INPUT_LIDAR_TOPICS) - 1) * milliseconds * 40 + ) # add 40 ms + pointcloud_timestamp = Time( + seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds + ).to_msg() + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=True, + frame_id_index=len(INPUT_LIDAR_TOPICS) - 1, + is_base_link=False, + ) + + self.pointcloud_publishers[len(INPUT_LIDAR_TOPICS) - 1].publish(pointcloud_msg) + + time.sleep(TIMEOUT_SEC) # timeout threshold + rclpy.spin_once(self.node, timeout_sec=0.1) + + # Should receive only one concatenate pointcloud + self.assertEqual( + len(self.msg_buffer), + 2, + "The number of concatenate pointcloud has different number as expected.", + ) + + self.assertEqual( + len(get_output_points(self.msg_buffer[0])), + NUM_OF_POINTS * (len(FRAME_ID_LISTS) - 1), + "The concatenate pointcloud has a different number of point as expected", + ) + + self.assertEqual( + len(get_output_points(self.msg_buffer[1])), + NUM_OF_POINTS, + "The concatenate pointcloud has a different number of point as expected", + ) + + global_seconds += 1 + + def test_8_abnormal_pointcloud_drop_continue_normal(self): + """Test the abnormal situation when a pointcloud was dropped. Afterward, next iteration of pointclouds comes normally. + + This can test that + 1. The concatenate node concatenate the remain pointcloud after the timeout. + 2. The concatenate node concatenate next iteration pointclouds when all of the pointcloud arrived. + """ + time.sleep(1) + global global_seconds + + twist_msg = generate_twist_msg() + self.twist_publisher.publish(twist_msg) + + for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): + pointcloud_seconds = global_seconds + pointcloud_nanoseconds = global_nanosceonds + frame_idx * milliseconds * 40 # add 40 ms + pointcloud_timestamp = Time( + seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds + ).to_msg() + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=True, + frame_id_index=frame_idx, + is_base_link=False, + ) + self.pointcloud_publishers[frame_idx].publish(pointcloud_msg) + time.sleep(0.01) + + time.sleep(TIMEOUT_SEC) + rclpy.spin_once(self.node) + + next_global_nanosecond = global_nanosceonds + 100 * milliseconds + for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): + pointcloud_seconds = global_seconds + pointcloud_nanoseconds = ( + next_global_nanosecond + frame_idx * milliseconds * 40 + ) # add 40 ms + pointcloud_timestamp = Time( + seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds + ).to_msg() + pointcloud_msg = get_pointcloud_msg( + timestamp=pointcloud_timestamp, + is_generate_points=True, + frame_id_index=frame_idx, + is_base_link=False, + ) + self.pointcloud_publishers[frame_idx].publish(pointcloud_msg) + time.sleep(0.01) + + rclpy.spin_once(self.node) + print("len of msg buffer: ", len(self.msg_buffer)) + # Should receive only one concatenate pointcloud + self.assertEqual( + len(self.msg_buffer), + 2, + "The number of concatenate pointcloud has different number as expected.", + ) + + self.assertEqual( + len(get_output_points(self.msg_buffer[0])), + NUM_OF_POINTS * (len(FRAME_ID_LISTS) - 1), + "The concatenate pointcloud has a different number of point as expected", + ) + + self.assertEqual( + len(get_output_points(self.msg_buffer[1])), + NUM_OF_POINTS * len(FRAME_ID_LISTS), + "The concatenate pointcloud has a different number of point as expected", + ) + + global_seconds += 1 From 946365af7dc928d651ee7260ea3fff398c6a8644 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 19 Sep 2024 11:41:28 +0900 Subject: [PATCH 002/115] chore: solve conflicts Signed-off-by: vividf --- .../CMakeLists.txt | 8 +- .../docs/concatenate-data.md | 113 +- .../docs/distortion-corrector.md | 8 + .../image/concatenate_algorithm.drawio.svg | 810 ++++++ .../docs/image/concatenate_data.drawio.svg | 298 --- .../image/concatenate_edge_case.drawio.svg | 2334 +++++++++++++++++ .../image/ideal_timestamp_offset.drawio.svg | 784 ++++++ .../image/noise_timestamp_offset.drawio.svg | 2023 ++++++++++++++ .../concatenate_data/cloud_collector.hpp | 3 +- .../distortion_corrector.hpp | 16 +- .../cocatenate_and_time_sync_node.schema.json | 25 +- .../src/concatenate_data/cloud_collector.cpp | 2 +- .../concatenate_and_time_sync_node.cpp | 38 +- .../distortion_corrector.cpp | 8 +- ....py => test_concatenate_node_component.py} | 294 ++- ...tor.cpp => test_concatenate_node_unit.cpp} | 40 +- .../test/test_distortion_corrector_node.cpp | 22 +- 17 files changed, 6315 insertions(+), 511 deletions(-) create mode 100644 sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_algorithm.drawio.svg delete mode 100644 sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_data.drawio.svg create mode 100644 sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_edge_case.drawio.svg create mode 100644 sensing/autoware_pointcloud_preprocessor/docs/image/ideal_timestamp_offset.drawio.svg create mode 100644 sensing/autoware_pointcloud_preprocessor/docs/image/noise_timestamp_offset.drawio.svg rename sensing/autoware_pointcloud_preprocessor/test/{test_concatenate_node.py => test_concatenate_node_component.py} (79%) rename sensing/autoware_pointcloud_preprocessor/test/{test_cloud_collector.cpp => test_concatenate_node_unit.cpp} (95%) diff --git a/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt b/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt index 8998c1aa38ad7..76933b35c4914 100644 --- a/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt +++ b/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt @@ -243,16 +243,16 @@ if(BUILD_TESTING) test/test_distortion_corrector_node.cpp ) - ament_add_gtest(test_cloud_collector - test/test_cloud_collector.cpp + ament_add_gtest(test_concatenate_node_unit + test/test_concatenate_node_unit.cpp ) target_link_libraries(test_utilities pointcloud_preprocessor_filter) target_link_libraries(test_distortion_corrector_node pointcloud_preprocessor_filter) - target_link_libraries(test_cloud_collector pointcloud_preprocessor_filter) + target_link_libraries(test_concatenate_node_unit pointcloud_preprocessor_filter) add_ros_test( - test/test_concatenate_node.py + test/test_concatenate_node_component.py TIMEOUT "50" ) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index 08f7b92f88975..64477a1e34cdd 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -1,75 +1,102 @@ -# concatenate_data +# concatenate_and_time_synchronize_node ## Purpose -Many self-driving cars combine multiple LiDARs to expand the sensing range. Therefore, a function to combine a plurality of point clouds is required. +The `concatenate_and_time_synchronize_node` is a ROS2 node that combines and synchronizes multiple point clouds into a single concatenated point cloud. This enhances the sensing range for autonomous driving vehicles by integrating data from multiple LiDARs. -To combine multiple sensor data with a similar timestamp, the [message_filters](https://github.com/ros2/message_filters) is often used in the ROS-based system, but this requires the assumption that all inputs can be received. Since safety must be strongly considered in autonomous driving, the point clouds concatenate node must be designed so that even if one sensor fails, the remaining sensor information can be output. +## Inner Workings / Algorithms -## Inner-workings / Algorithms +![concatenate_algorithm](./image/concatenate_algorithm.drawio.svg) -The figure below represents the reception time of each sensor data and how it is combined in the case. +### Step 1: Match and Create Collector -![concatenate_data_timing_chart](./image/concatenate_data.drawio.svg) +When a point cloud arrives, its timestamp is checked, and an offset is subtracted to get the reference timestamp. The node then checks if there is an existing collector with the same reference timestamp. If such a collector exists, the point cloud is added to it. If no such collector exists, a new collector is created with the reference timestamp. + +### Step 2: Trigger the Timer + +Once a collector is created, a timer for that collector starts counting down (this value is defined by `timeout_sec`). The collector begins to concatenate the point clouds either when the required number of point clouds has been collected or when the timer counts down to zero. + +### Step 3: Concatenate the Point Clouds + +The concatenation process involves merging multiple point clouds into a single, concatenated point cloud. The timestamp of the concatenated point cloud will be the earliest timestamp from the input point clouds. By setting the parameter `is_motion_compensated` to `true`, the node will consider the timestamps of the input point clouds and utilize the `twist` information from `geometry_msgs::msg::TwistWithCovarianceStamped` to compensate for motion, aligning the point cloud to the selected (earliest) timestamp. + +### Step 4: Publish the Point Cloud + +After concatenation, the concatenated point cloud is published, and the collector is deleted to free up resources. ## Inputs / Outputs ### Input -| Name | Type | Description | -| --------------- | ------------------------------------------------ | ----------------------------------------------------------------------------- | -| `~/input/twist` | `geometry_msgs::msg::TwistWithCovarianceStamped` | The vehicle odometry is used to interpolate the timestamp of each sensor data | +| Name | Type | Description | +| --------------- | ------------------------------------------------ | --------------------------------------------------------------------------------- | +| `~/input/twist` | `geometry_msgs::msg::TwistWithCovarianceStamped` | The twist information used to interpolate the timestamp of each LiDAR point cloud | +| `~/input/odom` | `nav_msgs::msg::Odometry` | The vehicle odometry used to interpolate the timestamp of each LiDAR point cloud | + +By setting the `input_twist_topic_type` parameter to `twist` or `odom`, the subscriber will subscribe to either `~/input/twist` or `~/input/odom`. If the user doesn't want to use the twist information or vehicle odometry to compensate for motion, set `is_motion_compensated` to `false`. ### Output | Name | Type | Description | | ----------------- | ------------------------------- | ------------------------- | -| `~/output/points` | `sensor_msgs::msg::Pointcloud2` | concatenated point clouds | +| `~/output/points` | `sensor_msgs::msg::Pointcloud2` | Concatenated point clouds | -## Parameters +### Core Parameters -| Name | Type | Default Value | Description | -| -------------------- | ---------------- | ------------- | ------------------------------------------------------------------- | -| `input/points` | vector of string | [] | input topic names that type must be `sensor_msgs::msg::Pointcloud2` | -| `input_frame` | string | "" | input frame id | -| `output_frame` | string | "" | output frame id | -| `has_static_tf_only` | bool | false | flag to listen TF only once | -| `max_queue_size` | int | 5 | max queue size of input/output topics | +{{ json_to_markdown("sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json") }} -### Core Parameters +### Parameter Settings + +Three parameters, `timeout_sec`, `lidar_timestamp_offsets`, and `lidar_timestamp_noise_window`, are critical for collecting point clouds in the same collector and handling edge cases effectively. + +#### timeout_sec + +When network issues occur or when point clouds experience delays in the previous processing pipeline, some point clouds may be delayed or dropped. To address this, the `timeout_sec` parameter is used. If the timer reaches zero, the collector will not wait for delayed or dropped point clouds but will concatenate the remaining point clouds in the collector directly. The figure below demonstrates how `timeout_sec` works with `concatenate_and_time_sync_node`. + +![concatenate_edge_case](./image/concatenate_edge_case.drawio.svg) + +#### lidar_timestamp_offsets + +Since different vehicles have varied designs for LiDAR scanning, the timestamps of each LiDAR may differ. Users need to know the offsets between each LiDAR and set the values in `lidar_timestamp_offsets`. For instance, if there are three LiDARs (left, right, top), and the timestamps for the left, right, and top point clouds are 0.01, 0.05, and 0.09 seconds respectively, the parameters should be set as [0.0, 0.04, 0.08]. This reflects the timestamp differences between the current point cloud and the point cloud with the earliest timestamp. Note that the order of the `lidar_timestamp_offsets` corresponds to the order of the `input_topics`. + +The figure below demonstrates how `lidar_timestamp_offsets` works with `concatenate_and_time_sync_node`. + +![ideal_timestamp_offset](./image/ideal_timestamp_offset.drawio.svg) + +#### lidar_timestamp_noise_window + +Additionally, due to the mechanical design of LiDARs, there may be some jitter in the timestamps of each scan. For example, if the scan frequency is set to 10 Hz (scanning every 100 ms), the timestamps between each scan might not be exactly 100 ms apart. To handle this noise, the `lidar_timestamp_noise_window` parameter is provided. + +Take the left LiDAR from the above example: if the timestamps of the left point clouds are 0.01, 0.11, and 0.21 seconds, the timestamp is ideal without any noise. Then the example will be the same as above. However, if the timestamps of the left point clouds are 0.010, 0.115, and 0.210 seconds respectively, resulting in differences of 105 ms and 95 ms, the noise is 5 ms (compared to 100 ms). In this case, the user should set 0.005 in the `lidar_timestamp_noise_window` parameter. -| Name | Type | Default Value | Description | -| --------------------------------- | ---------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `timeout_sec` | double | 0.1 | tolerance of time to publish next pointcloud [s]
When this time limit is exceeded, the filter concatenates and publishes pointcloud, even if not all the point clouds are subscribed. | -| `input_offset` | vector of double | [] | This parameter can control waiting time for each input sensor pointcloud [s]. You must to set the same length of offsets with input pointclouds numbers.
For its tuning, please see [actual usage page](#how-to-tuning-timeout_sec-and-input_offset). | -| `publish_synchronized_pointcloud` | bool | false | If true, publish the time synchronized pointclouds. All input pointclouds are transformed and then re-published as message named `_synchronized`. | -| `input_twist_topic_type` | std::string | twist | Topic type for twist. Currently support `twist` or `odom`. | +The figure below demonstrates how `lidar_timestamp_noise_window` works with `concatenate_and_time_sync_node`. If the green `X` is in the range of the red triangles, it means that the point cloud matches the reference timestamp of the collector. -## Actual Usage +![noise_timestamp_offset](./image/noise_timestamp_offset.drawio.svg) -For the example of actual usage of this node, please refer to the [preprocessor.launch.py](../launch/preprocessor.launch.py) file. +## Launch -### How to tuning timeout_sec and input_offset +```bash +# The launch file will read the parameters from the concatenate_and_time_sync_node.param.yaml +ros2 launch autoware_pointcloud_preprocessor concatenate_and_time_sync_node.launch.xml +``` -The values in `timeout_sec` and `input_offset` are used in the timer_callback to control concatenation timings. +## Test -- Assumptions - - when the timer runs out, we concatenate the pointclouds in the buffer - - when the first pointcloud comes to buffer, we reset the timer to `timeout_sec` - - when the second and later pointclouds comes to buffer, we reset the timer to `timeout_sec` - `input_offset` - - we assume all lidar has same frequency +```bash +# build autoware_pointcloud_preprocessor +colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release --packages-up-to autoware_pointcloud_preprocessor -| Name | Description | How to tune | -| -------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `timeout_sec` | timeout sec for default timer | To avoid mis-concatenation, at least this value must be shorter than sampling time. | -| `input_offset` | timeout extension when a pointcloud comes to buffer. | The amount of waiting time will be `timeout_sec` - `input_offset`. So, you will need to set larger value for the last-coming pointcloud and smaller for fore-coming. | +# test autoware_pointcloud_preprocessor +colcon test --packages-select autoware_pointcloud_preprocessor --event-handlers console_cohesion+ +``` -### Node separation options for future +### Node separation options -Since the pointcloud concatenation has two process, "time synchronization" and "pointcloud concatenation", it is possible to separate these processes. +There is also an option to separate the concatenate_and_time_sync_node into two nodes: one for `time synchronization` and another for `concatenate pointclouds` ([See this PR](https://github.com/autowarefoundation/autoware.universe/pull/3312)). -In the future, Nodes will be completely separated in order to achieve node loosely coupled nature, but currently both nodes can be selected for backward compatibility ([See this PR](https://github.com/autowarefoundation/autoware.universe/pull/3312)). +Note that the `concatenate_pointclouds` and `time_synchronizer_nodelet` are using the old design of the concatenate node. -## Assumptions / Known limits +## Assumptions / Known Limits -It is necessary to assume that the vehicle odometry value exists, the sensor data and odometry timestamp are correct, and the TF from `base_link` to `sensor_frame` is also correct. +- If `is_motion_compensated` is set to `false`, the `concatenate_and_time_sync_node` will directly concatenate the point clouds without applying for motion compensation. This can save several milliseconds depending on the number of LiDARs being concatenated. Therefore, if the timestamp differences between point clouds are negligible, the user can set `is_motion_compensated` to `false` and omit the need for twist or odometry input for the node. +- As mentioned above, the user should clearly understand how their LiDAR's point cloud timestamps are managed to set the parameters correctly. diff --git a/sensing/autoware_pointcloud_preprocessor/docs/distortion-corrector.md b/sensing/autoware_pointcloud_preprocessor/docs/distortion-corrector.md index ab5a07b5279bc..44a064e89ad1a 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/distortion-corrector.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/distortion-corrector.md @@ -44,6 +44,14 @@ Please note that the processing time difference between the two distortion metho ros2 launch autoware_pointcloud_preprocessor distortion_corrector.launch.xml ``` +## Test + +```bash +colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release --packages-up-to autoware_pointcloud_preprocessor + +colcon test --packages-select autoware_pointcloud_preprocessor --event-handlers console_cohesion+ +``` + ## Assumptions / Known limits - The node requires time synchronization between the topics from lidars, twist, and IMU. diff --git a/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_algorithm.drawio.svg b/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_algorithm.drawio.svg new file mode 100644 index 0000000000000..0ca825f5acaa6 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_algorithm.drawio.svg @@ -0,0 +1,810 @@ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Collector +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ left pc +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ top pc +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ right pc +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + +
+
+
+ L +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
stamp: t1_left
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ stamp: t1_right +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
stamp: t1_top
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + +
+
+
+ reference timestamp +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ T +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ R +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + +
+
+
arrival time
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
+
+
+ compare with the +
reference timestamp.
+
If match, add to the group
+
if it doesn't match, create new group
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ Left pointcloud +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ Right pointcloud +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
Top pointcloud
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ timer +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ add to the collector +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + +
+
+
+ create a collector +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ set stamp as +
reference timestamp
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ trigger the timer +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ When timer count to zero +
+ concatenate + publish +
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ When group has +
left, right, top pointclouds
+
+ + concatenate + publish + +
+
+
+
+
+ +
+
+
+
+ + + + + + +
+
+
+
diff --git a/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_data.drawio.svg b/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_data.drawio.svg deleted file mode 100644 index 786fee4c22ed7..0000000000000 --- a/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_data.drawio.svg +++ /dev/null @@ -1,298 +0,0 @@ - - - - - - - - - - -
-
-
- input topic 1 -
-
-
-
- input topic... -
-
- - - - -
-
-
- input topic 2 -
-
-
-
- input topic... -
-
- - - - -
-
-
- input topic 3 -
-
-
-
- input topic... -
-
- - - - - - - -
-
-
- concatenated topic -
-
-
-
- concatenate... -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- timer -
- start -
-
-
-
- timer... -
-
- - - - -
-
-
- this data is abandoned -
-
-
-
- this data i... -
-
- - - - - - - - - -
-
-
- t0 -
-
-
-
- t0 -
-
- - - - -
-
-
- t1 -
-
-
-
- t1 -
-
- - - - -
-
-
- t2 -
-
-
-
- t2 -
-
- - - - -
-
-
- t3 -
-
-
-
- t3 -
-
- - - - - - - - - - - - - - -
-
-
- timer -
- start -
-
-
-
- timer... -
-
- - - - - -
-
-
- timeout -
-
-
-
- timeout -
-
- - - - - - -
-
-
- t4 -
-
-
-
- t4 -
-
-
- - - - Viewer does not support full SVG 1.1 - - -
diff --git a/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_edge_case.drawio.svg b/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_edge_case.drawio.svg new file mode 100644 index 0000000000000..bf81613c7e536 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_edge_case.drawio.svg @@ -0,0 +1,2334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 50 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 100 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 150 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 50 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 100 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 150 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 50 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 100 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 150 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 50 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 100 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 150 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+
+ receive all pc +
+ concatenate +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+
+ group 1: + timeout +
+ concatenate +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + Normal + +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ + One pointcloud + +
+ + drop + +
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ + Several pointclouds + +
+ + delay + +
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ + Several pointclouds + +
+ + delay, and one drop + +
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ + Several pointclouds + +
+ + delay, and drop + +
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ + One pointcloud + +
+ + delay + +
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 50 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 100 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 150 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
group 1 created.
+
timer start
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+
group 2 created.
+
timer start
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+
group 2
+
concatenate
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+
group 1 created.
+
timer start
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+
group 1
+
concatenate
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 50 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 100 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 150 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
group 1 created.
+
timer start
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + +
+
+
+
group 1
+
concatenate
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 50 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 100 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 150 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
group 1 created.
+
timer start
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + +
+
+
+
group 1
+
concatenate
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+
Can decide to
+
publish or not
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + +
+
+
120 ms
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + +
+
+
120 ms
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
120 ms
+
+
+
+ +
+
+
+
+
+
+
+
+
diff --git a/sensing/autoware_pointcloud_preprocessor/docs/image/ideal_timestamp_offset.drawio.svg b/sensing/autoware_pointcloud_preprocessor/docs/image/ideal_timestamp_offset.drawio.svg new file mode 100644 index 0000000000000..0b8eca9dd75aa --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/docs/image/ideal_timestamp_offset.drawio.svg @@ -0,0 +1,784 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0.01 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 0.05 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 0.09 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ reference +
stamp
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0.01 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 0.05 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ reference +
stamp
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ minus offset 40 ms +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + +
+
+
+ minus offset 80 ms +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ lidar_timestamp_offset: [0.0, 0.04, 0.08] +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ ideal timestamp +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ first arrive pointcloud +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ reference timestamp +
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + +
+
+
+ minus offset 80 ms +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + +
+
+
+ minus offset 40 ms +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ compare with +
reference stamp
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 0.09 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ compare with +
reference stamp
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ compare with +
reference stamp
+
+
+
+
+ +
+
+
+
+
+
+
+
diff --git a/sensing/autoware_pointcloud_preprocessor/docs/image/noise_timestamp_offset.drawio.svg b/sensing/autoware_pointcloud_preprocessor/docs/image/noise_timestamp_offset.drawio.svg new file mode 100644 index 0000000000000..bf5d604d2fb94 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/docs/image/noise_timestamp_offset.drawio.svg @@ -0,0 +1,2023 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0.01 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 0.05 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 0.09 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ reference +
stamp
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ 0 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 20 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 40 +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ reference +
stamp
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + +
+
+
+ minus offset 20 ms +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + +
+
+
+ minus offset 40 ms +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + +
+
+
-5ms
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+10ms
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
5
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
-5
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
-5
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
-10
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
5
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
10
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+
+ minus offset 40 ms +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
-15
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
-5
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
5
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
15
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + +
+
+
-15ms
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ minus offset 80 ms +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
-10
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
10
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
5
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
-5
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + +
+
+
+5ms
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+10ms
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
10
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
-10
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
-10
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
10
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
15
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
-15
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
-15ms
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ ideal timestamp +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ first arrive pointcloud +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ reference timestamp +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
real timestamp
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ possible reference timestamp +
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ possible timestamp +
for non-first pointcloud
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
+
+
+ timestamp of non-first pointcloud +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ lidar_timestamp_offset: [0.0, 0.04, 0.08] +
+ + lidar_timestamp_noise_window: [0.005, 0.01, 0.015] +
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ + Case 2 + +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ + Case 1 + +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 0.01 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 0.05 +
+
+
+
+ +
+
+
+
+ + + + + + + + +
+
+
+ 0.09 +
+
+
+
+ +
+
+
+
+
+
+
+
diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 29085472579c0..f71bb1df968b5 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -94,8 +94,7 @@ class CloudCollector void deleteCollector(); - std::unordered_map - get_topic_to_cloud_map(); + std::unordered_map getTopicToCloudMap(); private: std::shared_ptr concatenate_node_; diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/distortion_corrector/distortion_corrector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/distortion_corrector/distortion_corrector.hpp index e786bff04b3cd..7e26d4c1cf2c8 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/distortion_corrector/distortion_corrector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/distortion_corrector/distortion_corrector.hpp @@ -50,10 +50,10 @@ namespace autoware::pointcloud_preprocessor class DistortionCorrectorBase { public: - virtual bool pointcloud_transform_exists() = 0; - virtual bool pointcloud_transform_needed() = 0; - virtual std::deque get_twist_queue() = 0; - virtual std::deque get_angular_velocity_queue() = 0; + virtual bool pointcloudTransformExists() = 0; + virtual bool pointcloudTransformNeeded() = 0; + virtual std::deque getTwistQueue() = 0; + virtual std::deque getAngularVelocityQueue() = 0; virtual void processTwistMessage( const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr twist_msg) = 0; @@ -104,10 +104,10 @@ class DistortionCorrector : public DistortionCorrectorBase managed_tf_buffer_ = std::make_unique(node, has_static_tf_only); } - bool pointcloud_transform_exists(); - bool pointcloud_transform_needed(); - std::deque get_twist_queue(); - std::deque get_angular_velocity_queue(); + bool pointcloudTransformExists(); + bool pointcloudTransformNeeded(); + std::deque getTwistQueue(); + std::deque getAngularVelocityQueue(); void processTwistMessage( const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr twist_msg) override; diff --git a/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json index 059411e02ab92..183b807ec83b2 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json @@ -16,11 +16,6 @@ "default": 0.0, "description": "Timeout in seconds." }, - "offset_tolerance": { - "type": "number", - "default": 0.0, - "description": "Tolerance for offset." - }, "is_motion_compensated": { "type": "boolean", "default": true, @@ -36,6 +31,11 @@ "default": true, "description": "Flag to indicate if input frame should be kept in synchronized point cloud." }, + "publish_previous_but_late_pointcloud": { + "type": "boolean", + "default": false, + "description": "Flag to indicate if a concatenated point cloud should be published if its timestamp is earlier than the previous published concatenated point cloud." + }, "synchronized_pointcloud_postfix": { "type": "string", "default": "pointcloud", @@ -56,7 +56,7 @@ }, "output_frame": { "type": "string", - "default": "", + "default": "base_link", "description": "Output frame." }, "lidar_timestamp_offsets": { @@ -66,20 +66,29 @@ }, "default": [], "description": "List of LiDAR timestamp offsets." + }, + "lidar_timestamp_noise_window": { + "type": "array", + "items": { + "type": "number" + }, + "default": [], + "description": "List of LiDAR timestamp noise window." } }, "required": [ "maximum_queue_size", "timeout_sec", - "offset_tolerance", "is_motion_compensated", "publish_synchronized_pointcloud", "keep_input_frame_in_synchronized_pointcloud", + "publish_previous_but_late_pointcloud", "synchronized_pointcloud_postfix", "input_twist_topic_type", "input_topics", "output_frame", - "lidar_timestamp_offsets" + "lidar_timestamp_offsets", + "lidar_timestamp_noise_window" ] } }, diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 28ae3fc85dc12..315d53e304c4c 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -149,7 +149,7 @@ void CloudCollector::deleteCollector() } std::unordered_map -CloudCollector::get_topic_to_cloud_map() +CloudCollector::getTopicToCloudMap() { return topic_to_cloud_map_; } diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 475a203319727..920f2aa6e2466 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -147,23 +147,25 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro } // Subscribers - if (params_.input_twist_topic_type == "twist") { - twist_sub_ = this->create_subscription( - "~/input/twist", rclcpp::QoS{100}, - std::bind( - &PointCloudConcatenateDataSynchronizerComponent::twist_callback, this, - std::placeholders::_1)); - } else if (params_.input_twist_topic_type == "odom") { - odom_sub_ = this->create_subscription( - "~/input/odom", rclcpp::QoS{100}, - std::bind( - &PointCloudConcatenateDataSynchronizerComponent::odom_callback, this, - std::placeholders::_1)); - } else { - RCLCPP_ERROR_STREAM( - get_logger(), "input_twist_topic_type is invalid: " << params_.input_twist_topic_type); - throw std::runtime_error( - "input_twist_topic_type is invalid: " + params_.input_twist_topic_type); + if (params_.is_motion_compensated) { + if (params_.input_twist_topic_type == "twist") { + twist_sub_ = this->create_subscription( + "~/input/twist", rclcpp::QoS{100}, + std::bind( + &PointCloudConcatenateDataSynchronizerComponent::twist_callback, this, + std::placeholders::_1)); + } else if (params_.input_twist_topic_type == "odom") { + odom_sub_ = this->create_subscription( + "~/input/odom", rclcpp::QoS{100}, + std::bind( + &PointCloudConcatenateDataSynchronizerComponent::odom_callback, this, + std::placeholders::_1)); + } else { + RCLCPP_ERROR_STREAM( + get_logger(), "input_twist_topic_type is invalid: " << params_.input_twist_topic_type); + throw std::runtime_error( + "input_twist_topic_type is invalid: " + params_.input_twist_topic_type); + } } pointcloud_subs.resize(params_.input_topics.size()); @@ -184,7 +186,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro RCLCPP_DEBUG_STREAM(get_logger(), " - " << input_topic); } - // Cloud handler + // Combine cloud handler combine_cloud_handler_ = std::make_shared( this, params_.input_topics, params_.output_frame, params_.is_motion_compensated, params_.keep_input_frame_in_synchronized_pointcloud); diff --git a/sensing/autoware_pointcloud_preprocessor/src/distortion_corrector/distortion_corrector.cpp b/sensing/autoware_pointcloud_preprocessor/src/distortion_corrector/distortion_corrector.cpp index d0119fbc44f24..eff4e726352b6 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/distortion_corrector/distortion_corrector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/distortion_corrector/distortion_corrector.cpp @@ -25,25 +25,25 @@ namespace autoware::pointcloud_preprocessor { template -bool DistortionCorrector::pointcloud_transform_exists() +bool DistortionCorrector::pointcloudTransformExists() { return pointcloud_transform_exists_; } template -bool DistortionCorrector::pointcloud_transform_needed() +bool DistortionCorrector::pointcloudTransformNeeded() { return pointcloud_transform_needed_; } template -std::deque DistortionCorrector::get_twist_queue() +std::deque DistortionCorrector::getTwistQueue() { return twist_queue_; } template -std::deque DistortionCorrector::get_angular_velocity_queue() +std::deque DistortionCorrector::getAngularVelocityQueue() { return angular_velocity_queue_; } diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py similarity index 79% rename from sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node.py rename to sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index ac1a63b9ef07a..ce0c2653022d1 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -17,21 +17,17 @@ import random import struct import time +from typing import List +from typing import Tuple import unittest -# TODO: remove unused later from geometry_msgs.msg import TransformStamped from geometry_msgs.msg import TwistWithCovarianceStamped import launch import launch.actions -from launch.logging import get_logger from launch_ros.actions import ComposableNodeContainer from launch_ros.descriptions import ComposableNode import launch_testing -import launch_testing.actions -import launch_testing.asserts -import launch_testing.markers -import launch_testing.tools import numpy as np import pytest import rclpy @@ -46,8 +42,6 @@ from std_msgs.msg import Header from tf2_ros.static_transform_broadcaster import StaticTransformBroadcaster -logger = get_logger(__name__) - INPUT_LIDAR_TOPICS = [ "/test/sensing/lidar/left/pointcloud", "/test/sensing/lidar/right/pointcloud", @@ -60,12 +54,19 @@ ] TIMEOUT_SEC = 0.2 +TIMESTAMP_OFFSET = [0.0, 0.04, 0.08] +TIMESTAMP_NOISE = 0.01 # 10 ms + NUM_OF_POINTS = 3 +DEBUG = False +MILLISECONDS = 1000000 + + +STANDARD_TOLERANCE = 1e-4 +COARSE_TOLERANCE = TIMESTAMP_NOISE * 2 global_seconds = 10 global_nanosceonds = 100000000 -milliseconds = 1000000 -global_timestamp = Time(seconds=global_seconds, nanoseconds=global_nanosceonds).to_msg() @pytest.mark.launch_test @@ -93,8 +94,12 @@ def generate_test_description(): "input_twist_topic_type": "twist", "input_topics": INPUT_LIDAR_TOPICS, "output_frame": "base_link", - "lidar_timestamp_offsets": [0.0, 0.04, 0.08], - "lidar_timestamp_noise_window": [0.01, 0.01, 0.01], + "lidar_timestamp_offsets": TIMESTAMP_OFFSET, + "lidar_timestamp_noise_window": [ + TIMESTAMP_NOISE, + TIMESTAMP_NOISE, + TIMESTAMP_NOISE, + ], } ], extra_arguments=[{"use_intra_process_comms": True}], @@ -119,7 +124,7 @@ def generate_test_description(): ) -def create_header(timestamp: Time, frame_id_index: int, is_base_link: bool): +def create_header(timestamp: Time, frame_id_index: int, is_base_link: bool) -> Header: header = Header() header.stamp = timestamp @@ -130,11 +135,13 @@ def create_header(timestamp: Time, frame_id_index: int, is_base_link: bool): return header -def create_points(): +def create_points() -> List[Tuple[float, float, float]]: return [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)] -def create_fields(): +def create_fields() -> ( + Tuple[List[int], List[int], List[int], List[float], List[float], List[float], List[int]] +): # The values of the fields do not influence the results. intensities = [255] * NUM_OF_POINTS return_types = [1] * NUM_OF_POINTS @@ -148,7 +155,7 @@ def create_fields(): def get_pointcloud_msg( timestamp: Time, is_generate_points: bool, frame_id_index: int, is_base_link: bool -): +) -> PointCloud2: header = create_header(timestamp, frame_id_index, is_base_link) points = create_points() intensities, return_types, channels, azimuths, elevations, distances, timestamps = ( @@ -196,9 +203,19 @@ def get_pointcloud_msg( return pointcloud_msg -def generate_transform_msg(parent_frame, child_frame, x, y, z, qx, qy, qz, qw): +def generate_transform_msg( + parent_frame: str, + child_frame: str, + x: float, + y: float, + z: float, + qx: float, + qy: float, + qz: float, + qw: float, +) -> TransformStamped: tf_msg = TransformStamped() - tf_msg.header.stamp = global_timestamp + tf_msg.header.stamp = Time(seconds=global_seconds, nanoseconds=global_nanosceonds).to_msg() tf_msg.header.frame_id = parent_frame tf_msg.child_frame_id = child_frame tf_msg.transform.translation.x = x @@ -211,7 +228,7 @@ def generate_transform_msg(parent_frame, child_frame, x, y, z, qx, qy, qz, qw): return tf_msg -def generate_static_transform_msgs(): +def generate_static_transform_msgs() -> List[TransformStamped]: tf_top_lidar_msg = generate_transform_msg( parent_frame="base_link", child_frame=FRAME_ID_LISTS[0], @@ -251,7 +268,7 @@ def generate_static_transform_msgs(): return [tf_top_lidar_msg, tf_right_lidar_msg, tf_left_lidar_msg] -def generate_twist_msg(): +def generate_twist_msg() -> TwistWithCovarianceStamped: twist_header = Header() twist_header.stamp = Time(seconds=global_seconds, nanoseconds=global_nanosceonds).to_msg() twist_header.frame_id = "base_link" @@ -262,7 +279,7 @@ def generate_twist_msg(): return twist_msg -def get_output_points(cloud_msg): +def get_output_points(cloud_msg) -> np.ndarray: points_list = [] for point in point_cloud2.read_points(cloud_msg, field_names=("x", "y", "z"), skip_nans=True): points_list.append([point[0], point[1], point[2]]) @@ -344,7 +361,7 @@ def test_1_normal_inputs(self): for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + frame_idx * milliseconds * 40 # add 40 ms + pointcloud_nanoseconds = global_nanosceonds + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -364,18 +381,6 @@ def test_1_normal_inputs(self): 1, "The number of concatenate pointcloud has different number as expected.", ) - self.assertEqual( - len(get_output_points(self.msg_buffer[0])), - NUM_OF_POINTS * len(FRAME_ID_LISTS), - "The concatenate pointcloud has a different number of point as expected", - ) - - # test tf - self.assertEqual( - self.msg_buffer[0].header.frame_id, - "base_link", - "The concatenate pointcloud frame id is not base_link", - ) expected_pointcloud = np.array( [ @@ -392,13 +397,22 @@ def test_1_normal_inputs(self): dtype=np.float32, ) - cloud_arr = get_output_points(self.msg_buffer[0]) - print("cloud_arr: ", cloud_arr) + concatenate_cloud = get_output_points(self.msg_buffer[0]) + + if DEBUG: + print("concatenate_cloud: ", concatenate_cloud) + self.assertTrue( - np.allclose(cloud_arr, expected_pointcloud, atol=1e-3), + np.allclose(concatenate_cloud, expected_pointcloud, atol=1e-3), "The concatenation node have wierd output", ) + self.assertEqual( + self.msg_buffer[0].header.frame_id, + "base_link", + "The concatenate pointcloud frame id is not base_link", + ) + global_seconds += 1 def test_2_normal_inputs_with_noise(self): @@ -414,10 +428,10 @@ def test_2_normal_inputs_with_noise(self): self.twist_publisher.publish(twist_msg) for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): - noise = random.uniform(-10, 10) * milliseconds + noise = random.uniform(-10, 10) * MILLISECONDS pointcloud_seconds = global_seconds pointcloud_nanoseconds = ( - global_nanosceonds + frame_idx * milliseconds * 40 + noise + global_nanosceonds + frame_idx * MILLISECONDS * 40 + noise ) # add 40 ms and noise (-10 to 10 ms) pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds @@ -438,20 +452,7 @@ def test_2_normal_inputs_with_noise(self): 1, "The number of concatenate pointcloud has different number as expected.", ) - self.assertEqual( - len(get_output_points(self.msg_buffer[0])), - NUM_OF_POINTS * len(FRAME_ID_LISTS), - "The concatenate pointcloud has a different number of point as expected", - ) - # test tf - self.assertEqual( - self.msg_buffer[0].header.frame_id, - "base_link", - "The concatenate pointcloud frame id is not base_link", - ) - - # test transformed points expected_pointcloud = np.array( [ [1.08, -5, 5], @@ -467,10 +468,12 @@ def test_2_normal_inputs_with_noise(self): dtype=np.float32, ) - cloud_arr = get_output_points(self.msg_buffer[0]) - print("cloud_arr: ", cloud_arr) + concatenate_cloud = get_output_points(self.msg_buffer[0]) + if DEBUG: + print("concatenate_cloud: ", concatenate_cloud) + self.assertTrue( - np.allclose(cloud_arr, expected_pointcloud, atol=1e-2), + np.allclose(concatenate_cloud, expected_pointcloud, atol=2e-2), "The concatenation node have wierd output", ) @@ -488,7 +491,7 @@ def test_3_abnormal_null_pointcloud(self): for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + frame_idx * milliseconds * 40 # add 40 ms + pointcloud_nanoseconds = global_nanosceonds + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -519,10 +522,26 @@ def test_3_abnormal_null_pointcloud(self): 1, "The number of concatenate pointcloud has different number as expected.", ) - self.assertEqual( - len(get_output_points(self.msg_buffer[0])), - NUM_OF_POINTS * (len(FRAME_ID_LISTS) - 1), - "The concatenate pointcloud has a different number of point as expected", + + expected_pointcloud = np.array( + [ + [1.04, 5, 5], + [0.04, 6, 5], + [0.04, 5, 6], + [1, 0, 5], + [0, 1, 5], + [0, 0, 6], + ], + dtype=np.float32, + ) + + concatenate_cloud = get_output_points(self.msg_buffer[0]) + if DEBUG: + print("concatenate_cloud: ", concatenate_cloud) + + self.assertTrue( + np.allclose(concatenate_cloud, expected_pointcloud, atol=1e-3), + "The concatenation node have wierd output", ) global_seconds += 1 @@ -567,10 +586,10 @@ def test_4_abnormal_null_pointcloud_and_drop(self): global_seconds += 1 def test_5_abnormal_multiple_pointcloud_drop(self): - """Test the abnormal situation when a pointcloud was dropped. + """Test the abnormal situation when multiple pointclouds were dropped (only one poincloud arrive). This can test that - 1. The concatenate node concatenates the remaining pointcloud after the timeout. + 1. The concatenate node concatenates the single pointcloud after the timeout. """ time.sleep(1) global global_seconds @@ -602,10 +621,23 @@ def test_5_abnormal_multiple_pointcloud_drop(self): 1, "The number of concatenate pointcloud has different number as expected.", ) - self.assertEqual( - len(get_output_points(self.msg_buffer[0])), - 3, - "The concatenate pointcloud has a different number of point as expected", + + expected_pointcloud = np.array( + [ + [1, 0, 5], + [0, 1, 5], + [0, 0, 6], + ], + dtype=np.float32, + ) + + concatenate_cloud = get_output_points(self.msg_buffer[0]) + if DEBUG: + print("concatenate_cloud: ", concatenate_cloud) + + self.assertTrue( + np.allclose(concatenate_cloud, expected_pointcloud, atol=1e-3), + "The concatenation node have wierd output", ) def test_6_abnormal_single_pointcloud_drop(self): @@ -622,7 +654,7 @@ def test_6_abnormal_single_pointcloud_drop(self): for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + frame_idx * milliseconds * 40 # add 40 ms + pointcloud_nanoseconds = global_nanosceonds + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -645,10 +677,25 @@ def test_6_abnormal_single_pointcloud_drop(self): "The number of concatenate pointcloud has different number as expected.", ) - self.assertEqual( - len(get_output_points(self.msg_buffer[0])), - NUM_OF_POINTS * (len(FRAME_ID_LISTS) - 1), - "The concatenate pointcloud has a different number of point as expected", + expected_pointcloud = np.array( + [ + [1.04, 5, 5], + [0.04, 6, 5], + [0.04, 5, 6], + [1, 0, 5], + [0, 1, 5], + [0, 0, 6], + ], + dtype=np.float32, + ) + + concatenate_cloud = get_output_points(self.msg_buffer[0]) + if DEBUG: + print("concatenate_cloud: ", concatenate_cloud) + + self.assertTrue( + np.allclose(concatenate_cloud, expected_pointcloud, atol=1e-3), + "The concatenation node have wierd output", ) global_seconds += 1 @@ -668,7 +715,7 @@ def test_7_abnormal_pointcloud_delay(self): for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + frame_idx * milliseconds * 40 # add 40 ms + pointcloud_nanoseconds = global_nanosceonds + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -686,7 +733,7 @@ def test_7_abnormal_pointcloud_delay(self): pointcloud_seconds = global_seconds pointcloud_nanoseconds = ( - global_nanosceonds + (len(INPUT_LIDAR_TOPICS) - 1) * milliseconds * 40 + global_nanosceonds + (len(INPUT_LIDAR_TOPICS) - 1) * MILLISECONDS * 40 ) # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds @@ -710,16 +757,43 @@ def test_7_abnormal_pointcloud_delay(self): "The number of concatenate pointcloud has different number as expected.", ) - self.assertEqual( - len(get_output_points(self.msg_buffer[0])), - NUM_OF_POINTS * (len(FRAME_ID_LISTS) - 1), - "The concatenate pointcloud has a different number of point as expected", + expected_pointcloud1 = np.array( + [ + [1.04, 5, 5], + [0.04, 6, 5], + [0.04, 5, 6], + [1, 0, 5], + [0, 1, 5], + [0, 0, 6], + ], + dtype=np.float32, ) - self.assertEqual( - len(get_output_points(self.msg_buffer[1])), - NUM_OF_POINTS, - "The concatenate pointcloud has a different number of point as expected", + concatenate_cloud1 = get_output_points(self.msg_buffer[0]) + if DEBUG: + print("concatenate_cloud 1: ", concatenate_cloud1) + + self.assertTrue( + np.allclose(concatenate_cloud1, expected_pointcloud1, atol=1e-3), + "The concatenation node have wierd output", + ) + + expected_pointcloud2 = np.array( + [ + [1, -5, 5], + [0, -4, 5], + [0, -5, 6], + ], + dtype=np.float32, + ) + + concatenate_cloud2 = get_output_points(self.msg_buffer[1]) + if DEBUG: + print("concatenate_cloud 2: ", concatenate_cloud2) + + self.assertTrue( + np.allclose(concatenate_cloud2, expected_pointcloud2, atol=1e-3), + "The concatenation node have wierd output", ) global_seconds += 1 @@ -739,7 +813,7 @@ def test_8_abnormal_pointcloud_drop_continue_normal(self): for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + frame_idx * milliseconds * 40 # add 40 ms + pointcloud_nanoseconds = global_nanosceonds + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -755,11 +829,11 @@ def test_8_abnormal_pointcloud_drop_continue_normal(self): time.sleep(TIMEOUT_SEC) rclpy.spin_once(self.node) - next_global_nanosecond = global_nanosceonds + 100 * milliseconds + next_global_nanosecond = global_nanosceonds + 100 * MILLISECONDS for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): pointcloud_seconds = global_seconds pointcloud_nanoseconds = ( - next_global_nanosecond + frame_idx * milliseconds * 40 + next_global_nanosecond + frame_idx * MILLISECONDS * 40 ) # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds @@ -774,24 +848,56 @@ def test_8_abnormal_pointcloud_drop_continue_normal(self): time.sleep(0.01) rclpy.spin_once(self.node) - print("len of msg buffer: ", len(self.msg_buffer)) - # Should receive only one concatenate pointcloud + self.assertEqual( len(self.msg_buffer), 2, "The number of concatenate pointcloud has different number as expected.", ) - self.assertEqual( - len(get_output_points(self.msg_buffer[0])), - NUM_OF_POINTS * (len(FRAME_ID_LISTS) - 1), - "The concatenate pointcloud has a different number of point as expected", + expected_pointcloud1 = np.array( + [ + [1.04, 5, 5], + [0.04, 6, 5], + [0.04, 5, 6], + [1, 0, 5], + [0, 1, 5], + [0, 0, 6], + ], + dtype=np.float32, ) - self.assertEqual( - len(get_output_points(self.msg_buffer[1])), - NUM_OF_POINTS * len(FRAME_ID_LISTS), - "The concatenate pointcloud has a different number of point as expected", + concatenate_cloud1 = get_output_points(self.msg_buffer[0]) + if DEBUG: + print("concatenate_cloud 1: ", concatenate_cloud1) + + self.assertTrue( + np.allclose(concatenate_cloud1, expected_pointcloud1, atol=1e-3), + "The concatenation node have wierd output", + ) + + expected_pointcloud2 = np.array( + [ + [1.08, -5, 5], + [0.08, -4, 5], + [0.08, -5, 6], + [1.04, 5, 5], + [0.04, 6, 5], + [0.04, 5, 6], + [1, 0, 5], + [0, 1, 5], + [0, 0, 6], + ], + dtype=np.float32, + ) + + concatenate_cloud2 = get_output_points(self.msg_buffer[1]) + if DEBUG: + print("concatenate_cloud 2: ", concatenate_cloud2) + + self.assertTrue( + np.allclose(concatenate_cloud2, expected_pointcloud2, atol=1e-3), + "The concatenation node have wierd output", ) global_seconds += 1 diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp similarity index 95% rename from sensing/autoware_pointcloud_preprocessor/test/test_cloud_collector.cpp rename to sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index f3f0626364f88..397310f5b4335 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -11,10 +11,10 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// cloud_collector_test.cpp -// cloud_collector_test.cpp -// cloud_collector_test.cpp +// Note: To regenerate the ground truth (GT) for the expected undistorted point cloud values, +// set the "debug_" value to true to display the point cloud values. Then, +// replace the expected values with the newly displayed undistorted point cloud values. #include "autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp" #include "autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp" @@ -175,12 +175,12 @@ class ConcatenateCloudTest : public ::testing::Test static constexpr size_t number_of_points_{3}; static constexpr float standard_tolerance_{1e-4}; static constexpr int number_of_pointcloud_{3}; - static constexpr float timeout_sec_{1}; - bool debug_{true}; + static constexpr float timeout_sec_{0.2}; + bool debug_{false}; }; //////////////////////////////// Test combine_cloud_handler //////////////////////////////// -TEST_F(ConcatenateCloudTest, ProcessTwist) +TEST_F(ConcatenateCloudTest, TestProcessTwist) { auto twist_msg = std::make_shared(); twist_msg->header.stamp = rclcpp::Time(10, 0); @@ -194,7 +194,7 @@ TEST_F(ConcatenateCloudTest, ProcessTwist) EXPECT_EQ(combine_cloud_handler_->twist_ptr_queue_.front()->twist.angular.z, 0.1); } -TEST_F(ConcatenateCloudTest, ProcessOdometry) +TEST_F(ConcatenateCloudTest, TestProcessOdometry) { auto odom_msg = std::make_shared(); odom_msg->header.stamp = rclcpp::Time(10, 0); @@ -208,7 +208,7 @@ TEST_F(ConcatenateCloudTest, ProcessOdometry) EXPECT_EQ(combine_cloud_handler_->twist_ptr_queue_.front()->twist.angular.z, 0.1); } -TEST_F(ConcatenateCloudTest, ComputeTransformToAdjustForOldTimestamp) +TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) { rclcpp::Time old_stamp(10, 100000000, RCL_ROS_TIME); rclcpp::Time new_stamp(10, 150000000, RCL_ROS_TIME); @@ -249,7 +249,7 @@ TEST_F(ConcatenateCloudTest, ComputeTransformToAdjustForOldTimestamp) //////////////////////////////// Test cloud_collector //////////////////////////////// -TEST_F(ConcatenateCloudTest, SetAndGetReferenceTimeStampBoundary) +TEST_F(ConcatenateCloudTest, TestSetAndGetReferenceTimeStampBoundary) { double reference_timestamp = 10.0; double noise_window = 0.1; @@ -259,7 +259,7 @@ TEST_F(ConcatenateCloudTest, SetAndGetReferenceTimeStampBoundary) EXPECT_DOUBLE_EQ(max, 10.1); } -TEST_F(ConcatenateCloudTest, concatenateAndPublishClouds) +TEST_F(ConcatenateCloudTest, TestConcatenateClouds) { rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40000000, RCL_ROS_TIME); @@ -311,14 +311,14 @@ TEST_F(ConcatenateCloudTest, concatenateAndPublishClouds) EXPECT_FLOAT_EQ(*iter_z, expected_pointcloud[i].z()); } - // concatenate cloud should have the oldest pointcloud's timestamp - EXPECT_FLOAT_EQ( - top_timestamp.seconds(), rclcpp::Time(concatenate_cloud_ptr->header.stamp).seconds()); - if (debug_) { RCLCPP_INFO(concatenate_node_->get_logger(), "%s", oss.str().c_str()); } + // test concatenate cloud has the oldest pointcloud's timestamp + EXPECT_FLOAT_EQ( + top_timestamp.seconds(), rclcpp::Time(concatenate_cloud_ptr->header.stamp).seconds()); + // test seperated transformed cloud std::array expected_top_pointcloud = { {Eigen::Vector3f(10.0f, 0.0f, 0.0f), Eigen::Vector3f(0.0f, 10.0f, 0.0f), @@ -405,13 +405,13 @@ TEST_F(ConcatenateCloudTest, concatenateAndPublishClouds) EXPECT_FLOAT_EQ(right_timestamp.seconds(), topic_to_original_stamp_map["lidar_right"]); } -TEST_F(ConcatenateCloudTest, DeleteCollector) +TEST_F(ConcatenateCloudTest, TestDeleteCollector) { collector_->deleteCollector(); EXPECT_TRUE(collectors_.empty()); } -TEST_F(ConcatenateCloudTest, ProcessSingleCloud) +TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) { rclcpp::Time timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = @@ -420,19 +420,19 @@ TEST_F(ConcatenateCloudTest, ProcessSingleCloud) std::make_shared(top_pointcloud); collector_->processCloud("lidar_top", top_pointcloud_ptr); - auto topic_to_cloud_map = collector_->get_topic_to_cloud_map(); + auto topic_to_cloud_map = collector_->getTopicToCloudMap(); EXPECT_EQ(topic_to_cloud_map["lidar_top"], top_pointcloud_ptr); EXPECT_FALSE(collectors_.empty()); - // Sleep for 1.5 seconds - std::this_thread::sleep_for(std::chrono::milliseconds(1500)); + // Sleep for timeout seconds (200 ms) + std::this_thread::sleep_for(std::chrono::milliseconds(200)); rclcpp::spin_some(concatenate_node_); // Collector should concatenate and publish the pointcloud, also delete itself. EXPECT_TRUE(collectors_.empty()); } -TEST_F(ConcatenateCloudTest, ProcessMultipleCloud) +TEST_F(ConcatenateCloudTest, TestProcessMultipleCloud) { rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40000000, RCL_ROS_TIME); diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_distortion_corrector_node.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_distortion_corrector_node.cpp index 047d021a4c6da..2e0306c6217c2 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_distortion_corrector_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_distortion_corrector_node.cpp @@ -286,9 +286,9 @@ TEST_F(DistortionCorrectorTest, TestProcessTwistMessage) auto twist_msg = generateTwistMsg(twist_linear_x_, twist_angular_z_, timestamp); distortion_corrector_2d_->processTwistMessage(twist_msg); - ASSERT_FALSE(distortion_corrector_2d_->get_twist_queue().empty()); - EXPECT_EQ(distortion_corrector_2d_->get_twist_queue().front().twist.linear.x, twist_linear_x_); - EXPECT_EQ(distortion_corrector_2d_->get_twist_queue().front().twist.angular.z, twist_angular_z_); + ASSERT_FALSE(distortion_corrector_2d_->getTwistQueue().empty()); + EXPECT_EQ(distortion_corrector_2d_->getTwistQueue().front().twist.linear.x, twist_linear_x_); + EXPECT_EQ(distortion_corrector_2d_->getTwistQueue().front().twist.angular.z, twist_angular_z_); } TEST_F(DistortionCorrectorTest, TestProcessIMUMessage) @@ -297,9 +297,9 @@ TEST_F(DistortionCorrectorTest, TestProcessIMUMessage) auto imu_msg = generateImuMsg(imu_angular_x_, imu_angular_y_, imu_angular_z_, timestamp); distortion_corrector_2d_->processIMUMessage("base_link", imu_msg); - ASSERT_FALSE(distortion_corrector_2d_->get_angular_velocity_queue().empty()); + ASSERT_FALSE(distortion_corrector_2d_->getAngularVelocityQueue().empty()); EXPECT_NEAR( - distortion_corrector_2d_->get_angular_velocity_queue().front().vector.z, -0.03159, + distortion_corrector_2d_->getAngularVelocityQueue().front().vector.z, -0.03159, standard_tolerance_); } @@ -329,22 +329,22 @@ TEST_F(DistortionCorrectorTest, TestIsInputValid) TEST_F(DistortionCorrectorTest, TestSetPointCloudTransformWithBaseLink) { distortion_corrector_2d_->setPointCloudTransform("base_link", "base_link"); - EXPECT_TRUE(distortion_corrector_2d_->pointcloud_transform_exists()); - EXPECT_FALSE(distortion_corrector_2d_->pointcloud_transform_needed()); + EXPECT_TRUE(distortion_corrector_2d_->pointcloudTransformExists()); + EXPECT_FALSE(distortion_corrector_2d_->pointcloudTransformNeeded()); } TEST_F(DistortionCorrectorTest, TestSetPointCloudTransformWithLidarFrame) { distortion_corrector_2d_->setPointCloudTransform("base_link", "lidar_top"); - EXPECT_TRUE(distortion_corrector_2d_->pointcloud_transform_exists()); - EXPECT_TRUE(distortion_corrector_2d_->pointcloud_transform_needed()); + EXPECT_TRUE(distortion_corrector_2d_->pointcloudTransformExists()); + EXPECT_TRUE(distortion_corrector_2d_->pointcloudTransformNeeded()); } TEST_F(DistortionCorrectorTest, TestSetPointCloudTransformWithMissingFrame) { distortion_corrector_2d_->setPointCloudTransform("base_link", "missing_lidar_frame"); - EXPECT_FALSE(distortion_corrector_2d_->pointcloud_transform_exists()); - EXPECT_FALSE(distortion_corrector_2d_->pointcloud_transform_needed()); + EXPECT_FALSE(distortion_corrector_2d_->pointcloudTransformExists()); + EXPECT_FALSE(distortion_corrector_2d_->pointcloudTransformNeeded()); } TEST_F(DistortionCorrectorTest, TestUndistortPointCloudWithEmptyTwist) From d4978f7ef9e3d2116c78c7b603e855f25f9d24c0 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 1 Aug 2024 13:46:07 +0900 Subject: [PATCH 003/115] chore: fix cpp check Signed-off-by: vividf --- .../src/concatenate_data/concatenate_and_time_sync_node.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 920f2aa6e2466..e0f71fee8eb0c 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -182,7 +182,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro RCLCPP_DEBUG_STREAM( get_logger(), "Subscribing to " << params_.input_topics.size() << " user given topics as inputs:"); - for (auto & input_topic : params_.input_topics) { + for (const auto & input_topic : params_.input_topics) { RCLCPP_DEBUG_STREAM(get_logger(), " - " << input_topic); } @@ -438,8 +438,6 @@ void PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus( diagnostic_updater::DiagnosticStatusWrapper & stat) { if (publish_pointcloud_ || drop_previous_but_late_pointcloud_) { - std::set missed_cloud; - stat.add("concatenated cloud timestamp", formatTimestamp(current_concat_cloud_timestamp_)); stat.add("reference timestamp min", formatTimestamp(diagnostic_reference_timestamp_min_)); stat.add("reference timestamp max", formatTimestamp(diagnostic_reference_timestamp_max_)); From 63a870b1e4709c04472f51d2e9988e016efc7e2f Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 1 Aug 2024 15:22:23 +0900 Subject: [PATCH 004/115] chore: add diagnostics readme Signed-off-by: vividf --- .../docs/concatenate-data.md | 88 ++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index 64477a1e34cdd..4e0dd2887dc8f 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -90,7 +90,93 @@ colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release --package colcon test --packages-select autoware_pointcloud_preprocessor --event-handlers console_cohesion+ ``` -### Node separation options +## Debug and Diagnostics + +To verify whether the node has successfully concatenated the point clouds, the user can examine the `/diagnostics` topic using the following command: + +```bash +ros2 topic echo /diagnostics +``` + +Below is an example output when the point clouds are concatenated successfully: + +- Each point cloud has a value of `1`. +- The `concatenate status` is `1`. +- The `level` value is `\0`. (diagnostic_msgs::msg::DiagnosticStatus::OK) + +```bash +header: + stamp: + sec: 1722492015 + nanosec: 848508777 + frame_id: '' +status: +- level: "\0" + name: 'concatenate_and_time_sync_node: concat_status' + message: Concatenated pointcloud is published and include all topics + hardware_id: concatenate_data_checker + values: + - key: concatenated cloud timestamp + value: '1718260240.159229994' + - key: reference timestamp min + value: '1718260240.149230003' + - key: reference timestamp max + value: '1718260240.169229984' + - key: /sensing/lidar/left/pointcloud_before_sync timestamp + value: '1718260240.159229994' + - key: /sensing/lidar/left/pointcloud_before_sync + value: '1' + - key: /sensing/lidar/right/pointcloud_before_sync timestamp + value: '1718260240.194104910' + - key: /sensing/lidar/right/pointcloud_before_sync + value: '1' + - key: /sensing/lidar/top/pointcloud_before_sync timestamp + value: '1718260240.234578133' + - key: /sensing/lidar/top/pointcloud_before_sync + value: '1' + - key: concatenate status + value: '1' +``` + +Below is an example when point clouds fail to concatenate successfully. + +- Some point clouds might have values of `0`. +- The `concatenate status` is `0`. +- The `level` value is `\x02`. (diagnostic_msgs::msg::DiagnosticStatus::ERROR) + +```bash +header: + stamp: + sec: 1722492663 + nanosec: 344942959 + frame_id: '' +status: +- level: "\x02" + name: 'concatenate_and_time_sync_node: concat_status' + message: Concatenated pointcloud is published but miss some topics + hardware_id: concatenate_data_checker + values: + - key: concatenated cloud timestamp + value: '1718260240.859827995' + - key: reference timestamp min + value: '1718260240.849828005' + - key: reference timestamp max + value: '1718260240.869827986' + - key: /sensing/lidar/left/pointcloud_before_sync timestamp + value: '1718260240.859827995' + - key: /sensing/lidar/left/pointcloud_before_sync + value: '1' + - key: /sensing/lidar/right/pointcloud_before_sync timestamp + value: '1718260240.895193815' + - key: /sensing/lidar/right/pointcloud_before_sync + value: '1' + - key: /sensing/lidar/top/pointcloud_before_sync + value: '0' + - key: concatenate status + value: '0' +``` + +## Node separation options There is also an option to separate the concatenate_and_time_sync_node into two nodes: one for `time synchronization` and another for `concatenate pointclouds` ([See this PR](https://github.com/autowarefoundation/autoware.universe/pull/3312)). From c8cca1f95fad5cfef642cd56a1d80fac4e65717d Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 1 Aug 2024 15:34:33 +0900 Subject: [PATCH 005/115] chore: update figure Signed-off-by: vividf --- .../image/concatenate_edge_case.drawio.svg | 115 +++++++++++------- 1 file changed, 71 insertions(+), 44 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_edge_case.drawio.svg b/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_edge_case.drawio.svg index bf81613c7e536..33835b6396e51 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_edge_case.drawio.svg +++ b/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_edge_case.drawio.svg @@ -8,7 +8,7 @@ width="2232px" height="963px" viewBox="-0.5 -0.5 2232 963" - content="<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" scale="1" border="0" version="24.7.6"> <diagram name="Page-1" id="NLJ4wg49r_tGfpfCC3yf"> <mxGraphModel dx="3417" dy="2014" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-385" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1995" y="757.5000000000002" as="sourcePoint" /> <mxPoint x="2225" y="757.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-386" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2095" y="797.5000000000002" as="sourcePoint" /> <mxPoint x="2225" y="797.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-387" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2175" y="837.5" as="sourcePoint" /> <mxPoint x="2225" y="837.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-388" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-389" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="797.5" as="sourcePoint" /> <mxPoint x="2095" y="797.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-389" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1825" y="787.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-390" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-431" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1995" y="877.5" as="sourcePoint" /> <mxPoint x="2225" y="877.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-391" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-392" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="837.5" as="sourcePoint" /> <mxPoint x="2095" y="837.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-392" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1925" y="827.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-393" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1715" y="707.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-394" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1835" y="707.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-395" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1955" y="707.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-396" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="757.5" as="sourcePoint" /> <mxPoint x="1975" y="757.5000000000002" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-397" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-389" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1795" y="797.5" as="sourcePoint" /> <mxPoint x="2075" y="797.5000000000002" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-398" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1735" y="747.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-399" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2075" y="707.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-400" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="877.5" as="sourcePoint" /> <mxPoint x="2000" y="877.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-401" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-392" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1940" y="837.5" as="sourcePoint" /> <mxPoint x="2155" y="837.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-406" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1995" y="1097.5" as="sourcePoint" /> <mxPoint x="2225" y="1097.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-407" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2085" y="1137.5" as="sourcePoint" /> <mxPoint x="2225" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-408" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2145" y="1177.5" as="sourcePoint" /> <mxPoint x="2225" y="1177.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-409" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-410" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="1137.5" as="sourcePoint" /> <mxPoint x="2095" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-410" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1825" y="1127.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-411" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2215" y="1217.5" as="sourcePoint" /> <mxPoint x="2225" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-412" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-413" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="1177.5" as="sourcePoint" /> <mxPoint x="2095" y="1177.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-413" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1935" y="1167.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-414" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1715" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-415" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1835" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-416" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1955" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-417" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="1097.5" as="sourcePoint" /> <mxPoint x="1975" y="1097.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-418" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-410" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1795" y="1137.5" as="sourcePoint" /> <mxPoint x="2065" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-419" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1735" y="1087.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-420" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2075" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-421" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="1217.5" as="sourcePoint" /> <mxPoint x="2025" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-422" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-413" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1915" y="1177.5" as="sourcePoint" /> <mxPoint x="2125" y="1177.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-423" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2015" y="1217.5" as="sourcePoint" /> <mxPoint x="2215" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-427" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1975" y="745" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-428" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2075" y="785" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-429" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2155" y="822.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-430" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-431" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1995" y="877.5" as="sourcePoint" /> <mxPoint x="2225" y="877.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-431" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2205" y="862.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-433" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1975" y="1082.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-434" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2065" y="1122.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-435" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2125" y="1162.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-444" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1130" y="440.25" as="sourcePoint" /> <mxPoint x="1360" y="440.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-445" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1170" y="480.25" as="sourcePoint" /> <mxPoint x="1360" y="480.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-446" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-484" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="520.25" as="sourcePoint" /> <mxPoint x="1360" y="520.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-447" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-448" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="480.25" as="sourcePoint" /> <mxPoint x="1230" y="480.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-448" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="910" y="470.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-449" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-486" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="560.25" as="sourcePoint" /> <mxPoint x="1360" y="560.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-450" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-451" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="520.25" as="sourcePoint" /> <mxPoint x="1230" y="520.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-451" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="950" y="510.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-453" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="850" y="390.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-454" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="970" y="390.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-455" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1090" y="390.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-456" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="440.25" as="sourcePoint" /> <mxPoint x="1110" y="440.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-457" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-448" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="930" y="480.25" as="sourcePoint" /> <mxPoint x="1150" y="480.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-458" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="870" y="430.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-459" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1210" y="390.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-460" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1120" y="760" as="sourcePoint" /> <mxPoint x="1350" y="760" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-461" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1160" y="800" as="sourcePoint" /> <mxPoint x="1350" y="800" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-462" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-489" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="840" as="sourcePoint" /> <mxPoint x="1350" y="840" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-463" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-464" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="800" as="sourcePoint" /> <mxPoint x="1220" y="800" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-464" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="900" y="790" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-465" value="" style="endArrow=none;dashed=1;html=1;rounded=0;exitX=0.681;exitY=0.753;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-491" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="880" as="sourcePoint" /> <mxPoint x="1350" y="880" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-466" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-467" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="840" as="sourcePoint" /> <mxPoint x="1220" y="840" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-467" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="940" y="830" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-468" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="840" y="710" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-469" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="960" y="710" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-470" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1080" y="710" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-471" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="760" as="sourcePoint" /> <mxPoint x="1100" y="760" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-472" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-464" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="920" y="800" as="sourcePoint" /> <mxPoint x="1140" y="800" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-473" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="860" y="750" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-474" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1200" y="710" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-475" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1000" y="360.25" as="sourcePoint" /> <mxPoint x="999.6600000000001" y="595.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-476" value="&lt;div&gt;&lt;font color=&quot;#ff0000&quot;&gt;receive all pc&lt;/font&gt;&lt;/div&gt;&lt;font color=&quot;#ff0000&quot;&gt;concatenate&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="1000" y="575.25" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-477" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1144.8899999999999" y="707.5" as="sourcePoint" /> <mxPoint x="1144.55" y="942.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-480" value="&lt;div&gt;group 1:&amp;nbsp;&lt;span style=&quot;background-color: initial;&quot;&gt;timeout&lt;/span&gt;&lt;/div&gt;concatenate" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="1100" y="950" width="110" height="40" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-481" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1115" y="425.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-482" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1150" y="465.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-483" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-451" target="80uhdLyXJ-cf-2mWR6Fi-484" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="970" y="520.25" as="sourcePoint" /> <mxPoint x="1360" y="520.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-484" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1190" y="505.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-485" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-486" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="560.25" as="sourcePoint" /> <mxPoint x="1360" y="560.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-486" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1230" y="545.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-487" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1095" y="745" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-488" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1135" y="785" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-490" value="" style="endArrow=none;dashed=1;html=1;rounded=0;entryX=0.625;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-491" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="880" as="sourcePoint" /> <mxPoint x="1350" y="880" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-491" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1215" y="870" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-503" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-467" target="80uhdLyXJ-cf-2mWR6Fi-489" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="960" y="840" as="sourcePoint" /> <mxPoint x="1350" y="840" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-489" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1175" y="827.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-505" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;Normal&lt;/b&gt;&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="660" y="486.25" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-506" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;One pointcloud&lt;/b&gt;&lt;/font&gt;&lt;div&gt;&lt;font size=&quot;3&quot;&gt;&lt;b&gt;drop&lt;/b&gt;&lt;/font&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="630" y="797.5" width="160" height="50" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-507" value="&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;Several pointclouds&lt;/b&gt;&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;&amp;nbsp;delay&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="1510" y="470.25" width="190" height="60" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-508" value="&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;Several pointclouds&lt;/b&gt;&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;&amp;nbsp;delay, and one drop&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="1480" y="785" width="200" height="60" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-509" value="&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;Several pointclouds&lt;/b&gt;&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;&amp;nbsp;delay, and drop&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="1510" y="1127.5" width="190" height="60" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-570" value="&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;One pointcloud&lt;/b&gt;&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;&amp;nbsp;delay&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="640" y="1127.5" width="160" height="60" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-452" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="990" y="550.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-639" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1125" y="1097.5" as="sourcePoint" /> <mxPoint x="1355" y="1097.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-640" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1165" y="1137.5" as="sourcePoint" /> <mxPoint x="1355" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-641" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-666" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1177.5" as="sourcePoint" /> <mxPoint x="1355" y="1177.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-642" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-643" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1137.5" as="sourcePoint" /> <mxPoint x="1225" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-643" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="905" y="1127.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-644" value="" style="endArrow=none;dashed=1;html=1;rounded=0;exitX=0.681;exitY=0.753;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-660" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1217.5" as="sourcePoint" /> <mxPoint x="1355" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-645" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-646" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1177.5" as="sourcePoint" /> <mxPoint x="1225" y="1177.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-646" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="945" y="1167.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-647" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="845" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-648" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="965" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-649" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1085" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-650" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1097.5" as="sourcePoint" /> <mxPoint x="1105" y="1097.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-651" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-643" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="925" y="1137.5" as="sourcePoint" /> <mxPoint x="1145" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-652" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="865" y="1087.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-653" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1205" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-654" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1064.6299999999999" y="1040" as="sourcePoint" /> <mxPoint x="1064.29" y="1275" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-657" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1100" y="1082.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-658" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1140" y="1122.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-659" value="" style="endArrow=none;dashed=1;html=1;rounded=0;entryX=0.625;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-699" target="80uhdLyXJ-cf-2mWR6Fi-660" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1217.5" as="sourcePoint" /> <mxPoint x="1355" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-660" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1220" y="1207.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-665" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-646" target="80uhdLyXJ-cf-2mWR6Fi-666" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="965" y="1177.5" as="sourcePoint" /> <mxPoint x="1355" y="1177.5" as="targetPoint" /> <Array as="points" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-666" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1180" y="1165" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-700" value="" style="endArrow=none;dashed=1;html=1;rounded=0;entryX=0.625;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-699" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1217.5" as="sourcePoint" /> <mxPoint x="1230" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-699" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1055" y="1207.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-2" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="870.3399999999998" y="710" as="sourcePoint" /> <mxPoint x="869.9999999999999" y="945" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-4" value="" style="endArrow=none;html=1;rounded=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1105.3399999999997" y="710" as="sourcePoint" /> <mxPoint x="1105" y="945" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-5" value="&lt;div&gt;group 1 created.&lt;/div&gt;&lt;div&gt;timer start&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="815" y="950" width="110" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-6" value="&lt;div&gt;group 2 created.&lt;/div&gt;&lt;div&gt;timer start&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#6c8ebf;fillColor=#dae8fc;" parent="1" vertex="1"> <mxGeometry x="1040" y="660" width="110" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-7" value="" style="endArrow=none;html=1;rounded=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1224.9799999999998" y="710" as="sourcePoint" /> <mxPoint x="1224.64" y="945" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-8" value="&lt;div&gt;group 2&lt;/div&gt;&lt;div&gt;&amp;nbsp;concatenate&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#6c8ebf;fillColor=#dae8fc;" parent="1" vertex="1"> <mxGeometry x="1185" y="660" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-9" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="874.6299999999998" y="1040" as="sourcePoint" /> <mxPoint x="874.2899999999998" y="1275" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-10" value="&lt;div&gt;group 1 created.&lt;/div&gt;&lt;div&gt;timer start&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="820" y="1280" width="110" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-11" value="&lt;div&gt;group 1&lt;/div&gt;&lt;div&gt;&amp;nbsp;concatenate&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="1025" y="1280" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-42" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-65" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="437.5" as="sourcePoint" /> <mxPoint x="2265" y="437.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-43" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2095" y="477.5" as="sourcePoint" /> <mxPoint x="2265" y="477.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-44" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2145" y="517.5" as="sourcePoint" /> <mxPoint x="2265" y="517.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-45" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-46" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="477.5" as="sourcePoint" /> <mxPoint x="2095" y="477.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-46" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1835" y="467.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-47" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2195" y="557.5" as="sourcePoint" /> <mxPoint x="2265" y="557.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-48" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-49" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="517.5" as="sourcePoint" /> <mxPoint x="2095" y="517.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-49" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1935" y="507.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-50" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1715" y="387.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-51" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1835" y="387.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-52" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1955" y="387.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-53" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="437.5" as="sourcePoint" /> <mxPoint x="1975" y="437.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-54" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-46" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1795" y="477.5" as="sourcePoint" /> <mxPoint x="2075" y="477.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-55" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1735" y="427.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-56" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2075" y="387.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-57" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-63" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="557.5" as="sourcePoint" /> <mxPoint x="2225" y="557.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-58" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-49" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1940" y="517.5" as="sourcePoint" /> <mxPoint x="2125" y="517.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-64" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-65" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1995" y="437.5" as="sourcePoint" /> <mxPoint x="2265" y="437.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-65" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1"> <mxGeometry x="2215" y="427.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-66" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-63" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2020" y="557.5" as="sourcePoint" /> <mxPoint x="2175" y="557.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-68" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1975" y="425" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-69" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2075" y="462.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-70" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2125" y="502.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-71" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2175" y="542.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-74" value="&lt;div&gt;group 1 created.&lt;/div&gt;&lt;div&gt;timer start&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="1700" y="605" width="110" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-75" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1745.3399999999997" y="360" as="sourcePoint" /> <mxPoint x="1745" y="595" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-76" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" source="viYa4thklxoO5ObslHZt-63" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2024.1599999999999" y="363.75" as="sourcePoint" /> <mxPoint x="2023.82" y="598.75" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-77" value="&lt;div&gt;group 1&lt;/div&gt;&lt;div&gt;&amp;nbsp;concatenate&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="1975" y="612.5" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-79" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2685" y="476.25" as="sourcePoint" /> <mxPoint x="2855" y="476.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-80" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2735" y="516.25" as="sourcePoint" /> <mxPoint x="2855" y="516.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-81" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-82" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2315" y="476.25" as="sourcePoint" /> <mxPoint x="2685" y="476.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-82" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="2425" y="466.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-83" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2785" y="556.25" as="sourcePoint" /> <mxPoint x="2855" y="556.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-84" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-85" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2315" y="516.25" as="sourcePoint" /> <mxPoint x="2685" y="516.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-85" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="2525" y="506.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-86" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2305" y="386.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-87" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2425" y="386.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-88" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2545" y="386.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-89" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2315" y="436.25" as="sourcePoint" /> <mxPoint x="2565" y="436.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-90" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-82" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2385" y="476.25" as="sourcePoint" /> <mxPoint x="2665" y="476.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-91" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="2325" y="426.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-92" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2665" y="386.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-93" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-95" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2315" y="556.25" as="sourcePoint" /> <mxPoint x="2815" y="556.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-94" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-85" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2530" y="516.25" as="sourcePoint" /> <mxPoint x="2715" y="516.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-95" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1"> <mxGeometry x="2680" y="547.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-96" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2585" y="436.25" as="sourcePoint" /> <mxPoint x="2860" y="436.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-98" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-95" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2610" y="556.25" as="sourcePoint" /> <mxPoint x="2765" y="556.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-99" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2565" y="423.75" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-100" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2665" y="461.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-101" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2715" y="501.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-102" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2765" y="541.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-103" value="&lt;div&gt;group 1 created.&lt;/div&gt;&lt;div&gt;timer start&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="2290" y="603.75" width="110" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-104" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2335.3399999999997" y="358.75" as="sourcePoint" /> <mxPoint x="2335" y="593.75" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-105" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2615.3399999999997" y="358.75" as="sourcePoint" /> <mxPoint x="2615" y="593.75" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-106" value="&lt;div&gt;group 1&lt;/div&gt;&lt;div&gt;&amp;nbsp;concatenate&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="2565" y="603.75" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-108" value="" style="endArrow=none;html=1;rounded=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2689.75" y="362.5" as="sourcePoint" /> <mxPoint x="2689.41" y="597.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-109" value="&lt;div&gt;Can decide to&lt;/div&gt;&lt;div&gt;&amp;nbsp;publish or not&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#82b366;fillColor=#d5e8d4;" parent="1" vertex="1"> <mxGeometry x="2690" y="605" width="100" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-112" value="" style="group" parent="1" vertex="1" connectable="0"> <mxGeometry x="2340" y="557.5" width="275" height="30" as="geometry" /> </mxCell> <mxCell id="cD1KZ4Ka8YFXE9963_W6-1" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" parent="viYa4thklxoO5ObslHZt-112" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint y="29.289999999999964" as="sourcePoint" /> <mxPoint x="275" y="29.289999999999964" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="cD1KZ4Ka8YFXE9963_W6-2" value="120 ms" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="viYa4thklxoO5ObslHZt-112" vertex="1"> <mxGeometry x="110" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-113" value="" style="group" parent="1" vertex="1" connectable="0"> <mxGeometry x="870" y="900" width="275" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-114" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" parent="viYa4thklxoO5ObslHZt-113" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint y="29.289999999999964" as="sourcePoint" /> <mxPoint x="275" y="29.289999999999964" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-115" value="120 ms" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="viYa4thklxoO5ObslHZt-113" vertex="1"> <mxGeometry x="110" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-122" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" target="viYa4thklxoO5ObslHZt-63" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2024.1599999999999" y="363.75" as="sourcePoint" /> <mxPoint x="2023.82" y="598.75" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-63" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="2015" y="547.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-123" value="" style="group" parent="1" vertex="1" connectable="0"> <mxGeometry x="1745" y="565" width="275" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-124" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" parent="viYa4thklxoO5ObslHZt-123" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint y="29.289999999999964" as="sourcePoint" /> <mxPoint x="275" y="29.289999999999964" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-125" value="120 ms" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="viYa4thklxoO5ObslHZt-123" vertex="1"> <mxGeometry x="110" width="60" height="30" as="geometry" /> </mxCell> </root> </mxGraphModel> </diagram> </mxfile> " + content="<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" scale="1" border="0" version="24.7.6"> <diagram name="Page-1" id="NLJ4wg49r_tGfpfCC3yf"> <mxGraphModel dx="1709" dy="1007" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-385" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1995" y="757.5000000000002" as="sourcePoint" /> <mxPoint x="2225" y="757.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-386" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2095" y="797.5000000000002" as="sourcePoint" /> <mxPoint x="2225" y="797.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-387" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2175" y="837.5" as="sourcePoint" /> <mxPoint x="2225" y="837.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-388" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-389" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="797.5" as="sourcePoint" /> <mxPoint x="2095" y="797.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-389" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1825" y="787.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-390" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-431" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1995" y="877.5" as="sourcePoint" /> <mxPoint x="2225" y="877.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-391" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-392" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="837.5" as="sourcePoint" /> <mxPoint x="2095" y="837.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-392" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1925" y="827.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-393" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1715" y="707.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-394" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1835" y="707.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-395" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1955" y="707.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-396" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="757.5" as="sourcePoint" /> <mxPoint x="1975" y="757.5000000000002" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-397" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-389" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1795" y="797.5" as="sourcePoint" /> <mxPoint x="2075" y="797.5000000000002" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-398" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1735" y="747.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-399" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2075" y="707.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-400" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="877.5" as="sourcePoint" /> <mxPoint x="2000" y="877.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-401" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-392" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1940" y="837.5" as="sourcePoint" /> <mxPoint x="2155" y="837.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-406" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1995" y="1097.5" as="sourcePoint" /> <mxPoint x="2225" y="1097.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-407" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2085" y="1137.5" as="sourcePoint" /> <mxPoint x="2225" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-408" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2145" y="1177.5" as="sourcePoint" /> <mxPoint x="2225" y="1177.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-409" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-410" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="1137.5" as="sourcePoint" /> <mxPoint x="2095" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-410" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1825" y="1127.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-411" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2215" y="1217.5" as="sourcePoint" /> <mxPoint x="2225" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-412" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-413" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="1177.5" as="sourcePoint" /> <mxPoint x="2095" y="1177.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-413" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1935" y="1167.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-414" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1715" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-415" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1835" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-416" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1955" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-417" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="1097.5" as="sourcePoint" /> <mxPoint x="1975" y="1097.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-418" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-410" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1795" y="1137.5" as="sourcePoint" /> <mxPoint x="2065" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-419" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1735" y="1087.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-420" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2075" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-421" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="1217.5" as="sourcePoint" /> <mxPoint x="2025" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-422" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-413" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1915" y="1177.5" as="sourcePoint" /> <mxPoint x="2125" y="1177.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-423" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2015" y="1217.5" as="sourcePoint" /> <mxPoint x="2215" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-427" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1975" y="745" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-428" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2075" y="785" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-429" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2155" y="822.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-430" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-431" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1995" y="877.5" as="sourcePoint" /> <mxPoint x="2225" y="877.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-431" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2205" y="862.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-433" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1975" y="1082.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-434" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2065" y="1122.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-435" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2125" y="1162.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-444" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1130" y="440.25" as="sourcePoint" /> <mxPoint x="1360" y="440.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-445" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1170" y="480.25" as="sourcePoint" /> <mxPoint x="1360" y="480.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-446" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-484" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="520.25" as="sourcePoint" /> <mxPoint x="1360" y="520.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-447" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-448" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="480.25" as="sourcePoint" /> <mxPoint x="1230" y="480.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-448" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="910" y="470.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-449" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-486" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="560.25" as="sourcePoint" /> <mxPoint x="1360" y="560.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-450" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-451" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="520.25" as="sourcePoint" /> <mxPoint x="1230" y="520.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-451" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="950" y="510.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-453" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="850" y="390.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-454" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="970" y="390.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-455" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1090" y="390.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-456" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="440.25" as="sourcePoint" /> <mxPoint x="1110" y="440.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-457" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-448" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="930" y="480.25" as="sourcePoint" /> <mxPoint x="1150" y="480.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-458" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="870" y="430.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-459" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1210" y="390.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-460" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1120" y="760" as="sourcePoint" /> <mxPoint x="1350" y="760" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-461" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1160" y="800" as="sourcePoint" /> <mxPoint x="1350" y="800" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-462" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-489" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="840" as="sourcePoint" /> <mxPoint x="1350" y="840" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-463" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-464" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="800" as="sourcePoint" /> <mxPoint x="1220" y="800" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-464" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="900" y="790" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-465" value="" style="endArrow=none;dashed=1;html=1;rounded=0;exitX=0.681;exitY=0.753;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-491" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="880" as="sourcePoint" /> <mxPoint x="1350" y="880" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-466" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-467" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="840" as="sourcePoint" /> <mxPoint x="1220" y="840" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-467" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="940" y="830" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-468" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="840" y="710" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-469" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="960" y="710" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-470" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1080" y="710" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-471" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="760" as="sourcePoint" /> <mxPoint x="1100" y="760" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-472" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-464" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="920" y="800" as="sourcePoint" /> <mxPoint x="1140" y="800" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-473" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="860" y="750" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-474" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1200" y="710" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-475" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1000" y="360.25" as="sourcePoint" /> <mxPoint x="999.6600000000001" y="595.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-476" value="&lt;div&gt;&lt;font color=&quot;#ff0000&quot;&gt;receive all pc&lt;/font&gt;&lt;/div&gt;&lt;font color=&quot;#ff0000&quot;&gt;concatenate&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="1000" y="575.25" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-477" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1144.8899999999999" y="707.5" as="sourcePoint" /> <mxPoint x="1144.55" y="942.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-480" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;collector&lt;/span&gt;&amp;nbsp;1:&amp;nbsp;&lt;span style=&quot;background-color: initial;&quot;&gt;timeout&lt;/span&gt;&lt;/div&gt;concatenate" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="1095" y="950" width="120" height="40" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-481" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1115" y="425.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-482" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1150" y="465.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-483" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-451" target="80uhdLyXJ-cf-2mWR6Fi-484" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="970" y="520.25" as="sourcePoint" /> <mxPoint x="1360" y="520.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-484" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1190" y="505.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-485" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-486" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="860" y="560.25" as="sourcePoint" /> <mxPoint x="1360" y="560.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-486" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1230" y="545.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-487" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1095" y="745" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-488" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1135" y="785" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-490" value="" style="endArrow=none;dashed=1;html=1;rounded=0;entryX=0.625;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-491" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="850" y="880" as="sourcePoint" /> <mxPoint x="1350" y="880" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-491" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1215" y="870" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-503" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-467" target="80uhdLyXJ-cf-2mWR6Fi-489" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="960" y="840" as="sourcePoint" /> <mxPoint x="1350" y="840" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-489" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1175" y="827.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-505" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;Normal&lt;/b&gt;&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="660" y="486.25" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-506" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;One pointcloud&lt;/b&gt;&lt;/font&gt;&lt;div&gt;&lt;font size=&quot;3&quot;&gt;&lt;b&gt;drop&lt;/b&gt;&lt;/font&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="630" y="797.5" width="160" height="50" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-507" value="&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;Several pointclouds&lt;/b&gt;&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;&amp;nbsp;delay&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="1510" y="470.25" width="190" height="60" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-508" value="&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;Several pointclouds&lt;/b&gt;&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;&amp;nbsp;delay, and one drop&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="1480" y="785" width="200" height="60" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-509" value="&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;Several pointclouds&lt;/b&gt;&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;&amp;nbsp;delay, and drop&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="1510" y="1127.5" width="190" height="60" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-570" value="&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;One pointcloud&lt;/b&gt;&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;b&gt;&amp;nbsp;delay&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1"> <mxGeometry x="640" y="1127.5" width="160" height="60" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-452" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="990" y="550.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-639" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1125" y="1097.5" as="sourcePoint" /> <mxPoint x="1355" y="1097.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-640" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1165" y="1137.5" as="sourcePoint" /> <mxPoint x="1355" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-641" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-666" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1177.5" as="sourcePoint" /> <mxPoint x="1355" y="1177.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-642" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-643" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1137.5" as="sourcePoint" /> <mxPoint x="1225" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-643" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="905" y="1127.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-644" value="" style="endArrow=none;dashed=1;html=1;rounded=0;exitX=0.681;exitY=0.753;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-660" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1217.5" as="sourcePoint" /> <mxPoint x="1355" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-645" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-646" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1177.5" as="sourcePoint" /> <mxPoint x="1225" y="1177.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-646" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="945" y="1167.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-647" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="845" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-648" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="965" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-649" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1085" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-650" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1097.5" as="sourcePoint" /> <mxPoint x="1105" y="1097.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-651" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-643" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="925" y="1137.5" as="sourcePoint" /> <mxPoint x="1145" y="1137.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-652" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="865" y="1087.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-653" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1205" y="1047.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-654" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1064.6299999999999" y="1040" as="sourcePoint" /> <mxPoint x="1064.29" y="1275" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-657" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1100" y="1082.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-658" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1140" y="1122.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-659" value="" style="endArrow=none;dashed=1;html=1;rounded=0;entryX=0.625;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-699" target="80uhdLyXJ-cf-2mWR6Fi-660" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1217.5" as="sourcePoint" /> <mxPoint x="1355" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-660" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1220" y="1207.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-665" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="80uhdLyXJ-cf-2mWR6Fi-646" target="80uhdLyXJ-cf-2mWR6Fi-666" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="965" y="1177.5" as="sourcePoint" /> <mxPoint x="1355" y="1177.5" as="targetPoint" /> <Array as="points" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-666" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1180" y="1165" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-700" value="" style="endArrow=none;dashed=1;html=1;rounded=0;entryX=0.625;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" target="80uhdLyXJ-cf-2mWR6Fi-699" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="855" y="1217.5" as="sourcePoint" /> <mxPoint x="1230" y="1217.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="80uhdLyXJ-cf-2mWR6Fi-699" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1055" y="1207.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-2" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="870.3399999999998" y="710" as="sourcePoint" /> <mxPoint x="869.9999999999999" y="945" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-4" value="" style="endArrow=none;html=1;rounded=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1105.3399999999997" y="710" as="sourcePoint" /> <mxPoint x="1105" y="945" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-5" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;collector&lt;/span&gt;&amp;nbsp;1 created.&lt;/div&gt;&lt;div&gt;timer start&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="810" y="950" width="120" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-6" value="&lt;div&gt;collector 2 created.&lt;/div&gt;&lt;div&gt;timer start&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#6c8ebf;fillColor=#dae8fc;" parent="1" vertex="1"> <mxGeometry x="1035" y="660" width="120" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-7" value="" style="endArrow=none;html=1;rounded=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1224.9799999999998" y="710" as="sourcePoint" /> <mxPoint x="1224.64" y="945" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-8" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;collector&lt;/span&gt;&amp;nbsp;2&lt;/div&gt;&lt;div&gt;&amp;nbsp;concatenate&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#6c8ebf;fillColor=#dae8fc;" parent="1" vertex="1"> <mxGeometry x="1185" y="660" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-9" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="874.6299999999998" y="1040" as="sourcePoint" /> <mxPoint x="874.2899999999998" y="1275" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-10" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;collector&lt;/span&gt;&amp;nbsp;1 created.&lt;/div&gt;&lt;div&gt;timer start&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="815" y="1280" width="120" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-11" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;collector&lt;/span&gt;&amp;nbsp;1&lt;/div&gt;&lt;div&gt;&amp;nbsp;concatenate&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="1025" y="1280" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-42" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-65" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="437.5" as="sourcePoint" /> <mxPoint x="2265" y="437.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-43" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2095" y="477.5" as="sourcePoint" /> <mxPoint x="2265" y="477.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-44" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2145" y="517.5" as="sourcePoint" /> <mxPoint x="2265" y="517.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-45" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-46" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="477.5" as="sourcePoint" /> <mxPoint x="2095" y="477.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-46" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1835" y="467.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-47" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2195" y="557.5" as="sourcePoint" /> <mxPoint x="2265" y="557.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-48" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-49" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="517.5" as="sourcePoint" /> <mxPoint x="2095" y="517.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-49" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1935" y="507.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-50" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1715" y="387.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-51" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1835" y="387.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-52" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="1955" y="387.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-53" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="437.5" as="sourcePoint" /> <mxPoint x="1975" y="437.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-54" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-46" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1795" y="477.5" as="sourcePoint" /> <mxPoint x="2075" y="477.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-55" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="1735" y="427.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-56" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2075" y="387.5" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-57" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-63" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1725" y="557.5" as="sourcePoint" /> <mxPoint x="2225" y="557.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-58" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-49" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1940" y="517.5" as="sourcePoint" /> <mxPoint x="2125" y="517.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-64" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-65" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1995" y="437.5" as="sourcePoint" /> <mxPoint x="2265" y="437.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-65" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1"> <mxGeometry x="2215" y="427.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-66" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-63" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2020" y="557.5" as="sourcePoint" /> <mxPoint x="2175" y="557.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-68" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="1975" y="425" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-69" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2075" y="462.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-70" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2125" y="502.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-71" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2175" y="542.5" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-74" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;collector&lt;/span&gt;&amp;nbsp;1 created.&lt;/div&gt;&lt;div&gt;timer start&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="1695" y="605" width="120" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-75" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="1745.3399999999997" y="360" as="sourcePoint" /> <mxPoint x="1745" y="595" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-76" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" source="viYa4thklxoO5ObslHZt-63" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2024.1599999999999" y="363.75" as="sourcePoint" /> <mxPoint x="2023.82" y="598.75" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-77" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;collector&lt;/span&gt;&amp;nbsp;1&lt;/div&gt;&lt;div&gt;&amp;nbsp;concatenate&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="1975" y="612.5" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-79" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2685" y="476.25" as="sourcePoint" /> <mxPoint x="2855" y="476.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-80" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2735" y="516.25" as="sourcePoint" /> <mxPoint x="2855" y="516.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-81" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-82" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2315" y="476.25" as="sourcePoint" /> <mxPoint x="2685" y="476.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-82" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="2425" y="466.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-83" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2785" y="556.25" as="sourcePoint" /> <mxPoint x="2855" y="556.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-84" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-85" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2315" y="516.25" as="sourcePoint" /> <mxPoint x="2685" y="516.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-85" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="2525" y="506.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-86" value="0" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2305" y="386.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-87" value="50" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2425" y="386.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-88" value="100" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2545" y="386.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-89" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2315" y="436.25" as="sourcePoint" /> <mxPoint x="2565" y="436.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-90" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-82" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2385" y="476.25" as="sourcePoint" /> <mxPoint x="2665" y="476.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-91" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="2325" y="426.25" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-92" value="150" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1"> <mxGeometry x="2665" y="386.25" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-93" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" target="viYa4thklxoO5ObslHZt-95" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2315" y="556.25" as="sourcePoint" /> <mxPoint x="2815" y="556.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-94" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-85" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2530" y="516.25" as="sourcePoint" /> <mxPoint x="2715" y="516.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-95" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1"> <mxGeometry x="2680" y="547.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-96" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2585" y="436.25" as="sourcePoint" /> <mxPoint x="2860" y="436.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-98" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" source="viYa4thklxoO5ObslHZt-95" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2610" y="556.25" as="sourcePoint" /> <mxPoint x="2765" y="556.25" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-99" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2565" y="423.75" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-100" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2665" y="461.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-101" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2715" y="501.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-102" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1"> <mxGeometry x="2765" y="541.25" width="20" height="25" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-103" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;collector&lt;/span&gt;&amp;nbsp;1 created.&lt;/div&gt;&lt;div&gt;timer start&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="2285" y="603.75" width="120" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-104" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2335.3399999999997" y="358.75" as="sourcePoint" /> <mxPoint x="2335" y="593.75" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-105" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2615.3399999999997" y="358.75" as="sourcePoint" /> <mxPoint x="2615" y="593.75" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-106" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;collector&lt;/span&gt;&amp;nbsp;1&lt;/div&gt;&lt;div&gt;&amp;nbsp;concatenate&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#b85450;fillColor=#f8cecc;" parent="1" vertex="1"> <mxGeometry x="2565" y="603.75" width="90" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-108" value="" style="endArrow=none;html=1;rounded=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2689.75" y="362.5" as="sourcePoint" /> <mxPoint x="2689.41" y="597.5" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-109" value="&lt;div&gt;Can decide to&lt;/div&gt;&lt;div&gt;&amp;nbsp;publish or not&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=#82b366;fillColor=#d5e8d4;" parent="1" vertex="1"> <mxGeometry x="2690" y="605" width="100" height="40" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-112" value="" style="group" parent="1" vertex="1" connectable="0"> <mxGeometry x="2340" y="557.5" width="275" height="30" as="geometry" /> </mxCell> <mxCell id="cD1KZ4Ka8YFXE9963_W6-1" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" parent="viYa4thklxoO5ObslHZt-112" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint y="29.289999999999964" as="sourcePoint" /> <mxPoint x="275" y="29.289999999999964" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="cD1KZ4Ka8YFXE9963_W6-2" value="120 ms" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="viYa4thklxoO5ObslHZt-112" vertex="1"> <mxGeometry x="110" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-113" value="" style="group" parent="1" vertex="1" connectable="0"> <mxGeometry x="870" y="900" width="275" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-114" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" parent="viYa4thklxoO5ObslHZt-113" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint y="29.289999999999964" as="sourcePoint" /> <mxPoint x="275" y="29.289999999999964" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-115" value="120 ms" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="viYa4thklxoO5ObslHZt-113" vertex="1"> <mxGeometry x="110" width="60" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-122" value="" style="endArrow=none;html=1;rounded=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" target="viYa4thklxoO5ObslHZt-63" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="2024.1599999999999" y="363.75" as="sourcePoint" /> <mxPoint x="2023.82" y="598.75" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-63" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1"> <mxGeometry x="2015" y="547.5" width="20" height="20" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-123" value="" style="group" parent="1" vertex="1" connectable="0"> <mxGeometry x="1745" y="565" width="275" height="30" as="geometry" /> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-124" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" parent="viYa4thklxoO5ObslHZt-123" edge="1"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint y="29.289999999999964" as="sourcePoint" /> <mxPoint x="275" y="29.289999999999964" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="viYa4thklxoO5ObslHZt-125" value="120 ms" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="viYa4thklxoO5ObslHZt-123" vertex="1"> <mxGeometry x="110" width="60" height="30" as="geometry" /> </mxCell> </root> </mxGraphModel> </diagram> </mxfile> " style="background-color: rgb(255, 255, 255);" > @@ -42,7 +42,9 @@ - + + + @@ -157,7 +159,7 @@ - + @@ -655,7 +657,7 @@ - + @@ -856,7 +858,7 @@ - + @@ -869,7 +871,8 @@
- group 1: + collector + 1: timeout
concatenate @@ -878,11 +881,11 @@
@@ -900,7 +903,7 @@ - + @@ -1197,7 +1200,7 @@ - + @@ -1410,7 +1413,7 @@ - + @@ -1422,18 +1425,21 @@ >
-
group 1 created.
+
+ collector + 1 created. +
timer start
@@ -1441,7 +1447,7 @@
- + @@ -1453,18 +1459,18 @@ >
-
group 2 created.
+
collector 2 created.
timer start
@@ -1489,7 +1495,10 @@ >
-
group 2
+
+ collector + 2 +
concatenate
@@ -1500,7 +1509,7 @@ y="307" width="69" height="32" - xlink:href="data:image/png;base64,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" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARQAAACACAYAAADK8JaYAAAAAXNSR0IArs4c6QAAIABJREFUeF7tnQXU5LqRhW+YmXHDzMzMTBtmpg0zMzNnkw0zMzNzNsy4YWYmfxNpTk2NbMvdsru3X9U5c977u21ZvpKuCtUHUkggEAgEAo0QOFCjdqKZQCAQCAQUhBKTIBAIBJohEITSDMpoKBAIBIJQYg4EAoFAMwSCUJpBGQ0FAoFAEErMgUAgEGiGQBBKMyijoUAgEAhCiTkQCAQCzRAIQmkD5UEl/dU1dVZJnyg0/6Du2nuaz9/YXXeZNt2IVgKBzSIQhNIG/yCUNjhGK/9G4CCSTiPpdJKOLOmwkn4r6ReSviDpc5L+to1gBaG0GZUglDY4HtBbOUVHGreRdC1Jhx8A4w+SXt6RzRMkfWqbQAtCaTMaQSh1OP6HpBukS/9X0mvqbtv5q5g/95Z0d0kHm/C2/5D06GRCe5N7QjPtLg1CaYNlEEodjreU9OR06XMlXb/utp2+CgJ5iaQrrfGWL05azT/XaKPJrUEoTWBUEEodjs+XdO0glH3AgmAhWiu/l/R0Sa9M/pLfSTqipDMm4rhe8rPYe24n6fF1wzDfVUEobbANQqnD8RuSThSEshes80h6n7RPke4XJV1W0jcHID2/JKKDhzHX/FzSCSRBPhuTIJQ20AehjON4dEk/NpeFySO9tnOqXs5g8qvO2XpqST8Yh3OPufhsdx1m06sr7p3tkiCUNtAGoYzjeHnnhD2gE8oRJKFVECLOglP2YeNQ7rniwJK+L+mY5vonpihRZRPtL9tGQjl7UvkuKOk4ktjZcDYRg/+8pPdKYjL+cEU4ziDpkl07tI+KeNQU52d3YIC/Jundkt4k6SuVz1iSUE6Y8LmopJN2Ts6jdZ7+wyV8ftY59z4r6a2S3pDep/IV9rsMZ+ElElbnlHQMSUeR9CdJP+368Mn0HByKhDFL4pP4xvpyxYrIzxzjl/M8cv9+nXwW/M2Cv7mk60g6VcLafj/2Tn3fX0jSO92XmIPfmtDgKzofy5XN9a9yf09oqs2l20QoJPI8RhILZUxI6nlSl9xzL0k4sGrk9JIemhZIzfWQGLF+slq/PnLDEoRy7G5XekCKjNhdra9rJEI9siPLR0n6Y80Lm2tQw7n3ZBX3/bIjmbt15P+MRPz2lpaEMuf4gadNFCMEe/BEJoS2fSZzC0LByXraRNSZrBnfKfKUzoy8hbnhXZIuPKWB1tduC6FcqssMhG0PNfEFPyPpYp2X/Ccj911F0gs6QjnExPa5nAVzheQ867t9bkI5U9KYmHhT5SOSwJf3qJH7dcRw35oL3TVEcMgx+bv5vBWhzD1+dBkSYRyz8P9ETiBkLy0IZQWI97vlpZKuaj5FW7xGi4ZXbWMbCAXTAxXdJvT8JX32nuTI47uTJ3UONd8Kqve5JHFPSbDdUQWxObOQEMTOg1MM7QNzhxTnU6ZnXNw19OfOq05tDinPJZmTUNjFPpTMMvvsN3cq+MskfSmlZdP/s6SwIv+18mlJ5xjAKF+LNgYJWMHUBCcyMjEJMa8IX16tC1MeyV3rbXhMCf4hD+8iF9c119P327r7IT2wtrLE+PE8oiM2aoJvgjR3zLwsmJTMI9YNeG9aGBPGIgsaziqbQbP32DShMNlYpPgysvA3rPvlwlsymPdPpo79ml2Vz73gX8Dvgh8my4+SxvHRARQJ28H+VmNiUZ6tp4ZiLkJB7f54qunI3f1NwgcS7hNsfha33XHBB5z6hIkJJpbYWfS0VdJuUNlfmLQf2yYbBBuBl6d1fq+bmQ9rnLJLjR/dQuuw6e6YcThI8Q/do5ujz0nXcC3EU2tqD0C+1ldEg5jbVvAvobVvTDZNKLCpneQ4+yiIYtEPCYNLck8WJvzxCoOMT+b25jrUWjSNGtBRHV/kOsFnqJVe5iIU+s47ZMGcuICkD1TMmDuktOx8KRocqe992OKIpu0skAL+rKEitENKgmipQcny9mSG+i6uQihLjR99RROzGtdX00YHJh+uwHvpS5ib1ryhsp25vVHZJKGgbXw7EUEG4U5uEfSBc/zkDbdmjI/B4y8hrGZVVrISbz0BcQbpzBWLZQ5CYWyY1Ccxz6cYzJsJfa/D/ZhDmIpZ7pKcrf4ezEieZaXv+AV/L5mv+E+yYE6iEWIeWZlKKEuOH/3EnLFzhc8e4o6amDB1Zr2UyA4+RyvnrdxoZu3YJgkF1RivtBUiGbXhYBJ42FGYuEwG/n6LaYxIBba/FdR6itJqBafcY91iwXZGPbYyB6EwQciitELIGBKuFd9/zEk0QC+YQ/cxHxIut1rH0PPA4x1pHBgL/j0uhd/tfVMJZcnxo5+eUNAGj5VC5LV4L3Ed/jG0yeyb4pmk6WOablw2SSgk8bADZCHlGLuwlTw42b65PbIPyWuZIuQd4JizggpMLoyVOQjlri7JaRV8cDJzXxZC4fgJfHo2/hiiZVkIR95qClAV104llCXHj+57QsGshNS3SfCRvC3lHuV+EcUjFb8vKLFo/zdJKBQ+2QpLNIx1Ki49cGgrNlrDoiFRa4pAFORwWOcmfg124LkJhcgUiV5ZcIDmwrrad2B8cSri68hC/cgHXQN+MWFWYV61lKmEsuT4lQgFzRQ/1LYIGj2RSes4RpOETGxJw0b7u0lC8SGv1gOI/8Cq7VP9J3lgKNLC1MjC+RP4euYmFJyd7EhZSLJDc5gq+DeOa266Sff3M83fTFBvwhGqfd3UB41cP5VQlhy/EqGgoa2Cd2PY9jRHdIyonY3AYb6yYda6CObo135tbpJQ/EJtHUPHxMEGzkJ+BYfYTBV8LmRpZiEj9KaukTlMHlKwbTh9ar/7rr+zS9bCwf0ddzFp4djpLWUqoSw5fiVC6YvotcRkrC3mFdqwNz/xrUH65E9tlWySUHDe2eQg4v4kP7US8gQObRqbUnhl+4B5QOJcFnIzSOqyMgeh+DBmK1we6BywJM5R/2OFJLihPJ1V+jKVUJYcvxKhzKGlTcGNPB/mmi9FIeP7RtviM/EvtE2EQpamddJOAb90rc98JDmJWp6pQpYqxXFZSunNcxAK5QQkdrUWb1qWCOXcKTu35bOnEsqS41ciFJIbKbDchJAq8HpnsuNQJ2+LDWFrZZOE4k0evPoU+7USclAIQ2dZtX2S4GyotRSim4NQqHq2OShMpqnFYzVYlkwedkVCwS1lKqEsOX7bRChohxyeZLV3yJVq560/g3eThOKdjq1j6YRLCZtmeWrhqL2aBYN/gUWX5RFd5IeQrpU5CIWUe1uTQ9boHWs6PPEazuXwtvgc/oOphLLk+G0LoZwvkYnNMfluOq7Cm6UTh3mZyzdJKD4sihMQZ2ArQWW0ZeecPXGRiY0TbsWWtxm5nP8JOc1NKD61es6zLrw/aw5taCqhLDl+20AonANEoqf1+xEQ4OyesVKUidN6vss3SSj4NDBDsuCExGdA6naNUPVqq0OpUrVFbKU6Icr/p5wMTtq9//W/Ukr6HBqKr+NBU5oj6gPWJEtZ5x+qtc2BGRsPUtZtSJOzWHzx3FRCWXL8Nk0olD7gq+Owrywk1l26y4WhGPT/jWySUEonVk1xBvqzIHw4l8xPX5E7NXpBmNn6LUhyw/vusxLnIBR2LLIgrUBwc/ywE+9oQ+rY7Exuf5RA38QmscpWdPNDVb6wciqhLDl+myQUNBLG1NZcUYwIwW+6onkykW2SUDgli13XpsPXHhCDKYLTzjquONyHKuQspfY51PeGlShBEiRXWcfo81yVc25qDkKhbZ/cRcgQ51ytgA9Vw4S+0UJQqX0SG20xmf1xEdSG4NcaE4508CHm0lGGnlBIuLPno/jnLDl+myQUMLZ5TawJNg5fXDk2Dlvx/SYJBQD8zoi5Q/bfWITBaw4wOREdrx769in4IqfkYxXo+/J/biF87LUGPp+LUPhZSv9bKxTN4V8YE/w+aHGcdpaFcgf7t22DZClbu4LdzsQeOoGd+QNRWd/U+zviwrnohUxPW+lNaj3+gSFZavw2RSheC0PzZY7NoYWOzZcm32+aUEj75pAYzjLJwg7KzlVK/WaRsNCJtNi+k1+CT8YLfhZSlDkHJAvqOYuyj1Rol/R0HK/WGcuvs12zB/W5CIUDluinzdTlkOgbp8ON+iYBPg12PnuAMeea4P/pq7bmO1Rte14thZEcdmULDPMzGTu0Dn/kIPVSpcOffEUzfhZ8QvjO+mSp8dsEoTDP8M9xvGeW1uUnTUhiSiObJhT6Cktzwrw/eBk1mnj89xJ5UJdD8eCJ3QuSJ4K/oc/ex1fDbmidhmgq1MZQkEg+DL4RfAbsyFcvHFTDkQEcfdCX6jwXofCqHN7NQrehRD4nrEwmJYSJeowZSM0R2gGZvCxGKzWJg14j4H4OpeK4STQYku1whIMTZOWPgBwK/ePkJVJlBVyflYrbcJjzLP8zEkuM3yYIhbmMxmiFv9dNp4eUfIX8FE5Y69ptIBRegEOg8Z9MPUSaxcQp35z0NiTsmgyWDcnVAgdh4W3HZ9MncxIKz2QXg1ztb7DU9p/rWKSUHtQI1/o8m5r7cMKiWdpDqu19HKfJ5jB0FmtfaH/u8dsEofCrDa2PiOA9MCPtuUA1Y9fsmm0hFF4IDYDkLXsMYd+LspNRCcoiqf2JCJLcMJX8TyL0PQOVHP8F9UVjP+84N6HQRwodOS4Tp7I9TmFoMkC4nNI2dYKhffDeXhssPQuTBXOzxoGL/4aNo+9nQIZyheYcvyCURpSyTYSSXwmnKRpB/qEvclPoJ2o9qhxRC6I5q5Zt44+gfRyJ+FYwddg9WRhoOvgYcArj+Byy7+0QLEEo+Xlk7UKKhBUxA+k/pkfOwyFlH3MIjQYzZUrejX0nTER2O/7hKCQsjG+G59gf+sK31PdDX6VpSmoAJMc4E4KHrDmPBV8aJ+zZSF3p/jnGLwhlhwml0atFM4FAILA0AtuooSyNQTwvEAgEGiEQhNIIyGgmEAgE9s3lCDwCgUAgEFgLgdBQ1oIvbg4EAgGLQBBKzIdAIBBohkAQSjMoo6FAIBAIQok5EAgEAs0QCEJpBmU0FAgEAkEoMQcCgUCgGQJBKM2gjIYCgUAgCCXmQCAQCDRDIAilGZTRUCAQCAShxBwIBAKBZggEoTSDMhoKBAKBIJSYA4FAINAMgSCUZlBGQ4FAIBCEEnMgEAgEmiEQhNIMymgoEAgEglBiDgQCgUAzBIJQmkEZDQUCgUAQSsyBQCAQaIZAEEozKKOhQCAQCEKJORAIBALNEAhCaQblAb6h86cfZwOI1wz8KPsBHqiGAPBDdTdI7fEDdeC+UQlC2Sj8O/Vwfrj9P9MbMcnHfgFwp15+Qy9zS0lPTs9+rqTrb6gfex8bhLLpEdid5/+fpOMGoSw6oM+XdO0glEUxj4ctgABEAqFkCQ1lAdAlfUPSiYJQlgE7nrIcAleV9NIglOUATz9e/2PzxDB5FoU/HjYnAo+VdLsglDkh3q/tyzsn7E4TCmoYL3xxSSdObHpISb9MatqHus+x/z674hCgYl9G0oUlnVrSUSQdSdLvJf1c0ve6iMN7Jb2ju+b9E55Bv85prj+bpI+bv8+dbNZzdU7HE0g6jKTfSmKn+KikV0l6naR/TnhmvvRgki7RtXPJ1IdjpPf6k6SfSvpk9/lbJb1E0h9WaP9YaUyIxoAZfx9O//452l9J+lF6xrskvUISzx2SD0gCj1phfHjOkIA373+h5I85mqSDp/f/STefPpwweJukP1c++GLpnnz5m7oxvbS59wiSrifpUt17n0bSUdP4MVe/Iuk9kp4h6QeVz7OXHUTSeTtcL9st/jNIOqkknse8Ya6Cx5e7Z39M0oslfXHkGQ+SdM8J/bhiReTniGktsVbpI5gfOc3rn6X1+nZJb5D0tbFnt3bKMhgP6MC/qSTAHJM3d4vxFl3o6ztjF6bvjy7pPpJukiZazW0M1t0lsVDGhAXLBMzCxH53WtjPknS5sQa6wflER5RXcj6Fsdto95GSTjZ2YSLlu6VJXkNcLOSHdP25kSRIq0Z+0ZHWXST9z8DFLQnlrJIebsLOY338dlpYLxq7MJEzG0WW93VjCqkimGpPS5vRUFOQ11276x5f8bx8CRseY3qKCfdAdqyd7/fc05JQ2ODvkMYZkhuTfyQlAELr69+e3amVoJWgEZxwYoPsBOwOHxm571SS3tJN8uNNbJ/LAQPwxibE6xNb20nBZGQSsnvVyrcknT6x/Ng99+t2xPuOXVT4Hg0P5+ffB+5Fi2J3OckK7XPLkzqN77967m1FKGgHz5R00BX6CBncKo1v3+1nTiSfv4fwITA2pacnDa320WABJmNyr25DfeDYRT3foxWcJ2lH/pJWhIJGj8ZxjhX6iKbIpvuZ0r2tCAU1icSaY7uHoKKihWCC/DWRAWr9+dx1v0nqFguxJMdP7bPbWoGEcAZ+OqnFh5VEsg8Edc2CFnOzro//PQDiq7vnXMF8f5W0aLOKjPlD8hDedVRWNDLUdEJ3mA9WHi3pTiMDBtszSaygHby204Y+lcw32j2jpKsVdtInSrpNzzMwFTDDUGOtMBFemdRryPzAyfxBNb+GJDC0cuMeTQW1mGcgmJWWtPCnWCct12AWeo0KQkTzs/KXLnLxvA5Xdms0EeYN5hmLDC3rOO568jBuPYAzxM7czPL5pAWwSUBizD3w+KAkFgvvhJnOPLDmL/f/Mc0vTNA+8Q7qfB95OpAw0TDMycN3GJ0yaUmQnhXmF2apN+sYmzw+aHTXNTfR/m1dO4yvbwNzC62dDdoK7gdwZ87gNqB/aFfggPvCcgWmGqT8dQ9CK0J5uSQWX5ZfpwXNpCgJKj6+gEOZL7FVL1i4mD6i+WB+ZPmbJMjBT0Z7O2CgcdiJ/rtu8pw2TdRSv/CBYHdmeWOyt/FZ3LCwSPJ1J+9s5Xe6yc6gQLAskJJAEgyeNUOYFDdPZo2/B1v3hYks7XdgBnZeUJ3Zga1gLkJgfaYSZIzZx/tk+WEKTQ75VPAD2HtqwsaMCwudCZ4FHwJzgwVVEtR0NASIxUo2TUv3nM7tppAUmwELlvGlLRstsW1gsvhNgb/ZLErCWOJnAMcs30y+Pp7bJ5iXEIQVSHlIo0Y7Yw1kqXXK+vuYC7gEeFc0+ZLgK2Nt4HLIwpwD933mUgtCwR71E/oiaYENYLgnq+/Z7gI6bu1dvmaCsWNbIUPwqUONp+/YaT7niItFxqItCc7IK7svABkVD8IYElR3nx2KSglplATfzAXcAF1UEmTZJywotDFrl2PSWL9Pvpfnoj1lQVvEmTwm7DzsYFZwlGJu9skqhMKYWp8UdjnaFCr/mPh7GWOIoySYqnzvhU0KTRYNqE/QYCA5nKlZwAE8SsLnfhPF2YkTeUwgN/qThfEbMklWIZQzJce77cs9JD10rHNpLqFh2Q2QtQLR7JUWhMKuiXmRBdsMr/aY8OyvOg3iCQW1jfasVx6VFTW2j039cx/V+TLuaD5Ee0KFRn31UiKUpyQ7fex9DpE0C6t1QVxeS6AdJijvboWFjH0/JphX+E+ygAM7BxpRFhziDDQaEt9hkrILjfmQ8v0sItTxLA9L9/f1bSqhsIOzc2NuZcGRjclZI/jRuN/6XXxELrdTIhTMAMbAJuP1PRet7v7mS6JhzJ+SYJYSZMiYM8fBvsZ57k0lfGOYHX0RvVUIBSc7mnYWNETMrdq1xFxG883iI2ZrO2VZPNhpLKYs2OGYMzWCDwFWZjGwM7H74hfIgs8Ee9VGjG4v6XE1jadr2PVo1wo7I+aQlxKhEHkZDZelhgjtsgtkweFqJ2P+nM+YqFkIT9ZGA/BdsLuCWf4HHmN9ZHLXTGz6RPSEccyCbY0G1idTCeXeKRqY24McMIFq+8d9+LKw7bP0+ZNKhMImmFPWB15rz1dsjqQCZGHxQWQ1fZ2COfOMeWCFQEefX3EqoZQ2PKwETKVa8WsJ0oNc9/qU1tVQcFp5EwV2HnJa1Xae6zABvLqIre5397E2sZGt/de30D2h4HSy6u7Yc7w29RinHeX7fXi6Vgsae36r7/FTED3JAvkOhcynEgqOepzzWVZ5fzQB7suCw9yaePnzEqFAJpBKjZzF5SJxD5oD+UctBQe/XzdoDzjnSzKVUM7uIqkQ4jGTI7r2PeALfGrkSGXBethr5q1LKITRMFOyoK2wg7YS8i2sfYeZghOvZnewfSDJzUaWsMFtNCdf6wkFZzOqaK3Yilvu6YtAoI0RusuCd97iWPu8ua5D47ERA+x78ir6ZCqhoFnZeUIIl9DxFPGbGU5jImLeB1UiFByyY0lkuS9EQ77gOoYZU+PrmfI+ON1ZP1aGzOCphEI00Jq8+KxyMeeUfvrNkBD5g3MD6xIKOzAmSBZMC6vyT+lo6Vocr9aBysBOyQfJbRINyudG8BmmCTuPF08oDBo7Ya1g6mFHZykRCrsbfhwrqO5Wra59Xu11qOhoezhliXIRhmfx8a+U/wFp4wDO0pJQSKLyGbOYvl7THXs3CJnxsoI26UOZJUJhZ+6L7PjnYop+yX1YSyj4iiBiHMYQEyY8mB+6kP/CWvSbcUtC8eURmMjWHzKGd/4ef6TdXPYxH9clFL9QbRZibQeHrvO2PF5m8iWmit9xAbOUleoJpSaXxPalhlBYzD4zeCjsOfVd7fXkVNw51dmgUq8qLQmFRTYUQl21j9xXWoAlQmFRk0JQI6sQCr4GNluieOussZaEQkR1jvNS9pkb67wsg0FCEN75LEMhtZrB89f4UBrqlrW9a9tEJSM8loUEJmsH5s+XIBQ0BF/DNBRern1Hf1123pLvsq60JBSfF7Ju3+z91Hb5EoulCQXzDd/OKpm/HouWhOJzrFrhTlLjXndCa0Lpy4lYtfPeyYmDlrj+VKGWhbBplr7Q36YIpZR/M/Ud7fWEYxlon3eCqYEZR04NWhpOQHwPPmw4pw+l5JNY513tvSXTcUlCIWKJA9uGw+kfJPeC5GAlaxyHrk94nNuH4v17rTDfx82xLqF4k4f0ZVKkW4nPcVm1fZxRNkW9L0y7BKGUTB78G4SCW0kpaRDtjlCwd/yVnjknoZA6z6KyQnIk5vIcshShoJHga7GZ2eS7gHlNfs3chELFNGUUWUistNnnTbBfl1C8o2dKPkXNC6A6WqcoA+ZrEGraIdZu6x7IBCWM5mUJQik5Jafk7tS8L9qJJXZS2TG1Ssl8pfa8M7ylyYPD1/sv+vKCat517JqlCAVzy28KY7Vjtu+Y4GjOVlqaPL6MgHC0ryEaw3L0+3UJxYeiSGMm2W2oAna0U+YCPMpkumZBTcRDPrV9f84J6p+NxuT2lyAUnuXDplQbc+xDCwF/VGqbDEilNeRfKz5PpCWh0AfOFrHZpswjm9BY28+a65YiFG9WY04Slu2r5fJ993kifN+SUHxtF3NwHUd9Eft1CQUbHTPECh7uYmlzoQdk79kKYvJLbDivVCfETkv6fa2wsMgZQKXMQuTDEtXShIIvCDMnC1mftihx7N0ImdqaCgiEgjeE7EpfXIfTrPagKQiJMbDV060Jxdfi1Ba2jeFS+n4pQiGb+DqmA/hN0FpqpXTkQUtCwTnvk+Q4aqRpxG1dQiml3pNTAFvXiM929OFcSrVh0lwmT5sku/nKzKFnEWb29jmfEYL2spSGgjZC+nkWTAB2i9pTyHzm77VSujzt+XJ9PptSPoCajuPWSmtC4bAi6oOyjFVm18ylvmuWIhR/9AUpD4xLjbA5kD+Df81KS0LBx8PRGHajwAIgvN1M1iUUOsLRdVc3PSLHgglco+qRsmsrN0s7Fedq2GxVSIfCtVqzx8ffYWSqkEsFUUsRCuUDZJda6Ssk9INNermvYLY1Hxyq5Os/UKd9BXFpElF0h9feZvFy3Vj0zmfKciTA0NESOGaZJ9Ys6zt3pW+yU2HNxoW2h8MZs7ZUObwUoVBpbuudMBtt9fDQon1Eyhfy1wxF/3ymLAWj1k9Yep6/p+/claG+EnlFGwZ3/u1TYNmCUPAU+9J+TquyxW+lDmLO4Gm2fSjlEZTar2VWGJ6yfTtxCR/b3dH2bSlC4ZloTTZJD4ccTrKhs0vBikHkeIgs++QBpPwHwsP2nJEarZEMXvKISGmnL2STZhnLgOZ4AJvBXKNF+rwItBTev+Y4UHJsOFzL1ln1naa2FKH4MhGyoan2Hjv/l82ShEiuJzvZZigPZVDjc7IHS9XkgJW01ynJm77UhroeEhX3EnkLQmHSeZuYz1DpqcMpaRLkkqDZWP8JO0zfocd+8tEmTqahXZC2UENJk86CWomPJ/sbPNEtSSglsqO0gAlWqjNhwbPD2Cpg+k+iHzu0FZ+/g6pLqUFf5SrJZrw7C5TKXzYEe1YNEwYcfclAfqavlcKcxG8zVHOFlkj5vD0hDu2RXx8cOsYBgmDuWAL7btJaS4t3KUIpFRHiCMchXhLMHIpU2eBYh5ialLHYqnMiMxy+VBJfsY4fDe2UsR4SH8HjWvyJrNe+Q7ToK2YqprrlDAgt/3Lhnme2IhQ89kwOW9FL+0xgsmkxU+gsNiImjk+44hg+VHlfvp2BIaSGQ8kfMcnuTMIQTlryKyAozAmYnVCkfT9MMDSCIdV/SULh3bwvhc9YvKjLaDBk9KJpsHNzmI0/ArPvsCh/0jvtEnVAM8N8YdLhpIZIyHTGIYwWB0Zojvh0/KFE+FGYxGg/LFx7ULHP8+F5jA31SZA34w7uPh+jlC+DKUpyGP8gNxYKdj8LDfKkjsQmjoEX71s6tY5+LEUoPMtHE/mMzZZFjFnIfAQLMOZcEggAASeKVSlGtQd8EebHzwhBk25gnaqM2T6HGyUHK5ssPjbWDNh4bZwoKZXZPv0CUsbvwztwPxiz3liXuDTsKXT0GU2Z8Wh+Ylte9CxkJuvUQ6SZoJDM2CHVeKRpn51tqrCAGLCxSMfShMJ7MOCw/1Rh8LGZ+3xJU2s3mHw4EZnUTHwWc57wvm8+SlYKefp72AHDJUgqAAADRklEQVT9+blcwztQaVx7Ir9tF2Kjz0M/Er4kobBIIU5rbo6NK3OSIwAgzhLB5vshZqvNERAhQXCouh9XhDWPc1v4yCBsf2buWF/z9+TbsJ720/RbaShWk6BuBmBqfkYDuw/TpebkLJ4BePhmYG0b+ekDgt0O9ZiQXE14bBOEQt/ZlYhc1ZAl5EhdUukkOIsDC5RzTWoqStEgqca2KQDsSmBXklLYvaSl2Hv7CIVr0MAg1tLk7xtbtDjOXR07G2dJQqGv1GWhlXtt2r8HxyyQuInvJSccMqfRHkrHWXpCoT3Occb/0rfW+giFe/HVgB8mldd8+zBHY8YRjv+meMpba0LJHSH9GLMDVZRFgv2dQ8zsfKhwpNXb08hr2ZHriBJg0tA+ER9CrqiEmE7knDDJABMW7jvwuPS8TREKfYEA0NTyD31hPrKTEEq2P/TFIh9z9Nl3I/+ACAo+DRKtMB+4HxLHV4FKzr+SpsMYcigzfif6h7OWCBO2fcnPQZSD4jgWBBMWnwv3MM4QYClUb/sKsfD+HLyNis28Ybenv0xmzAZID9PJR8n65s/ShEI/mOsQNOYZjlA2QkwINgP6TY4KUZnSJseYY1oyv3GMo7mwZjA58bl4wVcIKeBGwIzFXGUN4AZgXP05x/5+fHNoSORFgT+Y0wdMGdYTbgvyyvDT0YfBH4Cbi1CmkENcGwgEAjuCQBDKjgxkvEYgsA0IBKFswyhEHwKBHUEgCGVHBjJeIxDYBgSCULZhFKIPgcCOIBCEsiMDGa8RCGwDAkEo2zAK0YdAYEcQCELZkYGM1wgEtgGBIJRtGIXoQyCwIwgEoezIQMZrBALbgEAQyjaMQvQhENgRBIJQdmQg4zUCgW1AIAhlG0Yh+hAI7AgCQSg7MpDxGoHANiAQhLINoxB9CAR2BIEglB0ZyHiNQGAbEAhC2YZRiD4EAjuCQBDKjgxkvEYgsA0IBKFswyhEHwKBHUEgCGVHBjJeIxDYBgSCULZhFKIPgcCOIBCEsiMDGa8RCGwDAkEo2zAK0YdAYEcQCELZkYGM1wgEtgGBIJRtGIXoQyCwIwgEoezIQMZrBALbgEAQyjaMQvQhENgRBIJQdmQg4zUCgW1A4F97zin53c1O/QAAAABJRU5ErkJggg==" />
@@ -1513,7 +1522,7 @@
- + @@ -1525,18 +1534,21 @@ >
-
group 1 created.
+
+ collector + 1 created. +
timer start
@@ -1556,7 +1568,10 @@ >
-
group 1
+
+ collector + 1 +
concatenate
@@ -1567,7 +1582,7 @@ y="927" width="69" height="32" - xlink:href="data:image/png;base64,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" + xlink:href="data:image/png;base64,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" />
@@ -1803,7 +1818,7 @@
- + @@ -1815,18 +1830,21 @@ >
-
group 1 created.
+
+ collector + 1 created. +
timer start
@@ -1856,7 +1874,10 @@ >
-
group 1
+
+ collector + 1 +
concatenate
@@ -1867,7 +1888,7 @@ y="260" width="69" height="32" - xlink:href="data:image/png;base64,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" + xlink:href="data:image/png;base64,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" />
@@ -2098,7 +2119,7 @@
- + @@ -2110,18 +2131,21 @@ >
-
group 1 created.
+
+ collector + 1 created. +
timer start
@@ -2151,7 +2175,10 @@ >
-
group 1
+
+ collector + 1 +
concatenate
@@ -2162,7 +2189,7 @@ y="251" width="69" height="32" - xlink:href="data:image/png;base64,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" + xlink:href="data:image/png;base64,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" />
From 89650a293f2a148aa8f040f81cb48eadcc2b0495 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 2 Aug 2024 11:08:17 +0900 Subject: [PATCH 006/115] chore: upload jitter.png and add old design link Signed-off-by: vividf --- .../docs/concatenate-data.md | 8 +++++--- .../docs/image/jitter.png | Bin 0 -> 118229 bytes 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 sensing/autoware_pointcloud_preprocessor/docs/image/jitter.png diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index 4e0dd2887dc8f..00192d55a2fff 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -65,9 +65,11 @@ The figure below demonstrates how `lidar_timestamp_offsets` works with `concaten #### lidar_timestamp_noise_window -Additionally, due to the mechanical design of LiDARs, there may be some jitter in the timestamps of each scan. For example, if the scan frequency is set to 10 Hz (scanning every 100 ms), the timestamps between each scan might not be exactly 100 ms apart. To handle this noise, the `lidar_timestamp_noise_window` parameter is provided. +Additionally, due to the mechanical design of LiDARs, there may be some jitter in the timestamps of each scan like the image shown below. For example, if the scan frequency is set to 10 Hz (scanning every 100 ms), the timestamps between each scan might not be exactly 100 ms apart. To handle this noise, the `lidar_timestamp_noise_window` parameter is provided. -Take the left LiDAR from the above example: if the timestamps of the left point clouds are 0.01, 0.11, and 0.21 seconds, the timestamp is ideal without any noise. Then the example will be the same as above. However, if the timestamps of the left point clouds are 0.010, 0.115, and 0.210 seconds respectively, resulting in differences of 105 ms and 95 ms, the noise is 5 ms (compared to 100 ms). In this case, the user should set 0.005 in the `lidar_timestamp_noise_window` parameter. +![jitter](./image/jitter.png) + +From the example above, the noise is from 0 to 8 ms, the user should set 0.008 in the `lidar_timestamp_noise_window` parameter. The figure below demonstrates how `lidar_timestamp_noise_window` works with `concatenate_and_time_sync_node`. If the green `X` is in the range of the red triangles, it means that the point cloud matches the reference timestamp of the collector. @@ -180,7 +182,7 @@ status: There is also an option to separate the concatenate_and_time_sync_node into two nodes: one for `time synchronization` and another for `concatenate pointclouds` ([See this PR](https://github.com/autowarefoundation/autoware.universe/pull/3312)). -Note that the `concatenate_pointclouds` and `time_synchronizer_nodelet` are using the old design of the concatenate node. +Note that the `concatenate_pointclouds` and `time_synchronizer_nodelet` are using the [old design](https://github.com/autowarefoundation/autoware.universe/blob/9bb228fe5b7fa4c6edb47e4713c73489a02366e1/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md) of the concatenate node. ## Assumptions / Known Limits diff --git a/sensing/autoware_pointcloud_preprocessor/docs/image/jitter.png b/sensing/autoware_pointcloud_preprocessor/docs/image/jitter.png new file mode 100644 index 0000000000000000000000000000000000000000..c984e940171a647289e71323849df461021cac54 GIT binary patch literal 118229 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 wNgb0RjeFxaF_1%Nq)_H;E>UUjtih8@< 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%0AIEJ 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 V;c()a_x|1~$STXEUom+4zX1N`pFIEo literal 0 HcmV?d00001 From d423a2250c6235a297143879de35e987ee4df3f1 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 2 Aug 2024 13:53:00 +0900 Subject: [PATCH 007/115] chore: add the link to the tool for analyzing timestamp Signed-off-by: vividf --- .../docs/concatenate-data.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index 00192d55a2fff..34a0903c00951 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -51,7 +51,7 @@ Three parameters, `timeout_sec`, `lidar_timestamp_offsets`, and `lidar_timestamp #### timeout_sec -When network issues occur or when point clouds experience delays in the previous processing pipeline, some point clouds may be delayed or dropped. To address this, the `timeout_sec` parameter is used. If the timer reaches zero, the collector will not wait for delayed or dropped point clouds but will concatenate the remaining point clouds in the collector directly. The figure below demonstrates how `timeout_sec` works with `concatenate_and_time_sync_node`. +When network issues occur or when point clouds experience delays in the previous processing pipeline, some point clouds may be delayed or dropped. To address this, the `timeout_sec` parameter is used. Once the timer is created, it will start counting down from `timeout_sec`. If the timer reaches zero, the collector will not wait for delayed or dropped point clouds but will concatenate the remaining point clouds in the collector directly. The figure below demonstrates how `timeout_sec` works with `concatenate_and_time_sync_node`. ![concatenate_edge_case](./image/concatenate_edge_case.drawio.svg) @@ -65,13 +65,15 @@ The figure below demonstrates how `lidar_timestamp_offsets` works with `concaten #### lidar_timestamp_noise_window -Additionally, due to the mechanical design of LiDARs, there may be some jitter in the timestamps of each scan like the image shown below. For example, if the scan frequency is set to 10 Hz (scanning every 100 ms), the timestamps between each scan might not be exactly 100 ms apart. To handle this noise, the `lidar_timestamp_noise_window` parameter is provided. +Additionally, due to the mechanical design of LiDARs, there may be some jitter in the timestamps of each scan, as shown in the image below. For example, if the scan frequency is set to 10 Hz (scanning every 100 ms), the timestamps between each scan might not be exactly 100 ms apart. To handle this noise, the `lidar_timestamp_noise_window` parameter is provided. + +User can use [this tool](https://github.com/tier4/timestamp_analyzer) to visualize the noise betweeen each scan. ![jitter](./image/jitter.png) -From the example above, the noise is from 0 to 8 ms, the user should set 0.008 in the `lidar_timestamp_noise_window` parameter. +From the example above, the noise ranges from 0 to 8 ms, so the user should set `lidar_timestamp_noise_window` to `0.008`. -The figure below demonstrates how `lidar_timestamp_noise_window` works with `concatenate_and_time_sync_node`. If the green `X` is in the range of the red triangles, it means that the point cloud matches the reference timestamp of the collector. +The figure below demonstrates how `lidar_timestamp_noise_window` works with the `concatenate_and_time_sync_node`. If the green `X` is within the range of the red triangles, it indicates that the point cloud matches the reference timestamp of the collector. ![noise_timestamp_offset](./image/noise_timestamp_offset.drawio.svg) From 3d21b6c0cc6cdb2f0241c08c70ecf316ae9608a8 Mon Sep 17 00:00:00 2001 From: vividf Date: Wed, 4 Sep 2024 10:56:47 +0900 Subject: [PATCH 008/115] fix: fix bug that timer didn't cancel Signed-off-by: vividf --- .../src/concatenate_data/cloud_collector.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 315d53e304c4c..3f1339c47e22c 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -109,6 +109,7 @@ void CloudCollector::processCloud( void CloudCollector::concatenateCallback() { + timer_->cancel(); // lock for protecting collector list and concatenated pointcloud std::lock_guard lock(mutex_); auto [concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map] = From 542ce9737e17d9ea94668931f0e952f4fa049559 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 5 Sep 2024 21:59:30 +0900 Subject: [PATCH 009/115] chore: fix logic for logging Signed-off-by: vividf --- .../concatenate_and_time_sync_node.hpp | 4 +- .../concatenate_and_time_sync_node.cpp | 39 +++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 1f653b19fdb6a..93d324bd5c7c8 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -122,8 +122,8 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node std::vector lidar_timestamp_noise_window; } params_; - double current_concat_cloud_timestamp_{0.0}; - double lastest_concat_cloud_timestamp_{0.0}; + double current_concatenate_cloud_timestamp_{0.0}; + double lastest_concatenate_cloud_timestamp_{0.0}; bool drop_previous_but_late_pointcloud_{false}; bool publish_pointcloud_{false}; double diagnostic_reference_timestamp_min_{0.0}; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index e0f71fee8eb0c..0061b715e228e 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -314,32 +314,37 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( double reference_timestamp_min, double reference_timestamp_max) { // should never come to this state. - if (concatenate_cloud_ptr == nullptr) return; - - current_concat_cloud_timestamp_ = rclcpp::Time(concatenate_cloud_ptr->header.stamp).seconds(); + if (concatenate_cloud_ptr == nullptr) { + RCLCPP_ERROR(this->get_logger(), "Concatenate cloud is a nullptr."); + return; + } + current_concatenate_cloud_timestamp_ = + rclcpp::Time(concatenate_cloud_ptr->header.stamp).seconds(); if ( - current_concat_cloud_timestamp_ < lastest_concat_cloud_timestamp_ && + current_concatenate_cloud_timestamp_ < lastest_concatenate_cloud_timestamp_ && !params_.publish_previous_but_late_pointcloud) { drop_previous_but_late_pointcloud_ = true; } else { publish_pointcloud_ = true; - lastest_concat_cloud_timestamp_ = current_concat_cloud_timestamp_; - auto concat_output = std::make_unique(*concatenate_cloud_ptr); - concatenate_cloud_publisher_->publish(std::move(concat_output)); + lastest_concatenate_cloud_timestamp_ = current_concatenate_cloud_timestamp_; + auto concatenate_pointcloud_output = + std::make_unique(*concatenate_cloud_ptr); + concatenate_cloud_publisher_->publish(std::move(concatenate_pointcloud_output)); + // publish transformed raw pointclouds - for (const auto & pair : topic_to_transformed_cloud_map) { - if (pair.second) { - if (params_.publish_synchronized_pointcloud) { + if (params_.publish_synchronized_pointcloud) { + for (auto topic : params_.input_topics) { + if (topic_to_transformed_cloud_map.find(topic) != topic_to_transformed_cloud_map.end()) { auto transformed_cloud_output = - std::make_unique(*pair.second); - topic_to_transformed_cloud_publisher_map_[pair.first]->publish( + std::make_unique(*topic_to_transformed_cloud_map[topic]); + topic_to_transformed_cloud_publisher_map_[topic]->publish( std::move(transformed_cloud_output)); + } else { + RCLCPP_WARN( + this->get_logger(), + "transformed_raw_points[%s] is nullptr, skipping pointcloud publish.", topic.c_str()); } - } else { - RCLCPP_WARN( - this->get_logger(), "transformed_raw_points[%s] is nullptr, skipping pointcloud publish.", - pair.first.c_str()); } } } @@ -438,7 +443,7 @@ void PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus( diagnostic_updater::DiagnosticStatusWrapper & stat) { if (publish_pointcloud_ || drop_previous_but_late_pointcloud_) { - stat.add("concatenated cloud timestamp", formatTimestamp(current_concat_cloud_timestamp_)); + stat.add("concatenated cloud timestamp", formatTimestamp(current_concatenate_cloud_timestamp_)); stat.add("reference timestamp min", formatTimestamp(diagnostic_reference_timestamp_min_)); stat.add("reference timestamp max", formatTimestamp(diagnostic_reference_timestamp_max_)); From 77a3a794a710ca1ff96d0bd61cc915bb30700192 Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:21:29 +0900 Subject: [PATCH 010/115] Update sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../autoware_pointcloud_preprocessor/docs/concatenate-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index 34a0903c00951..dc2d60f0e2d20 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -14,7 +14,7 @@ When a point cloud arrives, its timestamp is checked, and an offset is subtracte ### Step 2: Trigger the Timer -Once a collector is created, a timer for that collector starts counting down (this value is defined by `timeout_sec`). The collector begins to concatenate the point clouds either when the required number of point clouds has been collected or when the timer counts down to zero. +Once a collector is created, a timer for that collector starts counting down (this value is defined by `timeout_sec`). The collector begins to concatenate the point clouds either when all point clouds defined in `input_topics` have been collected or when the timer counts down to zero. ### Step 3: Concatenate the Point Clouds From 52030a7282cc1eb60e2fb616c1246bea82376048 Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:22:35 +0900 Subject: [PATCH 011/115] Update sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../src/concatenate_data/combine_cloud_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 11985a4c8720d..da91605d46e70 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -160,7 +160,7 @@ CombineCloudHandler::combinePointClouds( output_frame_, *cloud, *transformed_cloud_ptr, tf_buffer_)) { RCLCPP_ERROR( node_->get_logger(), - "Transform pointcloud from %s to %s failed, Please check the defined output frame.", + "Transforming pointcloud from %s to %s failed, please check the defined output frame.", cloud->header.frame_id.c_str(), output_frame_.c_str()); transformed_cloud_ptr = cloud; } From 3ddf249863ea5fd137bf4241129cd836b772c3c0 Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:23:26 +0900 Subject: [PATCH 012/115] Update sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../schema/cocatenate_and_time_sync_node.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json index 183b807ec83b2..798f778feda04 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json @@ -73,7 +73,7 @@ "type": "number" }, "default": [], - "description": "List of LiDAR timestamp noise window." + "description": "List of LiDAR timestamp noise windows." } }, "required": [ From d6797360a27e3861b82e3860ec374c752a5eb6fd Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:23:55 +0900 Subject: [PATCH 013/115] Update sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../schema/cocatenate_and_time_sync_node.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json index 798f778feda04..3444c1419310f 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json @@ -52,7 +52,7 @@ "type": "string" }, "default": [], - "description": "List of input topics." + "description": "List of input point cloud topics." }, "output_frame": { "type": "string", From 1f9d24c972f46102249b6db129da7a201f5301b6 Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:24:28 +0900 Subject: [PATCH 014/115] Update sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../src/concatenate_data/combine_cloud_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index da91605d46e70..1b8c33037e108 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -81,7 +81,7 @@ CombineCloudHandler::CombineCloudHandler( void CombineCloudHandler::processTwist( const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input) { - // if rosbag restart, clear buffer + // If time jumps backwards (e.g. when a rosbag restarts), clear buffer if (!twist_ptr_queue_.empty()) { if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > rclcpp::Time(input->header.stamp)) { twist_ptr_queue_.clear(); From 09452e7560c7fce14308b377b4cd4c008556009f Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:24:44 +0900 Subject: [PATCH 015/115] Update sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../src/concatenate_data/combine_cloud_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 1b8c33037e108..27e55133f16c6 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -237,7 +237,7 @@ Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( if (twist_ptr_queue_.empty()) { RCLCPP_WARN_STREAM_THROTTLE( node_->get_logger(), *node_->get_clock(), std::chrono::milliseconds(10000).count(), - "No twist is available. Please confirm twist topic and timestamp"); + "No twist is available. Please confirm twist topic and timestamp. Leaving point cloud untransformed."); return Eigen::Matrix4f::Identity(); } From d900d3f8be1407274b5fc6987022b06bca5b6d73 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 19 Sep 2024 11:32:57 +0900 Subject: [PATCH 016/115] chore: remove distortion corrector related changes Signed-off-by: vividf --- .../docs/distortion-corrector.md | 8 ------- .../distortion_corrector.hpp | 16 +++++++------- .../distortion_corrector.cpp | 8 +++---- .../test/test_distortion_corrector_node.cpp | 22 +++++++++---------- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/distortion-corrector.md b/sensing/autoware_pointcloud_preprocessor/docs/distortion-corrector.md index 44a064e89ad1a..ab5a07b5279bc 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/distortion-corrector.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/distortion-corrector.md @@ -44,14 +44,6 @@ Please note that the processing time difference between the two distortion metho ros2 launch autoware_pointcloud_preprocessor distortion_corrector.launch.xml ``` -## Test - -```bash -colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release --packages-up-to autoware_pointcloud_preprocessor - -colcon test --packages-select autoware_pointcloud_preprocessor --event-handlers console_cohesion+ -``` - ## Assumptions / Known limits - The node requires time synchronization between the topics from lidars, twist, and IMU. diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/distortion_corrector/distortion_corrector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/distortion_corrector/distortion_corrector.hpp index 7e26d4c1cf2c8..e786bff04b3cd 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/distortion_corrector/distortion_corrector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/distortion_corrector/distortion_corrector.hpp @@ -50,10 +50,10 @@ namespace autoware::pointcloud_preprocessor class DistortionCorrectorBase { public: - virtual bool pointcloudTransformExists() = 0; - virtual bool pointcloudTransformNeeded() = 0; - virtual std::deque getTwistQueue() = 0; - virtual std::deque getAngularVelocityQueue() = 0; + virtual bool pointcloud_transform_exists() = 0; + virtual bool pointcloud_transform_needed() = 0; + virtual std::deque get_twist_queue() = 0; + virtual std::deque get_angular_velocity_queue() = 0; virtual void processTwistMessage( const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr twist_msg) = 0; @@ -104,10 +104,10 @@ class DistortionCorrector : public DistortionCorrectorBase managed_tf_buffer_ = std::make_unique(node, has_static_tf_only); } - bool pointcloudTransformExists(); - bool pointcloudTransformNeeded(); - std::deque getTwistQueue(); - std::deque getAngularVelocityQueue(); + bool pointcloud_transform_exists(); + bool pointcloud_transform_needed(); + std::deque get_twist_queue(); + std::deque get_angular_velocity_queue(); void processTwistMessage( const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr twist_msg) override; diff --git a/sensing/autoware_pointcloud_preprocessor/src/distortion_corrector/distortion_corrector.cpp b/sensing/autoware_pointcloud_preprocessor/src/distortion_corrector/distortion_corrector.cpp index eff4e726352b6..d0119fbc44f24 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/distortion_corrector/distortion_corrector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/distortion_corrector/distortion_corrector.cpp @@ -25,25 +25,25 @@ namespace autoware::pointcloud_preprocessor { template -bool DistortionCorrector::pointcloudTransformExists() +bool DistortionCorrector::pointcloud_transform_exists() { return pointcloud_transform_exists_; } template -bool DistortionCorrector::pointcloudTransformNeeded() +bool DistortionCorrector::pointcloud_transform_needed() { return pointcloud_transform_needed_; } template -std::deque DistortionCorrector::getTwistQueue() +std::deque DistortionCorrector::get_twist_queue() { return twist_queue_; } template -std::deque DistortionCorrector::getAngularVelocityQueue() +std::deque DistortionCorrector::get_angular_velocity_queue() { return angular_velocity_queue_; } diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_distortion_corrector_node.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_distortion_corrector_node.cpp index 2e0306c6217c2..047d021a4c6da 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_distortion_corrector_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_distortion_corrector_node.cpp @@ -286,9 +286,9 @@ TEST_F(DistortionCorrectorTest, TestProcessTwistMessage) auto twist_msg = generateTwistMsg(twist_linear_x_, twist_angular_z_, timestamp); distortion_corrector_2d_->processTwistMessage(twist_msg); - ASSERT_FALSE(distortion_corrector_2d_->getTwistQueue().empty()); - EXPECT_EQ(distortion_corrector_2d_->getTwistQueue().front().twist.linear.x, twist_linear_x_); - EXPECT_EQ(distortion_corrector_2d_->getTwistQueue().front().twist.angular.z, twist_angular_z_); + ASSERT_FALSE(distortion_corrector_2d_->get_twist_queue().empty()); + EXPECT_EQ(distortion_corrector_2d_->get_twist_queue().front().twist.linear.x, twist_linear_x_); + EXPECT_EQ(distortion_corrector_2d_->get_twist_queue().front().twist.angular.z, twist_angular_z_); } TEST_F(DistortionCorrectorTest, TestProcessIMUMessage) @@ -297,9 +297,9 @@ TEST_F(DistortionCorrectorTest, TestProcessIMUMessage) auto imu_msg = generateImuMsg(imu_angular_x_, imu_angular_y_, imu_angular_z_, timestamp); distortion_corrector_2d_->processIMUMessage("base_link", imu_msg); - ASSERT_FALSE(distortion_corrector_2d_->getAngularVelocityQueue().empty()); + ASSERT_FALSE(distortion_corrector_2d_->get_angular_velocity_queue().empty()); EXPECT_NEAR( - distortion_corrector_2d_->getAngularVelocityQueue().front().vector.z, -0.03159, + distortion_corrector_2d_->get_angular_velocity_queue().front().vector.z, -0.03159, standard_tolerance_); } @@ -329,22 +329,22 @@ TEST_F(DistortionCorrectorTest, TestIsInputValid) TEST_F(DistortionCorrectorTest, TestSetPointCloudTransformWithBaseLink) { distortion_corrector_2d_->setPointCloudTransform("base_link", "base_link"); - EXPECT_TRUE(distortion_corrector_2d_->pointcloudTransformExists()); - EXPECT_FALSE(distortion_corrector_2d_->pointcloudTransformNeeded()); + EXPECT_TRUE(distortion_corrector_2d_->pointcloud_transform_exists()); + EXPECT_FALSE(distortion_corrector_2d_->pointcloud_transform_needed()); } TEST_F(DistortionCorrectorTest, TestSetPointCloudTransformWithLidarFrame) { distortion_corrector_2d_->setPointCloudTransform("base_link", "lidar_top"); - EXPECT_TRUE(distortion_corrector_2d_->pointcloudTransformExists()); - EXPECT_TRUE(distortion_corrector_2d_->pointcloudTransformNeeded()); + EXPECT_TRUE(distortion_corrector_2d_->pointcloud_transform_exists()); + EXPECT_TRUE(distortion_corrector_2d_->pointcloud_transform_needed()); } TEST_F(DistortionCorrectorTest, TestSetPointCloudTransformWithMissingFrame) { distortion_corrector_2d_->setPointCloudTransform("base_link", "missing_lidar_frame"); - EXPECT_FALSE(distortion_corrector_2d_->pointcloudTransformExists()); - EXPECT_FALSE(distortion_corrector_2d_->pointcloudTransformNeeded()); + EXPECT_FALSE(distortion_corrector_2d_->pointcloud_transform_exists()); + EXPECT_FALSE(distortion_corrector_2d_->pointcloud_transform_needed()); } TEST_F(DistortionCorrectorTest, TestUndistortPointCloudWithEmptyTwist) From bcbe94a27b8e5721d07fc96ecb9c172c2a340db1 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 19 Sep 2024 12:32:40 +0900 Subject: [PATCH 017/115] feat: add managed tf buffer Signed-off-by: vividf --- .../concatenate_and_time_sync_node.param.yaml | 1 + .../combine_cloud_handler.hpp | 9 +++--- .../concatenate_and_time_sync_node.hpp | 2 +- .../cocatenate_and_time_sync_node.schema.json | 8 ++++- .../combine_cloud_handler.cpp | 31 +++++++------------ .../concatenate_and_time_sync_node.cpp | 3 +- .../test/test_concatenate_node_component.py | 1 + .../test/test_concatenate_node_unit.cpp | 5 +-- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml index d023479acf6e3..d99849b532f3e 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml @@ -1,5 +1,6 @@ /**: ros__parameters: + has_static_tf_only: false maximum_queue_size: 5 timeout_sec: 0.2 is_motion_compensated: true diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index 1e51e072928e3..e2a7b4c69da70 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -64,6 +64,7 @@ // ROS includes #include "autoware_point_types/types.hpp" +#include #include #include @@ -81,8 +82,6 @@ #include #include #include -#include -#include namespace autoware::pointcloud_preprocessor { @@ -91,13 +90,12 @@ class CombineCloudHandler { private: rclcpp::Node * node_; - tf2_ros::Buffer tf_buffer_; - tf2_ros::TransformListener tf_listener_; std::vector input_topics_; std::string output_frame_; bool is_motion_compensated_; bool keep_input_frame_in_synchronized_pointcloud_; + std::unique_ptr managed_tf_buffer_{nullptr}; struct RclcppTimeHash_ { @@ -112,7 +110,8 @@ class CombineCloudHandler CombineCloudHandler( rclcpp::Node * node, std::vector input_topics, std::string output_frame, - bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud); + bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud, + bool has_static_tf_only); void processTwist(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input); void processOdometry(const nav_msgs::msg::Odometry::ConstSharedPtr & input); diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 93d324bd5c7c8..993a07ee98568 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -67,7 +67,6 @@ #include "combine_cloud_handler.hpp" #include -#include #include #include #include @@ -108,6 +107,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node private: struct Parameters { + bool has_static_tf_only; int maximum_queue_size; double timeout_sec; bool is_motion_compensated; diff --git a/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json index 3444c1419310f..21abc823381dd 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json @@ -74,6 +74,11 @@ }, "default": [], "description": "List of LiDAR timestamp noise windows." + }, + "has_static_tf_only": { + "type": "boolean", + "default": false, + "description": "Flag to indicate if only static TF is used." } }, "required": [ @@ -88,7 +93,8 @@ "input_topics", "output_frame", "lidar_timestamp_offsets", - "lidar_timestamp_noise_window" + "lidar_timestamp_noise_window", + "has_static_tf_only" ] } }, diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 27e55133f16c6..2f0dc20813b9b 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -67,14 +67,15 @@ namespace autoware::pointcloud_preprocessor CombineCloudHandler::CombineCloudHandler( rclcpp::Node * node, std::vector input_topics, std::string output_frame, - bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud) + bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud, + bool has_static_tf_only) : node_(node), - tf_buffer_(node_->get_clock()), - tf_listener_(tf_buffer_), input_topics_(input_topics), output_frame_(output_frame), is_motion_compensated_(is_motion_compensated), - keep_input_frame_in_synchronized_pointcloud_(keep_input_frame_in_synchronized_pointcloud) + keep_input_frame_in_synchronized_pointcloud_(keep_input_frame_in_synchronized_pointcloud), + managed_tf_buffer_( + std::make_unique(node_, has_static_tf_only)) { } @@ -155,18 +156,7 @@ CombineCloudHandler::combinePointClouds( sensor_msgs::msg::PointCloud2::SharedPtr cloud = pair.second; auto transformed_cloud_ptr = std::make_shared(); - if (output_frame_ != cloud->header.frame_id) { - if (!pcl_ros::transformPointCloud( - output_frame_, *cloud, *transformed_cloud_ptr, tf_buffer_)) { - RCLCPP_ERROR( - node_->get_logger(), - "Transforming pointcloud from %s to %s failed, please check the defined output frame.", - cloud->header.frame_id.c_str(), output_frame_.c_str()); - transformed_cloud_ptr = cloud; - } - } else { - transformed_cloud_ptr = cloud; - } + managed_tf_buffer_->transformPointcloud(output_frame_, *cloud, *transformed_cloud_ptr); topic_to_original_stamp_map[topic] = rclcpp::Time(cloud->header.stamp).seconds(); @@ -212,9 +202,9 @@ CombineCloudHandler::combinePointClouds( if (keep_input_frame_in_synchronized_pointcloud_ && need_transform_to_sensor_frame) { sensor_msgs::msg::PointCloud2::SharedPtr transformed_cloud_ptr_in_sensor_frame( new sensor_msgs::msg::PointCloud2()); - pcl_ros::transformPointCloud( - (std::string)cloud->header.frame_id, *transformed_delay_compensated_cloud_ptr, - *transformed_cloud_ptr_in_sensor_frame, tf_buffer_); + managed_tf_buffer_->transformPointcloud( + cloud->header.frame_id, *transformed_delay_compensated_cloud_ptr, + *transformed_cloud_ptr_in_sensor_frame); transformed_cloud_ptr_in_sensor_frame->header.stamp = oldest_stamp; transformed_cloud_ptr_in_sensor_frame->header.frame_id = cloud->header.frame_id; topic_to_transformed_cloud_map[topic] = transformed_cloud_ptr_in_sensor_frame; @@ -237,7 +227,8 @@ Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( if (twist_ptr_queue_.empty()) { RCLCPP_WARN_STREAM_THROTTLE( node_->get_logger(), *node_->get_clock(), std::chrono::milliseconds(10000).count(), - "No twist is available. Please confirm twist topic and timestamp. Leaving point cloud untransformed."); + "No twist is available. Please confirm twist topic and timestamp. Leaving point cloud " + "untransformed."); return Eigen::Matrix4f::Identity(); } diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 0061b715e228e..390cc422f736e 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -84,6 +84,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro stop_watch_ptr_->tic("processing_time"); // initialize parameters + params_.has_static_tf_only = declare_parameter("has_static_tf_only"); params_.maximum_queue_size = declare_parameter("maximum_queue_size"); params_.timeout_sec = declare_parameter("timeout_sec"); params_.is_motion_compensated = declare_parameter("is_motion_compensated"); @@ -189,7 +190,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro // Combine cloud handler combine_cloud_handler_ = std::make_shared( this, params_.input_topics, params_.output_frame, params_.is_motion_compensated, - params_.keep_input_frame_in_synchronized_pointcloud); + params_.keep_input_frame_in_synchronized_pointcloud, params_.has_static_tf_only); // Diagnostic Updater updater_.setHardwareID("concatenate_data_checker"); diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index ce0c2653022d1..84f08a2b8725e 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -84,6 +84,7 @@ def generate_test_description(): ], parameters=[ { + "has_static_tf_only": False, "maximum_queue_size": 5, "timeout_sec": TIMEOUT_SEC, "is_motion_compensated": True, diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 397310f5b4335..4cc513b01b62b 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -41,7 +41,8 @@ class ConcatenateCloudTest : public ::testing::Test // Instead of "input_topics", other parameters are not unsed. // They just helps to setup the concatenate node node_options.parameter_overrides( - {{"maximum_queue_size", 5}, + {{"has_static_tf_only", false}, + {"maximum_queue_size", 5}, {"timeout_sec", 0.2}, {"is_motion_compensated", true}, {"publish_synchronized_pointcloud", true}, @@ -60,7 +61,7 @@ class ConcatenateCloudTest : public ::testing::Test combine_cloud_handler_ = std::make_shared( concatenate_node_.get(), std::vector{"lidar_top", "lidar_left", "lidar_right"}, - "base_link", true, true); + "base_link", true, true, false); collector_ = std::make_shared( std::dynamic_pointer_cast< From 22b654a82fb41cb8d26cca2728ebc0b0829da59b Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 15:25:31 +0900 Subject: [PATCH 018/115] chore: fix filename Signed-off-by: vividf --- ...ode.schema.json => concatenate_and_time_sync_node.schema.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sensing/autoware_pointcloud_preprocessor/schema/{cocatenate_and_time_sync_node.schema.json => concatenate_and_time_sync_node.schema.json} (100%) diff --git a/sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json similarity index 100% rename from sensing/autoware_pointcloud_preprocessor/schema/cocatenate_and_time_sync_node.schema.json rename to sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json From 076bfaa87f54e1342a688d1931b183d88dc8e501 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 15:48:58 +0900 Subject: [PATCH 019/115] chore: add explanataion for maximum queue size Signed-off-by: vividf --- .../schema/concatenate_and_time_sync_node.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json index 21abc823381dd..67f31b6003b5a 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json @@ -9,7 +9,7 @@ "maximum_queue_size": { "type": "integer", "default": 5, - "description": "Maximum size of the queue." + "description": "Maximum size of the queue for the Keep Last policy in QoS history." }, "timeout_sec": { "type": "number", From 0e59f48aa25c3b399c2f1fb3a56d9474b5a0b4d7 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 16:06:11 +0900 Subject: [PATCH 020/115] chore: add explanation for timeout_sec Signed-off-by: vividf --- .../autoware_pointcloud_preprocessor/docs/concatenate-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index dc2d60f0e2d20..c86b8a5aa9dee 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -51,7 +51,7 @@ Three parameters, `timeout_sec`, `lidar_timestamp_offsets`, and `lidar_timestamp #### timeout_sec -When network issues occur or when point clouds experience delays in the previous processing pipeline, some point clouds may be delayed or dropped. To address this, the `timeout_sec` parameter is used. Once the timer is created, it will start counting down from `timeout_sec`. If the timer reaches zero, the collector will not wait for delayed or dropped point clouds but will concatenate the remaining point clouds in the collector directly. The figure below demonstrates how `timeout_sec` works with `concatenate_and_time_sync_node`. +When network issues occur or when point clouds experience delays in the previous processing pipeline, some point clouds may be delayed or dropped. To address this, the `timeout_sec` parameter is used. Once the timer is created, it will start counting down from `timeout_sec`. If the timer reaches zero, the collector will not wait for delayed or dropped point clouds but will concatenate the remaining point clouds in the collector directly. The figure below demonstrates how `timeout_sec` works with `concatenate_and_time_sync_node` when `timeout_sec` is set to `0.12` (120 ms). ![concatenate_edge_case](./image/concatenate_edge_case.drawio.svg) From 8e7997687f6ac8d1d01b3aa0d2f2aa9a8dd84773 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 16:49:54 +0900 Subject: [PATCH 021/115] chore: fix schema's explanation Signed-off-by: vividf --- .../schema/concatenate_and_time_sync_node.schema.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json index 67f31b6003b5a..254fb06e3ffa0 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json @@ -13,8 +13,8 @@ }, "timeout_sec": { "type": "number", - "default": 0.0, - "description": "Timeout in seconds." + "default": 0.1, + "description": "Timer's timeout duration in seconds when collectors are created. Collectors will concatenate the point clouds when timeout_sec reaches zero." }, "is_motion_compensated": { "type": "boolean", @@ -39,7 +39,7 @@ "synchronized_pointcloud_postfix": { "type": "string", "default": "pointcloud", - "description": "Postfix for the synchronized point cloud." + "description": "Postfix for the topic name of the synchronized point cloud." }, "input_twist_topic_type": { "type": "string", @@ -65,7 +65,7 @@ "type": "number" }, "default": [], - "description": "List of LiDAR timestamp offsets." + "description": "List of LiDAR timestamp offsets in seconds. The offset values should be specified in the same order as the input_topics." }, "lidar_timestamp_noise_window": { "type": "array", @@ -73,7 +73,7 @@ "type": "number" }, "default": [], - "description": "List of LiDAR timestamp noise windows." + "description": "List of LiDAR timestamp noise windows in seconds. The noise values should be specified in the same order as the input_topics." }, "has_static_tf_only": { "type": "boolean", From 66b4092d6044f7d83f2178c7092a005ce3d5725d Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 17:11:31 +0900 Subject: [PATCH 022/115] chore: fix description for twist and odom Signed-off-by: vividf --- .../docs/concatenate-data.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index c86b8a5aa9dee..2303e65129fda 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -28,10 +28,10 @@ After concatenation, the concatenated point cloud is published, and the collecto ### Input -| Name | Type | Description | -| --------------- | ------------------------------------------------ | --------------------------------------------------------------------------------- | -| `~/input/twist` | `geometry_msgs::msg::TwistWithCovarianceStamped` | The twist information used to interpolate the timestamp of each LiDAR point cloud | -| `~/input/odom` | `nav_msgs::msg::Odometry` | The vehicle odometry used to interpolate the timestamp of each LiDAR point cloud | +| Name | Type | Description | +| --------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `~/input/twist` | `geometry_msgs::msg::TwistWithCovarianceStamped` | Twist information adjusts the point cloud scans based on vehicle motion, allowing LiDARs with different timestamp to be synchronized for concatenation. | +| `~/input/odom` | `nav_msgs::msg::Odometry` | Vehicle odometry adjusts the point cloud scans based on vehicle motion, allowing LiDARs with different timestamp to be synchronized for concatenation. | By setting the `input_twist_topic_type` parameter to `twist` or `odom`, the subscriber will subscribe to either `~/input/twist` or `~/input/odom`. If the user doesn't want to use the twist information or vehicle odometry to compensate for motion, set `is_motion_compensated` to `false`. From 10d83ee10c6a8f5cd62efa1cb6f2d1e57b7d75fd Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 17:13:57 +0900 Subject: [PATCH 023/115] chore: remove license that are not used Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 37 ------------------- .../combine_cloud_handler.hpp | 37 ------------------- .../concatenate_and_time_sync_node.hpp | 37 ------------------- .../src/concatenate_data/cloud_collector.cpp | 37 ------------------- .../combine_cloud_handler.cpp | 37 ------------------- .../concatenate_and_time_sync_node.cpp | 37 ------------------- 6 files changed, 222 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index f71bb1df968b5..d589e07a2caa1 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -12,43 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* - * Software License Agreement (BSD License) - * - * Copyright (c) 2009, Willow Garage, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Willow Garage, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ - * - */ - #ifndef AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CLOUD_COLLECTOR_HPP_ #define AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CLOUD_COLLECTOR_HPP_ diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index e2a7b4c69da70..61209c9395cba 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -12,43 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* - * Software License Agreement (BSD License) - * - * Copyright (c) 2009, Willow Garage, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Willow Garage, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ - * - */ - #ifndef AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__COMBINE_CLOUD_HANDLER_HPP_ #define AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__COMBINE_CLOUD_HANDLER_HPP_ diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 993a07ee98568..3ddc76dacc8f2 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -12,43 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* - * Software License Agreement (BSD License) - * - * Copyright (c) 2009, Willow Garage, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Willow Garage, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ - * - */ - #ifndef AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODE_HPP_ #define AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODE_HPP_ diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 3f1339c47e22c..ccad72777f83e 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -12,43 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* - * Software License Agreement (BSD License) - * - * Copyright (c) 2009, Willow Garage, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Willow Garage, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ - * - */ - #include "autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp" #include "autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp" diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 2f0dc20813b9b..fbf1a6570b17b 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -12,43 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* - * Software License Agreement (BSD License) - * - * Copyright (c) 2009, Willow Garage, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Willow Garage, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ - * - */ - #include "autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp" #include diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 390cc422f736e..26a986f4041ad 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -12,43 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* - * Software License Agreement (BSD License) - * - * Copyright (c) 2009, Willow Garage, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Willow Garage, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ - * - */ - #include "autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp" #include "autoware/pointcloud_preprocessor/utility/memory.hpp" From fbb5fe9ab325538a936bd23a33aa0f4f63cf8110 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 18:19:28 +0900 Subject: [PATCH 024/115] chore: change guard to prama once Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 7 +------ .../concatenate_data/combine_cloud_handler.hpp | 7 +------ .../concatenate_data/concatenate_and_time_sync_node.hpp | 7 +------ 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index d589e07a2caa1..c7d3956c1b268 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -12,8 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CLOUD_COLLECTOR_HPP_ -#define AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CLOUD_COLLECTOR_HPP_ +#pragma once #include "combine_cloud_handler.hpp" @@ -73,7 +72,3 @@ class CloudCollector }; } // namespace autoware::pointcloud_preprocessor - -// clang-format off -#endif // AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CLOUD_COLLECTOR_HPP_ // NOLINT -// clang-format on diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index 61209c9395cba..bcbd40bc6a2a3 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -12,8 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__COMBINE_CLOUD_HANDLER_HPP_ -#define AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__COMBINE_CLOUD_HANDLER_HPP_ +#pragma once #include #include @@ -90,7 +89,3 @@ class CombineCloudHandler }; } // namespace autoware::pointcloud_preprocessor - -// clang-format off -#endif // AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__COMBINE_CLOUD_HANDLER_HPP_ // NOLINT -// clang-format on diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 3ddc76dacc8f2..91342abea386d 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -12,8 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODE_HPP_ -#define AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODE_HPP_ +#pragma once #include #include @@ -129,7 +128,3 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node }; } // namespace autoware::pointcloud_preprocessor - -// clang-format off -#endif // AUTOWARE__POINTCLOUD_PREPROCESSOR__CONCATENATE_DATA__CONCATENATE_AND_TIME_SYNC_NODE_HPP_ // NOLINT -// clang-format on From 3f0732e46d8e81869e0aecf11477356cf85e250c Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 18:26:29 +0900 Subject: [PATCH 025/115] chore: default value change to string Signed-off-by: vividf --- .../schema/concatenate_and_time_sync_node.schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json index 254fb06e3ffa0..37608d2900fe3 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json @@ -8,12 +8,12 @@ "properties": { "maximum_queue_size": { "type": "integer", - "default": 5, + "default": "5", "description": "Maximum size of the queue for the Keep Last policy in QoS history." }, "timeout_sec": { "type": "number", - "default": 0.1, + "default": "0.1", "description": "Timer's timeout duration in seconds when collectors are created. Collectors will concatenate the point clouds when timeout_sec reaches zero." }, "is_motion_compensated": { From c19250b0630815f422be4e8cf2917dbbf622ffe3 Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:32:36 +0900 Subject: [PATCH 026/115] Update sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../test/test_concatenate_node_unit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 4cc513b01b62b..c2b82841ecddb 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -172,7 +172,7 @@ class ConcatenateCloudTest : public ::testing::Test std::shared_ptr tf_broadcaster_; static constexpr int32_t timestamp_seconds_{10}; - static constexpr uint32_t timestamp_nanoseconds_{100000000}; + static constexpr uint32_t timestamp_nanoseconds_{100'000'000}; static constexpr size_t number_of_points_{3}; static constexpr float standard_tolerance_{1e-4}; static constexpr int number_of_pointcloud_{3}; From 5ec228c303c0858d661033a25987810b25da135c Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:41:04 +0900 Subject: [PATCH 027/115] Update sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../test/test_concatenate_node_unit.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index c2b82841ecddb..a3cc6a1d15e7e 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -436,8 +436,8 @@ TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) TEST_F(ConcatenateCloudTest, TestProcessMultipleCloud) { rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); - rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40000000, RCL_ROS_TIME); - rclcpp::Time right_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 80000000, RCL_ROS_TIME); + rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40'000'000, RCL_ROS_TIME); + rclcpp::Time right_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 80'000'000, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = generatePointCloudMsg(true, false, "lidar_top", top_timestamp); sensor_msgs::msg::PointCloud2 left_pointcloud = From f4c869e2863372480224a30175dbf3606c4fbe8f Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:42:03 +0900 Subject: [PATCH 028/115] Update sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../test/test_concatenate_node_unit.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index a3cc6a1d15e7e..4569aab45c2ec 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -263,8 +263,8 @@ TEST_F(ConcatenateCloudTest, TestSetAndGetReferenceTimeStampBoundary) TEST_F(ConcatenateCloudTest, TestConcatenateClouds) { rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); - rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40000000, RCL_ROS_TIME); - rclcpp::Time right_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 80000000, RCL_ROS_TIME); + rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40'000'000, RCL_ROS_TIME); + rclcpp::Time right_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 80'000'000, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = generatePointCloudMsg(true, false, "lidar_top", top_timestamp); sensor_msgs::msg::PointCloud2 left_pointcloud = From 4cca6c118c03ee620ea86d774056ff7092ca6828 Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:43:23 +0900 Subject: [PATCH 029/115] Update sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../test/test_concatenate_node_unit.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 4569aab45c2ec..491954f4399aa 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -211,18 +211,18 @@ TEST_F(ConcatenateCloudTest, TestProcessOdometry) TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) { - rclcpp::Time old_stamp(10, 100000000, RCL_ROS_TIME); - rclcpp::Time new_stamp(10, 150000000, RCL_ROS_TIME); + rclcpp::Time old_stamp(10, 100'000'000, RCL_ROS_TIME); + rclcpp::Time new_stamp(10, 150'000'000, RCL_ROS_TIME); // Time difference between twist msg is more than 100 miliseconds, won't calculate the difference auto twist_msg1 = std::make_shared(); - twist_msg1->header.stamp = rclcpp::Time(10, 130000000, RCL_ROS_TIME); + twist_msg1->header.stamp = rclcpp::Time(10, 130'000'000, RCL_ROS_TIME); twist_msg1->twist.linear.x = 1.0; twist_msg1->twist.angular.z = 0.1; combine_cloud_handler_->twist_ptr_queue_.push_back(twist_msg1); auto twist_msg2 = std::make_shared(); - twist_msg2->header.stamp = rclcpp::Time(10, 160000000, RCL_ROS_TIME); + twist_msg2->header.stamp = rclcpp::Time(10, 160'000'000, RCL_ROS_TIME); twist_msg2->twist.linear.x = 1.0; twist_msg2->twist.angular.z = 0.1; combine_cloud_handler_->twist_ptr_queue_.push_back(twist_msg2); From 53279b59ae5660d3dfd4c6f68c3b236703ecca14 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:45:37 +0000 Subject: [PATCH 030/115] style(pre-commit): autofix --- .../test/test_concatenate_node_unit.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 491954f4399aa..6aa45446c7a6c 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -263,8 +263,10 @@ TEST_F(ConcatenateCloudTest, TestSetAndGetReferenceTimeStampBoundary) TEST_F(ConcatenateCloudTest, TestConcatenateClouds) { rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); - rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40'000'000, RCL_ROS_TIME); - rclcpp::Time right_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 80'000'000, RCL_ROS_TIME); + rclcpp::Time left_timestamp( + timestamp_seconds_, timestamp_nanoseconds_ + 40'000'000, RCL_ROS_TIME); + rclcpp::Time right_timestamp( + timestamp_seconds_, timestamp_nanoseconds_ + 80'000'000, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = generatePointCloudMsg(true, false, "lidar_top", top_timestamp); sensor_msgs::msg::PointCloud2 left_pointcloud = @@ -436,8 +438,10 @@ TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) TEST_F(ConcatenateCloudTest, TestProcessMultipleCloud) { rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); - rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40'000'000, RCL_ROS_TIME); - rclcpp::Time right_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 80'000'000, RCL_ROS_TIME); + rclcpp::Time left_timestamp( + timestamp_seconds_, timestamp_nanoseconds_ + 40'000'000, RCL_ROS_TIME); + rclcpp::Time right_timestamp( + timestamp_seconds_, timestamp_nanoseconds_ + 80'000'000, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = generatePointCloudMsg(true, false, "lidar_top", top_timestamp); sensor_msgs::msg::PointCloud2 left_pointcloud = From 853b8fe3927a10a69d632381f24a785b31374673 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 18:58:11 +0900 Subject: [PATCH 031/115] chore: clang-tidy style for static constexpr Signed-off-by: vividf --- .../test/test_concatenate_node_unit.cpp | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 491954f4399aa..95643ab9c720d 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -67,7 +67,7 @@ class ConcatenateCloudTest : public ::testing::Test std::dynamic_pointer_cast< autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent>( concatenate_node_->shared_from_this()), - collectors_, combine_cloud_handler_, number_of_pointcloud_, timeout_sec_); + collectors_, combine_cloud_handler_, number_of_pointcloud, timeout_sec); collectors_.push_back(collector_); @@ -88,7 +88,7 @@ class ConcatenateCloudTest : public ::testing::Test const std::string & parent_frame, const std::string & child_frame, double x, double y, double z, double qx, double qy, double qz, double qw) { - rclcpp::Time timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); + rclcpp::Time timestamp(timestamp_seconds, timestamp_nanoseconds, RCL_ROS_TIME); geometry_msgs::msg::TransformStamped tf_msg; tf_msg.header.stamp = timestamp; tf_msg.header.frame_id = parent_frame; @@ -114,7 +114,7 @@ class ConcatenateCloudTest : public ::testing::Test pointcloud_msg.is_bigendian = false; if (generate_points) { - std::array points = {{ + std::array points = {{ Eigen::Vector3f(10.0f, 0.0f, 0.0f), // point 1 Eigen::Vector3f(0.0f, 10.0f, 0.0f), // point 2 Eigen::Vector3f(0.0f, 0.0f, 10.0f), // point 3 @@ -130,14 +130,14 @@ class ConcatenateCloudTest : public ::testing::Test sensor_msgs::msg::PointField::FLOAT32, "distance", 1, sensor_msgs::msg::PointField::FLOAT32, "time_stamp", 1, sensor_msgs::msg::PointField::UINT32); - modifier.resize(number_of_points_); + modifier.resize(number_of_points); sensor_msgs::PointCloud2Iterator iter_x(pointcloud_msg, "x"); sensor_msgs::PointCloud2Iterator iter_y(pointcloud_msg, "y"); sensor_msgs::PointCloud2Iterator iter_z(pointcloud_msg, "z"); sensor_msgs::PointCloud2Iterator iter_t(pointcloud_msg, "time_stamp"); - for (size_t i = 0; i < number_of_points_; ++i) { + for (size_t i = 0; i < number_of_points; ++i) { *iter_x = points[i].x(); *iter_y = points[i].y(); *iter_z = points[i].z(); @@ -171,12 +171,12 @@ class ConcatenateCloudTest : public ::testing::Test std::shared_ptr collector_; std::shared_ptr tf_broadcaster_; - static constexpr int32_t timestamp_seconds_{10}; - static constexpr uint32_t timestamp_nanoseconds_{100'000'000}; - static constexpr size_t number_of_points_{3}; - static constexpr float standard_tolerance_{1e-4}; - static constexpr int number_of_pointcloud_{3}; - static constexpr float timeout_sec_{0.2}; + static constexpr int32_t timestamp_seconds{10}; + static constexpr uint32_t timestamp_nanoseconds{100'000'000}; + static constexpr size_t number_of_points{3}; + static constexpr float standard_tolerance{1e-4}; + static constexpr int number_of_pointcloud{3}; + static constexpr float timeout_sec{0.2}; bool debug_{false}; }; @@ -231,14 +231,14 @@ TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) combine_cloud_handler_->computeTransformToAdjustForOldTimestamp(old_stamp, new_stamp); // translation - EXPECT_NEAR(transform(0, 3), 0.0499996, standard_tolerance_); - EXPECT_NEAR(transform(1, 3), 0.000189999, standard_tolerance_); + EXPECT_NEAR(transform(0, 3), 0.0499996, standard_tolerance); + EXPECT_NEAR(transform(1, 3), 0.000189999, standard_tolerance); // rotation, yaw = 0.005 - EXPECT_NEAR(transform(0, 0), 0.999987, standard_tolerance_); - EXPECT_NEAR(transform(0, 1), -0.00499998, standard_tolerance_); - EXPECT_NEAR(transform(1, 0), 0.00499998, standard_tolerance_); - EXPECT_NEAR(transform(1, 1), 0.999987, standard_tolerance_); + EXPECT_NEAR(transform(0, 0), 0.999987, standard_tolerance); + EXPECT_NEAR(transform(0, 1), -0.00499998, standard_tolerance); + EXPECT_NEAR(transform(1, 0), 0.00499998, standard_tolerance); + EXPECT_NEAR(transform(1, 1), 0.999987, standard_tolerance); std::ostringstream oss; oss << "Transformation matrix:\n" << transform; @@ -262,9 +262,9 @@ TEST_F(ConcatenateCloudTest, TestSetAndGetReferenceTimeStampBoundary) TEST_F(ConcatenateCloudTest, TestConcatenateClouds) { - rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); - rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40'000'000, RCL_ROS_TIME); - rclcpp::Time right_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 80'000'000, RCL_ROS_TIME); + rclcpp::Time top_timestamp(timestamp_seconds, timestamp_nanoseconds, RCL_ROS_TIME); + rclcpp::Time left_timestamp(timestamp_seconds, timestamp_nanoseconds + 40'000'000, RCL_ROS_TIME); + rclcpp::Time right_timestamp(timestamp_seconds, timestamp_nanoseconds + 80'000'000, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = generatePointCloudMsg(true, false, "lidar_top", top_timestamp); sensor_msgs::msg::PointCloud2 left_pointcloud = @@ -414,7 +414,7 @@ TEST_F(ConcatenateCloudTest, TestDeleteCollector) TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) { - rclcpp::Time timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); + rclcpp::Time timestamp(timestamp_seconds, timestamp_nanoseconds, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = generatePointCloudMsg(true, false, "lidar_top", timestamp); sensor_msgs::msg::PointCloud2::SharedPtr top_pointcloud_ptr = @@ -435,9 +435,9 @@ TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) TEST_F(ConcatenateCloudTest, TestProcessMultipleCloud) { - rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); - rclcpp::Time left_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 40'000'000, RCL_ROS_TIME); - rclcpp::Time right_timestamp(timestamp_seconds_, timestamp_nanoseconds_ + 80'000'000, RCL_ROS_TIME); + rclcpp::Time top_timestamp(timestamp_seconds, timestamp_nanoseconds, RCL_ROS_TIME); + rclcpp::Time left_timestamp(timestamp_seconds, timestamp_nanoseconds + 40'000'000, RCL_ROS_TIME); + rclcpp::Time right_timestamp(timestamp_seconds, timestamp_nanoseconds + 80'000'000, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = generatePointCloudMsg(true, false, "lidar_top", top_timestamp); sensor_msgs::msg::PointCloud2 left_pointcloud = From d32dfbce5105d31682f5db4cce9cc30d95c76d8b Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 18:59:31 +0900 Subject: [PATCH 032/115] chore: remove unused vector header Signed-off-by: vividf --- .../pointcloud_preprocessor/concatenate_data/cloud_collector.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index c7d3956c1b268..ed1767e983414 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -21,7 +21,6 @@ #include #include #include -#include namespace autoware::pointcloud_preprocessor { From 07cb7534ea90d6d3e32948c32eb57d87c7bf8722 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 19:04:16 +0900 Subject: [PATCH 033/115] chore: fix naming concatenated_cloud_publisher Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.hpp | 2 +- .../src/concatenate_data/concatenate_and_time_sync_node.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 91342abea386d..37545a247780b 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -105,7 +105,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node rclcpp::Subscription::SharedPtr odom_sub_; // publishers - rclcpp::Publisher::SharedPtr concatenate_cloud_publisher_; + rclcpp::Publisher::SharedPtr concatenated_cloud_publisher_; std::unordered_map::SharedPtr> topic_to_transformed_cloud_publisher_map_; std::unique_ptr debug_publisher_; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 26a986f4041ad..940abf541e025 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -96,7 +96,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro } // Publishers - concatenate_cloud_publisher_ = this->create_publisher( + concatenated_cloud_publisher_ = this->create_publisher( "output", rclcpp::SensorDataQoS().keep_last(params_.maximum_queue_size)); // Transformed Raw PointCloud2 Publisher to publish the transformed pointcloud @@ -294,7 +294,7 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( lastest_concatenate_cloud_timestamp_ = current_concatenate_cloud_timestamp_; auto concatenate_pointcloud_output = std::make_unique(*concatenate_cloud_ptr); - concatenate_cloud_publisher_->publish(std::move(concatenate_pointcloud_output)); + concatenated_cloud_publisher_->publish(std::move(concatenate_pointcloud_output)); // publish transformed raw pointclouds if (params_.publish_synchronized_pointcloud) { From 490b15cec2efa8ee8155746cc14100efcca40034 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 19:06:15 +0900 Subject: [PATCH 034/115] chore: fix namimg diagnostic_updater_ Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.hpp | 2 +- .../src/concatenate_data/concatenate_and_time_sync_node.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 37545a247780b..88cd68873bdbb 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -111,7 +111,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node std::unique_ptr debug_publisher_; std::unique_ptr> stop_watch_ptr_; - diagnostic_updater::Updater updater_{this}; + diagnostic_updater::Updater diagnostic_updater_{this}; void cloud_callback( const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, const std::string & topic_name); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 940abf541e025..777f5cbf2e22d 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -156,8 +156,8 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro params_.keep_input_frame_in_synchronized_pointcloud, params_.has_static_tf_only); // Diagnostic Updater - updater_.setHardwareID("concatenate_data_checker"); - updater_.add( + diagnostic_updater_.setHardwareID("concatenate_data_checker"); + diagnostic_updater_.add( "concat_status", this, &PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus); } @@ -316,7 +316,7 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( diagnostic_reference_timestamp_min_ = reference_timestamp_min; diagnostic_reference_timestamp_max_ = reference_timestamp_max; diagnostic_topic_to_original_stamp_map_ = topic_to_original_stamp_map; - updater_.force_update(); + diagnostic_updater_.force_update(); // add processing time for debug if (debug_publisher_) { From 8f168964fe8a96ed91c97ee705d6b4edc1a20cb5 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 19:40:36 +0900 Subject: [PATCH 035/115] chore: specify parameter in comment Signed-off-by: vividf --- .../src/concatenate_data/cloud_collector.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index ccad72777f83e..da56fa1334a1d 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -56,8 +56,8 @@ std::tuple CloudCollector::getReferenceTimeStampBoundary() void CloudCollector::processCloud( std::string topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud) { - // Check if the map already contains an entry for the same topic, shouldn't happened if the - // parameter set correctly. + // Check if the map already contains an entry for the same topic. This shouldn't happen if the + // parameter 'lidar_timestamp_noise_window' is set correctly. if (topic_to_cloud_map_.find(topic_name) != topic_to_cloud_map_.end()) { RCLCPP_WARN( concatenate_node_->get_logger(), From 1d89cdd8c3be3387f472b2b231b601d4eef16eda Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 19:47:40 +0900 Subject: [PATCH 036/115] chore: change RCLCPP_WARN to RCLCPP_WARN_STREAM_THROTTLE Signed-off-by: vividf --- .../src/concatenate_data/cloud_collector.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index da56fa1334a1d..6dfc433d617a7 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -59,10 +59,11 @@ void CloudCollector::processCloud( // Check if the map already contains an entry for the same topic. This shouldn't happen if the // parameter 'lidar_timestamp_noise_window' is set correctly. if (topic_to_cloud_map_.find(topic_name) != topic_to_cloud_map_.end()) { - RCLCPP_WARN( - concatenate_node_->get_logger(), - "Topic '%s' already exists in the collector. Check the timestamp of the pointcloud.", - topic_name.c_str()); + RCLCPP_WARN_STREAM_THROTTLE( + concatenate_node_->get_logger(), *concatenate_node_->get_clock(), + std::chrono::milliseconds(10000).count(), + "Topic '" << topic_name + << "' already exists in the collector. Check the timestamp of the pointcloud."); } topic_to_cloud_map_[topic_name] = cloud; if (topic_to_cloud_map_.size() == num_of_clouds_) { From 651c666f3eaabe7be12944d08509f11959412d2c Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Sep 2024 19:59:18 +0900 Subject: [PATCH 037/115] chore: add comment for cancelling timer Signed-off-by: vividf --- .../src/concatenate_data/cloud_collector.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 6dfc433d617a7..1887f3e07b4ad 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -73,7 +73,10 @@ void CloudCollector::processCloud( void CloudCollector::concatenateCallback() { + // All pointclouds are received or the timer has timed out, cancel the timer and concatenate the + // pointclouds in the collector. timer_->cancel(); + // lock for protecting collector list and concatenated pointcloud std::lock_guard lock(mutex_); auto [concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map] = From 370483c9a22c05c4c8bc2f6563c484e10d9d51b0 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 23 Sep 2024 23:19:30 +0900 Subject: [PATCH 038/115] chore: Simplify loop structure for topic-to-cloud mapping Signed-off-by: vividf --- .../concatenate_data/combine_cloud_handler.cpp | 9 +++------ .../concatenate_and_time_sync_node.cpp | 8 ++++---- .../test/test_concatenate_node_unit.cpp | 16 ++++++---------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index fbf1a6570b17b..6db3dc12d3295 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -106,18 +106,15 @@ CombineCloudHandler::combinePointClouds( std::unordered_map topic_to_original_stamp_map; std::vector pc_stamps; - for (const auto & pair : topic_to_cloud_map) { - pc_stamps.push_back(rclcpp::Time(pair.second->header.stamp)); + for (const auto & [topic, cloud] : topic_to_cloud_map) { + pc_stamps.push_back(rclcpp::Time(cloud->header.stamp)); } std::sort(pc_stamps.begin(), pc_stamps.end(), std::greater()); const auto oldest_stamp = pc_stamps.back(); std::unordered_map transform_memo; - for (const auto & pair : topic_to_cloud_map) { - std::string topic = pair.first; - sensor_msgs::msg::PointCloud2::SharedPtr cloud = pair.second; - + for (const auto & [topic, cloud] : topic_to_cloud_map) { auto transformed_cloud_ptr = std::make_shared(); managed_tf_buffer_->transformPointcloud(output_frame_, *cloud, *transformed_cloud_ptr); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 777f5cbf2e22d..975ebd68fee69 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -327,15 +327,15 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( debug_publisher_->publish( "debug/processing_time_ms", processing_time_ms); - for (const auto & pair : topic_to_transformed_cloud_map) { - if (pair.second != nullptr) { + for (const auto & [topic, transformed_cloud] : topic_to_transformed_cloud_map) { + if (transformed_cloud != nullptr) { const auto pipeline_latency_ms = std::chrono::duration( std::chrono::nanoseconds( - (this->get_clock()->now() - pair.second->header.stamp).nanoseconds())) + (this->get_clock()->now() - transformed_cloud->header.stamp).nanoseconds())) .count(); debug_publisher_->publish( - "debug" + pair.first + "/pipeline_latency_ms", pipeline_latency_ms); + "debug" + topic + "/pipeline_latency_ms", pipeline_latency_ms); } } } diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index f1ae44d770a63..95643ab9c720d 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -262,11 +262,9 @@ TEST_F(ConcatenateCloudTest, TestSetAndGetReferenceTimeStampBoundary) TEST_F(ConcatenateCloudTest, TestConcatenateClouds) { - rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); - rclcpp::Time left_timestamp( - timestamp_seconds_, timestamp_nanoseconds_ + 40'000'000, RCL_ROS_TIME); - rclcpp::Time right_timestamp( - timestamp_seconds_, timestamp_nanoseconds_ + 80'000'000, RCL_ROS_TIME); + rclcpp::Time top_timestamp(timestamp_seconds, timestamp_nanoseconds, RCL_ROS_TIME); + rclcpp::Time left_timestamp(timestamp_seconds, timestamp_nanoseconds + 40'000'000, RCL_ROS_TIME); + rclcpp::Time right_timestamp(timestamp_seconds, timestamp_nanoseconds + 80'000'000, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = generatePointCloudMsg(true, false, "lidar_top", top_timestamp); sensor_msgs::msg::PointCloud2 left_pointcloud = @@ -437,11 +435,9 @@ TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) TEST_F(ConcatenateCloudTest, TestProcessMultipleCloud) { - rclcpp::Time top_timestamp(timestamp_seconds_, timestamp_nanoseconds_, RCL_ROS_TIME); - rclcpp::Time left_timestamp( - timestamp_seconds_, timestamp_nanoseconds_ + 40'000'000, RCL_ROS_TIME); - rclcpp::Time right_timestamp( - timestamp_seconds_, timestamp_nanoseconds_ + 80'000'000, RCL_ROS_TIME); + rclcpp::Time top_timestamp(timestamp_seconds, timestamp_nanoseconds, RCL_ROS_TIME); + rclcpp::Time left_timestamp(timestamp_seconds, timestamp_nanoseconds + 40'000'000, RCL_ROS_TIME); + rclcpp::Time right_timestamp(timestamp_seconds, timestamp_nanoseconds + 80'000'000, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = generatePointCloudMsg(true, false, "lidar_top", top_timestamp); sensor_msgs::msg::PointCloud2 left_pointcloud = From 4c0b585cddea1aed99a4b6bb6101a25c22ebc59c Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 23 Sep 2024 23:28:54 +0900 Subject: [PATCH 039/115] chore: fix spell errors Signed-off-by: vividf --- .../docs/concatenate-data.md | 2 +- .../concatenate_and_time_sync_node.hpp | 2 +- .../concatenate_and_time_sync_node.cpp | 6 +-- .../test/test_concatenate_node_component.py | 44 +++++++++---------- .../test/test_concatenate_node_unit.cpp | 2 +- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index 2303e65129fda..a81dd3f91c6ef 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -67,7 +67,7 @@ The figure below demonstrates how `lidar_timestamp_offsets` works with `concaten Additionally, due to the mechanical design of LiDARs, there may be some jitter in the timestamps of each scan, as shown in the image below. For example, if the scan frequency is set to 10 Hz (scanning every 100 ms), the timestamps between each scan might not be exactly 100 ms apart. To handle this noise, the `lidar_timestamp_noise_window` parameter is provided. -User can use [this tool](https://github.com/tier4/timestamp_analyzer) to visualize the noise betweeen each scan. +User can use [this tool](https://github.com/tier4/timestamp_analyzer) to visualize the noise between each scan. ![jitter](./image/jitter.png) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 88cd68873bdbb..055718f45829e 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -85,7 +85,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node } params_; double current_concatenate_cloud_timestamp_{0.0}; - double lastest_concatenate_cloud_timestamp_{0.0}; + double latest_concatenate_cloud_timestamp_{0.0}; bool drop_previous_but_late_pointcloud_{false}; bool publish_pointcloud_{false}; double diagnostic_reference_timestamp_min_{0.0}; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 975ebd68fee69..75bb32781c694 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -86,7 +86,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro } if (params_.lidar_timestamp_noise_window.size() != params_.input_topics.size()) { RCLCPP_ERROR( - get_logger(), "The number of topics does not match the number of timestamp noise windwo"); + get_logger(), "The number of topics does not match the number of timestamp noise window"); return; } @@ -286,12 +286,12 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( rclcpp::Time(concatenate_cloud_ptr->header.stamp).seconds(); if ( - current_concatenate_cloud_timestamp_ < lastest_concatenate_cloud_timestamp_ && + current_concatenate_cloud_timestamp_ < latest_concatenate_cloud_timestamp_ && !params_.publish_previous_but_late_pointcloud) { drop_previous_but_late_pointcloud_ = true; } else { publish_pointcloud_ = true; - lastest_concatenate_cloud_timestamp_ = current_concatenate_cloud_timestamp_; + latest_concatenate_cloud_timestamp_ = current_concatenate_cloud_timestamp_; auto concatenate_pointcloud_output = std::make_unique(*concatenate_cloud_ptr); concatenated_cloud_publisher_->publish(std::move(concatenate_pointcloud_output)); diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index 84f08a2b8725e..b0ecc7e961b7d 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -66,7 +66,7 @@ COARSE_TOLERANCE = TIMESTAMP_NOISE * 2 global_seconds = 10 -global_nanosceonds = 100000000 +global_nanoseconds = 100000000 @pytest.mark.launch_test @@ -216,7 +216,7 @@ def generate_transform_msg( qw: float, ) -> TransformStamped: tf_msg = TransformStamped() - tf_msg.header.stamp = Time(seconds=global_seconds, nanoseconds=global_nanosceonds).to_msg() + tf_msg.header.stamp = Time(seconds=global_seconds, nanoseconds=global_nanoseconds).to_msg() tf_msg.header.frame_id = parent_frame tf_msg.child_frame_id = child_frame tf_msg.transform.translation.x = x @@ -271,7 +271,7 @@ def generate_static_transform_msgs() -> List[TransformStamped]: def generate_twist_msg() -> TwistWithCovarianceStamped: twist_header = Header() - twist_header.stamp = Time(seconds=global_seconds, nanoseconds=global_nanosceonds).to_msg() + twist_header.stamp = Time(seconds=global_seconds, nanoseconds=global_nanoseconds).to_msg() twist_header.frame_id = "base_link" twist_msg = TwistWithCovarianceStamped() twist_msg.header = twist_header @@ -362,7 +362,7 @@ def test_1_normal_inputs(self): for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + frame_idx * MILLISECONDS * 40 # add 40 ms + pointcloud_nanoseconds = global_nanoseconds + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -405,7 +405,7 @@ def test_1_normal_inputs(self): self.assertTrue( np.allclose(concatenate_cloud, expected_pointcloud, atol=1e-3), - "The concatenation node have wierd output", + "The concatenation node have weird output", ) self.assertEqual( @@ -432,7 +432,7 @@ def test_2_normal_inputs_with_noise(self): noise = random.uniform(-10, 10) * MILLISECONDS pointcloud_seconds = global_seconds pointcloud_nanoseconds = ( - global_nanosceonds + frame_idx * MILLISECONDS * 40 + noise + global_nanoseconds + frame_idx * MILLISECONDS * 40 + noise ) # add 40 ms and noise (-10 to 10 ms) pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds @@ -475,7 +475,7 @@ def test_2_normal_inputs_with_noise(self): self.assertTrue( np.allclose(concatenate_cloud, expected_pointcloud, atol=2e-2), - "The concatenation node have wierd output", + "The concatenation node have weird output", ) def test_3_abnormal_null_pointcloud(self): @@ -492,7 +492,7 @@ def test_3_abnormal_null_pointcloud(self): for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + frame_idx * MILLISECONDS * 40 # add 40 ms + pointcloud_nanoseconds = global_nanoseconds + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -542,7 +542,7 @@ def test_3_abnormal_null_pointcloud(self): self.assertTrue( np.allclose(concatenate_cloud, expected_pointcloud, atol=1e-3), - "The concatenation node have wierd output", + "The concatenation node have weird output", ) global_seconds += 1 @@ -560,7 +560,7 @@ def test_4_abnormal_null_pointcloud_and_drop(self): self.twist_publisher.publish(twist_msg) pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + pointcloud_nanoseconds = global_nanoseconds pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -599,7 +599,7 @@ def test_5_abnormal_multiple_pointcloud_drop(self): self.twist_publisher.publish(twist_msg) pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + pointcloud_nanoseconds = global_nanoseconds pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -638,7 +638,7 @@ def test_5_abnormal_multiple_pointcloud_drop(self): self.assertTrue( np.allclose(concatenate_cloud, expected_pointcloud, atol=1e-3), - "The concatenation node have wierd output", + "The concatenation node have weird output", ) def test_6_abnormal_single_pointcloud_drop(self): @@ -655,7 +655,7 @@ def test_6_abnormal_single_pointcloud_drop(self): for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + frame_idx * MILLISECONDS * 40 # add 40 ms + pointcloud_nanoseconds = global_nanoseconds + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -696,7 +696,7 @@ def test_6_abnormal_single_pointcloud_drop(self): self.assertTrue( np.allclose(concatenate_cloud, expected_pointcloud, atol=1e-3), - "The concatenation node have wierd output", + "The concatenation node have weird output", ) global_seconds += 1 @@ -716,7 +716,7 @@ def test_7_abnormal_pointcloud_delay(self): for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + frame_idx * MILLISECONDS * 40 # add 40 ms + pointcloud_nanoseconds = global_nanoseconds + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -734,7 +734,7 @@ def test_7_abnormal_pointcloud_delay(self): pointcloud_seconds = global_seconds pointcloud_nanoseconds = ( - global_nanosceonds + (len(INPUT_LIDAR_TOPICS) - 1) * MILLISECONDS * 40 + global_nanoseconds + (len(INPUT_LIDAR_TOPICS) - 1) * MILLISECONDS * 40 ) # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds @@ -776,7 +776,7 @@ def test_7_abnormal_pointcloud_delay(self): self.assertTrue( np.allclose(concatenate_cloud1, expected_pointcloud1, atol=1e-3), - "The concatenation node have wierd output", + "The concatenation node have weird output", ) expected_pointcloud2 = np.array( @@ -794,7 +794,7 @@ def test_7_abnormal_pointcloud_delay(self): self.assertTrue( np.allclose(concatenate_cloud2, expected_pointcloud2, atol=1e-3), - "The concatenation node have wierd output", + "The concatenation node have weird output", ) global_seconds += 1 @@ -814,7 +814,7 @@ def test_8_abnormal_pointcloud_drop_continue_normal(self): for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanosceonds + frame_idx * MILLISECONDS * 40 # add 40 ms + pointcloud_nanoseconds = global_nanoseconds + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -830,7 +830,7 @@ def test_8_abnormal_pointcloud_drop_continue_normal(self): time.sleep(TIMEOUT_SEC) rclpy.spin_once(self.node) - next_global_nanosecond = global_nanosceonds + 100 * MILLISECONDS + next_global_nanosecond = global_nanoseconds + 100 * MILLISECONDS for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): pointcloud_seconds = global_seconds pointcloud_nanoseconds = ( @@ -874,7 +874,7 @@ def test_8_abnormal_pointcloud_drop_continue_normal(self): self.assertTrue( np.allclose(concatenate_cloud1, expected_pointcloud1, atol=1e-3), - "The concatenation node have wierd output", + "The concatenation node have weird output", ) expected_pointcloud2 = np.array( @@ -898,7 +898,7 @@ def test_8_abnormal_pointcloud_drop_continue_normal(self): self.assertTrue( np.allclose(concatenate_cloud2, expected_pointcloud2, atol=1e-3), - "The concatenation node have wierd output", + "The concatenation node have weird output", ) global_seconds += 1 diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 95643ab9c720d..8124dd2bff3ee 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -320,7 +320,7 @@ TEST_F(ConcatenateCloudTest, TestConcatenateClouds) EXPECT_FLOAT_EQ( top_timestamp.seconds(), rclcpp::Time(concatenate_cloud_ptr->header.stamp).seconds()); - // test seperated transformed cloud + // test separated transformed cloud std::array expected_top_pointcloud = { {Eigen::Vector3f(10.0f, 0.0f, 0.0f), Eigen::Vector3f(0.0f, 10.0f, 0.0f), Eigen::Vector3f(0.0f, 0.0f, 10.0f)}}; From 2decb6503b9bd29b14978878a4c178a3e894bec2 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 23 Sep 2024 23:36:13 +0900 Subject: [PATCH 040/115] chore: fix more spell error Signed-off-by: vividf --- .../docs/concatenate-data.md | 2 +- .../test/test_concatenate_node_component.py | 6 +++--- .../test/test_concatenate_node_unit.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index a81dd3f91c6ef..d558fa4ba98d1 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -2,7 +2,7 @@ ## Purpose -The `concatenate_and_time_synchronize_node` is a ROS2 node that combines and synchronizes multiple point clouds into a single concatenated point cloud. This enhances the sensing range for autonomous driving vehicles by integrating data from multiple LiDARs. +The `concatenate_and_time_synchronize_node` is a ros2 node that combines and synchronizes multiple point clouds into a single concatenated point cloud. This enhances the sensing range for autonomous driving vehicles by integrating data from multiple LiDARs. ## Inner Workings / Algorithms diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index b0ecc7e961b7d..86b138d553af8 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -108,7 +108,7 @@ def generate_test_description(): ) container = ComposableNodeContainer( - name="test_concateante_data_container", + name="test_concatenate_data_container", namespace="pointcloud_preprocessor", package="rclcpp_components", executable="component_container", @@ -282,7 +282,7 @@ def generate_twist_msg() -> TwistWithCovarianceStamped: def get_output_points(cloud_msg) -> np.ndarray: points_list = [] - for point in point_cloud2.read_points(cloud_msg, field_names=("x", "y", "z"), skip_nans=True): + for point in point_cloud2.read_points(cloud_msg, field_names=("x", "y", "z")): points_list.append([point[0], point[1], point[2]]) points = np.array(points_list, dtype=np.float32) return points @@ -587,7 +587,7 @@ def test_4_abnormal_null_pointcloud_and_drop(self): global_seconds += 1 def test_5_abnormal_multiple_pointcloud_drop(self): - """Test the abnormal situation when multiple pointclouds were dropped (only one poincloud arrive). + """Test the abnormal situation when multiple pointclouds were dropped (only one pointcloud arrive). This can test that 1. The concatenate node concatenates the single pointcloud after the timeout. diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 8124dd2bff3ee..44e96e9b04295 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -38,7 +38,7 @@ class ConcatenateCloudTest : public ::testing::Test void SetUp() override { rclcpp::NodeOptions node_options; - // Instead of "input_topics", other parameters are not unsed. + // Instead of "input_topics", other parameters are not used. // They just helps to setup the concatenate node node_options.parameter_overrides( {{"has_static_tf_only", false}, From eeb310dcef5c7bfc8332e8f25af23e9bc5a5c003 Mon Sep 17 00:00:00 2001 From: vividf Date: Wed, 25 Sep 2024 09:25:50 +0900 Subject: [PATCH 041/115] chore: rename mutex and lock Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.hpp | 2 +- .../src/concatenate_data/concatenate_and_time_sync_node.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 055718f45829e..b7f9796cbbf1d 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -95,7 +95,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node std::shared_ptr combine_cloud_handler_; std::shared_ptr cloud_collector_; std::list> cloud_collectors_; - std::mutex mutex_; + std::mutex cloud_collectors_mutex_; std::unordered_map topic_to_offset_map_; std::unordered_map topic_to_noise_window_map_; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 75bb32781c694..32a5f7188416b 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -220,7 +220,7 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( } // protect cloud collectors list - std::unique_lock lock(mutex_); + std::unique_lock cloud_collectors_lock(cloud_collectors_mutex_); // For each callback, check whether there is a exist collector that matches this cloud bool collector_found = false; @@ -235,7 +235,7 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( reference_timestamp_max + topic_to_noise_window_map_[topic_name] && rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] > reference_timestamp_min - topic_to_noise_window_map_[topic_name]) { - lock.unlock(); + cloud_collectors_lock.unlock(); cloud_collector->processCloud(topic_name, input_ptr); collector_found = true; break; @@ -250,7 +250,7 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( cloud_collectors_, combine_cloud_handler_, params_.input_topics.size(), params_.timeout_sec); cloud_collectors_.push_back(new_cloud_collector); - lock.unlock(); + cloud_collectors_lock.unlock(); new_cloud_collector->setReferenceTimeStamp( rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name], topic_to_noise_window_map_[topic_name]); From 9a85a521956db4b6309bf25a5791c958bbb03639 Mon Sep 17 00:00:00 2001 From: vividf Date: Wed, 25 Sep 2024 09:33:32 +0900 Subject: [PATCH 042/115] chore: const reference for string parameter Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 2 +- .../src/concatenate_data/cloud_collector.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index ed1767e983414..1210f43048c5e 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -38,7 +38,7 @@ class CloudCollector void setReferenceTimeStamp(double timestamp, double noise_window); std::tuple getReferenceTimeStampBoundary(); - void processCloud(std::string topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud); + void processCloud(const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud); void concatenateCallback(); std::tuple< sensor_msgs::msg::PointCloud2::SharedPtr, diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 1887f3e07b4ad..d1dc88ad80a93 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -54,7 +54,7 @@ std::tuple CloudCollector::getReferenceTimeStampBoundary() } void CloudCollector::processCloud( - std::string topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud) + const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud) { // Check if the map already contains an entry for the same topic. This shouldn't happen if the // parameter 'lidar_timestamp_noise_window' is set correctly. From 111489858eef6d518a9ff78d3b75e47c34da8e4f Mon Sep 17 00:00:00 2001 From: vividf Date: Wed, 25 Sep 2024 17:41:28 +0900 Subject: [PATCH 043/115] chore: add explaination for RclcppTimeHash_ Signed-off-by: vividf --- .../concatenate_data/combine_cloud_handler.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index bcbd40bc6a2a3..68d3d97b26d25 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -59,6 +59,8 @@ class CombineCloudHandler bool keep_input_frame_in_synchronized_pointcloud_; std::unique_ptr managed_tf_buffer_{nullptr}; + /// @brief RclcppTimeHash_ structure defines a custom hash function for the rclcpp::Time type by + /// using its nanoseconds representation as the hash value. struct RclcppTimeHash_ { std::size_t operator()(const rclcpp::Time & t) const From 84b547c93c5dd9821352f1b28c5cbbaaae89d344 Mon Sep 17 00:00:00 2001 From: vividf Date: Wed, 25 Sep 2024 17:57:18 +0900 Subject: [PATCH 044/115] chore: change the concatenate node to parent node Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 4 ++-- .../src/concatenate_data/cloud_collector.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 1210f43048c5e..be18aa7052f45 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -32,7 +32,7 @@ class CloudCollector { public: CloudCollector( - std::shared_ptr concatenate_node, + std::shared_ptr ros2_parent_node, std::list> & collectors, std::shared_ptr combine_cloud_handler, int num_of_clouds, double time); @@ -58,7 +58,7 @@ class CloudCollector std::unordered_map getTopicToCloudMap(); private: - std::shared_ptr concatenate_node_; + std::shared_ptr ros2_parent_node_; std::list> & collectors_; std::shared_ptr combine_cloud_handler_; rclcpp::TimerBase::SharedPtr timer_; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index d1dc88ad80a93..53112842350ea 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -25,10 +25,10 @@ namespace autoware::pointcloud_preprocessor { CloudCollector::CloudCollector( - std::shared_ptr concatenate_node, + std::shared_ptr ros2_parent_node, std::list> & collectors, std::shared_ptr combine_cloud_handler, int num_of_clouds, double timeout_sec) -: concatenate_node_(concatenate_node), +: ros2_parent_node_(ros2_parent_node), collectors_(collectors), combine_cloud_handler_(combine_cloud_handler), num_of_clouds_(num_of_clouds), @@ -38,7 +38,7 @@ CloudCollector::CloudCollector( std::chrono::duration(timeout_sec_)); timer_ = rclcpp::create_timer( - concatenate_node_, concatenate_node_->get_clock(), period_ns, + ros2_parent_node_, ros2_parent_node_->get_clock(), period_ns, std::bind(&CloudCollector::concatenateCallback, this)); } @@ -60,7 +60,7 @@ void CloudCollector::processCloud( // parameter 'lidar_timestamp_noise_window' is set correctly. if (topic_to_cloud_map_.find(topic_name) != topic_to_cloud_map_.end()) { RCLCPP_WARN_STREAM_THROTTLE( - concatenate_node_->get_logger(), *concatenate_node_->get_clock(), + ros2_parent_node_->get_logger(), *ros2_parent_node_->get_clock(), std::chrono::milliseconds(10000).count(), "Topic '" << topic_name << "' already exists in the collector. Check the timestamp of the pointcloud."); @@ -101,7 +101,7 @@ void CloudCollector::publishClouds( topic_to_transformed_cloud_map, std::unordered_map topic_to_original_stamp_map) { - concatenate_node_->publishClouds( + ros2_parent_node_->publishClouds( concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map, reference_timestamp_min_, reference_timestamp_max_); } From e49d521da8ca0096b6243319fa16cd37f4f73012 Mon Sep 17 00:00:00 2001 From: vividf Date: Wed, 25 Sep 2024 18:51:59 +0900 Subject: [PATCH 045/115] chore: clean processOdometry and processTwist Signed-off-by: vividf --- .../concatenate_data/combine_cloud_handler.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 6db3dc12d3295..cf240a0660988 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -52,11 +52,11 @@ void CombineCloudHandler::processTwist( } } - // pop old data + // twist data that older than 1s will be cleared + auto cutoff_time = rclcpp::Time(input->header.stamp) - rclcpp::Duration::from_seconds(1.0); + while (!twist_ptr_queue_.empty()) { - if ( - rclcpp::Time(twist_ptr_queue_.front()->header.stamp) + rclcpp::Duration::from_seconds(1.0) > - rclcpp::Time(input->header.stamp)) { + if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > cutoff_time) { break; } twist_ptr_queue_.pop_front(); @@ -77,11 +77,11 @@ void CombineCloudHandler::processOdometry(const nav_msgs::msg::Odometry::ConstSh } } - // pop old data + // odometry data that older than 1s will be cleared + auto cutoff_time = rclcpp::Time(input->header.stamp) - rclcpp::Duration::from_seconds(1.0); + while (!twist_ptr_queue_.empty()) { - if ( - rclcpp::Time(twist_ptr_queue_.front()->header.stamp) + rclcpp::Duration::from_seconds(1.0) > - rclcpp::Time(input->header.stamp)) { + if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > cutoff_time) { break; } twist_ptr_queue_.pop_front(); From 5ae681c37aab4bcd12a74b4012c747280e95853c Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 10:56:08 +0900 Subject: [PATCH 046/115] chore: change twist shared pointer queue to twist queue Signed-off-by: vividf --- .../combine_cloud_handler.hpp | 6 +- .../combine_cloud_handler.cpp | 100 ++++++++++-------- .../test/test_concatenate_node_unit.cpp | 81 ++++++++++---- 3 files changed, 116 insertions(+), 71 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index 68d3d97b26d25..133f063f7370f 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -59,6 +59,8 @@ class CombineCloudHandler bool keep_input_frame_in_synchronized_pointcloud_; std::unique_ptr managed_tf_buffer_{nullptr}; + std::deque twist_queue_; + /// @brief RclcppTimeHash_ structure defines a custom hash function for the rclcpp::Time type by /// using its nanoseconds representation as the hash value. struct RclcppTimeHash_ @@ -70,8 +72,6 @@ class CombineCloudHandler }; public: - std::deque twist_ptr_queue_; - CombineCloudHandler( rclcpp::Node * node, std::vector input_topics, std::string output_frame, bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud, @@ -88,6 +88,8 @@ class CombineCloudHandler Eigen::Matrix4f computeTransformToAdjustForOldTimestamp( const rclcpp::Time & old_stamp, const rclcpp::Time & new_stamp); + + std::deque getTwistQueue(); }; } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index cf240a0660988..6f9ba805e5779 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -42,55 +42,65 @@ CombineCloudHandler::CombineCloudHandler( { } +// TODO(vivid): change this to process_twist_message void CombineCloudHandler::processTwist( - const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input) + const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & twist_msg) { + geometry_msgs::msg::TwistStamped msg; + msg.header = twist_msg->header; + msg.twist = twist_msg->twist.twist; + // If time jumps backwards (e.g. when a rosbag restarts), clear buffer - if (!twist_ptr_queue_.empty()) { - if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > rclcpp::Time(input->header.stamp)) { - twist_ptr_queue_.clear(); + if (!twist_queue_.empty()) { + if (rclcpp::Time(twist_queue_.front().header.stamp) > rclcpp::Time(msg.header.stamp)) { + twist_queue_.clear(); } } - // twist data that older than 1s will be cleared - auto cutoff_time = rclcpp::Time(input->header.stamp) - rclcpp::Duration::from_seconds(1.0); + // Twist data in the queue that is older than the current twist by 1 second will be cleared. + auto cutoff_time = rclcpp::Time(msg.header.stamp) - rclcpp::Duration::from_seconds(1.0); - while (!twist_ptr_queue_.empty()) { - if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > cutoff_time) { + while (!twist_queue_.empty()) { + if (rclcpp::Time(twist_queue_.front().header.stamp) > cutoff_time) { break; } - twist_ptr_queue_.pop_front(); + twist_queue_.pop_front(); } - auto twist_ptr = std::make_shared(); - twist_ptr->header = input->header; - twist_ptr->twist = input->twist.twist; - twist_ptr_queue_.push_back(twist_ptr); + twist_queue_.push_back(msg); } -void CombineCloudHandler::processOdometry(const nav_msgs::msg::Odometry::ConstSharedPtr & input) +// TODO(vivid): change this to process_odometry_message +void CombineCloudHandler::processOdometry( + const nav_msgs::msg::Odometry::ConstSharedPtr & odometry_msg) { - // if rosbag restart, clear buffer - if (!twist_ptr_queue_.empty()) { - if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > rclcpp::Time(input->header.stamp)) { - twist_ptr_queue_.clear(); + geometry_msgs::msg::TwistStamped msg; + msg.header = odometry_msg->header; + msg.twist = odometry_msg->twist.twist; + + // If time jumps backwards (e.g. when a rosbag restarts), clear buffer + if (!twist_queue_.empty()) { + if (rclcpp::Time(twist_queue_.front().header.stamp) > rclcpp::Time(msg.header.stamp)) { + twist_queue_.clear(); } } - // odometry data that older than 1s will be cleared - auto cutoff_time = rclcpp::Time(input->header.stamp) - rclcpp::Duration::from_seconds(1.0); + // Twist data in the queue that is older than the current twist by 1 second will be cleared. + auto cutoff_time = rclcpp::Time(msg.header.stamp) - rclcpp::Duration::from_seconds(1.0); - while (!twist_ptr_queue_.empty()) { - if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > cutoff_time) { + while (!twist_queue_.empty()) { + if (rclcpp::Time(twist_queue_.front().header.stamp) > cutoff_time) { break; } - twist_ptr_queue_.pop_front(); + twist_queue_.pop_front(); } - auto twist_ptr = std::make_shared(); - twist_ptr->header = input->header; - twist_ptr->twist = input->twist.twist; - twist_ptr_queue_.push_back(twist_ptr); + twist_queue_.push_back(msg); +} + +std::deque CombineCloudHandler::getTwistQueue() +{ + return twist_queue_; } std::tuple< @@ -184,7 +194,7 @@ Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( const rclcpp::Time & old_stamp, const rclcpp::Time & new_stamp) { // return identity if no twist is available - if (twist_ptr_queue_.empty()) { + if (twist_queue_.empty()) { RCLCPP_WARN_STREAM_THROTTLE( node_->get_logger(), *node_->get_clock(), std::chrono::milliseconds(10000).count(), "No twist is available. Please confirm twist topic and timestamp. Leaving point cloud " @@ -192,31 +202,29 @@ Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( return Eigen::Matrix4f::Identity(); } - auto old_twist_ptr_it = std::lower_bound( - std::begin(twist_ptr_queue_), std::end(twist_ptr_queue_), old_stamp, - [](const geometry_msgs::msg::TwistStamped::ConstSharedPtr & x_ptr, const rclcpp::Time & t) { - return rclcpp::Time(x_ptr->header.stamp) < t; + auto old_twist_it = std::lower_bound( + std::begin(twist_queue_), std::end(twist_queue_), old_stamp, + [](const geometry_msgs::msg::TwistStamped & x, const rclcpp::Time & t) { + return rclcpp::Time(x.header.stamp) < t; }); - old_twist_ptr_it = - old_twist_ptr_it == twist_ptr_queue_.end() ? (twist_ptr_queue_.end() - 1) : old_twist_ptr_it; + old_twist_it = old_twist_it == twist_queue_.end() ? (twist_queue_.end() - 1) : old_twist_it; - auto new_twist_ptr_it = std::lower_bound( - std::begin(twist_ptr_queue_), std::end(twist_ptr_queue_), new_stamp, - [](const geometry_msgs::msg::TwistStamped::ConstSharedPtr & x_ptr, const rclcpp::Time & t) { - return rclcpp::Time(x_ptr->header.stamp) < t; + auto new_twist_it = std::lower_bound( + std::begin(twist_queue_), std::end(twist_queue_), new_stamp, + [](const geometry_msgs::msg::TwistStamped & x, const rclcpp::Time & t) { + return rclcpp::Time(x.header.stamp) < t; }); - new_twist_ptr_it = - new_twist_ptr_it == twist_ptr_queue_.end() ? (twist_ptr_queue_.end() - 1) : new_twist_ptr_it; + new_twist_it = new_twist_it == twist_queue_.end() ? (twist_queue_.end() - 1) : new_twist_it; auto prev_time = old_stamp; double x = 0.0; double y = 0.0; double yaw = 0.0; tf2::Quaternion baselink_quat{}; - for (auto twist_ptr_it = old_twist_ptr_it; twist_ptr_it != new_twist_ptr_it + 1; ++twist_ptr_it) { + for (auto twist_it = old_twist_it; twist_it != new_twist_it + 1; ++twist_it) { const double dt = - (twist_ptr_it != new_twist_ptr_it) - ? (rclcpp::Time((*twist_ptr_it)->header.stamp) - rclcpp::Time(prev_time)).seconds() + (twist_it != new_twist_it) + ? (rclcpp::Time((*twist_it).header.stamp) - rclcpp::Time(prev_time)).seconds() : (rclcpp::Time(new_stamp) - rclcpp::Time(prev_time)).seconds(); if (std::fabs(dt) > 0.1) { @@ -227,11 +235,11 @@ Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( break; } - const double dis = (*twist_ptr_it)->twist.linear.x * dt; - yaw += (*twist_ptr_it)->twist.angular.z * dt; + const double dis = (*twist_it).twist.linear.x * dt; + yaw += (*twist_it).twist.angular.z * dt; x += dis * std::cos(yaw); y += dis * std::sin(yaw); - prev_time = (*twist_ptr_it)->header.stamp; + prev_time = (*twist_it).header.stamp; } Eigen::Matrix4f transformation_matrix = Eigen::Matrix4f::Identity(); diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 44e96e9b04295..95ec3133cbc25 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -190,9 +190,9 @@ TEST_F(ConcatenateCloudTest, TestProcessTwist) combine_cloud_handler_->processTwist(twist_msg); - ASSERT_FALSE(combine_cloud_handler_->twist_ptr_queue_.empty()); - EXPECT_EQ(combine_cloud_handler_->twist_ptr_queue_.front()->twist.linear.x, 1.0); - EXPECT_EQ(combine_cloud_handler_->twist_ptr_queue_.front()->twist.angular.z, 0.1); + ASSERT_FALSE(combine_cloud_handler_->getTwistQueue().empty()); + EXPECT_EQ(combine_cloud_handler_->getTwistQueue().front().twist.linear.x, 1.0); + EXPECT_EQ(combine_cloud_handler_->getTwistQueue().front().twist.angular.z, 0.1); } TEST_F(ConcatenateCloudTest, TestProcessOdometry) @@ -204,31 +204,65 @@ TEST_F(ConcatenateCloudTest, TestProcessOdometry) combine_cloud_handler_->processOdometry(odom_msg); - ASSERT_FALSE(combine_cloud_handler_->twist_ptr_queue_.empty()); - EXPECT_EQ(combine_cloud_handler_->twist_ptr_queue_.front()->twist.linear.x, 1.0); - EXPECT_EQ(combine_cloud_handler_->twist_ptr_queue_.front()->twist.angular.z, 0.1); + ASSERT_FALSE(combine_cloud_handler_->getTwistQueue().empty()); + EXPECT_EQ(combine_cloud_handler_->getTwistQueue().front().twist.linear.x, 1.0); + EXPECT_EQ(combine_cloud_handler_->getTwistQueue().front().twist.angular.z, 0.1); } TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) { - rclcpp::Time old_stamp(10, 100'000'000, RCL_ROS_TIME); - rclcpp::Time new_stamp(10, 150'000'000, RCL_ROS_TIME); + // If time difference between twist msg and pointcloud stamp is more than 100 miliseconds, return + // Identity transformation. case 1: time differecne larger than 100 miliseconds + rclcpp::Time pointcloud_stamp1(10, 100'000'000, RCL_ROS_TIME); + rclcpp::Time pointcloud_stamp2(10, 210'000'000, RCL_ROS_TIME); + auto twist_msg1 = std::make_shared(); + twist_msg1->header.stamp = rclcpp::Time(9, 130'000'000, RCL_ROS_TIME); + twist_msg1->twist.twist.linear.x = 1.0; + twist_msg1->twist.twist.angular.z = 0.1; + combine_cloud_handler_->processTwist(twist_msg1); + + auto twist_msg2 = std::make_shared(); + twist_msg2->header.stamp = rclcpp::Time(9, 160'000'000, RCL_ROS_TIME); + twist_msg2->twist.twist.linear.x = 1.0; + twist_msg2->twist.twist.angular.z = 0.1; + combine_cloud_handler_->processTwist(twist_msg2); + + Eigen::Matrix4f transform = combine_cloud_handler_->computeTransformToAdjustForOldTimestamp( + pointcloud_stamp1, pointcloud_stamp2); - // Time difference between twist msg is more than 100 miliseconds, won't calculate the difference - auto twist_msg1 = std::make_shared(); - twist_msg1->header.stamp = rclcpp::Time(10, 130'000'000, RCL_ROS_TIME); - twist_msg1->twist.linear.x = 1.0; - twist_msg1->twist.angular.z = 0.1; - combine_cloud_handler_->twist_ptr_queue_.push_back(twist_msg1); + // translation + EXPECT_NEAR(transform(0, 3), 0.0, standard_tolerance); + EXPECT_NEAR(transform(1, 3), 0.0, standard_tolerance); + + EXPECT_NEAR(transform(0, 0), 1.0, standard_tolerance); + EXPECT_NEAR(transform(0, 1), 0.0, standard_tolerance); + EXPECT_NEAR(transform(1, 0), 0.0, standard_tolerance); + EXPECT_NEAR(transform(1, 1), 1.0, standard_tolerance); + + std::ostringstream oss; + oss << "Transformation matrix from cloud 2 to cloud 1:\n" << transform; + + if (debug_) { + RCLCPP_INFO(concatenate_node_->get_logger(), "%s", oss.str().c_str()); + } + + // case 2: time difference smaller than 100 miliseconds + rclcpp::Time pointcloud_stamp3(11, 100'000'000, RCL_ROS_TIME); + rclcpp::Time pointcloud_stamp4(11, 150'000'000, RCL_ROS_TIME); + auto twist_msg3 = std::make_shared(); + twist_msg3->header.stamp = rclcpp::Time(11, 130'000'000, RCL_ROS_TIME); + twist_msg3->twist.twist.linear.x = 1.0; + twist_msg3->twist.twist.angular.z = 0.1; + combine_cloud_handler_->processTwist(twist_msg3); - auto twist_msg2 = std::make_shared(); - twist_msg2->header.stamp = rclcpp::Time(10, 160'000'000, RCL_ROS_TIME); - twist_msg2->twist.linear.x = 1.0; - twist_msg2->twist.angular.z = 0.1; - combine_cloud_handler_->twist_ptr_queue_.push_back(twist_msg2); + auto twist_msg4 = std::make_shared(); + twist_msg4->header.stamp = rclcpp::Time(11, 160'000'000, RCL_ROS_TIME); + twist_msg4->twist.twist.linear.x = 1.0; + twist_msg4->twist.twist.angular.z = 0.1; + combine_cloud_handler_->processTwist(twist_msg4); - Eigen::Matrix4f transform = - combine_cloud_handler_->computeTransformToAdjustForOldTimestamp(old_stamp, new_stamp); + transform = combine_cloud_handler_->computeTransformToAdjustForOldTimestamp( + pointcloud_stamp3, pointcloud_stamp4); // translation EXPECT_NEAR(transform(0, 3), 0.0499996, standard_tolerance); @@ -240,8 +274,9 @@ TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) EXPECT_NEAR(transform(1, 0), 0.00499998, standard_tolerance); EXPECT_NEAR(transform(1, 1), 0.999987, standard_tolerance); - std::ostringstream oss; - oss << "Transformation matrix:\n" << transform; + oss.str(""); + oss.clear(); + oss << "Transformation matrix from cloud 4 to cloud 3:\n" << transform; if (debug_) { RCLCPP_INFO(concatenate_node_->get_logger(), "%s", oss.str().c_str()); From 3a8ff07acff3a6d5b747830b518569f409c10686 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 13:33:36 +0900 Subject: [PATCH 047/115] chore: refactor compensate pointcloud to function Signed-off-by: vividf --- .../combine_cloud_handler.hpp | 6 +++ .../combine_cloud_handler.cpp | 53 +++++++++++-------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index 133f063f7370f..d8af18a27b6b5 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -71,6 +71,12 @@ class CombineCloudHandler } }; + void correctPointCloudMotion( + const std::shared_ptr & transformed_cloud_ptr, + const std::vector & pc_stamps, + std::unordered_map & transform_memo, + std::shared_ptr transformed_delay_compensated_cloud_ptr); + public: CombineCloudHandler( rclcpp::Node * node, std::vector input_topics, std::string output_frame, diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 6f9ba805e5779..dd5f8f00bc459 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -103,6 +103,31 @@ std::deque CombineCloudHandler::getTwistQueue( return twist_queue_; } +void CombineCloudHandler::correctPointCloudMotion( + const std::shared_ptr & transformed_cloud_ptr, + const std::vector & pc_stamps, + std::unordered_map & transform_memo, + std::shared_ptr transformed_delay_compensated_cloud_ptr) +{ + Eigen::Matrix4f adjust_to_old_data_transform = Eigen::Matrix4f::Identity(); + rclcpp::Time current_cloud_stamp = rclcpp::Time(transformed_cloud_ptr->header.stamp); + for (const auto & stamp : pc_stamps) { + if (stamp >= current_cloud_stamp) continue; + + Eigen::Matrix4f new_to_old_transform; + if (transform_memo.find(stamp) != transform_memo.end()) { + new_to_old_transform = transform_memo[stamp]; + } else { + new_to_old_transform = computeTransformToAdjustForOldTimestamp(stamp, current_cloud_stamp); + transform_memo[stamp] = new_to_old_transform; + } + adjust_to_old_data_transform = new_to_old_transform * adjust_to_old_data_transform; + current_cloud_stamp = stamp; + } + pcl_ros::transformPointCloud( + adjust_to_old_data_transform, *transformed_cloud_ptr, *transformed_delay_compensated_cloud_ptr); +} + std::tuple< sensor_msgs::msg::PointCloud2::SharedPtr, std::unordered_map, @@ -130,30 +155,12 @@ CombineCloudHandler::combinePointClouds( topic_to_original_stamp_map[topic] = rclcpp::Time(cloud->header.stamp).seconds(); - auto transformed_delay_compensated_cloud_ptr = - std::make_shared(); - + // compensate pointcloud + std::shared_ptr transformed_delay_compensated_cloud_ptr; if (is_motion_compensated_) { - Eigen::Matrix4f adjust_to_old_data_transform = Eigen::Matrix4f::Identity(); - rclcpp::Time current_cloud_stamp = rclcpp::Time(cloud->header.stamp); - for (const auto & stamp : pc_stamps) { - if (stamp >= current_cloud_stamp) continue; - - Eigen::Matrix4f new_to_old_transform; - if (transform_memo.find(stamp) != transform_memo.end()) { - new_to_old_transform = transform_memo[stamp]; - } else { - new_to_old_transform = - computeTransformToAdjustForOldTimestamp(stamp, current_cloud_stamp); - transform_memo[stamp] = new_to_old_transform; - } - adjust_to_old_data_transform = new_to_old_transform * adjust_to_old_data_transform; - current_cloud_stamp = stamp; - } - pcl_ros::transformPointCloud( - adjust_to_old_data_transform, *transformed_cloud_ptr, - *transformed_delay_compensated_cloud_ptr); - + transformed_delay_compensated_cloud_ptr = std::make_shared(); + correctPointCloudMotion( + transformed_cloud_ptr, pc_stamps, transform_memo, transformed_delay_compensated_cloud_ptr); } else { transformed_delay_compensated_cloud_ptr = transformed_cloud_ptr; } From 49b54d4d49865b7745791d4204f2a1593c1122a2 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 13:49:28 +0900 Subject: [PATCH 048/115] chore: reallocate memory for concatenate_cloud_ptr Signed-off-by: vividf --- .../combine_cloud_handler.cpp | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index dd5f8f00bc459..1d49d6c9cc781 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -165,15 +165,36 @@ CombineCloudHandler::combinePointClouds( transformed_delay_compensated_cloud_ptr = transformed_cloud_ptr; } - // concatenate + // Check if concatenate_cloud_ptr is nullptr, if so initialize it if (concatenate_cloud_ptr == nullptr) { - concatenate_cloud_ptr = - std::make_shared(*transformed_delay_compensated_cloud_ptr); - } else { - pcl::concatenatePointCloud( - *concatenate_cloud_ptr, *transformed_delay_compensated_cloud_ptr, *concatenate_cloud_ptr); + // Initialize concatenate_cloud_ptr without copying the data + concatenate_cloud_ptr = std::make_shared(); + concatenate_cloud_ptr->header = transformed_delay_compensated_cloud_ptr->header; + concatenate_cloud_ptr->height = transformed_delay_compensated_cloud_ptr->height; + concatenate_cloud_ptr->width = 0; // Will be updated with total points + concatenate_cloud_ptr->is_dense = transformed_delay_compensated_cloud_ptr->is_dense; + concatenate_cloud_ptr->is_bigendian = transformed_delay_compensated_cloud_ptr->is_bigendian; + concatenate_cloud_ptr->fields = transformed_delay_compensated_cloud_ptr->fields; + concatenate_cloud_ptr->point_step = transformed_delay_compensated_cloud_ptr->point_step; + concatenate_cloud_ptr->row_step = 0; // Will be updated after concatenation + concatenate_cloud_ptr->data.clear(); + + // Reserve space for the data (assume max points you expect to concatenate) + auto num_of_points = transformed_delay_compensated_cloud_ptr->width * + transformed_delay_compensated_cloud_ptr->height; + concatenate_cloud_ptr->data.reserve(num_of_points * concatenate_cloud_ptr->point_step); } + // Concatenate the current pointcloud to the concatenated cloud + pcl::concatenatePointCloud( + *concatenate_cloud_ptr, *transformed_delay_compensated_cloud_ptr, *concatenate_cloud_ptr); + + // Update width and row_step to reflect the new size + concatenate_cloud_ptr->width = + concatenate_cloud_ptr->data.size() / concatenate_cloud_ptr->point_step; + concatenate_cloud_ptr->row_step = + concatenate_cloud_ptr->width * concatenate_cloud_ptr->point_step; + // convert to original sensor frame if necessary bool need_transform_to_sensor_frame = (cloud->header.frame_id != output_frame_); if (keep_input_frame_in_synchronized_pointcloud_ && need_transform_to_sensor_frame) { From de94fa6cf2ff7ae9b2a00bf705271fba7c7bc30a Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 13:51:45 +0900 Subject: [PATCH 049/115] chore: remove new to make shared Signed-off-by: vividf --- .../src/concatenate_data/combine_cloud_handler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 1d49d6c9cc781..dd0184ef2188d 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -198,8 +198,8 @@ CombineCloudHandler::combinePointClouds( // convert to original sensor frame if necessary bool need_transform_to_sensor_frame = (cloud->header.frame_id != output_frame_); if (keep_input_frame_in_synchronized_pointcloud_ && need_transform_to_sensor_frame) { - sensor_msgs::msg::PointCloud2::SharedPtr transformed_cloud_ptr_in_sensor_frame( - new sensor_msgs::msg::PointCloud2()); + auto transformed_cloud_ptr_in_sensor_frame = + std::make_shared(); managed_tf_buffer_->transformPointcloud( cloud->header.frame_id, *transformed_delay_compensated_cloud_ptr, *transformed_cloud_ptr_in_sensor_frame); From 79791530af64f437b89324fb5ce40377c96f1f1b Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 14:08:24 +0900 Subject: [PATCH 050/115] chore: dis to distance Signed-off-by: vividf --- .../src/concatenate_data/combine_cloud_handler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index dd0184ef2188d..7f6d01cc9da8b 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -263,10 +263,10 @@ Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( break; } - const double dis = (*twist_it).twist.linear.x * dt; + const double distance = (*twist_it).twist.linear.x * dt; yaw += (*twist_it).twist.angular.z * dt; - x += dis * std::cos(yaw); - y += dis * std::sin(yaw); + x += distance * std::cos(yaw); + y += distance * std::sin(yaw); prev_time = (*twist_it).header.stamp; } From b344427281484082c80fd8c5b3993754652dd91d Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 14:23:42 +0900 Subject: [PATCH 051/115] chore: refacotr poitncloud_sub Signed-off-by: vividf --- .../concatenate_and_time_sync_node.hpp | 2 +- .../concatenate_and_time_sync_node.cpp | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index b7f9796cbbf1d..f93266d4153a9 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -100,7 +100,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node std::unordered_map topic_to_noise_window_map_; // subscribers - std::vector::SharedPtr> pointcloud_subs; + std::vector::SharedPtr> pointcloud_subs_; rclcpp::Subscription::SharedPtr twist_sub_; rclcpp::Subscription::SharedPtr odom_sub_; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 32a5f7188416b..cf5c248f1c4e4 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -132,16 +132,14 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro } } - pointcloud_subs.resize(params_.input_topics.size()); - for (size_t topic_id = 0; topic_id < params_.input_topics.size(); ++topic_id) { + for (const std::string & topic : params_.input_topics) { std::function callback = std::bind( &PointCloudConcatenateDataSynchronizerComponent::cloud_callback, this, std::placeholders::_1, - params_.input_topics[topic_id]); + topic); - pointcloud_subs[topic_id].reset(); - pointcloud_subs[topic_id] = this->create_subscription( - params_.input_topics[topic_id], rclcpp::SensorDataQoS().keep_last(params_.maximum_queue_size), - callback); + auto pointcloud_sub = this->create_subscription( + topic, rclcpp::SensorDataQoS().keep_last(params_.maximum_queue_size), callback); + pointcloud_subs_.push_back(pointcloud_sub); } RCLCPP_DEBUG_STREAM( get_logger(), From b0c8a7c479b189cd3ab0772598bb0bac9f75ec06 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 14:34:08 +0900 Subject: [PATCH 052/115] chore: return early return but throw runtime error Signed-off-by: vividf --- .../concatenate_and_time_sync_node.cpp | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index cf5c248f1c4e4..215d5e4d163b5 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -46,6 +46,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro stop_watch_ptr_->tic("cyclic_time"); stop_watch_ptr_->tic("processing_time"); + bool parameters_valid = true; // initialize parameters params_.has_static_tf_only = declare_parameter("has_static_tf_only"); params_.maximum_queue_size = declare_parameter("maximum_queue_size"); @@ -69,25 +70,25 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro if (params_.input_topics.empty()) { RCLCPP_ERROR(get_logger(), "Need a 'input_topics' parameter to be set before continuing!"); - return; + parameters_valid = false; } else if (params_.input_topics.size() == 1) { RCLCPP_ERROR(get_logger(), "Only one topic given. Need at least two topics to continue."); - return; + parameters_valid = false; } if (params_.output_frame.empty()) { RCLCPP_ERROR(get_logger(), "Need an 'output_frame' parameter to be set before continuing!"); - return; + parameters_valid = false; } if (params_.lidar_timestamp_offsets.size() != params_.input_topics.size()) { RCLCPP_ERROR( get_logger(), "The number of topics does not match the number of timestamp offsets"); - return; + parameters_valid = false; } if (params_.lidar_timestamp_noise_window.size() != params_.input_topics.size()) { RCLCPP_ERROR( get_logger(), "The number of topics does not match the number of timestamp noise window"); - return; + parameters_valid = false; } for (size_t i = 0; i < params_.input_topics.size(); i++) { @@ -127,11 +128,16 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro } else { RCLCPP_ERROR_STREAM( get_logger(), "input_twist_topic_type is invalid: " << params_.input_twist_topic_type); - throw std::runtime_error( - "input_twist_topic_type is invalid: " + params_.input_twist_topic_type); + parameters_valid = false; } } + if (!parameters_valid) { + throw std::runtime_error( + "Invalid parameter setting detected. Please review the provided parameter values and refer " + "to the error logs for detailed information."); + } + for (const std::string & topic : params_.input_topics) { std::function callback = std::bind( &PointCloudConcatenateDataSynchronizerComponent::cloud_callback, this, std::placeholders::_1, From fa0c4dc5534cde4c586aa8fe5996113919821501 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 15:11:23 +0900 Subject: [PATCH 053/115] chore: replace #define DEFAULT_SYNC_TOPIC_POSTFIX with member variable Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.hpp | 3 +++ .../src/concatenate_data/concatenate_and_time_sync_node.cpp | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index f93266d4153a9..bd835504591f9 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -99,6 +99,9 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node std::unordered_map topic_to_offset_map_; std::unordered_map topic_to_noise_window_map_; + // default postfix name for synchronized pointcloud + static constexpr const char * default_sync_topic_postfix_ = "_synchronized"; + // subscribers std::vector::SharedPtr> pointcloud_subs_; rclcpp::Subscription::SharedPtr twist_sub_; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 215d5e4d163b5..8f141cfe16b36 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -28,9 +28,6 @@ #include #include -#define DEFAULT_SYNC_TOPIC_POSTFIX \ - "_synchronized" // default postfix name for synchronized pointcloud - namespace autoware::pointcloud_preprocessor { @@ -190,7 +187,7 @@ std::string PointCloudConcatenateDataSynchronizerComponent::replaceSyncTopicName << " have the same postfix with synchronized pointcloud. We use " "the postfix " "to the end of the topic name."); - replaced_topic_name = original_topic_name + DEFAULT_SYNC_TOPIC_POSTFIX; + replaced_topic_name = original_topic_name + default_sync_topic_postfix_; } return replaced_topic_name; } From 66a62d183e0586d2c2350bd9316f92dda6c9eb03 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 15:14:24 +0900 Subject: [PATCH 054/115] chore: fix spell error Signed-off-by: vividf --- .../test/test_concatenate_node_unit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 95ec3133cbc25..aca5156689236 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -212,7 +212,7 @@ TEST_F(ConcatenateCloudTest, TestProcessOdometry) TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) { // If time difference between twist msg and pointcloud stamp is more than 100 miliseconds, return - // Identity transformation. case 1: time differecne larger than 100 miliseconds + // Identity transformation. case 1: time difference larger than 100 miliseconds rclcpp::Time pointcloud_stamp1(10, 100'000'000, RCL_ROS_TIME); rclcpp::Time pointcloud_stamp2(10, 210'000'000, RCL_ROS_TIME); auto twist_msg1 = std::make_shared(); From 67bfa264801ad357304985cf49a87c0e974e9289 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 15:20:03 +0900 Subject: [PATCH 055/115] chore: remove redundant function call Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 7 ------- .../src/concatenate_data/cloud_collector.cpp | 15 +++------------ 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index be18aa7052f45..82f71ccb0e9cf 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -46,13 +46,6 @@ class CloudCollector std::unordered_map> concatenateClouds( std::unordered_map topic_to_cloud_map); - - void publishClouds( - sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr, - std::unordered_map - topic_to_transformed_cloud_map, - std::unordered_map topic_to_original_stamp_map); - void deleteCollector(); std::unordered_map getTopicToCloudMap(); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 53112842350ea..7eacc0bcebc36 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -81,7 +81,9 @@ void CloudCollector::concatenateCallback() std::lock_guard lock(mutex_); auto [concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map] = concatenateClouds(topic_to_cloud_map_); - publishClouds(concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map); + ros2_parent_node_->publishClouds( + concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map, + reference_timestamp_min_, reference_timestamp_max_); deleteCollector(); } @@ -95,17 +97,6 @@ CloudCollector::concatenateClouds( return combine_cloud_handler_->combinePointClouds(topic_to_cloud_map); } -void CloudCollector::publishClouds( - sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr, - std::unordered_map - topic_to_transformed_cloud_map, - std::unordered_map topic_to_original_stamp_map) -{ - ros2_parent_node_->publishClouds( - concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map, - reference_timestamp_min_, reference_timestamp_max_); -} - void CloudCollector::deleteCollector() { auto it = std::find_if( From bafaea1f1154747960e4751678a3c9b4c1f0a903 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 16:23:47 +0900 Subject: [PATCH 056/115] chore: replace conplex tuple to structure Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 7 +- .../combine_cloud_handler.hpp | 22 +++--- .../concatenate_and_time_sync_node.hpp | 7 +- .../src/concatenate_data/cloud_collector.cpp | 12 +-- .../combine_cloud_handler.cpp | 76 ++++++++++--------- .../concatenate_and_time_sync_node.cpp | 28 +++---- 6 files changed, 76 insertions(+), 76 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 82f71ccb0e9cf..759318f0a8fcb 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -40,11 +40,8 @@ class CloudCollector std::tuple getReferenceTimeStampBoundary(); void processCloud(const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud); void concatenateCallback(); - std::tuple< - sensor_msgs::msg::PointCloud2::SharedPtr, - std::unordered_map, - std::unordered_map> - concatenateClouds( + + ConcatenatedCloudResult concatenateClouds( std::unordered_map topic_to_cloud_map); void deleteCollector(); diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index d8af18a27b6b5..60fbeb4ba6c83 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -48,6 +48,14 @@ namespace autoware::pointcloud_preprocessor { +struct ConcatenatedCloudResult +{ + sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr{nullptr}; + std::unordered_map + topic_to_transformed_cloud_map; + std::unordered_map topic_to_original_stamp_map; +}; + class CombineCloudHandler { private: @@ -61,9 +69,9 @@ class CombineCloudHandler std::deque twist_queue_; - /// @brief RclcppTimeHash_ structure defines a custom hash function for the rclcpp::Time type by + /// @brief RclcppTimeHash structure defines a custom hash function for the rclcpp::Time type by /// using its nanoseconds representation as the hash value. - struct RclcppTimeHash_ + struct RclcppTimeHash { std::size_t operator()(const rclcpp::Time & t) const { @@ -74,7 +82,7 @@ class CombineCloudHandler void correctPointCloudMotion( const std::shared_ptr & transformed_cloud_ptr, const std::vector & pc_stamps, - std::unordered_map & transform_memo, + std::unordered_map & transform_memo, std::shared_ptr transformed_delay_compensated_cloud_ptr); public: @@ -85,12 +93,8 @@ class CombineCloudHandler void processTwist(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input); void processOdometry(const nav_msgs::msg::Odometry::ConstSharedPtr & input); - std::tuple< - sensor_msgs::msg::PointCloud2::SharedPtr, - std::unordered_map, - std::unordered_map> - combinePointClouds(std::unordered_map & - topic_to_cloud_map_); + ConcatenatedCloudResult combinePointClouds( + std::unordered_map & topic_to_cloud_map); Eigen::Matrix4f computeTransformToAdjustForOldTimestamp( const rclcpp::Time & old_stamp, const rclcpp::Time & new_stamp); diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index bd835504591f9..2954315eebdf6 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -60,11 +60,8 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node explicit PointCloudConcatenateDataSynchronizerComponent(const rclcpp::NodeOptions & node_options); virtual ~PointCloudConcatenateDataSynchronizerComponent() {} void publishClouds( - sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr, - std::unordered_map & - topic_to_transformed_cloud_map, - std::unordered_map & topic_to_original_stamp_map, - double reference_timestamp_min, double reference_timestamp_max); + ConcatenatedCloudResult concatenated_cloud_result, double reference_timestamp_min, + double reference_timestamp_max); private: struct Parameters diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 7eacc0bcebc36..ef3311c6f118a 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -79,19 +79,13 @@ void CloudCollector::concatenateCallback() // lock for protecting collector list and concatenated pointcloud std::lock_guard lock(mutex_); - auto [concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map] = - concatenateClouds(topic_to_cloud_map_); + auto concatenated_cloud_result = concatenateClouds(topic_to_cloud_map_); ros2_parent_node_->publishClouds( - concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map, - reference_timestamp_min_, reference_timestamp_max_); + concatenated_cloud_result, reference_timestamp_min_, reference_timestamp_max_); deleteCollector(); } -std::tuple< - sensor_msgs::msg::PointCloud2::SharedPtr, - std::unordered_map, - std::unordered_map> -CloudCollector::concatenateClouds( +ConcatenatedCloudResult CloudCollector::concatenateClouds( std::unordered_map topic_to_cloud_map) { return combine_cloud_handler_->combinePointClouds(topic_to_cloud_map); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 7f6d01cc9da8b..3bf4668051858 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -106,7 +106,7 @@ std::deque CombineCloudHandler::getTwistQueue( void CombineCloudHandler::correctPointCloudMotion( const std::shared_ptr & transformed_cloud_ptr, const std::vector & pc_stamps, - std::unordered_map & transform_memo, + std::unordered_map & transform_memo, std::shared_ptr transformed_delay_compensated_cloud_ptr) { Eigen::Matrix4f adjust_to_old_data_transform = Eigen::Matrix4f::Identity(); @@ -128,17 +128,10 @@ void CombineCloudHandler::correctPointCloudMotion( adjust_to_old_data_transform, *transformed_cloud_ptr, *transformed_delay_compensated_cloud_ptr); } -std::tuple< - sensor_msgs::msg::PointCloud2::SharedPtr, - std::unordered_map, - std::unordered_map> -CombineCloudHandler::combinePointClouds( +ConcatenatedCloudResult CombineCloudHandler::combinePointClouds( std::unordered_map & topic_to_cloud_map) { - sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr = nullptr; - std::unordered_map - topic_to_transformed_cloud_map; - std::unordered_map topic_to_original_stamp_map; + ConcatenatedCloudResult concatenate_cloud_result; std::vector pc_stamps; for (const auto & [topic, cloud] : topic_to_cloud_map) { @@ -147,13 +140,14 @@ CombineCloudHandler::combinePointClouds( std::sort(pc_stamps.begin(), pc_stamps.end(), std::greater()); const auto oldest_stamp = pc_stamps.back(); - std::unordered_map transform_memo; + std::unordered_map transform_memo; for (const auto & [topic, cloud] : topic_to_cloud_map) { auto transformed_cloud_ptr = std::make_shared(); managed_tf_buffer_->transformPointcloud(output_frame_, *cloud, *transformed_cloud_ptr); - topic_to_original_stamp_map[topic] = rclcpp::Time(cloud->header.stamp).seconds(); + concatenate_cloud_result.topic_to_original_stamp_map[topic] = + rclcpp::Time(cloud->header.stamp).seconds(); // compensate pointcloud std::shared_ptr transformed_delay_compensated_cloud_ptr; @@ -166,34 +160,47 @@ CombineCloudHandler::combinePointClouds( } // Check if concatenate_cloud_ptr is nullptr, if so initialize it - if (concatenate_cloud_ptr == nullptr) { + if (concatenate_cloud_result.concatenate_cloud_ptr == nullptr) { // Initialize concatenate_cloud_ptr without copying the data - concatenate_cloud_ptr = std::make_shared(); - concatenate_cloud_ptr->header = transformed_delay_compensated_cloud_ptr->header; - concatenate_cloud_ptr->height = transformed_delay_compensated_cloud_ptr->height; - concatenate_cloud_ptr->width = 0; // Will be updated with total points - concatenate_cloud_ptr->is_dense = transformed_delay_compensated_cloud_ptr->is_dense; - concatenate_cloud_ptr->is_bigendian = transformed_delay_compensated_cloud_ptr->is_bigendian; - concatenate_cloud_ptr->fields = transformed_delay_compensated_cloud_ptr->fields; - concatenate_cloud_ptr->point_step = transformed_delay_compensated_cloud_ptr->point_step; - concatenate_cloud_ptr->row_step = 0; // Will be updated after concatenation - concatenate_cloud_ptr->data.clear(); + concatenate_cloud_result.concatenate_cloud_ptr = + std::make_shared(); + concatenate_cloud_result.concatenate_cloud_ptr->header = + transformed_delay_compensated_cloud_ptr->header; + concatenate_cloud_result.concatenate_cloud_ptr->height = + transformed_delay_compensated_cloud_ptr->height; + concatenate_cloud_result.concatenate_cloud_ptr->width = + 0; // Will be updated with total points + concatenate_cloud_result.concatenate_cloud_ptr->is_dense = + transformed_delay_compensated_cloud_ptr->is_dense; + concatenate_cloud_result.concatenate_cloud_ptr->is_bigendian = + transformed_delay_compensated_cloud_ptr->is_bigendian; + concatenate_cloud_result.concatenate_cloud_ptr->fields = + transformed_delay_compensated_cloud_ptr->fields; + concatenate_cloud_result.concatenate_cloud_ptr->point_step = + transformed_delay_compensated_cloud_ptr->point_step; + concatenate_cloud_result.concatenate_cloud_ptr->row_step = + 0; // Will be updated after concatenation + concatenate_cloud_result.concatenate_cloud_ptr->data.clear(); // Reserve space for the data (assume max points you expect to concatenate) auto num_of_points = transformed_delay_compensated_cloud_ptr->width * transformed_delay_compensated_cloud_ptr->height; - concatenate_cloud_ptr->data.reserve(num_of_points * concatenate_cloud_ptr->point_step); + concatenate_cloud_result.concatenate_cloud_ptr->data.reserve( + num_of_points * concatenate_cloud_result.concatenate_cloud_ptr->point_step); } // Concatenate the current pointcloud to the concatenated cloud pcl::concatenatePointCloud( - *concatenate_cloud_ptr, *transformed_delay_compensated_cloud_ptr, *concatenate_cloud_ptr); + *concatenate_cloud_result.concatenate_cloud_ptr, *transformed_delay_compensated_cloud_ptr, + *concatenate_cloud_result.concatenate_cloud_ptr); // Update width and row_step to reflect the new size - concatenate_cloud_ptr->width = - concatenate_cloud_ptr->data.size() / concatenate_cloud_ptr->point_step; - concatenate_cloud_ptr->row_step = - concatenate_cloud_ptr->width * concatenate_cloud_ptr->point_step; + concatenate_cloud_result.concatenate_cloud_ptr->width = + concatenate_cloud_result.concatenate_cloud_ptr->data.size() / + concatenate_cloud_result.concatenate_cloud_ptr->point_step; + concatenate_cloud_result.concatenate_cloud_ptr->row_step = + concatenate_cloud_result.concatenate_cloud_ptr->width * + concatenate_cloud_result.concatenate_cloud_ptr->point_step; // convert to original sensor frame if necessary bool need_transform_to_sensor_frame = (cloud->header.frame_id != output_frame_); @@ -205,17 +212,18 @@ CombineCloudHandler::combinePointClouds( *transformed_cloud_ptr_in_sensor_frame); transformed_cloud_ptr_in_sensor_frame->header.stamp = oldest_stamp; transformed_cloud_ptr_in_sensor_frame->header.frame_id = cloud->header.frame_id; - topic_to_transformed_cloud_map[topic] = transformed_cloud_ptr_in_sensor_frame; + concatenate_cloud_result.topic_to_transformed_cloud_map[topic] = + transformed_cloud_ptr_in_sensor_frame; } else { transformed_delay_compensated_cloud_ptr->header.stamp = oldest_stamp; transformed_delay_compensated_cloud_ptr->header.frame_id = output_frame_; - topic_to_transformed_cloud_map[topic] = transformed_delay_compensated_cloud_ptr; + concatenate_cloud_result.topic_to_transformed_cloud_map[topic] = + transformed_delay_compensated_cloud_ptr; } } - concatenate_cloud_ptr->header.stamp = oldest_stamp; + concatenate_cloud_result.concatenate_cloud_ptr->header.stamp = oldest_stamp; - return std::make_tuple( - concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map); + return concatenate_cloud_result; } Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 8f141cfe16b36..9f4e74217c124 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -272,19 +272,16 @@ void PointCloudConcatenateDataSynchronizerComponent::odom_callback( } void PointCloudConcatenateDataSynchronizerComponent::publishClouds( - sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr, - std::unordered_map & - topic_to_transformed_cloud_map, - std::unordered_map & topic_to_original_stamp_map, - double reference_timestamp_min, double reference_timestamp_max) + ConcatenatedCloudResult concatenated_cloud_result, double reference_timestamp_min, + double reference_timestamp_max) { // should never come to this state. - if (concatenate_cloud_ptr == nullptr) { + if (concatenated_cloud_result.concatenate_cloud_ptr == nullptr) { RCLCPP_ERROR(this->get_logger(), "Concatenate cloud is a nullptr."); return; } current_concatenate_cloud_timestamp_ = - rclcpp::Time(concatenate_cloud_ptr->header.stamp).seconds(); + rclcpp::Time(concatenated_cloud_result.concatenate_cloud_ptr->header.stamp).seconds(); if ( current_concatenate_cloud_timestamp_ < latest_concatenate_cloud_timestamp_ && @@ -293,16 +290,18 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( } else { publish_pointcloud_ = true; latest_concatenate_cloud_timestamp_ = current_concatenate_cloud_timestamp_; - auto concatenate_pointcloud_output = - std::make_unique(*concatenate_cloud_ptr); + auto concatenate_pointcloud_output = std::make_unique( + *concatenated_cloud_result.concatenate_cloud_ptr); concatenated_cloud_publisher_->publish(std::move(concatenate_pointcloud_output)); // publish transformed raw pointclouds if (params_.publish_synchronized_pointcloud) { for (auto topic : params_.input_topics) { - if (topic_to_transformed_cloud_map.find(topic) != topic_to_transformed_cloud_map.end()) { - auto transformed_cloud_output = - std::make_unique(*topic_to_transformed_cloud_map[topic]); + if ( + concatenated_cloud_result.topic_to_transformed_cloud_map.find(topic) != + concatenated_cloud_result.topic_to_transformed_cloud_map.end()) { + auto transformed_cloud_output = std::make_unique( + *concatenated_cloud_result.topic_to_transformed_cloud_map[topic]); topic_to_transformed_cloud_publisher_map_[topic]->publish( std::move(transformed_cloud_output)); } else { @@ -316,7 +315,7 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( diagnostic_reference_timestamp_min_ = reference_timestamp_min; diagnostic_reference_timestamp_max_ = reference_timestamp_max; - diagnostic_topic_to_original_stamp_map_ = topic_to_original_stamp_map; + diagnostic_topic_to_original_stamp_map_ = concatenated_cloud_result.topic_to_original_stamp_map; diagnostic_updater_.force_update(); // add processing time for debug @@ -328,7 +327,8 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( debug_publisher_->publish( "debug/processing_time_ms", processing_time_ms); - for (const auto & [topic, transformed_cloud] : topic_to_transformed_cloud_map) { + for (const auto & [topic, transformed_cloud] : + concatenated_cloud_result.topic_to_transformed_cloud_map) { if (transformed_cloud != nullptr) { const auto pipeline_latency_ms = std::chrono::duration( From c1a4001b74a9165b7e2fc9a3f54d3d4bfc8f3974 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 16:56:48 +0900 Subject: [PATCH 057/115] chore: use reference instead of a pointer to conveys node Signed-off-by: vividf --- .../concatenate_data/combine_cloud_handler.hpp | 4 ++-- .../src/concatenate_data/combine_cloud_handler.cpp | 8 ++++---- .../concatenate_data/concatenate_and_time_sync_node.cpp | 2 +- .../test/test_concatenate_node_unit.cpp | 5 +++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index 60fbeb4ba6c83..ba00981d0423b 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -59,7 +59,7 @@ struct ConcatenatedCloudResult class CombineCloudHandler { private: - rclcpp::Node * node_; + rclcpp::Node & node_; std::vector input_topics_; std::string output_frame_; @@ -87,7 +87,7 @@ class CombineCloudHandler public: CombineCloudHandler( - rclcpp::Node * node, std::vector input_topics, std::string output_frame, + rclcpp::Node & node, std::vector input_topics, std::string output_frame, bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud, bool has_static_tf_only); void processTwist(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 3bf4668051858..9d1ea74e3b3e8 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -29,7 +29,7 @@ namespace autoware::pointcloud_preprocessor { CombineCloudHandler::CombineCloudHandler( - rclcpp::Node * node, std::vector input_topics, std::string output_frame, + rclcpp::Node & node, std::vector input_topics, std::string output_frame, bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud, bool has_static_tf_only) : node_(node), @@ -38,7 +38,7 @@ CombineCloudHandler::CombineCloudHandler( is_motion_compensated_(is_motion_compensated), keep_input_frame_in_synchronized_pointcloud_(keep_input_frame_in_synchronized_pointcloud), managed_tf_buffer_( - std::make_unique(node_, has_static_tf_only)) + std::make_unique(&node_, has_static_tf_only)) { } @@ -232,7 +232,7 @@ Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( // return identity if no twist is available if (twist_queue_.empty()) { RCLCPP_WARN_STREAM_THROTTLE( - node_->get_logger(), *node_->get_clock(), std::chrono::milliseconds(10000).count(), + node_.get_logger(), *node_.get_clock(), std::chrono::milliseconds(10000).count(), "No twist is available. Please confirm twist topic and timestamp. Leaving point cloud " "untransformed."); return Eigen::Matrix4f::Identity(); @@ -265,7 +265,7 @@ Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( if (std::fabs(dt) > 0.1) { RCLCPP_WARN_STREAM_THROTTLE( - node_->get_logger(), *node_->get_clock(), std::chrono::milliseconds(10000).count(), + node_.get_logger(), *node_.get_clock(), std::chrono::milliseconds(10000).count(), "Time difference is too large. Cloud not interpolate. Please confirm twist topic and " "timestamp"); break; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 9f4e74217c124..c9f4ee0cf2347 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -153,7 +153,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro // Combine cloud handler combine_cloud_handler_ = std::make_shared( - this, params_.input_topics, params_.output_frame, params_.is_motion_compensated, + *this, params_.input_topics, params_.output_frame, params_.is_motion_compensated, params_.keep_input_frame_in_synchronized_pointcloud, params_.has_static_tf_only); // Diagnostic Updater diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index aca5156689236..b36ea771123ae 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -60,8 +60,9 @@ class ConcatenateCloudTest : public ::testing::Test node_options); combine_cloud_handler_ = std::make_shared( - concatenate_node_.get(), std::vector{"lidar_top", "lidar_left", "lidar_right"}, - "base_link", true, true, false); + *concatenate_node_.get(), + std::vector{"lidar_top", "lidar_left", "lidar_right"}, "base_link", true, true, + false); collector_ = std::make_shared( std::dynamic_pointer_cast< From 7ebd33277b51d4fca6fb7117d568f04f6bc628bb Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Sep 2024 17:13:30 +0900 Subject: [PATCH 058/115] chore: fix camel to snake case Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 16 ++++--- .../combine_cloud_handler.hpp | 12 ++--- .../concatenate_and_time_sync_node.hpp | 8 ++-- .../src/concatenate_data/cloud_collector.cpp | 24 +++++----- .../combine_cloud_handler.cpp | 19 ++++---- .../concatenate_and_time_sync_node.cpp | 35 +++++++------- .../test/test_concatenate_node_unit.cpp | 46 +++++++++---------- 7 files changed, 81 insertions(+), 79 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 759318f0a8fcb..16d85d645259a 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -36,16 +36,18 @@ class CloudCollector std::list> & collectors, std::shared_ptr combine_cloud_handler, int num_of_clouds, double time); - void setReferenceTimeStamp(double timestamp, double noise_window); - std::tuple getReferenceTimeStampBoundary(); - void processCloud(const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud); - void concatenateCallback(); + void set_reference_timestamp(double timestamp, double noise_window); + std::tuple get_reference_timestamp_boundary(); + void process_pointcloud( + const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud); + void concatenate_callback(); - ConcatenatedCloudResult concatenateClouds( + ConcatenatedCloudResult concatenate_pointclouds( std::unordered_map topic_to_cloud_map); - void deleteCollector(); + void delete_collector(); - std::unordered_map getTopicToCloudMap(); + std::unordered_map + get_topic_to_cloud_map(); private: std::shared_ptr ros2_parent_node_; diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index ba00981d0423b..7de66d42026a0 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -79,7 +79,7 @@ class CombineCloudHandler } }; - void correctPointCloudMotion( + void correct_pointcloud_motion( const std::shared_ptr & transformed_cloud_ptr, const std::vector & pc_stamps, std::unordered_map & transform_memo, @@ -90,16 +90,16 @@ class CombineCloudHandler rclcpp::Node & node, std::vector input_topics, std::string output_frame, bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud, bool has_static_tf_only); - void processTwist(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input); - void processOdometry(const nav_msgs::msg::Odometry::ConstSharedPtr & input); + void process_twist(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input); + void process_odometry(const nav_msgs::msg::Odometry::ConstSharedPtr & input); - ConcatenatedCloudResult combinePointClouds( + ConcatenatedCloudResult combine_pointclouds( std::unordered_map & topic_to_cloud_map); - Eigen::Matrix4f computeTransformToAdjustForOldTimestamp( + Eigen::Matrix4f compute_transform_to_adjust_for_old_timestamp( const rclcpp::Time & old_stamp, const rclcpp::Time & new_stamp); - std::deque getTwistQueue(); + std::deque get_twist_queue(); }; } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 2954315eebdf6..4bbfa0885f005 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -118,11 +118,11 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node void twist_callback(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr input); void odom_callback(const nav_msgs::msg::Odometry::ConstSharedPtr input); - std::string formatTimestamp(double timestamp); - void checkConcatStatus(diagnostic_updater::DiagnosticStatusWrapper & stat); - std::string replaceSyncTopicNamePostfix( + std::string format_timestamp(double timestamp); + void check_concat_status(diagnostic_updater::DiagnosticStatusWrapper & stat); + std::string replace_sync_topic_name_postfix( const std::string & original_topic_name, const std::string & postfix); - void convertToXYZIRCCloud( + void convert_to_xyzirc_cloud( const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr); }; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index ef3311c6f118a..e3160018e81c0 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -39,21 +39,21 @@ CloudCollector::CloudCollector( timer_ = rclcpp::create_timer( ros2_parent_node_, ros2_parent_node_->get_clock(), period_ns, - std::bind(&CloudCollector::concatenateCallback, this)); + std::bind(&CloudCollector::concatenate_callback, this)); } -void CloudCollector::setReferenceTimeStamp(double timestamp, double noise_window) +void CloudCollector::set_reference_timestamp(double timestamp, double noise_window) { reference_timestamp_max_ = timestamp + noise_window; reference_timestamp_min_ = timestamp - noise_window; } -std::tuple CloudCollector::getReferenceTimeStampBoundary() +std::tuple CloudCollector::get_reference_timestamp_boundary() { return std::make_tuple(reference_timestamp_min_, reference_timestamp_max_); } -void CloudCollector::processCloud( +void CloudCollector::process_pointcloud( const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud) { // Check if the map already contains an entry for the same topic. This shouldn't happen if the @@ -67,11 +67,11 @@ void CloudCollector::processCloud( } topic_to_cloud_map_[topic_name] = cloud; if (topic_to_cloud_map_.size() == num_of_clouds_) { - concatenateCallback(); + concatenate_callback(); } } -void CloudCollector::concatenateCallback() +void CloudCollector::concatenate_callback() { // All pointclouds are received or the timer has timed out, cancel the timer and concatenate the // pointclouds in the collector. @@ -79,19 +79,19 @@ void CloudCollector::concatenateCallback() // lock for protecting collector list and concatenated pointcloud std::lock_guard lock(mutex_); - auto concatenated_cloud_result = concatenateClouds(topic_to_cloud_map_); + auto concatenated_cloud_result = concatenate_pointclouds(topic_to_cloud_map_); ros2_parent_node_->publishClouds( concatenated_cloud_result, reference_timestamp_min_, reference_timestamp_max_); - deleteCollector(); + delete_collector(); } -ConcatenatedCloudResult CloudCollector::concatenateClouds( +ConcatenatedCloudResult CloudCollector::concatenate_pointclouds( std::unordered_map topic_to_cloud_map) { - return combine_cloud_handler_->combinePointClouds(topic_to_cloud_map); + return combine_cloud_handler_->combine_pointclouds(topic_to_cloud_map); } -void CloudCollector::deleteCollector() +void CloudCollector::delete_collector() { auto it = std::find_if( collectors_.begin(), collectors_.end(), @@ -102,7 +102,7 @@ void CloudCollector::deleteCollector() } std::unordered_map -CloudCollector::getTopicToCloudMap() +CloudCollector::get_topic_to_cloud_map() { return topic_to_cloud_map_; } diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 9d1ea74e3b3e8..f975edc12064b 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -42,8 +42,7 @@ CombineCloudHandler::CombineCloudHandler( { } -// TODO(vivid): change this to process_twist_message -void CombineCloudHandler::processTwist( +void CombineCloudHandler::process_twist( const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & twist_msg) { geometry_msgs::msg::TwistStamped msg; @@ -70,8 +69,7 @@ void CombineCloudHandler::processTwist( twist_queue_.push_back(msg); } -// TODO(vivid): change this to process_odometry_message -void CombineCloudHandler::processOdometry( +void CombineCloudHandler::process_odometry( const nav_msgs::msg::Odometry::ConstSharedPtr & odometry_msg) { geometry_msgs::msg::TwistStamped msg; @@ -98,12 +96,12 @@ void CombineCloudHandler::processOdometry( twist_queue_.push_back(msg); } -std::deque CombineCloudHandler::getTwistQueue() +std::deque CombineCloudHandler::get_twist_queue() { return twist_queue_; } -void CombineCloudHandler::correctPointCloudMotion( +void CombineCloudHandler::correct_pointcloud_motion( const std::shared_ptr & transformed_cloud_ptr, const std::vector & pc_stamps, std::unordered_map & transform_memo, @@ -118,7 +116,8 @@ void CombineCloudHandler::correctPointCloudMotion( if (transform_memo.find(stamp) != transform_memo.end()) { new_to_old_transform = transform_memo[stamp]; } else { - new_to_old_transform = computeTransformToAdjustForOldTimestamp(stamp, current_cloud_stamp); + new_to_old_transform = + compute_transform_to_adjust_for_old_timestamp(stamp, current_cloud_stamp); transform_memo[stamp] = new_to_old_transform; } adjust_to_old_data_transform = new_to_old_transform * adjust_to_old_data_transform; @@ -128,7 +127,7 @@ void CombineCloudHandler::correctPointCloudMotion( adjust_to_old_data_transform, *transformed_cloud_ptr, *transformed_delay_compensated_cloud_ptr); } -ConcatenatedCloudResult CombineCloudHandler::combinePointClouds( +ConcatenatedCloudResult CombineCloudHandler::combine_pointclouds( std::unordered_map & topic_to_cloud_map) { ConcatenatedCloudResult concatenate_cloud_result; @@ -153,7 +152,7 @@ ConcatenatedCloudResult CombineCloudHandler::combinePointClouds( std::shared_ptr transformed_delay_compensated_cloud_ptr; if (is_motion_compensated_) { transformed_delay_compensated_cloud_ptr = std::make_shared(); - correctPointCloudMotion( + correct_pointcloud_motion( transformed_cloud_ptr, pc_stamps, transform_memo, transformed_delay_compensated_cloud_ptr); } else { transformed_delay_compensated_cloud_ptr = transformed_cloud_ptr; @@ -226,7 +225,7 @@ ConcatenatedCloudResult CombineCloudHandler::combinePointClouds( return concatenate_cloud_result; } -Eigen::Matrix4f CombineCloudHandler::computeTransformToAdjustForOldTimestamp( +Eigen::Matrix4f CombineCloudHandler::compute_transform_to_adjust_for_old_timestamp( const rclcpp::Time & old_stamp, const rclcpp::Time & new_stamp) { // return identity if no twist is available diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index c9f4ee0cf2347..404378b564d25 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -101,7 +101,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro if (params_.publish_synchronized_pointcloud) { for (auto & topic : params_.input_topics) { std::string new_topic = - replaceSyncTopicNamePostfix(topic, params_.synchronized_pointcloud_postfix); + replace_sync_topic_name_postfix(topic, params_.synchronized_pointcloud_postfix); auto publisher = this->create_publisher( new_topic, rclcpp::SensorDataQoS().keep_last(params_.maximum_queue_size)); topic_to_transformed_cloud_publisher_map_.insert({topic, publisher}); @@ -159,10 +159,10 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro // Diagnostic Updater diagnostic_updater_.setHardwareID("concatenate_data_checker"); diagnostic_updater_.add( - "concat_status", this, &PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus); + "concat_status", this, &PointCloudConcatenateDataSynchronizerComponent::check_concat_status); } -std::string PointCloudConcatenateDataSynchronizerComponent::replaceSyncTopicNamePostfix( +std::string PointCloudConcatenateDataSynchronizerComponent::replace_sync_topic_name_postfix( const std::string & original_topic_name, const std::string & postfix) { std::string replaced_topic_name; @@ -217,7 +217,7 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( return; } else { // convert to XYZIRC pointcloud if pointcloud is not empty - convertToXYZIRCCloud(input, xyzirc_input_ptr); + convert_to_xyzirc_cloud(input, xyzirc_input_ptr); } // protect cloud collectors list @@ -229,7 +229,7 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( if (!cloud_collectors_.empty()) { for (const auto & cloud_collector : cloud_collectors_) { auto [reference_timestamp_min, reference_timestamp_max] = - cloud_collector->getReferenceTimeStampBoundary(); + cloud_collector->get_reference_timestamp_boundary(); if ( rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] < @@ -237,7 +237,7 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] > reference_timestamp_min - topic_to_noise_window_map_[topic_name]) { cloud_collectors_lock.unlock(); - cloud_collector->processCloud(topic_name, input_ptr); + cloud_collector->process_pointcloud(topic_name, input_ptr); collector_found = true; break; } @@ -252,23 +252,23 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( cloud_collectors_.push_back(new_cloud_collector); cloud_collectors_lock.unlock(); - new_cloud_collector->setReferenceTimeStamp( + new_cloud_collector->set_reference_timestamp( rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name], topic_to_noise_window_map_[topic_name]); - new_cloud_collector->processCloud(topic_name, input_ptr); + new_cloud_collector->process_pointcloud(topic_name, input_ptr); } } void PointCloudConcatenateDataSynchronizerComponent::twist_callback( const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr input) { - combine_cloud_handler_->processTwist(input); + combine_cloud_handler_->process_twist(input); } void PointCloudConcatenateDataSynchronizerComponent::odom_callback( const nav_msgs::msg::Odometry::ConstSharedPtr input) { - combine_cloud_handler_->processOdometry(input); + combine_cloud_handler_->process_odometry(input); } void PointCloudConcatenateDataSynchronizerComponent::publishClouds( @@ -342,7 +342,7 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( } } -void PointCloudConcatenateDataSynchronizerComponent::convertToXYZIRCCloud( +void PointCloudConcatenateDataSynchronizerComponent::convert_to_xyzirc_cloud( const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr) { @@ -397,20 +397,21 @@ void PointCloudConcatenateDataSynchronizerComponent::convertToXYZIRCCloud( } } -std::string PointCloudConcatenateDataSynchronizerComponent::formatTimestamp(double timestamp) +std::string PointCloudConcatenateDataSynchronizerComponent::format_timestamp(double timestamp) { std::ostringstream oss; oss << std::fixed << std::setprecision(9) << timestamp; return oss.str(); } -void PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus( +void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( diagnostic_updater::DiagnosticStatusWrapper & stat) { if (publish_pointcloud_ || drop_previous_but_late_pointcloud_) { - stat.add("concatenated cloud timestamp", formatTimestamp(current_concatenate_cloud_timestamp_)); - stat.add("reference timestamp min", formatTimestamp(diagnostic_reference_timestamp_min_)); - stat.add("reference timestamp max", formatTimestamp(diagnostic_reference_timestamp_max_)); + stat.add( + "concatenated cloud timestamp", format_timestamp(current_concatenate_cloud_timestamp_)); + stat.add("reference timestamp min", format_timestamp(diagnostic_reference_timestamp_min_)); + stat.add("reference timestamp max", format_timestamp(diagnostic_reference_timestamp_max_)); bool topic_miss = false; @@ -422,7 +423,7 @@ void PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus( diagnostic_topic_to_original_stamp_map_.end()) { cloud_status = 1; stat.add( - topic + " timestamp", formatTimestamp(diagnostic_topic_to_original_stamp_map_[topic])); + topic + " timestamp", format_timestamp(diagnostic_topic_to_original_stamp_map_[topic])); } else { topic_miss = true; cloud_status = 0; diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index b36ea771123ae..9197abd6bf6d1 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -189,11 +189,11 @@ TEST_F(ConcatenateCloudTest, TestProcessTwist) twist_msg->twist.twist.linear.x = 1.0; twist_msg->twist.twist.angular.z = 0.1; - combine_cloud_handler_->processTwist(twist_msg); + combine_cloud_handler_->process_twist(twist_msg); - ASSERT_FALSE(combine_cloud_handler_->getTwistQueue().empty()); - EXPECT_EQ(combine_cloud_handler_->getTwistQueue().front().twist.linear.x, 1.0); - EXPECT_EQ(combine_cloud_handler_->getTwistQueue().front().twist.angular.z, 0.1); + ASSERT_FALSE(combine_cloud_handler_->get_twist_queue().empty()); + EXPECT_EQ(combine_cloud_handler_->get_twist_queue().front().twist.linear.x, 1.0); + EXPECT_EQ(combine_cloud_handler_->get_twist_queue().front().twist.angular.z, 0.1); } TEST_F(ConcatenateCloudTest, TestProcessOdometry) @@ -203,11 +203,11 @@ TEST_F(ConcatenateCloudTest, TestProcessOdometry) odom_msg->twist.twist.linear.x = 1.0; odom_msg->twist.twist.angular.z = 0.1; - combine_cloud_handler_->processOdometry(odom_msg); + combine_cloud_handler_->process_odometry(odom_msg); - ASSERT_FALSE(combine_cloud_handler_->getTwistQueue().empty()); - EXPECT_EQ(combine_cloud_handler_->getTwistQueue().front().twist.linear.x, 1.0); - EXPECT_EQ(combine_cloud_handler_->getTwistQueue().front().twist.angular.z, 0.1); + ASSERT_FALSE(combine_cloud_handler_->get_twist_queue().empty()); + EXPECT_EQ(combine_cloud_handler_->get_twist_queue().front().twist.linear.x, 1.0); + EXPECT_EQ(combine_cloud_handler_->get_twist_queue().front().twist.angular.z, 0.1); } TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) @@ -220,15 +220,15 @@ TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) twist_msg1->header.stamp = rclcpp::Time(9, 130'000'000, RCL_ROS_TIME); twist_msg1->twist.twist.linear.x = 1.0; twist_msg1->twist.twist.angular.z = 0.1; - combine_cloud_handler_->processTwist(twist_msg1); + combine_cloud_handler_->process_twist(twist_msg1); auto twist_msg2 = std::make_shared(); twist_msg2->header.stamp = rclcpp::Time(9, 160'000'000, RCL_ROS_TIME); twist_msg2->twist.twist.linear.x = 1.0; twist_msg2->twist.twist.angular.z = 0.1; - combine_cloud_handler_->processTwist(twist_msg2); + combine_cloud_handler_->process_twist(twist_msg2); - Eigen::Matrix4f transform = combine_cloud_handler_->computeTransformToAdjustForOldTimestamp( + Eigen::Matrix4f transform = combine_cloud_handler_->compute_transform_to_adjust_for_old_timestamp( pointcloud_stamp1, pointcloud_stamp2); // translation @@ -254,15 +254,15 @@ TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) twist_msg3->header.stamp = rclcpp::Time(11, 130'000'000, RCL_ROS_TIME); twist_msg3->twist.twist.linear.x = 1.0; twist_msg3->twist.twist.angular.z = 0.1; - combine_cloud_handler_->processTwist(twist_msg3); + combine_cloud_handler_->process_twist(twist_msg3); auto twist_msg4 = std::make_shared(); twist_msg4->header.stamp = rclcpp::Time(11, 160'000'000, RCL_ROS_TIME); twist_msg4->twist.twist.linear.x = 1.0; twist_msg4->twist.twist.angular.z = 0.1; - combine_cloud_handler_->processTwist(twist_msg4); + combine_cloud_handler_->process_twist(twist_msg4); - transform = combine_cloud_handler_->computeTransformToAdjustForOldTimestamp( + transform = combine_cloud_handler_->compute_transform_to_adjust_for_old_timestamp( pointcloud_stamp3, pointcloud_stamp4); // translation @@ -290,8 +290,8 @@ TEST_F(ConcatenateCloudTest, TestSetAndGetReferenceTimeStampBoundary) { double reference_timestamp = 10.0; double noise_window = 0.1; - collector_->setReferenceTimeStamp(reference_timestamp, noise_window); - auto [min, max] = collector_->getReferenceTimeStampBoundary(); + collector_->set_reference_timestamp(reference_timestamp, noise_window); + auto [min, max] = collector_->get_reference_timestamp_boundary(); EXPECT_DOUBLE_EQ(min, 9.9); EXPECT_DOUBLE_EQ(max, 10.1); } @@ -321,7 +321,7 @@ TEST_F(ConcatenateCloudTest, TestConcatenateClouds) topic_to_cloud_map["lidar_right"] = right_pointcloud_ptr; auto [concatenate_cloud_ptr, topic_to_transformed_cloud_map, topic_to_original_stamp_map] = - collector_->concatenateClouds(topic_to_cloud_map); + collector_->concatenate_pointclouds(topic_to_cloud_map); // test output concatenate cloud // No input twist, so it will not do the motion compensation @@ -444,7 +444,7 @@ TEST_F(ConcatenateCloudTest, TestConcatenateClouds) TEST_F(ConcatenateCloudTest, TestDeleteCollector) { - collector_->deleteCollector(); + collector_->delete_collector(); EXPECT_TRUE(collectors_.empty()); } @@ -455,9 +455,9 @@ TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) generatePointCloudMsg(true, false, "lidar_top", timestamp); sensor_msgs::msg::PointCloud2::SharedPtr top_pointcloud_ptr = std::make_shared(top_pointcloud); - collector_->processCloud("lidar_top", top_pointcloud_ptr); + collector_->process_pointcloud("lidar_top", top_pointcloud_ptr); - auto topic_to_cloud_map = collector_->getTopicToCloudMap(); + auto topic_to_cloud_map = collector_->get_topic_to_cloud_map(); EXPECT_EQ(topic_to_cloud_map["lidar_top"], top_pointcloud_ptr); EXPECT_FALSE(collectors_.empty()); @@ -488,9 +488,9 @@ TEST_F(ConcatenateCloudTest, TestProcessMultipleCloud) sensor_msgs::msg::PointCloud2::SharedPtr right_pointcloud_ptr = std::make_shared(right_pointcloud); - collector_->processCloud("lidar_top", top_pointcloud_ptr); - collector_->processCloud("lidar_left", left_pointcloud_ptr); - collector_->processCloud("lidar_right", right_pointcloud_ptr); + collector_->process_pointcloud("lidar_top", top_pointcloud_ptr); + collector_->process_pointcloud("lidar_left", left_pointcloud_ptr); + collector_->process_pointcloud("lidar_right", right_pointcloud_ptr); EXPECT_TRUE(collectors_.empty()); } From 52ed5ed0b19bad40a86a1405cc8db1d35b012567 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 27 Sep 2024 21:49:48 +0900 Subject: [PATCH 059/115] chore: fix logic of publish synchronized pointcloud Signed-off-by: vividf --- .../combine_cloud_handler.hpp | 9 ++-- .../concatenate_and_time_sync_node.hpp | 1 - .../combine_cloud_handler.cpp | 47 +++++++++++-------- .../concatenate_and_time_sync_node.cpp | 33 ++++++------- .../test/test_concatenate_node_unit.cpp | 21 +++++---- 5 files changed, 61 insertions(+), 50 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index 7de66d42026a0..082a37a223ed9 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -17,9 +17,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -51,7 +51,7 @@ namespace autoware::pointcloud_preprocessor struct ConcatenatedCloudResult { sensor_msgs::msg::PointCloud2::SharedPtr concatenate_cloud_ptr{nullptr}; - std::unordered_map + std::optional> topic_to_transformed_cloud_map; std::unordered_map topic_to_original_stamp_map; }; @@ -64,6 +64,7 @@ class CombineCloudHandler std::vector input_topics_; std::string output_frame_; bool is_motion_compensated_; + bool publish_synchronized_pointcloud_; bool keep_input_frame_in_synchronized_pointcloud_; std::unique_ptr managed_tf_buffer_{nullptr}; @@ -88,8 +89,8 @@ class CombineCloudHandler public: CombineCloudHandler( rclcpp::Node & node, std::vector input_topics, std::string output_frame, - bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud, - bool has_static_tf_only); + bool is_motion_compensated, bool publish_synchronized_pointcloud, + bool keep_input_frame_in_synchronized_pointcloud, bool has_static_tf_only); void process_twist(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input); void process_odometry(const nav_msgs::msg::Odometry::ConstSharedPtr & input); diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 4bbfa0885f005..4ca904e585e55 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -14,7 +14,6 @@ #pragma once -#include #include #include #include diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index f975edc12064b..7ccee2e223b23 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -30,12 +30,13 @@ namespace autoware::pointcloud_preprocessor CombineCloudHandler::CombineCloudHandler( rclcpp::Node & node, std::vector input_topics, std::string output_frame, - bool is_motion_compensated, bool keep_input_frame_in_synchronized_pointcloud, - bool has_static_tf_only) + bool is_motion_compensated, bool publish_synchronized_pointcloud, + bool keep_input_frame_in_synchronized_pointcloud, bool has_static_tf_only) : node_(node), input_topics_(input_topics), output_frame_(output_frame), is_motion_compensated_(is_motion_compensated), + publish_synchronized_pointcloud_(publish_synchronized_pointcloud), keep_input_frame_in_synchronized_pointcloud_(keep_input_frame_in_synchronized_pointcloud), managed_tf_buffer_( std::make_unique(&node_, has_static_tf_only)) @@ -201,23 +202,31 @@ ConcatenatedCloudResult CombineCloudHandler::combine_pointclouds( concatenate_cloud_result.concatenate_cloud_ptr->width * concatenate_cloud_result.concatenate_cloud_ptr->point_step; - // convert to original sensor frame if necessary - bool need_transform_to_sensor_frame = (cloud->header.frame_id != output_frame_); - if (keep_input_frame_in_synchronized_pointcloud_ && need_transform_to_sensor_frame) { - auto transformed_cloud_ptr_in_sensor_frame = - std::make_shared(); - managed_tf_buffer_->transformPointcloud( - cloud->header.frame_id, *transformed_delay_compensated_cloud_ptr, - *transformed_cloud_ptr_in_sensor_frame); - transformed_cloud_ptr_in_sensor_frame->header.stamp = oldest_stamp; - transformed_cloud_ptr_in_sensor_frame->header.frame_id = cloud->header.frame_id; - concatenate_cloud_result.topic_to_transformed_cloud_map[topic] = - transformed_cloud_ptr_in_sensor_frame; - } else { - transformed_delay_compensated_cloud_ptr->header.stamp = oldest_stamp; - transformed_delay_compensated_cloud_ptr->header.frame_id = output_frame_; - concatenate_cloud_result.topic_to_transformed_cloud_map[topic] = - transformed_delay_compensated_cloud_ptr; + if (publish_synchronized_pointcloud_) { + if (!concatenate_cloud_result.topic_to_transformed_cloud_map) { + // Initialize the map if it is not present + concatenate_cloud_result.topic_to_transformed_cloud_map = + std::unordered_map(); + } + // convert to original sensor frame if necessary + bool need_transform_to_sensor_frame = (cloud->header.frame_id != output_frame_); + if (keep_input_frame_in_synchronized_pointcloud_ && need_transform_to_sensor_frame) { + auto transformed_cloud_ptr_in_sensor_frame = + std::make_shared(); + managed_tf_buffer_->transformPointcloud( + cloud->header.frame_id, *transformed_delay_compensated_cloud_ptr, + *transformed_cloud_ptr_in_sensor_frame); + transformed_cloud_ptr_in_sensor_frame->header.stamp = oldest_stamp; + transformed_cloud_ptr_in_sensor_frame->header.frame_id = cloud->header.frame_id; + + (*concatenate_cloud_result.topic_to_transformed_cloud_map)[topic] = + transformed_cloud_ptr_in_sensor_frame; + } else { + transformed_delay_compensated_cloud_ptr->header.stamp = oldest_stamp; + transformed_delay_compensated_cloud_ptr->header.frame_id = output_frame_; + (*concatenate_cloud_result.topic_to_transformed_cloud_map)[topic] = + transformed_delay_compensated_cloud_ptr; + } } } concatenate_cloud_result.concatenate_cloud_ptr->header.stamp = oldest_stamp; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 404378b564d25..1d621740a3e39 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -154,7 +154,8 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro // Combine cloud handler combine_cloud_handler_ = std::make_shared( *this, params_.input_topics, params_.output_frame, params_.is_motion_compensated, - params_.keep_input_frame_in_synchronized_pointcloud, params_.has_static_tf_only); + params_.publish_synchronized_pointcloud, params_.keep_input_frame_in_synchronized_pointcloud, + params_.has_static_tf_only); // Diagnostic Updater diagnostic_updater_.setHardwareID("concatenate_data_checker"); @@ -295,13 +296,16 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( concatenated_cloud_publisher_->publish(std::move(concatenate_pointcloud_output)); // publish transformed raw pointclouds - if (params_.publish_synchronized_pointcloud) { + if ( + params_.publish_synchronized_pointcloud && + concatenated_cloud_result.topic_to_transformed_cloud_map) { for (auto topic : params_.input_topics) { + // Get a reference to the internal map if ( - concatenated_cloud_result.topic_to_transformed_cloud_map.find(topic) != - concatenated_cloud_result.topic_to_transformed_cloud_map.end()) { + (*concatenated_cloud_result.topic_to_transformed_cloud_map).find(topic) != + (*concatenated_cloud_result.topic_to_transformed_cloud_map).end()) { auto transformed_cloud_output = std::make_unique( - *concatenated_cloud_result.topic_to_transformed_cloud_map[topic]); + *(*concatenated_cloud_result.topic_to_transformed_cloud_map).at(topic)); topic_to_transformed_cloud_publisher_map_[topic]->publish( std::move(transformed_cloud_output)); } else { @@ -327,17 +331,14 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( debug_publisher_->publish( "debug/processing_time_ms", processing_time_ms); - for (const auto & [topic, transformed_cloud] : - concatenated_cloud_result.topic_to_transformed_cloud_map) { - if (transformed_cloud != nullptr) { - const auto pipeline_latency_ms = - std::chrono::duration( - std::chrono::nanoseconds( - (this->get_clock()->now() - transformed_cloud->header.stamp).nanoseconds())) - .count(); - debug_publisher_->publish( - "debug" + topic + "/pipeline_latency_ms", pipeline_latency_ms); - } + for (const auto & [topic, stamp] : concatenated_cloud_result.topic_to_original_stamp_map) { + const auto pipeline_latency_ms = + std::chrono::duration( + std::chrono::duration_cast( + std::chrono::duration(this->get_clock()->now().nanoseconds() - stamp * 1e9))) + .count(); + debug_publisher_->publish( + "debug" + topic + "/pipeline_latency_ms", pipeline_latency_ms); } } } diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 9197abd6bf6d1..d00b463b2f9eb 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -62,7 +62,7 @@ class ConcatenateCloudTest : public ::testing::Test std::make_shared( *concatenate_node_.get(), std::vector{"lidar_top", "lidar_left", "lidar_right"}, "base_link", true, true, - false); + true, false); collector_ = std::make_shared( std::dynamic_pointer_cast< @@ -370,12 +370,13 @@ TEST_F(ConcatenateCloudTest, TestConcatenateClouds) oss.clear(); oss.str(""); i = 0; + sensor_msgs::PointCloud2Iterator top_pc_iter_x( - *topic_to_transformed_cloud_map["lidar_top"], "x"); + *(topic_to_transformed_cloud_map.value().at("lidar_top")), "x"); sensor_msgs::PointCloud2Iterator top_pc_iter_y( - *topic_to_transformed_cloud_map["lidar_top"], "y"); + *(topic_to_transformed_cloud_map.value().at("lidar_top")), "y"); sensor_msgs::PointCloud2Iterator top_pc_iter_z( - *topic_to_transformed_cloud_map["lidar_top"], "z"); + *(topic_to_transformed_cloud_map.value().at("lidar_top")), "z"); for (; top_pc_iter_x != top_pc_iter_x.end(); ++top_pc_iter_x, ++top_pc_iter_y, ++top_pc_iter_z, ++i) { @@ -394,11 +395,11 @@ TEST_F(ConcatenateCloudTest, TestConcatenateClouds) oss.str(""); i = 0; sensor_msgs::PointCloud2Iterator left_pc_iter_x( - *topic_to_transformed_cloud_map["lidar_left"], "x"); + *(topic_to_transformed_cloud_map.value().at("lidar_left")), "x"); sensor_msgs::PointCloud2Iterator left_pc_iter_y( - *topic_to_transformed_cloud_map["lidar_left"], "y"); + *(topic_to_transformed_cloud_map.value().at("lidar_left")), "y"); sensor_msgs::PointCloud2Iterator left_pc_iter_z( - *topic_to_transformed_cloud_map["lidar_left"], "z"); + *(topic_to_transformed_cloud_map.value().at("lidar_left")), "z"); for (; left_pc_iter_x != left_pc_iter_x.end(); ++left_pc_iter_x, ++left_pc_iter_y, ++left_pc_iter_z, ++i) { @@ -417,11 +418,11 @@ TEST_F(ConcatenateCloudTest, TestConcatenateClouds) oss.str(""); i = 0; sensor_msgs::PointCloud2Iterator right_pc_iter_x( - *topic_to_transformed_cloud_map["lidar_right"], "x"); + *(topic_to_transformed_cloud_map.value().at("lidar_right")), "x"); sensor_msgs::PointCloud2Iterator right_pc_iter_y( - *topic_to_transformed_cloud_map["lidar_right"], "y"); + *(topic_to_transformed_cloud_map.value().at("lidar_right")), "y"); sensor_msgs::PointCloud2Iterator right_pc_iter_z( - *topic_to_transformed_cloud_map["lidar_right"], "z"); + *(topic_to_transformed_cloud_map.value().at("lidar_right")), "z"); for (; right_pc_iter_x != right_pc_iter_x.end(); ++right_pc_iter_x, ++right_pc_iter_y, ++right_pc_iter_z, ++i) { From b863d4939291bdf1878ccf29c17bc633a2b25c33 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 27 Sep 2024 22:03:25 +0900 Subject: [PATCH 060/115] chore: fix cpp check Signed-off-by: vividf --- .../src/concatenate_data/concatenate_and_time_sync_node.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 1d621740a3e39..bba13a5834e3d 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -354,17 +354,17 @@ void PointCloudConcatenateDataSynchronizerComponent::convert_to_xyzirc_cloud( output_modifier.reserve(input_ptr->width); bool has_valid_intensity = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](auto & field) { + std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { return field.name == "intensity" && field.datatype == sensor_msgs::msg::PointField::UINT8; }); bool has_valid_return_type = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](auto & field) { + std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { return field.name == "return_type" && field.datatype == sensor_msgs::msg::PointField::UINT8; }); bool has_valid_channel = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](auto & field) { + std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { return field.name == "channel" && field.datatype == sensor_msgs::msg::PointField::UINT16; }); From 92d69a4c6a4bb13c0ef2aa3d5e593fbda283c247 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 30 Sep 2024 10:55:26 +0900 Subject: [PATCH 061/115] chore: remove logging and throw error directly Signed-off-by: vividf --- .../concatenate_and_time_sync_node.cpp | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index bba13a5834e3d..b924eafba8253 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -43,7 +43,6 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro stop_watch_ptr_->tic("cyclic_time"); stop_watch_ptr_->tic("processing_time"); - bool parameters_valid = true; // initialize parameters params_.has_static_tf_only = declare_parameter("has_static_tf_only"); params_.maximum_queue_size = declare_parameter("maximum_queue_size"); @@ -66,26 +65,21 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro declare_parameter>("lidar_timestamp_noise_window"); if (params_.input_topics.empty()) { - RCLCPP_ERROR(get_logger(), "Need a 'input_topics' parameter to be set before continuing!"); - parameters_valid = false; + throw std::runtime_error("Need a 'input_topics' parameter to be set before continuing."); } else if (params_.input_topics.size() == 1) { - RCLCPP_ERROR(get_logger(), "Only one topic given. Need at least two topics to continue."); - parameters_valid = false; + throw std::runtime_error("Only one topic given. Need at least two topics to continue."); } if (params_.output_frame.empty()) { - RCLCPP_ERROR(get_logger(), "Need an 'output_frame' parameter to be set before continuing!"); - parameters_valid = false; + throw std::runtime_error("Need an 'output_frame' parameter to be set before continuing."); } if (params_.lidar_timestamp_offsets.size() != params_.input_topics.size()) { - RCLCPP_ERROR( - get_logger(), "The number of topics does not match the number of timestamp offsets"); - parameters_valid = false; + throw std::runtime_error( + "The number of topics does not match the number of timestamp offsets."); } if (params_.lidar_timestamp_noise_window.size() != params_.input_topics.size()) { - RCLCPP_ERROR( - get_logger(), "The number of topics does not match the number of timestamp noise window"); - parameters_valid = false; + throw std::runtime_error( + "The number of topics does not match the number of timestamp noise window."); } for (size_t i = 0; i < params_.input_topics.size(); i++) { @@ -123,18 +117,11 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro &PointCloudConcatenateDataSynchronizerComponent::odom_callback, this, std::placeholders::_1)); } else { - RCLCPP_ERROR_STREAM( - get_logger(), "input_twist_topic_type is invalid: " << params_.input_twist_topic_type); - parameters_valid = false; + throw std::runtime_error( + "input_twist_topic_type is invalid: " + params_.input_twist_topic_type); } } - if (!parameters_valid) { - throw std::runtime_error( - "Invalid parameter setting detected. Please review the provided parameter values and refer " - "to the error logs for detailed information."); - } - for (const std::string & topic : params_.input_topics) { std::function callback = std::bind( &PointCloudConcatenateDataSynchronizerComponent::cloud_callback, this, std::placeholders::_1, From edb061080dd50fc7c5f9d6aab69e0d432bff9685 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 30 Sep 2024 13:34:31 +0900 Subject: [PATCH 062/115] chore: fix clangd warnings Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 6 +-- .../combine_cloud_handler.hpp | 3 -- .../concatenate_and_time_sync_node.hpp | 11 +++-- .../src/concatenate_data/cloud_collector.cpp | 6 +-- .../concatenate_and_time_sync_node.cpp | 44 +++++++++---------- 5 files changed, 32 insertions(+), 38 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 16d85d645259a..7d7e9277b641c 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -32,7 +32,7 @@ class CloudCollector { public: CloudCollector( - std::shared_ptr ros2_parent_node, + std::shared_ptr && ros2_parent_node, std::list> & collectors, std::shared_ptr combine_cloud_handler, int num_of_clouds, double time); @@ -57,8 +57,8 @@ class CloudCollector std::unordered_map topic_to_cloud_map_; uint64_t num_of_clouds_; double timeout_sec_; - double reference_timestamp_min_; - double reference_timestamp_max_; + double reference_timestamp_min_{0.0}; + double reference_timestamp_max_{0.0}; std::mutex mutex_; }; diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index 082a37a223ed9..d8a363c63be21 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -16,15 +16,12 @@ #include #include -#include #include -#include #include #include #include // ROS includes -#include "autoware_point_types/types.hpp" #include #include diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 4ca904e585e55..f4d651aa3b0f1 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -57,8 +56,8 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node { public: explicit PointCloudConcatenateDataSynchronizerComponent(const rclcpp::NodeOptions & node_options); - virtual ~PointCloudConcatenateDataSynchronizerComponent() {} - void publishClouds( + ~PointCloudConcatenateDataSynchronizerComponent() override = default; + void publish_clouds( ConcatenatedCloudResult concatenated_cloud_result, double reference_timestamp_min, double reference_timestamp_max); @@ -96,7 +95,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node std::unordered_map topic_to_noise_window_map_; // default postfix name for synchronized pointcloud - static constexpr const char * default_sync_topic_postfix_ = "_synchronized"; + static constexpr const char * default_sync_topic_postfix = "_synchronized"; // subscribers std::vector::SharedPtr> pointcloud_subs_; @@ -117,11 +116,11 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node void twist_callback(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr input); void odom_callback(const nav_msgs::msg::Odometry::ConstSharedPtr input); - std::string format_timestamp(double timestamp); + static std::string format_timestamp(double timestamp); void check_concat_status(diagnostic_updater::DiagnosticStatusWrapper & stat); std::string replace_sync_topic_name_postfix( const std::string & original_topic_name, const std::string & postfix); - void convert_to_xyzirc_cloud( + static void convert_to_xyzirc_cloud( const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr); }; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index e3160018e81c0..e25ad352f284c 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -25,10 +25,10 @@ namespace autoware::pointcloud_preprocessor { CloudCollector::CloudCollector( - std::shared_ptr ros2_parent_node, + std::shared_ptr && ros2_parent_node, std::list> & collectors, std::shared_ptr combine_cloud_handler, int num_of_clouds, double timeout_sec) -: ros2_parent_node_(ros2_parent_node), +: ros2_parent_node_(std::move(ros2_parent_node)), collectors_(collectors), combine_cloud_handler_(combine_cloud_handler), num_of_clouds_(num_of_clouds), @@ -80,7 +80,7 @@ void CloudCollector::concatenate_callback() // lock for protecting collector list and concatenated pointcloud std::lock_guard lock(mutex_); auto concatenated_cloud_result = concatenate_pointclouds(topic_to_cloud_map_); - ros2_parent_node_->publishClouds( + ros2_parent_node_->publish_clouds( concatenated_cloud_result, reference_timestamp_min_, reference_timestamp_max_); delete_collector(); } diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index b924eafba8253..c22876b19c258 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -45,7 +45,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro // initialize parameters params_.has_static_tf_only = declare_parameter("has_static_tf_only"); - params_.maximum_queue_size = declare_parameter("maximum_queue_size"); + params_.maximum_queue_size = static_cast(declare_parameter("maximum_queue_size")); params_.timeout_sec = declare_parameter("timeout_sec"); params_.is_motion_compensated = declare_parameter("is_motion_compensated"); params_.publish_synchronized_pointcloud = @@ -66,7 +66,8 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro if (params_.input_topics.empty()) { throw std::runtime_error("Need a 'input_topics' parameter to be set before continuing."); - } else if (params_.input_topics.size() == 1) { + } + if (params_.input_topics.size() == 1) { throw std::runtime_error("Only one topic given. Need at least two topics to continue."); } @@ -155,18 +156,18 @@ std::string PointCloudConcatenateDataSynchronizerComponent::replace_sync_topic_n { std::string replaced_topic_name; // separate the topic name by '/' and replace the last element with the new postfix - size_t pos = original_topic_name.find_last_of("/"); + size_t pos = original_topic_name.find_last_of('/'); if (pos == std::string::npos) { // not found '/': this is not a namespaced topic RCLCPP_WARN_STREAM( get_logger(), "The topic name is not namespaced. The postfix will be added to the end of the topic name."); return original_topic_name + postfix; - } else { - // replace the last element with the new postfix - replaced_topic_name = original_topic_name.substr(0, pos) + "/" + postfix; } + // replace the last element with the new postfix + replaced_topic_name = original_topic_name.substr(0, pos) + "/" + postfix; + // if topic name is the same with original topic name, add postfix to the end of the topic name if (replaced_topic_name == original_topic_name) { RCLCPP_WARN_STREAM( @@ -175,7 +176,7 @@ std::string PointCloudConcatenateDataSynchronizerComponent::replace_sync_topic_n << " have the same postfix with synchronized pointcloud. We use " "the postfix " "to the end of the topic name."); - replaced_topic_name = original_topic_name + default_sync_topic_postfix_; + replaced_topic_name = original_topic_name + default_sync_topic_postfix; } return replaced_topic_name; } @@ -203,11 +204,11 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( RCLCPP_WARN_STREAM_THROTTLE( this->get_logger(), *this->get_clock(), 1000, "Empty sensor points!"); return; - } else { - // convert to XYZIRC pointcloud if pointcloud is not empty - convert_to_xyzirc_cloud(input, xyzirc_input_ptr); } + // convert to XYZIRC pointcloud if pointcloud is not empty + convert_to_xyzirc_cloud(input, xyzirc_input_ptr); + // protect cloud collectors list std::unique_lock cloud_collectors_lock(cloud_collectors_mutex_); @@ -259,7 +260,7 @@ void PointCloudConcatenateDataSynchronizerComponent::odom_callback( combine_cloud_handler_->process_odometry(input); } -void PointCloudConcatenateDataSynchronizerComponent::publishClouds( +void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( ConcatenatedCloudResult concatenated_cloud_result, double reference_timestamp_min, double reference_timestamp_max) { @@ -286,7 +287,7 @@ void PointCloudConcatenateDataSynchronizerComponent::publishClouds( if ( params_.publish_synchronized_pointcloud && concatenated_cloud_result.topic_to_transformed_cloud_map) { - for (auto topic : params_.input_topics) { + for (const auto & topic : params_.input_topics) { // Get a reference to the internal map if ( (*concatenated_cloud_result.topic_to_transformed_cloud_map).find(topic) != @@ -403,36 +404,33 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( bool topic_miss = false; - int concatenate_status = 1; - for (auto topic : params_.input_topics) { - int cloud_status; // 1 for success, 0 for failure + int concatenated_cloud_status = 1; // 1 for success, 0 for failure + int cloud_status = 1; // for each lidar's pointcloud + for (const auto & topic : params_.input_topics) { if ( diagnostic_topic_to_original_stamp_map_.find(topic) != diagnostic_topic_to_original_stamp_map_.end()) { - cloud_status = 1; stat.add( topic + " timestamp", format_timestamp(diagnostic_topic_to_original_stamp_map_[topic])); } else { topic_miss = true; + concatenated_cloud_status = 0; cloud_status = 0; - concatenate_status = 0; } stat.add(topic, cloud_status); } - stat.add("concatenate status", concatenate_status); + stat.add("concatenate status", concatenated_cloud_status); + + int8_t level = diagnostic_msgs::msg::DiagnosticStatus::OK; + std::string message = "Concatenated pointcloud is published and include all topics"; - int8_t level; - std::string message; if (topic_miss) { level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; message = "Concatenated pointcloud is published but miss some topics"; } else if (drop_previous_but_late_pointcloud_) { level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; message = "Concatenated pointcloud is not published as it is too late"; - } else { - level = diagnostic_msgs::msg::DiagnosticStatus::OK; - message = "Concatenated pointcloud is published and include all topics"; } stat.summary(level, message); From b6700a9963c6cc2806fcabd93a4c2f6708641fc6 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 30 Sep 2024 14:06:50 +0900 Subject: [PATCH 063/115] chore: fix json schema Signed-off-by: vividf --- ...concatenate_and_time_sync_node.schema.json | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json index 37608d2900fe3..022e7f30f8d73 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json @@ -9,11 +9,13 @@ "maximum_queue_size": { "type": "integer", "default": "5", + "minimum": 1, "description": "Maximum size of the queue for the Keep Last policy in QoS history." }, "timeout_sec": { "type": "number", "default": "0.1", + "minimum": 0.001, "description": "Timer's timeout duration in seconds when collectors are created. Collectors will concatenate the point clouds when timeout_sec reaches zero." }, "is_motion_compensated": { @@ -43,36 +45,44 @@ }, "input_twist_topic_type": { "type": "string", + "enum": ["twist", "odom"], "default": "twist", "description": "Type of the input twist topic." }, "input_topics": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 1 }, - "default": [], + "default": ["cloud_topic1", "cloud_topic2", "cloud_topic3"], + "minItems": 2, "description": "List of input point cloud topics." }, "output_frame": { "type": "string", "default": "base_link", + "minLength": 1, "description": "Output frame." }, "lidar_timestamp_offsets": { "type": "array", "items": { - "type": "number" + "type": "number", + "minimum": 0.0 }, - "default": [], - "description": "List of LiDAR timestamp offsets in seconds. The offset values should be specified in the same order as the input_topics." + "default": [0.0, 0.0, 0.0], + "minItems": 2, + "description": "List of LiDAR timestamp offsets in seconds (relative to the earliest LiDAR timestamp). The offsets should be provided in the same order as the input topics." }, "lidar_timestamp_noise_window": { "type": "array", "items": { - "type": "number" + "type": "number", + "minimum": 0.0 }, - "default": [], + "default": [0.01, 0.01, 0.01], + "minItems": 2, "description": "List of LiDAR timestamp noise windows in seconds. The noise values should be specified in the same order as the input_topics." }, "has_static_tf_only": { From 130bcb87d6c697663a8bed2fd5de9f14290a39f9 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 30 Sep 2024 14:13:00 +0900 Subject: [PATCH 064/115] chore: fix clangd warning Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 2 +- .../src/concatenate_data/cloud_collector.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 7d7e9277b641c..b7a3cacc19325 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -34,7 +34,7 @@ class CloudCollector CloudCollector( std::shared_ptr && ros2_parent_node, std::list> & collectors, - std::shared_ptr combine_cloud_handler, int num_of_clouds, double time); + std::shared_ptr & combine_cloud_handler, int num_of_clouds, double time); void set_reference_timestamp(double timestamp, double noise_window); std::tuple get_reference_timestamp_boundary(); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index e25ad352f284c..b6f84889f704c 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -27,7 +27,8 @@ namespace autoware::pointcloud_preprocessor CloudCollector::CloudCollector( std::shared_ptr && ros2_parent_node, std::list> & collectors, - std::shared_ptr combine_cloud_handler, int num_of_clouds, double timeout_sec) + std::shared_ptr & combine_cloud_handler, int num_of_clouds, + double timeout_sec) : ros2_parent_node_(std::move(ros2_parent_node)), collectors_(collectors), combine_cloud_handler_(combine_cloud_handler), From 31500f83abe8b482d06dc0c9dabd2cd0b4510a24 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 30 Sep 2024 14:32:58 +0900 Subject: [PATCH 065/115] chore: remove unused variable Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index f4d651aa3b0f1..3144a03a92fd1 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -88,7 +88,6 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node std::unordered_map diagnostic_topic_to_original_stamp_map_; std::shared_ptr combine_cloud_handler_; - std::shared_ptr cloud_collector_; std::list> cloud_collectors_; std::mutex cloud_collectors_mutex_; std::unordered_map topic_to_offset_map_; From 000c890bf32853e2bbdf283ccf5020d52415ea10 Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 1 Oct 2024 14:40:03 +0900 Subject: [PATCH 066/115] chore: fix launcher Signed-off-by: vividf --- .../launch/concatenate_and_time_sync_node.launch.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/launch/concatenate_and_time_sync_node.launch.xml b/sensing/autoware_pointcloud_preprocessor/launch/concatenate_and_time_sync_node.launch.xml index aa9579305dfb8..f553c15f01210 100644 --- a/sensing/autoware_pointcloud_preprocessor/launch/concatenate_and_time_sync_node.launch.xml +++ b/sensing/autoware_pointcloud_preprocessor/launch/concatenate_and_time_sync_node.launch.xml @@ -5,7 +5,7 @@ - + From 55e0d242b6991006d79b0e303efb29cbdfe0de09 Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 1 Oct 2024 19:37:35 +0900 Subject: [PATCH 067/115] chore: fix clangd warning Signed-off-by: vividf --- .../test/test_concatenate_node_component.py | 76 +++++++++---------- .../test/test_concatenate_node_unit.cpp | 36 ++++----- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index 86b138d553af8..0aeced645de96 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -65,8 +65,8 @@ STANDARD_TOLERANCE = 1e-4 COARSE_TOLERANCE = TIMESTAMP_NOISE * 2 -global_seconds = 10 -global_nanoseconds = 100000000 +GLOBAL_SECONDS = 10 +GLOBAL_NANOSECONDS = 100000000 @pytest.mark.launch_test @@ -216,7 +216,7 @@ def generate_transform_msg( qw: float, ) -> TransformStamped: tf_msg = TransformStamped() - tf_msg.header.stamp = Time(seconds=global_seconds, nanoseconds=global_nanoseconds).to_msg() + tf_msg.header.stamp = Time(seconds=GLOBAL_SECONDS, nanoseconds=GLOBAL_NANOSECONDS).to_msg() tf_msg.header.frame_id = parent_frame tf_msg.child_frame_id = child_frame tf_msg.transform.translation.x = x @@ -271,7 +271,7 @@ def generate_static_transform_msgs() -> List[TransformStamped]: def generate_twist_msg() -> TwistWithCovarianceStamped: twist_header = Header() - twist_header.stamp = Time(seconds=global_seconds, nanoseconds=global_nanoseconds).to_msg() + twist_header.stamp = Time(seconds=GLOBAL_SECONDS, nanoseconds=GLOBAL_NANOSECONDS).to_msg() twist_header.frame_id = "base_link" twist_msg = TwistWithCovarianceStamped() twist_msg.header = twist_header @@ -355,14 +355,14 @@ def test_1_normal_inputs(self): 2. The motion compensation of concatenation works well. """ time.sleep(1) - global global_seconds + global GLOBAL_SECONDS twist_msg = generate_twist_msg() self.twist_publisher.publish(twist_msg) for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): - pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanoseconds + frame_idx * MILLISECONDS * 40 # add 40 ms + pointcloud_seconds = GLOBAL_SECONDS + pointcloud_nanoseconds = GLOBAL_NANOSECONDS + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -414,7 +414,7 @@ def test_1_normal_inputs(self): "The concatenate pointcloud frame id is not base_link", ) - global_seconds += 1 + GLOBAL_SECONDS += 1 def test_2_normal_inputs_with_noise(self): """Test the normal situation when no pointcloud is delayed or dropped. Additionally, the pointcloud's timestamp is not ideal which has some noise. @@ -423,16 +423,16 @@ def test_2_normal_inputs_with_noise(self): 1. Concatenate works fine when pointclouds' timestamp has noise. """ time.sleep(1) - global global_seconds + global GLOBAL_SECONDS twist_msg = generate_twist_msg() self.twist_publisher.publish(twist_msg) for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): noise = random.uniform(-10, 10) * MILLISECONDS - pointcloud_seconds = global_seconds + pointcloud_seconds = GLOBAL_SECONDS pointcloud_nanoseconds = ( - global_nanoseconds + frame_idx * MILLISECONDS * 40 + noise + GLOBAL_NANOSECONDS + frame_idx * MILLISECONDS * 40 + noise ) # add 40 ms and noise (-10 to 10 ms) pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds @@ -485,14 +485,14 @@ def test_3_abnormal_null_pointcloud(self): 1. The concatenate node ignore empty pointcloud and concatenate the remain pointcloud. """ time.sleep(1) - global global_seconds + global GLOBAL_SECONDS twist_msg = generate_twist_msg() self.twist_publisher.publish(twist_msg) for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): - pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanoseconds + frame_idx * MILLISECONDS * 40 # add 40 ms + pointcloud_seconds = GLOBAL_SECONDS + pointcloud_nanoseconds = GLOBAL_NANOSECONDS + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -545,7 +545,7 @@ def test_3_abnormal_null_pointcloud(self): "The concatenation node have weird output", ) - global_seconds += 1 + GLOBAL_SECONDS += 1 def test_4_abnormal_null_pointcloud_and_drop(self): """Test the abnormal situation when a pointcloud is empty and other pointclouds are dropped. @@ -554,13 +554,13 @@ def test_4_abnormal_null_pointcloud_and_drop(self): 1. The concatenate node ignore empty pointcloud and do not publish any pointcloud. """ time.sleep(1) - global global_seconds + global GLOBAL_SECONDS twist_msg = generate_twist_msg() self.twist_publisher.publish(twist_msg) - pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanoseconds + pointcloud_seconds = GLOBAL_SECONDS + pointcloud_nanoseconds = GLOBAL_NANOSECONDS pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -584,7 +584,7 @@ def test_4_abnormal_null_pointcloud_and_drop(self): "The number of concatenate pointcloud has different number as expected.", ) - global_seconds += 1 + GLOBAL_SECONDS += 1 def test_5_abnormal_multiple_pointcloud_drop(self): """Test the abnormal situation when multiple pointclouds were dropped (only one pointcloud arrive). @@ -593,13 +593,13 @@ def test_5_abnormal_multiple_pointcloud_drop(self): 1. The concatenate node concatenates the single pointcloud after the timeout. """ time.sleep(1) - global global_seconds + global GLOBAL_SECONDS twist_msg = generate_twist_msg() self.twist_publisher.publish(twist_msg) - pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanoseconds + pointcloud_seconds = GLOBAL_SECONDS + pointcloud_nanoseconds = GLOBAL_NANOSECONDS pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -648,14 +648,14 @@ def test_6_abnormal_single_pointcloud_drop(self): 1. The concatenate node concatenate the remain pointcloud after the timeout. """ time.sleep(1) - global global_seconds + global GLOBAL_SECONDS twist_msg = generate_twist_msg() self.twist_publisher.publish(twist_msg) for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): - pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanoseconds + frame_idx * MILLISECONDS * 40 # add 40 ms + pointcloud_seconds = GLOBAL_SECONDS + pointcloud_nanoseconds = GLOBAL_NANOSECONDS + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -699,7 +699,7 @@ def test_6_abnormal_single_pointcloud_drop(self): "The concatenation node have weird output", ) - global_seconds += 1 + GLOBAL_SECONDS += 1 def test_7_abnormal_pointcloud_delay(self): """Test the abnormal situation when a pointcloud was delayed after the timeout. @@ -709,14 +709,14 @@ def test_7_abnormal_pointcloud_delay(self): 2. The concatenate node will publish the delayed pointcloud after the timeout. """ time.sleep(1) - global global_seconds + global GLOBAL_SECONDS twist_msg = generate_twist_msg() self.twist_publisher.publish(twist_msg) for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): - pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanoseconds + frame_idx * MILLISECONDS * 40 # add 40 ms + pointcloud_seconds = GLOBAL_SECONDS + pointcloud_nanoseconds = GLOBAL_NANOSECONDS + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -732,9 +732,9 @@ def test_7_abnormal_pointcloud_delay(self): time.sleep(TIMEOUT_SEC) # timeout threshold rclpy.spin_once(self.node, timeout_sec=0.1) - pointcloud_seconds = global_seconds + pointcloud_seconds = GLOBAL_SECONDS pointcloud_nanoseconds = ( - global_nanoseconds + (len(INPUT_LIDAR_TOPICS) - 1) * MILLISECONDS * 40 + GLOBAL_NANOSECONDS + (len(INPUT_LIDAR_TOPICS) - 1) * MILLISECONDS * 40 ) # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds @@ -797,7 +797,7 @@ def test_7_abnormal_pointcloud_delay(self): "The concatenation node have weird output", ) - global_seconds += 1 + GLOBAL_SECONDS += 1 def test_8_abnormal_pointcloud_drop_continue_normal(self): """Test the abnormal situation when a pointcloud was dropped. Afterward, next iteration of pointclouds comes normally. @@ -807,14 +807,14 @@ def test_8_abnormal_pointcloud_drop_continue_normal(self): 2. The concatenate node concatenate next iteration pointclouds when all of the pointcloud arrived. """ time.sleep(1) - global global_seconds + global GLOBAL_SECONDS twist_msg = generate_twist_msg() self.twist_publisher.publish(twist_msg) for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS[:-1]): - pointcloud_seconds = global_seconds - pointcloud_nanoseconds = global_nanoseconds + frame_idx * MILLISECONDS * 40 # add 40 ms + pointcloud_seconds = GLOBAL_SECONDS + pointcloud_nanoseconds = GLOBAL_NANOSECONDS + frame_idx * MILLISECONDS * 40 # add 40 ms pointcloud_timestamp = Time( seconds=pointcloud_seconds, nanoseconds=pointcloud_nanoseconds ).to_msg() @@ -830,9 +830,9 @@ def test_8_abnormal_pointcloud_drop_continue_normal(self): time.sleep(TIMEOUT_SEC) rclpy.spin_once(self.node) - next_global_nanosecond = global_nanoseconds + 100 * MILLISECONDS + next_global_nanosecond = GLOBAL_NANOSECONDS + 100 * MILLISECONDS for frame_idx, _ in enumerate(INPUT_LIDAR_TOPICS): - pointcloud_seconds = global_seconds + pointcloud_seconds = GLOBAL_SECONDS pointcloud_nanoseconds = ( next_global_nanosecond + frame_idx * MILLISECONDS * 40 ) # add 40 ms @@ -901,4 +901,4 @@ def test_8_abnormal_pointcloud_drop_continue_normal(self): "The concatenation node have weird output", ) - global_seconds += 1 + GLOBAL_SECONDS += 1 diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index d00b463b2f9eb..7f15599810e0d 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -60,9 +60,8 @@ class ConcatenateCloudTest : public ::testing::Test node_options); combine_cloud_handler_ = std::make_shared( - *concatenate_node_.get(), - std::vector{"lidar_top", "lidar_left", "lidar_right"}, "base_link", true, true, - true, false); + *concatenate_node_, std::vector{"lidar_top", "lidar_left", "lidar_right"}, + "base_link", true, true, true, false); collector_ = std::make_shared( std::dynamic_pointer_cast< @@ -74,7 +73,7 @@ class ConcatenateCloudTest : public ::testing::Test // Setup TF tf_broadcaster_ = std::make_shared(concatenate_node_); - tf_broadcaster_->sendTransform(generateStaticTransformMsg()); + tf_broadcaster_->sendTransform(generate_static_transform_msgs()); // Spin the node for a while to ensure transforms are published auto start = std::chrono::steady_clock::now(); @@ -85,7 +84,7 @@ class ConcatenateCloudTest : public ::testing::Test } } - geometry_msgs::msg::TransformStamped generateTransformMsg( + static geometry_msgs::msg::TransformStamped generate_transform_msg( const std::string & parent_frame, const std::string & child_frame, double x, double y, double z, double qx, double qy, double qz, double qw) { @@ -104,8 +103,9 @@ class ConcatenateCloudTest : public ::testing::Test return tf_msg; } - sensor_msgs::msg::PointCloud2 generatePointCloudMsg( - bool generate_points, bool is_lidar_frame, std::string topic_name, rclcpp::Time stamp) + static sensor_msgs::msg::PointCloud2 generate_pointcloud_msg( + bool generate_points, bool is_lidar_frame, const std::string & topic_name, + const rclcpp::Time & stamp) { sensor_msgs::msg::PointCloud2 pointcloud_msg; pointcloud_msg.header.stamp = stamp; @@ -156,13 +156,13 @@ class ConcatenateCloudTest : public ::testing::Test return pointcloud_msg; } - std::vector generateStaticTransformMsg() + static std::vector generate_static_transform_msgs() { // generate defined transformations return { - generateTransformMsg("base_link", "lidar_top", 5.0, 5.0, 5.0, 0.683, 0.5, 0.183, 0.499), - generateTransformMsg("base_link", "lidar_left", 1.0, 1.0, 3.0, 0.278, 0.717, 0.441, 0.453)}; - generateTransformMsg("base_link", "lidar_right", 1.0, 1.0, 3.0, 0.278, 0.717, 0.441, 0.453); + generate_transform_msg("base_link", "lidar_top", 5.0, 5.0, 5.0, 0.683, 0.5, 0.183, 0.499), + generate_transform_msg("base_link", "lidar_left", 1.0, 1.0, 3.0, 0.278, 0.717, 0.441, 0.453)}; + generate_transform_msg("base_link", "lidar_right", 1.0, 1.0, 3.0, 0.278, 0.717, 0.441, 0.453); } std::shared_ptr @@ -302,11 +302,11 @@ TEST_F(ConcatenateCloudTest, TestConcatenateClouds) rclcpp::Time left_timestamp(timestamp_seconds, timestamp_nanoseconds + 40'000'000, RCL_ROS_TIME); rclcpp::Time right_timestamp(timestamp_seconds, timestamp_nanoseconds + 80'000'000, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = - generatePointCloudMsg(true, false, "lidar_top", top_timestamp); + generate_pointcloud_msg(true, false, "lidar_top", top_timestamp); sensor_msgs::msg::PointCloud2 left_pointcloud = - generatePointCloudMsg(true, false, "lidar_left", left_timestamp); + generate_pointcloud_msg(true, false, "lidar_left", left_timestamp); sensor_msgs::msg::PointCloud2 right_pointcloud = - generatePointCloudMsg(true, false, "lidar_right", right_timestamp); + generate_pointcloud_msg(true, false, "lidar_right", right_timestamp); sensor_msgs::msg::PointCloud2::SharedPtr top_pointcloud_ptr = std::make_shared(top_pointcloud); @@ -453,7 +453,7 @@ TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) { rclcpp::Time timestamp(timestamp_seconds, timestamp_nanoseconds, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = - generatePointCloudMsg(true, false, "lidar_top", timestamp); + generate_pointcloud_msg(true, false, "lidar_top", timestamp); sensor_msgs::msg::PointCloud2::SharedPtr top_pointcloud_ptr = std::make_shared(top_pointcloud); collector_->process_pointcloud("lidar_top", top_pointcloud_ptr); @@ -476,11 +476,11 @@ TEST_F(ConcatenateCloudTest, TestProcessMultipleCloud) rclcpp::Time left_timestamp(timestamp_seconds, timestamp_nanoseconds + 40'000'000, RCL_ROS_TIME); rclcpp::Time right_timestamp(timestamp_seconds, timestamp_nanoseconds + 80'000'000, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = - generatePointCloudMsg(true, false, "lidar_top", top_timestamp); + generate_pointcloud_msg(true, false, "lidar_top", top_timestamp); sensor_msgs::msg::PointCloud2 left_pointcloud = - generatePointCloudMsg(true, false, "lidar_left", left_timestamp); + generate_pointcloud_msg(true, false, "lidar_left", left_timestamp); sensor_msgs::msg::PointCloud2 right_pointcloud = - generatePointCloudMsg(true, false, "lidar_right", right_timestamp); + generate_pointcloud_msg(true, false, "lidar_right", right_timestamp); sensor_msgs::msg::PointCloud2::SharedPtr top_pointcloud_ptr = std::make_shared(top_pointcloud); From 9199a3d41a06826231bcfc3b96592beddce7c68a Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 4 Oct 2024 20:27:43 +0900 Subject: [PATCH 068/115] chore: ensure thread safety Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 8 ++--- .../concatenate_and_time_sync_node.hpp | 3 ++ .../src/concatenate_data/cloud_collector.cpp | 16 ++-------- .../combine_cloud_handler.cpp | 4 +-- .../concatenate_and_time_sync_node.cpp | 30 ++++++++++++++++++- .../test/test_concatenate_node_unit.cpp | 19 +++++++----- 6 files changed, 48 insertions(+), 32 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index b7a3cacc19325..7b2b6503be097 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -16,7 +16,6 @@ #include "combine_cloud_handler.hpp" -#include #include #include #include @@ -28,12 +27,11 @@ namespace autoware::pointcloud_preprocessor class PointCloudConcatenateDataSynchronizerComponent; class CombineCloudHandler; -class CloudCollector +class CloudCollector { public: CloudCollector( std::shared_ptr && ros2_parent_node, - std::list> & collectors, std::shared_ptr & combine_cloud_handler, int num_of_clouds, double time); void set_reference_timestamp(double timestamp, double noise_window); @@ -44,14 +42,13 @@ class CloudCollector ConcatenatedCloudResult concatenate_pointclouds( std::unordered_map topic_to_cloud_map); - void delete_collector(); + //void delete_collector(); std::unordered_map get_topic_to_cloud_map(); private: std::shared_ptr ros2_parent_node_; - std::list> & collectors_; std::shared_ptr combine_cloud_handler_; rclcpp::TimerBase::SharedPtr timer_; std::unordered_map topic_to_cloud_map_; @@ -59,7 +56,6 @@ class CloudCollector double timeout_sec_; double reference_timestamp_min_{0.0}; double reference_timestamp_max_{0.0}; - std::mutex mutex_; }; } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 3144a03a92fd1..a44878a23e2e9 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -60,6 +60,9 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node void publish_clouds( ConcatenatedCloudResult concatenated_cloud_result, double reference_timestamp_min, double reference_timestamp_max); + void delete_collector(CloudCollector & cloud_collector); + std::list> get_cloud_collectors(); + void add_cloud_collector(const std::shared_ptr & collector); private: struct Parameters diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index b6f84889f704c..fabb7136f8256 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -26,11 +26,9 @@ namespace autoware::pointcloud_preprocessor CloudCollector::CloudCollector( std::shared_ptr && ros2_parent_node, - std::list> & collectors, std::shared_ptr & combine_cloud_handler, int num_of_clouds, double timeout_sec) : ros2_parent_node_(std::move(ros2_parent_node)), - collectors_(collectors), combine_cloud_handler_(combine_cloud_handler), num_of_clouds_(num_of_clouds), timeout_sec_(timeout_sec) @@ -78,12 +76,11 @@ void CloudCollector::concatenate_callback() // pointclouds in the collector. timer_->cancel(); - // lock for protecting collector list and concatenated pointcloud - std::lock_guard lock(mutex_); auto concatenated_cloud_result = concatenate_pointclouds(topic_to_cloud_map_); + ros2_parent_node_->publish_clouds( concatenated_cloud_result, reference_timestamp_min_, reference_timestamp_max_); - delete_collector(); + ros2_parent_node_->delete_collector(*this); } ConcatenatedCloudResult CloudCollector::concatenate_pointclouds( @@ -92,15 +89,6 @@ ConcatenatedCloudResult CloudCollector::concatenate_pointclouds( return combine_cloud_handler_->combine_pointclouds(topic_to_cloud_map); } -void CloudCollector::delete_collector() -{ - auto it = std::find_if( - collectors_.begin(), collectors_.end(), - [this](const std::shared_ptr & collector) { return collector.get() == this; }); - if (it != collectors_.end()) { - collectors_.erase(it); - } -} std::unordered_map CloudCollector::get_topic_to_cloud_map() diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 7ccee2e223b23..9cdc7ad5d0b2f 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -21,8 +21,6 @@ #include #include #include -#include -#include #include namespace autoware::pointcloud_preprocessor @@ -135,7 +133,7 @@ ConcatenatedCloudResult CombineCloudHandler::combine_pointclouds( std::vector pc_stamps; for (const auto & [topic, cloud] : topic_to_cloud_map) { - pc_stamps.push_back(rclcpp::Time(cloud->header.stamp)); + pc_stamps.emplace_back(rclcpp::Time(cloud->header.stamp)); } std::sort(pc_stamps.begin(), pc_stamps.end(), std::greater()); const auto oldest_stamp = pc_stamps.back(); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index c22876b19c258..0b757ca0bd4ff 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -237,7 +237,7 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( if (!collector_found) { auto new_cloud_collector = std::make_shared( std::dynamic_pointer_cast(shared_from_this()), - cloud_collectors_, combine_cloud_handler_, params_.input_topics.size(), params_.timeout_sec); + combine_cloud_handler_, params_.input_topics.size(), params_.timeout_sec); cloud_collectors_.push_back(new_cloud_collector); cloud_collectors_lock.unlock(); @@ -331,6 +331,23 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( } } +void PointCloudConcatenateDataSynchronizerComponent::delete_collector(CloudCollector & cloud_collector) { + // protect cloud collectors list + std::lock_guard cloud_collectors_lock(cloud_collectors_mutex_); + + // change this to something else + auto it = std::find_if( + cloud_collectors_.begin(), cloud_collectors_.end(), + [&cloud_collector](const std::shared_ptr & collector) { return collector.get() == &cloud_collector; }); + if (it != cloud_collectors_.end()) { + cloud_collectors_.erase(it); + } + else { + throw std::runtime_error("Try to delete a cloud_collector that is not in the cloud_collectors"); + } +} + + void PointCloudConcatenateDataSynchronizerComponent::convert_to_xyzirc_cloud( const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr) @@ -445,6 +462,17 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( } } +std::list> PointCloudConcatenateDataSynchronizerComponent::get_cloud_collectors() +{ + return cloud_collectors_; +} + +void PointCloudConcatenateDataSynchronizerComponent::add_cloud_collector(const std::shared_ptr & collector) +{ + cloud_collectors_.push_back(collector); +} + + } // namespace autoware::pointcloud_preprocessor #include diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 7f15599810e0d..d0fbfe87b543c 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -67,9 +67,8 @@ class ConcatenateCloudTest : public ::testing::Test std::dynamic_pointer_cast< autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent>( concatenate_node_->shared_from_this()), - collectors_, combine_cloud_handler_, number_of_pointcloud, timeout_sec); + combine_cloud_handler_, number_of_pointcloud, timeout_sec); - collectors_.push_back(collector_); // Setup TF tf_broadcaster_ = std::make_shared(concatenate_node_); @@ -167,7 +166,6 @@ class ConcatenateCloudTest : public ::testing::Test std::shared_ptr concatenate_node_; - std::list> collectors_; std::shared_ptr combine_cloud_handler_; std::shared_ptr collector_; std::shared_ptr tf_broadcaster_; @@ -445,12 +443,15 @@ TEST_F(ConcatenateCloudTest, TestConcatenateClouds) TEST_F(ConcatenateCloudTest, TestDeleteCollector) { - collector_->delete_collector(); - EXPECT_TRUE(collectors_.empty()); + concatenate_node_->add_cloud_collector(collector_); + concatenate_node_->delete_collector(*collector_); + EXPECT_TRUE(concatenate_node_->get_cloud_collectors().empty()); } TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) { + concatenate_node_->add_cloud_collector(collector_); + rclcpp::Time timestamp(timestamp_seconds, timestamp_nanoseconds, RCL_ROS_TIME); sensor_msgs::msg::PointCloud2 top_pointcloud = generate_pointcloud_msg(true, false, "lidar_top", timestamp); @@ -460,18 +461,20 @@ TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) auto topic_to_cloud_map = collector_->get_topic_to_cloud_map(); EXPECT_EQ(topic_to_cloud_map["lidar_top"], top_pointcloud_ptr); - EXPECT_FALSE(collectors_.empty()); + EXPECT_FALSE(concatenate_node_->get_cloud_collectors().empty()); // Sleep for timeout seconds (200 ms) std::this_thread::sleep_for(std::chrono::milliseconds(200)); rclcpp::spin_some(concatenate_node_); // Collector should concatenate and publish the pointcloud, also delete itself. - EXPECT_TRUE(collectors_.empty()); + EXPECT_TRUE(concatenate_node_->get_cloud_collectors().empty()); } TEST_F(ConcatenateCloudTest, TestProcessMultipleCloud) { + concatenate_node_->add_cloud_collector(collector_); + rclcpp::Time top_timestamp(timestamp_seconds, timestamp_nanoseconds, RCL_ROS_TIME); rclcpp::Time left_timestamp(timestamp_seconds, timestamp_nanoseconds + 40'000'000, RCL_ROS_TIME); rclcpp::Time right_timestamp(timestamp_seconds, timestamp_nanoseconds + 80'000'000, RCL_ROS_TIME); @@ -493,7 +496,7 @@ TEST_F(ConcatenateCloudTest, TestProcessMultipleCloud) collector_->process_pointcloud("lidar_left", left_pointcloud_ptr); collector_->process_pointcloud("lidar_right", right_pointcloud_ptr); - EXPECT_TRUE(collectors_.empty()); + EXPECT_TRUE(concatenate_node_->get_cloud_collectors().empty()); } int main(int argc, char ** argv) From d6c7a481ad495016623b59278727058258c8ace6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 11:30:03 +0000 Subject: [PATCH 069/115] style(pre-commit): autofix --- .../concatenate_data/cloud_collector.hpp | 4 ++-- .../src/concatenate_data/cloud_collector.cpp | 1 - .../concatenate_and_time_sync_node.cpp | 19 +++++++++++-------- .../test/test_concatenate_node_unit.cpp | 1 - 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 7b2b6503be097..bb00f767a3573 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -27,7 +27,7 @@ namespace autoware::pointcloud_preprocessor class PointCloudConcatenateDataSynchronizerComponent; class CombineCloudHandler; -class CloudCollector +class CloudCollector { public: CloudCollector( @@ -42,7 +42,7 @@ class CloudCollector ConcatenatedCloudResult concatenate_pointclouds( std::unordered_map topic_to_cloud_map); - //void delete_collector(); + // void delete_collector(); std::unordered_map get_topic_to_cloud_map(); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index fabb7136f8256..a8224658ec7f8 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -89,7 +89,6 @@ ConcatenatedCloudResult CloudCollector::concatenate_pointclouds( return combine_cloud_handler_->combine_pointclouds(topic_to_cloud_map); } - std::unordered_map CloudCollector::get_topic_to_cloud_map() { diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 0b757ca0bd4ff..578f3e278897c 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -331,23 +331,25 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( } } -void PointCloudConcatenateDataSynchronizerComponent::delete_collector(CloudCollector & cloud_collector) { +void PointCloudConcatenateDataSynchronizerComponent::delete_collector( + CloudCollector & cloud_collector) +{ // protect cloud collectors list std::lock_guard cloud_collectors_lock(cloud_collectors_mutex_); // change this to something else auto it = std::find_if( cloud_collectors_.begin(), cloud_collectors_.end(), - [&cloud_collector](const std::shared_ptr & collector) { return collector.get() == &cloud_collector; }); + [&cloud_collector](const std::shared_ptr & collector) { + return collector.get() == &cloud_collector; + }); if (it != cloud_collectors_.end()) { cloud_collectors_.erase(it); - } - else { + } else { throw std::runtime_error("Try to delete a cloud_collector that is not in the cloud_collectors"); } } - void PointCloudConcatenateDataSynchronizerComponent::convert_to_xyzirc_cloud( const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr) @@ -462,17 +464,18 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( } } -std::list> PointCloudConcatenateDataSynchronizerComponent::get_cloud_collectors() +std::list> +PointCloudConcatenateDataSynchronizerComponent::get_cloud_collectors() { return cloud_collectors_; } -void PointCloudConcatenateDataSynchronizerComponent::add_cloud_collector(const std::shared_ptr & collector) +void PointCloudConcatenateDataSynchronizerComponent::add_cloud_collector( + const std::shared_ptr & collector) { cloud_collectors_.push_back(collector); } - } // namespace autoware::pointcloud_preprocessor #include diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index d0fbfe87b543c..15a17d8f6c9f1 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -69,7 +69,6 @@ class ConcatenateCloudTest : public ::testing::Test concatenate_node_->shared_from_this()), combine_cloud_handler_, number_of_pointcloud, timeout_sec); - // Setup TF tf_broadcaster_ = std::make_shared(concatenate_node_); tf_broadcaster_->sendTransform(generate_static_transform_msgs()); From 481591720940aea841c684f82a9aee9ba0118d27 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 7 Oct 2024 13:13:33 +0900 Subject: [PATCH 070/115] chore: clean code Signed-off-by: vividf --- .../pointcloud_preprocessor/concatenate_data/cloud_collector.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index bb00f767a3573..1cf569df0b98f 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -42,7 +42,6 @@ class CloudCollector ConcatenatedCloudResult concatenate_pointclouds( std::unordered_map topic_to_cloud_map); - // void delete_collector(); std::unordered_map get_topic_to_cloud_map(); From 40fe11ebf057baaa121714260085b75c1e70b0ba Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 7 Oct 2024 14:13:27 +0900 Subject: [PATCH 071/115] chore: add parameters for handling rosbag replay in loops Signed-off-by: vividf --- .../concatenate_and_time_sync_node.param.yaml | 2 ++ .../concatenate_and_time_sync_node.hpp | 2 ++ ...concatenate_and_time_sync_node.schema.json | 27 ++++++++++++++----- .../concatenate_and_time_sync_node.cpp | 16 ++++++++++- .../test/test_concatenate_node_component.py | 2 ++ .../test/test_concatenate_node_unit.cpp | 2 ++ 6 files changed, 43 insertions(+), 8 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml index d99849b532f3e..5f5014d173111 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml @@ -1,6 +1,8 @@ /**: ros__parameters: has_static_tf_only: false + rosbag_replay: true + rosbag_length: 20.0 maximum_queue_size: 5 timeout_sec: 0.2 is_motion_compensated: true diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index a44878a23e2e9..58a576d445e7e 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -68,6 +68,8 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node struct Parameters { bool has_static_tf_only; + bool rosbag_replay; + double rosbag_length; int maximum_queue_size; double timeout_sec; bool is_motion_compensated; diff --git a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json index 022e7f30f8d73..5df447959c728 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json @@ -6,6 +6,22 @@ "concatenate_and_time_sync_node": { "type": "object", "properties": { + "has_static_tf_only": { + "type": "boolean", + "default": false, + "description": "Flag to indicate if only static TF is used." + }, + "rosbag_replay": { + "type": "boolean", + "default": true, + "description": "Flag to indicate whether the user is replaying the rosbag in loops without relaunching the node." + }, + "rosbag_length": { + "type": "boolean", + "default": 20.0, + "minimum": 0.0, + "description": "If rosbag_replay is enabled, this value helps the node determine if the rosbag has started from the beginning again. The value should be set slightly smaller (by 2-3 seconds) than the actual length of the bag." + }, "maximum_queue_size": { "type": "integer", "default": "5", @@ -84,14 +100,12 @@ "default": [0.01, 0.01, 0.01], "minItems": 2, "description": "List of LiDAR timestamp noise windows in seconds. The noise values should be specified in the same order as the input_topics." - }, - "has_static_tf_only": { - "type": "boolean", - "default": false, - "description": "Flag to indicate if only static TF is used." } }, "required": [ + "has_static_tf_only", + "rosbag_replay", + "rosbag_length", "maximum_queue_size", "timeout_sec", "is_motion_compensated", @@ -103,8 +117,7 @@ "input_topics", "output_frame", "lidar_timestamp_offsets", - "lidar_timestamp_noise_window", - "has_static_tf_only" + "lidar_timestamp_noise_window" ] } }, diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 578f3e278897c..beea27d50a184 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -45,6 +45,8 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro // initialize parameters params_.has_static_tf_only = declare_parameter("has_static_tf_only"); + params_.rosbag_replay = declare_parameter("rosbag_replay"); + params_.rosbag_length = declare_parameter("rosbag_length"); params_.maximum_queue_size = static_cast(declare_parameter("maximum_queue_size")); params_.timeout_sec = declare_parameter("timeout_sec"); params_.is_motion_compensated = declare_parameter("is_motion_compensated"); @@ -275,9 +277,21 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( if ( current_concatenate_cloud_timestamp_ < latest_concatenate_cloud_timestamp_ && !params_.publish_previous_but_late_pointcloud) { - drop_previous_but_late_pointcloud_ = true; + // Check if we're in rosbag replay mode and the time difference is close to the rosbag length + if ( + params_.rosbag_replay && + (latest_concatenate_cloud_timestamp_ - current_concatenate_cloud_timestamp_ > + params_.rosbag_length)) { + publish_pointcloud_ = true; // Force publishing in this case + } else { + drop_previous_but_late_pointcloud_ = true; // Otherwise, drop the late pointcloud + } } else { + // Publish pointcloud if timestamps are valid or the condition doesn't apply publish_pointcloud_ = true; + } + + if (publish_pointcloud_) { latest_concatenate_cloud_timestamp_ = current_concatenate_cloud_timestamp_; auto concatenate_pointcloud_output = std::make_unique( *concatenated_cloud_result.concatenate_cloud_ptr); diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index 0aeced645de96..9cf2134703b20 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -85,6 +85,8 @@ def generate_test_description(): parameters=[ { "has_static_tf_only": False, + "rosbag_replay": False, + "rosbag_length": 0.0, "maximum_queue_size": 5, "timeout_sec": TIMEOUT_SEC, "is_motion_compensated": True, diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 15a17d8f6c9f1..b166e878f0f31 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -42,6 +42,8 @@ class ConcatenateCloudTest : public ::testing::Test // They just helps to setup the concatenate node node_options.parameter_overrides( {{"has_static_tf_only", false}, + {"rosbag_replay", false}, + {"rosbag_length", 0.0}, {"maximum_queue_size", 5}, {"timeout_sec", 0.2}, {"is_motion_compensated", true}, From 3433bf0b4aa5171cd676700e1e447d4d306bd4e8 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 7 Oct 2024 18:40:29 +0900 Subject: [PATCH 072/115] chore: fix diagonistic Signed-off-by: vividf --- .../src/concatenate_data/concatenate_and_time_sync_node.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index beea27d50a184..3e1b1758dfa84 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -437,9 +437,9 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( bool topic_miss = false; - int concatenated_cloud_status = 1; // 1 for success, 0 for failure - int cloud_status = 1; // for each lidar's pointcloud + int concatenated_cloud_status = 1; // Status of concatenated cloud, 1: success, 0: failure for (const auto & topic : params_.input_topics) { + int cloud_status = 1; // Status of each lidar's pointcloud if ( diagnostic_topic_to_original_stamp_map_.find(topic) != diagnostic_topic_to_original_stamp_map_.end()) { From 09b8ce384e413c3a16b538a260fb263567a0bdb7 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 21 Oct 2024 15:27:02 +0900 Subject: [PATCH 073/115] chore: reduce copy operation Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.hpp | 2 +- .../src/concatenate_data/cloud_collector.cpp | 2 +- .../src/concatenate_data/concatenate_and_time_sync_node.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 58a576d445e7e..f8b0201f0967e 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -58,7 +58,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node explicit PointCloudConcatenateDataSynchronizerComponent(const rclcpp::NodeOptions & node_options); ~PointCloudConcatenateDataSynchronizerComponent() override = default; void publish_clouds( - ConcatenatedCloudResult concatenated_cloud_result, double reference_timestamp_min, + ConcatenatedCloudResult && concatenated_cloud_result, double reference_timestamp_min, double reference_timestamp_max); void delete_collector(CloudCollector & cloud_collector); std::list> get_cloud_collectors(); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index a8224658ec7f8..60d1e2e4ebc80 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -79,7 +79,7 @@ void CloudCollector::concatenate_callback() auto concatenated_cloud_result = concatenate_pointclouds(topic_to_cloud_map_); ros2_parent_node_->publish_clouds( - concatenated_cloud_result, reference_timestamp_min_, reference_timestamp_max_); + std::move(concatenated_cloud_result), reference_timestamp_min_, reference_timestamp_max_); ros2_parent_node_->delete_collector(*this); } diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 3e1b1758dfa84..fc97980cd8fd7 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -263,7 +263,7 @@ void PointCloudConcatenateDataSynchronizerComponent::odom_callback( } void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( - ConcatenatedCloudResult concatenated_cloud_result, double reference_timestamp_min, + ConcatenatedCloudResult && concatenated_cloud_result, double reference_timestamp_min, double reference_timestamp_max) { // should never come to this state. @@ -294,7 +294,7 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( if (publish_pointcloud_) { latest_concatenate_cloud_timestamp_ = current_concatenate_cloud_timestamp_; auto concatenate_pointcloud_output = std::make_unique( - *concatenated_cloud_result.concatenate_cloud_ptr); + std::move(*concatenated_cloud_result.concatenate_cloud_ptr)); concatenated_cloud_publisher_->publish(std::move(concatenate_pointcloud_output)); // publish transformed raw pointclouds From 782228fb9b1e97b5f066647c37ae357324ad2b71 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 21 Oct 2024 16:08:49 +0900 Subject: [PATCH 074/115] chore: reserve space for concatenated pointcloud Signed-off-by: vividf --- .../combine_cloud_handler.cpp | 51 +++++-------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 9cdc7ad5d0b2f..b8c6e5aa3ae15 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -140,6 +140,18 @@ ConcatenatedCloudResult CombineCloudHandler::combine_pointclouds( std::unordered_map transform_memo; + // Before combining the pointclouds, initialize and reserve space for the concatenated pointcloud + concatenate_cloud_result.concatenate_cloud_ptr = + std::make_shared(); + + // Reserve space based on the total size of the pointcloud data to speed up the concatenation + // process + size_t total_data_size = 0; + for (const auto & [topic, cloud] : topic_to_cloud_map) { + total_data_size += cloud->data.size(); + } + concatenate_cloud_result.concatenate_cloud_ptr->data.reserve(total_data_size); + for (const auto & [topic, cloud] : topic_to_cloud_map) { auto transformed_cloud_ptr = std::make_shared(); managed_tf_buffer_->transformPointcloud(output_frame_, *cloud, *transformed_cloud_ptr); @@ -157,49 +169,10 @@ ConcatenatedCloudResult CombineCloudHandler::combine_pointclouds( transformed_delay_compensated_cloud_ptr = transformed_cloud_ptr; } - // Check if concatenate_cloud_ptr is nullptr, if so initialize it - if (concatenate_cloud_result.concatenate_cloud_ptr == nullptr) { - // Initialize concatenate_cloud_ptr without copying the data - concatenate_cloud_result.concatenate_cloud_ptr = - std::make_shared(); - concatenate_cloud_result.concatenate_cloud_ptr->header = - transformed_delay_compensated_cloud_ptr->header; - concatenate_cloud_result.concatenate_cloud_ptr->height = - transformed_delay_compensated_cloud_ptr->height; - concatenate_cloud_result.concatenate_cloud_ptr->width = - 0; // Will be updated with total points - concatenate_cloud_result.concatenate_cloud_ptr->is_dense = - transformed_delay_compensated_cloud_ptr->is_dense; - concatenate_cloud_result.concatenate_cloud_ptr->is_bigendian = - transformed_delay_compensated_cloud_ptr->is_bigendian; - concatenate_cloud_result.concatenate_cloud_ptr->fields = - transformed_delay_compensated_cloud_ptr->fields; - concatenate_cloud_result.concatenate_cloud_ptr->point_step = - transformed_delay_compensated_cloud_ptr->point_step; - concatenate_cloud_result.concatenate_cloud_ptr->row_step = - 0; // Will be updated after concatenation - concatenate_cloud_result.concatenate_cloud_ptr->data.clear(); - - // Reserve space for the data (assume max points you expect to concatenate) - auto num_of_points = transformed_delay_compensated_cloud_ptr->width * - transformed_delay_compensated_cloud_ptr->height; - concatenate_cloud_result.concatenate_cloud_ptr->data.reserve( - num_of_points * concatenate_cloud_result.concatenate_cloud_ptr->point_step); - } - - // Concatenate the current pointcloud to the concatenated cloud pcl::concatenatePointCloud( *concatenate_cloud_result.concatenate_cloud_ptr, *transformed_delay_compensated_cloud_ptr, *concatenate_cloud_result.concatenate_cloud_ptr); - // Update width and row_step to reflect the new size - concatenate_cloud_result.concatenate_cloud_ptr->width = - concatenate_cloud_result.concatenate_cloud_ptr->data.size() / - concatenate_cloud_result.concatenate_cloud_ptr->point_step; - concatenate_cloud_result.concatenate_cloud_ptr->row_step = - concatenate_cloud_result.concatenate_cloud_ptr->width * - concatenate_cloud_result.concatenate_cloud_ptr->point_step; - if (publish_synchronized_pointcloud_) { if (!concatenate_cloud_result.topic_to_transformed_cloud_map) { // Initialize the map if it is not present From 360611489426baac301ad48a190ce4ef7fdac5f2 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 21 Oct 2024 16:12:35 +0900 Subject: [PATCH 075/115] chore: fix clangd error Signed-off-by: vividf --- .../src/concatenate_data/combine_cloud_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index b8c6e5aa3ae15..bc6a5b6bc81d3 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -133,7 +133,7 @@ ConcatenatedCloudResult CombineCloudHandler::combine_pointclouds( std::vector pc_stamps; for (const auto & [topic, cloud] : topic_to_cloud_map) { - pc_stamps.emplace_back(rclcpp::Time(cloud->header.stamp)); + pc_stamps.emplace_back(cloud->header.stamp); } std::sort(pc_stamps.begin(), pc_stamps.end(), std::greater()); const auto oldest_stamp = pc_stamps.back(); From 6ed75375b58f85d6c153b49c5e14ab3aaba52bd8 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 21 Oct 2024 16:32:41 +0900 Subject: [PATCH 076/115] chore: fix pipeline latency Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index fc97980cd8fd7..1a3b9d7a03a7e 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -334,11 +334,9 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( "debug/processing_time_ms", processing_time_ms); for (const auto & [topic, stamp] : concatenated_cloud_result.topic_to_original_stamp_map) { - const auto pipeline_latency_ms = - std::chrono::duration( - std::chrono::duration_cast( - std::chrono::duration(this->get_clock()->now().nanoseconds() - stamp * 1e9))) - .count(); + const auto pipeline_latency_ms = std::chrono::duration( + this->get_clock()->now().nanoseconds() - stamp * 1e9) + .count(); debug_publisher_->publish( "debug" + topic + "/pipeline_latency_ms", pipeline_latency_ms); } From 0248a24cdb98f1cd9d79c2e7c812dbf99d5d4b82 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 21 Oct 2024 20:59:05 +0900 Subject: [PATCH 077/115] chore: add debug mode Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 4 ++- .../concatenate_and_time_sync_node.hpp | 1 + .../src/concatenate_data/cloud_collector.cpp | 29 +++++++++++++++++-- .../concatenate_and_time_sync_node.cpp | 11 ++++++- .../test/test_concatenate_node_component.py | 1 + .../test/test_concatenate_node_unit.cpp | 8 +++-- 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 1cf569df0b98f..b90fd4a526632 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -32,7 +32,8 @@ class CloudCollector public: CloudCollector( std::shared_ptr && ros2_parent_node, - std::shared_ptr & combine_cloud_handler, int num_of_clouds, double time); + std::shared_ptr & combine_cloud_handler, int num_of_clouds, + double timeout_sec, bool debug_mode); void set_reference_timestamp(double timestamp, double noise_window); std::tuple get_reference_timestamp_boundary(); @@ -55,6 +56,7 @@ class CloudCollector double timeout_sec_; double reference_timestamp_min_{0.0}; double reference_timestamp_max_{0.0}; + bool debug_mode_; }; } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index f8b0201f0967e..2438725f867e0 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -67,6 +67,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node private: struct Parameters { + bool debug_mode; bool has_static_tf_only; bool rosbag_replay; double rosbag_length; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 60d1e2e4ebc80..5b87fda0ee0e2 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -27,11 +27,12 @@ namespace autoware::pointcloud_preprocessor CloudCollector::CloudCollector( std::shared_ptr && ros2_parent_node, std::shared_ptr & combine_cloud_handler, int num_of_clouds, - double timeout_sec) + double timeout_sec, bool debug_mode) : ros2_parent_node_(std::move(ros2_parent_node)), combine_cloud_handler_(combine_cloud_handler), num_of_clouds_(num_of_clouds), - timeout_sec_(timeout_sec) + timeout_sec_(timeout_sec), + debug_mode_(debug_mode) { const auto period_ns = std::chrono::duration_cast( std::chrono::duration(timeout_sec_)); @@ -74,12 +75,36 @@ void CloudCollector::concatenate_callback() { // All pointclouds are received or the timer has timed out, cancel the timer and concatenate the // pointclouds in the collector. + auto time_until_trigger = timer_->time_until_trigger(); timer_->cancel(); auto concatenated_cloud_result = concatenate_pointclouds(topic_to_cloud_map_); + if (debug_mode_) { + std::stringstream log_stream; + log_stream << "Collector's concatenate callback time: " + << ros2_parent_node_->get_clock()->now().seconds() << " seconds\n"; + + log_stream << "Collector's reference time min: " << reference_timestamp_min_ + << " to max: " << reference_timestamp_max_ << " seconds\n"; + + log_stream << "Time until trigger: " << (time_until_trigger.count() / 1e9) << " seconds\n"; + + log_stream << "Pointclouds: ["; + for (auto it = concatenated_cloud_result.topic_to_original_stamp_map.begin(); + it != concatenated_cloud_result.topic_to_original_stamp_map.end(); ++it) { + log_stream << "[" << it->first << ", " << it->second << "]"; + if (std::next(it) != concatenated_cloud_result.topic_to_original_stamp_map.end()) { + log_stream << ", "; + } + } + log_stream << "]\n"; + + RCLCPP_INFO(ros2_parent_node_->get_logger(), "%s", log_stream.str().c_str()); + } ros2_parent_node_->publish_clouds( std::move(concatenated_cloud_result), reference_timestamp_min_, reference_timestamp_max_); + ros2_parent_node_->delete_collector(*this); } diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 1a3b9d7a03a7e..a30c0a0b13f1e 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -44,6 +44,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro stop_watch_ptr_->tic("processing_time"); // initialize parameters + params_.debug_mode = declare_parameter("debug_mode"); params_.has_static_tf_only = declare_parameter("has_static_tf_only"); params_.rosbag_replay = declare_parameter("rosbag_replay"); params_.rosbag_length = declare_parameter("rosbag_length"); @@ -200,6 +201,14 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( return; } + if (params_.debug_mode) { + RCLCPP_INFO( + this->get_logger(), " pointcloud %s timestamp: %lf arrive time: %lf seconds, latency: %lf", + topic_name.c_str(), rclcpp::Time(input_ptr->header.stamp).seconds(), + this->get_clock()->now().seconds(), + this->get_clock()->now().seconds() - rclcpp::Time(input_ptr->header.stamp).seconds()); + } + sensor_msgs::msg::PointCloud2::SharedPtr xyzirc_input_ptr(new sensor_msgs::msg::PointCloud2()); auto input = std::make_shared(*input_ptr); if (input->data.empty()) { @@ -239,7 +248,7 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( if (!collector_found) { auto new_cloud_collector = std::make_shared( std::dynamic_pointer_cast(shared_from_this()), - combine_cloud_handler_, params_.input_topics.size(), params_.timeout_sec); + combine_cloud_handler_, params_.input_topics.size(), params_.timeout_sec, params_.debug_mode); cloud_collectors_.push_back(new_cloud_collector); cloud_collectors_lock.unlock(); diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index 9cf2134703b20..d346a80534c8f 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -84,6 +84,7 @@ def generate_test_description(): ], parameters=[ { + "debug_mode": True, "has_static_tf_only": False, "rosbag_replay": False, "rosbag_length": 0.0, diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index b166e878f0f31..8f58e1c32e02e 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -41,7 +41,8 @@ class ConcatenateCloudTest : public ::testing::Test // Instead of "input_topics", other parameters are not used. // They just helps to setup the concatenate node node_options.parameter_overrides( - {{"has_static_tf_only", false}, + {{"debug_mode", true}, + {"has_static_tf_only", false}, {"rosbag_replay", false}, {"rosbag_length", 0.0}, {"maximum_queue_size", 5}, @@ -69,7 +70,7 @@ class ConcatenateCloudTest : public ::testing::Test std::dynamic_pointer_cast< autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent>( concatenate_node_->shared_from_this()), - combine_cloud_handler_, number_of_pointcloud, timeout_sec); + combine_cloud_handler_, number_of_pointcloud, timeout_sec, collector_debug_mode); // Setup TF tf_broadcaster_ = std::make_shared(concatenate_node_); @@ -177,7 +178,8 @@ class ConcatenateCloudTest : public ::testing::Test static constexpr float standard_tolerance{1e-4}; static constexpr int number_of_pointcloud{3}; static constexpr float timeout_sec{0.2}; - bool debug_{false}; + static constexpr bool collector_debug_mode{true}; // For showing collector information + bool debug_{false}; // For the Unit test }; //////////////////////////////// Test combine_cloud_handler //////////////////////////////// From 76d3b4c248b48968b756d2ccd6c97ff1941316c5 Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 22 Oct 2024 10:28:49 +0900 Subject: [PATCH 078/115] chore: refactor convert_to_xyzirc_cloud function Signed-off-by: vividf --- .../combine_cloud_handler.hpp | 7 +++ .../concatenate_and_time_sync_node.hpp | 8 --- .../combine_cloud_handler.cpp | 63 ++++++++++++++++++- .../concatenate_and_time_sync_node.cpp | 62 +----------------- 4 files changed, 70 insertions(+), 70 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index d8a363c63be21..c56b65ebabcd1 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -22,6 +22,7 @@ #include // ROS includes +#include "autoware_point_types/types.hpp" #include #include @@ -44,6 +45,8 @@ namespace autoware::pointcloud_preprocessor { +using autoware_point_types::PointXYZIRC; +using point_cloud_msg_wrapper::PointCloud2Modifier; struct ConcatenatedCloudResult { @@ -77,6 +80,10 @@ class CombineCloudHandler } }; + static void convert_to_xyzirc_cloud( + const sensor_msgs::msg::PointCloud2::SharedPtr & input_cloud, + sensor_msgs::msg::PointCloud2::SharedPtr & xyzirc_cloud); + void correct_pointcloud_motion( const std::shared_ptr & transformed_cloud_ptr, const std::vector & pc_stamps, diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 2438725f867e0..3e6febbdbe844 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -22,7 +22,6 @@ #include // ROS includes -#include "autoware_point_types/types.hpp" #include "cloud_collector.hpp" #include "combine_cloud_handler.hpp" @@ -48,10 +47,6 @@ namespace autoware::pointcloud_preprocessor { - -using autoware_point_types::PointXYZIRC; -using point_cloud_msg_wrapper::PointCloud2Modifier; - class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node { public: @@ -125,9 +120,6 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node void check_concat_status(diagnostic_updater::DiagnosticStatusWrapper & stat); std::string replace_sync_topic_name_postfix( const std::string & original_topic_name, const std::string & postfix); - static void convert_to_xyzirc_cloud( - const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, - sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr); }; } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index bc6a5b6bc81d3..6888f45ce53c1 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -100,6 +100,61 @@ std::deque CombineCloudHandler::get_twist_queu return twist_queue_; } +void CombineCloudHandler::convert_to_xyzirc_cloud( + const sensor_msgs::msg::PointCloud2::SharedPtr & input_cloud, + sensor_msgs::msg::PointCloud2::SharedPtr & xyzirc_cloud) +{ + xyzirc_cloud->header = input_cloud->header; + + PointCloud2Modifier output_modifier{ + *xyzirc_cloud, input_cloud->header.frame_id}; + output_modifier.reserve(input_cloud->width); + + bool has_valid_intensity = + std::any_of(input_cloud->fields.begin(), input_cloud->fields.end(), [](const auto & field) { + return field.name == "intensity" && field.datatype == sensor_msgs::msg::PointField::UINT8; + }); + + bool has_valid_return_type = + std::any_of(input_cloud->fields.begin(), input_cloud->fields.end(), [](const auto & field) { + return field.name == "return_type" && field.datatype == sensor_msgs::msg::PointField::UINT8; + }); + + bool has_valid_channel = + std::any_of(input_cloud->fields.begin(), input_cloud->fields.end(), [](const auto & field) { + return field.name == "channel" && field.datatype == sensor_msgs::msg::PointField::UINT16; + }); + + sensor_msgs::PointCloud2Iterator it_x(*input_cloud, "x"); + sensor_msgs::PointCloud2Iterator it_y(*input_cloud, "y"); + sensor_msgs::PointCloud2Iterator it_z(*input_cloud, "z"); + + if (has_valid_intensity && has_valid_return_type && has_valid_channel) { + sensor_msgs::PointCloud2Iterator it_i(*input_cloud, "intensity"); + sensor_msgs::PointCloud2Iterator it_r(*input_cloud, "return_type"); + sensor_msgs::PointCloud2Iterator it_c(*input_cloud, "channel"); + + for (; it_x != it_x.end(); ++it_x, ++it_y, ++it_z, ++it_i, ++it_r, ++it_c) { + PointXYZIRC point; + point.x = *it_x; + point.y = *it_y; + point.z = *it_z; + point.intensity = *it_i; + point.return_type = *it_r; + point.channel = *it_c; + output_modifier.push_back(std::move(point)); + } + } else { + for (; it_x != it_x.end(); ++it_x, ++it_y, ++it_z) { + PointXYZIRC point; + point.x = *it_x; + point.y = *it_y; + point.z = *it_z; + output_modifier.push_back(std::move(point)); + } + } +} + void CombineCloudHandler::correct_pointcloud_motion( const std::shared_ptr & transformed_cloud_ptr, const std::vector & pc_stamps, @@ -153,8 +208,14 @@ ConcatenatedCloudResult CombineCloudHandler::combine_pointclouds( concatenate_cloud_result.concatenate_cloud_ptr->data.reserve(total_data_size); for (const auto & [topic, cloud] : topic_to_cloud_map) { + // convert to XYZIRC pointcloud if pointcloud is not empty + // auto xyzirc_cloud = std::make_shared(); + + sensor_msgs::msg::PointCloud2::SharedPtr xyzirc_cloud(new sensor_msgs::msg::PointCloud2()); + convert_to_xyzirc_cloud(cloud, xyzirc_cloud); + auto transformed_cloud_ptr = std::make_shared(); - managed_tf_buffer_->transformPointcloud(output_frame_, *cloud, *transformed_cloud_ptr); + managed_tf_buffer_->transformPointcloud(output_frame_, *xyzirc_cloud, *transformed_cloud_ptr); concatenate_cloud_result.topic_to_original_stamp_map[topic] = rclcpp::Time(cloud->header.stamp).seconds(); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index a30c0a0b13f1e..f1ed64e03df51 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -209,17 +209,12 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( this->get_clock()->now().seconds() - rclcpp::Time(input_ptr->header.stamp).seconds()); } - sensor_msgs::msg::PointCloud2::SharedPtr xyzirc_input_ptr(new sensor_msgs::msg::PointCloud2()); - auto input = std::make_shared(*input_ptr); - if (input->data.empty()) { + if (input_ptr->data.empty()) { RCLCPP_WARN_STREAM_THROTTLE( this->get_logger(), *this->get_clock(), 1000, "Empty sensor points!"); return; } - // convert to XYZIRC pointcloud if pointcloud is not empty - convert_to_xyzirc_cloud(input, xyzirc_input_ptr); - // protect cloud collectors list std::unique_lock cloud_collectors_lock(cloud_collectors_mutex_); @@ -371,61 +366,6 @@ void PointCloudConcatenateDataSynchronizerComponent::delete_collector( } } -void PointCloudConcatenateDataSynchronizerComponent::convert_to_xyzirc_cloud( - const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, - sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr) -{ - output_ptr->header = input_ptr->header; - - PointCloud2Modifier output_modifier{ - *output_ptr, input_ptr->header.frame_id}; - output_modifier.reserve(input_ptr->width); - - bool has_valid_intensity = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { - return field.name == "intensity" && field.datatype == sensor_msgs::msg::PointField::UINT8; - }); - - bool has_valid_return_type = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { - return field.name == "return_type" && field.datatype == sensor_msgs::msg::PointField::UINT8; - }); - - bool has_valid_channel = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { - return field.name == "channel" && field.datatype == sensor_msgs::msg::PointField::UINT16; - }); - - sensor_msgs::PointCloud2Iterator it_x(*input_ptr, "x"); - sensor_msgs::PointCloud2Iterator it_y(*input_ptr, "y"); - sensor_msgs::PointCloud2Iterator it_z(*input_ptr, "z"); - - if (has_valid_intensity && has_valid_return_type && has_valid_channel) { - sensor_msgs::PointCloud2Iterator it_i(*input_ptr, "intensity"); - sensor_msgs::PointCloud2Iterator it_r(*input_ptr, "return_type"); - sensor_msgs::PointCloud2Iterator it_c(*input_ptr, "channel"); - - for (; it_x != it_x.end(); ++it_x, ++it_y, ++it_z, ++it_i, ++it_r, ++it_c) { - PointXYZIRC point; - point.x = *it_x; - point.y = *it_y; - point.z = *it_z; - point.intensity = *it_i; - point.return_type = *it_r; - point.channel = *it_c; - output_modifier.push_back(std::move(point)); - } - } else { - for (; it_x != it_x.end(); ++it_x, ++it_y, ++it_z) { - PointXYZIRC point; - point.x = *it_x; - point.y = *it_y; - point.z = *it_z; - output_modifier.push_back(std::move(point)); - } - } -} - std::string PointCloudConcatenateDataSynchronizerComponent::format_timestamp(double timestamp) { std::ostringstream oss; From e709d37756d342601d7c795487ef43bdba9cb159 Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 22 Oct 2024 10:35:17 +0900 Subject: [PATCH 079/115] chore: fix json schema Signed-off-by: vividf --- .../config/concatenate_and_time_sync_node.param.yaml | 1 + .../schema/concatenate_and_time_sync_node.schema.json | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml index 5f5014d173111..20e0bb057cc4d 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml @@ -1,5 +1,6 @@ /**: ros__parameters: + debug_mode: false has_static_tf_only: false rosbag_replay: true rosbag_length: 20.0 diff --git a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json index 5df447959c728..98196262f8339 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json @@ -6,6 +6,11 @@ "concatenate_and_time_sync_node": { "type": "object", "properties": { + "debug_mode": { + "type": "boolean", + "default": false, + "description": "Flag to enables debug mode to display additional logging information." + }, "has_static_tf_only": { "type": "boolean", "default": false, @@ -17,7 +22,7 @@ "description": "Flag to indicate whether the user is replaying the rosbag in loops without relaunching the node." }, "rosbag_length": { - "type": "boolean", + "type": "number", "default": 20.0, "minimum": 0.0, "description": "If rosbag_replay is enabled, this value helps the node determine if the rosbag has started from the beginning again. The value should be set slightly smaller (by 2-3 seconds) than the actual length of the bag." @@ -103,6 +108,7 @@ } }, "required": [ + "debug_mode", "has_static_tf_only", "rosbag_replay", "rosbag_length", From fcdb98908b098a13098ad624cac5870397abfcc4 Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 22 Oct 2024 11:01:10 +0900 Subject: [PATCH 080/115] chore: fix logging output Signed-off-by: vividf --- .../src/concatenate_data/cloud_collector.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 5b87fda0ee0e2..307927f2eebec 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -81,6 +81,7 @@ void CloudCollector::concatenate_callback() auto concatenated_cloud_result = concatenate_pointclouds(topic_to_cloud_map_); if (debug_mode_) { std::stringstream log_stream; + log_stream << std::fixed << std::setprecision(6); log_stream << "Collector's concatenate callback time: " << ros2_parent_node_->get_clock()->now().seconds() << " seconds\n"; From 460b467ecb2de51e7639476b2dd514992df15cde Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 22 Oct 2024 16:13:34 +0900 Subject: [PATCH 081/115] chore: fix the output order of the debug mode Signed-off-by: vividf --- .../src/concatenate_data/cloud_collector.cpp | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 307927f2eebec..b0a7f746be778 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -73,13 +73,8 @@ void CloudCollector::process_pointcloud( void CloudCollector::concatenate_callback() { - // All pointclouds are received or the timer has timed out, cancel the timer and concatenate the - // pointclouds in the collector. - auto time_until_trigger = timer_->time_until_trigger(); - timer_->cancel(); - - auto concatenated_cloud_result = concatenate_pointclouds(topic_to_cloud_map_); if (debug_mode_) { + auto time_until_trigger = timer_->time_until_trigger(); std::stringstream log_stream; log_stream << std::fixed << std::setprecision(6); log_stream << "Collector's concatenate callback time: " @@ -91,18 +86,24 @@ void CloudCollector::concatenate_callback() log_stream << "Time until trigger: " << (time_until_trigger.count() / 1e9) << " seconds\n"; log_stream << "Pointclouds: ["; - for (auto it = concatenated_cloud_result.topic_to_original_stamp_map.begin(); - it != concatenated_cloud_result.topic_to_original_stamp_map.end(); ++it) { - log_stream << "[" << it->first << ", " << it->second << "]"; - if (std::next(it) != concatenated_cloud_result.topic_to_original_stamp_map.end()) { - log_stream << ", "; - } + std::string separator = ""; + for (const auto & [topic, cloud] : topic_to_cloud_map_) { + log_stream << separator; + log_stream << "[" << topic << ", " << rclcpp::Time(cloud->header.stamp).seconds() << "]"; + separator = ", "; } + log_stream << "]\n"; RCLCPP_INFO(ros2_parent_node_->get_logger(), "%s", log_stream.str().c_str()); } + // All pointclouds are received or the timer has timed out, cancel the timer and concatenate the + // pointclouds in the collector. + timer_->cancel(); + + auto concatenated_cloud_result = concatenate_pointclouds(topic_to_cloud_map_); + ros2_parent_node_->publish_clouds( std::move(concatenated_cloud_result), reference_timestamp_min_, reference_timestamp_max_); From 798cbd6e6a67f2536b9af52225e7af29f381c18d Mon Sep 17 00:00:00 2001 From: vividf Date: Wed, 23 Oct 2024 21:44:39 +0900 Subject: [PATCH 082/115] chore: fix pipeline latency output Signed-off-by: vividf --- .../src/concatenate_data/concatenate_and_time_sync_node.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index f1ed64e03df51..569a15d75c23b 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -338,9 +338,7 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( "debug/processing_time_ms", processing_time_ms); for (const auto & [topic, stamp] : concatenated_cloud_result.topic_to_original_stamp_map) { - const auto pipeline_latency_ms = std::chrono::duration( - this->get_clock()->now().nanoseconds() - stamp * 1e9) - .count(); + const auto pipeline_latency_ms = (this->get_clock()->now().seconds() - stamp) * 1000; debug_publisher_->publish( "debug" + topic + "/pipeline_latency_ms", pipeline_latency_ms); } From a2e8b77be9bcdce4e139cd42e26bc7cca519850b Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 24 Oct 2024 17:46:31 +0900 Subject: [PATCH 083/115] chore: clean code Signed-off-by: vividf --- .../config/concatenate_and_time_sync_node.param.yaml | 2 +- .../src/concatenate_data/combine_cloud_handler.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml index 20e0bb057cc4d..48a8467cbfe72 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml @@ -2,7 +2,7 @@ ros__parameters: debug_mode: false has_static_tf_only: false - rosbag_replay: true + rosbag_replay: false rosbag_length: 20.0 maximum_queue_size: 5 timeout_sec: 0.2 diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 6888f45ce53c1..1693f4e2972e6 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -209,9 +209,7 @@ ConcatenatedCloudResult CombineCloudHandler::combine_pointclouds( for (const auto & [topic, cloud] : topic_to_cloud_map) { // convert to XYZIRC pointcloud if pointcloud is not empty - // auto xyzirc_cloud = std::make_shared(); - - sensor_msgs::msg::PointCloud2::SharedPtr xyzirc_cloud(new sensor_msgs::msg::PointCloud2()); + auto xyzirc_cloud = std::make_shared(); convert_to_xyzirc_cloud(cloud, xyzirc_cloud); auto transformed_cloud_ptr = std::make_shared(); From 2562d6ec3debf5d865d6e31bbec7b017931d8102 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 24 Oct 2024 18:08:25 +0900 Subject: [PATCH 084/115] chore: set some parameters to false in testing Signed-off-by: vividf --- .../test/test_concatenate_node_component.py | 6 ++++-- .../test/test_concatenate_node_unit.cpp | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index d346a80534c8f..3ea2948e393fc 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -58,7 +58,6 @@ TIMESTAMP_NOISE = 0.01 # 10 ms NUM_OF_POINTS = 3 -DEBUG = False MILLISECONDS = 1000000 @@ -68,6 +67,9 @@ GLOBAL_SECONDS = 10 GLOBAL_NANOSECONDS = 100000000 +# Set to True if you want to check the output of the component tests. +DEBUG = False + @pytest.mark.launch_test def generate_test_description(): @@ -84,7 +86,7 @@ def generate_test_description(): ], parameters=[ { - "debug_mode": True, + "debug_mode": False, "has_static_tf_only": False, "rosbag_replay": False, "rosbag_length": 0.0, diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 8f58e1c32e02e..bd22376b1c564 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -41,7 +41,7 @@ class ConcatenateCloudTest : public ::testing::Test // Instead of "input_topics", other parameters are not used. // They just helps to setup the concatenate node node_options.parameter_overrides( - {{"debug_mode", true}, + {{"debug_mode", false}, {"has_static_tf_only", false}, {"rosbag_replay", false}, {"rosbag_length", 0.0}, @@ -178,8 +178,8 @@ class ConcatenateCloudTest : public ::testing::Test static constexpr float standard_tolerance{1e-4}; static constexpr int number_of_pointcloud{3}; static constexpr float timeout_sec{0.2}; - static constexpr bool collector_debug_mode{true}; // For showing collector information - bool debug_{false}; // For the Unit test + static constexpr bool collector_debug_mode{false}; // For showing collector information + bool debug_{false}; // For the Unit test }; //////////////////////////////// Test combine_cloud_handler //////////////////////////////// From a970f795a15949be8c4f94f921fd57002bffa8d9 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 24 Oct 2024 18:13:07 +0900 Subject: [PATCH 085/115] chore: fix default value for schema Signed-off-by: vividf --- .../schema/concatenate_and_time_sync_node.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json index 98196262f8339..581e540b43e02 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json @@ -18,7 +18,7 @@ }, "rosbag_replay": { "type": "boolean", - "default": true, + "default": false, "description": "Flag to indicate whether the user is replaying the rosbag in loops without relaunching the node." }, "rosbag_length": { From 4e39bbcdbc04e6e76b221140ff760073ce801cb6 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 28 Oct 2024 12:41:23 +0900 Subject: [PATCH 086/115] chore: fix diagnostic msgs Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 569a15d75c23b..219f391b58b22 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -403,12 +403,17 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( int8_t level = diagnostic_msgs::msg::DiagnosticStatus::OK; std::string message = "Concatenated pointcloud is published and include all topics"; - if (topic_miss) { + if (topic_miss && drop_previous_but_late_pointcloud_) { level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; - message = "Concatenated pointcloud is published but miss some topics"; + message = + "Concatenated pointcloud is missing some topics and is not published because it arrived " + "too late."; } else if (drop_previous_but_late_pointcloud_) { level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; message = "Concatenated pointcloud is not published as it is too late"; + } else if (topic_miss) { + level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; + message = "Concatenated pointcloud is published but miss some topics"; } stat.summary(level, message); From afa000d15a36542e0b180908ee0c3f26b365262f Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 29 Oct 2024 14:48:56 +0900 Subject: [PATCH 087/115] chore: fix parameter for sample ros bag Signed-off-by: vividf --- .../config/concatenate_and_time_sync_node.param.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml index 48a8467cbfe72..c5bd139e1e962 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml @@ -13,10 +13,10 @@ synchronized_pointcloud_postfix: pointcloud input_twist_topic_type: twist input_topics: [ - "/sensing/lidar/left/pointcloud_before_sync", "/sensing/lidar/right/pointcloud_before_sync", - "/sensing/lidar/top/pointcloud_before_sync" + "/sensing/lidar/top/pointcloud_before_sync", + "/sensing/lidar/left/pointcloud_before_sync", ] output_frame: base_link - lidar_timestamp_offsets: [0.0, 0.04, 0.08] + lidar_timestamp_offsets: [0.0, 0.015, 0.016] lidar_timestamp_noise_window: [0.01, 0.01, 0.01] From 3123849d4b69b30eb6ef597830e7a2a289577da2 Mon Sep 17 00:00:00 2001 From: vividf Date: Wed, 13 Nov 2024 14:21:37 +0900 Subject: [PATCH 088/115] chore: update readme Signed-off-by: vividf --- .../docs/concatenate-data.md | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index d558fa4ba98d1..e0d114b5a4f23 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -57,7 +57,25 @@ When network issues occur or when point clouds experience delays in the previous #### lidar_timestamp_offsets -Since different vehicles have varied designs for LiDAR scanning, the timestamps of each LiDAR may differ. Users need to know the offsets between each LiDAR and set the values in `lidar_timestamp_offsets`. For instance, if there are three LiDARs (left, right, top), and the timestamps for the left, right, and top point clouds are 0.01, 0.05, and 0.09 seconds respectively, the parameters should be set as [0.0, 0.04, 0.08]. This reflects the timestamp differences between the current point cloud and the point cloud with the earliest timestamp. Note that the order of the `lidar_timestamp_offsets` corresponds to the order of the `input_topics`. +Since different vehicles have varied designs for LiDAR scanning, the timestamps of each LiDAR may differ. Users need to know the offsets between each LiDAR and set the values in `lidar_timestamp_offsets`. + +To monitor the timestamps of each LiDAR, run the following command: + +```bash +ros2 topic echo "pointcloud_topic" --field header +``` + +The timestamps should increase steadily by approximately 100 ms, as per the Autoware default. You should see output like this: + +```bash +nanosec: 156260951 +nanosec: 257009560 +nanosec: 355444581 +``` + +This pattern indicates a LiDAR timestamp is 0.05. + +If there are three LiDARs (left, right, top), and the timestamps for the left, right, and top point clouds are `0.01`, `0.05`, and `0.09` seconds respectively, the parameters should be set as [0.0, 0.04, 0.08]. This reflects the timestamp differences between the current point cloud and the point cloud with the earliest timestamp. Note that the order of the `lidar_timestamp_offsets` corresponds to the order of the `input_topics`. The figure below demonstrates how `lidar_timestamp_offsets` works with `concatenate_and_time_sync_node`. From e1150716bb6680fe5309b22d7fc03935b8d4cf60 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 22 Nov 2024 18:53:39 +0900 Subject: [PATCH 089/115] chore: fix empty pointcloud Signed-off-by: vividf --- .../concatenate_and_time_sync_node.hpp | 1 + .../concatenate_and_time_sync_node.cpp | 35 ++++++++++++------- .../test/test_concatenate_node_component.py | 4 +-- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 3e6febbdbe844..34309473e021b 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -84,6 +84,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node double latest_concatenate_cloud_timestamp_{0.0}; bool drop_previous_but_late_pointcloud_{false}; bool publish_pointcloud_{false}; + bool is_concatenated_cloud_empty_{false}; double diagnostic_reference_timestamp_min_{0.0}; double diagnostic_reference_timestamp_max_{0.0}; std::unordered_map diagnostic_topic_to_original_stamp_map_; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 219f391b58b22..dacab6f35fe2d 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -212,7 +212,6 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( if (input_ptr->data.empty()) { RCLCPP_WARN_STREAM_THROTTLE( this->get_logger(), *this->get_clock(), 1000, "Empty sensor points!"); - return; } // protect cloud collectors list @@ -275,6 +274,11 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( RCLCPP_ERROR(this->get_logger(), "Concatenate cloud is a nullptr."); return; } + if (concatenated_cloud_result.concatenate_cloud_ptr == nullptr) { + RCLCPP_WARN(this->get_logger(), "Concatenate cloud is an empty pointcloud."); + is_concatenated_cloud_empty_ = true; + } + current_concatenate_cloud_timestamp_ = rclcpp::Time(concatenated_cloud_result.concatenate_cloud_ptr->header.stamp).seconds(); @@ -403,17 +407,24 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( int8_t level = diagnostic_msgs::msg::DiagnosticStatus::OK; std::string message = "Concatenated pointcloud is published and include all topics"; - if (topic_miss && drop_previous_but_late_pointcloud_) { - level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; - message = - "Concatenated pointcloud is missing some topics and is not published because it arrived " - "too late."; - } else if (drop_previous_but_late_pointcloud_) { - level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; - message = "Concatenated pointcloud is not published as it is too late"; - } else if (topic_miss) { - level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; - message = "Concatenated pointcloud is published but miss some topics"; + if (drop_previous_but_late_pointcloud_) { + if (topic_miss) { + level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; + message = + "Concatenated pointcloud is missing some topics and is not published because it arrived " + "too late"; + } else { + level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; + message = "Concatenated pointcloud is not published as it is too late"; + } + } else { + if (is_concatenated_cloud_empty_) { + level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; + message = "Concatenated pointcloud is empty"; + } else if (topic_miss) { + level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; + message = "Concatenated pointcloud is published but miss some topics"; + } } stat.summary(level, message); diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index 3ea2948e393fc..12e5eb6313800 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -556,7 +556,7 @@ def test_4_abnormal_null_pointcloud_and_drop(self): """Test the abnormal situation when a pointcloud is empty and other pointclouds are dropped. This can test that - 1. The concatenate node ignore empty pointcloud and do not publish any pointcloud. + 1. The concatenate node publish an empty pointcloud. """ time.sleep(1) global GLOBAL_SECONDS @@ -585,7 +585,7 @@ def test_4_abnormal_null_pointcloud_and_drop(self): self.assertEqual( len(self.msg_buffer), - 0, + 1, "The number of concatenate pointcloud has different number as expected.", ) From 16d1bce1712652dcfa697f4ba0398e55b9adfeaa Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 2 Dec 2024 11:25:42 +0900 Subject: [PATCH 090/115] chore: remove duplicated logic Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index dacab6f35fe2d..58d2173087b9a 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -269,13 +269,8 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( ConcatenatedCloudResult && concatenated_cloud_result, double reference_timestamp_min, double reference_timestamp_max) { - // should never come to this state. if (concatenated_cloud_result.concatenate_cloud_ptr == nullptr) { - RCLCPP_ERROR(this->get_logger(), "Concatenate cloud is a nullptr."); - return; - } - if (concatenated_cloud_result.concatenate_cloud_ptr == nullptr) { - RCLCPP_WARN(this->get_logger(), "Concatenate cloud is an empty pointcloud."); + RCLCPP_ERROR(this->get_logger(), "Concatenated cloud is an empty pointcloud."); is_concatenated_cloud_empty_ = true; } From 8d7d1e8e71f16189ed42ff780e93e585ab32c18a Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 2 Dec 2024 11:53:06 +0900 Subject: [PATCH 091/115] chore: fix logic for handling empty pointcloud Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 58d2173087b9a..08bca6ef5eb50 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -269,11 +269,19 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( ConcatenatedCloudResult && concatenated_cloud_result, double reference_timestamp_min, double reference_timestamp_max) { + // should never come to this state. if (concatenated_cloud_result.concatenate_cloud_ptr == nullptr) { + RCLCPP_ERROR(this->get_logger(), "Concatenated cloud is a nullptr."); + return; + } + + if (concatenated_cloud_result.concatenate_cloud_ptr->data.empty()) { RCLCPP_ERROR(this->get_logger(), "Concatenated cloud is an empty pointcloud."); is_concatenated_cloud_empty_ = true; } + std::cout << "is_concatenated_cloud_empty_: " << is_concatenated_cloud_empty_ << std::endl; + current_concatenate_cloud_timestamp_ = rclcpp::Time(concatenated_cloud_result.concatenate_cloud_ptr->header.stamp).seconds(); @@ -426,6 +434,7 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( publish_pointcloud_ = false; drop_previous_but_late_pointcloud_ = false; + is_concatenated_cloud_empty_ = false; } else { const int8_t level = diagnostic_msgs::msg::DiagnosticStatus::OK; const std::string message = From 7b20619d6ff41c748aa39acd59bd6c1cb503ba25 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 2 Dec 2024 16:06:13 +0900 Subject: [PATCH 092/115] chore: clean code Signed-off-by: vividf --- .../src/concatenate_data/concatenate_and_time_sync_node.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 08bca6ef5eb50..178a3e0b06d9f 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -280,8 +280,6 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( is_concatenated_cloud_empty_ = true; } - std::cout << "is_concatenated_cloud_empty_: " << is_concatenated_cloud_empty_ << std::endl; - current_concatenate_cloud_timestamp_ = rclcpp::Time(concatenated_cloud_result.concatenate_cloud_ptr->header.stamp).seconds(); From 972758f6c025012d33f56abc24d4bf95f10f5cd1 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 2 Dec 2024 17:45:37 +0900 Subject: [PATCH 093/115] chore: remove rosbag_replay parameter Signed-off-by: vividf --- .../config/concatenate_and_time_sync_node.param.yaml | 3 +-- .../concatenate_and_time_sync_node.hpp | 1 - .../schema/concatenate_and_time_sync_node.schema.json | 10 ++-------- .../concatenate_and_time_sync_node.cpp | 8 +++----- .../test/test_concatenate_node_component.py | 1 - .../test/test_concatenate_node_unit.cpp | 1 - 6 files changed, 6 insertions(+), 18 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml index c5bd139e1e962..909845d5e1eca 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml @@ -2,8 +2,7 @@ ros__parameters: debug_mode: false has_static_tf_only: false - rosbag_replay: false - rosbag_length: 20.0 + rosbag_length: 10.0 maximum_queue_size: 5 timeout_sec: 0.2 is_motion_compensated: true diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 34309473e021b..29cc1265eeb5e 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -64,7 +64,6 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node { bool debug_mode; bool has_static_tf_only; - bool rosbag_replay; double rosbag_length; int maximum_queue_size; double timeout_sec; diff --git a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json index 581e540b43e02..918dc1b44ea74 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json @@ -16,16 +16,11 @@ "default": false, "description": "Flag to indicate if only static TF is used." }, - "rosbag_replay": { - "type": "boolean", - "default": false, - "description": "Flag to indicate whether the user is replaying the rosbag in loops without relaunching the node." - }, "rosbag_length": { "type": "number", - "default": 20.0, + "default": 10.0, "minimum": 0.0, - "description": "If rosbag_replay is enabled, this value helps the node determine if the rosbag has started from the beginning again. The value should be set slightly smaller (by 2-3 seconds) than the actual length of the bag." + "description": " This value determine if the rosbag has started from the beginning again. The value should be set smaller than the actual length of the bag." }, "maximum_queue_size": { "type": "integer", @@ -110,7 +105,6 @@ "required": [ "debug_mode", "has_static_tf_only", - "rosbag_replay", "rosbag_length", "maximum_queue_size", "timeout_sec", diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 178a3e0b06d9f..9fde86e4d1b8e 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -46,7 +46,6 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro // initialize parameters params_.debug_mode = declare_parameter("debug_mode"); params_.has_static_tf_only = declare_parameter("has_static_tf_only"); - params_.rosbag_replay = declare_parameter("rosbag_replay"); params_.rosbag_length = declare_parameter("rosbag_length"); params_.maximum_queue_size = static_cast(declare_parameter("maximum_queue_size")); params_.timeout_sec = declare_parameter("timeout_sec"); @@ -286,11 +285,10 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( if ( current_concatenate_cloud_timestamp_ < latest_concatenate_cloud_timestamp_ && !params_.publish_previous_but_late_pointcloud) { - // Check if we're in rosbag replay mode and the time difference is close to the rosbag length + // Publish the cloud if the rosbag replays in loop if ( - params_.rosbag_replay && - (latest_concatenate_cloud_timestamp_ - current_concatenate_cloud_timestamp_ > - params_.rosbag_length)) { + latest_concatenate_cloud_timestamp_ - current_concatenate_cloud_timestamp_ > + params_.rosbag_length) { publish_pointcloud_ = true; // Force publishing in this case } else { drop_previous_but_late_pointcloud_ = true; // Otherwise, drop the late pointcloud diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index 12e5eb6313800..346cdcb0fe430 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -88,7 +88,6 @@ def generate_test_description(): { "debug_mode": False, "has_static_tf_only": False, - "rosbag_replay": False, "rosbag_length": 0.0, "maximum_queue_size": 5, "timeout_sec": TIMEOUT_SEC, diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index bd22376b1c564..64dde67565b1c 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -43,7 +43,6 @@ class ConcatenateCloudTest : public ::testing::Test node_options.parameter_overrides( {{"debug_mode", false}, {"has_static_tf_only", false}, - {"rosbag_replay", false}, {"rosbag_length", 0.0}, {"maximum_queue_size", 5}, {"timeout_sec", 0.2}, From dde84669b1302378bb98f58d1c98ba50a3bb684e Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 6 Dec 2024 13:43:58 +0900 Subject: [PATCH 094/115] chore: remove nodelet cpp Signed-off-by: vividf --- .../concatenate_and_time_sync_nodelet.cpp | 718 ------------------ 1 file changed, 718 deletions(-) delete mode 100644 sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_nodelet.cpp diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_nodelet.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_nodelet.cpp deleted file mode 100644 index f8baae3405873..0000000000000 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_nodelet.cpp +++ /dev/null @@ -1,718 +0,0 @@ -// Copyright 2020 Tier IV, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* - * Software License Agreement (BSD License) - * - * Copyright (c) 2009, Willow Garage, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Willow Garage, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: concatenate_data.cpp 35231 2011-01-14 05:33:20Z rusu $ - * - */ - -#include "autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_nodelet.hpp" - -#include "autoware/pointcloud_preprocessor/utility/memory.hpp" - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#define DEFAULT_SYNC_TOPIC_POSTFIX \ - "_synchronized" // default postfix name for synchronized pointcloud - -////////////////////////////////////////////////////////////////////////////////////////////// - -namespace autoware::pointcloud_preprocessor -{ -PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchronizerComponent( - const rclcpp::NodeOptions & node_options) -: Node("point_cloud_concatenator_component", node_options), - input_twist_topic_type_(declare_parameter("input_twist_topic_type", "twist")) -{ - // initialize debug tool - { - using autoware::universe_utils::DebugPublisher; - using autoware::universe_utils::StopWatch; - stop_watch_ptr_ = std::make_unique>(); - debug_publisher_ = std::make_unique(this, "concatenate_data_synchronizer"); - stop_watch_ptr_->tic("cyclic_time"); - stop_watch_ptr_->tic("processing_time"); - } - - // Set parameters - { - output_frame_ = static_cast(declare_parameter("output_frame", "")); - if (output_frame_.empty()) { - RCLCPP_ERROR(get_logger(), "Need an 'output_frame' parameter to be set before continuing!"); - return; - } - has_static_tf_only_ = declare_parameter("has_static_tf_only", false); - declare_parameter("input_topics", std::vector()); - input_topics_ = get_parameter("input_topics").as_string_array(); - if (input_topics_.empty()) { - RCLCPP_ERROR(get_logger(), "Need a 'input_topics' parameter to be set before continuing!"); - return; - } - if (input_topics_.size() == 1) { - RCLCPP_ERROR(get_logger(), "Only one topic given. Need at least two topics to continue."); - return; - } - - // Optional parameters - maximum_queue_size_ = static_cast(declare_parameter("max_queue_size", 5)); - timeout_sec_ = static_cast(declare_parameter("timeout_sec", 0.1)); - - input_offset_ = declare_parameter("input_offset", std::vector{}); - if (!input_offset_.empty() && input_topics_.size() != input_offset_.size()) { - RCLCPP_ERROR(get_logger(), "The number of topics does not match the number of offsets."); - return; - } - - // Check if publish synchronized pointcloud - publish_synchronized_pointcloud_ = declare_parameter("publish_synchronized_pointcloud", true); - keep_input_frame_in_synchronized_pointcloud_ = - declare_parameter("keep_input_frame_in_synchronized_pointcloud", true); - synchronized_pointcloud_postfix_ = - declare_parameter("synchronized_pointcloud_postfix", "pointcloud"); - } - - // Initialize not_subscribed_topic_names_ - { - for (const std::string & e : input_topics_) { - not_subscribed_topic_names_.insert(e); - } - } - - // Initialize offset map - { - for (size_t i = 0; i < input_offset_.size(); ++i) { - offset_map_[input_topics_[i]] = input_offset_[i]; - } - } - - // tf2 listener - { - managed_tf_buffer_ = - std::make_unique(this, has_static_tf_only_); - } - - // Output Publishers - { - rclcpp::PublisherOptions pub_options; - pub_options.qos_overriding_options = rclcpp::QosOverridingOptions::with_default_policies(); - pub_output_ = this->create_publisher( - "output", rclcpp::SensorDataQoS().keep_last(maximum_queue_size_), pub_options); - } - - // Subscribers - { - RCLCPP_DEBUG_STREAM( - get_logger(), "Subscribing to " << input_topics_.size() << " user given topics as inputs:"); - for (const auto & input_topic : input_topics_) { - RCLCPP_DEBUG_STREAM(get_logger(), " - " << input_topic); - } - - // Subscribe to the filters - filters_.resize(input_topics_.size()); - - // First input_topics_.size () filters are valid - for (size_t d = 0; d < input_topics_.size(); ++d) { - cloud_stdmap_.insert(std::make_pair(input_topics_[d], nullptr)); - cloud_stdmap_tmp_ = cloud_stdmap_; - - // CAN'T use auto type here. - std::function cb = std::bind( - &PointCloudConcatenateDataSynchronizerComponent::cloud_callback, this, - std::placeholders::_1, input_topics_[d]); - - filters_[d].reset(); - filters_[d] = this->create_subscription( - input_topics_[d], rclcpp::SensorDataQoS().keep_last(maximum_queue_size_), cb); - } - - if (input_twist_topic_type_ == "twist") { - auto twist_cb = std::bind( - &PointCloudConcatenateDataSynchronizerComponent::twist_callback, this, - std::placeholders::_1); - sub_twist_ = this->create_subscription( - "~/input/twist", rclcpp::QoS{100}, twist_cb); - } else if (input_twist_topic_type_ == "odom") { - auto odom_cb = std::bind( - &PointCloudConcatenateDataSynchronizerComponent::odom_callback, this, - std::placeholders::_1); - sub_odom_ = this->create_subscription( - "~/input/odom", rclcpp::QoS{100}, odom_cb); - } else { - RCLCPP_ERROR_STREAM( - get_logger(), "input_twist_topic_type is invalid: " << input_twist_topic_type_); - throw std::runtime_error("input_twist_topic_type is invalid: " + input_twist_topic_type_); - } - } - - // Transformed Raw PointCloud2 Publisher to publish the transformed pointcloud - if (publish_synchronized_pointcloud_) { - rclcpp::PublisherOptions pub_options; - pub_options.qos_overriding_options = rclcpp::QosOverridingOptions::with_default_policies(); - - for (auto & topic : input_topics_) { - std::string new_topic = replaceSyncTopicNamePostfix(topic, synchronized_pointcloud_postfix_); - auto publisher = this->create_publisher( - new_topic, rclcpp::SensorDataQoS().keep_last(maximum_queue_size_), pub_options); - transformed_raw_pc_publisher_map_.insert({topic, publisher}); - } - } - - // Set timer - { - const auto period_ns = std::chrono::duration_cast( - std::chrono::duration(timeout_sec_)); - timer_ = rclcpp::create_timer( - this, get_clock(), period_ns, - std::bind(&PointCloudConcatenateDataSynchronizerComponent::timer_callback, this)); - } - - // Diagnostic Updater - { - updater_.setHardwareID("concatenate_data_checker"); - updater_.add( - "concat_status", this, &PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus); - } -} - -std::string PointCloudConcatenateDataSynchronizerComponent::replaceSyncTopicNamePostfix( - const std::string & original_topic_name, const std::string & postfix) -{ - std::string replaced_topic_name; - // separate the topic name by '/' and replace the last element with the new postfix - size_t pos = original_topic_name.find_last_of("/"); - if (pos == std::string::npos) { - // not found '/': this is not a namespaced topic - RCLCPP_WARN_STREAM( - get_logger(), - "The topic name is not namespaced. The postfix will be added to the end of the topic name."); - return original_topic_name + postfix; - } else { - // replace the last element with the new postfix - replaced_topic_name = original_topic_name.substr(0, pos) + "/" + postfix; - } - - // if topic name is the same with original topic name, add postfix to the end of the topic name - if (replaced_topic_name == original_topic_name) { - RCLCPP_WARN_STREAM( - get_logger(), "The topic name " - << original_topic_name - << " have the same postfix with synchronized pointcloud. We use the postfix " - "to the end of the topic name."); - replaced_topic_name = original_topic_name + DEFAULT_SYNC_TOPIC_POSTFIX; - } - return replaced_topic_name; -} - -/** - * @brief compute transform to adjust for old timestamp - * - * @param old_stamp - * @param new_stamp - * @return Eigen::Matrix4f: transformation matrix from new_stamp to old_stamp - */ -Eigen::Matrix4f -PointCloudConcatenateDataSynchronizerComponent::computeTransformToAdjustForOldTimestamp( - const rclcpp::Time & old_stamp, const rclcpp::Time & new_stamp) -{ - // return identity if no twist is available - if (twist_ptr_queue_.empty()) { - RCLCPP_WARN_STREAM_THROTTLE( - get_logger(), *get_clock(), std::chrono::milliseconds(10000).count(), - "No twist is available. Please confirm twist topic and timestamp"); - return Eigen::Matrix4f::Identity(); - } - - // return identity if old_stamp is newer than new_stamp - if (old_stamp > new_stamp) { - RCLCPP_DEBUG_STREAM_THROTTLE( - get_logger(), *get_clock(), std::chrono::milliseconds(10000).count(), - "old_stamp is newer than new_stamp,"); - return Eigen::Matrix4f::Identity(); - } - - auto old_twist_ptr_it = std::lower_bound( - std::begin(twist_ptr_queue_), std::end(twist_ptr_queue_), old_stamp, - [](const geometry_msgs::msg::TwistStamped::ConstSharedPtr & x_ptr, const rclcpp::Time & t) { - return rclcpp::Time(x_ptr->header.stamp) < t; - }); - old_twist_ptr_it = - old_twist_ptr_it == twist_ptr_queue_.end() ? (twist_ptr_queue_.end() - 1) : old_twist_ptr_it; - - auto new_twist_ptr_it = std::lower_bound( - std::begin(twist_ptr_queue_), std::end(twist_ptr_queue_), new_stamp, - [](const geometry_msgs::msg::TwistStamped::ConstSharedPtr & x_ptr, const rclcpp::Time & t) { - return rclcpp::Time(x_ptr->header.stamp) < t; - }); - new_twist_ptr_it = - new_twist_ptr_it == twist_ptr_queue_.end() ? (twist_ptr_queue_.end() - 1) : new_twist_ptr_it; - - auto prev_time = old_stamp; - double x = 0.0; - double y = 0.0; - double yaw = 0.0; - for (auto twist_ptr_it = old_twist_ptr_it; twist_ptr_it != new_twist_ptr_it + 1; ++twist_ptr_it) { - const double dt = - (twist_ptr_it != new_twist_ptr_it) - ? (rclcpp::Time((*twist_ptr_it)->header.stamp) - rclcpp::Time(prev_time)).seconds() - : (rclcpp::Time(new_stamp) - rclcpp::Time(prev_time)).seconds(); - - if (std::fabs(dt) > 0.1) { - RCLCPP_WARN_STREAM_THROTTLE( - get_logger(), *get_clock(), std::chrono::milliseconds(10000).count(), - "Time difference is too large. Cloud not interpolate. Please confirm twist topic and " - "timestamp"); - break; - } - - const double dis = (*twist_ptr_it)->twist.linear.x * dt; - yaw += (*twist_ptr_it)->twist.angular.z * dt; - x += dis * std::cos(yaw); - y += dis * std::sin(yaw); - prev_time = (*twist_ptr_it)->header.stamp; - } - Eigen::AngleAxisf rotation_x(0, Eigen::Vector3f::UnitX()); - Eigen::AngleAxisf rotation_y(0, Eigen::Vector3f::UnitY()); - Eigen::AngleAxisf rotation_z(yaw, Eigen::Vector3f::UnitZ()); - Eigen::Translation3f translation(x, y, 0); - Eigen::Matrix4f rotation_matrix = (translation * rotation_z * rotation_y * rotation_x).matrix(); - return rotation_matrix; -} - -std::map -PointCloudConcatenateDataSynchronizerComponent::combineClouds( - sensor_msgs::msg::PointCloud2::SharedPtr & concat_cloud_ptr) -{ - // map for storing the transformed point clouds - std::map transformed_clouds; - - // Step1. gather stamps and sort it - std::vector pc_stamps; - for (const auto & e : cloud_stdmap_) { - transformed_clouds[e.first] = nullptr; - if (e.second != nullptr) { - if (e.second->data.size() == 0) { - continue; - } - pc_stamps.push_back(rclcpp::Time(e.second->header.stamp)); - } - } - if (pc_stamps.empty()) { - return transformed_clouds; - } - // sort stamps and get oldest stamp - std::sort(pc_stamps.begin(), pc_stamps.end()); - std::reverse(pc_stamps.begin(), pc_stamps.end()); - const auto oldest_stamp = pc_stamps.back(); - - // Step2. Calculate compensation transform and concatenate with the oldest stamp - for (const auto & e : cloud_stdmap_) { - if (e.second != nullptr) { - if (e.second->data.size() == 0) { - continue; - } - sensor_msgs::msg::PointCloud2::SharedPtr transformed_cloud_ptr( - new sensor_msgs::msg::PointCloud2()); - managed_tf_buffer_->transformPointcloud(output_frame_, *e.second, *transformed_cloud_ptr); - - // calculate transforms to oldest stamp - Eigen::Matrix4f adjust_to_old_data_transform = Eigen::Matrix4f::Identity(); - rclcpp::Time transformed_stamp = rclcpp::Time(e.second->header.stamp); - for (const auto & stamp : pc_stamps) { - const auto new_to_old_transform = - computeTransformToAdjustForOldTimestamp(stamp, transformed_stamp); - adjust_to_old_data_transform = new_to_old_transform * adjust_to_old_data_transform; - transformed_stamp = std::min(transformed_stamp, stamp); - } - sensor_msgs::msg::PointCloud2::SharedPtr transformed_delay_compensated_cloud_ptr( - new sensor_msgs::msg::PointCloud2()); - pcl_ros::transformPointCloud( - adjust_to_old_data_transform, *transformed_cloud_ptr, - *transformed_delay_compensated_cloud_ptr); - - // concatenate - if (concat_cloud_ptr == nullptr) { - concat_cloud_ptr = - std::make_shared(*transformed_delay_compensated_cloud_ptr); - } else { - pcl::concatenatePointCloud( - *concat_cloud_ptr, *transformed_delay_compensated_cloud_ptr, *concat_cloud_ptr); - } - // convert to original sensor frame if necessary - bool need_transform_to_sensor_frame = (e.second->header.frame_id != output_frame_); - if (keep_input_frame_in_synchronized_pointcloud_ && need_transform_to_sensor_frame) { - sensor_msgs::msg::PointCloud2::SharedPtr transformed_cloud_ptr_in_sensor_frame( - new sensor_msgs::msg::PointCloud2()); - managed_tf_buffer_->transformPointcloud( - e.second->header.frame_id, *transformed_delay_compensated_cloud_ptr, - *transformed_cloud_ptr_in_sensor_frame); - transformed_cloud_ptr_in_sensor_frame->header.stamp = oldest_stamp; - transformed_cloud_ptr_in_sensor_frame->header.frame_id = e.second->header.frame_id; - transformed_clouds[e.first] = transformed_cloud_ptr_in_sensor_frame; - } else { - transformed_delay_compensated_cloud_ptr->header.stamp = oldest_stamp; - transformed_delay_compensated_cloud_ptr->header.frame_id = output_frame_; - transformed_clouds[e.first] = transformed_delay_compensated_cloud_ptr; - } - - } else { - not_subscribed_topic_names_.insert(e.first); - } - } - concat_cloud_ptr->header.stamp = oldest_stamp; - return transformed_clouds; -} - -void PointCloudConcatenateDataSynchronizerComponent::publish() -{ - stop_watch_ptr_->toc("processing_time", true); - sensor_msgs::msg::PointCloud2::SharedPtr concat_cloud_ptr = nullptr; - not_subscribed_topic_names_.clear(); - - const auto & transformed_raw_points = - PointCloudConcatenateDataSynchronizerComponent::combineClouds(concat_cloud_ptr); - - // publish concatenated pointcloud - if (concat_cloud_ptr) { - auto output = std::make_unique(*concat_cloud_ptr); - pub_output_->publish(std::move(output)); - } else { - RCLCPP_WARN(this->get_logger(), "concat_cloud_ptr is nullptr, skipping pointcloud publish."); - } - - // publish transformed raw pointclouds - if (publish_synchronized_pointcloud_) { - for (const auto & e : transformed_raw_points) { - if (e.second) { - auto output = std::make_unique(*e.second); - transformed_raw_pc_publisher_map_[e.first]->publish(std::move(output)); - } else { - RCLCPP_WARN( - this->get_logger(), "transformed_raw_points[%s] is nullptr, skipping pointcloud publish.", - e.first.c_str()); - } - } - } - - updater_.force_update(); - - cloud_stdmap_ = cloud_stdmap_tmp_; - std::for_each(std::begin(cloud_stdmap_tmp_), std::end(cloud_stdmap_tmp_), [](auto & e) { - e.second = nullptr; - }); - // add processing time for debug - if (debug_publisher_) { - const double cyclic_time_ms = stop_watch_ptr_->toc("cyclic_time", true); - const double processing_time_ms = stop_watch_ptr_->toc("processing_time", true); - debug_publisher_->publish( - "debug/cyclic_time_ms", cyclic_time_ms); - debug_publisher_->publish( - "debug/processing_time_ms", processing_time_ms); - } - for (const auto & e : cloud_stdmap_) { - if (e.second != nullptr) { - if (debug_publisher_) { - const auto pipeline_latency_ms = - std::chrono::duration( - std::chrono::nanoseconds( - (this->get_clock()->now() - e.second->header.stamp).nanoseconds())) - .count(); - debug_publisher_->publish( - "debug" + e.first + "/pipeline_latency_ms", pipeline_latency_ms); - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void PointCloudConcatenateDataSynchronizerComponent::convertToXYZIRCCloud( - const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, - sensor_msgs::msg::PointCloud2::SharedPtr & output_ptr) -{ - output_ptr->header = input_ptr->header; - - PointCloud2Modifier output_modifier{ - *output_ptr, input_ptr->header.frame_id}; - output_modifier.reserve(input_ptr->width); - - bool has_valid_intensity = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { - return field.name == "intensity" && field.datatype == sensor_msgs::msg::PointField::UINT8; - }); - - bool has_valid_return_type = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { - return field.name == "return_type" && field.datatype == sensor_msgs::msg::PointField::UINT8; - }); - - bool has_valid_channel = - std::any_of(input_ptr->fields.begin(), input_ptr->fields.end(), [](const auto & field) { - return field.name == "channel" && field.datatype == sensor_msgs::msg::PointField::UINT16; - }); - - sensor_msgs::PointCloud2Iterator it_x(*input_ptr, "x"); - sensor_msgs::PointCloud2Iterator it_y(*input_ptr, "y"); - sensor_msgs::PointCloud2Iterator it_z(*input_ptr, "z"); - - if (has_valid_intensity && has_valid_return_type && has_valid_channel) { - sensor_msgs::PointCloud2Iterator it_i(*input_ptr, "intensity"); - sensor_msgs::PointCloud2Iterator it_r(*input_ptr, "return_type"); - sensor_msgs::PointCloud2Iterator it_c(*input_ptr, "channel"); - - for (; it_x != it_x.end(); ++it_x, ++it_y, ++it_z, ++it_i, ++it_r, ++it_c) { - PointXYZIRC point; - point.x = *it_x; - point.y = *it_y; - point.z = *it_z; - point.intensity = *it_i; - point.return_type = *it_r; - point.channel = *it_c; - output_modifier.push_back(std::move(point)); - } - } else { - for (; it_x != it_x.end(); ++it_x, ++it_y, ++it_z) { - PointXYZIRC point; - point.x = *it_x; - point.y = *it_y; - point.z = *it_z; - output_modifier.push_back(std::move(point)); - } - } -} - -void PointCloudConcatenateDataSynchronizerComponent::setPeriod(const int64_t new_period) -{ - if (!timer_) { - return; - } - int64_t old_period = 0; - rcl_ret_t ret = rcl_timer_get_period(timer_->get_timer_handle().get(), &old_period); - if (ret != RCL_RET_OK) { - rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't get old period"); - } - ret = rcl_timer_exchange_period(timer_->get_timer_handle().get(), new_period, &old_period); - if (ret != RCL_RET_OK) { - rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't exchange_period"); - } -} - -void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( - const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_ptr, const std::string & topic_name) -{ - if (!utils::is_data_layout_compatible_with_point_xyzirc(*input_ptr)) { - RCLCPP_ERROR( - get_logger(), "The pointcloud layout is not compatible with PointXYZIRC. Aborting"); - - if (utils::is_data_layout_compatible_with_point_xyzi(*input_ptr)) { - RCLCPP_ERROR( - get_logger(), - "The pointcloud layout is compatible with PointXYZI. You may be using legacy code/data"); - } - - return; - } - - std::lock_guard lock(mutex_); - sensor_msgs::msg::PointCloud2::SharedPtr xyzirc_input_ptr(new sensor_msgs::msg::PointCloud2()); - auto input = std::make_shared(*input_ptr); - if (input->data.empty()) { - RCLCPP_WARN_STREAM_THROTTLE( - this->get_logger(), *this->get_clock(), 1000, "Empty sensor points!"); - } else { - // convert to XYZIRC pointcloud if pointcloud is not empty - convertToXYZIRCCloud(input, xyzirc_input_ptr); - } - - const bool is_already_subscribed_this = (cloud_stdmap_[topic_name] != nullptr); - const bool is_already_subscribed_tmp = std::any_of( - std::begin(cloud_stdmap_tmp_), std::end(cloud_stdmap_tmp_), - [](const auto & e) { return e.second != nullptr; }); - - if (is_already_subscribed_this) { - cloud_stdmap_tmp_[topic_name] = xyzirc_input_ptr; - - if (!is_already_subscribed_tmp) { - auto period = std::chrono::duration_cast( - std::chrono::duration(timeout_sec_)); - try { - setPeriod(period.count()); - } catch (rclcpp::exceptions::RCLError & ex) { - RCLCPP_WARN_THROTTLE(get_logger(), *get_clock(), 5000, "%s", ex.what()); - } - timer_->reset(); - } - } else { - cloud_stdmap_[topic_name] = xyzirc_input_ptr; - - const bool is_subscribed_all = std::all_of( - std::begin(cloud_stdmap_), std::end(cloud_stdmap_), - [](const auto & e) { return e.second != nullptr; }); - - if (is_subscribed_all) { - for (const auto & e : cloud_stdmap_tmp_) { - if (e.second != nullptr) { - cloud_stdmap_[e.first] = e.second; - } - } - std::for_each(std::begin(cloud_stdmap_tmp_), std::end(cloud_stdmap_tmp_), [](auto & e) { - e.second = nullptr; - }); - - timer_->cancel(); - publish(); - } else if (offset_map_.size() > 0) { - timer_->cancel(); - auto period = std::chrono::duration_cast( - std::chrono::duration(timeout_sec_ - offset_map_[topic_name])); - try { - setPeriod(period.count()); - } catch (rclcpp::exceptions::RCLError & ex) { - RCLCPP_WARN_THROTTLE(get_logger(), *get_clock(), 5000, "%s", ex.what()); - } - timer_->reset(); - } - } -} - -void PointCloudConcatenateDataSynchronizerComponent::timer_callback() -{ - using std::chrono_literals::operator""ms; - timer_->cancel(); - if (mutex_.try_lock()) { - publish(); - mutex_.unlock(); - } else { - try { - std::chrono::nanoseconds period = 10ms; - setPeriod(period.count()); - } catch (rclcpp::exceptions::RCLError & ex) { - RCLCPP_WARN_THROTTLE(get_logger(), *get_clock(), 5000, "%s", ex.what()); - } - timer_->reset(); - } -} - -void PointCloudConcatenateDataSynchronizerComponent::twist_callback( - const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr input) -{ - // if rosbag restart, clear buffer - if (!twist_ptr_queue_.empty()) { - if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > rclcpp::Time(input->header.stamp)) { - twist_ptr_queue_.clear(); - } - } - - // pop old data - while (!twist_ptr_queue_.empty()) { - if ( - rclcpp::Time(twist_ptr_queue_.front()->header.stamp) + rclcpp::Duration::from_seconds(1.0) > - rclcpp::Time(input->header.stamp)) { - break; - } - twist_ptr_queue_.pop_front(); - } - - auto twist_ptr = std::make_shared(); - twist_ptr->header = input->header; - twist_ptr->twist = input->twist.twist; - twist_ptr_queue_.push_back(twist_ptr); -} - -void PointCloudConcatenateDataSynchronizerComponent::odom_callback( - const nav_msgs::msg::Odometry::ConstSharedPtr input) -{ - // if rosbag restart, clear buffer - if (!twist_ptr_queue_.empty()) { - if (rclcpp::Time(twist_ptr_queue_.front()->header.stamp) > rclcpp::Time(input->header.stamp)) { - twist_ptr_queue_.clear(); - } - } - - // pop old data - while (!twist_ptr_queue_.empty()) { - if ( - rclcpp::Time(twist_ptr_queue_.front()->header.stamp) + rclcpp::Duration::from_seconds(1.0) > - rclcpp::Time(input->header.stamp)) { - break; - } - twist_ptr_queue_.pop_front(); - } - - auto twist_ptr = std::make_shared(); - twist_ptr->header = input->header; - twist_ptr->twist = input->twist.twist; - twist_ptr_queue_.push_back(twist_ptr); -} - -void PointCloudConcatenateDataSynchronizerComponent::checkConcatStatus( - diagnostic_updater::DiagnosticStatusWrapper & stat) -{ - for (const std::string & e : input_topics_) { - const std::string subscribe_status = not_subscribed_topic_names_.count(e) ? "NG" : "OK"; - stat.add(e, subscribe_status); - } - - const int8_t level = not_subscribed_topic_names_.empty() - ? diagnostic_msgs::msg::DiagnosticStatus::OK - : diagnostic_msgs::msg::DiagnosticStatus::WARN; - const std::string message = not_subscribed_topic_names_.empty() - ? "Concatenate all topics" - : "Some topics are not concatenated"; - stat.summary(level, message); -} -} // namespace autoware::pointcloud_preprocessor - -#include -RCLCPP_COMPONENTS_REGISTER_NODE( - autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent) From 1cc54ac6c94ecdf8b542eced6b95fdc3d9db2da3 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 6 Dec 2024 14:03:14 +0900 Subject: [PATCH 095/115] chore: clang tidy warning Signed-off-by: vividf --- .../concatenate_data/combine_cloud_handler.hpp | 5 +++-- .../src/concatenate_data/combine_cloud_handler.cpp | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index 41a643fa81e74..2a9532760a465 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -95,8 +95,9 @@ class CombineCloudHandler rclcpp::Node & node, std::vector input_topics, std::string output_frame, bool is_motion_compensated, bool publish_synchronized_pointcloud, bool keep_input_frame_in_synchronized_pointcloud, bool has_static_tf_only); - void process_twist(const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & input); - void process_odometry(const nav_msgs::msg::Odometry::ConstSharedPtr & input); + void process_twist( + const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & twist_msg); + void process_odometry(const nav_msgs::msg::Odometry::ConstSharedPtr & odometry_msg); ConcatenatedCloudResult combine_pointclouds( std::unordered_map & topic_to_cloud_map); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 135ab943bebd4..9647649324d8e 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -19,8 +19,12 @@ #include #include +#include +#include #include #include +#include +#include #include namespace autoware::pointcloud_preprocessor @@ -31,8 +35,8 @@ CombineCloudHandler::CombineCloudHandler( bool is_motion_compensated, bool publish_synchronized_pointcloud, bool keep_input_frame_in_synchronized_pointcloud, bool has_static_tf_only) : node_(node), - input_topics_(input_topics), - output_frame_(output_frame), + input_topics_(std::move(input_topics)), + output_frame_(std::move(output_frame)), is_motion_compensated_(is_motion_compensated), publish_synchronized_pointcloud_(publish_synchronized_pointcloud), keep_input_frame_in_synchronized_pointcloud_(keep_input_frame_in_synchronized_pointcloud), From aa89a1fd4d7391e3b1daf441d891dc8d449776f7 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 9 Dec 2024 11:47:41 +0900 Subject: [PATCH 096/115] feat: add naive approach for unsynchronized pointclouds Signed-off-by: vividf --- .../concatenate_and_time_sync_node.param.yaml | 2 + .../concatenate_data/cloud_collector.hpp | 6 ++ .../concatenate_and_time_sync_node.hpp | 4 +- .../src/concatenate_data/cloud_collector.cpp | 22 +++++- .../concatenate_and_time_sync_node.cpp | 78 ++++++++++++++----- .../test/test_concatenate_node_component.py | 1 + .../test/test_concatenate_node_unit.cpp | 7 +- 7 files changed, 98 insertions(+), 22 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml index 909845d5e1eca..02236966569c0 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml @@ -1,5 +1,7 @@ /**: ros__parameters: + # ignore the lidar_timestamp_offsets and lidar_timestamp_noise_window if 'use_naive_approach' is true + use_naive_approach: true debug_mode: false has_static_tf_only: false rosbag_length: 10.0 diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index b90fd4a526632..1cdce9408083c 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -35,8 +35,13 @@ class CloudCollector std::shared_ptr & combine_cloud_handler, int num_of_clouds, double timeout_sec, bool debug_mode); + // Naive approach + void set_arrival_timestamp(double timestamp); + double get_arrival_timestamp() const; + // Advanced approach void set_reference_timestamp(double timestamp, double noise_window); std::tuple get_reference_timestamp_boundary(); + bool topic_exists(const std::string & topic_name); void process_pointcloud( const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud); void concatenate_callback(); @@ -56,6 +61,7 @@ class CloudCollector double timeout_sec_; double reference_timestamp_min_{0.0}; double reference_timestamp_max_{0.0}; + double arrival_timestamp_{0.0}; // This is used for the naive approach bool debug_mode_; }; diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 29cc1265eeb5e..781935e621408 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -54,7 +54,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node ~PointCloudConcatenateDataSynchronizerComponent() override = default; void publish_clouds( ConcatenatedCloudResult && concatenated_cloud_result, double reference_timestamp_min, - double reference_timestamp_max); + double reference_timestamp_max, double arrival_timestamp); void delete_collector(CloudCollector & cloud_collector); std::list> get_cloud_collectors(); void add_cloud_collector(const std::shared_ptr & collector); @@ -62,6 +62,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node private: struct Parameters { + bool use_naive_approach; bool debug_mode; bool has_static_tf_only; double rosbag_length; @@ -86,6 +87,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node bool is_concatenated_cloud_empty_{false}; double diagnostic_reference_timestamp_min_{0.0}; double diagnostic_reference_timestamp_max_{0.0}; + double diagnostic_arrival_timestamp_{0.0}; std::unordered_map diagnostic_topic_to_original_stamp_map_; std::shared_ptr combine_cloud_handler_; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index b0a7f746be778..bc74a06e21a3f 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -19,7 +19,11 @@ #include +#include +#include #include +#include +#include namespace autoware::pointcloud_preprocessor { @@ -42,6 +46,16 @@ CloudCollector::CloudCollector( std::bind(&CloudCollector::concatenate_callback, this)); } +void CloudCollector::set_arrival_timestamp(double timestamp) +{ + arrival_timestamp_ = timestamp; +} + +double CloudCollector::get_arrival_timestamp() const +{ + return arrival_timestamp_; +} + void CloudCollector::set_reference_timestamp(double timestamp, double noise_window) { reference_timestamp_max_ = timestamp + noise_window; @@ -53,6 +67,11 @@ std::tuple CloudCollector::get_reference_timestamp_boundary() return std::make_tuple(reference_timestamp_min_, reference_timestamp_max_); } +bool CloudCollector::topic_exists(const std::string & topic_name) +{ + return topic_to_cloud_map_.find(topic_name) != topic_to_cloud_map_.end(); +} + void CloudCollector::process_pointcloud( const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud) { @@ -105,7 +124,8 @@ void CloudCollector::concatenate_callback() auto concatenated_cloud_result = concatenate_pointclouds(topic_to_cloud_map_); ros2_parent_node_->publish_clouds( - std::move(concatenated_cloud_result), reference_timestamp_min_, reference_timestamp_max_); + std::move(concatenated_cloud_result), reference_timestamp_min_, reference_timestamp_max_, + arrival_timestamp_); ros2_parent_node_->delete_collector(*this); } diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 9fde86e4d1b8e..03d5bd89aab0d 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro stop_watch_ptr_->tic("processing_time"); // initialize parameters + params_.use_naive_approach = declare_parameter("use_naive_approach"); params_.debug_mode = declare_parameter("debug_mode"); params_.has_static_tf_only = declare_parameter("has_static_tf_only"); params_.rosbag_length = declare_parameter("rosbag_length"); @@ -186,6 +188,8 @@ std::string PointCloudConcatenateDataSynchronizerComponent::replace_sync_topic_n void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, const std::string & topic_name) { + double cloud_arrival_time = this->get_clock()->now().seconds(); + stop_watch_ptr_->toc("processing_time", true); if (!utils::is_data_layout_compatible_with_point_xyzirc(*input_ptr)) { RCLCPP_ERROR( @@ -203,9 +207,8 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( if (params_.debug_mode) { RCLCPP_INFO( this->get_logger(), " pointcloud %s timestamp: %lf arrive time: %lf seconds, latency: %lf", - topic_name.c_str(), rclcpp::Time(input_ptr->header.stamp).seconds(), - this->get_clock()->now().seconds(), - this->get_clock()->now().seconds() - rclcpp::Time(input_ptr->header.stamp).seconds()); + topic_name.c_str(), rclcpp::Time(input_ptr->header.stamp).seconds(), cloud_arrival_time, + cloud_arrival_time - rclcpp::Time(input_ptr->header.stamp).seconds()); } if (input_ptr->data.empty()) { @@ -220,19 +223,45 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( bool collector_found = false; if (!cloud_collectors_.empty()) { - for (const auto & cloud_collector : cloud_collectors_) { - auto [reference_timestamp_min, reference_timestamp_max] = - cloud_collector->get_reference_timestamp_boundary(); + if (params_.use_naive_approach) { + // Navie approach: Concatenate the pointlcouds based on the pointclouds' arrival time on the + // node + std::optional smallest_time_difference = std::nullopt; + std::shared_ptr closest_collector = nullptr; + + for (const auto & cloud_collector : cloud_collectors_) { + if (!cloud_collector->topic_exists(topic_name)) { + auto collector_timestamp = cloud_collector->get_arrival_timestamp(); + double time_difference = std::abs(cloud_arrival_time - collector_timestamp); + + if (!smallest_time_difference || time_difference < smallest_time_difference) { + smallest_time_difference = time_difference; + closest_collector = cloud_collector; + } + } + } - if ( - rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] < - reference_timestamp_max + topic_to_noise_window_map_[topic_name] && - rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] > - reference_timestamp_min - topic_to_noise_window_map_[topic_name]) { + if (smallest_time_difference) { cloud_collectors_lock.unlock(); - cloud_collector->process_pointcloud(topic_name, input_ptr); + closest_collector->process_pointcloud(topic_name, input_ptr); collector_found = true; - break; + } + } else { + // Advanced approach: Concatenate the pointlcouds based on the pointclouds' timestamps + for (const auto & cloud_collector : cloud_collectors_) { + auto [reference_timestamp_min, reference_timestamp_max] = + cloud_collector->get_reference_timestamp_boundary(); + + if ( + rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] < + reference_timestamp_max + topic_to_noise_window_map_[topic_name] && + rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] > + reference_timestamp_min - topic_to_noise_window_map_[topic_name]) { + cloud_collectors_lock.unlock(); + cloud_collector->process_pointcloud(topic_name, input_ptr); + collector_found = true; + break; + } } } } @@ -245,9 +274,14 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( cloud_collectors_.push_back(new_cloud_collector); cloud_collectors_lock.unlock(); - new_cloud_collector->set_reference_timestamp( - rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name], - topic_to_noise_window_map_[topic_name]); + + if (params_.use_naive_approach) { + new_cloud_collector->set_arrival_timestamp(cloud_arrival_time); + } else { + new_cloud_collector->set_reference_timestamp( + rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name], + topic_to_noise_window_map_[topic_name]); + } new_cloud_collector->process_pointcloud(topic_name, input_ptr); } } @@ -266,7 +300,7 @@ void PointCloudConcatenateDataSynchronizerComponent::odom_callback( void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( ConcatenatedCloudResult && concatenated_cloud_result, double reference_timestamp_min, - double reference_timestamp_max) + double reference_timestamp_max, double arrival_timestamp) { // should never come to this state. if (concatenated_cloud_result.concatenate_cloud_ptr == nullptr) { @@ -328,6 +362,7 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( diagnostic_reference_timestamp_min_ = reference_timestamp_min; diagnostic_reference_timestamp_max_ = reference_timestamp_max; + diagnostic_arrival_timestamp_ = arrival_timestamp; diagnostic_topic_to_original_stamp_map_ = concatenated_cloud_result.topic_to_original_stamp_map; diagnostic_updater_.force_update(); @@ -380,8 +415,13 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( if (publish_pointcloud_ || drop_previous_but_late_pointcloud_) { stat.add( "concatenated cloud timestamp", format_timestamp(current_concatenate_cloud_timestamp_)); - stat.add("reference timestamp min", format_timestamp(diagnostic_reference_timestamp_min_)); - stat.add("reference timestamp max", format_timestamp(diagnostic_reference_timestamp_max_)); + + if (params_.use_naive_approach) { + stat.add("first cloud's arrival timestamp", format_timestamp(diagnostic_arrival_timestamp_)); + } else { + stat.add("reference timestamp min", format_timestamp(diagnostic_reference_timestamp_min_)); + stat.add("reference timestamp max", format_timestamp(diagnostic_reference_timestamp_max_)); + } bool topic_miss = false; diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index 346cdcb0fe430..c94eaaf1eade1 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -86,6 +86,7 @@ def generate_test_description(): ], parameters=[ { + "use_naive_approach": False, "debug_mode": False, "has_static_tf_only": False, "rosbag_length": 0.0, diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 64dde67565b1c..a8bfe395b618e 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -28,9 +28,13 @@ #include #include +#include #include #include +#include #include +#include +#include class ConcatenateCloudTest : public ::testing::Test { @@ -41,7 +45,8 @@ class ConcatenateCloudTest : public ::testing::Test // Instead of "input_topics", other parameters are not used. // They just helps to setup the concatenate node node_options.parameter_overrides( - {{"debug_mode", false}, + {{"use_naive_approach", false}, + {"debug_mode", false}, {"has_static_tf_only", false}, {"rosbag_length", 0.0}, {"maximum_queue_size", 5}, From 76ac99f7d5fec6e6d9ed02a0bfddabc1f0022f51 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 9 Dec 2024 12:07:10 +0900 Subject: [PATCH 097/115] chore: add more explanations in docs for naive approach Signed-off-by: vividf --- .../docs/concatenate-data.md | 18 ++++++++++-------- .../concatenate_and_time_sync_node.schema.json | 6 ++++++ .../concatenate_and_time_sync_node.cpp | 4 ++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index e0d114b5a4f23..cd13a80b42944 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -28,10 +28,10 @@ After concatenation, the concatenated point cloud is published, and the collecto ### Input -| Name | Type | Description | -| --------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `~/input/twist` | `geometry_msgs::msg::TwistWithCovarianceStamped` | Twist information adjusts the point cloud scans based on vehicle motion, allowing LiDARs with different timestamp to be synchronized for concatenation. | -| `~/input/odom` | `nav_msgs::msg::Odometry` | Vehicle odometry adjusts the point cloud scans based on vehicle motion, allowing LiDARs with different timestamp to be synchronized for concatenation. | +| Name | Type | Description | +| --------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `~/input/twist` | `geometry_msgs::msg::TwistWithCovarianceStamped` | Twist information adjusts the point cloud scans based on vehicle motion, allowing LiDARs with different timestamps to be synchronized for concatenation. | +| `~/input/odom` | `nav_msgs::msg::Odometry` | Vehicle odometry adjusts the point cloud scans based on vehicle motion, allowing LiDARs with different timestamps to be synchronized for concatenation. | By setting the `input_twist_topic_type` parameter to `twist` or `odom`, the subscriber will subscribe to either `~/input/twist` or `~/input/odom`. If the user doesn't want to use the twist information or vehicle odometry to compensate for motion, set `is_motion_compensated` to `false`. @@ -47,7 +47,9 @@ By setting the `input_twist_topic_type` parameter to `twist` or `odom`, the subs ### Parameter Settings -Three parameters, `timeout_sec`, `lidar_timestamp_offsets`, and `lidar_timestamp_noise_window`, are critical for collecting point clouds in the same collector and handling edge cases effectively. +If your LiDAR sensor does not support synchronization, set `use_naive_approach` to `true` to use the naive approach, which bypasses point cloud timestamps and directly concatenates the point clouds. On the other hand, if your LiDAR sensors are synchronized, set `use_naive_approach` to `false` and adjust the `lidar_timestamp_offsets` and `lidar_timestamp_noise_window` parameters accordingly. + +The three parameters, `timeout_sec`, `lidar_timestamp_offsets`, and `lidar_timestamp_noise_window`, are essential for efficiently collecting point clouds in the same collector and managing edge cases effectively. #### timeout_sec @@ -73,7 +75,7 @@ nanosec: 257009560 nanosec: 355444581 ``` -This pattern indicates a LiDAR timestamp is 0.05. +This pattern indicates a LiDAR timestamp of 0.05. If there are three LiDARs (left, right, top), and the timestamps for the left, right, and top point clouds are `0.01`, `0.05`, and `0.09` seconds respectively, the parameters should be set as [0.0, 0.04, 0.08]. This reflects the timestamp differences between the current point cloud and the point cloud with the earliest timestamp. Note that the order of the `lidar_timestamp_offsets` corresponds to the order of the `input_topics`. @@ -85,7 +87,7 @@ The figure below demonstrates how `lidar_timestamp_offsets` works with `concaten Additionally, due to the mechanical design of LiDARs, there may be some jitter in the timestamps of each scan, as shown in the image below. For example, if the scan frequency is set to 10 Hz (scanning every 100 ms), the timestamps between each scan might not be exactly 100 ms apart. To handle this noise, the `lidar_timestamp_noise_window` parameter is provided. -User can use [this tool](https://github.com/tier4/timestamp_analyzer) to visualize the noise between each scan. +Users can use [this tool](https://github.com/tier4/timestamp_analyzer) to visualize the noise between each scan. ![jitter](./image/jitter.png) @@ -207,4 +209,4 @@ Note that the `concatenate_pointclouds` and `time_synchronizer_nodelet` are usin ## Assumptions / Known Limits - If `is_motion_compensated` is set to `false`, the `concatenate_and_time_sync_node` will directly concatenate the point clouds without applying for motion compensation. This can save several milliseconds depending on the number of LiDARs being concatenated. Therefore, if the timestamp differences between point clouds are negligible, the user can set `is_motion_compensated` to `false` and omit the need for twist or odometry input for the node. -- As mentioned above, the user should clearly understand how their LiDAR's point cloud timestamps are managed to set the parameters correctly. +- As mentioned above, the user should clearly understand how their LiDAR's point cloud timestamps are managed to set the parameters correctly. If the user does not synchronize the point clouds, please set `use_naive_approach` to `true`. diff --git a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json index 918dc1b44ea74..800a79d35dcf8 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json @@ -6,6 +6,11 @@ "concatenate_and_time_sync_node": { "type": "object", "properties": { + "use_naive_approach": { + "type": "boolean", + "default": false, + "description": "Flag to enable a naive approach for concatenating point clouds without considering their timestamps. Set this to `false` and adjust `lidar_timestamp_offsets` and `lidar_timestamp_noise_window` if your LiDAR sensor supports synchronization." + }, "debug_mode": { "type": "boolean", "default": false, @@ -103,6 +108,7 @@ } }, "required": [ + "use_naive_approach", "debug_mode", "has_static_tf_only", "rosbag_length", diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 03d5bd89aab0d..d940e51a4542c 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -450,7 +450,7 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( if (topic_miss) { level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; message = - "Concatenated pointcloud is missing some topics and is not published because it arrived " + "Concatenated pointcloud misses some topics and is not published because it arrived " "too late"; } else { level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; @@ -462,7 +462,7 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( message = "Concatenated pointcloud is empty"; } else if (topic_miss) { level = diagnostic_msgs::msg::DiagnosticStatus::ERROR; - message = "Concatenated pointcloud is published but miss some topics"; + message = "Concatenated pointcloud is published but misses some topics"; } } From fb9bebc0703d2ab4c069952aba14cc4a8f48e9e4 Mon Sep 17 00:00:00 2001 From: vividf Date: Wed, 11 Dec 2024 19:50:16 +0900 Subject: [PATCH 098/115] feat: refactor naive method and fix the multithreading issue Signed-off-by: vividf --- .../CMakeLists.txt | 1 + .../concatenate_and_time_sync_node.param.yaml | 8 +- .../docs/concatenate-data.md | 33 ++++- .../docs/image/concatenate_all.png | Bin 0 -> 493592 bytes .../docs/image/concatenate_left.png | Bin 0 -> 44393 bytes .../docs/image/concatenate_right.png | Bin 0 -> 44147 bytes .../docs/image/concatenate_top.png | Bin 0 -> 286968 bytes .../concatenate_data/cloud_collector.hpp | 6 +- .../collector_matching_strategy.hpp | 82 ++++++++++++ .../concatenate_and_time_sync_node.hpp | 8 +- ...concatenate_and_time_sync_node.schema.json | 73 +++++++---- .../src/concatenate_data/cloud_collector.cpp | 23 +++- .../collector_matching_strategy.cpp | 111 +++++++++++++++++ .../concatenate_and_time_sync_node.cpp | 117 ++++++------------ .../test/test_concatenate_node_component.py | 6 +- .../test/test_concatenate_node_unit.cpp | 21 ++-- 16 files changed, 349 insertions(+), 140 deletions(-) create mode 100644 sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_all.png create mode 100644 sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_left.png create mode 100644 sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_right.png create mode 100644 sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_top.png create mode 100644 sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp create mode 100644 sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp diff --git a/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt b/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt index b4c49863fdba6..19c698b11bc5f 100644 --- a/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt +++ b/sensing/autoware_pointcloud_preprocessor/CMakeLists.txt @@ -67,6 +67,7 @@ ament_auto_add_library(pointcloud_preprocessor_filter SHARED src/concatenate_data/concatenate_and_time_sync_node.cpp src/concatenate_data/combine_cloud_handler.cpp src/concatenate_data/cloud_collector.cpp + src/concatenate_data/collector_matching_strategy.cpp src/concatenate_data/concatenate_pointclouds.cpp src/crop_box_filter/crop_box_filter_node.cpp src/time_synchronizer/time_synchronizer_node.cpp diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml index 02236966569c0..cec253e1ebb4b 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml @@ -1,7 +1,5 @@ /**: ros__parameters: - # ignore the lidar_timestamp_offsets and lidar_timestamp_noise_window if 'use_naive_approach' is true - use_naive_approach: true debug_mode: false has_static_tf_only: false rosbag_length: 10.0 @@ -19,5 +17,7 @@ "/sensing/lidar/left/pointcloud_before_sync", ] output_frame: base_link - lidar_timestamp_offsets: [0.0, 0.015, 0.016] - lidar_timestamp_noise_window: [0.01, 0.01, 0.01] + matching_strategy: + type: advanced + lidar_timestamp_offsets: [0.0, 0.015, 0.016] + lidar_timestamp_noise_window: [0.01, 0.01, 0.01] diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index cd13a80b42944..b6dd147353ebf 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -2,7 +2,32 @@ ## Purpose -The `concatenate_and_time_synchronize_node` is a ros2 node that combines and synchronizes multiple point clouds into a single concatenated point cloud. This enhances the sensing range for autonomous driving vehicles by integrating data from multiple LiDARs. +The `concatenate_and_time_synchronize_node` is a ROS2 node designed to combine and synchronize multiple point clouds into a single, unified point cloud. By integrating data from multiple LiDARs, this node significantly enhances the sensing range and coverage of autonomous vehicles, enabling more accurate perception of the surrounding environment. Synchronization ensures that point clouds are aligned temporally, reducing errors caused by mismatched timestamps. + +For example, consider a vehicle equipped with three LiDAR sensors mounted on the left, right, and top positions. Each LiDAR captures data from its respective field of view, as shown below: + +
+ + + + + + + + + + + +
Concatenate LeftConcatenate TopConcatenate Right
LeftRightTop
+
+ +After processing the data through the `concatenate_and_time_synchronize_node`, the outputs from all LiDARs are combined into a single comprehensive point cloud that provides a complete view of the environment: + +
+ Full Scene View +
+ +This resulting point cloud allows autonomous systems to detect obstacles, map the environment, and navigate more effectively, leveraging the complementary fields of view from multiple LiDAR sensors. ## Inner Workings / Algorithms @@ -47,9 +72,9 @@ By setting the `input_twist_topic_type` parameter to `twist` or `odom`, the subs ### Parameter Settings -If your LiDAR sensor does not support synchronization, set `use_naive_approach` to `true` to use the naive approach, which bypasses point cloud timestamps and directly concatenates the point clouds. On the other hand, if your LiDAR sensors are synchronized, set `use_naive_approach` to `false` and adjust the `lidar_timestamp_offsets` and `lidar_timestamp_noise_window` parameters accordingly. +If you didn't synchronize your LiDAR sensors, set the `type` parameter of `matching_strategy` to `naive` to concatenate the point clouds directly. However, if your LiDAR sensors are synchronized, set type to `advanced` and adjust the `lidar_timestamp_offsets` and `lidar_timestamp_noise_window` parameters accordingly. -The three parameters, `timeout_sec`, `lidar_timestamp_offsets`, and `lidar_timestamp_noise_window`, are essential for efficiently collecting point clouds in the same collector and managing edge cases effectively. +The three parameters, `timeout_sec`, `lidar_timestamp_offsets`, and `lidar_timestamp_noise_window`, are essential for efficiently collecting point clouds in the same collector and managing edge cases (point cloud drops or delays) effectively. #### timeout_sec @@ -209,4 +234,4 @@ Note that the `concatenate_pointclouds` and `time_synchronizer_nodelet` are usin ## Assumptions / Known Limits - If `is_motion_compensated` is set to `false`, the `concatenate_and_time_sync_node` will directly concatenate the point clouds without applying for motion compensation. This can save several milliseconds depending on the number of LiDARs being concatenated. Therefore, if the timestamp differences between point clouds are negligible, the user can set `is_motion_compensated` to `false` and omit the need for twist or odometry input for the node. -- As mentioned above, the user should clearly understand how their LiDAR's point cloud timestamps are managed to set the parameters correctly. If the user does not synchronize the point clouds, please set `use_naive_approach` to `true`. +- As mentioned above, the user should clearly understand how their LiDAR's point cloud timestamps are managed to set the parameters correctly. If the user does not synchronize the point clouds, please set `matching_strategy.type` to `naive`. diff --git a/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_all.png b/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_all.png new file mode 100644 index 0000000000000000000000000000000000000000..2410df95a74c7c8e82f5d1b133f205f46d1c5093 GIT binary patch literal 493592 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
=zNW>WDrorp$c#YN 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 z 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

)Nxo{v*B&%+`N=<7T!vTmKn_#TqwrG8I@M&kCH5 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 literal 0 HcmV?d00001 diff --git a/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_right.png b/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_right.png new file mode 100644 index 0000000000000000000000000000000000000000..cd6d64eabd49a1c63e43d6092533a98e0522af2f GIT binary patch literal 44147 zcmeFY^;cBy7dAYUl!OQl0s|-rh%`udD@aI63@u1^HxiO62q+CA3R2QBbVzr@4Barm z&_lcj_4B;XPtRZQoyA(`jGlGQx$nLAwXc2cJ@3?16o?3@2tXhZ(KAKammm;MKL~_9 zfp-h|mkIc42XMf4k$$F$2Yh_-%p-vB_g&?5T{RqDyLy;7TYxO>9qcUFUCf*^B$5I9r&wzHzXp*L-7V0n&7{py%eIm$fjX=i%bvqvv`m%qt|!#YeC9 zg8sRNCR9*81q7l8J(HEv^i18J^Ld}<;WfE$Sz6A&{lTrj`}q9f)UD{|j}|F`cD7I# z{71x&oA;zKF@!hxxYC~G#Y5u-G`i^+mEl3(AOy-X&-I461{o9Xtx96CpM*4@5_C|` z@JxPmc%n3IR(~M(Yw1cVCS%O!)m+o74b)lOiF5x@030bQ+W4b_xmWBCumliDQdU-U zY2ymx&zHTbcaFq=&i;4&-#*+p!vFl>e}3>kKloo9{0|5IhXen^f&bya|8U@cIPgCl z_#Y1Z4+s7qI6(8uxpi^m@-7H8Uvj4(0~9Dzwf3jPc09#f1A&?z6s-EFr<-olfC3pp zZVGOidt)%zxX%g-oFkee_A?Gzx>t7k3`o&cbT#Vo2ouya@b3B$cnxC3rwEvKuI>bFf0REOBWuhf>1>u)m>3)r z(N(u9g2VzfU6I{v0v5lwe`gUYRz@;>;&%4BK={!?`&@x;SjMKfZ~1w*@|qtAG--76 za*p#?-aTVw*-Ize4cJqvK30kmLH_7bJ3^mi01q`C3$Y zEKEY0W&kgJxstVugbY+6b>bo-e(cTwiyvbGulQogVINx^WQ) z2fHPJBz~OF@+^ow?4FnjAqF{?-@V!C5dMordik}d;oF;u zpII_yXtH`<>^OPX=lIM$7oGGkI6vSC4~I7Rl5-Z9;6gz2R?IgJ6aH_NINHQcj#VXnCXlE^yL-Lrj6EFSlyHJxR*0>GKnfxYG$4>5V=MLaL{W6WY=cN{ zeTm;MpTn?YH@5a~#}r&>v%?ff0rX+^ZpQoFGW@DpRKA2i~ z@s!7>w@1gdSnqNA1Oy(Aq+vtf1|Gow=KbJ_VBm%Z{YW#bAwT)b|~|c zJ&m3C&9Mm|ZvV_n(9nMW?z-588XCC6?dumOfD6X@{xPYf{D2Mda{J@HJN47UPV{SE zLeNC*Z+3isN1^H_ekzL(i(+Ja(kZ9oI{!L^^guobd}rmaoAabwryuX?P4Luyx1hT0 zSbcM75zrbZ%+z0;5<8?no#TGJ!0SV~3eLX_IIC+fDK|uXq)}I?eG!Cv2%ke#`MOdH` z9{d}4!FY0u-R9M<&6H(BE_mft--CKVn8i-FCi+ANVZdZdH{x$|dL zw=N?h7K2xEPEVN%^Z-BlYt_LH5>*8+XTREdPwgJ5%sf6LI#{XSFVR@L27{|b(`fkg z6?`268QHAzP(W^)&4~;%JdEsP0>b$34N8odz@vHP;R3|j$%UPJ3stwcyAnFk&yJ5n6avog>nj6`qxo@T zVB^L^2Pt2}f0O-=uP<_w5-}$_ zH&@}Ddr=3`SceKi|BIVD~3j7-WUH>Zd|Y9c@$r@-W+1 zs`Wuu)sd;|GqENefun5Es;jiIF<5ea1?_4<@v-TFK6|*AyRFg>aRhB;fvTGVa>(ai z_PKH~>hpm>(*JMCCiNn{pSX)x*XdCQ1a$Ea>2>r@kcF8FS-RPe+iB29)|CJfGTUT+ljfOs*U#q6-0=d5FMcA{H-pvE(s8 zx-~aDOdlpdWa@0+>c3CdCo42V`fI>?@=ZhG1m$P}9bfp)V()pIhnp5>K5|s2Q`2Zu z?iF7r>fq^4T}R!wZZoZ$n=S^&dN5>rPNh?7?QB5-%l*CY!-pN*kQ|Mqu=&j*=n_HV zX8&hOu{zqAp0%ggN9-_4!%;`Y^@=_zzk`7_`#Np#& z>QEqKgnJ!jeWpwd>9_pPE9f6S3`0=2oeqV$JrS5#ay)8yio9x@{0r<*)`g~#zgvZ z2Ke9SDC>kF2R4%VS?IB zTByTSx7~)D(L!bnx;@zB&!~4yAfUicN3V}+z7hVbXhmyhzfRCaHKEuxgNhC@DDDisbmEKzvy}lX z<1bJL=Jc*7=LUJzdk8au+0t$mSDs|g4w9zQ{WQwI_uABTQ#_-_1xWL@$+C_0CU)J^ z5vFs)p5hH=Zkht49uYc584T}og za`R#`q6<03fIh9e&&YTYR`O8$cvg1Q>Sfo{nWL75%%oB$PTZUea$|Wqp;B^wZV1e*-#+bdLGfsbHXiE&yN7if1S~)SiI4`OvkSrT19g@RXI@qhSOVC_N?-_Yk!jv$WZ69 zoRKl?B9{K=${q$cBF7ueA90q;bSHPiCfZ?-vZ9pNLLQM)X!6XjRBR*oEDNZvbloUAX0JsTQE>aXE7 zESx`!jUK1Z(UhSBbR~TG9-8{1ToW2c>pOCN%}#_94<>4nad1m5LQvC(*JB59fKn3P zvq8<@c0QsKgX*85DZBLl`KpYm(teJLdb~JJ-+qQnzsfFVsyYAWMRlv}`HLD>Ypc(v zxBOo>S_0XswUdgt-vF$>J#}|UA%LtbeB#1f7#7f{VNxrRudoStZ;@YxzuoCZ#Um&K{V4sLjd=AWE# zGD8Al53u1rFSd_80)c)SXxOMG`z&r;0S#hDMiB>8&HlJRqY%FXoE-M~v+gWR#C>O$ zf}5h$Y^}cA`;p_Kk! z8x>tPYdP07ehnx);DSl6dyZP;4!3{+*k=6aJAHpgrR~|8C*x_Zra#j++V?xfc+dA` z+wR^OkU*a~T}(gSKj?|9cI(;UKHpwj91@%JsUkTQKyZUso<*tK1z5kV3)nJL`e4*&DKrd!M`0_eBkE|3ZLH$RZ(*rU zpQ&Nyo@$X@4fMJ;uVJb3_{V|gYrukgIuxL$JHKOZ=#(Bs`1uEtPLTkl zhZ*Wy%svbmAkpBnbh5aGIF;X?DicsiPl;;Yw*6@opx0ZJ5(5$OXxnX{?1_{dLEwag8(TvkZxbH#$Q%!%cpAAnY;Hahh|Yy$_~C zJM3>ZqVhc*ojiPBA5NXFY9V6h>O4C4JL%|B`0bA=E0u+_VAfS(9$pm!(@*XtU1a9j5`^&0E z3&q|v@8|Vbkqai59-1VFrp~+K%|1?~@go_v{Qkzm-7nCxtNv~4Eq<7%kfW}B&!7H3 z1S`aa5ZB0~3T6Ny?;evVBqZ_CoO(%LzLag-3jtUO+*i^{hlZ15JAJoUT=Z_cSNO>_ zFn-ZA@+Z58xWZRmmM*Wa5bbmUdsu*Xew%bglPpbdIYfw5*~d&xO-+`V_=&Df4a9cR z2C(fSN*Q)*(M8FK1~GSbwzi_sP|xKd1v3*9GM^Lidm0)V+(*%tZKI>SrpKohrS?11 zla9k(PMgp2+Vai5EhTFeES19II$<+aoe&QMb=`^h9UI&-rwt_z@t@b%kAG_ z(-rGced>&R(YcPJD#m-!Lf>)1Zs_)MZYb(msp{KCYto#k`$BAFr6ywpaXHfdLjEtn181arweB9J{#w_%dbn{AyKlK^j?EXmK*xe}G zyv)h3%dag^sC*QZFn~I;Ox~!=J5%sJ^i4-7xJgo7Kq8T2>fSEOG7!+qeI0$=crNH2 zy8M)Y+|0unnRJrCGj)nw1hv1&9pGc=MVWf{=0r?MztXnD;j*K62@B~%Qfd38BZNfk zt%LuTzV546&8sg`9LEy-CVzR|oi5~my|(JKJIiJ}BM*a1*6oBYe`N78sR3R49NI9( zcWf@w%<56-=b?UY37PZ0an)n*?dLSjs*lv0X+E}_aqKHf1D}93F=U9OmlwGq$464h zh8MB1ExJan-sC07%@kdsoZJ1ihoQ^Uh*gu>(W;ayTrYB_?2{+{@(LRjAq(aE-KXz9 zwGizS?U|e?P;p(W$H2Pkb>tSl#89t{FQ9sBNP!g%QG_-N-7Ia0_p5oCCyLrA?&NZT zZocVCe{ZZ95!adSNfyjvdQxOaNk^w3oYr#CP0*j7(heN-3h!Y{1+qkv%9Vnnu>$F_ zljMfbn^2X!kwHG9F)eB(US@7qX6{@Y%C2u8-ZPq$Q?P^ABgpJ)_iufFew=-o-X1R; zjWrZFW&OLP3b(oXct>>GC%9+zE-M>16>UnV7WwhfP6i!as^jBjT$zpGEnJy{me^+W ztIn|P&vY@3@QYwF5v1vbG)0GMyFfoi>IrDD1tmBpEh{T~Sd1Uy@(}oBTh2e?^u@SB z6<_e|)-Ih1dY$vp`fWQ!$3`1Z6s1v8Q_H~AJG(TPxm{M-^2hXwU%`ob>Viq6L!_@^3t*@;lXq_dag$rJ9p5 zd>-GHrt^dO50z&<7d3uCOQia?g{i$Li9dI8209API7M9e3KBO{MLzs zn7)pj5^sdzFBLU6)j8VvzBjC{z(I+G^f~W5ItzL)ao|s!mZ#p|`0B<^mjx{n@SBr6 zcJvt;EjG|gjq2ksbH7)dJ?DK$9)4CfMpxSXW__hupp`1*atnnbbfgg=OT_Ivuv(%4 zZ5}bLuHADyKfJxVw#F?W5htG$B%gy1F1p@?yC}bSkp(Tz8M4aMtNidu)*k%Vvm{7soS8$mH0`*O=xBZdLgZ zXOud{aRK^x5IJJf$m*T8-$D?*CMV;(v*SfxM18xy!e}7pFtK^ud;fY&{XFzk?4YHv zReve;a_vn5AvHo7u%N8Yy<2NHM_C#3;m5PyJ$+Uc`jh2ey#9OZIn^txDS%5=JC-&y z#J0D$|EB4YH}T3DYPy&eRY?<$IXm+jrH1CSonq8TcobC`KsZwC7t?I@NH75Xv64Ila8hF0eE+3bjTQQnnGwqx(&at}Wq zLQbgHK0c=johu9?axo7Y?(-|QIC;_N`yzBdxH;ya<rS}&jweW!o+jouddYvHkXoL{wERSyo4yZikmZf`+NfEk7F6FmXucw%A+^oOL%^W=V`dGnrT-*OA_oL2ZI#37n=!+O^7n(l(Kh8XGW3J$lSRR_lXGUa1A@-ee_F=Z5hcC;Xls zrSCrrrJm(IVAhzdJlZ6@CdJPDinH+RDJ@Y}VeD-)p3xH881Y#>)fRs!;%krF(EOC% zj!nKqHnDwZC;LE~a+$6waoF;>GTCuJ26a6zm+SSc@Ey8JtiYp?g(?img(fdz(no*= zvY*`g2h-vwTb#>p00D4hLjBXz?t}A&$C2oW4$vF(R5~)As zhI9n2vqicI<1h8EyRAxm54?AcrYL$xQWN!!cXLwJT0B(o(N}?a10L%ws-q z(QqoGsdVjv6S}p;tWz$6qTWrlsai3)=2x6RcV_i?C$!cAI_sI(p88JbS?sk(E_+@dcB-K zSYUpRYxSo_ZpM3WyrTm=($i2WQ-4DJd?N5K%lV+1XmK%_=FwE7Sz29x8yuLRC{3qT z`np(>6x&r8QtdcY5O*RwvR{2aG0Vj3S}05Ao4%%2sNKwa*O>)%I_eIWp}2RZ~8LiG~jf8)0fne%w^iHW}SH zhh|w_Ne=5JMsorazu2k7!4d@|v}E3mJfz*=<4QH}H@#UIGU5YQ6B~vBTYGym-nL6= zsA|cK8^|pZz1{!r=ES#Z)|FwpkZD|>{kJp0yRXrMp0m&Pb=LP$gB~3-0?V!av&? zqxX$}wUy_TF^3k&%&Z%Yn>1!~QX&bPowN{Jmu|GVKIeLCWqv_!)_Y!&bmKuA%_k0N zRAla*G&}^L@g-^+((}n%Zmyv55T57>k_?vze~Z$vyqoSssuWTZ>E*%1X3GRE`B zlj9W1=YSJ^9@HecliXS#TBFA6%`nrYO&nM&-5n@%eX)YlE%@1tGiYD&A;E%?5r0G@ zUX7zgDhp&>#LC43rMK<>&gFh9{?V!s#qrf(b7m(wrM}FzDe1t*Na#OEkO%0E=y_DsujAbOeX%$ ziVsz9oL}emKMK;IT@X`qCYPEfL@3C?2>xv`Z1r?AkT9ZXeFe@Pr)F(9^1^ zC0C&rQ?J65BKKS+YYxcj8<}I)QlxTxn{Kvq-bd|T3;0cw_uh6)pB$8UgfbZz+|TWU z3fj@WG%=tf7|+l&Zsd23Sg&#;XUbV9chW()pX1}?rbtVjF>KD4KgZ;<->~smyM^%H z5AnWWI=}mjk`rPZo!L9dl|;8d3f$U+(|Q5NBi`u^*+# z6->QNRWz3F$yypHuLc=#K1I$2OtLAhx5yFpqPI53tSdZ9mjoxB$F#m{=3n^`Oz#6b z4_+DTV_+@MJ76({OmljyKnY=|evm=w6N&uH@&S@zI&G7*MD<$nQJ(8O-{rHyrJ>`h zv*gH@BfmmaL(M&uKTfEP-iNJnj}?xO~HD}!z z`?LTZ-07jUUq<(>vjXJ8FNfEWqm3`17m>6=x}z7b4~<_o5&n+JpiI>$O^?j5;D+9j zfc+YJkU6fw(@VCddlQaVPtE=!<+6UVk>tYOi}iLJ*7?j_-2(`SXR<1}0Agc9fg! zt*^}|h8H%FTmgU&mE#s;1}@gQB}AXrBraa(wqB(Wt?J5$A#U7TR_E4#{(W>LJg(iN zsQ7G|=RO|kydbgmgf=%XZ-@r7YIbd9m~}2si5W+m)}R2NC<{`h>22frkH|=sx)@j1 zn2jHzOdZT6W}PbE^CMrEy{mYRQ|L~Z78_#A6tgWS%t#I*?JkOn+bluehTN|Gnpyq? zFO^U~g~hxcmz*8q&Rre_O*Emw3qD?KTxWscJ?+lWRG%Ccm~hT64!ybqIUbOcvE8XU z;M$G-{PG=sdEp38bW+A(W|OnTsp^w_w`TGR7`{=LN_kRiyE(l zSzh6coQV!^@M+3Pv3_b9FVxAIvNiRC!F|%oEC(LMzY~I>gji`u)oYnXg^iL-mM`BW z0$FTHYX0Y3Qg|sZU-(A?|MGk0nGXA3-y;bN(5<_VGY=OBZ8APG%xoGiNH(0z&ftdZ zEnW|s)C>C;wX;1goxqY@=vc_|-e7pbG7D>#|I6);|BGC`oqK>rWzw527Oqtn{W>Ss zmMJ6}F_|cf4{)HVpTP&#XW2Nbmj)B)=eaKxb6*10dgrm7zRv?MVaVJ0BX6;Tw_m=Q znY&_Vvt;WPws0h=b0j&Thy+9n1{Ui(0*8q?n>fU?TdN<9(nx%5ZQvNiBv~1rwmkTd z=G+5>wb&?+M3L;OVJ|KPrBATD=f1;X(7oETK+GjcBOB4xKo!&zf@l%5>6Nre1gRn@ z*C^H@R$n1Zpg+oC%!~x`m+RsF%LGPwOPN#l439CFzT6V}Ec^19Y*3jAOP&0|GkBmo z>n8%eHR{M)t4@6f_G&l9c;UgjeJ86LnlCZ8RMLe*Yifjg`}!76_m>2{HpvsU6P*qx zxqJKj7M6c#l=N5tPH4ap{@jA7@Ach+0t-GWqG}b~vmZaAj$ctU7UXc%Na4vXqVnu6X~BsjYK0xDzMhLf+P2q)rx}0#k{Sq}odL1o_F>8t=Z%EOWsh1c2lNBC=&QK6Wg*C-T#%3oP zHcn16*RKxX70fpzD<+8D(3MrEr#uDOa~TRIzsBll;Di(6@!_Rnt}N@Rx6PE+4_>NG zRK7N+A33R|0~kv74U81Oh8H(g^PXSFo#8y?p}70H-u(A<(&y6xLXS%00Phei>A$q9 zHkW4n{T$CMNweGJyFps$*g|xy>b8P6~372=r7K5Px^E>gw=jG)E*rKk!e#kuT zRmBli!uEUeQ_N%_&wl}X+mK*O34_Rw-+ozA;(3k^E}5xj=G!?igmlC9OR%KDbe?UZ73?sQv{?nTbgK@XIQQJ_4Ftly8kN=$Q}m(g~Y?1^AT|0o(wX78TM_9y5ZPG-B&H#N$L9?aSZZba7*L#muj^g4gkRjzjWqg z|II`Bj6TE(c8pN&!l97t9o)mmwzAXDbhaj!VQ) zSTe+Texmp&_(I+OYPePcoO;CcFSgMBx7wa3Pt-Iix@0l4=_Sb?;RO#8cH>-2;7N*S zAX98GK0dpD_T%x*YByX7TwGj|Q!)^aRrPv>i-$in1f7@hh{fFSZV*WiDqS!Zyd(%R zzzYVcM_k=$nDH&1*!nK8`F$*(6dOZ`J$z_Leb#%Xa9t3>5*ZA9*`95HbhVZ2d|2zx z$Dv-G_VY?0_LN8o*COY~Z7%bY4l3<($P3^hQN|s}y7Z$3IBX$`*KcgEAs}x(!*u8> zL-cv^24Vivw;rv`!;WUj8DU}HHPP5J6N=*`emFPXz1lxCa3$t%-KHMROhPNonilqL zg^OpiBJ$C*VAvvVk$*%%K5PCMy&#gmcVy#`*E-q5QkR%cIJ)M{i;dmO#H(&3Y)R13 za3X5sat92a!+Hiwb8F6s`J9{z>``Zh&$02$u^oUhjx@L*8BP3Pv3hopU>0LvUz+&4 zcfpq^R(}}<6H69GVv5om3gfVc`5N_@qT{KQOFdAZCKBGw`kds)PN|VKaIE1)i4N}3 zX2(Yp4b9hC7YiRG)SzefGkuGld+nu?YfRg>48y@EB zW%{q$&Ka_nR$&_2aDA`r`N{0rN3~b_A&hd@l!ib^DJ#MmqY`Cc(9Z2yn(h_nZ5NN$ z0|nYM@pSLa$87BESyQ&zbHt%5(gJMY`tP%H#YkG(cl!sfoTNGl^z&{sjaeMet@t)6 z@2kVGqz%C%XODj$yr(+CDaq&AuB)Gag?>q!s}Bn>FVQxaycKFxM+|o#^vW zwVwJ1Iprf6?q~I}EwSqj)lT$ybA18oL7Iq^bt9DzAaua(u!Zsc2SM(=22p2KtlO$> z0zFa4VRi45J)y)!5x0b^2p9H0oRjJw=j2XIOdN8j0jh4$9)G_fxKO)?n)Bq*U3{9! z0MJJk49Q{(T7e!i`=sKZKQqS;_`5{Xh-m`qW^F#87pmz!^;hT06fQGblQ0ga*TwvY zp#@_A=wz-q>lzx$F~v)jO|`7{rDU>#ujjiLC)|u})+5!v7mB5ZmyQv_qHx_NISGQ- zgK4b;Ebj;{mz5OW7a|`OzDrbJ7)fK7buwq~uMW9AfyVJ^AAvo_UC2dBuQZsJeFmzs*4C8KQQrngTJ9kHKO5VJ5`VK<=fc^ zo4ajKJ#{jjuq)n^o+lq(*KF0U7k)dzoyV(PfktqIeOV7V*@lyV3?|yo&xNfksPuOD zsbZMMx3&N}_g>?1^!HYhih9!3R)Xy{G;W~3ez+kXrgH(s+u`w#w;9$12JP;La&sDR z_`gyxQ6=TG2}lZeG92XL^fwH@oSqko$`}t)**kA1*%L`wYLsXLfHJ@DaB>+zq4ITe@|{l7A}jv%#GqiXyN3fA z_8~DnMJu8R8;N$^IV!_0xj>mRM2BYVcP7ZH&ycHG5(D*Yt(e_ko0Y4dbc|Ak;| zh;{*vI@yDFWZf$~R7c)OfA^Yb*@r^CKg0`QRMfW!zdzv*0OEl>+M0o2m~$X2i#=w1-hixT6b!3|WQn zp3nBTpVm2GvGNbOz-?wz+Q)z>V#-gGR1N0%{5K2m`Lco%RmUoQO&qugcAJ!-C~xQs zC#Mv6+p|&sw0vwBPGHy6&_otb^T8e!FZ}tdt!g zD=iI!D)4-g=6PUd?Mya-oWTV*#UcE_zowGoxibm|$7`N4wd z+$y3&a5LJrx7l95e(jCxM{NIr5WbKRJZSjz1i3ah-q$||rQelzb`cxuRv22DpDlpv!Ti9nGO#aPs;S$`6z z#oK>vU9X=-T53SK_d^pTCCL=uY+Vy=T@lQj?n_1@alr)$au}clJL*8Nl?Rsn#C`+e zdKhB;#jm%mE{$yP&H!Rr8JFT{c;Q!%lU&1Gjt00}6>eY6@7JV;0i_s?|2Y?c zRg8~=+H)U?p6)St?q!AVjb^nbe-;Y)@Zk#kyoPs;oN~!Or~cmanARN!2v_BK*%P6r z$t8giiGrLG_R)upoXNfzAgiH?%?$M+|0g6%MaKQ@Y(FG>8}XA*p0L0keZx85NsV*vgQ~CG=R(h`Ul+4y4Kf z^kz#KXdr$sEY8s`IMQ1toe!PLCY^h5(z*xl%3S9kK;!Qzt>CRJN5G%rt>R>%^#Yd! zzmMnZLSIRFjmZ~khb##ur=*0i_2?St$>&V9XoSZt{*VcGTpWGO#>N3HALtYP(NaC+ zkPu*`uyKzwlb_^C3G!Il5rzAlGkW|=kYs6mP}>tImT6w6Aaslyh46->GKSQ>E%-nJ zyAivwDc7QuA5)C9%}B5+h>6Er`{KR>nn2(g2E{7ksBxiT&G#YxmiqV~4Y&yV9~Sp7 zoxY3LH7d>lDC(50iJcwMkvx|BR2Z;UzAr#o1^fZYBRM03#4d#}i^xTmNe6UWIqBqE z|DE2F!(H+(W*eN}>OPo&Ilm$+;R>U7SRxlC@}lG5%X%O)L*naWEtrgG)Ls++Q!w`S^4M`;btT`Z= zt1Xi=C0jNIa8qg4M`rDnfOZWiQ$Sh-if8LFR-O&z_ii3D3s>e*GcRCu3xd)p_6B$_ z;p}g?7qW8wM;45k@SDLbeCf$20@g8tkNEeqGk#BO5&bMIeDLYHiK*>vV8*VFG2=dn zyfLq}x}C720)0?&f_{NwAb2j|^`N?f?PPj=8ux(9es9h2^@^e(^ddjDPyqyx9AVG~`iKfwopn%U71{7x2AV0Q*f z{kcIci395)>K-!1OHFR!*7f_r;w=_iVoawcIjKoI_HM2sqKal27xrnoRR$urof8xb zj0rEdub3okcAWcuZbl4#5id8=NAXU;q(RS_Jr)QxFuLCHx13D;$9}}HfS&L7@hws^ zvSn8)1_lP8_yXo%j**BsU~WJLavz`QlTVhHfC!kBp!+gJRBS(R!Dc zcG@Z#)tKFS)!pI37CPf8$#Nq!uf+a0|KjTIP7ElpnAppFL}-%j&bBt}j*bp49$`#% za&H%wbo-_qL$V7HBoORg=l8}dV5UCc15Q>~;$T3a2k7zJ`D19(oSS-~cQfknkV&HH zB;5l@3ZVBubJb^UeC?tpHSR28ny{mf!P+6J{*(eTxovd)9dqe501x8t!!Pe~`f~Wc zjN@zU!=)g12Z|`Shg8w}%;?~QxGfe~Dd&YfMn~Gv{LGgRCiRY7w>f9|D4oIA@v$wC z!k{gmUx0c@)UvOhc?kW-P5b||jtn3%A9-V$nVG$}xF1XpC{fu%R+1BLjAZYAixLJl zc#4#((h6vByybk$QP-?frOttuC8Mx_%oDLk@`EKiZ(0~;Zgv!DD!3|nJ2=~<^DXUD zWtQ_Tq51jMuh&!Kl9KvITTJFP)m;``kfC1Pp(c=wWf6n2mN~ZRXrpaB zRt0Q7+4vl=%yy06%qb16ME|o_S&7~c`p?QuZUzzA*v~$e0L^%1rYK)=5~)M#xw$@P zX2I`eJ#O{*pP(Z3;L321flKtU0us}EK|neVDcDR}*1=bS8_;KTi^?IC9fu z)%vRO__s5ej*;Jn-rD%D=oTm^+xXQ(5_G$N&V>micjj^^-}; zWEb+lcUjC&HEe(Uv<3{o!o%~Se|>~QHn!r)@0w3YWibl139}h*%DORY=|qd~n-Kv6 zYM|Y)hB|*rxCq{8V-;y{jt>Io+A`}z7u<}I3H+*qYAtrFbL+z!Fejstpl)@0-Wo&It_L1?(y$$1#=WyLh zDS?`xE&KpEVpk$Em*y%Eu@N#j=Yy5aDD_-XS6BCK>caGF#=}X@&pry|5 z(9Hm7@j|GFkFUyz(QZ(s8S5UFxn+UA0;v!hv%u0Rhxmc8mIJigR98ea}h>a@vM-Cwz_W@YTha*cS2PH}+Z|xsJUaIPYr+ zN3Wep1OVB1RZTaY$e+z0s<087Wh`9&ju4Eo|5S-Xwx?fiDx$BcvSm|kzklP=bYd8Q zEA7kx=?uDMa7eN%E68?Ig#WGA5G3trG!eAq`Ql*r#F#-$alTHfhVdED^&fPD)QX2~ z9RtNEn#A~|Xb#ZNz~oxZ$wg+u5;*FlS#PKq)i=fBwlE2m2;p_4vIcHrTEC$!<2CLM zw8uRcT%rqpya22edVX@W(T@E&`!;29RYr=`uCJTLmLzy@(b6>`QVP#bjG{0Zn4wYk zRx(l)iwy$$jmst%Db4q4++2S*|9F#mD6+rcv0+9Xz6jWqe0Tmq){Q8U#PA>McnRnR zUGaIqEY^^Hg}0DHu3jh$o)v$}WP_iGta|X^%mb2OB>nA^Q0asD+AB0Z>{~UkzV2u1 zwXwjY_~I9v6XS_pdKP5b(@h(IeC54_PI5&2Hs^&sP1?QXo%0G8r1*}pXbQIV(brxq z1?B)K)FlDa5{!o;!rLMjl76^q-syD0K!@__RZv!3ehGroIurLy;wLW;fxe8?IkW3z zkB|8A{J>;QqC$KLg4dJs`x!UR_)znGFlex7ptUlL4){g!^b4;9@!$V| zGwdI5x|C=aK1h4c@Tf;IZ{)oOl%WQgo_W**s9?WiV$Z|v+-H{ej(_7|#4d%d#4a_3 z&WJ3R&F}%gq6kE=fw2`oMwkOLAp1_+b5&pggfF4{?ktX6m-l_o6XPK@(_OpIJ{z6@ zZxc?Ff0w_;)XoBaQgrWVHXKsjMWAV%;e;vNx9NX{S`1S?Tf6#P8`Ade?|wBsQ)u-P^z*td41O>plt&1697+|fPSFU z_Nh}f2ba)O{KpSMrJa}s2k+W*F1s6^#<(0Z8P;mBb-e{AB2mn`?mN>sa_S|}f}<8^ zPEo+ypvJ_RdPmZ8&R!INUXjfL;-Dvh8{uGkCiN7oz?*7?c_J^Cmmi_GBaJ3}f+{MQ zS|>LJy5qw5o0+9a@WNLR8pZ}l#I+PfsG-#QPnba5a6vG=A^H*!Me+KoQa=VYOgU|x z&SOWN$>DUUw$YJMbzXeOM33zEIWIM81!-=-XkNND2-tLsoV(%*^BM;)0IkAwZ`wn4UhZi$0!63x?xRlnXss z)x(x*>W9p5jw#@RN>*W{wWlP)(KGe$r+qbdUR3GkStRTpNIUGn&frp+t2wOESXpW! zL_iC1u*T7@9A;SkpxPGlA!w-K#7Pt%?Kd_(2sds%JWqxHePCf_`ZWokWzl#4WR zV1To&ULUD4W=#b6OqV&?uhh2jg5q1)7)6bByMn~`?tw8pU3h`%0vYL#rs>E<7Jnrq z&JtdR+-6lzl+lp-B;+l8L2_33Eu2;hDv9#ruE7zQ9AGSm|^_;fQyJ0bZtjoHgN{@XP zVSSYgmodc*z1h8Z6u^1OM+Y3uFnrMz1y3+)~oEVF#R1+(Wt(Zh8D;@G2} z=)FSMKU`?x|70FuPJ90J-QDE$^p1($Mmkl|ee7{+JH8b5=;&yAfiGAf!J?>|npZz3 zCO&kTX97v&Suj0zcxYsHgeGgujWbUa&8d;&FhpX?bsc}1zclgkD4S(4w(cgZs)!9S z@08kxMis-YB+2FQf+Ljq3ANNi*1&SVpWQFirUngex|RSxw1`449C6dB8{6(nuLtk` ze@uM`Jk{U-zZRvUL88zgE29uux2WvBcZ$sHJ?>4CB%3Sq+FUZSxwzpYdyi}Hy)LeO zhyPK1e~*6;b#;w<&-=X2>-Bs+pU-pN{+*3>D|~zI=(k%4mzzSK5bJlfZdbAo!@kyQ zrjQ@~tj%$?c$aZ-mGSp@&$E%ZzzOt$E#n^QEyMJ+*VTLs5p*#5fGu^+o4moXn)K)7 zj$LF{35zZ?N#$F2lSkfrM3Ee4?f>Eo;s4=`i@Da;L#0j>qz!&VV~cuLkJXdS{^|R` zUVDmcrCA1B8!dOPxVV_?13?sv^mL%^x!wFRk+v zTyVuXoJabcd^z(QCQTCqY{1C0qnCSt*nzO>&{fK|7}F@*UHug<*&)cSvy+2A#zddd zY%Unoe}w^!vY;n3z$L_eOj+?A?v>H29tjCR)or)6}#{nmDVe*=^}IRYm2PmL`+YcN6jb*TGPjIxjCg zeZRw7W%+$lyfP=dAvRz}K4nUR5K1gCs;ispxIG>e%RS@Q2mb~)eRk5#v$mQo(_#Qp-75B=2$+@1`87srmZSw>; znaMFOce6}wM$2d;oR>)>ub)2t_MZ0pq_$VEBkuL4(^i{^oZFG_5uip2HlaLFNgy(? ze=<4)q-tNB&g8{i>Dt7=7iqGWWfZtL< zYN_VA-?chULBwiZ6cQSAaC*xa|CnCjbM?mg>Oqg+=<3GhJ3Y%b2S#)^Vu0Fp<>l3o z**7K*r})j^spM()Y&fg%6o7uQ&c*OsaIndnYIMGj18x4+==}b2;c+nCtN2h3_VJm3 zroiq*DRf*ZyEL1&B=_c@&W`3&S0lQ|L1(JRS3f>i%vlR@;|G<>>9_KS&G0`F~^7%DwO?RXVRIjnbq3SXjSdQ<3# z8;2e+5##NDY#gQfo$B;z{b22;r=U0TB?=lty0_T3kCcfOi*_BZ2Fk8(P-*PWp z=8dCOE1q%6Gm?h~IMU8GIRHLo=s@@}vj4ZT>!}}B*Y8fEF-% zvH65FA-u>=WTtEgLV%rX9^Ji9(D|S_FL_I%J+~+NF|&NHOl)m`ue2KxLR}q<2tjB+ zGP(})*}}LCA($pO%c&CCbSPdk!e^-;{(%zm|6{&h7F7!eY+Ie9It%o-VVfKFHP0A$ zKqPism8le9KvNs0)Myvr#_yoNT_&wX_9gy_M={3eQ@@@Q1?>Uu{?HB#^&`7X5pspST~` z@l1CQivWboY~>TMpx%zn<}+eAJGubL{bjmSns>j4?1Vj8L*KqsotGx{(N>UTm0Tb@ znRS2@k%Rr*qLJSU7WsDE?k9PI2cb0oYNjDeu;=B)cZ*;E)93F29Mn*_lnbn1mAHQ5U=j@es=ju7dkafW3%22XG{}iG(*vm_Q4?$ zg4yO^{B4+GPZ*{A7BBVOb5j2aIx~4^WbxrWBNIV9=okRK@VaM)Tnnm4 z9d}Q;U*ns&4Vmw)sj;6D3wAW0jsIhA?EPoTcMca03DWXm4Bq9uP1o(r`0*yobpC3p zT3+(7O-j$s>AoHE>+J7AH1=30Hi2O|2MyAb<;k%+%4Daqs}Np!=u{f~&ToAAt!(6G z$6?Cb($CWPr;%;#C#lVHP{(E2k%lc5D6^&KgU z`=*;|hhdbnN!=TV^z^@dEy8TM&rdG7CYIWIFbx;|EprN#3i)5bw zK!>j;tV`9NgXFVX)%>n#$Z5UQeq3JNmj|jveHyE<^m()38f7!eSAhgX57Bw!>Y*l_{5h$&PX$o7GYdVxaep8 zDV1CHyB|ilAlJ%f)j&u0xu=;~LnT}oh&Z_G*Dao`T$y^~DMEcplJ}R}H1MB>xMp3Z zQmDlb z7)ffbT3|(@=y^R-?N(@LD}Fbv5LT#b3srbIeYh3L)mXmDUTtx3JLr&Yg@5Fc&@pJH zaknVePP$9yt$!P)s+vQXM9Edpf}U?^=;k%Y$Gs1Xb2U_qjVaEaT3r=P@8jst(fmbQ zT_9oh$XRA-M_`4khrBp<)!wDXO~#_UkTJ=ceqre6+c1_3pplGkc@~uldkOrIJzEH= zh-)|dN-MG|g$W-<3GlD=PhhF@cuq<$LLj_rnVtA)A4PYvVOMd<>3j`0Zj_S}^|4S7 zpD4Cgey+}as{rsKS1-5XsgDI91@$DA_PZ&NFUJAkGE%N7H9x&JR~n6fD){s#671C0Ny@J&_CFCV8EX(;U?8q3~;?MAMZ zm$+l3OpVX>YXFkQt{W;aSMWUIb#uTZI_#`S60r%BVzw zpAUT=1#et>6N}J%U2SFm6ZK?L|2%iiPjoapkWQuTsA7ko`k zj=gp63QT6Hn*EJK7n4T$dtft3Ck`Ic*iTf#`2(m-nh3WaXla2aiCs=0!Nss#2oup_ z`qO81fbMAP(m0X12yOqM~17hP;iNyMc+{w7MWCw^MfT=QvEUgo2$bFC=7 zm$p-N=Q|HnCxGbXv%X$CR(%i;%2u#t3{290>-T}7~i)7bZKd6 zSEhnX*mHW(gCF7SDPNNvCIsDxyH^m$-akS@-uR3@X&?8-6-%%Pq^#j##vZ1eUDD}REyvgMbTjZ5b@&}kNoBAD|lV1U>sQ69Y4@qetnehtqKwkuG&8++) zqN+kqeV9S4gvZj6o$2Eo{K@i7`_*wzJt5&^FO2?DeW}pgto~v_YeU?vYhl!uh5Jli zhD;fu322x6^|my7{GrsJZI_-K|663I!RCO5fY4_r+E{qh{6wovP0z2H-g{JxBHxPM zpLi{e3hpP$UG}LhW10R2ld*8me~HwZ&Q!bEF+bJ%*eYiv9t(XE@aYN7Il!O8BUPj| zuwg2gwj;v|AazTl20mXraksIg%j2{IXC^^51hQM+;ensCA{>Me;$C$o;`iP}qKt2Wnu2a{-m7%6)iOb&adj%EUOW!lE7jeQ;5u6zqHH>&!E zI+EP@{$qlmLCCMzec@eb2UYXkIy%U6TAd1mU0RaZBT{}d{syy|gTk`w4>E9*;8e^n zUy}aEn8wHes=w!L6A)z2K7Ym&BL!e;1r>0b16PCwr$xEm&<+vX@Dxg@&ck`Q2ji-8 zBvmx~wY9>YI-V^Z*40o>`sBEFQ|%Y9w&u@*Zp9Gx>Oo}7okZEUK=!$Re*zxJMdq~F zIo-mB_%D{9|NHlw(om~w!|fhr(n-0WK!dIG(xkk6{0&sy*CCfVHQ@B076tmG|1sE? zu%JEA93{U+^g}HSstGg?PDDvBW73>B7R)t=VEjWgcLO8NvNPOXn()+%3Q)ok zK(=Dx%Ut~~E)c-${9vYk+OL z!^=+MRw`mvZNsU{<`I1fd&Jv%xxw0{dY zdFI2wtCRB^$!<$GI_h4(KHJvz1e0$#N`gA@Zo5E+i3#Jyss#N z2uF@Zi(eG>3LpgFA?h;&9P>7ztSRWIZ32A};!$*MHy&zkgoq-BL?0(zH_wW~FTUla z?0}A`iFC|?(}I3k-9I^JmnF;DsdDcMVS5u&}(# zr6s+W_pUZxzwllM{XFHh3f!zT*MI;G{`QTXfqtxo6e&Y7^4xx5RW?KUy|Tt4n`CRv z4@L{~+60Xa%;==KU6CoeU9fIEYVD$d{Bt*dQ%Do7nlvlEzH_#{DUy?*2*wE~-4Q`^ z%{_HVA|`O)G2&I75HsW^BepeqP5NO50==E?KJz1LO-Ley0bUq|_LI_?Zk*j#g65>YKB22j)p;x_|r0m z_t=V9O4N|-H-b2?1zTEJ$bg8vIA8SUor07U{8B@f3VV7_&!gB(B!77l$=$Z$`gQmk zKb@boTi2m({*__ksDiRYqD0exFG;7+`jo}qDtyg&?Lt-LIQ3|oIKIK&4dEFx!I~cQ z7@erGkv0KC42SB-8IRrinst=l;xj*F2#)I`(=j}I^6={dpYg*M%hEZ1ZxDrPZ_^$d z7%q0alkd=y4^Whsmb%+GyG3iHC{k3x;+#=+gKb#JFY(P!YgvorQb&8Jv!|YUk_Vh& zx9ZK$?{UGIAAOTqq%!;?>Zc1i2foT(WF5#6yiBcIq~O$u@lJg_DZ^P~>0mXMsEKlV zhv|3ULuIbX8(g2sKZh7TE=(^Kx0`Z?v_t!%h!@ANFC2p8$GiG;&qAdVy{mKWXeP=L zw!&zi)x|9K=ubjClc)l*$EEpT=3ok!FM_cI{!NDoWTl>lp<>(3j$M_OaqEmJoij1F z*C)}+(h>JLW7;CppWWfx++~2G9}FZ37w8QBLgFT>Zpg|B=yO2_U!~pc6VT-0Z7zBU zRuui`yV4Ppi`wio5wj%_WAM;rZjqf;?jx8kJ?iTOjj_JE^!D!XVtK>bTE~K8jK`in zyw}`iMR(gvvWR!%)5I{0-z>{86lTHcrpdO{2_26hu54VutqHa7p#>^JeFoKzJSLvI zA6>)F2GV6l;SVU)XTv+o7s?<@qKTFFIPh_)Yvtl0wIJJ*{h<7K@-TJQ;~-pG@X^z| zh%PqtWI;-b>rk}@MvX&T!$?OZcer>ad-@TGx69b}ebGbMjUMkvy~tPEo`5 z>pHJ&3$(_qA`v=GslJ$%p;0mF6}S}fPQyoh4U9q<`gi?YPgl#cg4!y5{s|(iq?o1O z;kIDBBKU(;%gZ)EwwfPkaC-12MzOF)`p8Sx)Z^^&m#bFvQkmo>8MS>{=K7<+8F}~U z)mRw9yD+5opB5-))@v86%T!eETv2IrFl~IpAWOx51dZ6n8(x%DeoxUg9RF~bt_&-? zr>~{`JpoJxD?M<1^HPJv@E$%LOXbintRg_AvNc)%1{ z2yj|ATTu)ud2M^w>O{>7JL)NxCcs~5eHX#%U$crMx^Rcwf2xGapWmFGuY$WPcU8{r zNOXi-#&O;V+Qt(p14doAcVDUdK2xcCEi(XqSWmO=nFVsDOVlKdEB zR(k0Wk2VQK%|Cfhjzm)q`8x#Py>&=-efzgd4d7B*C@1nJIo+0?chvQdr3W(GUE5KEEENOiBS zd0#w{&BYL$qnDRsRA6muYqa}HDk4R36?1&nEz_8!!gd}@S!alDoO5#vF-Kc+xGA#D zb$Z`Jj@>dhz>uSEe0YbpMQ)~SaBLEMlY7mbmA|t=#WQw>$D4`}f@bL6`**12mKVYJ z%qQYBrqL7%iOS%;Mf-HLiZ8@s9%#Co&g;>EDI(1M3&89i8!PODPn*1C+Rvq#nGU69 zL!_1_kG4hvLc7wyDBo$zTO5H#<(oZgt*vHpc32sUDj7`6Z_HE6TcYoBKPh1)bf0)$ z5mM}5?0PCr+v|kPg4~|dW5Y|sNH3dz5U2dYH6UY*x62v&A!8Q8X9htt@|lw!A;(rl zr`}+Lk+(?69b|>PFj2jy2L|;>XO>#q6s=mkq9o zES0iyT-oHhM%A{6;|^F1ekIC6eU<=Kv-!cU^X{5x1DNEZ``j-mRmtu>x%8`tkU>YIb=xiZ7+J3)}~Oe zV0VHWVwM*gVp=rZ4CTxBZqdForw#lBR#{+$sSLo4DJpoK8ZFEiA>p)d@@?r6&m+rR zw=@>y0O>ydzWg3*{^p3FZ_?zzg(@6{WRdYo&fChCDB=vjOI{I0ysKhL)jR`2CP|0* z^flx4Bo<|YoLr@wmqIE+b5-<#>>-{)Ik9cFe?X{nNhqJ2v&bj32wSe<*>IVY*mBv= z_+;{$XzNaJhW$rDeeU&Plys!-es0T4Xt)<;V8L`fx{;?Ewnnf z5}RVzWrukk&aFM?ASQHbxI?vN_6nSpL$iPz0y>HwgqY-~kfaWY6Qvx($~v6hlU_w) zTkTPl`)&x-x)&_fWAY`ZIG$Xy(<6)EWfy+vrtL1ld@^X}zk|N^EmV1_Y}8Jj@218* zCd{xoA|rh6%S_;>!5_N0E~{!FEmEG78bQMlGg}8>vNJo)=~ngNnVojs);p|*$mM0% zNAf763{e)e2jMYsDICM){%uoC@W})I8<8eADak*6rKZjW`N!1sh3~Ux8NDClI^ci` zYQ1RIc&~@n7<<8Cv3DcJr8IVq`dl=G@6IohoBXnic%6F}u?;eEJVtoSRG=NP7UQOz zY7P}eN=}I0+4m04aB_1-c**eg!>SjV4q=w4d(=i*&BrL)DJPIw?Fs*a*q3)IW)h3h zHBUmyu%v1gBkhH=E!bK5z9IQUbK$e>S1vV&fo$0;=u+D+Kd~*O$(*~?T&cJ&k|*wx z6c4McMRKvqUc3k`)q~%g{xY*g;#gUpLF#=U9&%aho|fW__AGEIDJ)EO7XzEAGC%_d z{vEE=)oeAB?FDU5PHEgLY0QriA4iIr4LSIln`p}kZgIpoTWM@lc%5E#2L%Hb+gelR zX$2R~f_1CqQA;!m5blj?x_w7O>BBx9PQ$X;m{w zN;NMgcdMDebh6#}H=V#@`SQN1_)fPJsC)yf02ETUF`sX?ETuVrmSz*LSs=vH(48Vh zK^s!iH>Q=0tkTtTky0(iwOS?iM5*=Wn?F73TxFd9&AaMZAX%sJ({!mhoKzLl#B_0~ zhS_Iz=Du`9+T~SDn*fSVBVWHFnmA^*rfF@FFUSJeaI$(vI7Y6t*M>ALM z!Hf@fH@yP(*1^Tk{J1T`N7ejD)W^y^M+{e&?uZ`0@Vj_YFxo3(tW!x$PtdX&=E>P8qT9eL15Yy$_uliAPwo9&kH zWlMlQjq9F0%e3Su(eb;sS)SG7BY~F7h)C`ELh1jp{%7*aTeE>KuH4q)_Zu412tz>U7Kv4n^GnQ|Eq z8~FMyo@m+$Xt1|utL1?bYn5UEpTrtRdqqvsX>4SycZ=d;O);s;_>BBz?8Kza*j;=u zyf+@Ts<%exq8#EiSSr>Fd$?r{dRpbgYu2LFN2$!O8KkRMfF8By-nM`VxRKQ_9eP~-l@U9giGO? z8Z1i%#7vO}Jh^JQTsJ9m43HKbaixy8rBm#6kQ#CWdWJY$qo)3qxSB>aPiwXvB|CdX zQ`CxvC;mVD4d~vb@B4%0rX(>AFMVa$p)srGfyOb5#}SyP7yNdLoIV>LEFBu~g)FQ} zip$(W2APY;9T^en0d^b2WDvu`^nYC@nb?N<9FTeMeVZpi$taZUa}h*AqwiS1?=DT@OiQ|0G~7sBJ)%=VaDA#>xFNl;Z- zGLLL*Ze)=$1`=F5SH$dXENW|zCB#gR8RNd2==EL|ExLPP^#+X8-uoYH=&3zJVHTHZ zRT_8iXBZ((%}HLdR8jDpGPPWq+Q5&T(utmW4zmscb7!~NjXmc>HJw`5Ev_-DX7qkd zv!hI{fT09CpK*}~jtP4`Bu%e0r&m(8laUxdi*Ju^~>}yqy*?HEv_ncA5E=w)vZr z5~ED@FDI=hjc^}-QRa_Ro$D|B+<80Vs$hB{4Pzj?x0TL-j;3Z>>dzik#oLKdrN1Md zSUi2`79@6zW-UFllTpz076%tM3~fjW`v^q?z9!Q=v$U}A&eM>u8L1oSW9U8)uK zaV%kRc_&YQp5Hr1i<7cEjx$c~;_Zch*z;u%OFj>vVji>sgj8wfyW)(% z7TH6>srSQ0FRcedIKf&}Eoci%dCkY(U~ZAAHc;Fps%`bOsC_81=H<9S!^mNPsEOO2 z7ko`#G{x1ftCHB76u6DA>M|!iMvh@SoD<7=vD7W&*h<3033wlQOK%~PF(!R7)c-^^ z%6TS!q|u3G8oO`MCoTT}IaTJqfj(8+ExUZ=)^?n8S;^aRsuo9qTRhFoyIfb!XB!pd z>RRS7<+ss0YFyTyKhXagdfBo_ta1j=Vq7BCLbvX2fLI2@d=_JHBz5Q`amdoq(P`!F zZQx)314MmX9{fhDnWBP%l}XCO7S%QfjRNg?VeaU*idg)Kn^i;L_1nXA3MFT7 zF_`qIk-Hl>)|zJ`M?caWl_Dim!WO<3Q?lL930TcJvZmA)eQukfrCB`C>l1+OE3wlH zN!Sp|l@sYDRm~wL^30DXE^|T4bh!kK#iC_eCk7$1NfPCd zOI?$+YZv=jIq+Rfw_e61IY}2{bq?I}d)t27TB5r$1Yg#AB(iWg5?#(1pOC^$bbnTG zVXJ4Z4g+t2EN{4T*@(%ZJh&#k%`?|idqd18?%#ixJ;cdLa~Z^FYl(uUdCvNW5eESw zA+|mNm!!_VW&?an$nB?^=1=*`t>9%(%B*r>xkjG5c~wg}*rn9m+BEf~8u^Hl`5p0* zBC`T0ZWqyWjt(jf$gL12@zmI5OU+!(ry*(2_>uu#SA*YQUg}AUi;q`F>O{=3gAfGe zS1Ydtp!2zGP`3sgS){B47#Jwen#t$^XX!ljU2RyDPc2uwEBE;h?0V7_WyHvZisEAv zS@HLe4jaZXi~GCE-$%7Q`b)=dscjgFt1lvoqLlV2R)$0MISE}aCLLs3LCdsJJYH~p zm}D3`tDWumUJp_w%7R}f)`^45r1V2m&xsm*k_=LU=R4V8gmD6A%t++?{d~xHYQ&(- zS6OLm>3MWl!KQ&uak10aS7|u|H~iK-Rf1L2gAYI7n7PQn$f#*)EZgxjQzQCeL$Bwg zr|v0vPWhZ{qPgzLg=*E_u{?l{r)-nHeyd6WWUboI^yM^{ry+1Q45Mt_YZcV~T2DBV z1N0|{#8ZdfweusifDW-|`JtDVuUC_z!hYuuWTBx77_bo-5V+*uaJTVI=0XswCTBF_ zU?Q`;KSPub4QZs+!h7*3I73Q!c{%XdMF05Z#XW`Z=8;7O?3`Xwi*ak(c(&jCuvm=^ zUVK&?Zr|S&os+$$T@~Z`!05Z$h7^A*;W-SVaO92UjA`DhhPrsERd#u~rGrLm>OYg2 z6Us*CoMGR*xaLEVA?_@d|9Jrn!E+-l%L=kt79RIJB?Jmaet-6cB5vHcK|^_NUO42< zBlX&k$|1AONKwu;F7}Zxtq;HJX&Yzp6U>(`9n%+a!kuN-3oKiN=G8aDlTVjZ%%MfI z>;0VKlD_v$Z#i@*bXh>5O48<;nt2$SOXRfY&kFd=eX&KYyE2M9YDgfA>r`}rGytyT zBmM_{kIgRYQl34{8GXa_=I2WYW$=})=|;U9X^g+vK7ZEAn>A3|Zw?hr3z6INPrR_c z7A&6PR*E;-Gd_~qn($8^=GAQ_8p7NYYm*_Hoz>#QlXiWCrq%P8+FZ()oe!pBrp-f^&29n8zYU=SdxDz6!Ucg+ zk#R{$TBT~h1E*H0rj?-=O(a-2MhV%=d0${HNyAQYr4w_4?MGtl18K5dX zyK^T?JT?FIo#gw{q0Lwv*9k!p*z9ookO*aclS#D!Nq%vUr(tQl`*1E@$W?NQa3;|+ zx;-Fr-3yhzxL!25cA-@&g1Eh*B64gGGn7BlWlCJ2-ZN&C-HXTP8nSSB)o|c*PMvbk zJy!AF5t>I=`uNPIKEj?9ps7!H{^(L#&kvx*q7P%L8n$Uq<5<0Byi60!*kk2dE##)( z>Fh3lK3$sZ-6x?Klj)M~`~oZ{LL_wku#Sm)&8T~a*q1Y=ueqBNN4ejgTL?3X49F7T z7cU;u=aGGU3A9Q6AMYMslDZyweFQf30h|^!zmGS&`T4Z(2ZcTjv8#5UX#r%I7^mLy~t_h1e^E?_vcQwkb}F_^fKh_IxMg3c74i#d+;nkD&W6va^R}cDMe9U-CUSj&G7Yg|9p&LWpX_eHpv-;$!*wQHG%W|7^B+MM)xmHc$3-l zk*C%2wNIt$?%!2}h}>@o``YLs*rs^6hwmSo?4RpT_r566u^!tujruK!S5d`Le2t5b z)y~sW2dqTyKtvi`vMvmK5G2q5FD9eh#`5qRXyt$=HVsa0p#E@NInRDA7%*)>8){|N z;iNLYJ?UCP7z%@DHDmI1xf;UsLhfhHH;2(|Q)t9-Szzn@IJLPr;ni6`5q#Q=a-^S0 z$*5tc_gHbPkJuQI&&w}k46*x=MEIC7HtFdcEX0b}MrAb@WAuxRMW+^R+(EbZ-=GF& zws~(A#k6?b7_8dgU2hD4wBINJfYOg%)k~_=U=|#@Qn-Ynoiwc_Fw3_;w zjjfNO-+g_|Ef2O5Jie^nVI_}Z9?eaz*W=@)qzce*NpM}PFv;$*_*XlsRkTdXlC_FC z0LS03*|Lbs)yw1IJ-S4GBdc-#p;FeZh}q^PhGGX6?%IG$moHzoWQkRgSNnz4ZnIq8 zthWiyax|%CEf8@B(0sR z8UbXN53>P*C%VMz=(Dyyr?1yvjM$U+e9?PKcD`}8ulI$OWS*z`ZkmfC)AuOVUF5dK z%jjcdo5!z$k&Vy&0wpSX-J#ejc@)!V-d3QjqJnGfi2z;WwP)q&srUKP;hE=$-q3R& zp948;`k)Z3fIEf4^2GuM8zpD9mA*_oHJADN;vU1TTLUAd)-?Qzd4|PWpq53X>;25G ze4`q`mgPKsj<04V3042nj++@%#ezk~z z)u#T;W(clYcxNuC|6LP+hiM$g%}cFVyMXzhIvSZ~Ek*5(tO$TtgU&3-N6yI$KK$(U zZ8W?-B@DQ3OQ1xN#3IW`wYP79dZQ}9FtD?uI<8vDqvcytt&ou4)eC z7{n`J5{y~X-F}`2*DiQ+KSQzvi~o%qT>wMQZ95;H;?^#bS@|?S8SWgA9>6kwKl(T~ z=SWGq^OLcvxlV_t*Y>D@;`^}*Rb>BnrKj^WKv)Agj{Y&odiR@p-p~dqe@+K21gLMi znUX1mr`yKo-*>*TtsSZ_x}A)_G`WrFX}o}QLAo#$eJ**|!JrFgJ)TMTEY<6InuW-B ztjY9?RHr=qQDnED`)C8^0~55fhG|!DVaj27?3Iqo1i$e**M)+JqJpKQ2Qf{?k=n|Y znBJ~-ESniRbI46>Yb|PRas|R8yG(aEKd-Vy@Gb=J@6csU>7`2p)O!1Se~Fx2c6;k= zahF<2+90ng1OA8y>A!>|A43otVlfdOc53)N<}g6&)q`HwQ+;)?K-+3uy7EiBN6yeM&KAfLCZ)r?eE&-FCs zN314QDIo?gq!t^ON7Xc~7~nCo!=qJaY`pZ{*ptw0Z>_55w|-y6c11!R#&H!y)P%g` zb$#_#V%mQHVH>Y<&ZdRvO=|DO8(r46BCj&Q>L%0rhqgyTtQtAt^UK>w0X}WbHkoP38UBUq-ESKG6>c}c zRfojoWdd(Q!r&W=S_6IChf2}BgT*)1SgvUSo_9z`ZdDDrCx9{m@@t0Ls(^sN!?vOl z6QT%fse%P0bdR$WHP<)gKa<~%iK=zp?Dv?vPS*_eQe805-xNFE=87nqTo&&D zZki7R(tk`fKZ-V+Zk!o91#V>6H(#D*s^m(sV2j#c#{jmCido`E?659<@Mpl`fo1k? zaS@Jh_Nz-?3I3cDol_#LIiZ&Tc84aoTJU6{Fa{BJKL=SE0&z_A$-alF>ugyC=Je>7* z8}(e(sUn3-s_jjBN^a^LTMreiP+^LVi=y!Hpw}cy0{L}Po^zi>vIf2yjfxJG7Au~b ziO3hhi6Ir;8+BWqksnoG8?dJ6uQ6B?2lq$@BVPRZ;H0torF_rcO~$vtzyGG*B?Gqf zU+?ewGe4sEJm*J4cZ25ASpkVhNYjUzAIw@9icEFkz`nuI7;#BTE;!h7G2ig(?>9la zlk2^XAb!8OA}W81+1j#S3uI?v)?&F9-nEI;D&Qy+?x^iIHbQEc#y!gT{aZl5ba|@I zZv^&Q?y+}Vf{k)?fYe>$l(dkg=LqWaT*U=fNIP~-m&w!B@Cd$2NW427Ll;dfSIP*| zzAS(A1B`d;BFAvSLr%PAE`H=9g+iGAL_jffiN?^=l#^1u)x5VyVQBUBg55sUw|)NM zg>!%@QT@x5%-$ims0V8(`qtd_H%GX?u1zwc`THjlmP=y1k1o+ifNXUMk#e9S`fwjl+{XJ*1~llQp?)9?IHOy) z@p11IRtGaoV>5p@HSJDGQRhrpnd-JEJWE$a6hna!dL>hMV762nR&rG%3=n@ z_x_#sZ=BIZww1eRWg`n7j^o$+%^pY9RX3#9W4cCpu}Cq8D&}|SH>C~EG40GJxf#vL z>Z5e*T*hpMIv z1NU2hz5AESRCWMjIS`oDr9&*bQluzFdUtk8IHPku&pNyxbN@Q{!!XxEAX6(B*+2As zFyAoE{a(Ie{1Z@tt(L>&r6qbq2(jloqV5+ARc6YfJzkz{4@u-n<*s*L4+p-f1U(3%d!wGD1Ki4o1ky+Qo ze9ELtzc$&uSiX3K`mfk{iMphi`DYX~(+%rRTBtkLi69-47B%vHg5-oq85708H4?zZ zduqI}{)Na%N~ZYJlx?af8z4C)#D0u!RIB?Be;f=@SRX}ctE)$D(|>N)QHX!?z89{p zJn;GQtx|`ES0@D+h||E^ynX1sjUQDEmXDmjC6teeHMcn_H4m~LfSa;WFXek}Xd=}H zye*%j+_0)Ce7SENjP@a$$t2w98Sp=(eD^MXGfBjb(h24Scj}g>g z_2S|F_ike;gpP8n$=a+}uQ%X`wF}AdiFU<^k|hW_ zdsjQ%6k_Y46eh1`f^aiza~b8PrE)_A*%_1*h`dX@yY)de#@E5ru#x|pnpBnV1I{K| zt50lIfKm#zd(^tyzD0OA5S^@S!tP}6SSAK;uhDTM-G;oH@ug1K*kwpX4}OGjyrT@>{vI*2m(E?GME+=WKmJTs zYsY)$e%jFpPrASDe(+V}c*hVQ`jcFd_0r-yp?0`L)wiB4sMmzp%K zpU?nDsG#CNoMP#1X>igsu`p4;>)4x7li?0@Kp;l0|=(CWEUaV0MXEw&TK{5TGl(M=2mot@q zK4I;I`68PNti<{Tl(=?$d15nk2hl@6pq1Ox&0S8|SI;aav@|bN(YcPq#8iF6wIkS~QLe9I*Ylr=P8^HuMQ7AbiNO&2 zj>`C8)~+c7Zfuu(AERH)CtC(Z%e{67OO0Nq^ycyL>BHqT4q{K~%NiJ}34cN&RJ{4a zI2~~8jUl|sz#WAE$N}zJ+Tg~18-db76}1e#zB)gOXc?BBrA|X2SwaS$dlb|!K8UKw zG4~`Cr4~(M&vq<3SsUK1Ag+QdImmCAgiyIR4jE2t>tuiHrW_8IB`01MduJ5tqdMH> zu8;<^eH9Cu(>}EK>^GyD_nY@rWs4?_H1_;H{h@iTo!o630Dzw;*ReG4_r5HU*MLh5 zWD;axr&O|J zbYYFEW6?tC?T}psy8smPJJKBvyiiq)?K_A$2LLo?$$NJ2qY&8$;^3*Kf<1{OkC$qD zqSbUhwRR|%cGl!$m(i)J6Kl_H=06<@8I+TCqT8?_0>+*`#=L)aim7j6c@ZNXGH?`P z23Zvk%uhLmSgZtEXA0y)3=7_HBBhF@#=+ky!g;En5w|A??Z*3>&$XGw(8j?h{%W58)xl{J`>@06Z1!N7N zt6y7Ee{kj0a@sJF58afOMc`+;Td8ec$%4N;(Koe>1HXWf=Vz^1>zTGMP{rb7gKuSA z(0(t!!p|jkqFm(${ZRvc^}RY;+X%$2nR5@LC&W7vl`v+;6Oifm=#*ofy~r~wdgN<6 z?bU)Dcn0k9)@bO@^E>iQ=YJccK}t{=2AS4g@^5i;A8DTGW_BLcc2N#Chq56F8>AL{ zydtr0epuN<`J;Kh66gM1Y_cd3H_^9*9k*TnOl^ z_HMz~+N!dOaMT{3=8R|`l1Ne!8V8{lx@MP4+z=ilcwOW?MC^C}p|^Wu-w_(i$ae}X z(hr(1|6Ui%MhUbijWVK-***)g`t#1t^@$W`=m@G@3>LG$wbsN<>*^k`&Mf>Yrm}cO zcUfwb+fdQ)#P;d%7qCxRHq-*rcU=6G`;TLhu5PbiMqTZive;QWB~IdLEYgXU9QJ#Q z1FK>fkv|6SUkvGSAqBqugauz7{rKJ; zTs2!WuH1`a?ogCMyT}yMJ6TXWR}Nk{gf_Fa>z3m3#ik|6KWpLnzd7&9P4n(fcPCAp zYQpYQHJ_j$JYnjCmx^JK^O|w<_ov{yg`fUG>||o=W{^q9VIulI3!a;p;<~VszLtv* zH9wi761Qg(*^34dy~}mc4_Pf6k+|G)*4mtFMS(&C)K2aFX{@u*5$Gq5?alFb{52~^s+|ig(OcT1$=qKd{ULe8!?+Z*d-n1XX`UvAs z$?u7hE5oyR>2HTXtarBdkte_T;O}oH<5k2q1Qt^SjO{VQ$&>J?bw9RV)@3fCUMDb5 zn;L(wHkr|BKjmRNT++ki9!9^iU^%d+TcC?mfjc0{$btUQh;Z=7g%xt^e(jTaP(neV z0l{3blDCuH$!z-q0=(oeWyph*%?o6|8ot)K`)ne2UUKmsd1FfbvQD1f?0@!WX33OQ zNFh_doR%+I6%~NDJc?x)^E-)|Eb>2%2-%cuvyaKESd7G075tWMLJ#d|wg>omch0we zY9m?Fx4|j(W9V%AFQ-onC-36l72h=dM1iO}T&UCJ0nfUxq~(B~4r~%bnaD#Ig zSjDK~(a;!88w?DiIfY@gD4=Fh@UPywOb1lN8l1Ra^g0Jj3$!NM1rN>8qFA^8DlhKjAjhVTfl>|B=N~II>gU(BTN(II!{+Wmvv4F FO#p3?f_4A^ literal 0 HcmV?d00001 diff --git a/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_top.png b/sensing/autoware_pointcloud_preprocessor/docs/image/concatenate_top.png new file mode 100644 index 0000000000000000000000000000000000000000..c317642bd979979a9bbb47220edaebb9764b9fb9 GIT binary patch literal 286968 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

I~+KWUaP=xrc?2V|Ny_j!^+R$4|^CAKe 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

t}yBf6qH1*CWJfXMDHF}KMk?$g)(l#71ABE*RN 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ü=X%?XaJ#7K8C8Nx5?4%a=t^dU8fs3 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 +| Left | Top | Right | +|:-:|:-:|:-:| +| ![Concatenate Left](./image/concatenate_left.png) | ![Concatenate Top](./image/concatenate_top.png) | ![Concatenate Right](./image/concatenate_right.png) | + After processing the data through the `concatenate_and_time_synchronize_node`, the outputs from all LiDARs are combined into a single comprehensive point cloud that provides a complete view of the environment: From 0487272209bd4cd7f5cb1b664858cd01b4400496 Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:58:44 +0900 Subject: [PATCH 103/115] Update sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../autoware_pointcloud_preprocessor/docs/concatenate-data.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index f1e60431035e6..c1102f59af84a 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -13,8 +13,8 @@ For example, consider a vehicle equipped with three LiDAR sensors mounted on the After processing the data through the `concatenate_and_time_synchronize_node`, the outputs from all LiDARs are combined into a single comprehensive point cloud that provides a complete view of the environment: -
- Full Scene View +
+ ![Full Scene View](./image/concatenate_all.png)
This resulting point cloud allows autonomous systems to detect obstacles, map the environment, and navigate more effectively, leveraging the complementary fields of view from multiple LiDAR sensors. From 3e9d8f437008d2e51223a2581601f7da3101042e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:01:17 +0000 Subject: [PATCH 104/115] style(pre-commit): autofix --- .../docs/concatenate-data.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index c1102f59af84a..1d6d5e88ccb13 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -6,11 +6,10 @@ The `concatenate_and_time_synchronize_node` is a node designed to combine and sy For example, consider a vehicle equipped with three LiDAR sensors mounted on the left, right, and top positions. Each LiDAR captures data from its respective field of view, as shown below: -| Left | Top | Right | -|:-:|:-:|:-:| +| Left | Top | Right | +| :-----------------------------------------------: | :---------------------------------------------: | :-------------------------------------------------: | | ![Concatenate Left](./image/concatenate_left.png) | ![Concatenate Top](./image/concatenate_top.png) | ![Concatenate Right](./image/concatenate_right.png) | - After processing the data through the `concatenate_and_time_synchronize_node`, the outputs from all LiDARs are combined into a single comprehensive point cloud that provides a complete view of the environment:
From 1dd7fbc6c70cf2210da66b4619df4c625bfadadf Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 23 Dec 2024 23:25:25 +0900 Subject: [PATCH 105/115] feat: remove mutually exclusive approaches Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 24 +++--- .../collector_matching_strategy.hpp | 9 ++- .../concatenate_and_time_sync_node.hpp | 7 +- .../src/concatenate_data/cloud_collector.cpp | 80 +++++++++---------- .../collector_matching_strategy.cpp | 33 +++++--- .../concatenate_and_time_sync_node.cpp | 27 ++++--- .../test/test_concatenate_node_unit.cpp | 13 +-- 7 files changed, 108 insertions(+), 85 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 867b0adbca1b5..0364226de4050 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -27,6 +27,15 @@ namespace autoware::pointcloud_preprocessor class PointCloudConcatenateDataSynchronizerComponent; class CombineCloudHandler; +enum class CollectorStrategyType { Naive, Advanced }; + +struct CollectorInfo +{ + double timestamp{0.0}; + double noise_window{0.0}; + CollectorStrategyType strategy_type{CollectorStrategyType::Naive}; +}; + class CloudCollector { public: @@ -34,13 +43,6 @@ class CloudCollector std::shared_ptr && ros2_parent_node, std::shared_ptr & combine_cloud_handler, int num_of_clouds, double timeout_sec, bool debug_mode); - - // Naive approach - void set_arrival_timestamp(double timestamp); - double get_arrival_timestamp() const; - // Advanced approach - void set_reference_timestamp(double timestamp, double noise_window); - std::tuple get_reference_timestamp_boundary(); bool topic_exists(const std::string & topic_name); bool process_pointcloud( const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud); @@ -54,6 +56,10 @@ class CloudCollector bool concatenate_finished() const; + void set_info(CollectorInfo collector_info); + CollectorInfo get_info() const; + void show_debug_message(); + private: std::shared_ptr ros2_parent_node_; std::shared_ptr combine_cloud_handler_; @@ -61,12 +67,10 @@ class CloudCollector std::unordered_map topic_to_cloud_map_; uint64_t num_of_clouds_; double timeout_sec_; - double reference_timestamp_min_{0.0}; - double reference_timestamp_max_{0.0}; - double arrival_timestamp_{0.0}; // This is used for the naive approach bool debug_mode_; bool concatenate_finished_{false}; std::mutex concatenate_mutex_; + CollectorInfo collector_info_; }; } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp index c02131956156c..a6aa39ef7ab74 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp @@ -45,8 +45,11 @@ class CollectorMatchingStrategy [[nodiscard]] virtual std::optional> match_cloud_to_collector( const std::list> & cloud_collectors, const MatchingParams & params) const = 0; - virtual void set_collector_timestamp( + virtual void set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) = 0; + +protected: + CollectorStrategyType strategy_type_; }; class NaiveMatchingStrategy : public CollectorMatchingStrategy @@ -56,7 +59,7 @@ class NaiveMatchingStrategy : public CollectorMatchingStrategy [[nodiscard]] std::optional> match_cloud_to_collector( const std::list> & cloud_collectors, const MatchingParams & params) const override; - void set_collector_timestamp( + void set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) override; }; @@ -68,7 +71,7 @@ class AdvancedMatchingStrategy : public CollectorMatchingStrategy [[nodiscard]] std::optional> match_cloud_to_collector( const std::list> & cloud_collectors, const MatchingParams & params) const override; - void set_collector_timestamp( + void set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) override; private: diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 26bbc72908db0..618b622927b94 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -54,8 +54,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node explicit PointCloudConcatenateDataSynchronizerComponent(const rclcpp::NodeOptions & node_options); ~PointCloudConcatenateDataSynchronizerComponent() override = default; void publish_clouds( - ConcatenatedCloudResult && concatenated_cloud_result, double reference_timestamp_min, - double reference_timestamp_max, double arrival_timestamp); + ConcatenatedCloudResult && concatenated_cloud_result, CollectorInfo collector_info); void manage_collector_list(); std::list> get_cloud_collectors(); void add_cloud_collector(const std::shared_ptr & collector); @@ -85,9 +84,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node bool drop_previous_but_late_pointcloud_{false}; bool publish_pointcloud_{false}; bool is_concatenated_cloud_empty_{false}; - double diagnostic_reference_timestamp_min_{0.0}; - double diagnostic_reference_timestamp_max_{0.0}; - double diagnostic_arrival_timestamp_{0.0}; + CollectorInfo diagnostic_collector_info_; std::unordered_map diagnostic_topic_to_original_stamp_map_; std::shared_ptr combine_cloud_handler_; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index a539d7c817654..9b2394c823a43 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -49,25 +48,14 @@ CloudCollector::CloudCollector( }); } -void CloudCollector::set_arrival_timestamp(double timestamp) +void CloudCollector::set_info(CollectorInfo collector_info) { - arrival_timestamp_ = timestamp; + collector_info_ = collector_info; } -double CloudCollector::get_arrival_timestamp() const +CollectorInfo CloudCollector::get_info() const { - return arrival_timestamp_; -} - -void CloudCollector::set_reference_timestamp(double timestamp, double noise_window) -{ - reference_timestamp_max_ = timestamp + noise_window; - reference_timestamp_min_ = timestamp - noise_window; -} - -std::tuple CloudCollector::get_reference_timestamp_boundary() -{ - return std::make_tuple(reference_timestamp_min_, reference_timestamp_max_); + return collector_info_; } bool CloudCollector::topic_exists(const std::string & topic_name) @@ -106,28 +94,7 @@ bool CloudCollector::concatenate_finished() const void CloudCollector::concatenate_callback() { if (debug_mode_) { - auto time_until_trigger = timer_->time_until_trigger(); - std::stringstream log_stream; - log_stream << std::fixed << std::setprecision(6); - log_stream << "Collector's concatenate callback time: " - << ros2_parent_node_->get_clock()->now().seconds() << " seconds\n"; - - log_stream << "Collector's reference time min: " << reference_timestamp_min_ - << " to max: " << reference_timestamp_max_ << " seconds\n"; - - log_stream << "Time until trigger: " << (time_until_trigger.count() / 1e9) << " seconds\n"; - - log_stream << "Pointclouds: ["; - std::string separator = ""; - for (const auto & [topic, cloud] : topic_to_cloud_map_) { - log_stream << separator; - log_stream << "[" << topic << ", " << rclcpp::Time(cloud->header.stamp).seconds() << "]"; - separator = ", "; - } - - log_stream << "]\n"; - - RCLCPP_INFO(ros2_parent_node_->get_logger(), "%s", log_stream.str().c_str()); + show_debug_message(); } // All pointclouds are received or the timer has timed out, cancel the timer and concatenate the @@ -136,9 +103,7 @@ void CloudCollector::concatenate_callback() auto concatenated_cloud_result = concatenate_pointclouds(topic_to_cloud_map_); - ros2_parent_node_->publish_clouds( - std::move(concatenated_cloud_result), reference_timestamp_min_, reference_timestamp_max_, - arrival_timestamp_); + ros2_parent_node_->publish_clouds(std::move(concatenated_cloud_result), collector_info_); concatenate_finished_ = true; } @@ -155,4 +120,37 @@ CloudCollector::get_topic_to_cloud_map() return topic_to_cloud_map_; } +void CloudCollector::show_debug_message() +{ + auto time_until_trigger = timer_->time_until_trigger(); + std::stringstream log_stream; + log_stream << std::fixed << std::setprecision(6); + log_stream << "Collector's concatenate callback time: " + << ros2_parent_node_->get_clock()->now().seconds() << " seconds\n"; + + if (collector_info_.strategy_type == CollectorStrategyType::Advanced) { + log_stream << "Advanced stratygy:\n Collector's reference time min: " + << collector_info_.timestamp - collector_info_.noise_window + << " to max: " << collector_info_.timestamp + collector_info_.noise_window + << " seconds\n"; + } else if (collector_info_.strategy_type == CollectorStrategyType::Naive) { + log_stream << "Naive stratygy:\n Collector's first cloud arrival time: " + << collector_info_.timestamp << " seconds\n"; + } + + log_stream << "Time until trigger: " << (time_until_trigger.count() / 1e9) << " seconds\n"; + + log_stream << "Pointclouds: ["; + std::string separator = ""; + for (const auto & [topic, cloud] : topic_to_cloud_map_) { + log_stream << separator; + log_stream << "[" << topic << ", " << rclcpp::Time(cloud->header.stamp).seconds() << "]"; + separator = ", "; + } + + log_stream << "]\n"; + + RCLCPP_INFO(ros2_parent_node_->get_logger(), "%s", log_stream.str().c_str()); +} + } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp index 81de3268d4a03..db42b04981ece 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp" + #include #include @@ -26,6 +28,7 @@ namespace autoware::pointcloud_preprocessor NaiveMatchingStrategy::NaiveMatchingStrategy(rclcpp::Node & node) { + strategy_type_ = CollectorStrategyType::Naive; RCLCPP_INFO(node.get_logger(), "Utilize naive matching strategy"); } @@ -38,8 +41,8 @@ std::optional> NaiveMatchingStrategy::match_clou for (const auto & cloud_collector : cloud_collectors) { if (!cloud_collector->topic_exists(params.topic_name)) { - double time_difference = - std::abs(params.cloud_arrival_time - cloud_collector->get_arrival_timestamp()); + CollectorInfo collector_info = cloud_collector->get_info(); + double time_difference = std::abs(params.cloud_arrival_time - collector_info.timestamp); if (!smallest_time_difference || time_difference < smallest_time_difference) { smallest_time_difference = time_difference; closest_collector = cloud_collector; @@ -50,15 +53,20 @@ std::optional> NaiveMatchingStrategy::match_clou return closest_collector; } -void NaiveMatchingStrategy::set_collector_timestamp( +void NaiveMatchingStrategy::set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) { - collector->set_arrival_timestamp(matching_params.cloud_arrival_time); + CollectorInfo collector_info; + ; + collector_info.timestamp = matching_params.cloud_arrival_time; + collector_info.strategy_type = strategy_type_; + collector->set_info(collector_info); } AdvancedMatchingStrategy::AdvancedMatchingStrategy( rclcpp::Node & node, std::vector input_topics) { + strategy_type_ = CollectorStrategyType::Advanced; auto lidar_timestamp_offsets = node.declare_parameter>("matching_strategy.lidar_timestamp_offsets"); auto lidar_timestamp_noise_window = @@ -88,8 +96,9 @@ std::optional> AdvancedMatchingStrategy::match_c const MatchingParams & params) const { for (const auto & cloud_collector : cloud_collectors) { - auto [reference_timestamp_min, reference_timestamp_max] = - cloud_collector->get_reference_timestamp_boundary(); + CollectorInfo collector_info = cloud_collector->get_info(); + auto reference_timestamp_min = collector_info.timestamp - collector_info.noise_window; + auto reference_timestamp_max = collector_info.timestamp + collector_info.noise_window; double time = params.cloud_timestamp - topic_to_offset_map_.at(params.topic_name); if ( time < reference_timestamp_max + topic_to_noise_window_map_.at(params.topic_name) && @@ -100,12 +109,16 @@ std::optional> AdvancedMatchingStrategy::match_c return std::nullopt; } -void AdvancedMatchingStrategy::set_collector_timestamp( +void AdvancedMatchingStrategy::set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) { - collector->set_reference_timestamp( - matching_params.cloud_timestamp - topic_to_offset_map_[matching_params.topic_name], - topic_to_noise_window_map_[matching_params.topic_name]); + CollectorInfo collector_info; + ; + collector_info.timestamp = + matching_params.cloud_timestamp - topic_to_offset_map_[matching_params.topic_name]; + collector_info.noise_window = topic_to_noise_window_map_[matching_params.topic_name]; + collector_info.strategy_type = strategy_type_; + collector->set_info(collector_info); } } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 01ec4f7bf1b9f..f82df17ad13ac 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -242,7 +242,7 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( cloud_collectors_.push_back(new_cloud_collector); cloud_collectors_lock.unlock(); - collector_matching_strategy_->set_collector_timestamp(new_cloud_collector, matching_params); + collector_matching_strategy_->set_collector_info(new_cloud_collector, matching_params); (void)new_cloud_collector->process_pointcloud(topic_name, input_ptr); } } @@ -260,8 +260,7 @@ void PointCloudConcatenateDataSynchronizerComponent::odom_callback( } void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( - ConcatenatedCloudResult && concatenated_cloud_result, double reference_timestamp_min, - double reference_timestamp_max, double arrival_timestamp) + ConcatenatedCloudResult && concatenated_cloud_result, CollectorInfo collector_info) { // should never come to this state. if (concatenated_cloud_result.concatenate_cloud_ptr == nullptr) { @@ -321,9 +320,8 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( } } - diagnostic_reference_timestamp_min_ = reference_timestamp_min; - diagnostic_reference_timestamp_max_ = reference_timestamp_max; - diagnostic_arrival_timestamp_ = arrival_timestamp; + diagnostic_collector_info_ = collector_info; + diagnostic_topic_to_original_stamp_map_ = concatenated_cloud_result.topic_to_original_stamp_map; diagnostic_updater_.force_update(); @@ -371,11 +369,18 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( stat.add( "concatenated cloud timestamp", format_timestamp(current_concatenate_cloud_timestamp_)); - if (params_.matching_strategy == "naive") { - stat.add("first cloud's arrival timestamp", format_timestamp(diagnostic_arrival_timestamp_)); - } else { - stat.add("reference timestamp min", format_timestamp(diagnostic_reference_timestamp_min_)); - stat.add("reference timestamp max", format_timestamp(diagnostic_reference_timestamp_max_)); + if (diagnostic_collector_info_.strategy_type == CollectorStrategyType::Naive) { + stat.add( + "first cloud's arrival timestamp", format_timestamp(diagnostic_collector_info_.timestamp)); + } else if (diagnostic_collector_info_.strategy_type == CollectorStrategyType::Advanced) { + stat.add( + "reference timestamp min", + format_timestamp( + diagnostic_collector_info_.timestamp - diagnostic_collector_info_.noise_window)); + stat.add( + "reference timestamp max", + format_timestamp( + diagnostic_collector_info_.timestamp + diagnostic_collector_info_.noise_window)); } bool topic_miss = false; diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 0561644ea5815..1666c62d0325c 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -291,12 +291,15 @@ TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) //////////////////////////////// Test cloud_collector //////////////////////////////// -TEST_F(ConcatenateCloudTest, TestSetAndGetReferenceTimeStampBoundary) +TEST_F(ConcatenateCloudTest, TestSetAndGetCollectorInfo) { - double reference_timestamp = 10.0; - double noise_window = 0.1; - collector_->set_reference_timestamp(reference_timestamp, noise_window); - auto [min, max] = collector_->get_reference_timestamp_boundary(); + autoware::pointcloud_preprocessor::CollectorInfo collector_info; + collector_info.timestamp = 10.0; + collector_info.noise_window = 0.1; + collector_->set_info(collector_info); + auto collector_info_new = collector_->get_info(); + auto min = collector_info_new.timestamp - collector_info_new.noise_window; + auto max = collector_info_new.timestamp + collector_info_new.noise_window; EXPECT_DOUBLE_EQ(min, 9.9); EXPECT_DOUBLE_EQ(max, 10.1); } From 9c1f870b17455561b6d79bae697b29097d7b3704 Mon Sep 17 00:00:00 2001 From: vividf Date: Mon, 23 Dec 2024 23:28:32 +0900 Subject: [PATCH 106/115] chore: fix spell error Signed-off-by: vividf --- .../src/concatenate_data/cloud_collector.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index 9b2394c823a43..d926e20bcd4ca 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -129,12 +129,12 @@ void CloudCollector::show_debug_message() << ros2_parent_node_->get_clock()->now().seconds() << " seconds\n"; if (collector_info_.strategy_type == CollectorStrategyType::Advanced) { - log_stream << "Advanced stratygy:\n Collector's reference time min: " + log_stream << "Advanced strategy:\n Collector's reference time min: " << collector_info_.timestamp - collector_info_.noise_window << " to max: " << collector_info_.timestamp + collector_info_.noise_window << " seconds\n"; } else if (collector_info_.strategy_type == CollectorStrategyType::Naive) { - log_stream << "Naive stratygy:\n Collector's first cloud arrival time: " + log_stream << "Naive strategy:\n Collector's first cloud arrival time: " << collector_info_.timestamp << " seconds\n"; } From 2efabf475b4571ad3f5b0a0a03a6db3f4efd68c7 Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 24 Dec 2024 14:00:10 +0900 Subject: [PATCH 107/115] chore: remove unused variable Signed-off-by: vividf --- .../src/concatenate_data/combine_cloud_handler.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index 9647649324d8e..d86e4b392a2f5 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -298,7 +298,6 @@ Eigen::Matrix4f CombineCloudHandler::compute_transform_to_adjust_for_old_timesta double x = 0.0; double y = 0.0; double yaw = 0.0; - tf2::Quaternion baselink_quat{}; for (auto twist_it = old_twist_it; twist_it != new_twist_it + 1; ++twist_it) { const double dt = (twist_it != new_twist_it) From fd7db92821b09e31edda9144aaf558f58e92be6d Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 24 Dec 2024 19:50:59 +0900 Subject: [PATCH 108/115] refactor: refactor collectorInfo to polymorphic Signed-off-by: vividf --- .../concatenate_data/cloud_collector.hpp | 32 ++++++++++--- .../concatenate_and_time_sync_node.hpp | 5 +- .../src/concatenate_data/cloud_collector.cpp | 26 +++++----- .../collector_matching_strategy.cpp | 48 +++++++++---------- .../concatenate_and_time_sync_node.cpp | 19 ++++---- .../test/test_concatenate_node_unit.cpp | 36 +++++++++++--- 6 files changed, 107 insertions(+), 59 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 0364226de4050..ae97752b2f6d7 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -29,11 +29,31 @@ class CombineCloudHandler; enum class CollectorStrategyType { Naive, Advanced }; -struct CollectorInfo +struct CollectorInfoBase +{ + virtual ~CollectorInfoBase() = default; + [[nodiscard]] virtual CollectorStrategyType getStrategyType() const = 0; +}; + +struct NaiveCollectorInfo : public CollectorInfoBase +{ + double timestamp{0.0}; + + [[nodiscard]] CollectorStrategyType getStrategyType() const override + { + return CollectorStrategyType::Naive; + } +}; + +struct AdvancedCollectorInfo : public CollectorInfoBase { double timestamp{0.0}; double noise_window{0.0}; - CollectorStrategyType strategy_type{CollectorStrategyType::Naive}; + + [[nodiscard]] CollectorStrategyType getStrategyType() const override + { + return CollectorStrategyType::Advanced; + } }; class CloudCollector @@ -54,10 +74,10 @@ class CloudCollector std::unordered_map get_topic_to_cloud_map(); - bool concatenate_finished() const; + [[nodiscard]] bool concatenate_finished() const; - void set_info(CollectorInfo collector_info); - CollectorInfo get_info() const; + void set_info(std::shared_ptr collector_info); + [[nodiscard]] std::shared_ptr get_info() const; void show_debug_message(); private: @@ -70,7 +90,7 @@ class CloudCollector bool debug_mode_; bool concatenate_finished_{false}; std::mutex concatenate_mutex_; - CollectorInfo collector_info_; + std::shared_ptr collector_info_; }; } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 618b622927b94..c84c19a8728dd 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -54,7 +54,8 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node explicit PointCloudConcatenateDataSynchronizerComponent(const rclcpp::NodeOptions & node_options); ~PointCloudConcatenateDataSynchronizerComponent() override = default; void publish_clouds( - ConcatenatedCloudResult && concatenated_cloud_result, CollectorInfo collector_info); + ConcatenatedCloudResult && concatenated_cloud_result, + std::shared_ptr collector_info); void manage_collector_list(); std::list> get_cloud_collectors(); void add_cloud_collector(const std::shared_ptr & collector); @@ -84,7 +85,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node bool drop_previous_but_late_pointcloud_{false}; bool publish_pointcloud_{false}; bool is_concatenated_cloud_empty_{false}; - CollectorInfo diagnostic_collector_info_; + std::shared_ptr diagnostic_collector_info_; std::unordered_map diagnostic_topic_to_original_stamp_map_; std::shared_ptr combine_cloud_handler_; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index d926e20bcd4ca..c3e2d7eedff25 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -48,12 +48,12 @@ CloudCollector::CloudCollector( }); } -void CloudCollector::set_info(CollectorInfo collector_info) +void CloudCollector::set_info(std::shared_ptr collector_info) { - collector_info_ = collector_info; + collector_info_ = std::move(collector_info); } -CollectorInfo CloudCollector::get_info() const +std::shared_ptr CloudCollector::get_info() const { return collector_info_; } @@ -128,14 +128,18 @@ void CloudCollector::show_debug_message() log_stream << "Collector's concatenate callback time: " << ros2_parent_node_->get_clock()->now().seconds() << " seconds\n"; - if (collector_info_.strategy_type == CollectorStrategyType::Advanced) { - log_stream << "Advanced strategy:\n Collector's reference time min: " - << collector_info_.timestamp - collector_info_.noise_window - << " to max: " << collector_info_.timestamp + collector_info_.noise_window - << " seconds\n"; - } else if (collector_info_.strategy_type == CollectorStrategyType::Naive) { - log_stream << "Naive strategy:\n Collector's first cloud arrival time: " - << collector_info_.timestamp << " seconds\n"; + if (collector_info_) { + if (collector_info_->getStrategyType() == CollectorStrategyType::Advanced) { + auto advanced_info = std::dynamic_pointer_cast(collector_info_); + log_stream << "Advanced strategy:\n Collector's reference time min: " + << advanced_info->timestamp - advanced_info->noise_window + << " to max: " << advanced_info->timestamp + advanced_info->noise_window + << " seconds\n"; + } else if (collector_info_->getStrategyType() == CollectorStrategyType::Naive) { + auto naive_info = std::dynamic_pointer_cast(collector_info_); + log_stream << "Naive strategy:\n Collector's timestamp: " << naive_info->timestamp + << " seconds\n"; + } } log_stream << "Time until trigger: " << (time_until_trigger.count() / 1e9) << " seconds\n"; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp index db42b04981ece..fa233994c34e7 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp @@ -41,11 +41,13 @@ std::optional> NaiveMatchingStrategy::match_clou for (const auto & cloud_collector : cloud_collectors) { if (!cloud_collector->topic_exists(params.topic_name)) { - CollectorInfo collector_info = cloud_collector->get_info(); - double time_difference = std::abs(params.cloud_arrival_time - collector_info.timestamp); - if (!smallest_time_difference || time_difference < smallest_time_difference) { - smallest_time_difference = time_difference; - closest_collector = cloud_collector; + auto info = cloud_collector->get_info(); + if (auto naive_info = std::dynamic_pointer_cast(info)) { + double time_difference = std::abs(params.cloud_arrival_time - naive_info->timestamp); + if (!smallest_time_difference || time_difference < smallest_time_difference) { + smallest_time_difference = time_difference; + closest_collector = cloud_collector; + } } } } @@ -56,11 +58,9 @@ std::optional> NaiveMatchingStrategy::match_clou void NaiveMatchingStrategy::set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) { - CollectorInfo collector_info; - ; - collector_info.timestamp = matching_params.cloud_arrival_time; - collector_info.strategy_type = strategy_type_; - collector->set_info(collector_info); + auto info = std::make_shared(); + info->timestamp = matching_params.cloud_arrival_time; + collector->set_info(info); } AdvancedMatchingStrategy::AdvancedMatchingStrategy( @@ -96,14 +96,16 @@ std::optional> AdvancedMatchingStrategy::match_c const MatchingParams & params) const { for (const auto & cloud_collector : cloud_collectors) { - CollectorInfo collector_info = cloud_collector->get_info(); - auto reference_timestamp_min = collector_info.timestamp - collector_info.noise_window; - auto reference_timestamp_max = collector_info.timestamp + collector_info.noise_window; - double time = params.cloud_timestamp - topic_to_offset_map_.at(params.topic_name); - if ( - time < reference_timestamp_max + topic_to_noise_window_map_.at(params.topic_name) && - time > reference_timestamp_min - topic_to_noise_window_map_.at(params.topic_name)) { - return cloud_collector; + auto info = cloud_collector->get_info(); + if (auto advanced_info = std::dynamic_pointer_cast(info)) { + auto reference_timestamp_min = advanced_info->timestamp - advanced_info->noise_window; + auto reference_timestamp_max = advanced_info->timestamp + advanced_info->noise_window; + double time = params.cloud_timestamp - topic_to_offset_map_.at(params.topic_name); + if ( + time < reference_timestamp_max + topic_to_noise_window_map_.at(params.topic_name) && + time > reference_timestamp_min - topic_to_noise_window_map_.at(params.topic_name)) { + return cloud_collector; + } } } return std::nullopt; @@ -112,13 +114,11 @@ std::optional> AdvancedMatchingStrategy::match_c void AdvancedMatchingStrategy::set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) { - CollectorInfo collector_info; - ; - collector_info.timestamp = + auto info = std::make_shared(); + info->timestamp = matching_params.cloud_timestamp - topic_to_offset_map_[matching_params.topic_name]; - collector_info.noise_window = topic_to_noise_window_map_[matching_params.topic_name]; - collector_info.strategy_type = strategy_type_; - collector->set_info(collector_info); + info->noise_window = topic_to_noise_window_map_[matching_params.topic_name]; + collector->set_info(info); } } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index f82df17ad13ac..0a73a76e358aa 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -260,7 +260,8 @@ void PointCloudConcatenateDataSynchronizerComponent::odom_callback( } void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( - ConcatenatedCloudResult && concatenated_cloud_result, CollectorInfo collector_info) + ConcatenatedCloudResult && concatenated_cloud_result, + std::shared_ptr collector_info) { // should never come to this state. if (concatenated_cloud_result.concatenate_cloud_ptr == nullptr) { @@ -369,18 +370,18 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( stat.add( "concatenated cloud timestamp", format_timestamp(current_concatenate_cloud_timestamp_)); - if (diagnostic_collector_info_.strategy_type == CollectorStrategyType::Naive) { - stat.add( - "first cloud's arrival timestamp", format_timestamp(diagnostic_collector_info_.timestamp)); - } else if (diagnostic_collector_info_.strategy_type == CollectorStrategyType::Advanced) { + if ( + auto naive_info = std::dynamic_pointer_cast(diagnostic_collector_info_)) { + stat.add("first cloud's arrival timestamp", format_timestamp(naive_info->timestamp)); + } else if ( + auto advanced_info = + std::dynamic_pointer_cast(diagnostic_collector_info_)) { stat.add( "reference timestamp min", - format_timestamp( - diagnostic_collector_info_.timestamp - diagnostic_collector_info_.noise_window)); + format_timestamp(advanced_info->timestamp - advanced_info->noise_window)); stat.add( "reference timestamp max", - format_timestamp( - diagnostic_collector_info_.timestamp + diagnostic_collector_info_.noise_window)); + format_timestamp(advanced_info->timestamp + advanced_info->noise_window)); } bool topic_miss = false; diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 1666c62d0325c..8ca9f5c727ec4 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -291,15 +291,37 @@ TEST_F(ConcatenateCloudTest, TestComputeTransformToAdjustForOldTimestamp) //////////////////////////////// Test cloud_collector //////////////////////////////// -TEST_F(ConcatenateCloudTest, TestSetAndGetCollectorInfo) +TEST_F(ConcatenateCloudTest, TestSetAndGetNaiveCollectorInfo) { - autoware::pointcloud_preprocessor::CollectorInfo collector_info; - collector_info.timestamp = 10.0; - collector_info.noise_window = 0.1; - collector_->set_info(collector_info); + auto naive_info = std::make_shared(); + naive_info->timestamp = 15.0; + + collector_->set_info(naive_info); + auto collector_info_new = collector_->get_info(); + + auto naive_info_new = + std::dynamic_pointer_cast( + collector_info_new); + ASSERT_NE(naive_info_new, nullptr) << "Collector info is not of type NaiveCollectorInfo"; + + EXPECT_DOUBLE_EQ(naive_info_new->timestamp, 15.0); +} + +TEST_F(ConcatenateCloudTest, TestSetAndGetAdvancedCollectorInfo) +{ + auto advanced_info = std::make_shared(); + advanced_info->timestamp = 10.0; + advanced_info->noise_window = 0.1; + collector_->set_info(advanced_info); auto collector_info_new = collector_->get_info(); - auto min = collector_info_new.timestamp - collector_info_new.noise_window; - auto max = collector_info_new.timestamp + collector_info_new.noise_window; + auto advanced_info_new = + std::dynamic_pointer_cast( + collector_info_new); + ASSERT_NE(advanced_info_new, nullptr) << "Collector info is not of type AdvancedCollectorInfo"; + + // Validate the values + auto min = advanced_info_new->timestamp - advanced_info_new->noise_window; + auto max = advanced_info_new->timestamp + advanced_info_new->noise_window; EXPECT_DOUBLE_EQ(min, 9.9); EXPECT_DOUBLE_EQ(max, 10.1); } From f278aea13ae2a98a784429511604d3534b163fc1 Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 24 Dec 2024 20:10:30 +0900 Subject: [PATCH 109/115] chore: fix variable name Signed-off-by: vividf --- .../concatenate_and_time_sync_node.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 0a73a76e358aa..72ddb489f4dac 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -386,9 +386,9 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( bool topic_miss = false; - int concatenated_cloud_status = 1; // Status of concatenated cloud, 1: success, 0: failure + bool concatenation_success = true; for (const auto & topic : params_.input_topics) { - int cloud_status = 1; // Status of each lidar's pointcloud + bool input_cloud_concatenated = true; if ( diagnostic_topic_to_original_stamp_map_.find(topic) != diagnostic_topic_to_original_stamp_map_.end()) { @@ -396,13 +396,13 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( topic + " timestamp", format_timestamp(diagnostic_topic_to_original_stamp_map_[topic])); } else { topic_miss = true; - concatenated_cloud_status = 0; - cloud_status = 0; + concatenation_success = false; + input_cloud_concatenated = false; } - stat.add(topic, cloud_status); + stat.add(topic + " is concatenated", input_cloud_concatenated); } - stat.add("concatenate status", concatenated_cloud_status); + stat.add("cloud concatenation success", concatenation_success); int8_t level = diagnostic_msgs::msg::DiagnosticStatus::OK; std::string message = "Concatenated pointcloud is published and include all topics"; From 9523a3b78d6bfe19f043009404b28098563d4e1c Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 26 Dec 2024 11:58:44 +0900 Subject: [PATCH 110/115] chore: fix figure and diagnostic msg in readme Signed-off-by: vividf --- .../docs/concatenate-data.md | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index 1d6d5e88ccb13..3c142cbd032de 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -12,9 +12,7 @@ For example, consider a vehicle equipped with three LiDAR sensors mounted on the After processing the data through the `concatenate_and_time_synchronize_node`, the outputs from all LiDARs are combined into a single comprehensive point cloud that provides a complete view of the environment: -
- ![Full Scene View](./image/concatenate_all.png) -
+![Full Scene View](./image/concatenate_all.png) This resulting point cloud allows autonomous systems to detect obstacles, map the environment, and navigate more effectively, leveraging the complementary fields of view from multiple LiDAR sensors. @@ -130,7 +128,7 @@ colcon test --packages-select autoware_pointcloud_preprocessor --event-handlers ## Debug and Diagnostics -To verify whether the node has successfully concatenated the point clouds, the user can examine the `/diagnostics` topic using the following command: +To verify whether the node has successfully concatenated the point clouds, the user can examine rqt or the `/diagnostics` topic using the following command: ```bash ros2 topic echo /diagnostics @@ -138,8 +136,8 @@ ros2 topic echo /diagnostics Below is an example output when the point clouds are concatenated successfully: -- Each point cloud has a value of `1`. -- The `concatenate status` is `1`. +- Each point cloud has a value of `True`. +- The `cloud concatenation success` is `True`. - The `level` value is `\0`. (diagnostic_msgs::msg::DiagnosticStatus::OK) ```bash @@ -162,24 +160,24 @@ status: value: '1718260240.169229984' - key: /sensing/lidar/left/pointcloud_before_sync timestamp value: '1718260240.159229994' - - key: /sensing/lidar/left/pointcloud_before_sync - value: '1' + - key: /sensing/lidar/left/pointcloud_before_sync is concatenated + value: 'True' - key: /sensing/lidar/right/pointcloud_before_sync timestamp value: '1718260240.194104910' - - key: /sensing/lidar/right/pointcloud_before_sync - value: '1' + - key: /sensing/lidar/right/pointcloud_before_sync is concatenated + value: 'True' - key: /sensing/lidar/top/pointcloud_before_sync timestamp value: '1718260240.234578133' - - key: /sensing/lidar/top/pointcloud_before_sync - value: '1' - - key: concatenate status - value: '1' + - key: /sensing/lidar/top/pointcloud_before_sync is concatenated + value: 'True' + - key: cloud concatenation success + value: 'True' ``` Below is an example when point clouds fail to concatenate successfully. -- Some point clouds might have values of `0`. -- The `concatenate status` is `0`. +- Some point clouds might have values of `False`. +- The `cloud concatenation success` is `False`. - The `level` value is `\x02`. (diagnostic_msgs::msg::DiagnosticStatus::ERROR) ```bash @@ -202,16 +200,16 @@ status: value: '1718260240.869827986' - key: /sensing/lidar/left/pointcloud_before_sync timestamp value: '1718260240.859827995' - - key: /sensing/lidar/left/pointcloud_before_sync - value: '1' + - key: /sensing/lidar/left/pointcloud_before_sync is concatenated + value: 'True' - key: /sensing/lidar/right/pointcloud_before_sync timestamp value: '1718260240.895193815' - - key: /sensing/lidar/right/pointcloud_before_sync - value: '1' - - key: /sensing/lidar/top/pointcloud_before_sync - value: '0' - - key: concatenate status - value: '0' + - key: /sensing/lidar/right/pointcloud_before_sync is concatenated + value: 'True' + - key: /sensing/lidar/top/pointcloud_before_sync is concatenated + value: 'False' + - key: cloud concatenation success + value: 'False' ``` ## Node separation options From d01eba81f0b51cb3103538ee302877228e51ee1b Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 7 Jan 2025 00:45:44 +0900 Subject: [PATCH 111/115] chroe: refactor collectorinfo structure Signed-off-by: vividf --- .../config/concatenate_pointclouds.param.yaml | 23 +++++++++++++------ .../concatenate_data/cloud_collector.hpp | 19 +++++---------- .../collector_matching_strategy.hpp | 3 --- .../src/concatenate_data/cloud_collector.cpp | 20 +++++++--------- .../collector_matching_strategy.cpp | 12 ++++------ 5 files changed, 34 insertions(+), 43 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_pointclouds.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_pointclouds.param.yaml index 449795c328402..56fe6643b9973 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_pointclouds.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_pointclouds.param.yaml @@ -1,12 +1,21 @@ /**: ros__parameters: - output_frame: base_link - has_static_tf_only: true + debug_mode: false + has_static_tf_only: false + rosbag_length: 10.0 + maximum_queue_size: 5 + timeout_sec: 0.2 + is_motion_compensated: true + publish_synchronized_pointcloud: true + keep_input_frame_in_synchronized_pointcloud: true + publish_previous_but_late_pointcloud: false + synchronized_pointcloud_postfix: pointcloud + input_twist_topic_type: twist input_topics: [ - "/sensing/lidar/left/pointcloud_before_sync", "/sensing/lidar/right/pointcloud_before_sync", - "/sensing/lidar/top/pointcloud_before_sync" + "/sensing/lidar/top/pointcloud_before_sync", + "/sensing/lidar/left/pointcloud_before_sync", ] - max_queue_size: 5 - timeout_sec: 0.033 - input_offset: [0.0 ,0.0 ,0.0] + output_frame: base_link + matching_strategy: + type: naive diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index ae97752b2f6d7..13604569df9a8 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -18,7 +18,6 @@ #include #include -#include #include namespace autoware::pointcloud_preprocessor @@ -27,32 +26,26 @@ namespace autoware::pointcloud_preprocessor class PointCloudConcatenateDataSynchronizerComponent; class CombineCloudHandler; -enum class CollectorStrategyType { Naive, Advanced }; - struct CollectorInfoBase { virtual ~CollectorInfoBase() = default; - [[nodiscard]] virtual CollectorStrategyType getStrategyType() const = 0; }; struct NaiveCollectorInfo : public CollectorInfoBase { - double timestamp{0.0}; + double timestamp; - [[nodiscard]] CollectorStrategyType getStrategyType() const override - { - return CollectorStrategyType::Naive; - } + explicit NaiveCollectorInfo(double timestamp = 0.0) : timestamp(timestamp) {} }; struct AdvancedCollectorInfo : public CollectorInfoBase { - double timestamp{0.0}; - double noise_window{0.0}; + double timestamp; + double noise_window; - [[nodiscard]] CollectorStrategyType getStrategyType() const override + explicit AdvancedCollectorInfo(double timestamp = 0.0, double noise_window = 0.0) + : timestamp(timestamp), noise_window(noise_window) { - return CollectorStrategyType::Advanced; } }; diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp index a6aa39ef7ab74..8502d3f89bf42 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp @@ -47,9 +47,6 @@ class CollectorMatchingStrategy const MatchingParams & params) const = 0; virtual void set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) = 0; - -protected: - CollectorStrategyType strategy_type_; }; class NaiveMatchingStrategy : public CollectorMatchingStrategy diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index c3e2d7eedff25..63ee23d204788 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -128,18 +128,14 @@ void CloudCollector::show_debug_message() log_stream << "Collector's concatenate callback time: " << ros2_parent_node_->get_clock()->now().seconds() << " seconds\n"; - if (collector_info_) { - if (collector_info_->getStrategyType() == CollectorStrategyType::Advanced) { - auto advanced_info = std::dynamic_pointer_cast(collector_info_); - log_stream << "Advanced strategy:\n Collector's reference time min: " - << advanced_info->timestamp - advanced_info->noise_window - << " to max: " << advanced_info->timestamp + advanced_info->noise_window - << " seconds\n"; - } else if (collector_info_->getStrategyType() == CollectorStrategyType::Naive) { - auto naive_info = std::dynamic_pointer_cast(collector_info_); - log_stream << "Naive strategy:\n Collector's timestamp: " << naive_info->timestamp - << " seconds\n"; - } + if (auto advanced_info = std::dynamic_pointer_cast(collector_info_)) { + log_stream << "Advanced strategy:\n Collector's reference time min: " + << advanced_info->timestamp - advanced_info->noise_window + << " to max: " << advanced_info->timestamp + advanced_info->noise_window + << " seconds\n"; + } else if (auto naive_info = std::dynamic_pointer_cast(collector_info_)) { + log_stream << "Naive strategy:\n Collector's timestamp: " << naive_info->timestamp + << " seconds\n"; } log_stream << "Time until trigger: " << (time_until_trigger.count() / 1e9) << " seconds\n"; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp index fa233994c34e7..5addd7044e579 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp @@ -28,7 +28,6 @@ namespace autoware::pointcloud_preprocessor NaiveMatchingStrategy::NaiveMatchingStrategy(rclcpp::Node & node) { - strategy_type_ = CollectorStrategyType::Naive; RCLCPP_INFO(node.get_logger(), "Utilize naive matching strategy"); } @@ -58,15 +57,13 @@ std::optional> NaiveMatchingStrategy::match_clou void NaiveMatchingStrategy::set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) { - auto info = std::make_shared(); - info->timestamp = matching_params.cloud_arrival_time; + auto info = std::make_shared(matching_params.cloud_arrival_time); collector->set_info(info); } AdvancedMatchingStrategy::AdvancedMatchingStrategy( rclcpp::Node & node, std::vector input_topics) { - strategy_type_ = CollectorStrategyType::Advanced; auto lidar_timestamp_offsets = node.declare_parameter>("matching_strategy.lidar_timestamp_offsets"); auto lidar_timestamp_noise_window = @@ -114,10 +111,9 @@ std::optional> AdvancedMatchingStrategy::match_c void AdvancedMatchingStrategy::set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) { - auto info = std::make_shared(); - info->timestamp = - matching_params.cloud_timestamp - topic_to_offset_map_[matching_params.topic_name]; - info->noise_window = topic_to_noise_window_map_[matching_params.topic_name]; + auto info = std::make_shared( + matching_params.cloud_timestamp - topic_to_offset_map_[matching_params.topic_name], + topic_to_noise_window_map_[matching_params.topic_name]); collector->set_info(info); } From f8fc63c0ce627da837050b42fd4c4b867806810f Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 7 Jan 2025 00:48:59 +0900 Subject: [PATCH 112/115] chore: revert wrong file changes Signed-off-by: vividf --- .../config/concatenate_pointclouds.param.yaml | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_pointclouds.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_pointclouds.param.yaml index 56fe6643b9973..449795c328402 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_pointclouds.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_pointclouds.param.yaml @@ -1,21 +1,12 @@ /**: ros__parameters: - debug_mode: false - has_static_tf_only: false - rosbag_length: 10.0 - maximum_queue_size: 5 - timeout_sec: 0.2 - is_motion_compensated: true - publish_synchronized_pointcloud: true - keep_input_frame_in_synchronized_pointcloud: true - publish_previous_but_late_pointcloud: false - synchronized_pointcloud_postfix: pointcloud - input_twist_topic_type: twist + output_frame: base_link + has_static_tf_only: true input_topics: [ - "/sensing/lidar/right/pointcloud_before_sync", - "/sensing/lidar/top/pointcloud_before_sync", "/sensing/lidar/left/pointcloud_before_sync", + "/sensing/lidar/right/pointcloud_before_sync", + "/sensing/lidar/top/pointcloud_before_sync" ] - output_frame: base_link - matching_strategy: - type: naive + max_queue_size: 5 + timeout_sec: 0.033 + input_offset: [0.0 ,0.0 ,0.0] From 074ab679ed897fc89097efb915c65866d56551f9 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 10 Jan 2025 13:12:47 +0900 Subject: [PATCH 113/115] chore: improve message Signed-off-by: vividf --- .../collector_matching_strategy.cpp | 2 +- .../concatenate_and_time_sync_node.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp index 5addd7044e579..a88495cd15915 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp @@ -112,7 +112,7 @@ void AdvancedMatchingStrategy::set_collector_info( std::shared_ptr & collector, const MatchingParams & matching_params) { auto info = std::make_shared( - matching_params.cloud_timestamp - topic_to_offset_map_[matching_params.topic_name], + matching_params.cloud_timestamp - topic_to_offset_map_.at(matching_params.topic_name), topic_to_noise_window_map_[matching_params.topic_name]); collector->set_info(info); } diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 72ddb489f4dac..59a97f2d6e789 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -368,19 +368,19 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( { if (publish_pointcloud_ || drop_previous_but_late_pointcloud_) { stat.add( - "concatenated cloud timestamp", format_timestamp(current_concatenate_cloud_timestamp_)); + "concatenated_cloud_timestamp", format_timestamp(current_concatenate_cloud_timestamp_)); if ( auto naive_info = std::dynamic_pointer_cast(diagnostic_collector_info_)) { - stat.add("first cloud's arrival timestamp", format_timestamp(naive_info->timestamp)); + stat.add("first_cloud_arrival_timestamp", format_timestamp(naive_info->timestamp)); } else if ( auto advanced_info = std::dynamic_pointer_cast(diagnostic_collector_info_)) { stat.add( - "reference timestamp min", + "reference_timestamp_min", format_timestamp(advanced_info->timestamp - advanced_info->noise_window)); stat.add( - "reference timestamp max", + "reference_timestamp_max", format_timestamp(advanced_info->timestamp + advanced_info->noise_window)); } @@ -393,16 +393,16 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( diagnostic_topic_to_original_stamp_map_.find(topic) != diagnostic_topic_to_original_stamp_map_.end()) { stat.add( - topic + " timestamp", format_timestamp(diagnostic_topic_to_original_stamp_map_[topic])); + topic + "_timestamp", format_timestamp(diagnostic_topic_to_original_stamp_map_[topic])); } else { topic_miss = true; concatenation_success = false; input_cloud_concatenated = false; } - stat.add(topic + " is concatenated", input_cloud_concatenated); + stat.add(topic + "_is_concatenated", input_cloud_concatenated); } - stat.add("cloud concatenation success", concatenation_success); + stat.add("cloud_concatenation_success", concatenation_success); int8_t level = diagnostic_msgs::msg::DiagnosticStatus::OK; std::string message = "Concatenated pointcloud is published and include all topics"; From ccd5aab323b1579fd9608a39e71b26e7dc4424b2 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 10 Jan 2025 16:57:38 +0900 Subject: [PATCH 114/115] chore: remove unused input topics Signed-off-by: vividf --- .../concatenate_data/collector_matching_strategy.hpp | 1 - .../concatenate_data/combine_cloud_handler.hpp | 7 +++---- .../src/concatenate_data/collector_matching_strategy.cpp | 2 -- .../src/concatenate_data/combine_cloud_handler.cpp | 7 +++---- .../concatenate_data/concatenate_and_time_sync_node.cpp | 6 +++--- .../test/test_concatenate_node_unit.cpp | 3 +-- 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp index 8502d3f89bf42..c314b24a07921 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp @@ -72,7 +72,6 @@ class AdvancedMatchingStrategy : public CollectorMatchingStrategy std::shared_ptr & collector, const MatchingParams & matching_params) override; private: - std::vector input_topics_; std::unordered_map topic_to_offset_map_; std::unordered_map topic_to_noise_window_map_; }; diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp index 2a9532760a465..fa8c58aa74c11 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/combine_cloud_handler.hpp @@ -61,7 +61,6 @@ class CombineCloudHandler private: rclcpp::Node & node_; - std::vector input_topics_; std::string output_frame_; bool is_motion_compensated_; bool publish_synchronized_pointcloud_; @@ -92,9 +91,9 @@ class CombineCloudHandler public: CombineCloudHandler( - rclcpp::Node & node, std::vector input_topics, std::string output_frame, - bool is_motion_compensated, bool publish_synchronized_pointcloud, - bool keep_input_frame_in_synchronized_pointcloud, bool has_static_tf_only); + rclcpp::Node & node, std::string output_frame, bool is_motion_compensated, + bool publish_synchronized_pointcloud, bool keep_input_frame_in_synchronized_pointcloud, + bool has_static_tf_only); void process_twist( const geometry_msgs::msg::TwistWithCovarianceStamped::ConstSharedPtr & twist_msg); void process_odometry(const nav_msgs::msg::Odometry::ConstSharedPtr & odometry_msg); diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp index a88495cd15915..192d2115e2a1d 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp @@ -83,8 +83,6 @@ AdvancedMatchingStrategy::AdvancedMatchingStrategy( topic_to_noise_window_map_[input_topics[i]] = lidar_timestamp_noise_window[i]; } - input_topics_ = input_topics; - RCLCPP_INFO(node.get_logger(), "Utilize advanced matching strategy"); } diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp index d86e4b392a2f5..d68218314830b 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/combine_cloud_handler.cpp @@ -31,11 +31,10 @@ namespace autoware::pointcloud_preprocessor { CombineCloudHandler::CombineCloudHandler( - rclcpp::Node & node, std::vector input_topics, std::string output_frame, - bool is_motion_compensated, bool publish_synchronized_pointcloud, - bool keep_input_frame_in_synchronized_pointcloud, bool has_static_tf_only) + rclcpp::Node & node, std::string output_frame, bool is_motion_compensated, + bool publish_synchronized_pointcloud, bool keep_input_frame_in_synchronized_pointcloud, + bool has_static_tf_only) : node_(node), - input_topics_(std::move(input_topics)), output_frame_(std::move(output_frame)), is_motion_compensated_(is_motion_compensated), publish_synchronized_pointcloud_(publish_synchronized_pointcloud), diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index 59a97f2d6e789..a09a15ac164c8 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -138,7 +138,7 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro // Combine cloud handler combine_cloud_handler_ = std::make_shared( - *this, params_.input_topics, params_.output_frame, params_.is_motion_compensated, + *this, params_.output_frame, params_.is_motion_compensated, params_.publish_synchronized_pointcloud, params_.keep_input_frame_in_synchronized_pointcloud, params_.has_static_tf_only); @@ -393,13 +393,13 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( diagnostic_topic_to_original_stamp_map_.find(topic) != diagnostic_topic_to_original_stamp_map_.end()) { stat.add( - topic + "_timestamp", format_timestamp(diagnostic_topic_to_original_stamp_map_[topic])); + topic + "/timestamp", format_timestamp(diagnostic_topic_to_original_stamp_map_[topic])); } else { topic_miss = true; concatenation_success = false; input_cloud_concatenated = false; } - stat.add(topic + "_is_concatenated", input_cloud_concatenated); + stat.add(topic + "/is_concatenated", input_cloud_concatenated); } stat.add("cloud_concatenation_success", concatenation_success); diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index 8ca9f5c727ec4..bc3c7e9538c84 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -67,8 +67,7 @@ class ConcatenateCloudTest : public ::testing::Test node_options); combine_cloud_handler_ = std::make_shared( - *concatenate_node_, std::vector{"lidar_top", "lidar_left", "lidar_right"}, - "base_link", true, true, true, false); + *concatenate_node_, "base_link", true, true, true, false); collector_ = std::make_shared( std::dynamic_pointer_cast< From 137766fd2e978d76b02e2b4da9c1ae7d0953be55 Mon Sep 17 00:00:00 2001 From: vividf Date: Tue, 14 Jan 2025 10:56:57 +0900 Subject: [PATCH 115/115] chore: change to explicit check Signed-off-by: vividf --- .../src/concatenate_data/collector_matching_strategy.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp index 192d2115e2a1d..50058df3d91af 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp @@ -51,7 +51,10 @@ std::optional> NaiveMatchingStrategy::match_clou } } - return closest_collector; + if (closest_collector != nullptr) { + return closest_collector; + } + return std::nullopt; } void NaiveMatchingStrategy::set_collector_info(

3EU}&_!zM$N0ulV&3`ZJP8wqw7Nvj=eR32P`a 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 literal 0 HcmV?d00001 diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp index 1cdce9408083c..867b0adbca1b5 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp @@ -42,7 +42,7 @@ class CloudCollector void set_reference_timestamp(double timestamp, double noise_window); std::tuple get_reference_timestamp_boundary(); bool topic_exists(const std::string & topic_name); - void process_pointcloud( + bool process_pointcloud( const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud); void concatenate_callback(); @@ -52,6 +52,8 @@ class CloudCollector std::unordered_map get_topic_to_cloud_map(); + bool concatenate_finished() const; + private: std::shared_ptr ros2_parent_node_; std::shared_ptr combine_cloud_handler_; @@ -63,6 +65,8 @@ class CloudCollector double reference_timestamp_max_{0.0}; double arrival_timestamp_{0.0}; // This is used for the naive approach bool debug_mode_; + bool concatenate_finished_{false}; + std::mutex concatenate_mutex_; }; } // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp new file mode 100644 index 0000000000000..c02131956156c --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/collector_matching_strategy.hpp @@ -0,0 +1,82 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "cloud_collector.hpp" + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace autoware::pointcloud_preprocessor +{ + +struct MatchingParams +{ + std::string topic_name; + double cloud_timestamp; + double cloud_arrival_time; +}; + +class CollectorMatchingStrategy +{ +public: + virtual ~CollectorMatchingStrategy() = default; + + [[nodiscard]] virtual std::optional> match_cloud_to_collector( + const std::list> & cloud_collectors, + const MatchingParams & params) const = 0; + virtual void set_collector_timestamp( + std::shared_ptr & collector, const MatchingParams & matching_params) = 0; +}; + +class NaiveMatchingStrategy : public CollectorMatchingStrategy +{ +public: + explicit NaiveMatchingStrategy(rclcpp::Node & node); + [[nodiscard]] std::optional> match_cloud_to_collector( + const std::list> & cloud_collectors, + const MatchingParams & params) const override; + void set_collector_timestamp( + std::shared_ptr & collector, const MatchingParams & matching_params) override; +}; + +class AdvancedMatchingStrategy : public CollectorMatchingStrategy +{ +public: + explicit AdvancedMatchingStrategy(rclcpp::Node & node, std::vector input_topics); + + [[nodiscard]] std::optional> match_cloud_to_collector( + const std::list> & cloud_collectors, + const MatchingParams & params) const override; + void set_collector_timestamp( + std::shared_ptr & collector, const MatchingParams & matching_params) override; + +private: + std::vector input_topics_; + std::unordered_map topic_to_offset_map_; + std::unordered_map topic_to_noise_window_map_; +}; + +std::shared_ptr parse_matching_strategy(rclcpp::Node & node); + +} // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 781935e621408..1e5d95066c556 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -23,6 +23,7 @@ // ROS includes #include "cloud_collector.hpp" +#include "collector_matching_strategy.hpp" #include "combine_cloud_handler.hpp" #include @@ -55,7 +56,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node void publish_clouds( ConcatenatedCloudResult && concatenated_cloud_result, double reference_timestamp_min, double reference_timestamp_max, double arrival_timestamp); - void delete_collector(CloudCollector & cloud_collector); + void manage_collector_list(); std::list> get_cloud_collectors(); void add_cloud_collector(const std::shared_ptr & collector); @@ -76,8 +77,6 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node std::string input_twist_topic_type; std::vector input_topics; std::string output_frame; - std::vector lidar_timestamp_offsets; - std::vector lidar_timestamp_noise_window; } params_; double current_concatenate_cloud_timestamp_{0.0}; @@ -92,9 +91,8 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node std::shared_ptr combine_cloud_handler_; std::list> cloud_collectors_; + std::unique_ptr collector_matching_strategy_; std::mutex cloud_collectors_mutex_; - std::unordered_map topic_to_offset_map_; - std::unordered_map topic_to_noise_window_map_; // default postfix name for synchronized pointcloud static constexpr const char * default_sync_topic_postfix = "_synchronized"; diff --git a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json index 800a79d35dcf8..c3758d05a8349 100644 --- a/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json +++ b/sensing/autoware_pointcloud_preprocessor/schema/concatenate_and_time_sync_node.schema.json @@ -6,11 +6,6 @@ "concatenate_and_time_sync_node": { "type": "object", "properties": { - "use_naive_approach": { - "type": "boolean", - "default": false, - "description": "Flag to enable a naive approach for concatenating point clouds without considering their timestamps. Set this to `false` and adjust `lidar_timestamp_offsets` and `lidar_timestamp_noise_window` if your LiDAR sensor supports synchronization." - }, "debug_mode": { "type": "boolean", "default": false, @@ -86,29 +81,56 @@ "minLength": 1, "description": "Output frame." }, - "lidar_timestamp_offsets": { - "type": "array", - "items": { - "type": "number", - "minimum": 0.0 + "matching_strategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["naive", "advanced"], + "default": "advanced", + "description": "Set it to `advanced` if you can synchronize your LiDAR sensor; otherwise, set it to `naive`." + }, + "lidar_timestamp_offsets": { + "type": "array", + "items": { + "type": "number", + "minimum": 0.0 + }, + "default": [0.0, 0.0, 0.0], + "minItems": 2, + "description": "List of LiDAR timestamp offsets in seconds (relative to the earliest LiDAR timestamp). The offsets should be provided in the same order as the input topics." + }, + "lidar_timestamp_noise_window": { + "type": "array", + "items": { + "type": "number", + "minimum": 0.0 + }, + "default": [0.01, 0.01, 0.01], + "minItems": 2, + "description": "List of LiDAR timestamp noise windows in seconds. The noise values should be specified in the same order as the input_topics." + } }, - "default": [0.0, 0.0, 0.0], - "minItems": 2, - "description": "List of LiDAR timestamp offsets in seconds (relative to the earliest LiDAR timestamp). The offsets should be provided in the same order as the input topics." - }, - "lidar_timestamp_noise_window": { - "type": "array", - "items": { - "type": "number", - "minimum": 0.0 - }, - "default": [0.01, 0.01, 0.01], - "minItems": 2, - "description": "List of LiDAR timestamp noise windows in seconds. The noise values should be specified in the same order as the input_topics." + "required": ["type"], + "dependencies": { + "type": { + "oneOf": [ + { + "properties": { "type": { "const": "naive" } }, + "not": { + "required": ["lidar_timestamp_offsets", "lidar_timestamp_noise_window"] + } + }, + { + "properties": { "type": { "const": "advanced" } }, + "required": ["lidar_timestamp_offsets", "lidar_timestamp_noise_window"] + } + ] + } + } } }, "required": [ - "use_naive_approach", "debug_mode", "has_static_tf_only", "rosbag_length", @@ -122,8 +144,7 @@ "input_twist_topic_type", "input_topics", "output_frame", - "lidar_timestamp_offsets", - "lidar_timestamp_noise_window" + "matching_strategy" ] } }, diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp index bc74a06e21a3f..a539d7c817654 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/cloud_collector.cpp @@ -41,9 +41,12 @@ CloudCollector::CloudCollector( const auto period_ns = std::chrono::duration_cast( std::chrono::duration(timeout_sec_)); - timer_ = rclcpp::create_timer( - ros2_parent_node_, ros2_parent_node_->get_clock(), period_ns, - std::bind(&CloudCollector::concatenate_callback, this)); + timer_ = + rclcpp::create_timer(ros2_parent_node_, ros2_parent_node_->get_clock(), period_ns, [this]() { + std::lock_guard concatenate_lock(concatenate_mutex_); + if (concatenate_finished_) return; + concatenate_callback(); + }); } void CloudCollector::set_arrival_timestamp(double timestamp) @@ -72,9 +75,12 @@ bool CloudCollector::topic_exists(const std::string & topic_name) return topic_to_cloud_map_.find(topic_name) != topic_to_cloud_map_.end(); } -void CloudCollector::process_pointcloud( +bool CloudCollector::process_pointcloud( const std::string & topic_name, sensor_msgs::msg::PointCloud2::SharedPtr cloud) { + std::lock_guard concatenate_lock(concatenate_mutex_); + if (concatenate_finished_) return false; + // Check if the map already contains an entry for the same topic. This shouldn't happen if the // parameter 'lidar_timestamp_noise_window' is set correctly. if (topic_to_cloud_map_.find(topic_name) != topic_to_cloud_map_.end()) { @@ -88,6 +94,13 @@ void CloudCollector::process_pointcloud( if (topic_to_cloud_map_.size() == num_of_clouds_) { concatenate_callback(); } + + return true; +} + +bool CloudCollector::concatenate_finished() const +{ + return concatenate_finished_; } void CloudCollector::concatenate_callback() @@ -127,7 +140,7 @@ void CloudCollector::concatenate_callback() std::move(concatenated_cloud_result), reference_timestamp_min_, reference_timestamp_max_, arrival_timestamp_); - ros2_parent_node_->delete_collector(*this); + concatenate_finished_ = true; } ConcatenatedCloudResult CloudCollector::concatenate_pointclouds( diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp new file mode 100644 index 0000000000000..81de3268d4a03 --- /dev/null +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/collector_matching_strategy.cpp @@ -0,0 +1,111 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include +#include +#include +#include + +namespace autoware::pointcloud_preprocessor +{ + +NaiveMatchingStrategy::NaiveMatchingStrategy(rclcpp::Node & node) +{ + RCLCPP_INFO(node.get_logger(), "Utilize naive matching strategy"); +} + +std::optional> NaiveMatchingStrategy::match_cloud_to_collector( + const std::list> & cloud_collectors, + const MatchingParams & params) const +{ + std::optional smallest_time_difference = std::nullopt; + std::shared_ptr closest_collector = nullptr; + + for (const auto & cloud_collector : cloud_collectors) { + if (!cloud_collector->topic_exists(params.topic_name)) { + double time_difference = + std::abs(params.cloud_arrival_time - cloud_collector->get_arrival_timestamp()); + if (!smallest_time_difference || time_difference < smallest_time_difference) { + smallest_time_difference = time_difference; + closest_collector = cloud_collector; + } + } + } + + return closest_collector; +} + +void NaiveMatchingStrategy::set_collector_timestamp( + std::shared_ptr & collector, const MatchingParams & matching_params) +{ + collector->set_arrival_timestamp(matching_params.cloud_arrival_time); +} + +AdvancedMatchingStrategy::AdvancedMatchingStrategy( + rclcpp::Node & node, std::vector input_topics) +{ + auto lidar_timestamp_offsets = + node.declare_parameter>("matching_strategy.lidar_timestamp_offsets"); + auto lidar_timestamp_noise_window = + node.declare_parameter>("matching_strategy.lidar_timestamp_noise_window"); + + if (lidar_timestamp_offsets.size() != input_topics.size()) { + throw std::runtime_error( + "The number of topics does not match the number of timestamp offsets."); + } + if (lidar_timestamp_noise_window.size() != input_topics.size()) { + throw std::runtime_error( + "The number of topics does not match the number of timestamp noise window."); + } + + for (size_t i = 0; i < input_topics.size(); i++) { + topic_to_offset_map_[input_topics[i]] = lidar_timestamp_offsets[i]; + topic_to_noise_window_map_[input_topics[i]] = lidar_timestamp_noise_window[i]; + } + + input_topics_ = input_topics; + + RCLCPP_INFO(node.get_logger(), "Utilize advanced matching strategy"); +} + +std::optional> AdvancedMatchingStrategy::match_cloud_to_collector( + const std::list> & cloud_collectors, + const MatchingParams & params) const +{ + for (const auto & cloud_collector : cloud_collectors) { + auto [reference_timestamp_min, reference_timestamp_max] = + cloud_collector->get_reference_timestamp_boundary(); + double time = params.cloud_timestamp - topic_to_offset_map_.at(params.topic_name); + if ( + time < reference_timestamp_max + topic_to_noise_window_map_.at(params.topic_name) && + time > reference_timestamp_min - topic_to_noise_window_map_.at(params.topic_name)) { + return cloud_collector; + } + } + return std::nullopt; +} + +void AdvancedMatchingStrategy::set_collector_timestamp( + std::shared_ptr & collector, const MatchingParams & matching_params) +{ + collector->set_reference_timestamp( + matching_params.cloud_timestamp - topic_to_offset_map_[matching_params.topic_name], + topic_to_noise_window_map_[matching_params.topic_name]); +} + +} // namespace autoware::pointcloud_preprocessor diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index d940e51a4542c..b4d8d30dbbeca 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -14,16 +14,17 @@ #include "autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp" +#include "autoware/pointcloud_preprocessor/concatenate_data/cloud_collector.hpp" #include "autoware/pointcloud_preprocessor/utility/memory.hpp" #include #include -#include #include #include #include +#include #include #include #include @@ -45,7 +46,6 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro stop_watch_ptr_->tic("processing_time"); // initialize parameters - params_.use_naive_approach = declare_parameter("use_naive_approach"); params_.debug_mode = declare_parameter("debug_mode"); params_.has_static_tf_only = declare_parameter("has_static_tf_only"); params_.rosbag_length = declare_parameter("rosbag_length"); @@ -63,10 +63,6 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro params_.input_twist_topic_type = declare_parameter("input_twist_topic_type"); params_.input_topics = declare_parameter>("input_topics"); params_.output_frame = declare_parameter("output_frame"); - params_.lidar_timestamp_offsets = - declare_parameter>("lidar_timestamp_offsets"); - params_.lidar_timestamp_noise_window = - declare_parameter>("lidar_timestamp_noise_window"); if (params_.input_topics.empty()) { throw std::runtime_error("Need a 'input_topics' parameter to be set before continuing."); @@ -78,18 +74,15 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro if (params_.output_frame.empty()) { throw std::runtime_error("Need an 'output_frame' parameter to be set before continuing."); } - if (params_.lidar_timestamp_offsets.size() != params_.input_topics.size()) { - throw std::runtime_error( - "The number of topics does not match the number of timestamp offsets."); - } - if (params_.lidar_timestamp_noise_window.size() != params_.input_topics.size()) { - throw std::runtime_error( - "The number of topics does not match the number of timestamp noise window."); - } - for (size_t i = 0; i < params_.input_topics.size(); i++) { - topic_to_offset_map_[params_.input_topics[i]] = params_.lidar_timestamp_offsets[i]; - topic_to_noise_window_map_[params_.input_topics[i]] = params_.lidar_timestamp_noise_window[i]; + auto matching_strategy = declare_parameter("matching_strategy.type"); + if (matching_strategy == "naive") { + collector_matching_strategy_ = std::make_unique(*this); + } else if (matching_strategy == "advanced") { + collector_matching_strategy_ = + std::make_unique(*this, params_.input_topics); + } else { + throw std::runtime_error("Matching strategy must be 'advanced' or 'naive'"); } // Publishers @@ -188,9 +181,10 @@ std::string PointCloudConcatenateDataSynchronizerComponent::replace_sync_topic_n void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( const sensor_msgs::msg::PointCloud2::SharedPtr & input_ptr, const std::string & topic_name) { + stop_watch_ptr_->toc("processing_time", true); double cloud_arrival_time = this->get_clock()->now().seconds(); + manage_collector_list(); - stop_watch_ptr_->toc("processing_time", true); if (!utils::is_data_layout_compatible_with_point_xyzirc(*input_ptr)) { RCLCPP_ERROR( get_logger(), "The pointcloud layout is not compatible with PointXYZIRC. Aborting"); @@ -220,54 +214,27 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( std::unique_lock cloud_collectors_lock(cloud_collectors_mutex_); // For each callback, check whether there is a exist collector that matches this cloud - bool collector_found = false; + std::optional> cloud_collector = std::nullopt; + MatchingParams matching_params; + matching_params.topic_name = topic_name; + matching_params.cloud_arrival_time = cloud_arrival_time; + matching_params.cloud_timestamp = rclcpp::Time(input_ptr->header.stamp).seconds(); if (!cloud_collectors_.empty()) { - if (params_.use_naive_approach) { - // Navie approach: Concatenate the pointlcouds based on the pointclouds' arrival time on the - // node - std::optional smallest_time_difference = std::nullopt; - std::shared_ptr closest_collector = nullptr; - - for (const auto & cloud_collector : cloud_collectors_) { - if (!cloud_collector->topic_exists(topic_name)) { - auto collector_timestamp = cloud_collector->get_arrival_timestamp(); - double time_difference = std::abs(cloud_arrival_time - collector_timestamp); - - if (!smallest_time_difference || time_difference < smallest_time_difference) { - smallest_time_difference = time_difference; - closest_collector = cloud_collector; - } - } - } - - if (smallest_time_difference) { - cloud_collectors_lock.unlock(); - closest_collector->process_pointcloud(topic_name, input_ptr); - collector_found = true; - } - } else { - // Advanced approach: Concatenate the pointlcouds based on the pointclouds' timestamps - for (const auto & cloud_collector : cloud_collectors_) { - auto [reference_timestamp_min, reference_timestamp_max] = - cloud_collector->get_reference_timestamp_boundary(); + cloud_collector = + collector_matching_strategy_->match_cloud_to_collector(cloud_collectors_, matching_params); + } - if ( - rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] < - reference_timestamp_max + topic_to_noise_window_map_[topic_name] && - rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name] > - reference_timestamp_min - topic_to_noise_window_map_[topic_name]) { - cloud_collectors_lock.unlock(); - cloud_collector->process_pointcloud(topic_name, input_ptr); - collector_found = true; - break; - } - } + bool process_success = false; + if (cloud_collector.has_value()) { + auto collector = cloud_collector.value(); + if (collector) { + cloud_collectors_lock.unlock(); + process_success = cloud_collector.value()->process_pointcloud(topic_name, input_ptr); } } - // if point cloud didn't find matched collector, create a new collector. - if (!collector_found) { + if (!process_success) { auto new_cloud_collector = std::make_shared( std::dynamic_pointer_cast(shared_from_this()), combine_cloud_handler_, params_.input_topics.size(), params_.timeout_sec, params_.debug_mode); @@ -275,14 +242,8 @@ void PointCloudConcatenateDataSynchronizerComponent::cloud_callback( cloud_collectors_.push_back(new_cloud_collector); cloud_collectors_lock.unlock(); - if (params_.use_naive_approach) { - new_cloud_collector->set_arrival_timestamp(cloud_arrival_time); - } else { - new_cloud_collector->set_reference_timestamp( - rclcpp::Time(input_ptr->header.stamp).seconds() - topic_to_offset_map_[topic_name], - topic_to_noise_window_map_[topic_name]); - } - new_cloud_collector->process_pointcloud(topic_name, input_ptr); + collector_matching_strategy_->set_collector_timestamp(new_cloud_collector, matching_params); + (void)new_cloud_collector->process_pointcloud(topic_name, input_ptr); } } @@ -383,22 +344,16 @@ void PointCloudConcatenateDataSynchronizerComponent::publish_clouds( } } -void PointCloudConcatenateDataSynchronizerComponent::delete_collector( - CloudCollector & cloud_collector) +void PointCloudConcatenateDataSynchronizerComponent::manage_collector_list() { - // protect cloud collectors list std::lock_guard cloud_collectors_lock(cloud_collectors_mutex_); - // change this to something else - auto it = std::find_if( - cloud_collectors_.begin(), cloud_collectors_.end(), - [&cloud_collector](const std::shared_ptr & collector) { - return collector.get() == &cloud_collector; - }); - if (it != cloud_collectors_.end()) { - cloud_collectors_.erase(it); - } else { - throw std::runtime_error("Try to delete a cloud_collector that is not in the cloud_collectors"); + for (auto it = cloud_collectors_.begin(); it != cloud_collectors_.end();) { + if ((*it)->concatenate_finished()) { + it = cloud_collectors_.erase(it); // Erase and move the iterator to the next element + } else { + ++it; // Move to the next element + } } } diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py index c94eaaf1eade1..3d15eecf0101c 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_component.py @@ -86,7 +86,6 @@ def generate_test_description(): ], parameters=[ { - "use_naive_approach": False, "debug_mode": False, "has_static_tf_only": False, "rosbag_length": 0.0, @@ -100,8 +99,9 @@ def generate_test_description(): "input_twist_topic_type": "twist", "input_topics": INPUT_LIDAR_TOPICS, "output_frame": "base_link", - "lidar_timestamp_offsets": TIMESTAMP_OFFSET, - "lidar_timestamp_noise_window": [ + "matching_strategy.type": "advanced", + "matching_strategy.lidar_timestamp_offsets": TIMESTAMP_OFFSET, + "matching_strategy.lidar_timestamp_noise_window": [ TIMESTAMP_NOISE, TIMESTAMP_NOISE, TIMESTAMP_NOISE, diff --git a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp index a8bfe395b618e..0561644ea5815 100644 --- a/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp +++ b/sensing/autoware_pointcloud_preprocessor/test/test_concatenate_node_unit.cpp @@ -45,8 +45,7 @@ class ConcatenateCloudTest : public ::testing::Test // Instead of "input_topics", other parameters are not used. // They just helps to setup the concatenate node node_options.parameter_overrides( - {{"use_naive_approach", false}, - {"debug_mode", false}, + {{"debug_mode", false}, {"has_static_tf_only", false}, {"rosbag_length", 0.0}, {"maximum_queue_size", 5}, @@ -59,8 +58,9 @@ class ConcatenateCloudTest : public ::testing::Test {"input_twist_topic_type", "twist"}, {"input_topics", std::vector{"lidar_top", "lidar_left", "lidar_right"}}, {"output_frame", "base_link"}, - {"lidar_timestamp_offsets", std::vector{0.0, 0.04, 0.08}}, - {"lidar_timestamp_noise_window", std::vector{0.01, 0.01, 0.01}}}); + {"matching_strategy.type", "advanced"}, + {"matching_strategy.lidar_timestamp_offsets", std::vector{0.0, 0.04, 0.08}}, + {"matching_strategy.lidar_timestamp_noise_window", std::vector{0.01, 0.01, 0.01}}}); concatenate_node_ = std::make_shared< autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent>( @@ -448,13 +448,6 @@ TEST_F(ConcatenateCloudTest, TestConcatenateClouds) EXPECT_FLOAT_EQ(right_timestamp.seconds(), topic_to_original_stamp_map["lidar_right"]); } -TEST_F(ConcatenateCloudTest, TestDeleteCollector) -{ - concatenate_node_->add_cloud_collector(collector_); - concatenate_node_->delete_collector(*collector_); - EXPECT_TRUE(concatenate_node_->get_cloud_collectors().empty()); -} - TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) { concatenate_node_->add_cloud_collector(collector_); @@ -468,6 +461,8 @@ TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) auto topic_to_cloud_map = collector_->get_topic_to_cloud_map(); EXPECT_EQ(topic_to_cloud_map["lidar_top"], top_pointcloud_ptr); + EXPECT_FALSE(collector_->concatenate_finished()); + concatenate_node_->manage_collector_list(); EXPECT_FALSE(concatenate_node_->get_cloud_collectors().empty()); // Sleep for timeout seconds (200 ms) @@ -475,6 +470,8 @@ TEST_F(ConcatenateCloudTest, TestProcessSingleCloud) rclcpp::spin_some(concatenate_node_); // Collector should concatenate and publish the pointcloud, also delete itself. + EXPECT_TRUE(collector_->concatenate_finished()); + concatenate_node_->manage_collector_list(); EXPECT_TRUE(concatenate_node_->get_cloud_collectors().empty()); } @@ -503,6 +500,8 @@ TEST_F(ConcatenateCloudTest, TestProcessMultipleCloud) collector_->process_pointcloud("lidar_left", left_pointcloud_ptr); collector_->process_pointcloud("lidar_right", right_pointcloud_ptr); + EXPECT_TRUE(collector_->concatenate_finished()); + concatenate_node_->manage_collector_list(); EXPECT_TRUE(concatenate_node_->get_cloud_collectors().empty()); } From f94427790bc9534ff8784d51c8e5fa0279a8fd58 Mon Sep 17 00:00:00 2001 From: vividf Date: Thu, 12 Dec 2024 12:09:58 +0900 Subject: [PATCH 099/115] chore: set parameter to naive Signed-off-by: vividf --- .../config/concatenate_and_time_sync_node.param.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml index cec253e1ebb4b..56fe6643b9973 100644 --- a/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml +++ b/sensing/autoware_pointcloud_preprocessor/config/concatenate_and_time_sync_node.param.yaml @@ -18,6 +18,4 @@ ] output_frame: base_link matching_strategy: - type: advanced - lidar_timestamp_offsets: [0.0, 0.015, 0.016] - lidar_timestamp_noise_window: [0.01, 0.01, 0.01] + type: naive From b5cd104abda144923264fd72bf386b2fa57b3cd5 Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 13 Dec 2024 18:58:08 +0900 Subject: [PATCH 100/115] chore: fix parameter Signed-off-by: vividf --- .../concatenate_data/concatenate_and_time_sync_node.hpp | 1 + .../concatenate_data/concatenate_and_time_sync_node.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp index 1e5d95066c556..26bbc72908db0 100644 --- a/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp +++ b/sensing/autoware_pointcloud_preprocessor/include/autoware/pointcloud_preprocessor/concatenate_data/concatenate_and_time_sync_node.hpp @@ -77,6 +77,7 @@ class PointCloudConcatenateDataSynchronizerComponent : public rclcpp::Node std::string input_twist_topic_type; std::vector input_topics; std::string output_frame; + std::string matching_strategy; } params_; double current_concatenate_cloud_timestamp_{0.0}; diff --git a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp index b4d8d30dbbeca..01ec4f7bf1b9f 100644 --- a/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp +++ b/sensing/autoware_pointcloud_preprocessor/src/concatenate_data/concatenate_and_time_sync_node.cpp @@ -75,10 +75,10 @@ PointCloudConcatenateDataSynchronizerComponent::PointCloudConcatenateDataSynchro throw std::runtime_error("Need an 'output_frame' parameter to be set before continuing."); } - auto matching_strategy = declare_parameter("matching_strategy.type"); - if (matching_strategy == "naive") { + params_.matching_strategy = declare_parameter("matching_strategy.type"); + if (params_.matching_strategy == "naive") { collector_matching_strategy_ = std::make_unique(*this); - } else if (matching_strategy == "advanced") { + } else if (params_.matching_strategy == "advanced") { collector_matching_strategy_ = std::make_unique(*this, params_.input_topics); } else { @@ -371,7 +371,7 @@ void PointCloudConcatenateDataSynchronizerComponent::check_concat_status( stat.add( "concatenated cloud timestamp", format_timestamp(current_concatenate_cloud_timestamp_)); - if (params_.use_naive_approach) { + if (params_.matching_strategy == "naive") { stat.add("first cloud's arrival timestamp", format_timestamp(diagnostic_arrival_timestamp_)); } else { stat.add("reference timestamp min", format_timestamp(diagnostic_reference_timestamp_min_)); From ab65b2febcb4c646ad8e5f04bde93b666bd8b29f Mon Sep 17 00:00:00 2001 From: vividf Date: Fri, 20 Dec 2024 11:17:01 +0900 Subject: [PATCH 101/115] chore: fix readme Signed-off-by: vividf --- .../autoware_pointcloud_preprocessor/docs/concatenate-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index b6dd147353ebf..1153951603c30 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -2,7 +2,7 @@ ## Purpose -The `concatenate_and_time_synchronize_node` is a ROS2 node designed to combine and synchronize multiple point clouds into a single, unified point cloud. By integrating data from multiple LiDARs, this node significantly enhances the sensing range and coverage of autonomous vehicles, enabling more accurate perception of the surrounding environment. Synchronization ensures that point clouds are aligned temporally, reducing errors caused by mismatched timestamps. +The `concatenate_and_time_synchronize_node` is a node designed to combine and synchronize multiple point clouds into a single, unified point cloud. By integrating data from multiple LiDARs, this node significantly enhances the sensing range and coverage of autonomous vehicles, enabling more accurate perception of the surrounding environment. Synchronization ensures that point clouds are aligned temporally, reducing errors caused by mismatched timestamps. For example, consider a vehicle equipped with three LiDAR sensors mounted on the left, right, and top positions. Each LiDAR captures data from its respective field of view, as shown below: From a69838f91822673aa5af36b1560f39fcd6f50ca8 Mon Sep 17 00:00:00 2001 From: "Yi-Hsiang Fang (Vivid)" <146902905+vividf@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:58:36 +0900 Subject: [PATCH 102/115] Update sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md Co-authored-by: Max Schmeller <6088931+mojomex@users.noreply.github.com> --- .../docs/concatenate-data.md | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md index 1153951603c30..f1e60431035e6 100644 --- a/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md +++ b/sensing/autoware_pointcloud_preprocessor/docs/concatenate-data.md @@ -6,20 +6,10 @@ The `concatenate_and_time_synchronize_node` is a node designed to combine and sy For example, consider a vehicle equipped with three LiDAR sensors mounted on the left, right, and top positions. Each LiDAR captures data from its respective field of view, as shown below: -