Repository: rsasaki0109/lidar_localization_ros2
Branch: main
Commit: 7204195a10ad
Files: 13
Total size: 63.6 KB
Directory structure:
gitextract_mandirxt/
├── .github/
│ └── workflows/
│ └── main.yml
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── include/
│ └── lidar_localization/
│ ├── lidar_localization_component.hpp
│ └── lidar_undistortion.hpp
├── launch/
│ └── lidar_localization.launch.py
├── package.xml
├── param/
│ └── localization.yaml
├── rviz/
│ └── localization.rviz
└── src/
├── lidar_localization_component.cpp
└── lidar_localization_node.cpp
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/main.yml
================================================
name: build
on:
pull_request:
branches:
- main
jobs:
humble_build:
name: Build on Humble
runs-on: ubuntu-22.04
container: ros:humble
steps:
- uses: actions/checkout@v1
with:
submodules: true
- name: Copy repository
run: |
mkdir -p ~/ros2_ws/src/lidar_localization_ros2
cp -rf . ~/ros2_ws/src/lidar_localization_ros2
- name: Install dependencies
run: |
source /opt/ros/humble/setup.bash
sudo apt install -y python3-rosdep
apt update
rosdep update
cd ~/ros2_ws/src
git clone https://github.com/rsasaki0109/ndt_omp_ros2.git -b humble
rosdep install -r -y --from-paths . --ignore-src
shell: bash
- name: Build packages
run: |
source /opt/ros/humble/setup.bash
sudo apt install python3-colcon-common-extensions
cd ~/ros2_ws
colcon build
source ~/ros2_ws/install/setup.bash
shell: bash
jazzy_build:
name: Build on Jazzy
runs-on: ubuntu-24.04
container: ros:jazzy
steps:
- uses: actions/checkout@v1
with:
submodules: true
- name: Copy repository
run: |
mkdir -p ~/ros2_ws/src/lidar_localization_ros2
cp -rf . ~/ros2_ws/src/lidar_localization_ros2
- name: Install dependencies
run: |
source /opt/ros/jazzy/setup.bash
sudo apt install -y python3-rosdep
apt update
rosdep update
cd ~/ros2_ws/src
git clone https://github.com/rsasaki0109/ndt_omp_ros2.git -b humble
rosdep install -r -y --from-paths . --ignore-src
shell: bash
- name: Build packages
run: |
source /opt/ros/jazzy/setup.bash
sudo apt install python3-colcon-common-extensions
cd ~/ros2_ws
colcon build
source ~/ros2_ws/install/setup.bash
shell: bash
================================================
FILE: .gitignore
================================================
.vscode
build
map/bin_tc-2017-10-15--ndmap.pcd
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.5)
project(lidar_localization_ros2)
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
SET(CMAKE_CXX_FLAGS "-O2 -g ${CMAKE_CXX_FLAGS}")
find_package(ament_cmake REQUIRED)
find_package(rclcpp_lifecycle REQUIRED)
find_package(lifecycle_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(nav_msgs REQUIRED)
find_package(tf2_ros REQUIRED)
find_package(tf2_geometry_msgs REQUIRED)
find_package(tf2_sensor_msgs REQUIRED)
find_package(tf2_eigen REQUIRED)
find_package(pcl_conversions REQUIRED)
find_package(PCL REQUIRED)
find_package(ndt_omp_ros2 REQUIRED)
find_package(OpenMP)
if (OPENMP_FOUND)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
endif()
add_library(lidar_localization_component SHARED
src/lidar_localization_component.cpp
)
ament_target_dependencies(lidar_localization_component
rclcpp
tf2_ros
tf2_geometry_msgs
tf2_sensor_msgs
tf2_eigen
geometry_msgs
sensor_msgs
nav_msgs
pcl_conversions
ndt_omp_ros2
)
include_directories(
include
${PCL_INCLUDE_DIRS}
${lifecycle_msgs_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS})
add_executable(lidar_localization_node src/lidar_localization_node.cpp)
target_link_libraries(lidar_localization_node
lidar_localization_component
${PCL_LIBRARIES}
${rclcpp_lifecycle_LIBRARIES}
${std_msgs_LIBRARIES}
)
link_directories(
${PCL_LIBRARY_DIRS}
)
add_definitions(${PCL_DEFINITIONS})
ament_export_libraries(lidar_localization_component)
install(TARGETS
lidar_localization_node
DESTINATION lib/${PROJECT_NAME})
install(TARGETS
lidar_localization_component
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
install(DIRECTORY
launch
param
DESTINATION share/${PROJECT_NAME}/
)
ament_package()
================================================
FILE: LICENSE
================================================
BSD 2-Clause License
Copyright (c) 2020, Ryohei Sasaki
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# lidar_localization_ros2
A ROS2 package of 3D LIDAR-based Localization.
<img src="./images/path.png" width="640px">
Green: path, Red: map
(the 5x5 grids in size of 50m × 50m)
## Requirements
- [ndt_omp_ros2](https://github.com/rsasaki0109/ndt_omp_ros2.git)
## IO
- input
/cloud (sensor_msgs/PointCloud2)
/map (sensor_msgs/PointCloud2)
/initialpose (geometry_msgs/PoseStamed)(when `set_initial_pose` is false)
/odom (nav_msgs/Odometry)(optional)
/imu (sensor_msgs/Imu)(optional)
- output
/pcl_pose (geometry_msgs/PoseStamped)
/path (nav_msgs/Path)
/initial_map (sensor_msgs/PointCloud2)(when `use_pcd_map` is true)
## params
|Name|Type|Default value|Description|
|---|---|---|---|
|registration_method|string|"NDT_OMP"|"NDT" or "GICP" or "NDT_OMP" or "GICP_OMP"|
|score_threshold|double|2.0|registration score threshold|
|ndt_resolution|double|2.0|resolution size of voxels[m]|
|ndt_step_size|double|0.1|step_size maximum step length[m]|
|ndt_num_threads|int|4|threads using NDT_OMP(if `0` is set, maximum alloawble threads are used.)|
|transform_epsilon|double|0.01|transform epsilon to stop iteration in registration|
|voxel_leaf_size|double|0.2|down sample size of input cloud[m]|
|scan_max_range|double|100.0|max range of input cloud[m]|
|scan_min_range|double|1.0|min range of input cloud[m]|
|scan_periad|double|0.1|scan period of input cloud[sec]|
|use_pcd_map|bool|false|whether pcd_map is used or not|
|map_path|string|"/map/map.pcd"|pcd_map or ply_map file path|
|set_initial_pose|bool|false|whether or not to set the default value in the param file|
|initial_pose_x|double|0.0|x-coordinate of the initial pose value[m]|
|initial_pose_y|double|0.0|y-coordinate of the initial pose value[m]|
|initial_pose_z|double|0.0|z-coordinate of the initial pose value[m]|
|initial_pose_qx|double|0.0|Quaternion x of the initial pose value|
|initial_pose_qy|double|0.0|Quaternion y of the initial pose value|
|initial_pose_qz|double|0.0|Quaternion z of the initial pose value|
|initial_pose_qw|double|1.0|Quaternion w of the initial pose value|
|use_odom|bool|false|whether odom is used or not for initial attitude in point cloud registration|
|use_imu|bool|false|whether 9-axis imu is used or not for point cloud distortion correction|
|enable_debug|bool|false|whether debug is done or not|
|enable_timer_publishing|bool|false|if true, publish tf and pose on a set timer frequency|
|pose_publish_frequency|double|10.0|publishing frequency if enable_timer_publishing is true|
## demo
demo data(ROS1) by Tier IV(The link has changed and is now broken.)
https://data.tier4.jp/rosbag_details/?id=212
To use ros1 rosbag , use [rosbags](https://pypi.org/project/rosbags/).
The Velodyne VLP-16 was used in this data.
Before running, put `bin_tc-2017-10-15-ndmap.pcd` into your `map` directory and
edit the `map_path` parameter of `localization.yaml` in the `param` directory accordingly.
```
rviz2 -d src/lidar_localization_ros2/rviz/localization.rviz
ros2 launch lidar_localization_ros2 lidar_localization.launch.py
ros2 bag play tc_2017-10-15-15-34-02_free_download/
```
<img src="./images/path.png" width="640px">
Green: path, Red: map
(the 5x5 grids in size of 50m × 50m)
================================================
FILE: include/lidar_localization/lidar_localization_component.hpp
================================================
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <pcl/registration/ndt.h>
#include <pcl/registration/gicp.h>
#include <pcl/io/ply_io.h>
#include <tf2/transform_datatypes.h>
#include <tf2/utils.h>
#include <tf2_ros/buffer.h>
#include <tf2_ros/transform_listener.h>
#include <tf2_ros/transform_broadcaster.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
#include <tf2_sensor_msgs/tf2_sensor_msgs.hpp>
#include <tf2_eigen/tf2_eigen.hpp>
#include <pcl_conversions/pcl_conversions.h>
#include "lifecycle_msgs/msg/transition.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_lifecycle/lifecycle_node.hpp"
#include "rclcpp_lifecycle/lifecycle_publisher.hpp"
#include "geometry_msgs/msg/pose_stamped.hpp"
#include "sensor_msgs/msg/point_cloud2.hpp"
#include "sensor_msgs/msg/imu.hpp"
#include "nav_msgs/msg/odometry.hpp"
#include "nav_msgs/msg/path.hpp"
#include <pclomp/ndt_omp.h>
#include <pclomp/ndt_omp_impl.hpp>
#include <pclomp/voxel_grid_covariance_omp.h>
#include <pclomp/voxel_grid_covariance_omp_impl.hpp>
#include <pclomp/gicp_omp.h>
#include <pclomp/gicp_omp_impl.hpp>
#include "lidar_localization/lidar_undistortion.hpp"
using namespace std::chrono_literals;
class PCLLocalization : public rclcpp_lifecycle::LifecycleNode
{
public:
explicit PCLLocalization(const rclcpp::NodeOptions & options);
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
CallbackReturn on_configure(const rclcpp_lifecycle::State &);
CallbackReturn on_activate(const rclcpp_lifecycle::State &);
CallbackReturn on_deactivate(const rclcpp_lifecycle::State &);
CallbackReturn on_cleanup(const rclcpp_lifecycle::State &);
CallbackReturn on_shutdown(const rclcpp_lifecycle::State & state);
CallbackReturn on_error(const rclcpp_lifecycle::State & state);
void initializeParameters();
void initializePubSub();
void initializeRegistration();
void initialPoseReceived(const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr msg);
void mapReceived(const sensor_msgs::msg::PointCloud2::SharedPtr msg);
void odomReceived(const nav_msgs::msg::Odometry::ConstSharedPtr msg);
void imuReceived(const sensor_msgs::msg::Imu::ConstSharedPtr msg);
void cloudReceived(const sensor_msgs::msg::PointCloud2::ConstSharedPtr msg);
// void gnssReceived();
tf2_ros::TransformBroadcaster broadcaster_;
rclcpp::Clock clock_;
tf2_ros::Buffer tfbuffer_;
tf2_ros::TransformListener tflistener_;
rclcpp::Subscription<geometry_msgs::msg::PoseWithCovarianceStamped>::ConstSharedPtr
initial_pose_sub_;
rclcpp_lifecycle::LifecyclePublisher<geometry_msgs::msg::PoseWithCovarianceStamped>::SharedPtr
pose_pub_;
rclcpp_lifecycle::LifecyclePublisher<nav_msgs::msg::Path>::SharedPtr
path_pub_;
rclcpp_lifecycle::LifecyclePublisher<sensor_msgs::msg::PointCloud2>::SharedPtr
initial_map_pub_;
rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::ConstSharedPtr
map_sub_;
rclcpp::Subscription<nav_msgs::msg::Odometry>::ConstSharedPtr
odom_sub_;
rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::ConstSharedPtr
cloud_sub_;
rclcpp::Subscription<sensor_msgs::msg::Imu>::ConstSharedPtr
imu_sub_;
boost::shared_ptr<pcl::Registration<pcl::PointXYZI, pcl::PointXYZI>> registration_;
pcl::VoxelGrid<pcl::PointXYZI> voxel_grid_filter_;
geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr corrent_pose_with_cov_stamped_ptr_;
nav_msgs::msg::Path::SharedPtr path_ptr_;
sensor_msgs::msg::PointCloud2::ConstSharedPtr last_scan_ptr_;
bool map_recieved_{false};
bool initialpose_recieved_{false};
// parameters
std::string global_frame_id_;
std::string odom_frame_id_;
std::string base_frame_id_;
std::string registration_method_;
double scan_max_range_;
double scan_min_range_;
double scan_period_;
double score_threshold_;
double ndt_resolution_;
double ndt_step_size_;
double transform_epsilon_;
double voxel_leaf_size_;
bool use_pcd_map_{false};
std::string map_path_;
bool set_initial_pose_{false};
double initial_pose_x_;
double initial_pose_y_;
double initial_pose_z_;
double initial_pose_qx_;
double initial_pose_qy_;
double initial_pose_qz_;
double initial_pose_qw_;
bool use_odom_{false};
double last_odom_received_time_;
bool use_imu_{false};
bool enable_debug_{false};
bool enable_map_odom_tf_{false};
int ndt_num_threads_;
int ndt_max_iterations_;
double pose_publish_frequency_;
bool enable_timer_publishing_{false};
// imu
LidarUndistortion lidar_undistortion_;
rclcpp::TimerBase::SharedPtr pose_publish_timer_;
void timerPublishPose();
};
================================================
FILE: include/lidar_localization/lidar_undistortion.hpp
================================================
#ifndef LIDAR_UNDISTORTION_HPP_
#define LIDAR_UNDISTORTION_HPP_
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/common/common.h>
#include <pcl/common/eigen.h>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <iostream>
class LidarUndistortion
{
public:
LidarUndistortion() {}
// Ref:LeGO-LOAM(BSD-3 LICENSE)
// https://github.com/RobustFieldAutonomyLab/LeGO-LOAM/blob/master/LeGO-LOAM/src/featureAssociation.cpp#L431-L459
void getImu(
Eigen::Vector3f angular_velo, Eigen::Vector3f acc, const Eigen::Quaternionf quat,
const double imu_time /*[sec]*/)
{
float roll, pitch, yaw;
Eigen::Affine3f affine(quat);
pcl::getEulerAngles(affine, roll, pitch, yaw);
imu_ptr_last_ = (imu_ptr_last_ + 1) % imu_que_length_;
if ((imu_ptr_last_ + 1) % imu_que_length_ == imu_ptr_front_) {
imu_ptr_front_ = (imu_ptr_front_ + 1) % imu_que_length_;
}
imu_time_[imu_ptr_last_] = imu_time;
imu_roll_[imu_ptr_last_] = roll;
imu_pitch_[imu_ptr_last_] = pitch;
imu_yaw_[imu_ptr_last_] = yaw;
imu_acc_x_[imu_ptr_last_] = acc.x();
imu_acc_y_[imu_ptr_last_] = acc.y();
imu_acc_z_[imu_ptr_last_] = acc.z();
imu_angular_velo_x_[imu_ptr_last_] = angular_velo.x();
imu_angular_velo_y_[imu_ptr_last_] = angular_velo.y();
imu_angular_velo_z_[imu_ptr_last_] = angular_velo.z();
Eigen::Matrix3f rot = quat.toRotationMatrix();
acc = rot * acc;
// angular_velo = rot * angular_velo;
int imu_ptr_back = (imu_ptr_last_ - 1 + imu_que_length_) % imu_que_length_;
double time_diff = imu_time_[imu_ptr_last_] - imu_time_[imu_ptr_back];
if (time_diff < scan_period_) {
imu_shift_x_[imu_ptr_last_] =
imu_shift_x_[imu_ptr_back] + imu_velo_x_[imu_ptr_back] * time_diff + acc(0) * time_diff *
time_diff * 0.5;
imu_shift_y_[imu_ptr_last_] =
imu_shift_y_[imu_ptr_back] + imu_velo_y_[imu_ptr_back] * time_diff + acc(1) * time_diff *
time_diff * 0.5;
imu_shift_z_[imu_ptr_last_] =
imu_shift_z_[imu_ptr_back] + imu_velo_z_[imu_ptr_back] * time_diff + acc(2) * time_diff *
time_diff * 0.5;
imu_velo_x_[imu_ptr_last_] = imu_velo_x_[imu_ptr_back] + acc(0) * time_diff;
imu_velo_y_[imu_ptr_last_] = imu_velo_y_[imu_ptr_back] + acc(1) * time_diff;
imu_velo_z_[imu_ptr_last_] = imu_velo_z_[imu_ptr_back] + acc(2) * time_diff;
imu_angular_rot_x_[imu_ptr_last_] = imu_angular_rot_x_[imu_ptr_back] + angular_velo(0) *
time_diff;
imu_angular_rot_y_[imu_ptr_last_] = imu_angular_rot_y_[imu_ptr_back] + angular_velo(1) *
time_diff;
imu_angular_rot_z_[imu_ptr_last_] = imu_angular_rot_z_[imu_ptr_back] + angular_velo(2) *
time_diff;
}
}
// Ref:LeGO-LOAM(BSD-3 LICENSE)
// https://github.com/RobustFieldAutonomyLab/LeGO-LOAM/blob/master/LeGO-LOAM/src/featureAssociation.cpp#L491-L619
void adjustDistortion(
pcl::PointCloud<pcl::PointXYZI>::Ptr & cloud,
const double scan_time /*[sec]*/)
{
bool half_passed = false;
int cloud_size = cloud->points.size();
float start_ori = -std::atan2(cloud->points[0].y, cloud->points[0].x);
float end_ori = -std::atan2(cloud->points[cloud_size - 1].y, cloud->points[cloud_size - 1].x);
if (end_ori - start_ori > 3 * M_PI) {
end_ori -= 2 * M_PI;
} else if (end_ori - start_ori < M_PI) {
end_ori += 2 * M_PI;
}
float ori_diff = end_ori - start_ori;
Eigen::Vector3f rpy_start, shift_start, velo_start, rpy_cur, shift_cur, velo_cur;
Eigen::Vector3f shift_from_start;
Eigen::Matrix3f r_s_i, r_c;
Eigen::Vector3f adjusted_p;
float ori_h;
for (int i = 0; i < cloud_size; ++i) {
pcl::PointXYZI & p = cloud->points[i];
ori_h = -std::atan2(p.y, p.x);
if (!half_passed) {
if (ori_h < start_ori - M_PI * 0.5) {
ori_h += 2 * M_PI;
} else if (ori_h > start_ori + M_PI * 1.5) {
ori_h -= 2 * M_PI;
}
if (ori_h - start_ori > M_PI) {
half_passed = true;
}
} else {
ori_h += 2 * M_PI;
if (ori_h < end_ori - 1.5 * M_PI) {
ori_h += 2 * M_PI;
} else if (ori_h > end_ori + 0.5 * M_PI) {
ori_h -= 2 * M_PI;
}
}
float rel_time = (ori_h - start_ori) / ori_diff * scan_period_;
if (imu_ptr_last_ > 0) {
imu_ptr_front_ = imu_ptr_last_iter_;
while (imu_ptr_front_ != imu_ptr_last_) {
if (scan_time + rel_time > imu_time_[imu_ptr_front_]) {
break;
}
imu_ptr_front_ = (imu_ptr_front_ + 1) % imu_que_length_;
}
if (scan_time + rel_time > imu_time_[imu_ptr_front_]) {
rpy_cur(0) = imu_roll_[imu_ptr_front_];
rpy_cur(1) = imu_pitch_[imu_ptr_front_];
rpy_cur(2) = imu_yaw_[imu_ptr_front_];
shift_cur(0) = imu_shift_x_[imu_ptr_front_];
shift_cur(1) = imu_shift_y_[imu_ptr_front_];
shift_cur(2) = imu_shift_z_[imu_ptr_front_];
velo_cur(0) = imu_velo_x_[imu_ptr_front_];
velo_cur(1) = imu_velo_y_[imu_ptr_front_];
velo_cur(2) = imu_velo_z_[imu_ptr_front_];
} else {
int imu_ptr_back = (imu_ptr_front_ - 1 + imu_que_length_) % imu_que_length_;
float ratio_front = (scan_time + rel_time - imu_time_[imu_ptr_back]) /
(imu_time_[imu_ptr_front_] - imu_time_[imu_ptr_back]);
float ratio_back = 1.0 - ratio_front;
rpy_cur(0) = imu_roll_[imu_ptr_front_] * ratio_front + imu_roll_[imu_ptr_back] *
ratio_back;
rpy_cur(1) = imu_pitch_[imu_ptr_front_] * ratio_front + imu_pitch_[imu_ptr_back] *
ratio_back;
rpy_cur(2) = imu_yaw_[imu_ptr_front_] * ratio_front + imu_yaw_[imu_ptr_back] * ratio_back;
shift_cur(0) = imu_shift_x_[imu_ptr_front_] * ratio_front + imu_shift_x_[imu_ptr_back] *
ratio_back;
shift_cur(1) = imu_shift_y_[imu_ptr_front_] * ratio_front + imu_shift_y_[imu_ptr_back] *
ratio_back;
shift_cur(2) = imu_shift_z_[imu_ptr_front_] * ratio_front + imu_shift_z_[imu_ptr_back] *
ratio_back;
velo_cur(0) = imu_velo_x_[imu_ptr_front_] * ratio_front + imu_velo_x_[imu_ptr_back] *
ratio_back;
velo_cur(1) = imu_velo_y_[imu_ptr_front_] * ratio_front + imu_velo_y_[imu_ptr_back] *
ratio_back;
velo_cur(2) = imu_velo_z_[imu_ptr_front_] * ratio_front + imu_velo_z_[imu_ptr_back] *
ratio_back;
}
r_c = (
Eigen::AngleAxisf(rpy_cur(2), Eigen::Vector3f::UnitZ()) *
Eigen::AngleAxisf(rpy_cur(1), Eigen::Vector3f::UnitY()) *
Eigen::AngleAxisf(rpy_cur(0), Eigen::Vector3f::UnitX())
).toRotationMatrix();
if (i == 0) {
rpy_start = rpy_cur;
shift_start = shift_cur;
velo_start = velo_cur;
r_s_i = r_c.inverse();
} else {
shift_from_start = shift_cur - shift_start - velo_start * rel_time;
adjusted_p = r_s_i * (r_c * Eigen::Vector3f(p.x, p.y, p.z) + shift_from_start);
p.x = adjusted_p.x();
p.y = adjusted_p.y();
p.z = adjusted_p.z();
}
}
imu_ptr_last_iter_ = imu_ptr_front_;
}
}
void setScanPeriod(const double scan_period /*[sec]*/)
{
scan_period_ = scan_period;
}
private:
double scan_period_{0.1};
static const int imu_que_length_{200};
int imu_ptr_front_{0}, imu_ptr_last_{-1}, imu_ptr_last_iter_{0};
std::array<double, imu_que_length_> imu_time_;
std::array<float, imu_que_length_> imu_roll_;
std::array<float, imu_que_length_> imu_pitch_;
std::array<float, imu_que_length_> imu_yaw_;
std::array<float, imu_que_length_> imu_acc_x_;
std::array<float, imu_que_length_> imu_acc_y_;
std::array<float, imu_que_length_> imu_acc_z_;
std::array<float, imu_que_length_> imu_velo_x_;
std::array<float, imu_que_length_> imu_velo_y_;
std::array<float, imu_que_length_> imu_velo_z_;
std::array<float, imu_que_length_> imu_shift_x_;
std::array<float, imu_que_length_> imu_shift_y_;
std::array<float, imu_que_length_> imu_shift_z_;
std::array<float, imu_que_length_> imu_angular_velo_x_;
std::array<float, imu_que_length_> imu_angular_velo_y_;
std::array<float, imu_que_length_> imu_angular_velo_z_;
std::array<float, imu_que_length_> imu_angular_rot_x_;
std::array<float, imu_que_length_> imu_angular_rot_y_;
std::array<float, imu_que_length_> imu_angular_rot_z_;
};
#endif // LIDAR_UNDISTORTION_HPP_
================================================
FILE: launch/lidar_localization.launch.py
================================================
import os
import launch
import launch.actions
import launch.events
import launch_ros
import launch_ros.actions
import launch_ros.events
from launch import LaunchDescription
from launch_ros.actions import LifecycleNode
from launch_ros.actions import Node
import lifecycle_msgs.msg
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
ld = launch.LaunchDescription()
lidar_tf = launch_ros.actions.Node(
name='lidar_tf',
package='tf2_ros',
executable='static_transform_publisher',
arguments=['0','0','0','0','0','0','1','base_link','velodyne']
)
imu_tf = launch_ros.actions.Node(
name='imu_tf',
package='tf2_ros',
executable='static_transform_publisher',
arguments=['0','0','0','0','0','0','1','base_link','imu_link']
)
localization_param_dir = launch.substitutions.LaunchConfiguration(
'localization_param_dir',
default=os.path.join(
get_package_share_directory('lidar_localization_ros2'),
'param',
'localization.yaml'))
lidar_localization = launch_ros.actions.LifecycleNode(
name='lidar_localization',
namespace='',
package='lidar_localization_ros2',
executable='lidar_localization_node',
parameters=[localization_param_dir],
remappings=[('/cloud','/velodyne_points')],
output='screen')
to_inactive = launch.actions.EmitEvent(
event=launch_ros.events.lifecycle.ChangeState(
lifecycle_node_matcher=launch.events.matches_action(lidar_localization),
transition_id=lifecycle_msgs.msg.Transition.TRANSITION_CONFIGURE,
)
)
from_unconfigured_to_inactive = launch.actions.RegisterEventHandler(
launch_ros.event_handlers.OnStateTransition(
target_lifecycle_node=lidar_localization,
goal_state='unconfigured',
entities=[
launch.actions.LogInfo(msg="-- Unconfigured --"),
launch.actions.EmitEvent(event=launch_ros.events.lifecycle.ChangeState(
lifecycle_node_matcher=launch.events.matches_action(lidar_localization),
transition_id=lifecycle_msgs.msg.Transition.TRANSITION_CONFIGURE,
)),
],
)
)
from_inactive_to_active = launch.actions.RegisterEventHandler(
launch_ros.event_handlers.OnStateTransition(
target_lifecycle_node=lidar_localization,
start_state = 'configuring',
goal_state='inactive',
entities=[
launch.actions.LogInfo(msg="-- Inactive --"),
launch.actions.EmitEvent(event=launch_ros.events.lifecycle.ChangeState(
lifecycle_node_matcher=launch.events.matches_action(lidar_localization),
transition_id=lifecycle_msgs.msg.Transition.TRANSITION_ACTIVATE,
)),
],
)
)
ld.add_action(from_unconfigured_to_inactive)
ld.add_action(from_inactive_to_active)
ld.add_action(lidar_localization)
ld.add_action(lidar_tf)
ld.add_action(to_inactive)
return ld
================================================
FILE: package.xml
================================================
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>lidar_localization_ros2</name>
<version>0.0.1</version>
<description>lidar_localization_ros2</description>
<maintainer email="rsasaki0109@gmail.com">ryohei sasaki</maintainer>
<license>BSD2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<build_depend>rclcpp_lifecycle</build_depend>
<build_depend>lifecycle_msgs</build_depend>
<build_depend>geometry_msgs</build_depend>
<build_depend>sensor_msgs</build_depend>
<build_depend>nav_msgs</build_depend>
<build_depend>tf2_ros</build_depend>
<build_depend>tf2_sensor_msgs</build_depend>
<build_depend>tf2_geometry_msgs</build_depend>
<build_depend>tf2_eigen</build_depend>
<build_depend>pcl_conversions</build_depend>
<exec_depend>rclcpp_lifecycle</exec_depend>
<exec_depend>lifecycle_msgs</exec_depend>
<exec_depend>geometry_msgs</exec_depend>
<exec_depend>sensor_msgs</exec_depend>
<exec_depend>nav_msgs</exec_depend>
<exec_depend>tf2_ros</exec_depend>
<exec_depend>tf2_sensor_msgs</exec_depend>
<exec_depend>tf2_geometry_msgs</exec_depend>
<exec_depend>tf2_eigen</exec_depend>
<exec_depend>pcl_conversions</exec_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>ros_testing</test_depend>
<test_depend>launch</test_depend>
<depend>ndt_omp_ros2</depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
================================================
FILE: param/localization.yaml
================================================
/**:
ros__parameters:
registration_method: "NDT_OMP"
score_threshold: 2.0
ndt_resolution: 1.0
ndt_step_size: 0.1
ndt_num_threads: 4
ndt_max_iterations: 35
transform_epsilon: 0.01
voxel_leaf_size: 0.2
scan_max_range: 100.0
scan_min_range: 1.0
scan_period: 0.1
use_pcd_map: true
map_path: ""
set_initial_pose: true
initial_pose_x: 0.0
initial_pose_y: 0.0
initial_pose_z: 0.0
initial_pose_qx: 0.0
initial_pose_qy: 0.0
initial_pose_qz: 0.0
initial_pose_qw: 0.0
use_odom: false
use_imu: false
enable_debug: true
enable_map_odom_tf: false
global_frame_id: map
odom_frame_id: odom
base_frame_id: base_link
enable_timer_publishing: false
pose_publish_frequency: 30.0
================================================
FILE: rviz/localization.rviz
================================================
Panels:
- Class: rviz_common/Displays
Help Height: 78
Name: Displays
Property Tree Widget:
Expanded:
- /Global Options1
- /Status1
Splitter Ratio: 0.5
Tree Height: 617
- Class: rviz_common/Selection
Name: Selection
- Class: rviz_common/Tool Properties
Expanded:
- /Publish Point1
Name: Tool Properties
Splitter Ratio: 0.5886790156364441
- Class: rviz_common/Views
Expanded:
- /Current View1
Name: Views
Splitter Ratio: 0.5
Visualization Manager:
Class: ""
Displays:
- Alpha: 0.5
Cell Size: 50
Class: rviz_default_plugins/Grid
Color: 160; 160; 164
Enabled: true
Line Style:
Line Width: 0.029999999329447746
Value: Lines
Name: Grid
Normal Cell Count: 0
Offset:
X: 0
Y: 0
Z: 0
Plane: XY
Plane Cell Count: 5
Reference Frame: <Fixed Frame>
Value: true
- Alpha: 1
Autocompute Intensity Bounds: true
Autocompute Value Bounds:
Max Value: 10
Min Value: -10
Value: true
Axis: Z
Channel Name: intensity
Class: rviz_default_plugins/PointCloud2
Color: 255; 255; 255
Color Transformer: ""
Decay Time: 0
Enabled: true
Invert Rainbow: false
Max Color: 255; 255; 255
Max Intensity: 4096
Min Color: 0; 0; 0
Min Intensity: 0
Name: PointCloud2
Position Transformer: ""
Selectable: true
Size (Pixels): 3
Size (m): 0.009999999776482582
Style: Flat Squares
Topic:
Depth: 5
Durability Policy: Volatile
Filter size: 10
History Policy: Keep Last
Reliability Policy: Reliable
Value: /map
Use Fixed Frame: true
Use rainbow: true
Value: true
- Alpha: 1
Autocompute Intensity Bounds: true
Autocompute Value Bounds:
Max Value: 21.498470306396484
Min Value: -1.9757282733917236
Value: true
Axis: Z
Channel Name: ring
Class: rviz_default_plugins/PointCloud2
Color: 255; 255; 255
Color Transformer: Intensity
Decay Time: 0
Enabled: true
Invert Rainbow: false
Max Color: 255; 255; 255
Max Intensity: 31
Min Color: 0; 0; 0
Min Intensity: 0
Name: PointCloud2
Position Transformer: XYZ
Selectable: true
Size (Pixels): 3
Size (m): 0.009999999776482582
Style: Points
Topic:
Depth: 5
Durability Policy: Volatile
Filter size: 10
History Policy: Keep Last
Reliability Policy: Reliable
Value: /velodyne_points
Use Fixed Frame: true
Use rainbow: true
Value: true
- Alpha: 1
Buffer Length: 1
Class: rviz_default_plugins/Path
Color: 25; 255; 0
Enabled: true
Head Diameter: 0.30000001192092896
Head Length: 0.20000000298023224
Length: 0.30000001192092896
Line Style: Lines
Line Width: 0.029999999329447746
Name: Path
Offset:
X: 0
Y: 0
Z: 0
Pose Color: 255; 85; 255
Pose Style: None
Radius: 0.029999999329447746
Shaft Diameter: 0.10000000149011612
Shaft Length: 0.10000000149011612
Topic:
Depth: 5
Durability Policy: Volatile
Filter size: 10
History Policy: Keep Last
Reliability Policy: Reliable
Value: /path
Value: true
- Alpha: 1
Autocompute Intensity Bounds: true
Autocompute Value Bounds:
Max Value: 29.598661422729492
Min Value: -3.1513378620147705
Value: true
Axis: Z
Channel Name: intensity
Class: rviz_default_plugins/PointCloud2
Color: 255; 255; 255
Color Transformer: AxisColor
Decay Time: 0
Enabled: true
Invert Rainbow: false
Max Color: 255; 255; 255
Max Intensity: 0
Min Color: 0; 0; 0
Min Intensity: 0
Name: PointCloud2
Position Transformer: XYZ
Selectable: true
Size (Pixels): 1
Size (m): 0.30000001192092896
Style: Points
Topic:
Depth: 5
Durability Policy: Volatile
Filter size: 10
History Policy: Keep Last
Reliability Policy: Reliable
Value: /initial_map
Use Fixed Frame: true
Use rainbow: true
Value: true
- Alpha: 1
Axes Length: 1
Axes Radius: 0.10000000149011612
Class: rviz_default_plugins/PoseWithCovariance
Color: 255; 25; 0
Covariance:
Orientation:
Alpha: 0.5
Color: 255; 255; 127
Color Style: Unique
Frame: Local
Offset: 1
Scale: 1
Value: true
Position:
Alpha: 0.30000001192092896
Color: 204; 51; 204
Scale: 1
Value: true
Value: true
Enabled: true
Head Length: 0.30000001192092896
Head Radius: 0.10000000149011612
Name: PoseWithCovariance
Shaft Length: 1
Shaft Radius: 0.05000000074505806
Shape: Arrow
Topic:
Depth: 5
Durability Policy: Volatile
Filter size: 10
History Policy: Keep Last
Reliability Policy: Reliable
Value: /pcl_pose
Value: true
Enabled: true
Global Options:
Background Color: 48; 48; 48
Fixed Frame: map
Frame Rate: 30
Name: root
Tools:
- Class: rviz_default_plugins/MoveCamera
- Class: rviz_default_plugins/Select
- Class: rviz_default_plugins/FocusCamera
- Class: rviz_default_plugins/Measure
Line color: 128; 128; 0
- Class: rviz_default_plugins/SetInitialPose
Covariance x: 0.25
Covariance y: 0.25
Covariance yaw: 0.06853891909122467
Topic:
Depth: 5
Durability Policy: Volatile
History Policy: Keep Last
Reliability Policy: Reliable
Value: /initialpose
- Class: rviz_default_plugins/SetGoal
Topic:
Depth: 5
Durability Policy: Volatile
History Policy: Keep Last
Reliability Policy: Reliable
Value: /move_base_simple/goal
- Class: rviz_default_plugins/PublishPoint
Single click: true
Topic:
Depth: 5
Durability Policy: Volatile
History Policy: Keep Last
Reliability Policy: Reliable
Value: /clicked_point
Transformation:
Current:
Class: rviz_default_plugins/TF
Value: true
Views:
Current:
Angle: 0
Class: rviz_default_plugins/TopDownOrtho
Enable Stereo Rendering:
Stereo Eye Separation: 0.05999999865889549
Stereo Focal Distance: 1
Swap Stereo Eyes: false
Value: false
Invert Z Axis: false
Name: Current View
Near Clip Distance: 0.009999999776482582
Scale: 16.579959869384766
Target Frame: base_link
Value: TopDownOrtho (rviz_default_plugins)
X: 0
Y: 0
Saved: ~
Window Geometry:
Displays:
collapsed: false
Height: 846
Hide Left Dock: false
Hide Right Dock: false
QMainWindow State: 000000ff00000000fd000000040000000000000156000002f4fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002f4000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000002f4fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d000002f4000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d006501000000000000045000000000000000000000023f000002f400000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
Selection:
collapsed: false
Tool Properties:
collapsed: false
Views:
collapsed: false
Width: 1200
X: 514
Y: 127
================================================
FILE: src/lidar_localization_component.cpp
================================================
#include <lidar_localization/lidar_localization_component.hpp>
#include <chrono>
PCLLocalization::PCLLocalization(const rclcpp::NodeOptions & options)
: rclcpp_lifecycle::LifecycleNode("lidar_localization", options),
clock_(RCL_ROS_TIME),
tfbuffer_(std::make_shared<rclcpp::Clock>(clock_)),
tflistener_(tfbuffer_),
broadcaster_(this)
{
declare_parameter("global_frame_id", "map");
declare_parameter("odom_frame_id", "odom");
declare_parameter("base_frame_id", "base_link");
declare_parameter("enable_map_odom_tf", false);
declare_parameter("registration_method", "NDT");
declare_parameter("score_threshold", 2.0);
declare_parameter("ndt_resolution", 1.0);
declare_parameter("ndt_step_size", 0.1);
declare_parameter("ndt_max_iterations", 35);
declare_parameter("ndt_num_threads", 4);
declare_parameter("transform_epsilon", 0.01);
declare_parameter("voxel_leaf_size", 0.2);
declare_parameter("scan_max_range", 100.0);
declare_parameter("scan_min_range", 1.0);
declare_parameter("scan_period", 0.1);
declare_parameter("use_pcd_map", false);
declare_parameter("map_path", "/map/map.pcd");
declare_parameter("set_initial_pose", false);
declare_parameter("initial_pose_x", 0.0);
declare_parameter("initial_pose_y", 0.0);
declare_parameter("initial_pose_z", 0.0);
declare_parameter("initial_pose_qx", 0.0);
declare_parameter("initial_pose_qy", 0.0);
declare_parameter("initial_pose_qz", 0.0);
declare_parameter("initial_pose_qw", 1.0);
declare_parameter("use_odom", false);
declare_parameter("use_imu", false);
declare_parameter("enable_debug", false);
declare_parameter("enable_timer_publishing", false);
declare_parameter("pose_publish_frequency", 10.0);
}
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
CallbackReturn PCLLocalization::on_configure(const rclcpp_lifecycle::State &)
{
RCLCPP_INFO(get_logger(), "Configuring");
initializeParameters();
initializePubSub();
initializeRegistration();
path_ptr_ = std::make_shared<nav_msgs::msg::Path>();
path_ptr_->header.frame_id = global_frame_id_;
RCLCPP_INFO(get_logger(), "Configuring end");
return CallbackReturn::SUCCESS;
}
CallbackReturn PCLLocalization::on_activate(const rclcpp_lifecycle::State &)
{
RCLCPP_INFO(get_logger(), "Activating");
pose_pub_->on_activate();
path_pub_->on_activate();
initial_map_pub_->on_activate();
if (set_initial_pose_) {
auto msg = std::make_shared<geometry_msgs::msg::PoseWithCovarianceStamped>();
msg->header.stamp = now();
msg->header.frame_id = global_frame_id_;
msg->pose.pose.position.x = initial_pose_x_;
msg->pose.pose.position.y = initial_pose_y_;
msg->pose.pose.position.z = initial_pose_z_;
msg->pose.pose.orientation.x = initial_pose_qx_;
msg->pose.pose.orientation.y = initial_pose_qy_;
msg->pose.pose.orientation.z = initial_pose_qz_;
msg->pose.pose.orientation.w = initial_pose_qw_;
geometry_msgs::msg::PoseStamped::SharedPtr pose_stamped(new geometry_msgs::msg::PoseStamped);
pose_stamped->header.stamp = msg->header.stamp;
pose_stamped->header.frame_id = global_frame_id_;
pose_stamped->pose = msg->pose.pose;
path_ptr_->poses.push_back(*pose_stamped);
initialPoseReceived(msg);
}
if (use_pcd_map_) {
pcl::PointCloud<pcl::PointXYZI>::Ptr map_cloud_ptr(new pcl::PointCloud<pcl::PointXYZI>);
// load a pcd or ply file
if (map_path_.rfind(".pcd") != std::string::npos) {
RCLCPP_INFO(get_logger(), "Loading pcd map from: %s", map_path_.c_str());
if (pcl::io::loadPCDFile(map_path_, *map_cloud_ptr) == -1) {
RCLCPP_ERROR(get_logger(), "Failed to load pcd file: %s", map_path_.c_str());
return CallbackReturn::FAILURE;
}
} else if (map_path_.rfind(".ply") != std::string::npos) {
RCLCPP_INFO(get_logger(), "Loading ply map from: %s", map_path_.c_str());
if (pcl::io::loadPLYFile(map_path_, *map_cloud_ptr) == -1) {
RCLCPP_ERROR(get_logger(), "Failed to load ply file: %s", map_path_.c_str());
return CallbackReturn::FAILURE;
}
} else {
RCLCPP_ERROR(
get_logger(), "Unsupported map file format. Please use .pcd or .ply: %s",
map_path_.c_str());
return CallbackReturn::FAILURE;
}
RCLCPP_INFO(get_logger(), "Map Size %ld", map_cloud_ptr->size());
sensor_msgs::msg::PointCloud2::SharedPtr map_msg_ptr(new sensor_msgs::msg::PointCloud2);
pcl::toROSMsg(*map_cloud_ptr, *map_msg_ptr);
map_msg_ptr->header.frame_id = global_frame_id_;
initial_map_pub_->publish(*map_msg_ptr);
RCLCPP_INFO(get_logger(), "Initial Map Published");
if (registration_method_ == "GICP" || registration_method_ == "GICP_OMP") {
pcl::PointCloud<pcl::PointXYZI>::Ptr filtered_cloud_ptr(new pcl::PointCloud<pcl::PointXYZI>());
voxel_grid_filter_.setInputCloud(map_cloud_ptr);
voxel_grid_filter_.filter(*filtered_cloud_ptr);
registration_->setInputTarget(filtered_cloud_ptr);
} else {
registration_->setInputTarget(map_cloud_ptr);
}
map_recieved_ = true;
}
RCLCPP_INFO(get_logger(), "Activating end");
return CallbackReturn::SUCCESS;
}
CallbackReturn PCLLocalization::on_deactivate(const rclcpp_lifecycle::State &)
{
RCLCPP_INFO(get_logger(), "Deactivating");
pose_pub_->on_deactivate();
path_pub_->on_deactivate();
initial_map_pub_->on_deactivate();
RCLCPP_INFO(get_logger(), "Deactivating end");
return CallbackReturn::SUCCESS;
}
CallbackReturn PCLLocalization::on_cleanup(const rclcpp_lifecycle::State &)
{
RCLCPP_INFO(get_logger(), "Cleaning Up");
initial_pose_sub_.reset();
initial_map_pub_.reset();
path_pub_.reset();
pose_pub_.reset();
odom_sub_.reset();
cloud_sub_.reset();
imu_sub_.reset();
if (enable_timer_publishing_){
pose_publish_timer_.reset();
}
RCLCPP_INFO(get_logger(), "Cleaning Up end");
return CallbackReturn::SUCCESS;
}
CallbackReturn PCLLocalization::on_shutdown(const rclcpp_lifecycle::State & state)
{
RCLCPP_INFO(get_logger(), "Shutting Down from %s", state.label().c_str());
return CallbackReturn::SUCCESS;
}
CallbackReturn PCLLocalization::on_error(const rclcpp_lifecycle::State & state)
{
RCLCPP_FATAL(get_logger(), "Error Processing from %s", state.label().c_str());
return CallbackReturn::SUCCESS;
}
void PCLLocalization::initializeParameters()
{
RCLCPP_INFO(get_logger(), "initializeParameters");
get_parameter("global_frame_id", global_frame_id_);
get_parameter("odom_frame_id", odom_frame_id_);
get_parameter("base_frame_id", base_frame_id_);
get_parameter("enable_map_odom_tf", enable_map_odom_tf_);
get_parameter("registration_method", registration_method_);
get_parameter("score_threshold", score_threshold_);
get_parameter("ndt_resolution", ndt_resolution_);
get_parameter("ndt_step_size", ndt_step_size_);
get_parameter("ndt_num_threads", ndt_num_threads_);
get_parameter("ndt_max_iterations", ndt_max_iterations_);
get_parameter("transform_epsilon", transform_epsilon_);
get_parameter("voxel_leaf_size", voxel_leaf_size_);
get_parameter("scan_max_range", scan_max_range_);
get_parameter("scan_min_range", scan_min_range_);
get_parameter("scan_period", scan_period_);
get_parameter("use_pcd_map", use_pcd_map_);
get_parameter("map_path", map_path_);
get_parameter("set_initial_pose", set_initial_pose_);
get_parameter("initial_pose_x", initial_pose_x_);
get_parameter("initial_pose_y", initial_pose_y_);
get_parameter("initial_pose_z", initial_pose_z_);
get_parameter("initial_pose_qx", initial_pose_qx_);
get_parameter("initial_pose_qy", initial_pose_qy_);
get_parameter("initial_pose_qz", initial_pose_qz_);
get_parameter("initial_pose_qw", initial_pose_qw_);
get_parameter("use_odom", use_odom_);
get_parameter("use_imu", use_imu_);
get_parameter("enable_debug", enable_debug_);
get_parameter("enable_timer_publishing", enable_timer_publishing_);
get_parameter("pose_publish_frequency", pose_publish_frequency_);
RCLCPP_INFO(get_logger(),"global_frame_id: %s", global_frame_id_.c_str());
RCLCPP_INFO(get_logger(),"odom_frame_id: %s", odom_frame_id_.c_str());
RCLCPP_INFO(get_logger(),"base_frame_id: %s", base_frame_id_.c_str());
RCLCPP_INFO(get_logger(),"enable_map_odom_tf: %d", enable_map_odom_tf_);
RCLCPP_INFO(get_logger(),"registration_method: %s", registration_method_.c_str());
RCLCPP_INFO(get_logger(),"ndt_resolution: %lf", ndt_resolution_);
RCLCPP_INFO(get_logger(),"ndt_step_size: %lf", ndt_step_size_);
RCLCPP_INFO(get_logger(),"ndt_num_threads: %d", ndt_num_threads_);
RCLCPP_INFO(get_logger(),"transform_epsilon: %lf", transform_epsilon_);
RCLCPP_INFO(get_logger(),"voxel_leaf_size: %lf", voxel_leaf_size_);
RCLCPP_INFO(get_logger(),"scan_max_range: %lf", scan_max_range_);
RCLCPP_INFO(get_logger(),"scan_min_range: %lf", scan_min_range_);
RCLCPP_INFO(get_logger(),"scan_period: %lf", scan_period_);
RCLCPP_INFO(get_logger(),"use_pcd_map: %d", use_pcd_map_);
RCLCPP_INFO(get_logger(),"map_path: %s", map_path_.c_str());
RCLCPP_INFO(get_logger(),"set_initial_pose: %d", set_initial_pose_);
RCLCPP_INFO(get_logger(),"use_odom: %d", use_odom_);
RCLCPP_INFO(get_logger(),"use_imu: %d", use_imu_);
RCLCPP_INFO(get_logger(),"enable_debug: %d", enable_debug_);
RCLCPP_INFO(get_logger(),"enable_timer_publishing: %d", enable_timer_publishing_);
RCLCPP_INFO(get_logger(),"pose_publish_frequency: %lf", pose_publish_frequency_);
}
void PCLLocalization::initializePubSub()
{
RCLCPP_INFO(get_logger(), "initializePubSub");
pose_pub_ = create_publisher<geometry_msgs::msg::PoseWithCovarianceStamped>(
"pcl_pose",
rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable());
path_pub_ = create_publisher<nav_msgs::msg::Path>(
"path",
rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable());
initial_map_pub_ = create_publisher<sensor_msgs::msg::PointCloud2>(
"initial_map",
rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable());
initial_pose_sub_ = create_subscription<geometry_msgs::msg::PoseWithCovarianceStamped>(
"initialpose", rclcpp::SystemDefaultsQoS(),
std::bind(&PCLLocalization::initialPoseReceived, this, std::placeholders::_1));
map_sub_ = create_subscription<sensor_msgs::msg::PointCloud2>(
"map", rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
std::bind(&PCLLocalization::mapReceived, this, std::placeholders::_1));
odom_sub_ = create_subscription<nav_msgs::msg::Odometry>(
"odom", rclcpp::SensorDataQoS(),
std::bind(&PCLLocalization::odomReceived, this, std::placeholders::_1));
cloud_sub_ = create_subscription<sensor_msgs::msg::PointCloud2>(
"cloud", rclcpp::SensorDataQoS(),
std::bind(&PCLLocalization::cloudReceived, this, std::placeholders::_1));
imu_sub_ = create_subscription<sensor_msgs::msg::Imu>(
"imu", rclcpp::SensorDataQoS(),
std::bind(&PCLLocalization::imuReceived, this, std::placeholders::_1));
if (enable_timer_publishing_) {
auto period = std::chrono::duration<double>(1.0 / pose_publish_frequency_);
pose_publish_timer_ = create_wall_timer(
std::chrono::duration_cast<std::chrono::nanoseconds>(period),
std::bind(&PCLLocalization::timerPublishPose, this));
}
RCLCPP_INFO(get_logger(), "initializePubSub end");
}
void PCLLocalization::initializeRegistration()
{
RCLCPP_INFO(get_logger(), "initializeRegistration");
if (registration_method_ == "GICP") {
boost::shared_ptr<pcl::GeneralizedIterativeClosestPoint<pcl::PointXYZI, pcl::PointXYZI>> gicp(
new pcl::GeneralizedIterativeClosestPoint<pcl::PointXYZI, pcl::PointXYZI>());
gicp->setTransformationEpsilon(transform_epsilon_);
registration_ = gicp;
}
else if (registration_method_ == "NDT") {
boost::shared_ptr<pcl::NormalDistributionsTransform<pcl::PointXYZI, pcl::PointXYZI>> ndt(
new pcl::NormalDistributionsTransform<pcl::PointXYZI, pcl::PointXYZI>());
ndt->setStepSize(ndt_step_size_);
ndt->setResolution(ndt_resolution_);
ndt->setTransformationEpsilon(transform_epsilon_);
registration_ = ndt;
}
else if (registration_method_ == "NDT_OMP") {
pclomp::NormalDistributionsTransform<pcl::PointXYZI, pcl::PointXYZI>::Ptr ndt_omp(
new pclomp::NormalDistributionsTransform<pcl::PointXYZI, pcl::PointXYZI>());
ndt_omp->setStepSize(ndt_step_size_);
ndt_omp->setResolution(ndt_resolution_);
ndt_omp->setTransformationEpsilon(transform_epsilon_);
if (ndt_num_threads_ > 0) {
ndt_omp->setNumThreads(ndt_num_threads_);
} else {
ndt_omp->setNumThreads(omp_get_max_threads());
}
registration_ = ndt_omp;
}
else if (registration_method_ == "GICP_OMP") {
pclomp::GeneralizedIterativeClosestPoint<pcl::PointXYZI, pcl::PointXYZI>::Ptr gicp_omp(
new pclomp::GeneralizedIterativeClosestPoint<pcl::PointXYZI, pcl::PointXYZI>());
gicp_omp->setTransformationEpsilon(transform_epsilon_);
registration_ = gicp_omp;
}
else {
RCLCPP_ERROR(get_logger(), "Invalid registration method.");
exit(EXIT_FAILURE);
}
registration_->setMaximumIterations(ndt_max_iterations_);
voxel_grid_filter_.setLeafSize(voxel_leaf_size_, voxel_leaf_size_, voxel_leaf_size_);
RCLCPP_INFO(get_logger(), "initializeRegistration end");
}
void PCLLocalization::initialPoseReceived(const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr msg)
{
RCLCPP_INFO(get_logger(), "initialPoseReceived");
if (msg->header.frame_id != global_frame_id_) {
RCLCPP_WARN(this->get_logger(), "initialpose_frame_id does not match global_frame_id");
return;
}
initialpose_recieved_ = true;
corrent_pose_with_cov_stamped_ptr_ = msg;
pose_pub_->publish(*corrent_pose_with_cov_stamped_ptr_);
if(last_scan_ptr_) {
cloudReceived(last_scan_ptr_);
}
RCLCPP_INFO(get_logger(), "initialPoseReceived end");
}
void PCLLocalization::mapReceived(const sensor_msgs::msg::PointCloud2::SharedPtr msg)
{
RCLCPP_INFO(get_logger(), "mapReceived");
pcl::PointCloud<pcl::PointXYZI>::Ptr map_cloud_ptr(new pcl::PointCloud<pcl::PointXYZI>);
if (msg->header.frame_id != global_frame_id_) {
RCLCPP_WARN(this->get_logger(), "map_frame_id does not match global_frame_id");
return;
}
pcl::fromROSMsg(*msg, *map_cloud_ptr);
if (registration_method_ == "GICP" || registration_method_ == "GICP_OMP") {
pcl::PointCloud<pcl::PointXYZI>::Ptr filtered_cloud_ptr(new pcl::PointCloud<pcl::PointXYZI>());
voxel_grid_filter_.setInputCloud(map_cloud_ptr);
voxel_grid_filter_.filter(*filtered_cloud_ptr);
registration_->setInputTarget(filtered_cloud_ptr);
} else {
registration_->setInputTarget(map_cloud_ptr);
}
map_recieved_ = true;
RCLCPP_INFO(get_logger(), "mapReceived end");
}
void PCLLocalization::odomReceived(const nav_msgs::msg::Odometry::ConstSharedPtr msg)
{
if (!use_odom_) {return;}
RCLCPP_INFO(get_logger(), "odomReceived");
double current_odom_received_time = msg->header.stamp.sec +
msg->header.stamp.nanosec * 1e-9;
double dt_odom = current_odom_received_time - last_odom_received_time_;
last_odom_received_time_ = current_odom_received_time;
if (dt_odom > 1.0 /* [sec] */) {
RCLCPP_WARN(this->get_logger(), "odom time interval is too large");
return;
}
if (dt_odom < 0.0 /* [sec] */) {
RCLCPP_WARN(this->get_logger(), "odom time interval is negative");
return;
}
tf2::Quaternion previous_quat_tf;
double roll, pitch, yaw;
tf2::fromMsg(corrent_pose_with_cov_stamped_ptr_->pose.pose.orientation, previous_quat_tf);
tf2::Matrix3x3(previous_quat_tf).getRPY(roll, pitch, yaw);
roll += msg->twist.twist.angular.x * dt_odom;
pitch += msg->twist.twist.angular.y * dt_odom;
yaw += msg->twist.twist.angular.z * dt_odom;
Eigen::Quaterniond quat_eig =
Eigen::AngleAxisd(roll, Eigen::Vector3d::UnitX()) *
Eigen::AngleAxisd(pitch, Eigen::Vector3d::UnitY()) *
Eigen::AngleAxisd(yaw, Eigen::Vector3d::UnitZ());
geometry_msgs::msg::Quaternion quat_msg = tf2::toMsg(quat_eig);
Eigen::Vector3d odom{
msg->twist.twist.linear.x,
msg->twist.twist.linear.y,
msg->twist.twist.linear.z};
Eigen::Vector3d delta_position = quat_eig.matrix() * dt_odom * odom;
corrent_pose_with_cov_stamped_ptr_->pose.pose.position.x += delta_position.x();
corrent_pose_with_cov_stamped_ptr_->pose.pose.position.y += delta_position.y();
corrent_pose_with_cov_stamped_ptr_->pose.pose.position.z += delta_position.z();
corrent_pose_with_cov_stamped_ptr_->pose.pose.orientation = quat_msg;
}
void PCLLocalization::imuReceived(const sensor_msgs::msg::Imu::ConstSharedPtr msg)
{
if (!use_imu_) {return;}
sensor_msgs::msg::Imu tf_converted_imu;
try {
const geometry_msgs::msg::TransformStamped transform = tfbuffer_.lookupTransform(
base_frame_id_, msg->header.frame_id, tf2::TimePointZero);
geometry_msgs::msg::Vector3Stamped angular_velocity, linear_acceleration, transformed_angular_velocity, transformed_linear_acceleration;
geometry_msgs::msg::Quaternion transformed_quaternion;
angular_velocity.header = msg->header;
angular_velocity.vector = msg->angular_velocity;
linear_acceleration.header = msg->header;
linear_acceleration.vector = msg->linear_acceleration;
tf2::doTransform(angular_velocity, transformed_angular_velocity, transform);
tf2::doTransform(linear_acceleration, transformed_linear_acceleration, transform);
tf_converted_imu.angular_velocity = transformed_angular_velocity.vector;
tf_converted_imu.linear_acceleration = transformed_linear_acceleration.vector;
tf_converted_imu.orientation = transformed_quaternion;
}
catch (tf2::TransformException& ex)
{
std::cout << "Failed to lookup transform" << std::endl;
RCLCPP_WARN(this->get_logger(), "Failed to lookup transform.");
return;
}
Eigen::Vector3f angular_velo{tf_converted_imu.angular_velocity.x, tf_converted_imu.angular_velocity.y,
tf_converted_imu.angular_velocity.z};
Eigen::Vector3f acc{tf_converted_imu.linear_acceleration.x, tf_converted_imu.linear_acceleration.y, tf_converted_imu.linear_acceleration.z};
Eigen::Quaternionf quat{msg->orientation.w, msg->orientation.x, msg->orientation.y,
msg->orientation.z};
double imu_time = msg->header.stamp.sec +
msg->header.stamp.nanosec * 1e-9;
lidar_undistortion_.getImu(angular_velo, acc, quat, imu_time);
}
void PCLLocalization::cloudReceived(const sensor_msgs::msg::PointCloud2::ConstSharedPtr msg)
{
if (!msg) {
RCLCPP_WARN(get_logger(), "Received null point cloud message");
return;
}
if (!map_recieved_ || !initialpose_recieved_) {return;}
RCLCPP_INFO(get_logger(), "cloudReceived");
pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_ptr(new pcl::PointCloud<pcl::PointXYZI>);
pcl::fromROSMsg(*msg, *cloud_ptr);
// If your cloud is not robot-centric, convert to base_frame.
if (msg->header.frame_id != base_frame_id_) {
RCLCPP_DEBUG(
this->get_logger(), "Transforming point cloud from %s to %s",
msg->header.frame_id.c_str(), base_frame_id_.c_str());
geometry_msgs::msg::TransformStamped base_to_lidar_stamped;
try {
base_to_lidar_stamped = tfbuffer_.lookupTransform(
base_frame_id_, msg->header.frame_id, msg->header.stamp,
rclcpp::Duration::from_seconds(0.1));
} catch (const tf2::TransformException & ex) {
RCLCPP_ERROR(
this->get_logger(), "Could not transform %s to %s: %s",
msg->header.frame_id.c_str(), base_frame_id_.c_str(), ex.what());
return;
}
Eigen::Matrix4f initial_transformation =
tf2::transformToEigen(base_to_lidar_stamped.transform).matrix().cast<float>();
pcl::PointCloud<pcl::PointXYZI>::Ptr transformed_cloud(new pcl::PointCloud<pcl::PointXYZI>());
pcl::transformPointCloud(*cloud_ptr, *transformed_cloud, initial_transformation);
cloud_ptr = transformed_cloud;
}
if (use_imu_) {
double received_time = msg->header.stamp.sec +
msg->header.stamp.nanosec * 1e-9;
lidar_undistortion_.adjustDistortion(cloud_ptr, received_time);
}
pcl::PointCloud<pcl::PointXYZI>::Ptr filtered_cloud_ptr(new pcl::PointCloud<pcl::PointXYZI>());
voxel_grid_filter_.setInputCloud(cloud_ptr);
voxel_grid_filter_.filter(*filtered_cloud_ptr);
double r;
pcl::PointCloud<pcl::PointXYZI> tmp;
for (const auto & p : filtered_cloud_ptr->points) {
r = sqrt(pow(p.x, 2.0) + pow(p.y, 2.0));
if (scan_min_range_ < r && r < scan_max_range_) {
tmp.push_back(p);
}
}
pcl::PointCloud<pcl::PointXYZI>::Ptr tmp_ptr(new pcl::PointCloud<pcl::PointXYZI>(tmp));
registration_->setInputSource(tmp_ptr);
Eigen::Affine3d affine;
tf2::fromMsg(corrent_pose_with_cov_stamped_ptr_->pose.pose, affine);
Eigen::Matrix4f init_guess = affine.matrix().cast<float>();
pcl::PointCloud<pcl::PointXYZI>::Ptr output_cloud(new pcl::PointCloud<pcl::PointXYZI>);
rclcpp::Clock system_clock;
rclcpp::Time time_align_start = system_clock.now();
registration_->align(*output_cloud, init_guess);
rclcpp::Time time_align_end = system_clock.now();
bool has_converged = registration_->hasConverged();
double fitness_score = registration_->getFitnessScore();
if (!has_converged) {
RCLCPP_WARN(get_logger(), "The registration didn't converge.");
return;
}
if (fitness_score > score_threshold_) {
RCLCPP_WARN(get_logger(), "The fitness score is over %lf.", score_threshold_);
}
Eigen::Matrix4f final_transformation = registration_->getFinalTransformation();
Eigen::Matrix3d rot_mat = final_transformation.block<3, 3>(0, 0).cast<double>();
Eigen::Quaterniond quat_eig(rot_mat);
geometry_msgs::msg::Quaternion quat_msg = tf2::toMsg(quat_eig);
corrent_pose_with_cov_stamped_ptr_->header.stamp = msg->header.stamp;
corrent_pose_with_cov_stamped_ptr_->header.frame_id = global_frame_id_;
corrent_pose_with_cov_stamped_ptr_->pose.pose.position.x = static_cast<double>(final_transformation(0, 3));
corrent_pose_with_cov_stamped_ptr_->pose.pose.position.y = static_cast<double>(final_transformation(1, 3));
corrent_pose_with_cov_stamped_ptr_->pose.pose.position.z = static_cast<double>(final_transformation(2, 3));
corrent_pose_with_cov_stamped_ptr_->pose.pose.orientation = quat_msg;
// publish here if timer is not enabled
if (!enable_timer_publishing_){
pose_pub_->publish(*corrent_pose_with_cov_stamped_ptr_);
geometry_msgs::msg::TransformStamped map_to_base_link_stamped;
map_to_base_link_stamped.header.stamp = msg->header.stamp;
map_to_base_link_stamped.header.frame_id = global_frame_id_;
map_to_base_link_stamped.child_frame_id = base_frame_id_;
map_to_base_link_stamped.transform.translation.x = static_cast<double>(final_transformation(0, 3));
map_to_base_link_stamped.transform.translation.y = static_cast<double>(final_transformation(1, 3));
map_to_base_link_stamped.transform.translation.z = static_cast<double>(final_transformation(2, 3));
map_to_base_link_stamped.transform.rotation = quat_msg;
if (!enable_map_odom_tf_) {
broadcaster_.sendTransform(map_to_base_link_stamped);
} else {
tf2::Transform map_to_base_link_tf;
tf2::fromMsg(map_to_base_link_stamped.transform, map_to_base_link_tf);
geometry_msgs::msg::TransformStamped odom_to_base_link_msg;
try {
odom_to_base_link_msg = tfbuffer_.lookupTransform(
odom_frame_id_, base_frame_id_, msg->header.stamp, rclcpp::Duration::from_seconds(0.1));
} catch (tf2::TransformException & ex) {
RCLCPP_WARN(
this->get_logger(), "Could not get transform %s to %s: %s",
base_frame_id_.c_str(), odom_frame_id_.c_str(), ex.what());
return;
}
tf2::Transform odom_to_base_link_tf;
tf2::fromMsg(odom_to_base_link_msg.transform, odom_to_base_link_tf);
tf2::Transform map_to_odom_tf = map_to_base_link_tf * odom_to_base_link_tf.inverse();
geometry_msgs::msg::TransformStamped map_to_odom_stamped;
map_to_odom_stamped.header.stamp = msg->header.stamp;
map_to_odom_stamped.header.frame_id = global_frame_id_;
map_to_odom_stamped.child_frame_id = odom_frame_id_;
map_to_odom_stamped.transform = tf2::toMsg(map_to_odom_tf);
broadcaster_.sendTransform(map_to_odom_stamped);
}
geometry_msgs::msg::PoseStamped::SharedPtr pose_stamped_ptr(new geometry_msgs::msg::PoseStamped);
pose_stamped_ptr->header.stamp = msg->header.stamp;
pose_stamped_ptr->header.frame_id = global_frame_id_;
pose_stamped_ptr->pose = corrent_pose_with_cov_stamped_ptr_->pose.pose;
path_ptr_->poses.push_back(*pose_stamped_ptr);
path_pub_->publish(*path_ptr_);
}
last_scan_ptr_ = msg;
if (enable_debug_) {
std::cout << "number of filtered cloud points: " << filtered_cloud_ptr->size() << std::endl;
std::cout << "align time:" << time_align_end.seconds() - time_align_start.seconds() <<
"[sec]" << std::endl;
std::cout << "has converged: " << has_converged << std::endl;
std::cout << "fitness score: " << fitness_score << std::endl;
std::cout << "final transformation:" << std::endl;
std::cout << final_transformation << std::endl;
/* delta_angle check
* trace(RotationMatrix) = 2(cos(theta) + 1)
*/
double init_cos_angle = 0.5 *
(init_guess.coeff(0, 0) + init_guess.coeff(1, 1) + init_guess.coeff(2, 2) - 1);
double cos_angle = 0.5 *
(final_transformation.coeff(0,
0) + final_transformation.coeff(1, 1) + final_transformation.coeff(2, 2) - 1);
double init_angle = acos(init_cos_angle);
double angle = acos(cos_angle);
// Ref:https://twitter.com/Atsushi_twi/status/1185868416864808960
double delta_angle = abs(atan2(sin(init_angle - angle), cos(init_angle - angle)));
std::cout << "delta_angle:" << delta_angle * 180 / M_PI << "[deg]" << std::endl;
std::cout << "-----------------------------------------------------" << std::endl;
}
}
void PCLLocalization::timerPublishPose()
{
if (!corrent_pose_with_cov_stamped_ptr_) {return;}
geometry_msgs::msg::PoseWithCovarianceStamped pose_copy = *corrent_pose_with_cov_stamped_ptr_;
pose_copy.header.stamp = now();
geometry_msgs::msg::PoseStamped stamped;
stamped.header = pose_copy.header;
stamped.header.frame_id = global_frame_id_;
stamped.pose = pose_copy.pose.pose;
path_ptr_->poses.push_back(stamped);
nav_msgs::msg::Path path_copy = *path_ptr_;
pose_pub_->publish(pose_copy);
path_pub_->publish(path_copy);
geometry_msgs::msg::TransformStamped map_to_base_link_stamped;
map_to_base_link_stamped.header.stamp = pose_copy.header.stamp;
map_to_base_link_stamped.header.frame_id = global_frame_id_;
map_to_base_link_stamped.child_frame_id = base_frame_id_;
map_to_base_link_stamped.transform.translation.x = pose_copy.pose.pose.position.x;
map_to_base_link_stamped.transform.translation.y = pose_copy.pose.pose.position.y;
map_to_base_link_stamped.transform.translation.z = pose_copy.pose.pose.position.z;
map_to_base_link_stamped.transform.rotation = pose_copy.pose.pose.orientation;
if (!enable_map_odom_tf_) {
broadcaster_.sendTransform(map_to_base_link_stamped);
} else {
tf2::Transform map_to_base_link_tf;
tf2::fromMsg(map_to_base_link_stamped.transform, map_to_base_link_tf);
geometry_msgs::msg::TransformStamped odom_to_base_link_msg;
try {
odom_to_base_link_msg = tfbuffer_.lookupTransform(
odom_frame_id_, base_frame_id_, pose_copy.header.stamp, rclcpp::Duration::from_seconds(0.1));
} catch (tf2::TransformException & ex) {
RCLCPP_WARN(
this->get_logger(), "Could not get transform %s to %s: %s",
base_frame_id_.c_str(), odom_frame_id_.c_str(), ex.what());
return;
}
tf2::Transform odom_to_base_link_tf;
tf2::fromMsg(odom_to_base_link_msg.transform, odom_to_base_link_tf);
tf2::Transform map_to_odom_tf = map_to_base_link_tf * odom_to_base_link_tf.inverse();
geometry_msgs::msg::TransformStamped map_to_odom_stamped;
map_to_odom_stamped.header.stamp = pose_copy.header.stamp;
map_to_odom_stamped.header.frame_id = global_frame_id_;
map_to_odom_stamped.child_frame_id = odom_frame_id_;
map_to_odom_stamped.transform = tf2::toMsg(map_to_odom_tf);
broadcaster_.sendTransform(map_to_odom_stamped);
}
}
================================================
FILE: src/lidar_localization_node.cpp
================================================
#include <lidar_localization/lidar_localization_component.hpp>
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::executors::SingleThreadedExecutor executor;
rclcpp::NodeOptions options;
std::shared_ptr<PCLLocalization> pcl_l = std::make_shared<PCLLocalization>(options);
executor.add_node(pcl_l->get_node_base_interface());
executor.spin();
rclcpp::shutdown();
return 0;
}
gitextract_mandirxt/
├── .github/
│ └── workflows/
│ └── main.yml
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── include/
│ └── lidar_localization/
│ ├── lidar_localization_component.hpp
│ └── lidar_undistortion.hpp
├── launch/
│ └── lidar_localization.launch.py
├── package.xml
├── param/
│ └── localization.yaml
├── rviz/
│ └── localization.rviz
└── src/
├── lidar_localization_component.cpp
└── lidar_localization_node.cpp
SYMBOL INDEX (14 symbols across 5 files)
FILE: include/lidar_localization/lidar_localization_component.hpp
class PCLLocalization (line 42) | class PCLLocalization : public rclcpp_lifecycle::LifecycleNode
FILE: include/lidar_localization/lidar_undistortion.hpp
class LidarUndistortion (line 12) | class LidarUndistortion
method LidarUndistortion (line 15) | LidarUndistortion() {}
method getImu (line 19) | void getImu(
method adjustDistortion (line 76) | void adjustDistortion(
method setScanPeriod (line 188) | void setScanPeriod(const double scan_period /*[sec]*/)
FILE: launch/lidar_localization.launch.py
function generate_launch_description (line 19) | def generate_launch_description():
FILE: src/lidar_localization_component.cpp
function CallbackReturn (line 45) | CallbackReturn PCLLocalization::on_configure(const rclcpp_lifecycle::Sta...
function CallbackReturn (line 60) | CallbackReturn PCLLocalization::on_activate(const rclcpp_lifecycle::Stat...
function CallbackReturn (line 135) | CallbackReturn PCLLocalization::on_deactivate(const rclcpp_lifecycle::St...
function CallbackReturn (line 147) | CallbackReturn PCLLocalization::on_cleanup(const rclcpp_lifecycle::State &)
function CallbackReturn (line 166) | CallbackReturn PCLLocalization::on_shutdown(const rclcpp_lifecycle::Stat...
function CallbackReturn (line 173) | CallbackReturn PCLLocalization::on_error(const rclcpp_lifecycle::State &...
FILE: src/lidar_localization_node.cpp
function main (line 3) | int main(int argc, char * argv[])
Condensed preview — 13 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (68K chars).
[
{
"path": ".github/workflows/main.yml",
"chars": 2053,
"preview": "name: build\n\non:\n pull_request:\n branches:\n - main\n\njobs:\n humble_build:\n name: Build on Humble\n runs-on"
},
{
"path": ".gitignore",
"chars": 46,
"preview": ".vscode\nbuild\nmap/bin_tc-2017-10-15--ndmap.pcd"
},
{
"path": "CMakeLists.txt",
"chars": 1981,
"preview": "cmake_minimum_required(VERSION 3.5)\nproject(lidar_localization_ros2)\n\nif(NOT CMAKE_C_STANDARD)\n set(CMAKE_C_STANDARD 99"
},
{
"path": "LICENSE",
"chars": 1323,
"preview": "BSD 2-Clause License\n\nCopyright (c) 2020, Ryohei Sasaki\nAll rights reserved.\n\nRedistribution and use in source and binar"
},
{
"path": "README.md",
"chars": 3217,
"preview": "# lidar_localization_ros2\nA ROS2 package of 3D LIDAR-based Localization.\n\n<img src=\"./images/path.png\" width=\"640px\">\n\nG"
},
{
"path": "include/lidar_localization/lidar_localization_component.hpp",
"chars": 4707,
"preview": "#include <chrono>\n#include <iostream>\n#include <memory>\n#include <string>\n#include <thread>\n#include <utility>\n\n#include"
},
{
"path": "include/lidar_localization/lidar_undistortion.hpp",
"chars": 8592,
"preview": "#ifndef LIDAR_UNDISTORTION_HPP_\n#define LIDAR_UNDISTORTION_HPP_\n\n#include <pcl/point_types.h>\n#include <pcl/io/pcd_io.h>"
},
{
"path": "launch/lidar_localization.launch.py",
"chars": 3219,
"preview": "import os\n\nimport launch\nimport launch.actions\nimport launch.events\n\nimport launch_ros\nimport launch_ros.actions\nimport "
},
{
"path": "package.xml",
"chars": 1584,
"preview": "<?xml version=\"1.0\"?>\n<?xml-model href=\"http://download.ros.org/schema/package_format3.xsd\" schematypens=\"http://www.w3."
},
{
"path": "param/localization.yaml",
"chars": 843,
"preview": "/**:\n ros__parameters:\n registration_method: \"NDT_OMP\"\n score_threshold: 2.0\n ndt_resolution: 1.0\n "
},
{
"path": "rviz/localization.rviz",
"chars": 8741,
"preview": "Panels:\n - Class: rviz_common/Displays\n Help Height: 78\n Name: Displays\n Property Tree Widget:\n Expanded:"
},
{
"path": "src/lidar_localization_component.cpp",
"chars": 28450,
"preview": "#include <lidar_localization/lidar_localization_component.hpp>\n#include <chrono>\n\nPCLLocalization::PCLLocalization(const"
},
{
"path": "src/lidar_localization_node.cpp",
"chars": 413,
"preview": "#include <lidar_localization/lidar_localization_component.hpp>\n\nint main(int argc, char * argv[])\n{\n rclcpp::init(argc,"
}
]
About this extraction
This page contains the full source code of the rsasaki0109/lidar_localization_ros2 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 13 files (63.6 KB), approximately 18.3k tokens, and a symbol index with 14 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.