diff --git a/localization/ndt_scan_matcher/CMakeLists.txt b/localization/ndt_scan_matcher/CMakeLists.txt
index 4806838cf0103..7ac78514c49fe 100644
--- a/localization/ndt_scan_matcher/CMakeLists.txt
+++ b/localization/ndt_scan_matcher/CMakeLists.txt
@@ -27,10 +27,11 @@ find_package(PCL REQUIRED COMPONENTS common io registration)
include_directories(${PCL_INCLUDE_DIRS})
ament_auto_add_executable(ndt_scan_matcher
- src/particle.cpp
- src/ndt_scan_matcher_node.cpp
- src/ndt_scan_matcher_core.cpp
+ src/diagnostics_module.cpp
src/map_update_module.cpp
+ src/ndt_scan_matcher_core.cpp
+ src/ndt_scan_matcher_node.cpp
+ src/particle.cpp
)
link_directories(${PCL_LIBRARY_DIRS})
@@ -45,15 +46,19 @@ if(BUILD_TESTING)
find_package(ament_cmake_gtest REQUIRED)
ament_auto_add_gtest(standard_sequence_for_initial_pose_estimation
test/test_cases/standard_sequence_for_initial_pose_estimation.cpp
- src/particle.cpp
- src/ndt_scan_matcher_core.cpp
+ src/diagnostics_module.cpp
src/map_update_module.cpp
+ src/ndt_scan_matcher_core.cpp
+# src/ndt_scan_matcher_node.cpp
+ src/particle.cpp
)
ament_auto_add_gtest(once_initialize_at_out_of_map_then_initialize_correctly
test/test_cases/once_initialize_at_out_of_map_then_initialize_correctly.cpp
- src/particle.cpp
- src/ndt_scan_matcher_core.cpp
+ src/diagnostics_module.cpp
src/map_update_module.cpp
+ src/ndt_scan_matcher_core.cpp
+# src/ndt_scan_matcher_node.cpp
+ src/particle.cpp
)
endif()
diff --git a/localization/ndt_scan_matcher/README.md b/localization/ndt_scan_matcher/README.md
index 424a7051de883..fbc2fa3c9a3f3 100644
--- a/localization/ndt_scan_matcher/README.md
+++ b/localization/ndt_scan_matcher/README.md
@@ -255,3 +255,103 @@ initial_pose_offset_model is rotated around (x,y) = (0,0) in the direction of th
initial_pose_offset_model_x & initial_pose_offset_model_y must have the same number of elements.
{{ json_to_markdown("localization/ndt_scan_matcher/schema/sub/covariance_covariance_estimation.json") }}
+
+## Diagnostics
+
+### scan_matching_status
+
+
+
+| Name | Description | Transition condition to Warning | Transition condition to Error | Whether to reject the estimation result (affects `skipping_publish_num`) |
+| ----------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- | ------------------------------------------------------------------------ |
+| `topic_time_stamp` | the time stamp of input topic | none | none | no |
+| `sensor_points_size` | the size of sensor points | the size is 0 | none | yes |
+| `sensor_points_delay_time_sec` | the delay time of sensor points | the time is **longer** than `validation.lidar_topic_timeout_sec` | none | yes |
+| `is_succeed_transform_sensor_points` | whether transform sensor points is succeed or not | none | failed | yes |
+| `sensor_points_max_distance` | the max distance of sensor points | the max distance is **shorter** than `sensor_points.required_distance` | none | yes |
+| `is_activated` | whether the node is in the "activate" state or not | not "activate" state | none | yes |
+| `is_succeed_interpolate_initial_pose` | whether the interpolate of initial pose is succeed or not | failed.
(1) the size of `initial_pose_buffer_` is **smaller** than 2.
(2) the timestamp difference between initial_pose and sensor pointcloud is **longer** than `validation.initial_pose_timeout_sec`.
(3) distance difference between two initial poses used for linear interpolation is **longer** than `validation.initial_pose_distance_tolerance_m` | none | yes |
+| `is_set_map_points` | whether the map points is set or not | not set | none | yes |
+| `iteration_num` | the number of times calculate alignment | the number of times is **larger** than `ndt.max_iterations` | none | yes |
+| `local_optimal_solution_oscillation_num` | the number of times the solution is judged to be oscillating | the number of times is **larger** than 10 | none | yes |
+| `transform_probability` | the score of how well the map aligns with the sensor points | the score is **smaller** than`score_estimation.converged_param_transform_probability` (only in the case of `score_estimation.converged_param_type` is 0=TRANSFORM_PROBABILITY) | none | yes |
+| `nearest_voxel_transformation_likelihood` | the score of how well the map aligns with the sensor points | the score is **smaller** than `score_estimation.converged_param_nearest_voxel_transformation_likelihood` (only in the case of `score_estimation.converged_param_type` is 1=NEAREST_VOXEL_TRANSFORMATION_LIKELIHOOD) | none | yes |
+| `distance_initial_to_result` | the distance between the position before convergence processing and the position after | the distance is **longer** than 3 | none | no |
+| `execution_time` | the time for convergence processing | the time is **longer** than `validation.critical_upper_bound_exe_time_ms` | none | no |
+| `skipping_publish_num` | the number of times rejected estimation results consecutively | none | the number of times is 5 or more | - |
+
+※The `sensor_points_callback` shares the same callback group as the `trigger_node_service` and `ndt_align_service`. Consequently, if the initial pose estimation takes too long, this diagnostic may become stale.
+
+### initial_pose_subscriber_status
+
+
+
+| Name | Description | Transition condition to Warning | Transition condition to Error |
+| ---------------------- | ------------------------------------------------------------------ | ------------------------------- | ----------------------------- |
+| `topic_time_stamp` | the time stamp of input topic | none | none |
+| `is_activated` | whether the node is in the "activate" state or not | not "activate" state | none |
+| `is_expected_frame_id` | whether the input frame_id is the same as `frame.map_frame` or not | none | not the same |
+
+### regularization_pose_subscriber_status
+
+
+
+| Name | Description | Transition condition to Warning | Transition condition to Error |
+| ------------------ | ----------------------------- | ------------------------------- | ----------------------------- |
+| `topic_time_stamp` | the time stamp of input topic | none | none |
+
+### trigger_node_service_status
+
+
+
+| Name | Description | Transition condition to Warning | Transition condition to Error |
+| ------------------------- | -------------------------------------------------- | ------------------------------- | ----------------------------- |
+| `service_call_time_stamp` | the time stamp of service calling | none | none |
+| `is_activated` | whether the node is in the "activate" state or not | none | none |
+| `is_succeed_service` | whether the process of service is succeed or not | none | none |
+
+※
+This diagnostic is only published when the service is called, so it becomes stale after the initial pose estimation is completed.
+
+### ndt_align_service_status
+
+
+
+| Name | Description | Transition condition to Warning | Transition condition to Error |
+| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------------------------- |
+| `service_call_time_stamp` | the time stamp of service calling | none | none |
+| `is_succeed_transform_initial_pose` | whether transform initial pose is succeed or not | none | failed |
+| `is_need_rebuild` | whether it need to rebuild the map. If the map has not been loaded yet or if `distance_last_update_position_to_current_position encounters` is an Error state, it is considered necessary to reconstruct the map, and `is_need_rebuild` becomes `True`. | none | none |
+| `maps_size_before` | the number of maps before update map | none | none |
+| `is_succeed_call_pcd_loader` | whether call pcd_loader service is succeed or not | failed | none |
+| `maps_to_add_size` | the number of maps to be added | none | none |
+| `maps_to_remove_size` | the number of maps to be removed | none | none |
+| `map_update_execution_time` | the time for map updating | none | none |
+| `maps_size_after` | the number of maps after update map | none | none |
+| `is_updated_map` | whether map is updated. If the map update couldn't be performed or there was no need to update the map, it becomes `False` | none | `is_updated_map` is `False` but `is_need_rebuild` is `True` |
+| `is_set_map_points` | whether the map points is set or not | not set | none |
+| `is_set_sensor_points` | whether the sensor points is set or not | not set | none |
+| `best_particle_score` | the best score of particle | none | none |
+| `is_succeed_service` | whether the process of service is succeed or not | failed | none |
+
+※
+This diagnostic is only published when the service is called, so it becomes stale after the initial pose estimation is completed.
+
+### map_update_status
+
+
+
+| Name | Description | Transition condition to Warning | Transition condition to Error |
+| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------- |
+| `timer_callback_time_stamp` | the time stamp of timer_callback calling | none | none |
+| `is_activated` | whether the node is in the "activate" state or not | not "activate" state | none |
+| `is_set_last_update_position` | whether the `last_update_position` is set or not | not set | none |
+| `distance_last_update_position_to_current_position` | the distance of `last_update_position` to current position | none | (the distance + `dynamic_map_loading.lidar_radius`) is **larger** than `dynamic_map_loading.map_radius` |
+| `is_need_rebuild` | whether it need to rebuild the map. If the map has not been loaded yet or if `distance_last_update_position_to_current_position encounters` is an Error state, it is considered necessary to reconstruct the map, and `is_need_rebuild` becomes `True`. | none | none |
+| `maps_size_before` | the number of maps before update map | none | none |
+| `is_succeed_call_pcd_loader` | whether call pcd_loader service is succeed or not | failed | none |
+| `maps_to_add_size` | the number of maps to be added | none | none |
+| `maps_to_remove_size` | the number of maps to be removed | none | none |
+| `map_update_execution_time` | the time for map updating | none | none |
+| `maps_size_after` | the number of maps after update map | none | none |
+| `is_updated_map` | whether map is updated. If the map update couldn't be performed or there was no need to update the map, it becomes `False` | none | `is_updated_map` is `False` but `is_need_rebuild` is `True` |
diff --git a/localization/ndt_scan_matcher/include/ndt_scan_matcher/diagnostics_module.hpp b/localization/ndt_scan_matcher/include/ndt_scan_matcher/diagnostics_module.hpp
new file mode 100644
index 0000000000000..828eb6bed346b
--- /dev/null
+++ b/localization/ndt_scan_matcher/include/ndt_scan_matcher/diagnostics_module.hpp
@@ -0,0 +1,59 @@
+// Copyright 2023 Autoware Foundation
+//
+// 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.
+
+#ifndef NDT_SCAN_MATCHER__DIAGNOSTICS_MODULE_HPP_
+#define NDT_SCAN_MATCHER__DIAGNOSTICS_MODULE_HPP_
+
+#include
+
+#include
+
+#include
+#include
+
+class DiagnosticsModule
+{
+public:
+ DiagnosticsModule(rclcpp::Node * node, const std::string & diagnostic_name);
+ void clear();
+ void addKeyValue(const diagnostic_msgs::msg::KeyValue & key_value_msg);
+ template
+ void addKeyValue(const std::string & key, const T & value);
+ void updateLevelAndMessage(const int8_t level, const std::string & message);
+ void publish();
+
+private:
+ diagnostic_msgs::msg::DiagnosticArray createDiagnosticsArray() const;
+
+ rclcpp::Clock::SharedPtr clock_;
+ rclcpp::Publisher::SharedPtr diagnostics_pub_;
+
+ diagnostic_msgs::msg::DiagnosticStatus diagnostics_status_msg_;
+};
+
+template
+void DiagnosticsModule::addKeyValue(const std::string & key, const T & value)
+{
+ diagnostic_msgs::msg::KeyValue key_value;
+ key_value.key = key;
+ key_value.value = std::to_string(value);
+ addKeyValue(key_value);
+}
+
+template <>
+void DiagnosticsModule::addKeyValue(const std::string & key, const std::string & value);
+template <>
+void DiagnosticsModule::addKeyValue(const std::string & key, const bool & value);
+
+#endif // NDT_SCAN_MATCHER__DIAGNOSTICS_MODULE_HPP_
diff --git a/localization/ndt_scan_matcher/include/ndt_scan_matcher/hyper_parameters.hpp b/localization/ndt_scan_matcher/include/ndt_scan_matcher/hyper_parameters.hpp
index ee1e2369c79bd..bc7bf1fe40d36 100644
--- a/localization/ndt_scan_matcher/include/ndt_scan_matcher/hyper_parameters.hpp
+++ b/localization/ndt_scan_matcher/include/ndt_scan_matcher/hyper_parameters.hpp
@@ -20,6 +20,7 @@
#include
#include
+#include
#include
#include
@@ -162,10 +163,11 @@ struct HyperParameters
initial_pose_offset_model_y[i];
}
} else {
- RCLCPP_WARN(
- node->get_logger(),
- "Invalid initial pose offset model parameters. Disable covariance estimation.");
- covariance.covariance_estimation.enable = false;
+ std::stringstream message;
+ message << "Invalid initial pose offset model parameters."
+ << "Please make sure that the number of elements in "
+ << "initial_pose_offset_model_x and initial_pose_offset_model_y are the same.";
+ throw std::runtime_error(message.str());
}
}
diff --git a/localization/ndt_scan_matcher/include/ndt_scan_matcher/map_update_module.hpp b/localization/ndt_scan_matcher/include/ndt_scan_matcher/map_update_module.hpp
index 04c762ae950a4..2bcb6f4cf6fb8 100644
--- a/localization/ndt_scan_matcher/include/ndt_scan_matcher/map_update_module.hpp
+++ b/localization/ndt_scan_matcher/include/ndt_scan_matcher/map_update_module.hpp
@@ -16,6 +16,7 @@
#define NDT_SCAN_MATCHER__MAP_UPDATE_MODULE_HPP_
#include "localization_util/util_func.hpp"
+#include "ndt_scan_matcher/diagnostics_module.hpp"
#include "ndt_scan_matcher/hyper_parameters.hpp"
#include "ndt_scan_matcher/particle.hpp"
@@ -54,10 +55,20 @@ class MapUpdateModule
private:
friend class NDTScanMatcher;
+ void callback_timer(
+ const bool is_activated, const std::optional & position,
+ std::unique_ptr & diagnostics_ptr);
+
+ [[nodiscard]] bool should_update_map(
+ const geometry_msgs::msg::Point & position,
+ std::unique_ptr & diagnostics_ptr);
+ void update_map(
+ const geometry_msgs::msg::Point & position,
+ std::unique_ptr & diagnostics_ptr);
// Update the specified NDT
- bool update_ndt(const geometry_msgs::msg::Point & position, NdtType & ndt);
- void update_map(const geometry_msgs::msg::Point & position);
- [[nodiscard]] bool should_update_map(const geometry_msgs::msg::Point & position);
+ bool update_ndt(
+ const geometry_msgs::msg::Point & position, NdtType & ndt,
+ std::unique_ptr & diagnostics_ptr);
void publish_partial_pcd_map();
rclcpp::Publisher::SharedPtr loaded_pcd_pub_;
diff --git a/localization/ndt_scan_matcher/include/ndt_scan_matcher/ndt_scan_matcher_core.hpp b/localization/ndt_scan_matcher/include/ndt_scan_matcher/ndt_scan_matcher_core.hpp
index 26c96ce7e3248..4f8042d106c75 100644
--- a/localization/ndt_scan_matcher/include/ndt_scan_matcher/ndt_scan_matcher_core.hpp
+++ b/localization/ndt_scan_matcher/include/ndt_scan_matcher/ndt_scan_matcher_core.hpp
@@ -18,6 +18,7 @@
#define FMT_HEADER_ONLY
#include "localization_util/smart_pose_buffer.hpp"
+#include "ndt_scan_matcher/diagnostics_module.hpp"
#include "ndt_scan_matcher/hyper_parameters.hpp"
#include "ndt_scan_matcher/map_update_module.hpp"
@@ -62,6 +63,7 @@
#include