diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb177df..e3006e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,16 +10,15 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup ROS - uses: ros-tooling/setup-ros@v0.6 + uses: ros-tooling/setup-ros@v0.7 with: required-ros-distributions: noetic - name: Build uses: ros-tooling/action-ros-ci@v0.3 with: - target-ros1-distro: noetic - vcs-repo-file-url: .rosinstall + target-ros1-distro: humble diff --git a/.rosinstall b/.rosinstall deleted file mode 100644 index e7191ee..0000000 --- a/.rosinstall +++ /dev/null @@ -1,4 +0,0 @@ -- git: - local-name: ndt_omp - uri: https://github.com/koide3/ndt_omp.git - version: master diff --git a/CMakeLists.txt b/CMakeLists.txt index 8542d42..f6ad3e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,71 +1,41 @@ cmake_minimum_required(VERSION 3.5) project(ndt_localizer) -add_compile_options(-std=c++14 -Wall) +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() -find_package(catkin REQUIRED COMPONENTS - geometry_msgs - nav_msgs - roscpp - rospy - sensor_msgs - std_msgs - tf2 - tf2_ros - tf2_geometry_msgs - pcl_conversions - ndt_omp -) +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() -find_package(PCL 1.8.0 REQUIRED) -find_package(Eigen 3 REQUIRED) +find_package(ament_cmake_auto REQUIRED) +ament_auto_find_build_dependencies() -find_package(OpenMP) +find_package(OpenMP REQUIRED) if(OPENMP_FOUND) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") endif() -catkin_package( - INCLUDE_DIRS include -# LIBRARIES ndt_localizer -# CATKIN_DEPENDS geometry_msgs nav_msgs roscpp rospy sensor_msgs std_msgs tf -# DEPENDS system_lib -) - -include_directories( - include - ${catkin_INCLUDE_DIRS} - ${PCL_INCLUDE_DIRS} - ${Eigen_INCLUDE_DIRS} -) - -link_directories( - ${PCL_LIBRARY_DIRS} -) - -add_definitions( - ${PCL_DEFINITIONS} -) - -add_executable(map_matcher src/map_matcher_node.cpp src/map_matcher.cpp) -add_executable(ndt_odom_integrator src/ndt_odom_integrator_node.cpp src/ndt_odom_integrator.cpp) +ament_auto_add_executable(map_matcher + src/map_matcher_node.cpp + src/map_matcher.cpp) +target_link_libraries(map_matcher OpenMP::OpenMP_CXX) -add_dependencies(map_matcher ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) -add_dependencies(ndt_odom_integrator ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) +ament_auto_add_executable(ndt_odom_integrator + src/ndt_odom_integrator_node.cpp + src/ndt_odom_integrator.cpp) -target_link_libraries(map_matcher - ${catkin_LIBRARIES} - ${PCL_LIBRARIES} +install(DIRECTORY + launch + DESTINATION share/${PROJECT_NAME} ) -target_link_libraries(ndt_odom_integrator - ${catkin_LIBRARIES} -) - -if(CATKIN_ENABLE_TESTING) - find_package(roslint REQUIRED) - set(ROSLINT_CPP_OPTS "--filter=-build/c++11,-runtime/references") - roslint_cpp() - roslint_add_test() +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + set(ament_cmake_copyright_FOUND TRUE) + ament_lint_auto_find_test_dependencies() endif() + +ament_auto_package() diff --git a/README.md b/README.md index 029bb5e..69e80ef 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # localizer ## Requirement -- ros (melodic or noetic) -- PCL 1.8 -- [ndt_omp](https://github.com/koide3/ndt_omp) +- ros (humble or later) ## Runtime requirements - tf from robot base frame (e.g. base_link) to sensor frame (e.g. velodyne) @@ -23,7 +21,7 @@ - /map_cloud (sensor_msgs/PointCloud2) - map data - /estimated_pose (geometry_msgs/PoseWithCovarianceStamped) - - used to set `init_guess` for NDT + - used to set `init_guess` for NDT ### ndt_odom_integrator #### Published topics diff --git a/include/ndt_localizer/map_matcher.h b/include/ndt_localizer/map_matcher.h deleted file mode 100644 index 475c676..0000000 --- a/include/ndt_localizer/map_matcher.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2023 amsl - -#ifndef NDT_LOCALIZER_MAP_MATCHER_H -#define NDT_LOCALIZER_MAP_MATCHER_H - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "pclomp/ndt_omp.h" - -#include - -namespace ndt_localizer -{ -class MapMatcher -{ -public: - typedef pcl::PointXYZ PointType; - typedef pcl::PointCloud CloudType; - typedef pcl::PointCloud::Ptr CloudTypePtr; - MapMatcher(void); - void pose_callback(const nav_msgs::OdometryConstPtr& msg); - void map_callback(const sensor_msgs::PointCloud2ConstPtr& msg); - void cloud_callback(const sensor_msgs::PointCloud2ConstPtr& msg); - Eigen::Matrix4f get_ndt_transform(const CloudTypePtr& cloud_ptr); - void apply_voxel_grid_filter(double leaf_size, CloudTypePtr& cloud_ptr); - void apply_passthrough_filter( - double range, const CloudTypePtr& cloud_ptr, - CloudTypePtr& output_cloud_ptr, - const Eigen::Vector3f& center = Eigen::Vector3f::Zero()); - void process(void); - -private: - ros::NodeHandle nh_; - ros::NodeHandle local_nh_; - ros::Publisher pose_pub_; - ros::Publisher cloud_pub_; - ros::Subscriber pose_sub_; - ros::Subscriber map_sub_; - ros::Subscriber cloud_sub_; - std::shared_ptr tfl_; - std::shared_ptr tf_; - - CloudTypePtr map_cloud_ptr_; - bool is_map_received_; - nav_msgs::Odometry received_pose_; - bool is_pose_updated_; - - // ndt params - double epsilon_; - double leaf_size_; - double step_size_; - double resolution_; - int max_iterations_; - - double range_; -}; - -} // namespace ndt_localizer - -#endif // NDT_LOCALIZER_MAP_MATCHER_H diff --git a/include/ndt_localizer/map_matcher.hpp b/include/ndt_localizer/map_matcher.hpp new file mode 100644 index 0000000..d9661ce --- /dev/null +++ b/include/ndt_localizer/map_matcher.hpp @@ -0,0 +1,72 @@ +// Copyright 2023 amsl + +#ifndef NDT_LOCALIZER__MAP_MATCHER_HPP_ +#define NDT_LOCALIZER__MAP_MATCHER_HPP_ + +#include +#include + +#include "Eigen/Dense" +#include "geometry_msgs/msg/pose_stamped.hpp" +#include "geometry_msgs/msg/pose_with_covariance_stamped.hpp" +#include "nav_msgs/msg/odometry.hpp" +#include "pcl/filters/passthrough.h" +#include "pcl/filters/voxel_grid.h" +#include "pcl/point_types.h" +#include "pcl/registration/ndt.h" +#include "pcl_conversions/pcl_conversions.h" +#include "pclomp/ndt_omp.h" +#include "rclcpp/rclcpp.hpp" +#include "sensor_msgs/msg/point_cloud2.hpp" +#include "tf2_eigen/tf2_eigen.hpp" +#include "tf2_ros/buffer.h" +#include "tf2_ros/transform_listener.h" + +namespace ndt_localizer +{ +class MapMatcher : public rclcpp::Node +{ +public: + typedef pcl::PointXYZ PointType; + typedef pcl::PointCloud CloudType; + typedef pcl::PointCloud::Ptr CloudTypePtr; + + MapMatcher(const rclcpp::NodeOptions & options = rclcpp::NodeOptions()); + virtual ~MapMatcher(void) = default; + + void pose_callback(const nav_msgs::msg::Odometry::ConstSharedPtr & msg); + void map_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr & msg); + void cloud_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr & msg); + Eigen::Matrix4f get_ndt_transform(const CloudTypePtr & cloud_ptr); + void apply_voxel_grid_filter(double leaf_size, CloudTypePtr & cloud_ptr); + void apply_passthrough_filter( + double range, const CloudTypePtr & cloud_ptr, CloudTypePtr & output_cloud_ptr, + const Eigen::Vector3f & center = Eigen::Vector3f::Zero()); + +private: + rclcpp::Publisher::SharedPtr pose_pub_; + rclcpp::Publisher::SharedPtr cloud_pub_; + rclcpp::Subscription::SharedPtr pose_sub_; + rclcpp::Subscription::SharedPtr map_sub_; + rclcpp::Subscription::SharedPtr cloud_sub_; + std::shared_ptr tfl_; + std::shared_ptr tf_; + + CloudTypePtr map_cloud_ptr_; + bool is_map_received_; + nav_msgs::msg::Odometry received_pose_; + bool is_pose_updated_; + + // ndt params + double epsilon_; + double leaf_size_; + double step_size_; + double resolution_; + int max_iterations_; + + double range_; +}; + +} // namespace ndt_localizer + +#endif // NDT_LOCALIZER__MAP_MATCHER_HPP_ diff --git a/include/ndt_localizer/ndt_odom_integrator.h b/include/ndt_localizer/ndt_odom_integrator.h deleted file mode 100644 index 154e52b..0000000 --- a/include/ndt_localizer/ndt_odom_integrator.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2023 amsl - -#ifndef NDT_LOCALIZER_NDT_ODOM_INTEGRATOR_H -#define NDT_LOCALIZER_NDT_ODOM_INTEGRATOR_H - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace ndt_localizer -{ -class NDTOdomIntegrator -{ -public: - NDTOdomIntegrator(void); - void ndt_pose_callback(const geometry_msgs::PoseStampedConstPtr& msg); - void odom_callback(const nav_msgs::OdometryConstPtr& msg); - void imu_callback(const sensor_msgs::ImuConstPtr& msg); - void map_callback(const sensor_msgs::PointCloud2ConstPtr& msg); - void init_pose_callback( - const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg); - void initialize_state(double x, double y, double z, double roll, double pitch, - double yaw); - geometry_msgs::PoseWithCovariance get_pose_msg_from_state(void); - void predict_by_odom(const Eigen::Vector3d& dp); - void predict_by_imu(const Eigen::Vector3d& dr); - void predict_between_timestamps(const ros::Time begin_stamp, const ros::Time end_stamp); - void update_by_ndt_pose(const Eigen::VectorXd& pose); - Eigen::Matrix3d get_rotation_matrix(double roll, double pitch, double yaw); - bool is_mahalanobis_distance_gate(const double mahalanobis_distance_threshold, const Eigen::VectorXd& ndt_pose, - const Eigen::VectorXd& last_pose, const Eigen::MatrixXd& cov); - bool is_covariance_large(const double pose_covariance_threshold, const double direction_covariance_threshold, - const Eigen::MatrixXd& covariance); - void publish_map_to_odom_tf(const ros::Time& stamp, - const geometry_msgs::Pose& pose); - void process(void); - -private: - ros::NodeHandle nh_; - ros::NodeHandle local_nh_; - ros::Publisher estimated_pose_pub_; - ros::Subscriber ndt_pose_sub_; - ros::Subscriber odom_sub_; - ros::Subscriber imu_sub_; - ros::Subscriber map_sub_; - ros::Subscriber init_pose_sub_; - - double init_sigma_position_; - double init_sigma_orientation_; - std::string map_frame_id_; - std::string odom_frame_id_; - std::string robot_frame_id_; - double sigma_odom_; - double sigma_imu_; - double sigma_ndt_; - bool enable_odom_tf_; - bool enable_tf_; - double mahalanobis_distance_threshold_; - double pose_covariance_threshold_; - double direction_covariance_threshold_; - - unsigned int state_dim_; - unsigned int position_dim_; - unsigned int orientation_dim_; - int queue_capacity_; - std::vector odom_queue_; - std::vector imu_queue_; - Eigen::VectorXd last_pose_; - Eigen::MatrixXd last_covariance_; - Eigen::VectorXd x_; - Eigen::MatrixXd p_; - Eigen::MatrixXd q_odom_; - Eigen::MatrixXd q_imu_; - Eigen::MatrixXd r_; - ros::Time last_odom_stamp_; - ros::Time last_imu_stamp_; - ros::Time last_pose_stamp_; - std::shared_ptr tfb_; - std::shared_ptr tfl_; - std::shared_ptr tf_; -}; - -} // namespace ndt_localizer - -#endif // NDT_LOCALIZER_NDT_ODOM_INTEGRATOR_H diff --git a/include/ndt_localizer/ndt_odom_integrator.hpp b/include/ndt_localizer/ndt_odom_integrator.hpp new file mode 100644 index 0000000..0f2cb85 --- /dev/null +++ b/include/ndt_localizer/ndt_odom_integrator.hpp @@ -0,0 +1,95 @@ +// Copyright 2023 amsl + +#ifndef NDT_LOCALIZER__NDT_ODOM_INTEGRATOR_HPP_ +#define NDT_LOCALIZER__NDT_ODOM_INTEGRATOR_HPP_ + +#include +#include +#include + +#include "Eigen/Dense" +#include "geometry_msgs/msg/pose_stamped.hpp" +#include "geometry_msgs/msg/pose_with_covariance.hpp" +#include "geometry_msgs/msg/pose_with_covariance_stamped.hpp" +#include "nav_msgs/msg/odometry.hpp" +#include "rclcpp/rclcpp.hpp" +#include "sensor_msgs/msg/imu.hpp" +#include "sensor_msgs/msg/point_cloud2.hpp" +#include "tf2/utils.h" +#include "tf2_ros/buffer.h" +#include "tf2_ros/transform_broadcaster.h" +#include "tf2_ros/transform_listener.h" + +namespace ndt_localizer +{ +class NDTOdomIntegrator : public rclcpp::Node +{ +public: + explicit NDTOdomIntegrator(const rclcpp::NodeOptions & options = rclcpp::NodeOptions()); + void ndt_pose_callback(const geometry_msgs::msg::PoseStamped::ConstSharedPtr & msg); + void odom_callback(const nav_msgs::msg::Odometry::ConstSharedPtr & msg); + void imu_callback(const sensor_msgs::msg::Imu::ConstSharedPtr & msg); + void map_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr & msg); + void init_pose_callback( + const geometry_msgs::msg::PoseWithCovarianceStamped::ConstSharedPtr & msg); + void initialize_state(double x, double y, double z, double roll, double pitch, double yaw); + geometry_msgs::msg::PoseWithCovariance get_pose_msg_from_state(void); + void predict_by_odom(const Eigen::Vector3d & dp); + void predict_by_imu(const Eigen::Vector3d & dr); + void predict_between_timestamps(const rclcpp::Time begin_stamp, const rclcpp::Time end_stamp); + void update_by_ndt_pose(const Eigen::VectorXd & pose); + Eigen::Matrix3d get_rotation_matrix(double roll, double pitch, double yaw); + bool is_mahalanobis_distance_gate( + const double mahalanobis_distance_threshold, const Eigen::VectorXd & ndt_pose, + const Eigen::VectorXd & last_pose, const Eigen::MatrixXd & cov); + bool is_covariance_large( + const double pose_covariance_threshold, const double direction_covariance_threshold, + const Eigen::MatrixXd & covariance); + void publish_map_to_odom_tf(const rclcpp::Time & stamp, const geometry_msgs::msg::Pose & pose); + +private: + rclcpp::Publisher::SharedPtr estimated_pose_pub_; + rclcpp::Subscription::SharedPtr ndt_pose_sub_; + rclcpp::Subscription::SharedPtr odom_sub_; + rclcpp::Subscription::SharedPtr imu_sub_; + rclcpp::Subscription::SharedPtr map_sub_; + rclcpp::Subscription::SharedPtr init_pose_sub_; + + double init_sigma_position_; + double init_sigma_orientation_; + std::string map_frame_id_; + std::string odom_frame_id_; + std::string robot_frame_id_; + double sigma_odom_; + double sigma_imu_; + double sigma_ndt_; + bool enable_odom_tf_; + bool enable_tf_; + double mahalanobis_distance_threshold_; + double pose_covariance_threshold_; + double direction_covariance_threshold_; + + unsigned int state_dim_; + unsigned int position_dim_; + unsigned int orientation_dim_; + int queue_capacity_; + std::vector odom_queue_; + std::vector imu_queue_; + Eigen::VectorXd last_pose_; + Eigen::MatrixXd last_covariance_; + Eigen::VectorXd x_; + Eigen::MatrixXd p_; + Eigen::MatrixXd q_odom_; + Eigen::MatrixXd q_imu_; + Eigen::MatrixXd r_; + rclcpp::Time last_odom_stamp_; + rclcpp::Time last_imu_stamp_; + rclcpp::Time last_pose_stamp_; + std::shared_ptr tfb_; + std::shared_ptr tfl_; + std::shared_ptr tf_; +}; + +} // namespace ndt_localizer + +#endif // NDT_LOCALIZER__NDT_ODOM_INTEGRATOR_HPP_ diff --git a/launch/ndt.launch b/launch/ndt.launch deleted file mode 100644 index 1014e17..0000000 --- a/launch/ndt.launch +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - --> - - - - diff --git a/launch/ndt.launch.py b/launch/ndt.launch.py new file mode 100644 index 0000000..fccced6 --- /dev/null +++ b/launch/ndt.launch.py @@ -0,0 +1,46 @@ +from launch_ros.actions import Node + +from launch import LaunchDescription + + +def generate_launch_description() -> LaunchDescription: + return LaunchDescription( + [ + Node( + package="ndt_localizer", + executable="map_matcher", + name="map_matcher", + output="screen", + parameters=[ + { + "leaf_size": 0.5, + "epsilon": 1e-2, + "max_iteration": 30, + "range": 50.0, + }, + ], + remappings=[ + ("scan_cloud", "velodyne_points"), + ("map_cloud", "cloud_pcd"), + ], + ), + Node( + package="ndt_localizer", + executable="ndt_odom_integrator", + name="ndt_odom_integrator", + output="screen", + parameters=[ + { + "sigma_odom": 0.01, + "sigma_imu": 0.01, + "sigma_ndt": 0.1, + }, + ], + remappings=[ + ("odom", "odom"), + ("imu/data", "imu/data"), + ("map_cloud", "cloud_pcd"), + ], + ), + ] + ) diff --git a/package.xml b/package.xml index 66f7981..ec3cf50 100644 --- a/package.xml +++ b/package.xml @@ -15,26 +15,26 @@ TODO - catkin + ament_cmake_auto geometry_msgs nav_msgs - roscpp - rospy + rclcpp sensor_msgs std_msgs tf2 + tf2_eigen tf2_ros tf2_geometry_msgs eigen libpcl-all-dev pcl_conversions ndt_omp - rostest - roslint + libomp-dev - - - + ament_lint_auto + ament_lint_common + + ament_cmake diff --git a/src/map_matcher.cpp b/src/map_matcher.cpp index 9b5f389..22ce9ea 100644 --- a/src/map_matcher.cpp +++ b/src/map_matcher.cpp @@ -1,63 +1,62 @@ // Copyright 2023 amsl +#include "ndt_localizer/map_matcher.hpp" + #include #include -#include "ndt_localizer/map_matcher.h" - namespace ndt_localizer { -MapMatcher::MapMatcher(void) - : local_nh_("~") +MapMatcher::MapMatcher(const rclcpp::NodeOptions& options) + : Node("map_matcher", options) { - pose_pub_ = nh_.advertise("ndt_pose", 1); - cloud_pub_ = nh_.advertise("cloud/aligned", 1); - pose_sub_ = - nh_.subscribe("estimated_pose", 1, &MapMatcher::pose_callback, this, - ros::TransportHints().reliable().tcpNoDelay(true)); - map_sub_ = nh_.subscribe("map_cloud", 1, &MapMatcher::map_callback, this, - ros::TransportHints().reliable().tcpNoDelay(true)); - cloud_sub_ = nh_.subscribe("scan_cloud", 1, &MapMatcher::cloud_callback, this, - ros::TransportHints().reliable().tcpNoDelay(true)); - - local_nh_.param("epsilon", epsilon_, 1e-2); - local_nh_.param("leaf_size", leaf_size_, 0.5); - local_nh_.param("step_size", step_size_, 0.1); - local_nh_.param("resolution", resolution_, 1.0); - local_nh_.param("max_iterations", max_iterations_, 30); - local_nh_.param("range", range_, 100); - - ROS_INFO("=== map_matcher ==="); - ROS_INFO_STREAM("epsilon: " << epsilon_); - ROS_INFO_STREAM("leaf_size: " << leaf_size_); - ROS_INFO_STREAM("step_size: " << step_size_); - ROS_INFO_STREAM("resolution: " << resolution_); - ROS_INFO_STREAM("max_iterations: " << max_iterations_); - ROS_INFO_STREAM("range: " << range_); + pose_pub_ = this->create_publisher("ndt_pose", 1); + cloud_pub_ = this->create_publisher("cloud/aligned", 1); + pose_sub_ = this->create_subscription( + "estimated_pose", 1, std::bind(&MapMatcher::pose_callback, this, std::placeholders::_1)); + map_sub_ = this->create_subscription( + "map_cloud", 1, std::bind(&MapMatcher::map_callback, this, std::placeholders::_1)); + cloud_sub_ = this->create_subscription( + "scan_cloud", 1, std::bind(&MapMatcher::cloud_callback, this, std::placeholders::_1)); + + epsilon_ = this->declare_parameter("epsilon", 1e-2); + leaf_size_ = this->declare_parameter("leaf_size", 0.5); + step_size_ = this->declare_parameter("step_size", 0.1); + resolution_ = this->declare_parameter("resolution", 1.0); + max_iterations_ = this->declare_parameter("max_iterations", 30); + range_ = this->declare_parameter("range", 100.0); + + RCLCPP_INFO(this->get_logger(), "=== map_matcher ==="); + RCLCPP_INFO_STREAM(this->get_logger(), "epsilon: " << epsilon_); + RCLCPP_INFO_STREAM(this->get_logger(), "leaf_size: " << leaf_size_); + RCLCPP_INFO_STREAM(this->get_logger(), "step_size: " << step_size_); + RCLCPP_INFO_STREAM(this->get_logger(), "resolution: " << resolution_); + RCLCPP_INFO_STREAM(this->get_logger(), "max_iterations: " << max_iterations_); + RCLCPP_INFO_STREAM(this->get_logger(), "range: " << range_); map_cloud_ptr_ = CloudTypePtr(new CloudType); is_map_received_ = false; is_pose_updated_ = false; - tf_ = std::make_shared(); + tf_ = std::make_shared(this->get_clock()); tf_->setUsingDedicatedThread(true); tfl_ = std::make_shared(*tf_); } -void MapMatcher::pose_callback(const nav_msgs::OdometryConstPtr& msg) +void MapMatcher::pose_callback(const nav_msgs::msg::Odometry::ConstSharedPtr& msg) { is_pose_updated_ = true; received_pose_ = *msg; } -void MapMatcher::map_callback(const sensor_msgs::PointCloud2ConstPtr& msg) +void MapMatcher::map_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg) { - ROS_INFO("received map"); + RCLCPP_INFO(this->get_logger(), "received map"); pcl::fromROSMsg(*msg, *map_cloud_ptr_); is_map_received_ = true; } -void MapMatcher::cloud_callback(const sensor_msgs::PointCloud2ConstPtr& msg) +void MapMatcher::cloud_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg) { if (is_map_received_ && is_pose_updated_) { @@ -65,25 +64,22 @@ void MapMatcher::cloud_callback(const sensor_msgs::PointCloud2ConstPtr& msg) pcl::fromROSMsg(*msg, *cloud_ptr); cloud_ptr->is_dense = false; cloud_ptr->width = cloud_ptr->points.size(); - // ROS_INFO_STREAM("bfr: " << cloud_ptr->points.size()); + // RCLCPP_INFO_STREAM(this->get_logger(), "bfr: " << cloud_ptr->points.size()); apply_voxel_grid_filter(leaf_size_, cloud_ptr); - // ROS_INFO_STREAM("aft: " << cloud_ptr->points.size()); - const geometry_msgs::TransformStamped sensor_to_base_tf = - tf_->lookupTransform(received_pose_.child_frame_id, - msg->header.frame_id, ros::Time(0)); + // RCLCPP_INFO_STREAM(this->get_logger(), "aft: " << cloud_ptr->points.size()); + const geometry_msgs::msg::TransformStamped sensor_to_base_tf = + tf_->lookupTransform(received_pose_.child_frame_id, msg->header.frame_id, msg->header.stamp); const Eigen::Matrix4f sensor_to_base_mat = - tf2::transformToEigen(sensor_to_base_tf.transform) - .matrix() - .cast(); + tf2::transformToEigen(sensor_to_base_tf.transform).matrix().cast(); pcl::transformPointCloud(*cloud_ptr, *cloud_ptr, sensor_to_base_mat); const Eigen::Matrix4f transform = get_ndt_transform(cloud_ptr); if (transform.isZero(1e-6)) { return; } - ROS_INFO_STREAM("transform:\n" - << transform); - geometry_msgs::PoseStamped aligned_pose; + RCLCPP_INFO_STREAM(this->get_logger(), "transform:\n" + << transform); + geometry_msgs::msg::PoseStamped aligned_pose; aligned_pose.header.stamp = msg->header.stamp; aligned_pose.header.frame_id = map_cloud_ptr_->header.frame_id; aligned_pose.pose.position.x = transform(0, 3); @@ -95,51 +91,52 @@ void MapMatcher::cloud_callback(const sensor_msgs::PointCloud2ConstPtr& msg) aligned_pose.pose.orientation.x = q.x(); aligned_pose.pose.orientation.y = q.y(); aligned_pose.pose.orientation.z = q.z(); - ROS_INFO_STREAM("aligned pose:\n" - << aligned_pose.pose); - pose_pub_.publish(aligned_pose); + RCLCPP_INFO_STREAM( + this->get_logger(), "aligned pose:\n" + << geometry_msgs::msg::to_yaml(aligned_pose.pose)); + pose_pub_->publish(aligned_pose); is_pose_updated_ = false; } else { if (!is_pose_updated_) { - ROS_WARN_THROTTLE(1.0, "cloud is received but pose has not been updated"); + RCLCPP_WARN_THROTTLE( + this->get_logger(), *this->get_clock(), 1000, + "cloud is received but pose has not been updated"); } if (!is_map_received_) { - ROS_WARN_THROTTLE(1.0, "cloud is received but map has not been received"); + RCLCPP_WARN_THROTTLE( + this->get_logger(), *this->get_clock(), 1000, + "cloud is received but map has not been received"); } } } Eigen::Matrix4f MapMatcher::get_ndt_transform(const CloudTypePtr& cloud_ptr) { - const Eigen::Vector3f translation_vector = - { - static_cast(received_pose_.pose.pose.position.x), - static_cast(received_pose_.pose.pose.position.y), - static_cast(received_pose_.pose.pose.position.z), - }; + const Eigen::Vector3f translation_vector = { + static_cast(received_pose_.pose.pose.position.x), + static_cast(received_pose_.pose.pose.position.y), + static_cast(received_pose_.pose.pose.position.z), + }; const Eigen::Translation3f init_translation(translation_vector); const Eigen::AngleAxisf init_rotation( - Eigen::Quaternionf(received_pose_.pose.pose.orientation.w, - received_pose_.pose.pose.orientation.x, - received_pose_.pose.pose.orientation.y, - received_pose_.pose.pose.orientation.z) + Eigen::Quaternionf( + received_pose_.pose.pose.orientation.w, received_pose_.pose.pose.orientation.x, + received_pose_.pose.pose.orientation.y, received_pose_.pose.pose.orientation.z) .normalized()); - const Eigen::Matrix4f init_guess = - (init_translation * init_rotation).matrix(); - ROS_INFO_STREAM("init_guess:\n" - << init_guess); + const Eigen::Matrix4f init_guess = (init_translation * init_rotation).matrix(); + RCLCPP_INFO_STREAM(this->get_logger(), "init_guess:\n" + << init_guess); CloudTypePtr filtered_map_cloud_ptr(new CloudType); - apply_passthrough_filter(range_, map_cloud_ptr_, filtered_map_cloud_ptr, - translation_vector); + apply_passthrough_filter(range_, map_cloud_ptr_, filtered_map_cloud_ptr, translation_vector); CloudTypePtr filtered_scan_cloud_ptr(new CloudType); apply_passthrough_filter(range_, cloud_ptr, filtered_scan_cloud_ptr); - ROS_INFO_STREAM("scan size: " << filtered_scan_cloud_ptr->points.size()); - ROS_INFO_STREAM("map size: " << filtered_map_cloud_ptr->points.size()); - // ROS_INFO_STREAM("received_pose:\n" << received_pose_.pose.pose); + RCLCPP_INFO_STREAM(this->get_logger(), "scan size: " << filtered_scan_cloud_ptr->points.size()); + RCLCPP_INFO_STREAM(this->get_logger(), "map size: " << filtered_map_cloud_ptr->points.size()); + // RCLCPP_INFO_STREAM(this->get_logger(), "received_pose:\n" << received_pose_.pose.pose); pclomp::NormalDistributionsTransform ndt; ndt.setTransformationEpsilon(epsilon_); @@ -154,22 +151,21 @@ Eigen::Matrix4f MapMatcher::get_ndt_transform(const CloudTypePtr& cloud_ptr) ndt.align(*aligned_cloud_ptr, init_guess); if (!ndt.hasConverged()) { - ROS_ERROR("ndt not converged!"); + RCLCPP_ERROR(this->get_logger(), "ndt not converged!"); return Eigen::Matrix4f::Zero(); } - ROS_INFO_STREAM("score: " << ndt.getFitnessScore()); - if (cloud_pub_.getNumSubscribers() > 0) + RCLCPP_INFO_STREAM(this->get_logger(), "score: " << ndt.getFitnessScore()); + if (cloud_pub_->get_subscription_count() > 0) { - sensor_msgs::PointCloud2 aligned_cloud_msg; + sensor_msgs::msg::PointCloud2 aligned_cloud_msg; aligned_cloud_ptr->header.frame_id = map_cloud_ptr_->header.frame_id; pcl::toROSMsg(*aligned_cloud_ptr, aligned_cloud_msg); - cloud_pub_.publish(aligned_cloud_msg); + cloud_pub_->publish(aligned_cloud_msg); } return ndt.getFinalTransformation(); } -void MapMatcher::apply_voxel_grid_filter(double leaf_size, - CloudTypePtr& cloud_ptr) +void MapMatcher::apply_voxel_grid_filter(double leaf_size, CloudTypePtr& cloud_ptr) { pcl::VoxelGrid vg; vg.setInputCloud(cloud_ptr); @@ -180,10 +176,9 @@ void MapMatcher::apply_voxel_grid_filter(double leaf_size, *cloud_ptr = *output_cloud_ptr; } -void MapMatcher::apply_passthrough_filter(double range, - const CloudTypePtr& cloud_ptr, - CloudTypePtr& output_cloud_ptr, - const Eigen::Vector3f& center) +void MapMatcher::apply_passthrough_filter( + double range, const CloudTypePtr& cloud_ptr, CloudTypePtr& output_cloud_ptr, + const Eigen::Vector3f& center) { pcl::PassThrough pass; pass.setInputCloud(cloud_ptr); @@ -200,9 +195,4 @@ void MapMatcher::apply_passthrough_filter(double range, pass.filter(*output_cloud_ptr); } -void MapMatcher::process(void) -{ - ros::spin(); -} - } // namespace ndt_localizer diff --git a/src/map_matcher_node.cpp b/src/map_matcher_node.cpp index 23f74ab..5a4ab5e 100644 --- a/src/map_matcher_node.cpp +++ b/src/map_matcher_node.cpp @@ -1,11 +1,12 @@ // Copyright 2023 amsl -#include "ndt_localizer/map_matcher.h" +#include "ndt_localizer/map_matcher.hpp" -int main(int argc, char** argv) +int main(int argc, char ** argv) { - ros::init(argc, argv, "map_matcher"); - ndt_localizer::MapMatcher map_matcher; - map_matcher.process(); + rclcpp::init(argc, argv); + const auto node = std::make_shared(); + rclcpp::spin(node); + rclcpp::shutdown(); return 0; } diff --git a/src/ndt_odom_integrator.cpp b/src/ndt_odom_integrator.cpp index 8fe5780..67ea4c7 100644 --- a/src/ndt_odom_integrator.cpp +++ b/src/ndt_odom_integrator.cpp @@ -1,73 +1,73 @@ // Copyright 2023 amsl -#include +#include "ndt_localizer/ndt_odom_integrator.hpp" + #include #include +#include -#include "ndt_localizer/ndt_odom_integrator.h" +#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp" namespace ndt_localizer { -NDTOdomIntegrator::NDTOdomIntegrator(void) - : local_nh_("~") +NDTOdomIntegrator::NDTOdomIntegrator(const rclcpp::NodeOptions& options) + : Node("ndt_odom_integrator", options) { - ROS_INFO("=== ndt_odom_integrator ==="); - estimated_pose_pub_ = nh_.advertise("estimated_pose", 1); - ndt_pose_sub_ = - nh_.subscribe("ndt_pose", 1, &NDTOdomIntegrator::ndt_pose_callback, this, - ros::TransportHints().reliable().tcpNoDelay(true)); - odom_sub_ = nh_.subscribe("odom", 1, &NDTOdomIntegrator::odom_callback, this, - ros::TransportHints().reliable().tcpNoDelay(true)); - imu_sub_ = - nh_.subscribe("imu/data", 1, &NDTOdomIntegrator::imu_callback, this, - ros::TransportHints().reliable().tcpNoDelay(true)); - map_sub_ = - nh_.subscribe("map_cloud", 1, &NDTOdomIntegrator::map_callback, this, - ros::TransportHints().reliable().tcpNoDelay(true)); - init_pose_sub_ = - nh_.subscribe("initialpose", 1, &NDTOdomIntegrator::init_pose_callback, this, - ros::TransportHints().reliable().tcpNoDelay(true)); - - local_nh_.param("init_sigma_position", init_sigma_position_, 10); - local_nh_.param("init_sigma_orientation", init_sigma_orientation_, - M_PI); - double init_x, init_y, init_z, init_roll, init_pitch, init_yaw; - local_nh_.param("init_x", init_x, 0); - local_nh_.param("init_y", init_y, 0); - local_nh_.param("init_z", init_z, 0); - local_nh_.param("init_roll", init_roll, 0); - local_nh_.param("init_pitch", init_pitch, 0); - local_nh_.param("init_yaw", init_yaw, 0); - local_nh_.param("sigma_odom", sigma_odom_, 1e-4); - local_nh_.param("sigma_imu", sigma_imu_, 1e-4); - local_nh_.param("sigma_ndt", sigma_ndt_, 1e-2); - local_nh_.param("enable_odom_tf", enable_odom_tf_, true); - local_nh_.param("enable_tf", enable_tf_, true); - local_nh_.param("queue_capacity", queue_capacity_, 1000); - local_nh_.param("mahalanobis_distance_threshold", mahalanobis_distance_threshold_, 1.5); - local_nh_.param("pose_covariance_threshold", pose_covariance_threshold_, 1.0); - local_nh_.param("direction_covariance_threshold", direction_covariance_threshold_, 1.0); - - ROS_INFO_STREAM("init_sigma_position: " << init_sigma_position_); - ROS_INFO_STREAM("init_sigma_orientation: " << init_sigma_orientation_); - ROS_INFO_STREAM("init_x: " << init_x); - ROS_INFO_STREAM("init_y: " << init_y); - ROS_INFO_STREAM("init_z: " << init_z); - ROS_INFO_STREAM("init_roll: " << init_roll); - ROS_INFO_STREAM("init_pitch: " << init_pitch); - ROS_INFO_STREAM("init_yaw: " << init_yaw); - ROS_INFO_STREAM("sigma_odom: " << sigma_odom_); - ROS_INFO_STREAM("sigma_imu: " << sigma_imu_); - ROS_INFO_STREAM("sigma_ndt: " << sigma_ndt_); - ROS_INFO_STREAM("enable_odom_tf: " << enable_odom_tf_); - ROS_INFO_STREAM("enable_tf: " << enable_tf_); - ROS_INFO_STREAM("queue_capacity: " << queue_capacity_); - ROS_INFO_STREAM("mahalanobis_distance_threshold: " << mahalanobis_distance_threshold_); - ROS_INFO_STREAM("pose_covariance_threshold: " << pose_covariance_threshold_); - ROS_INFO_STREAM("direction_covariance_threshold: " << direction_covariance_threshold_); - - tf_ = std::make_shared(); + RCLCPP_INFO(this->get_logger(), "=== ndt_odom_integrator ==="); + estimated_pose_pub_ = this->create_publisher("estimated_pose", 1); + ndt_pose_sub_ = this->create_subscription( + "ndt_pose", 1, std::bind(&NDTOdomIntegrator::ndt_pose_callback, this, std::placeholders::_1)); + odom_sub_ = this->create_subscription( + "odom", 1, std::bind(&NDTOdomIntegrator::odom_callback, this, std::placeholders::_1)); + imu_sub_ = this->create_subscription( + "imu/data", 1, std::bind(&NDTOdomIntegrator::imu_callback, this, std::placeholders::_1)); + map_sub_ = this->create_subscription( + "map_cloud", 1, std::bind(&NDTOdomIntegrator::map_callback, this, std::placeholders::_1)); + init_pose_sub_ = this->create_subscription( + "initialpose", 1, + std::bind(&NDTOdomIntegrator::init_pose_callback, this, std::placeholders::_1)); + + init_sigma_position_ = this->declare_parameter("init_sigma_position", 10.0); + init_sigma_orientation_ = this->declare_parameter("init_sigma_orientation", M_PI); + const double init_x = this->declare_parameter("init_x", 0.0); + const double init_y = this->declare_parameter("init_y", 0.0); + const double init_z = this->declare_parameter("init_z", 0.0); + const double init_roll = this->declare_parameter("init_roll", 0.0); + const double init_pitch = this->declare_parameter("init_pitch", 0.0); + const double init_yaw = this->declare_parameter("init_yaw", 0.0); + sigma_odom_ = this->declare_parameter("sigma_odom", 1e-4); + sigma_imu_ = this->declare_parameter("sigma_imu", 1e-4); + sigma_ndt_ = this->declare_parameter("sigma_ndt", 1e-2); + enable_odom_tf_ = this->declare_parameter("enable_odom_tf", true); + enable_tf_ = this->declare_parameter("enable_tf", true); + queue_capacity_ = this->declare_parameter("queue_capacity", 1000); + mahalanobis_distance_threshold_ = this->declare_parameter("mahalanobis_distance_threshold", 1.5); + pose_covariance_threshold_ = this->declare_parameter("pose_covariance_threshold", 1.0); + direction_covariance_threshold_ = this->declare_parameter("direction_covariance_threshold", 1.0); + + RCLCPP_INFO_STREAM(this->get_logger(), "init_sigma_position: " << init_sigma_position_); + RCLCPP_INFO_STREAM(this->get_logger(), "init_sigma_orientation: " << init_sigma_orientation_); + RCLCPP_INFO_STREAM(this->get_logger(), "init_x: " << init_x); + RCLCPP_INFO_STREAM(this->get_logger(), "init_y: " << init_y); + RCLCPP_INFO_STREAM(this->get_logger(), "init_z: " << init_z); + RCLCPP_INFO_STREAM(this->get_logger(), "init_roll: " << init_roll); + RCLCPP_INFO_STREAM(this->get_logger(), "init_pitch: " << init_pitch); + RCLCPP_INFO_STREAM(this->get_logger(), "init_yaw: " << init_yaw); + RCLCPP_INFO_STREAM(this->get_logger(), "sigma_odom: " << sigma_odom_); + RCLCPP_INFO_STREAM(this->get_logger(), "sigma_imu: " << sigma_imu_); + RCLCPP_INFO_STREAM(this->get_logger(), "sigma_ndt: " << sigma_ndt_); + RCLCPP_INFO_STREAM(this->get_logger(), "enable_odom_tf: " << enable_odom_tf_); + RCLCPP_INFO_STREAM(this->get_logger(), "enable_tf: " << enable_tf_); + RCLCPP_INFO_STREAM(this->get_logger(), "queue_capacity: " << queue_capacity_); + RCLCPP_INFO_STREAM( + this->get_logger(), "mahalanobis_distance_threshold: " << mahalanobis_distance_threshold_); + RCLCPP_INFO_STREAM( + this->get_logger(), "pose_covariance_threshold: " << pose_covariance_threshold_); + RCLCPP_INFO_STREAM( + this->get_logger(), "direction_covariance_threshold: " << direction_covariance_threshold_); + + tf_ = std::make_shared(this->get_clock()); tf_->setUsingDedicatedThread(true); - tfb_ = std::make_shared(); + tfb_ = std::make_shared(*this); tfl_ = std::make_shared(*tf_); state_dim_ = 6; @@ -80,25 +80,23 @@ NDTOdomIntegrator::NDTOdomIntegrator(void) q_odom_.block(0, 0, position_dim_, position_dim_) = sigma_odom_ * Eigen::MatrixXd::Identity(position_dim_, position_dim_); q_imu_ = Eigen::MatrixXd::Zero(state_dim_, state_dim_); - q_imu_.block(position_dim_, position_dim_, orientation_dim_, - orientation_dim_) = - sigma_imu_ * - Eigen::MatrixXd::Identity(orientation_dim_, orientation_dim_); + q_imu_.block(position_dim_, position_dim_, orientation_dim_, orientation_dim_) = + sigma_imu_ * Eigen::MatrixXd::Identity(orientation_dim_, orientation_dim_); r_ = sigma_ndt_ * Eigen::MatrixXd::Identity(state_dim_, state_dim_); - last_odom_stamp_ = ros::Time(0); - last_imu_stamp_ = ros::Time(0); + last_odom_stamp_ = rclcpp::Time(0); + last_imu_stamp_ = rclcpp::Time(0); map_frame_id_ = ""; odom_frame_id_ = ""; robot_frame_id_ = ""; } void NDTOdomIntegrator::ndt_pose_callback( - const geometry_msgs::PoseStampedConstPtr& msg) + const geometry_msgs::msg::PoseStamped::ConstSharedPtr& msg) { if (map_frame_id_.empty()) { - ROS_ERROR_THROTTLE(3.0, "frame_id is empty"); + RCLCPP_ERROR_THROTTLE(this->get_logger(), *this->get_clock(), 3000, "frame_id is empty"); return; } double roll, pitch, yaw; @@ -107,63 +105,75 @@ void NDTOdomIntegrator::ndt_pose_callback( tf2::Matrix3x3 rot(q); rot.getRPY(roll, pitch, yaw); Eigen::VectorXd received_pose = Eigen::VectorXd::Zero(state_dim_); - received_pose << msg->pose.position.x, msg->pose.position.y, msg->pose.position.z, - roll, pitch, yaw; + received_pose << msg->pose.position.x, msg->pose.position.y, msg->pose.position.z, roll, pitch, + yaw; // Predict update from last pose x_ = last_pose_; p_ = last_covariance_; - const ros::Time received_pose_stamp = msg->header.stamp; + const rclcpp::Time received_pose_stamp = msg->header.stamp; if (last_pose_stamp_ <= received_pose_stamp) { predict_between_timestamps(last_pose_stamp_, received_pose_stamp); - if (is_mahalanobis_distance_gate(mahalanobis_distance_threshold_, received_pose, x_, p_) || + if ( + is_mahalanobis_distance_gate(mahalanobis_distance_threshold_, received_pose, x_, p_) || is_covariance_large(pose_covariance_threshold_, direction_covariance_threshold_, p_)) + { update_by_ndt_pose(received_pose); + } last_pose_ = x_; last_covariance_ = p_; last_pose_stamp_ = msg->header.stamp; - predict_between_timestamps(received_pose_stamp, ros::Time::now()); + predict_between_timestamps(received_pose_stamp, this->get_clock()->now()); } - else // After initialize + else { - predict_between_timestamps(last_pose_stamp_, ros::Time::now()); + // After initialize + predict_between_timestamps(last_pose_stamp_, this->get_clock()->now()); } - const geometry_msgs::PoseWithCovariance p = get_pose_msg_from_state(); - nav_msgs::Odometry estimated_pose; + const geometry_msgs::msg::PoseWithCovariance p = get_pose_msg_from_state(); + nav_msgs::msg::Odometry estimated_pose; estimated_pose.header.frame_id = map_frame_id_; estimated_pose.header.stamp = msg->header.stamp; estimated_pose.child_frame_id = robot_frame_id_; estimated_pose.pose = p; - estimated_pose_pub_.publish(estimated_pose); + estimated_pose_pub_->publish(estimated_pose); // Erase queue before received pose - std::vector::iterator itr_odom = odom_queue_.begin(); - std::vector::iterator itr_imu = imu_queue_.begin(); + std::vector::iterator itr_odom = odom_queue_.begin(); + std::vector::iterator itr_imu = imu_queue_.begin(); while (itr_odom != odom_queue_.end()) { - if (itr_odom->header.stamp < received_pose_stamp) + if (rclcpp::Time(itr_odom->header.stamp) < received_pose_stamp) + { odom_queue_.erase(itr_odom); + } else + { itr_odom++; + } } while (itr_imu != imu_queue_.end()) { - if (itr_imu->header.stamp < received_pose_stamp) + if (rclcpp::Time(itr_imu->header.stamp) < received_pose_stamp) + { imu_queue_.erase(itr_imu); + } else + { itr_imu++; + } } } -void NDTOdomIntegrator::odom_callback(const nav_msgs::OdometryConstPtr& msg) +void NDTOdomIntegrator::odom_callback(const nav_msgs::msg::Odometry::ConstSharedPtr& msg) { if (enable_odom_tf_) { - geometry_msgs::TransformStamped odom_to_robot_tf; + geometry_msgs::msg::TransformStamped odom_to_robot_tf; odom_to_robot_tf.header = msg->header; odom_to_robot_tf.child_frame_id = msg->child_frame_id; odom_to_robot_tf.transform.translation.x = msg->pose.pose.position.x; @@ -175,34 +185,32 @@ void NDTOdomIntegrator::odom_callback(const nav_msgs::OdometryConstPtr& msg) if (map_frame_id_.empty()) { - ROS_ERROR_THROTTLE(3.0, "frame_id is empty"); + RCLCPP_ERROR_THROTTLE(this->get_logger(), *this->get_clock(), 3000, "frame_id is empty"); return; } - const ros::Time stamp = msg->header.stamp; + const rclcpp::Time stamp = msg->header.stamp; odom_frame_id_ = msg->header.frame_id; robot_frame_id_ = msg->child_frame_id; - if (last_odom_stamp_ != ros::Time(0)) + if (last_odom_stamp_ != rclcpp::Time(0)) { - const double dt = (stamp - last_odom_stamp_).toSec(); - const Eigen::Vector3d dp = - { - dt * msg->twist.twist.linear.x, - dt * msg->twist.twist.linear.y, - dt * msg->twist.twist.linear.z, - }; + const double dt = (stamp - last_odom_stamp_).seconds(); + const Eigen::Vector3d dp = { + dt * msg->twist.twist.linear.x, + dt * msg->twist.twist.linear.y, + dt * msg->twist.twist.linear.z, + }; predict_by_odom(dp); - const geometry_msgs::PoseWithCovariance p = get_pose_msg_from_state(); - nav_msgs::Odometry estimated_pose; + const geometry_msgs::msg::PoseWithCovariance p = get_pose_msg_from_state(); + nav_msgs::msg::Odometry estimated_pose; estimated_pose.header.frame_id = map_frame_id_; estimated_pose.header.stamp = msg->header.stamp; estimated_pose.child_frame_id = robot_frame_id_; estimated_pose.pose = p; - estimated_pose_pub_.publish(estimated_pose); + estimated_pose_pub_->publish(estimated_pose); if (enable_tf_) { - publish_map_to_odom_tf(estimated_pose.header.stamp, - estimated_pose.pose.pose); + publish_map_to_odom_tf(estimated_pose.header.stamp, estimated_pose.pose.pose); } } else @@ -213,23 +221,22 @@ void NDTOdomIntegrator::odom_callback(const nav_msgs::OdometryConstPtr& msg) odom_queue_.push_back(*msg); } -void NDTOdomIntegrator::imu_callback(const sensor_msgs::ImuConstPtr& msg) +void NDTOdomIntegrator::imu_callback(const sensor_msgs::msg::Imu::ConstSharedPtr& msg) { if (map_frame_id_.empty()) { - ROS_ERROR_THROTTLE(3.0, "frame_id is empty"); + RCLCPP_ERROR_THROTTLE(this->get_logger(), *this->get_clock(), 3000, "frame_id is empty"); return; } - const ros::Time stamp = msg->header.stamp; - if (last_imu_stamp_ != ros::Time(0)) + const rclcpp::Time stamp = msg->header.stamp; + if (last_imu_stamp_ != rclcpp::Time(0)) { - const double dt = (stamp - last_imu_stamp_).toSec(); - const Eigen::Vector3d dr = - { - dt * msg->angular_velocity.x, - dt * msg->angular_velocity.y, - dt * msg->angular_velocity.z, - }; + const double dt = (stamp - last_imu_stamp_).seconds(); + const Eigen::Vector3d dr = { + dt * msg->angular_velocity.x, + dt * msg->angular_velocity.y, + dt * msg->angular_velocity.z, + }; predict_by_imu(dr); } else @@ -240,30 +247,30 @@ void NDTOdomIntegrator::imu_callback(const sensor_msgs::ImuConstPtr& msg) imu_queue_.push_back(*msg); } -void NDTOdomIntegrator::map_callback( - const sensor_msgs::PointCloud2ConstPtr& msg) +void NDTOdomIntegrator::map_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg) { map_frame_id_ = msg->header.frame_id; - ROS_INFO_STREAM("frame_id is set as " << map_frame_id_); + RCLCPP_INFO_STREAM(this->get_logger(), "frame_id is set as " << map_frame_id_); } void NDTOdomIntegrator::init_pose_callback( - const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg) + const geometry_msgs::msg::PoseWithCovarianceStamped::ConstSharedPtr& msg) { - ROS_INFO("received init pose"); + RCLCPP_INFO(this->get_logger(), "received init pose"); if (map_frame_id_.empty()) { - ROS_ERROR_THROTTLE(3.0, "map has not been received"); + RCLCPP_ERROR_THROTTLE( + this->get_logger(), *this->get_clock(), 3000, "map has not been received"); return; } - geometry_msgs::PoseWithCovarianceStamped pose_in_map; + geometry_msgs::msg::PoseWithCovarianceStamped pose_in_map; try { - tf_->transform(*msg, pose_in_map, map_frame_id_, ros::Duration(0.1)); + tf_->transform(*msg, pose_in_map, map_frame_id_, tf2::durationFromSec(0.1)); } catch (tf2::TransformException& ex) { - ROS_WARN_STREAM_THROTTLE(3.0, ex.what()); + RCLCPP_WARN_STREAM_THROTTLE(this->get_logger(), *this->get_clock(), 3000, ex.what()); return; } double roll, pitch, yaw; @@ -271,16 +278,15 @@ void NDTOdomIntegrator::init_pose_callback( tf2::fromMsg(pose_in_map.pose.pose.orientation, q); tf2::Matrix3x3 rot(q); rot.getRPY(roll, pitch, yaw); - initialize_state(pose_in_map.pose.pose.position.x, - pose_in_map.pose.pose.position.y, - pose_in_map.pose.pose.position.z, roll, pitch, yaw); + initialize_state( + pose_in_map.pose.pose.position.x, pose_in_map.pose.pose.position.y, + pose_in_map.pose.pose.position.z, roll, pitch, yaw); last_pose_stamp_ = msg->header.stamp; - ROS_INFO_STREAM("pose: " << x_.transpose()); + RCLCPP_INFO_STREAM(this->get_logger(), "pose: " << x_.transpose()); } -void NDTOdomIntegrator::initialize_state(double x, double y, double z, - double roll, double pitch, - double yaw) +void NDTOdomIntegrator::initialize_state( + double x, double y, double z, double roll, double pitch, double yaw) { x_ = Eigen::VectorXd::Zero(state_dim_); x_ << x, y, z, roll, pitch, yaw; @@ -288,16 +294,17 @@ void NDTOdomIntegrator::initialize_state(double x, double y, double z, p_.block(0, 0, position_dim_, position_dim_) = init_sigma_position_ * p_.block(0, 0, position_dim_, position_dim_); p_.block(position_dim_, position_dim_, orientation_dim_, orientation_dim_) = - init_sigma_orientation_ * p_.block(position_dim_, position_dim_, - orientation_dim_, orientation_dim_); + init_sigma_orientation_ * + p_.block(position_dim_, position_dim_, orientation_dim_, orientation_dim_); - const geometry_msgs::PoseWithCovariance p = get_pose_msg_from_state(); - nav_msgs::Odometry estimated_pose; + const geometry_msgs::msg::PoseWithCovariance p = get_pose_msg_from_state(); + nav_msgs::msg::Odometry estimated_pose; estimated_pose.header.frame_id = map_frame_id_; - estimated_pose.header.stamp = ros::Time::now(); + // estimated_pose.header.stamp = rclcpp::Time::now(); + estimated_pose.header.stamp = this->get_clock()->now(); estimated_pose.child_frame_id = robot_frame_id_; estimated_pose.pose = p; - estimated_pose_pub_.publish(estimated_pose); + estimated_pose_pub_->publish(estimated_pose); last_pose_ = x_; last_covariance_ = p_; @@ -307,10 +314,9 @@ void NDTOdomIntegrator::initialize_state(double x, double y, double z, imu_queue_.reserve(queue_capacity_); } -geometry_msgs::PoseWithCovariance -NDTOdomIntegrator::get_pose_msg_from_state(void) +geometry_msgs::msg::PoseWithCovariance NDTOdomIntegrator::get_pose_msg_from_state(void) { - geometry_msgs::PoseWithCovariance p; + geometry_msgs::msg::PoseWithCovariance p; p.pose.position.x = x_(0); p.pose.position.y = x_(1); p.pose.position.z = x_(2); @@ -339,28 +345,24 @@ void NDTOdomIntegrator::predict_by_odom(const Eigen::Vector3d& dp) const double crz = cos(rz); const double srz = sin(rz); - // ROS_INFO("predict by odom"); - // ROS_INFO_STREAM("x: " << x_.transpose()); - // ROS_INFO_STREAM("p:\n" << p_); + // RCLCPP_INFO(this->get_logger(), "predict by odom"); + // RCLCPP_INFO_STREAM(this->get_logger(), "x: " << x_.transpose()); + // RCLCPP_INFO_STREAM(this->get_logger(), "p:\n" << p_); // f(x) Eigen::VectorXd f(state_dim_); - f.segment(0, position_dim_) = - x_.segment(0, position_dim_) + get_rotation_matrix(rx, ry, rz) * dp; - f.segment(position_dim_, orientation_dim_) = - x_.segment(position_dim_, orientation_dim_); + f.segment(0, position_dim_) = x_.segment(0, position_dim_) + get_rotation_matrix(rx, ry, rz) * dp; + f.segment(position_dim_, orientation_dim_) = x_.segment(position_dim_, orientation_dim_); // jf(x) Eigen::MatrixXd jf = Eigen::MatrixXd::Zero(state_dim_, state_dim_); jf.block(0, 0, position_dim_, position_dim_) = Eigen::Matrix3d::Identity(); jf(0, 3) = dp(1) * (crx * sry * crz) + dp(2) * (-srx * sry * crz + crx * srz); - jf(0, 4) = dp(0) * (-sry * crz) + dp(1) * (srx * cry + crz) + - dp(2) * (-srx * sry * crz + crx * crz); + jf(0, 4) = + dp(0) * (-sry * crz) + dp(1) * (srx * cry + crz) + dp(2) * (-srx * sry * crz + crx * crz); jf(0, 5) = dp(0) * (-crx * srz) + dp(1) * (-srx * sry * srz - crx * crz) + dp(2) * (-crx * sry * srz + srx * crz); - jf(1, 3) = dp(1) * (crx * sry * srz - srx * crz) + - dp(2) * (-srx * sry * srz - crx * crz); - jf(1, 4) = dp(0) * (-sry * srz) + dp(1) * (srx * cry * srz) + - dp(2) * (crx * cry * srz); + jf(1, 3) = dp(1) * (crx * sry * srz - srx * crz) + dp(2) * (-srx * sry * srz - crx * crz); + jf(1, 4) = dp(0) * (-sry * srz) + dp(1) * (srx * cry * srz) + dp(2) * (crx * cry * srz); jf(1, 5) = dp(0) * (cry * crz) + dp(1) * (srx * sry * crz - crx * srz) + dp(2) * (crx * sry * crz + srx * srz); jf(2, 3) = dp(1) * (crx * cry) + dp(2) * (-srx * cry); @@ -371,28 +373,28 @@ void NDTOdomIntegrator::predict_by_odom(const Eigen::Vector3d& dp) x_ = f; p_ = jf * p_ * jf.transpose() + q_odom_; - // ROS_INFO_STREAM("x: " << x_.transpose()); - // ROS_INFO_STREAM("p:\n" << p_); + // RCLCPP_INFO_STREAM(this->get_logger(), "x: " << x_.transpose()); + // RCLCPP_INFO_STREAM(this->get_logger(), "p:\n" << p_); } void NDTOdomIntegrator::predict_by_imu(const Eigen::Vector3d& dr) { const double rx = x_(3); const double ry = x_(4); - const double rz = x_(5); + // const double rz = x_(5); const double crx = cos(rx); const double srx = sin(rx); - const double t_rx = tan(rx); + // const double t_rx = tan(rx); const double cry = cos(ry); const double sry = sin(ry); const double t_ry = tan(ry); - const double crz = cos(rz); - const double srz = sin(rz); - const double t_rz = tan(rz); + // const double crz = cos(rz); + // const double srz = sin(rz); + // const double t_rz = tan(rz); - // ROS_INFO("predict by imu"); - // ROS_INFO_STREAM("x: " << x_.transpose()); - // ROS_INFO_STREAM("p:\n" << p_); + // RCLCPP_INFO(this->get_logger(), "predict by imu"); + // RCLCPP_INFO_STREAM(this->get_logger(), "x: " << x_.transpose()); + // RCLCPP_INFO_STREAM(this->get_logger(), "p:\n" << p_); Eigen::Matrix3d rot; rot << 1.0, srx * t_ry, crx * t_ry, 0.0, crx, -srx, 0.0, srx / cry, crx / cry; @@ -421,168 +423,189 @@ void NDTOdomIntegrator::predict_by_imu(const Eigen::Vector3d& dr) x_ = f; p_ = jf * p_ * jf.transpose() + q_imu_; - // ROS_INFO_STREAM("x: " << x_.transpose()); - // ROS_INFO_STREAM("p:\n" << p_); + // RCLCPP_INFO_STREAM(this->get_logger(), "x: " << x_.transpose()); + // RCLCPP_INFO_STREAM(this->get_logger(), "p:\n" << p_); } -void NDTOdomIntegrator::predict_between_timestamps(const ros::Time begin_stamp, const ros::Time end_stamp) +void NDTOdomIntegrator::predict_between_timestamps( + const rclcpp::Time begin_stamp, const rclcpp::Time end_stamp) { - std::vector::iterator itr_odom = odom_queue_.begin(); - std::vector::iterator itr_imu = imu_queue_.begin(); + std::vector::iterator itr_odom = odom_queue_.begin(); + std::vector::iterator itr_imu = imu_queue_.begin(); if (odom_queue_.size() >= 2) { - while ((itr_odom+1) != odom_queue_.end()) + while ((itr_odom + 1) != odom_queue_.end()) { - if (begin_stamp <= itr_odom->header.stamp && itr_odom->header.stamp < end_stamp) + if ( + begin_stamp <= rclcpp::Time(itr_odom->header.stamp) && + rclcpp::Time(itr_odom->header.stamp) < end_stamp) { - const double dt = ((itr_odom+1)->header.stamp - itr_odom->header.stamp).toSec(); - const Eigen::Vector3d dp = - { - dt * itr_odom->twist.twist.linear.x, - dt * itr_odom->twist.twist.linear.y, - dt * itr_odom->twist.twist.linear.z, - }; + const double dt = + (rclcpp::Time((itr_odom + 1)->header.stamp) - rclcpp::Time(itr_odom->header.stamp)) + .seconds(); + const Eigen::Vector3d dp = { + dt * itr_odom->twist.twist.linear.x, + dt * itr_odom->twist.twist.linear.y, + dt * itr_odom->twist.twist.linear.z, + }; predict_by_odom(dp); } - itr_odom++; + itr_odom++; } } if (imu_queue_.size() >= 2) { - while ((itr_imu+1) != imu_queue_.end()) + while ((itr_imu + 1) != imu_queue_.end()) { - if (begin_stamp <= itr_imu->header.stamp && itr_imu->header.stamp < end_stamp) + if ( + begin_stamp <= rclcpp::Time(itr_imu->header.stamp) && + rclcpp::Time(itr_imu->header.stamp) < end_stamp) { - const double dt = ((itr_imu+1)->header.stamp - itr_imu->header.stamp).toSec(); - const Eigen::Vector3d dr = - { - dt * itr_imu->angular_velocity.x, - dt * itr_imu->angular_velocity.y, - dt * itr_imu->angular_velocity.z, - }; + const double dt = + (rclcpp::Time((itr_imu + 1)->header.stamp) - rclcpp::Time(itr_imu->header.stamp)) + .seconds(); + const Eigen::Vector3d dr = { + dt * itr_imu->angular_velocity.x, + dt * itr_imu->angular_velocity.y, + dt * itr_imu->angular_velocity.z, + }; predict_by_imu(dr); } - itr_imu++; + itr_imu++; } } } void NDTOdomIntegrator::update_by_ndt_pose(const Eigen::VectorXd& pose) { - ROS_INFO("update by ndt"); - ROS_INFO_STREAM("x: " << x_.transpose()); - ROS_INFO_STREAM("p:\n" - << p_); + RCLCPP_INFO(this->get_logger(), "update by ndt"); + RCLCPP_INFO_STREAM(this->get_logger(), "x: " << x_.transpose()); + RCLCPP_INFO_STREAM(this->get_logger(), "p:\n" + << p_); const Eigen::VectorXd z = pose; - ROS_INFO_STREAM("z:\n" - << z.transpose()); + RCLCPP_INFO_STREAM(this->get_logger(), "z:\n" + << z.transpose()); const Eigen::MatrixXd jh = Eigen::MatrixXd::Identity(state_dim_, state_dim_); Eigen::VectorXd y = z - x_; for (unsigned int i = position_dim_; i < state_dim_; ++i) { y(i) = atan2(sin(y(i)), cos(y(i))); } - ROS_INFO_STREAM("y:\n" - << y.transpose()); + RCLCPP_INFO_STREAM(this->get_logger(), "y:\n" + << y.transpose()); const Eigen::MatrixXd s = jh * p_ * jh.transpose() + r_; - ROS_INFO_STREAM("s:\n" - << s); + RCLCPP_INFO_STREAM(this->get_logger(), "s:\n" + << s); const Eigen::MatrixXd k = p_ * jh.transpose() * s.inverse(); - ROS_INFO_STREAM("k:\n" - << k); + RCLCPP_INFO_STREAM(this->get_logger(), "k:\n" + << k); x_ = x_ + k * y; for (unsigned int i = position_dim_; i < state_dim_; ++i) { x_(i) = atan2(sin(x_(i)), cos(x_(i))); } p_ = (Eigen::MatrixXd::Identity(state_dim_, state_dim_) - k * jh) * p_; - ROS_INFO_STREAM("x: " << x_.transpose()); - ROS_INFO_STREAM("p:\n" - << p_); + RCLCPP_INFO_STREAM(this->get_logger(), "x: " << x_.transpose()); + RCLCPP_INFO_STREAM(this->get_logger(), "p:\n" + << p_); } -Eigen::Matrix3d -NDTOdomIntegrator::get_rotation_matrix(double roll, double pitch, double yaw) +Eigen::Matrix3d NDTOdomIntegrator::get_rotation_matrix(double roll, double pitch, double yaw) { - const Eigen::Matrix3d rot = - Eigen::Quaterniond(Eigen::AngleAxisd(roll, Eigen::Vector3d::UnitX()) * - Eigen::AngleAxisd(pitch, Eigen::Vector3d::UnitY()) * - Eigen::AngleAxisd(yaw, Eigen::Vector3d::UnitZ())) - .matrix(); + const Eigen::Matrix3d rot = Eigen::Quaterniond( + Eigen::AngleAxisd(roll, Eigen::Vector3d::UnitX()) * + Eigen::AngleAxisd(pitch, Eigen::Vector3d::UnitY()) * + Eigen::AngleAxisd(yaw, Eigen::Vector3d::UnitZ())) + .matrix(); return rot; } bool NDTOdomIntegrator::is_mahalanobis_distance_gate( - const double mahalanobis_distance_threshold, const Eigen::VectorXd& ndt_pose, - const Eigen::VectorXd& last_pose, const Eigen::MatrixXd& cov) + const double mahalanobis_distance_threshold, const Eigen::VectorXd& ndt_pose, + const Eigen::VectorXd& last_pose, const Eigen::MatrixXd& cov) { const double mahalanobis_distance = - std::sqrt((ndt_pose - last_pose).transpose() * cov.inverse() * (ndt_pose - last_pose)); + std::sqrt((ndt_pose - last_pose).transpose() * cov.inverse() * (ndt_pose - last_pose)); if (mahalanobis_distance > mahalanobis_distance_threshold) { - ROS_ERROR_STREAM("Mahalanobis_distance distance is over the threshold: " << mahalanobis_distance); + RCLCPP_ERROR_STREAM( + this->get_logger(), + "Mahalanobis_distance distance is over the threshold: " << mahalanobis_distance); return false; } else { - // ROS_WARN_STREAM("Mahalanobis_distance distance is under the threshold: " << mahalanobis_distance); + RCLCPP_WARN_STREAM( + this->get_logger(), + "Mahalanobis_distance distance is under the threshold: " << mahalanobis_distance); return true; } } -bool NDTOdomIntegrator::is_covariance_large(const double pose_covariance_threshold, - const double direction_covariance_threshold, const Eigen::MatrixXd& covariance) +bool NDTOdomIntegrator::is_covariance_large( + const double pose_covariance_threshold, const double direction_covariance_threshold, + const Eigen::MatrixXd& /*covariance*/) { - const double variance_x = p_(0, 0); + const double variance_x = p_(0, 0); const double covariance_xy = p_(0, 1); - const double variance_y = p_(1, 1); - const double variance_yaw = p_(5, 5); + const double variance_y = p_(1, 1); + const double variance_yaw = p_(5, 5); - if ((variance_x > pose_covariance_threshold) || (covariance_xy > pose_covariance_threshold) - || (variance_y > pose_covariance_threshold) || (variance_yaw > direction_covariance_threshold)) + if ( + (variance_x > pose_covariance_threshold) || (covariance_xy > pose_covariance_threshold) || + (variance_y > pose_covariance_threshold) || (variance_yaw > direction_covariance_threshold)) { if (variance_x > pose_covariance_threshold) - ROS_ERROR_STREAM("variance_x is over the threshold: " << variance_x); + { + RCLCPP_ERROR_STREAM(this->get_logger(), "variance_x is over the threshold: " << variance_x); + } if (covariance_xy > pose_covariance_threshold) - ROS_ERROR_STREAM("covariance_xy is over the threshold: " << covariance_xy); + { + RCLCPP_ERROR_STREAM( + this->get_logger(), "covariance_xy is over the threshold: " << covariance_xy); + } if (variance_y > pose_covariance_threshold) - ROS_ERROR_STREAM("variance_y is over the threshold: " << variance_y); + { + RCLCPP_ERROR_STREAM(this->get_logger(), "variance_y is over the threshold: " << variance_y); + } if (variance_yaw > direction_covariance_threshold) - ROS_ERROR_STREAM("variance_yaw is over the threshold: " << variance_yaw); + { + RCLCPP_ERROR_STREAM( + this->get_logger(), "variance_yaw is over the threshold: " << variance_yaw); + } return true; } else { - // ROS_WARN_STREAM("Covariance is under the threshold."); + // RCLCPP_WARN_STREAM(this->get_logger(), "Covariance is under the threshold."); return false; } } void NDTOdomIntegrator::publish_map_to_odom_tf( - const ros::Time& stamp, const geometry_msgs::Pose& pose) + const rclcpp::Time& stamp, const geometry_msgs::msg::Pose& pose) { tf2::Transform map_to_robot_tf; tf2::convert(pose, map_to_robot_tf); - geometry_msgs::PoseStamped robot_to_map_pose; + geometry_msgs::msg::PoseStamped robot_to_map_pose; robot_to_map_pose.header.frame_id = robot_frame_id_; robot_to_map_pose.header.stamp = stamp; tf2::toMsg(map_to_robot_tf.inverse(), robot_to_map_pose.pose); - geometry_msgs::PoseStamped odom_to_map_pose; + geometry_msgs::msg::PoseStamped odom_to_map_pose; try { - tf_->transform(robot_to_map_pose, odom_to_map_pose, odom_frame_id_, - ros::Duration(0.1)); + tf_->transform(robot_to_map_pose, odom_to_map_pose, odom_frame_id_, tf2::durationFromSec(0.1)); } catch (tf2::TransformException& ex) { - ROS_WARN_STREAM_THROTTLE(3.0, ex.what()); + RCLCPP_WARN_STREAM_THROTTLE(this->get_logger(), *this->get_clock(), 3000, ex.what()); return; } tf2::Transform odom_to_map_tf; tf2::convert(odom_to_map_pose.pose, odom_to_map_tf); - geometry_msgs::TransformStamped map_to_odom_tf; + geometry_msgs::msg::TransformStamped map_to_odom_tf; map_to_odom_tf.header.stamp = stamp; map_to_odom_tf.header.frame_id = map_frame_id_; map_to_odom_tf.child_frame_id = odom_frame_id_; @@ -590,9 +613,4 @@ void NDTOdomIntegrator::publish_map_to_odom_tf( tfb_->sendTransform(map_to_odom_tf); } -void NDTOdomIntegrator::process(void) -{ - ros::spin(); -} - } // namespace ndt_localizer diff --git a/src/ndt_odom_integrator_node.cpp b/src/ndt_odom_integrator_node.cpp index d4abd84..e3f4081 100644 --- a/src/ndt_odom_integrator_node.cpp +++ b/src/ndt_odom_integrator_node.cpp @@ -1,11 +1,12 @@ // Copyright 2023 amsl -#include "ndt_localizer/ndt_odom_integrator.h" +#include "ndt_localizer/ndt_odom_integrator.hpp" -int main(int argc, char** argv) +int main(int argc, char ** argv) { - ros::init(argc, argv, "ndt_odom_integrator"); - ndt_localizer::NDTOdomIntegrator ndt_odom_integrator; - ndt_odom_integrator.process(); + rclcpp::init(argc, argv); + const auto node = std::make_shared(); + rclcpp::spin(node); + rclcpp::shutdown(); return 0; }