20 #ifndef NDT_MAPPING_NODES__NDT_MAPPING_NODES_HPP_
21 #define NDT_MAPPING_NODES__NDT_MAPPING_NODES_HPP_
24 #include <sensor_msgs/msg/point_cloud2.hpp>
31 #include <tf2_msgs/msg/tf_message.hpp>
43 namespace ndt_mapping_nodes
73 WriteTriggerPolicyT>::value,
74 "Write trigger policy must implement the `TriggerPolicyBase` interface.");
78 ClearTriggerPolicyT>::value,
79 "Clear trigger policy must implement the `TriggerPolicyBase` interface.");
83 PrefixGeneratorT>::value,
84 "Prefix generator policy must implement the `PrefixGeneratorBase` interface.");
88 : Node{
"ndt_mapper_node", options},
89 m_observation_sub{create_subscription<Cloud>(
91 rclcpp::QoS{rclcpp::KeepLast{
92 static_cast<size_t>(declare_parameter(
"observation_sub.history_depth").template
94 [
this](
typename Cloud::ConstSharedPtr msg) {observation_callback(msg);})},
96 create_publisher<PoseWithCovarianceStamped>(
98 rclcpp::QoS{rclcpp::KeepLast{
99 static_cast<size_t>(declare_parameter(
100 "pose_pub.history_depth").template get<size_t>())}})),
101 m_base_fn_prefix{this->declare_parameter(
"file_name_prefix").template get<std::string>()}
108 if (m_map_ptr->size() > 0U) {
109 const auto & file_name_prefix = m_prefix_generator.get(m_base_fn_prefix);
110 RCLCPP_DEBUG(get_logger(),
"The map is written to" + file_name_prefix +
".pcd");
111 m_map_ptr->write(file_name_prefix);
118 auto get_point_param = [
this](
const std::string & config_name_prefix) {
120 point.x =
static_cast<float32_t>(declare_parameter(config_name_prefix +
".x").
121 template get<float32_t>());
122 point.y =
static_cast<float32_t>(this->declare_parameter(config_name_prefix +
".y").
123 template get<float32_t>());
124 point.z =
static_cast<float32_t>(this->declare_parameter(config_name_prefix +
".z").
125 template get<float32_t>());
130 const auto parse_grid_config = [
this, get_point_param](
const std::string & prefix) {
132 const auto capacity =
static_cast<std::size_t
>(
133 this->declare_parameter(prefix +
".capacity").template get<std::size_t>());
136 prefix +
".min_point"), get_point_param(prefix +
".max_point"),
137 get_point_param(prefix +
".voxel_size"), capacity};
142 static_cast<uint32_t
>(this->declare_parameter(
"localizer.scan.capacity").
143 template get<uint32_t>()),
144 std::chrono::milliseconds(
145 static_cast<uint64_t
>(
146 this->declare_parameter(
"localizer.guess_time_tolerance_ms").
template get<uint64_t>()))
149 const auto outlier_ratio{this->declare_parameter(
150 "localizer.optimization.outlier_ratio").template get<float64_t>()};
152 const common::optimization::OptimizationOptions optimization_options{
153 static_cast<uint64_t
>(
154 this->declare_parameter(
"localizer.optimizer.max_iterations").template get<uint64_t>()),
155 this->declare_parameter(
"localizer.optimizer.score_tolerance").template get<float64_t>(),
156 this->declare_parameter(
157 "localizer.optimizer.parameter_tolerance").template get<float64_t>(),
158 this->declare_parameter(
"localizer.optimizer.gradient_tolerance").template get<float64_t>()
161 m_localizer_ptr = std::make_unique<Localizer>(
164 common::optimization::MoreThuenteLineSearch{
165 static_cast<float32_t>(this->declare_parameter(
166 "localizer.optimizer.line_search.step_max")
167 .template get<float32_t>()),
168 static_cast<float32_t>(this->declare_parameter(
169 "localizer.optimizer.line_search.step_min")
170 .template get<float32_t>()),
173 optimization_options},
175 const auto & map_frame_id = this->declare_parameter(
"map.frame_id").template get<std::string>();
176 m_map_ptr = std::make_unique<VoxelMap>(
177 parse_grid_config(
"map"), map_frame_id,
178 NDTMap{parse_grid_config(
"localizer.map")});
180 if (this->declare_parameter(
"publish_map_increment").template get<bool8_t>()) {
181 m_increment_publisher = this->
template create_publisher<sensor_msgs::msg::PointCloud2>(
183 rclcpp::QoS{rclcpp::KeepLast{
184 static_cast<size_t>(this->declare_parameter(
"map_increment_pub.history_depth").template
188 if (declare_parameter(
"publish_tf").template get<bool>()) {
189 m_tf_publisher = create_publisher<tf2_msgs::msg::TFMessage>(
191 rclcpp::QoS{rclcpp::KeepLast{m_pose_publisher->get_queue_size()}});
194 m_previous_transform.transform.rotation.set__w(1.0);
195 m_previous_transform.header.frame_id = m_map_ptr->frame_id();
201 void observation_callback(Cloud::ConstSharedPtr msg_ptr)
204 RegistrationSummary summary{};
205 m_previous_transform.header.stamp = msg_ptr->header.stamp;
207 geometry_msgs::msg::PoseWithCovarianceStamped pose_out;
208 if (m_map_ptr->empty()) {
212 pose_out = get_initial_pose_once();
215 pose_out = m_localizer_ptr->register_measurement(
216 *msg_ptr, m_previous_transform, m_map_ptr->localizer_map(), &summary);
219 if (!validate_output(summary)) {
220 RCLCPP_WARN(get_logger(),
"Invalid pose estimate. The result is ignored.");
224 const auto & increment = get_map_increment(*msg_ptr, pose_out);
225 m_pose_publisher->publish(pose_out);
227 if (m_increment_publisher) {
228 m_increment_publisher->publish(increment);
230 if (m_tf_publisher) {
233 if (m_write_trigger.ready(*m_map_ptr)) {
234 const auto & file_name_prefix = m_prefix_generator.get(m_base_fn_prefix);
235 m_map_ptr->write(file_name_prefix);
236 RCLCPP_DEBUG(get_logger(),
"The map is written to" + file_name_prefix +
".pcd");
238 if (m_clear_trigger.ready(*m_map_ptr)) {
239 RCLCPP_DEBUG(get_logger(),
"The map is cleared.");
245 m_map_ptr->update(increment);
247 }
catch (
const std::runtime_error & e) {
248 RCLCPP_ERROR(get_logger(),
"Failed to register the measurement: ", e.what());
253 const RegistrationSummary & summary)
255 switch (summary.optimization_summary().termination_type()) {
263 RCLCPP_DEBUG(this->get_logger(),
"NDT mapper optimizer failed to converge.");
275 const Cloud & get_map_increment(
276 const Cloud & observation,
277 const PoseWithCovarianceStamped & registered_pose)
281 reset_cached_msg(obs_view.size());
284 tf.header.stamp = registered_pose.header.stamp;
285 tf.header.frame_id = registered_pose.header.frame_id;
286 tf.child_frame_id = observation.header.frame_id;
287 const auto & trans = registered_pose.pose.pose.position;
288 const auto & rot = registered_pose.pose.pose.orientation;
289 tf.transform.translation.set__x(trans.x).set__y(trans.y).set__z(trans.z);
290 tf.transform.rotation.set__x(rot.x).set__y(rot.y).set__z(rot.z).set__w(rot.w);
293 return m_cached_increment;
300 tf_message.transforms.emplace_back(transform);
301 m_tf_publisher->publish(tf_message);
305 void reset_cached_msg(std::size_t size)
309 inc_modifier.clear();
310 inc_modifier.resize(size);
315 const PoseWithCovarianceStamped & pose_msg,
316 const std::string & child_frame_id =
"base_link")
319 tf_stamped.header = pose_msg.header;
320 tf_stamped.child_frame_id = child_frame_id;
321 const auto & tf_trans = pose_msg.pose.pose.position;
322 const auto & tf_rot = pose_msg.pose.pose.orientation;
323 tf_stamped.transform.translation.set__x(tf_trans.x).set__y(tf_trans.y).
325 tf_stamped.transform.rotation.set__x(tf_rot.x).set__y(tf_rot.y).set__z(tf_rot.z).
334 PoseWithCovarianceStamped pose;
335 pose.header = transform_msg.header;
336 const auto & pose_trans = transform_msg.transform.translation;
337 const auto & pose_rot = transform_msg.transform.rotation;
338 pose.pose.pose.position.set__x(pose_trans.x).set__y(pose_trans.y).
339 set__z(pose_trans.z);
340 pose.pose.pose.orientation.set__x(pose_rot.x).set__y(pose_rot.y).set__z(pose_rot.z).
346 geometry_msgs::msg::PoseWithCovarianceStamped get_initial_pose_once()
348 if (!m_map_initialized) {
349 m_map_initialized =
true;
352 throw std::runtime_error(
353 "Current map is initialized yet empty, the scan cannot be "
358 std::unique_ptr<Localizer> m_localizer_ptr;
359 std::unique_ptr<VoxelMap> m_map_ptr;
360 typename rclcpp::Subscription<Cloud>::SharedPtr m_observation_sub;
361 typename rclcpp::Publisher<PoseWithCovarianceStamped>::SharedPtr m_pose_publisher;
362 typename rclcpp::Publisher<tf2_msgs::msg::TFMessage>::SharedPtr m_tf_publisher{
nullptr};
363 rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr m_increment_publisher{
nullptr};
365 Cloud m_cached_increment;
366 WriteTriggerPolicyT m_write_trigger{};
367 ClearTriggerPolicyT m_clear_trigger{};
368 PrefixGeneratorT m_prefix_generator{};
369 bool8_t m_map_initialized{
false};
370 std::string m_base_fn_prefix;
377 #endif // NDT_MAPPING_NODES__NDT_MAPPING_NODES_HPP_