17 #ifndef LOCALIZATION_NODES__LOCALIZATION_NODE_HPP_ 18 #define LOCALIZATION_NODES__LOCALIZATION_NODE_HPP_ 22 #include <rclcpp/rclcpp.hpp> 23 #include <tf2/buffer_core.h> 24 #include <tf2_ros/transform_listener.h> 25 #include <tf2_geometry_msgs/tf2_geometry_msgs.h> 26 #include <sensor_msgs/msg/point_cloud2.hpp> 38 namespace localization
40 namespace localization_nodes
65 template<
typename ObservationMsgT,
69 typename PoseInitializerT,
90 const std::string & node_name,
const std::string & name_space,
91 const TopicQoS & observation_sub_config,
94 const TopicQoS & initial_pose_sub_config,
95 const PoseInitializerT & pose_initializer,
97 : Node(node_name, name_space),
98 m_pose_initializer(pose_initializer),
99 m_tf_listener(m_tf_buffer, std::shared_ptr<rclcpp::Node>(this, [](auto) {}),
false),
100 m_observation_sub(create_subscription<ObservationMsgT>(
101 observation_sub_config.topic,
102 observation_sub_config.qos,
103 [
this](
typename ObservationMsgT::ConstSharedPtr msg) {observation_callback(msg);})),
105 create_subscription<MapMsgT>(
106 map_sub_config.topic, map_sub_config.qos,
107 [
this](
typename MapMsgT::ConstSharedPtr msg) {map_callback(msg);})),
109 create_publisher<PoseWithCovarianceStamped>(
110 pose_pub_config.topic,
111 pose_pub_config.qos)),
113 create_subscription<PoseWithCovarianceStamped>(
114 initial_pose_sub_config.topic, initial_pose_sub_config.qos,
115 [
this](
const typename PoseWithCovarianceStamped::ConstSharedPtr msg) {
116 initial_pose_callback(msg);
119 m_tf_publisher = create_publisher<tf2_msgs::msg::TFMessage>(
"/tf", pose_pub_config.qos);
126 const std::string & node_name,
127 const rclcpp::NodeOptions & options,
128 const PoseInitializerT & pose_initializer)
129 : Node(node_name, options),
130 m_pose_initializer(pose_initializer),
131 m_tf_listener(m_tf_buffer, std::shared_ptr<rclcpp::Node>(this, [](auto) {}),
false),
132 m_observation_sub(create_subscription<ObservationMsgT>(
134 rclcpp::QoS{rclcpp::KeepLast{
135 static_cast<size_t>(declare_parameter(
"observation_sub.history_depth").template
137 [
this](
typename ObservationMsgT::ConstSharedPtr msg) {observation_callback(msg);})),
139 create_subscription<MapMsgT>(
141 rclcpp::QoS{rclcpp::KeepLast{
142 static_cast<size_t>(declare_parameter(
"map_sub.history_depth").
143 template get<size_t>())}}.transient_local(),
144 [
this](
typename MapMsgT::ConstSharedPtr msg) {map_callback(msg);})),
146 create_publisher<PoseWithCovarianceStamped>(
148 rclcpp::QoS{rclcpp::KeepLast{
149 static_cast<size_t>(declare_parameter(
150 "pose_pub.history_depth").template get<size_t>())}})),
152 create_subscription<PoseWithCovarianceStamped>(
154 rclcpp::QoS{rclcpp::KeepLast{10}},
155 [
this](
const typename PoseWithCovarianceStamped::ConstSharedPtr msg) {
156 initial_pose_callback(msg);
167 const std::string & node_name,
const std::string & name_space,
168 const PoseInitializerT & pose_initializer)
169 : Node(node_name, name_space),
170 m_pose_initializer(pose_initializer),
171 m_tf_listener(m_tf_buffer, std::shared_ptr<rclcpp::Node>(this, [](auto) {}),
false),
172 m_observation_sub(create_subscription<ObservationMsgT>(
174 rclcpp::QoS{rclcpp::KeepLast{
175 static_cast<size_t>(declare_parameter(
"observation_sub.history_depth").template
177 [
this](
typename ObservationMsgT::ConstSharedPtr msg) {observation_callback(msg);})),
179 create_subscription<MapMsgT>(
181 rclcpp::QoS{rclcpp::KeepLast{
182 static_cast<size_t>(declare_parameter(
"map_sub.history_depth").
183 template get<size_t>())}}.transient_local(),
184 [
this](
typename MapMsgT::ConstSharedPtr msg) {map_callback(msg);})),
186 create_publisher<PoseWithCovarianceStamped>(
188 rclcpp::QoS{rclcpp::KeepLast{
189 static_cast<size_t>(declare_parameter(
190 "pose_pub.history_depth").template get<size_t>())}})),
192 create_subscription<PoseWithCovarianceStamped>(
194 rclcpp::QoS{rclcpp::KeepLast{10}},
195 [
this](
const typename PoseWithCovarianceStamped::ConstSharedPtr msg) {
196 initial_pose_callback(msg);
203 const typename rclcpp::Publisher<PoseWithCovarianceStamped>::ConstSharedPtr
get_publisher()
205 return m_pose_publisher;
213 m_localizer_ptr = std::forward<std::unique_ptr<LocalizerT>>(localizer_ptr);
216 void set_map(std::unique_ptr<MapT> && map_ptr)
218 m_map_ptr = std::forward<std::unique_ptr<MapT>>(map_ptr);
224 on_exception(eptr,
"on_bad_registration");
230 on_exception(eptr,
"on_bad_map");
233 void on_exception(std::exception_ptr eptr,
const std::string & error_source)
237 std::rethrow_exception(eptr);
239 RCLCPP_ERROR(get_logger(), error_source +
": error nullptr");
241 }
catch (
const std::exception & e) {
242 RCLCPP_ERROR(get_logger(), e.what());
250 get_logger(),
"Received observation without a valid map, " 251 "ignoring the observation.");
260 get_logger(),
"Relative localizer has an invalid pose estimate. " 261 "The result is ignored.");
283 template<
typename PtrT>
284 void assert_ptr_not_null(
const PtrT & ptr,
const std::string & name)
const 287 throw std::runtime_error(
288 name +
" pointer is null. Make sure it is properly initialized before using.");
294 if (declare_parameter(
"publish_tf").
template get<bool>()) {
295 m_tf_publisher = create_publisher<tf2_msgs::msg::TFMessage>(
297 rclcpp::QoS{rclcpp::KeepLast{m_pose_publisher->get_queue_size()}});
300 if (declare_parameter(
"init_hack.enabled",
false)) {
305 auto & tf = init_hack_transform.transform;
306 tf.rotation.x = declare_parameter(
"init_hack.quaternion.x").template get<float64_t>();
307 tf.rotation.y = declare_parameter(
"init_hack.quaternion.y").template get<float64_t>();
308 tf.rotation.z = declare_parameter(
"init_hack.quaternion.z").template get<float64_t>();
309 tf.rotation.w = declare_parameter(
"init_hack.quaternion.w").template get<float64_t>();
310 tf.translation.x = declare_parameter(
"init_hack.translation.x").template get<float64_t>();
311 tf.translation.y = declare_parameter(
"init_hack.translation.y").template get<float64_t>();
312 tf.translation.z = declare_parameter(
"init_hack.translation.z").template get<float64_t>();
313 init_hack_transform.header.frame_id =
"map";
314 init_hack_transform.child_frame_id =
"base_link";
315 m_external_pose = init_hack_transform;
316 m_external_pose_available =
true;
328 void observation_callback(
typename ObservationMsgT::ConstSharedPtr msg_ptr)
331 assert_ptr_not_null(m_localizer_ptr,
"localizer");
332 assert_ptr_not_null(m_map_ptr,
"map");
334 if (!m_map_ptr->valid()) {
335 on_observation_with_invalid_map(msg_ptr);
340 const auto & observation_frame =
get_frame_id(*msg_ptr);
341 const auto & map_frame = m_map_ptr->frame_id();
345 if (m_external_pose_available) {
347 if (m_external_pose.header.frame_id != map_frame ||
348 m_external_pose.child_frame_id != observation_frame)
350 throw std::runtime_error(
351 "The pose initializer's set_external_pose() " 352 "and guess() methods were called with different frames.");
354 m_external_pose_available =
false;
355 initial_guess = m_external_pose;
356 initial_guess.header.stamp =
get_stamp(*msg_ptr);
359 m_pose_initializer.guess(m_tf_buffer, observation_time, map_frame, observation_frame);
363 const auto pose_out =
364 m_localizer_ptr->register_measurement(*msg_ptr, initial_guess, *m_map_ptr, &summary);
365 if (validate_output(summary, pose_out, initial_guess)) {
366 m_pose_publisher->publish(pose_out);
369 if (m_tf_publisher) {
370 publish_tf(pose_out);
377 m_obs_republisher->publish(msg);
380 handle_registration_summary(summary);
382 on_invalid_output(pose_out);
386 if (m_tf_publisher && m_use_hack) {
389 on_bad_registration(std::current_exception());
395 void map_callback(
typename MapMsgT::ConstSharedPtr msg_ptr)
397 assert_ptr_not_null(m_map_ptr,
"map");
399 m_map_ptr->set(*msg_ptr);
401 on_bad_map(std::current_exception());
408 const auto & pose = pose_msg.pose.pose;
409 const auto & map_frame_id = m_map_ptr->frame_id();
410 tf2::Quaternion rotation{pose.orientation.x, pose.orientation.y, pose.orientation.z,
412 tf2::Vector3 translation{pose.position.x, pose.position.y, pose.position.z};
413 const tf2::Transform map_base_link_transform{rotation, translation};
416 bool odom_to_bl_found = m_tf_buffer.canTransform(
"odom",
"base_link", tf2::TimePointZero);
418 while (!odom_to_bl_found) {
419 RCLCPP_INFO(get_logger(),
"Waiting for odom to base_link transform to be available.");
420 std::this_thread::sleep_for(std::chrono::seconds(1));
421 odom_to_bl_found = m_tf_buffer.canTransform(
"odom",
"base_link", tf2::TimePointZero);
426 odom_tf = m_tf_buffer.lookupTransform(
429 }
catch (
const tf2::ExtrapolationException &) {
430 odom_tf = m_tf_buffer.lookupTransform(
"odom",
"base_link", tf2::TimePointZero);
432 tf2::Quaternion odom_rotation{odom_tf.transform.rotation.x,
433 odom_tf.transform.rotation.y, odom_tf.transform.rotation.z, odom_tf.transform.rotation.w};
434 tf2::Vector3 odom_translation{odom_tf.transform.translation.x, odom_tf.transform.translation.y,
435 odom_tf.transform.translation.z};
436 const tf2::Transform odom_base_link_transform{odom_rotation, odom_translation};
438 const auto map_odom_tf = map_base_link_transform * odom_base_link_transform.inverse();
442 tf_stamped.header.stamp = pose_msg.header.stamp;
443 tf_stamped.header.frame_id = map_frame_id;
444 tf_stamped.child_frame_id =
"odom";
445 const auto & tf_trans = map_odom_tf.getOrigin();
446 const auto & tf_rot = map_odom_tf.getRotation();
447 tf_stamped.transform.translation.set__x(tf_trans.x()).set__y(tf_trans.y()).
448 set__z(tf_trans.z());
449 tf_stamped.transform.rotation.set__x(tf_rot.x()).set__y(tf_rot.y()).set__z(tf_rot.z()).
451 tf_message.transforms.push_back(tf_stamped);
452 m_tf_publisher->publish(tf_message);
457 void republish_tf(builtin_interfaces::msg::Time stamp)
460 auto map_odom_tf = m_tf_buffer.lookupTransform(
461 m_map_ptr->frame_id(),
"odom",
463 map_odom_tf.header.stamp = stamp;
465 tf_message.transforms.push_back(map_odom_tf);
466 m_tf_publisher->publish(tf_message);
469 void initial_pose_callback(
const typename PoseWithCovarianceStamped::ConstSharedPtr msg_ptr)
473 assert_ptr_not_null(m_map_ptr,
"map");
474 const std::string & map_frame = m_map_ptr->frame_id();
475 if (!m_tf_buffer.canTransform(map_frame, msg_ptr->header.frame_id, tf2::TimePointZero)) {
478 "Failed to find transform from %s to %s frame. Failed to give initial pose.",
479 msg_ptr->header.frame_id, map_frame);
482 const auto transform = m_tf_buffer.lookupTransform(
483 map_frame, msg_ptr->header.frame_id,
488 input_pose_stamped.header = msg_ptr->header;
489 input_pose_stamped.child_frame_id =
"base_link";
490 input_pose_stamped.transform.rotation = msg_ptr->pose.pose.orientation;
491 input_pose_stamped.transform.translation.x = msg_ptr->pose.pose.position.x;
492 input_pose_stamped.transform.translation.y = msg_ptr->pose.pose.position.y;
493 input_pose_stamped.transform.translation.z = msg_ptr->pose.pose.position.z;
498 transformed_pose_stamped.child_frame_id =
"base_link";
508 m_external_pose = transformed_pose_stamped;
509 m_external_pose_available =
true;
512 std::unique_ptr<LocalizerT> m_localizer_ptr;
513 std::unique_ptr<MapT> m_map_ptr;
514 PoseInitializerT m_pose_initializer;
515 tf2::BufferCore m_tf_buffer;
516 tf2_ros::TransformListener m_tf_listener;
517 typename rclcpp::Subscription<ObservationMsgT>::SharedPtr m_observation_sub;
518 typename rclcpp::Subscription<MapMsgT>::SharedPtr m_map_sub;
519 typename rclcpp::Publisher<PoseWithCovarianceStamped>::SharedPtr m_pose_publisher;
520 typename rclcpp::Publisher<tf2_msgs::msg::TFMessage>::SharedPtr m_tf_publisher{
nullptr};
521 typename rclcpp::Publisher<ObservationMsgT>::SharedPtr m_obs_republisher{
522 create_publisher<ObservationMsgT>(
"observation_republish", 10)};
525 typename rclcpp::Subscription<PoseWithCovarianceStamped>::SharedPtr m_initial_pose_sub;
528 bool m_external_pose_available{
false};
529 bool m_use_hack{
false};
534 #endif // LOCALIZATION_NODES__LOCALIZATION_NODE_HPP_ const rclcpp::Publisher< PoseWithCovarianceStamped >::ConstSharedPtr get_publisher()
Get a const pointer of the output publisher. Can be used for matching against subscriptions.
Definition: localization_node.hpp:203
RelativeLocalizerNode(const std::string &node_name, const std::string &name_space, const PoseInitializerT &pose_initializer)
Definition: localization_node.hpp:166
geometry_msgs::msg::TransformStamped TransformStamped
Definition: motion_testing_publisher.hpp:37
RelativeLocalizerNode(const std::string &node_name, const std::string &name_space, const TopicQoS &observation_sub_config, const TopicQoS &map_sub_config, const TopicQoS &pose_pub_config, const TopicQoS &initial_pose_sub_config, const PoseInitializerT &pose_initializer, LocalizerPublishMode publish_tf=LocalizerPublishMode::NO_PUBLISH_TF)
Definition: localization_node.hpp:89
TIME_UTILS_PUBLIC builtin_interfaces::msg::Time to_message(std::chrono::system_clock::time_point t)
Convert from std::chrono::time_point to time message.
Definition: time_utils.cpp:67
void on_exception(std::exception_ptr eptr, const std::string &error_source)
Definition: localization_node.hpp:233
virtual void on_invalid_output(const PoseWithCovarianceStamped &pose)
Definition: localization_node.hpp:256
const TimeStamp & get_stamp(const T &msg) noexcept
Definition: message_adapters.hpp:101
std::string topic
Definition: localization_node.hpp:48
RelativeLocalizerNode(const std::string &node_name, const rclcpp::NodeOptions &options, const PoseInitializerT &pose_initializer)
Definition: localization_node.hpp:125
const std::string & get_frame_id(const T &msg) noexcept
Definition: message_adapters.hpp:83
Definition: optimized_registration_summary.hpp:32
virtual void on_bad_map(std::exception_ptr eptr)
Handle the exceptions during map setting.
Definition: localization_node.hpp:228
Definition: localization_node.hpp:72
tf2_msgs::msg::TFMessage TFMessage
Definition: motion_testing_publisher.hpp:34
void doTransform(const geometry_msgs::msg::Point32 &t_in, geometry_msgs::msg::Point32 &t_out, const geometry_msgs::msg::TransformStamped &transform)
Apply a geometry_msgs TransformStamped to an geometry_msgs Point32 type. This function is a specializ...
Definition: tf2_autoware_auto_msgs.hpp:54
TIME_UTILS_PUBLIC std::chrono::system_clock::time_point from_message(builtin_interfaces::msg::Time t) noexcept
Convert from std::chrono::time_point from time message.
Definition: time_utils.cpp:80
geometry_msgs::msg::PoseWithCovarianceStamped PoseWithCovarianceStamped
Definition: localization_node.hpp:75
This struct specifies the interface requirements of LocalizerT, given the InputT, MapT and SummaryT p...
Definition: localization_nodes/include/localization_nodes/constraints.hpp:95
virtual void on_bad_registration(std::exception_ptr eptr)
Handle the exceptions during registration.
Definition: localization_node.hpp:222
virtual void on_observation_with_invalid_map(typename ObservationMsgT::ConstSharedPtr)
Default behavior when an observation is received with no valid existing map.
Definition: localization_node.hpp:247
void set_map(std::unique_ptr< MapT > &&map_ptr)
Definition: localization_node.hpp:216
LocalizerPublishMode
Enum to specify if the localizer node must publish to /tf topic or not.
Definition: localization_node.hpp:53
Requires
Helper class for concepts-like API.
Definition: localization_nodes/include/localization_nodes/constraints.hpp:32
This file includes common helper functions.
geometry_msgs::msg::TransformStamped Transform
Definition: recordreplay_planner_node.hpp:56
rclcpp::QoS qos
Definition: localization_node.hpp:49
geometry_msgs::msg::TransformStamped TransformStamped
Definition: localization_node.hpp:76
This struct specifies the interface requirements of MapT given MapMsgT parameter of a relative locali...
Definition: localization_nodes/include/localization_nodes/constraints.hpp:49
Helper struct that groups topic name and QoS setting for a publisher or subscription.
Definition: localization_node.hpp:46
virtual bool validate_output(const RegistrationSummary &summary, const PoseWithCovarianceStamped &pose, const TransformStamped &guess)
Definition: localization_node.hpp:271
void set_localizer(std::unique_ptr< LocalizerT > &&localizer_ptr)
Definition: localization_node.hpp:211
This file defines the lanelet2_map_provider_node class.
Definition: quick_sort.hpp:24