├── .gitignore ├── README.md ├── floam ├── CMakeLists.txt ├── LICENSE ├── PCD │ └── readme.txt ├── README.md ├── img │ ├── floam_mapping.gif │ └── kitti_example.gif ├── include │ ├── laserMappingClass.h │ ├── laserProcessingClass.h │ ├── lidar.h │ ├── lidarOptimization.h │ └── odomEstimationClass.h ├── launch │ ├── floam.launch │ ├── floam_mapping.launch │ ├── floam_rsbpearl32.launch │ ├── floam_rslidar32.launch │ ├── floam_velodyne.launch │ └── floam_velodyne1.launch ├── package.xml ├── rviz │ ├── floam.rviz │ ├── floam_mapping.rviz │ └── floam_velodyne.rviz └── src │ ├── laserMappingClass.cpp │ ├── laserMappingNode.cpp │ ├── laserProcessingClass.cpp │ ├── laserProcessingNode.cpp │ ├── lidar.cpp │ ├── lidarOptimization.cpp │ ├── odomEstimationClass.cpp │ └── odomEstimationNode.cpp ├── livox_camera_calib ├── CMakeLists.txt ├── LICENSE ├── README.md ├── config │ ├── camera.yaml │ ├── camera_rsbpearl32.yaml │ ├── camera_velodyne16.yaml │ ├── camera_velodyne16_r.yaml │ ├── config_indoor.yaml │ ├── config_indoor_rsbpearl32.yaml │ ├── config_indoor_velodyne16.yaml │ ├── config_indoor_velodyne16_r.yaml │ └── config_outdoor.yaml ├── include │ ├── CustomMsg.h │ ├── CustomPoint.h │ ├── common.h │ └── lidar_camera_calib.hpp ├── launch │ ├── calib_indoor_livox.launch │ ├── calib_indoor_rsbpearl32.launch │ ├── calib_indoor_velodyne16.launch │ ├── calib_indoor_velodyne16_r.launch │ └── calib_outdoor.launch ├── package.xml ├── pics │ └── color_cloud.png ├── result │ ├── extrinsic copy.txt │ └── extrinsic.txt ├── rviz_cfg │ ├── calib.rviz │ └── calib_indoor.rviz └── src │ └── lidar_camera_calib.cpp └── pics ├── calib_result.png ├── floam_save_pcd.png └── map_pcd.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.pcd -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # extended_lidar_camera_calib 2 | 3 | This work is an extended version of [livox_camera_calib](https://github.com/hku-mars/livox_camera_calib.git), which is suitable for spinning LiDAR。 4 | 5 | In order to apply this algorithm on spinning LIDAR(e.g:VLP16), I add the preprocess process([FLOAM](https://github.com/wh200720041/floam.git)) to make the point cloud of the spinning LiDAR denser. 6 | 7 | ## Data Prepare 8 | 9 | When you calibrate the spinning lidar and camera, record the data by holding the device(lidar and camera) stationary for a period of time and then slowly and repeatedly move the entire device in this direction to accumulate the point cloud. 10 | 11 | ## Dependency 12 | ``` 13 | ceres-slover == 1.14.0 14 | pcl==1.8.0 15 | ``` 16 | 17 | ## Build 18 | ``` 19 | cd ~/catkin_ws/src 20 | git clone https://github.com/AFEICHINA/extended_lidar_camera_calib.git 21 | cd .. 22 | catkin_make 23 | source ~/catkin_ws/devel/setup.bash 24 | ``` 25 | 26 | ## Run 27 | step1: doing slam to accumulate dense pointcloud. 28 | ``` 29 | roslaunch floam floam_XXX.launch 30 | ``` 31 | 32 | 33 | step2: lidar camera calibration 34 | ``` 35 | roslaunch livox_camera_calib calib_XXX.launch 36 | ``` 37 | 38 | ## My Result 39 | In my test, I get better results in indoor enviroment. 40 | ``` 41 | LIDAR : Robosense RS-Bpearl 42 | Camera: MindVision MV-SUA133GC-T 43 | ``` 44 | slam result: 45 |

46 | 47 |

48 | 49 | map result: 50 |

51 | 52 |

53 | 54 | calib result: 55 |

56 | 57 |

58 | 59 | ## Acknowledgements 60 | Thanks for [livox_camera_calib](https://github.com/hku-mars/livox_camera_calib.git) and [FLOAM](https://github.com/wh200720041/floam.git). 61 | -------------------------------------------------------------------------------- /floam/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(floam) 3 | 4 | set(CMAKE_BUILD_TYPE "Release") 5 | set(CMAKE_CXX_FLAGS "-std=c++11") 6 | set(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wall -g") 7 | 8 | find_package(catkin REQUIRED COMPONENTS 9 | geometry_msgs 10 | nav_msgs 11 | sensor_msgs 12 | roscpp 13 | rospy 14 | rosbag 15 | std_msgs 16 | tf 17 | eigen_conversions 18 | ) 19 | 20 | find_package(Eigen3) 21 | if(NOT EIGEN3_FOUND) 22 | # Fallback to cmake_modules 23 | find_package(cmake_modules REQUIRED) 24 | find_package(Eigen REQUIRED) 25 | set(EIGEN3_INCLUDE_DIRS ${EIGEN_INCLUDE_DIRS}) 26 | set(EIGEN3_LIBRARIES ${EIGEN_LIBRARIES}) # Not strictly necessary as Eigen is head only 27 | # Possibly map additional variables to the EIGEN3_ prefix. 28 | else() 29 | set(EIGEN3_INCLUDE_DIRS ${EIGEN3_INCLUDE_DIR}) 30 | endif() 31 | find_package(PCL REQUIRED) 32 | find_package(Ceres REQUIRED) 33 | 34 | include_directories( 35 | include 36 | ${catkin_INCLUDE_DIRS} 37 | ${PCL_INCLUDE_DIRS} 38 | ${CERES_INCLUDE_DIRS} 39 | ${EIGEN3_INCLUDE_DIRS} 40 | ) 41 | 42 | link_directories( 43 | include 44 | ${PCL_LIBRARY_DIRS} 45 | ${CERES_LIBRARY_DIRS} 46 | ) 47 | 48 | 49 | catkin_package( 50 | CATKIN_DEPENDS geometry_msgs nav_msgs roscpp rospy std_msgs 51 | DEPENDS EIGEN3 PCL Ceres 52 | INCLUDE_DIRS include 53 | ) 54 | 55 | 56 | 57 | add_executable(floam_laser_processing_node src/laserProcessingNode.cpp src/laserProcessingClass.cpp src/lidar.cpp) 58 | target_link_libraries(floam_laser_processing_node ${EIGEN3_LIBRARIES} ${catkin_LIBRARIES} ${PCL_LIBRARIES} ${CERES_LIBRARIES}) 59 | 60 | add_executable(floam_odom_estimation_node src/odomEstimationNode.cpp src/lidarOptimization.cpp src/lidar.cpp src/odomEstimationClass.cpp) 61 | target_link_libraries(floam_odom_estimation_node ${EIGEN3_LIBRARIES} ${catkin_LIBRARIES} ${PCL_LIBRARIES} ${CERES_LIBRARIES}) 62 | 63 | add_executable(floam_laser_mapping_node src/laserMappingNode.cpp src/laserMappingClass.cpp src/lidar.cpp) 64 | target_link_libraries(floam_laser_mapping_node ${EIGEN3_LIBRARIES} ${catkin_LIBRARIES} ${PCL_LIBRARIES} ${CERES_LIBRARIES}) -------------------------------------------------------------------------------- /floam/LICENSE: -------------------------------------------------------------------------------- 1 | This is an optimized implementation of the algorithm described in the following paper: 2 | J. Zhang and S. Singh. LOAM: Lidar Odometry and Mapping in Real-time. 3 | Robotics: Science and Systems Conference (RSS). Berkeley, CA, July 2014. 4 | 5 | And this work is modified based on Advanced implementation of LOAM (A-LOAM) by 6 | Tong Qin qintonguav@gmail.com 7 | Shaozu Cao saozu.cao@connect.ust.hk 8 | 9 | Author of FLOAM 10 | Wang Han 11 | Nanyang Technological University, Singapore 12 | Email: wh200720041@gmail.com 13 | Homepage: https://wanghan.pro 14 | 15 | Copyright 2013, Ji Zhang, Carnegie Mellon University 16 | Further contributions copyright (c) 2016, Southwest Research Institute 17 | All rights reserved. 18 | 19 | Redistribution and use in source and binary forms, with or without 20 | modification, are permitted provided that the following conditions are met: 21 | 22 | 1. Redistributions of source code must retain the above copyright notice, this 23 | list of conditions and the following disclaimer. 24 | 2. Redistributions in binary form must reproduce the above copyright notice, 25 | this list of conditions and the following disclaimer in the documentation 26 | and/or other materials provided with the distribution. 27 | 3. Neither the name of the copyright holder nor the names of its contributors 28 | may be used to endorse or promote products derived from this software without 29 | specific prior written permission. 30 | 31 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 32 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 33 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 34 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 35 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 37 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,OR 39 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 40 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 | -------------------------------------------------------------------------------- /floam/PCD/readme.txt: -------------------------------------------------------------------------------- 1 | pcd files are saved here. -------------------------------------------------------------------------------- /floam/README.md: -------------------------------------------------------------------------------- 1 | # FLOAM 2 | ## Fast LOAM (Lidar Odometry And Mapping) 3 | 4 | This work is an optimized version of A-LOAM and LOAM with the computational cost reduced by up to 3 times. 5 | This code is modified from [LOAM](https://github.com/laboshinl/loam_velodyne) and [A-LOAM](https://github.com/HKUST-Aerial-Robotics/A-LOAM) . 6 | 7 | **Modifier:** [Wang Han](http://wanghan.pro), Nanyang Technological University, Singapore 8 | 9 | ## 1. Modification Highlights 10 | This includes some optimization on the original implementation 11 | 1. Analytic methods is used instead of auto differentiation. This is performed on se3 12 | 2. Use linear motion prediction model to estimate the initial pose 13 | 3. Laser odometry and laser mapping are merged 14 | 4. A dynamic local map is used instead of global map, in order to save memory cost. Based on massive experiments, this only has slight influence on the performance. 15 | 16 | ## 2. Evaluation 17 | ### 2.1. Computational efficiency evaluation 18 | Computational efficiency evaluation (based on KITTI dataset): 19 | Platform: Intel® Core™ i7-8700 CPU @ 3.20GHz 20 | | Dataset | ALOAM | FLOAM | 21 | |----------------------------------------------|----------------------------|------------------------| 22 | | `KITTI` | 151ms | 59ms | 23 | 24 | Localization error: 25 | | Dataset | ALOAM | FLOAM | 26 | |----------------------------------------------|----------------------------|------------------------| 27 | | `KITTI sequence 00` | 0.55% | 0.51% | 28 | | `KITTI sequence 02` | 3.93% | 1.25% | 29 | | `KITTI sequence 05` | 1.28% | 0.93% | 30 | 31 | ### 2.2. localization result 32 |

33 | 34 |

35 | 36 | ### 2.3. mapping result 37 |

38 | 39 | 40 | 41 |

42 | 43 | ## 3. Prerequisites 44 | ### 3.1 **Ubuntu** and **ROS** 45 | Ubuntu 64-bit 18.04. 46 | 47 | ROS Melodic. [ROS Installation](http://wiki.ros.org/ROS/Installation) 48 | 49 | ### 3.2. **Ceres Solver** 50 | Follow [Ceres Installation](http://ceres-solver.org/installation.html). 51 | 52 | ### 3.3. **PCL** 53 | Follow [PCL Installation](http://www.pointclouds.org/downloads/linux.html). 54 | 55 | ### 3.4. **Trajectory visualization** 56 | For visualization purpose, this package uses hector trajectory sever, you may install the package by 57 | ``` 58 | sudo apt-get install ros-melodic-hector-trajectory-server 59 | ``` 60 | Alternatively, you may remove the hector trajectory server node if trajectory visualization is not needed 61 | 62 | ## 4. Build 63 | ### 4.1 Clone repository: 64 | ``` 65 | cd ~/catkin_ws/src 66 | git clone https://github.com/wh200720041/floam.git 67 | cd .. 68 | catkin_make 69 | source ~/catkin_ws/devel/setup.bash 70 | ``` 71 | ### 4.2 Download test rosbag 72 | Download [KITTI sequence 05](https://drive.google.com/open?id=18ilF7GZDg2tmT6sD5pd1RjqO0XJLn9Mv) or [KITTI sequence 07](https://drive.google.com/open?id=1VpoKm7f4es4ISQ-psp4CV3iylcA4eu0-) 73 | 74 | Unzip compressed file 2011_09_30_0018.zip. If your system does not have unzip. please install unzip by 75 | ``` 76 | sudo apt-get install unzip 77 | ``` 78 | 79 | And then copy the file 2011_09_30_0018.bag into ~/catkin_ws/src/floam/dataset/ (this may take a few minutes to unzip the file) 80 | ``` 81 | cd ~/catkin_ws/src/floam/dataset/ 82 | unzip ~/Downloads/2011_09_30_0018.zip 83 | ``` 84 | 85 | ### 4.3 Launch ROS 86 | ``` 87 | roslaunch floam floam.launch 88 | ``` 89 | if you would like to create the map at the same time, you can run (more cpu cost) 90 | ``` 91 | roslaunch floam floam_mapping.launch 92 | ``` 93 | If the mapping process is slow, you may wish to change the rosbag speed by replacing "--clock -r 0.5" with "--clock -r 0.2" in your launch file, or you can change the map publish frequency manually (default is 10 Hz) 94 | 95 | 96 | ## 5. Test on other sequence 97 | To generate rosbag file of kitti dataset, you may use the tools provided by 98 | [kitti_to_rosbag](https://github.com/ethz-asl/kitti_to_rosbag) or [kitti2bag](https://github.com/tomas789/kitti2bag) 99 | 100 | ## 6. Test on Velodyne VLP-16 or HDL-32 101 | You may wish to test FLOAM on your own platform and sensor such as VLP-16 102 | You can install the velodyne sensor driver by 103 | ``` 104 | sudo apt-get install ros-melodic-velodyne-pointcloud 105 | ``` 106 | launch floam for your own velodyne sensor 107 | ``` 108 | roslaunch floam floam_velodyne.launch 109 | ``` 110 | If you are using HDL-32 or other sensor, please change the scan_line in the launch file 111 | 112 | 113 | ## 7.Acknowledgements 114 | Thanks for [A-LOAM](https://github.com/HKUST-Aerial-Robotics/A-LOAM) and LOAM(J. Zhang and S. Singh. LOAM: Lidar Odometry and Mapping in Real-time) and [LOAM_NOTED](https://github.com/cuitaixiang/LOAM_NOTED). 115 | 116 | -------------------------------------------------------------------------------- /floam/img/floam_mapping.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AFEICHINA/extended_lidar_camera_calib/dcb4a852be780104f8f52edf6e684fb0d83076dc/floam/img/floam_mapping.gif -------------------------------------------------------------------------------- /floam/img/kitti_example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AFEICHINA/extended_lidar_camera_calib/dcb4a852be780104f8f52edf6e684fb0d83076dc/floam/img/kitti_example.gif -------------------------------------------------------------------------------- /floam/include/laserMappingClass.h: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | 5 | #ifndef _LASER_MAPPING_H_ 6 | #define _LASER_MAPPING_H_ 7 | 8 | //PCL lib 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | //eigen lib 17 | #include 18 | #include 19 | 20 | //c++ lib 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | #define LASER_CELL_WIDTH 50.0 27 | #define LASER_CELL_HEIGHT 50.0 28 | #define LASER_CELL_DEPTH 50.0 29 | 30 | //separate map as many sub point clouds 31 | 32 | #define LASER_CELL_RANGE_HORIZONTAL 2 33 | #define LASER_CELL_RANGE_VERTICAL 2 34 | 35 | 36 | class LaserMappingClass 37 | { 38 | 39 | public: 40 | LaserMappingClass(); 41 | void init(double map_resolution); 42 | void updateCurrentPointsToMap(const pcl::PointCloud::Ptr& pc_in, const Eigen::Isometry3d& pose_current); 43 | pcl::PointCloud::Ptr getMap(void); 44 | 45 | private: 46 | int origin_in_map_x; 47 | int origin_in_map_y; 48 | int origin_in_map_z; 49 | int map_width; 50 | int map_height; 51 | int map_depth; 52 | std::vector::Ptr>>> map; 53 | pcl::VoxelGrid downSizeFilter; 54 | 55 | void addWidthCellNegative(void); 56 | void addWidthCellPositive(void); 57 | void addHeightCellNegative(void); 58 | void addHeightCellPositive(void); 59 | void addDepthCellNegative(void); 60 | void addDepthCellPositive(void); 61 | void checkPoints(int& x, int& y, int& z); 62 | 63 | }; 64 | 65 | 66 | #endif // _LASER_MAPPING_H_ 67 | 68 | -------------------------------------------------------------------------------- /floam/include/laserProcessingClass.h: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | #ifndef _LASER_PROCESSING_CLASS_H_ 5 | #define _LASER_PROCESSING_CLASS_H_ 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "lidar.h" 17 | 18 | //points covariance class 19 | class Double2d{ 20 | public: 21 | int id; 22 | double value; 23 | Double2d(int id_in, double value_in); 24 | }; 25 | //points info class 26 | class PointsInfo{ 27 | public: 28 | int layer; 29 | double time; 30 | PointsInfo(int layer_in, double time_in); 31 | }; 32 | 33 | 34 | class LaserProcessingClass 35 | { 36 | public: 37 | LaserProcessingClass(); 38 | void init(lidar::Lidar lidar_param_in); 39 | void featureExtraction(const pcl::PointCloud::Ptr& pc_in, pcl::PointCloud::Ptr& pc_out_edge, pcl::PointCloud::Ptr& pc_out_surf); 40 | void rsbp_featureExtraction(const pcl::PointCloud::Ptr& pc_in, pcl::PointCloud::Ptr& pc_out_edge, pcl::PointCloud::Ptr& pc_out_surf); 41 | void featureExtractionFromSector(const pcl::PointCloud::Ptr& pc_in, std::vector& cloudCurvature, pcl::PointCloud::Ptr& pc_out_edge, pcl::PointCloud::Ptr& pc_out_surf); 42 | private: 43 | lidar::Lidar lidar_param; 44 | }; 45 | 46 | 47 | 48 | #endif // _LASER_PROCESSING_CLASS_H_ 49 | 50 | -------------------------------------------------------------------------------- /floam/include/lidar.h: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | #ifndef _LIDAR_H_ 5 | #define _LIDAR_H_ 6 | 7 | //define lidar parameter 8 | 9 | namespace lidar{ 10 | 11 | class Lidar 12 | { 13 | public: 14 | Lidar(); 15 | 16 | void setScanPeriod(double scan_period_in); 17 | void setLines(double num_lines_in); 18 | void setVerticalAngle(double vertical_angle_in); 19 | void setVerticalResolution(double vertical_angle_resolution_in); 20 | //by default is 100. pls do not change 21 | void setMaxDistance(double max_distance_in); 22 | void setMinDistance(double min_distance_in); 23 | 24 | double max_distance; 25 | double min_distance; 26 | int num_lines; 27 | double scan_period; 28 | int points_per_line; 29 | double horizontal_angle_resolution; 30 | double horizontal_angle; 31 | double vertical_angle_resolution; 32 | double vertical_angle; 33 | }; 34 | 35 | 36 | } 37 | 38 | 39 | #endif // _LIDAR_H_ 40 | 41 | -------------------------------------------------------------------------------- /floam/include/lidarOptimization.h: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | #ifndef _LIDAR_OPTIMIZATION_ANALYTIC_H_ 5 | #define _LIDAR_OPTIMIZATION_ANALYTIC_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void getTransformFromSe3(const Eigen::Matrix& se3, Eigen::Quaterniond& q, Eigen::Vector3d& t); 13 | 14 | Eigen::Matrix3d skew(Eigen::Vector3d& mat_in); 15 | 16 | class EdgeAnalyticCostFunction : public ceres::SizedCostFunction<3, 7> { 17 | public: 18 | 19 | EdgeAnalyticCostFunction(Eigen::Vector3d curr_point_, Eigen::Vector3d last_point_a_, Eigen::Vector3d last_point_b_); 20 | virtual ~EdgeAnalyticCostFunction() {} 21 | virtual bool Evaluate(double const *const *parameters, double *residuals, double **jacobians) const; 22 | 23 | Eigen::Vector3d curr_point; 24 | Eigen::Vector3d last_point_a; 25 | Eigen::Vector3d last_point_b; 26 | }; 27 | 28 | class SurfNormAnalyticCostFunction : public ceres::SizedCostFunction<1, 7> { 29 | public: 30 | SurfNormAnalyticCostFunction(Eigen::Vector3d curr_point_, Eigen::Vector3d plane_unit_norm_, double negative_OA_dot_norm_); 31 | virtual ~SurfNormAnalyticCostFunction() {} 32 | virtual bool Evaluate(double const *const *parameters, double *residuals, double **jacobians) const; 33 | 34 | Eigen::Vector3d curr_point; 35 | Eigen::Vector3d plane_unit_norm; 36 | double negative_OA_dot_norm; 37 | }; 38 | 39 | class PoseSE3Parameterization : public ceres::LocalParameterization { 40 | public: 41 | 42 | PoseSE3Parameterization() {} 43 | virtual ~PoseSE3Parameterization() {} 44 | virtual bool Plus(const double* x, const double* delta, double* x_plus_delta) const; 45 | virtual bool ComputeJacobian(const double* x, double* jacobian) const; 46 | virtual int GlobalSize() const { return 7; } 47 | virtual int LocalSize() const { return 6; } 48 | }; 49 | 50 | 51 | 52 | #endif // _LIDAR_OPTIMIZATION_ANALYTIC_H_ 53 | 54 | -------------------------------------------------------------------------------- /floam/include/odomEstimationClass.h: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | #ifndef _ODOM_ESTIMATION_CLASS_H_ 5 | #define _ODOM_ESTIMATION_CLASS_H_ 6 | 7 | //std lib 8 | #include 9 | #include 10 | #include 11 | 12 | //PCL 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | //ceres 24 | #include 25 | #include 26 | 27 | //eigen 28 | #include 29 | #include 30 | 31 | //LOCAL LIB 32 | #include "lidar.h" 33 | #include "lidarOptimization.h" 34 | #include 35 | 36 | class OdomEstimationClass 37 | { 38 | 39 | public: 40 | OdomEstimationClass(); 41 | 42 | void init(lidar::Lidar lidar_param, double map_resolution); 43 | void initMapWithPoints(const pcl::PointCloud::Ptr& edge_in, const pcl::PointCloud::Ptr& surf_in); 44 | void updatePointsToMap(const pcl::PointCloud::Ptr& edge_in, const pcl::PointCloud::Ptr& surf_in); 45 | void getMap(pcl::PointCloud::Ptr& laserCloudMap); 46 | 47 | Eigen::Isometry3d odom; 48 | pcl::PointCloud::Ptr laserCloudCornerMap; 49 | pcl::PointCloud::Ptr laserCloudSurfMap; 50 | private: 51 | //optimization variable 52 | double parameters[7] = {0, 0, 0, 1, 0, 0, 0}; 53 | Eigen::Map q_w_curr = Eigen::Map(parameters); 54 | Eigen::Map t_w_curr = Eigen::Map(parameters + 4); 55 | 56 | Eigen::Isometry3d last_odom; 57 | 58 | //kd-tree 59 | pcl::KdTreeFLANN::Ptr kdtreeEdgeMap; 60 | pcl::KdTreeFLANN::Ptr kdtreeSurfMap; 61 | 62 | //points downsampling before add to map 63 | pcl::VoxelGrid downSizeFilterEdge; 64 | pcl::VoxelGrid downSizeFilterSurf; 65 | 66 | //local map 67 | pcl::CropBox cropBoxFilter; 68 | 69 | //optimization count 70 | int optimization_count; 71 | 72 | //function 73 | void addEdgeCostFactor(const pcl::PointCloud::Ptr& pc_in, const pcl::PointCloud::Ptr& map_in, ceres::Problem& problem, ceres::LossFunction *loss_function); 74 | void addSurfCostFactor(const pcl::PointCloud::Ptr& pc_in, const pcl::PointCloud::Ptr& map_in, ceres::Problem& problem, ceres::LossFunction *loss_function); 75 | void addPointsToMap(const pcl::PointCloud::Ptr& downsampledEdgeCloud, const pcl::PointCloud::Ptr& downsampledSurfCloud); 76 | void pointAssociateToMap(pcl::PointXYZI const *const pi, pcl::PointXYZI *const po); 77 | void downSamplingToMap(const pcl::PointCloud::Ptr& edge_pc_in, pcl::PointCloud::Ptr& edge_pc_out, const pcl::PointCloud::Ptr& surf_pc_in, pcl::PointCloud::Ptr& surf_pc_out); 78 | }; 79 | 80 | #endif // _ODOM_ESTIMATION_CLASS_H_ 81 | 82 | -------------------------------------------------------------------------------- /floam/launch/floam.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /floam/launch/floam_mapping.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /floam/launch/floam_rsbpearl32.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /floam/launch/floam_rslidar32.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /floam/launch/floam_velodyne.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /floam/launch/floam_velodyne1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /floam/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | floam 4 | 0.0.0 5 | 6 | 7 | This work is created based on some optimization on ALOAM by HKUST and LOAM by CMU 8 | 9 | 10 | Han Wang 11 | 12 | BSD 13 | https://wanghan.pro 14 | Han Wang 15 | 16 | catkin 17 | geometry_msgs 18 | nav_msgs 19 | roscpp 20 | rospy 21 | std_msgs 22 | rosbag 23 | sensor_msgs 24 | tf 25 | eigen_conversions 26 | geometry_msgs 27 | nav_msgs 28 | sensor_msgs 29 | roscpp 30 | rospy 31 | std_msgs 32 | rosbag 33 | tf 34 | eigen_conversions 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /floam/rviz/floam.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz/Displays 3 | Help Height: 78 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Status1 8 | - /Grid1 9 | - /TF1 10 | - /TF1/Frames1 11 | Splitter Ratio: 0.5 12 | Tree Height: 530 13 | - Class: rviz/Selection 14 | Name: Selection 15 | - Class: rviz/Tool Properties 16 | Expanded: 17 | - /2D Pose Estimate1 18 | - /2D Nav Goal1 19 | - /Publish Point1 20 | Name: Tool Properties 21 | Splitter Ratio: 0.5886790156364441 22 | - Class: rviz/Views 23 | Expanded: 24 | - /Current View1 25 | Name: Views 26 | Splitter Ratio: 0.5 27 | - Class: rviz/Time 28 | Experimental: false 29 | Name: Time 30 | SyncMode: 0 31 | SyncSource: Image 32 | Preferences: 33 | PromptSaveOnExit: true 34 | Toolbars: 35 | toolButtonStyle: 2 36 | Visualization Manager: 37 | Class: "" 38 | Displays: 39 | - Alpha: 0.5 40 | Cell Size: 50 41 | Class: rviz/Grid 42 | Color: 160; 160; 164 43 | Enabled: true 44 | Line Style: 45 | Line Width: 0.029999999329447746 46 | Value: Lines 47 | Name: Grid 48 | Normal Cell Count: 0 49 | Offset: 50 | X: 0 51 | Y: 0 52 | Z: 0 53 | Plane: XY 54 | Plane Cell Count: 15 55 | Reference Frame: 56 | Value: true 57 | - Class: rviz/Image 58 | Enabled: true 59 | Image Topic: /cam03/image_raw 60 | Max Value: 1 61 | Median window: 5 62 | Min Value: 0 63 | Name: Image 64 | Normalize Range: true 65 | Queue Size: 2 66 | Transport Hint: raw 67 | Unreliable: false 68 | Value: true 69 | - Class: rviz/TF 70 | Enabled: true 71 | Frame Timeout: 15 72 | Frames: 73 | All Enabled: false 74 | base_link: 75 | Value: true 76 | cam00: 77 | Value: false 78 | cam01: 79 | Value: false 80 | cam02: 81 | Value: false 82 | cam03: 83 | Value: false 84 | imu: 85 | Value: false 86 | map: 87 | Value: false 88 | velodyne: 89 | Value: true 90 | world: 91 | Value: true 92 | Marker Scale: 100 93 | Name: TF 94 | Show Arrows: false 95 | Show Axes: true 96 | Show Names: true 97 | Tree: 98 | world: 99 | imu: 100 | cam00: 101 | {} 102 | cam01: 103 | {} 104 | cam02: 105 | {} 106 | cam03: 107 | {} 108 | velodyne: 109 | {} 110 | map: 111 | base_link: 112 | {} 113 | Update Interval: 0 114 | Value: true 115 | - Alpha: 1 116 | Autocompute Intensity Bounds: true 117 | Autocompute Value Bounds: 118 | Max Value: 4.832673072814941 119 | Min Value: -8.602354049682617 120 | Value: true 121 | Axis: Z 122 | Channel Name: intensity 123 | Class: rviz/PointCloud2 124 | Color: 255; 255; 255 125 | Color Transformer: Intensity 126 | Decay Time: 0 127 | Enabled: true 128 | Invert Rainbow: false 129 | Max Color: 255; 255; 255 130 | Max Intensity: 0.9900000095367432 131 | Min Color: 0; 0; 0 132 | Min Intensity: 0 133 | Name: raw_data 134 | Position Transformer: XYZ 135 | Queue Size: 10 136 | Selectable: true 137 | Size (Pixels): 1 138 | Size (m): 0.009999999776482582 139 | Style: Points 140 | Topic: /velodyne_points 141 | Unreliable: false 142 | Use Fixed Frame: true 143 | Use rainbow: true 144 | Value: true 145 | - Alpha: 1 146 | Buffer Length: 1 147 | Class: rviz/Path 148 | Color: 239; 41; 41 149 | Enabled: true 150 | Head Diameter: 0.30000001192092896 151 | Head Length: 0.20000000298023224 152 | Length: 0.30000001192092896 153 | Line Style: Lines 154 | Line Width: 0.029999999329447746 155 | Name: ground_truth 156 | Offset: 157 | X: 0 158 | Y: 0 159 | Z: 0 160 | Pose Color: 255; 85; 255 161 | Pose Style: None 162 | Radius: 0.029999999329447746 163 | Shaft Diameter: 0.10000000149011612 164 | Shaft Length: 0.10000000149011612 165 | Topic: /gt/trajectory 166 | Unreliable: false 167 | Value: true 168 | - Alpha: 1 169 | Buffer Length: 1 170 | Class: rviz/Path 171 | Color: 0; 255; 0 172 | Enabled: true 173 | Head Diameter: 0.30000001192092896 174 | Head Length: 0.20000000298023224 175 | Length: 0.30000001192092896 176 | Line Style: Lines 177 | Line Width: 0.029999999329447746 178 | Name: floam_result 179 | Offset: 180 | X: 0 181 | Y: 0 182 | Z: 0 183 | Pose Color: 255; 85; 255 184 | Pose Style: None 185 | Radius: 0.029999999329447746 186 | Shaft Diameter: 0.10000000149011612 187 | Shaft Length: 0.10000000149011612 188 | Topic: /base_link/trajectory 189 | Unreliable: false 190 | Value: true 191 | Enabled: true 192 | Global Options: 193 | Background Color: 0; 0; 0 194 | Default Light: true 195 | Fixed Frame: world 196 | Frame Rate: 30 197 | Name: root 198 | Tools: 199 | - Class: rviz/Interact 200 | Hide Inactive Objects: true 201 | - Class: rviz/MoveCamera 202 | - Class: rviz/Select 203 | - Class: rviz/FocusCamera 204 | - Class: rviz/Measure 205 | - Class: rviz/SetInitialPose 206 | Theta std deviation: 0.2617993950843811 207 | Topic: /initialpose 208 | X std deviation: 0.5 209 | Y std deviation: 0.5 210 | - Class: rviz/SetGoal 211 | Topic: /move_base_simple/goal 212 | - Class: rviz/PublishPoint 213 | Single click: true 214 | Topic: /clicked_point 215 | Value: true 216 | Views: 217 | Current: 218 | Angle: -1.5749943256378174 219 | Class: rviz/TopDownOrtho 220 | Enable Stereo Rendering: 221 | Stereo Eye Separation: 0.05999999865889549 222 | Stereo Focal Distance: 1 223 | Swap Stereo Eyes: false 224 | Value: false 225 | Invert Z Axis: false 226 | Name: Current View 227 | Near Clip Distance: 0.009999999776482582 228 | Scale: 2.7801454067230225 229 | Target Frame: body 230 | Value: TopDownOrtho (rviz) 231 | X: 84.71299743652344 232 | Y: -50.52915954589844 233 | Saved: ~ 234 | Window Geometry: 235 | Displays: 236 | collapsed: false 237 | Height: 1025 238 | Hide Left Dock: false 239 | Hide Right Dock: false 240 | Image: 241 | collapsed: false 242 | QMainWindow State: 000000ff00000000fd00000004000000000000016a00000363fc020000000afb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d0000029d000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006501000002e0000000c00000001600fffffffb0000000600490053004300000002d9000000c70000000000000000000000010000010f00000363fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d00000363000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000073d0000003efc0100000002fb0000000800540069006d006501000000000000073d000002eb00fffffffb0000000800540069006d00650100000000000004500000000000000000000004b80000036300000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 243 | Selection: 244 | collapsed: false 245 | Time: 246 | collapsed: false 247 | Tool Properties: 248 | collapsed: false 249 | Views: 250 | collapsed: false 251 | Width: 1853 252 | X: 795 253 | Y: 27 254 | -------------------------------------------------------------------------------- /floam/rviz/floam_mapping.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz/Displays 3 | Help Height: 78 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Global Options1 8 | - /TF1/Frames1 9 | - /map1 10 | Splitter Ratio: 0.5 11 | Tree Height: 530 12 | - Class: rviz/Selection 13 | Name: Selection 14 | - Class: rviz/Tool Properties 15 | Expanded: 16 | - /2D Pose Estimate1 17 | - /2D Nav Goal1 18 | - /Publish Point1 19 | Name: Tool Properties 20 | Splitter Ratio: 0.5886790156364441 21 | - Class: rviz/Views 22 | Expanded: 23 | - /Current View1 24 | Name: Views 25 | Splitter Ratio: 0.5 26 | - Class: rviz/Time 27 | Experimental: false 28 | Name: Time 29 | SyncMode: 0 30 | SyncSource: Image 31 | Preferences: 32 | PromptSaveOnExit: true 33 | Toolbars: 34 | toolButtonStyle: 2 35 | Visualization Manager: 36 | Class: "" 37 | Displays: 38 | - Alpha: 0.5 39 | Cell Size: 50 40 | Class: rviz/Grid 41 | Color: 160; 160; 164 42 | Enabled: false 43 | Line Style: 44 | Line Width: 0.029999999329447746 45 | Value: Lines 46 | Name: Grid 47 | Normal Cell Count: 0 48 | Offset: 49 | X: 0 50 | Y: 0 51 | Z: 0 52 | Plane: XY 53 | Plane Cell Count: 15 54 | Reference Frame: 55 | Value: false 56 | - Class: rviz/Image 57 | Enabled: true 58 | Image Topic: /cam03/image_raw 59 | Max Value: 1 60 | Median window: 5 61 | Min Value: 0 62 | Name: Image 63 | Normalize Range: true 64 | Queue Size: 2 65 | Transport Hint: raw 66 | Unreliable: false 67 | Value: true 68 | - Class: rviz/TF 69 | Enabled: true 70 | Frame Timeout: 15 71 | Frames: 72 | All Enabled: false 73 | base_link: 74 | Value: true 75 | cam00: 76 | Value: false 77 | cam01: 78 | Value: false 79 | cam02: 80 | Value: false 81 | cam03: 82 | Value: false 83 | imu: 84 | Value: false 85 | map: 86 | Value: false 87 | velodyne: 88 | Value: false 89 | world: 90 | Value: true 91 | Marker Scale: 50 92 | Name: TF 93 | Show Arrows: false 94 | Show Axes: true 95 | Show Names: true 96 | Tree: 97 | world: 98 | imu: 99 | cam00: 100 | {} 101 | cam01: 102 | {} 103 | cam02: 104 | {} 105 | cam03: 106 | {} 107 | velodyne: 108 | {} 109 | map: 110 | base_link: 111 | {} 112 | Update Interval: 0 113 | Value: true 114 | - Alpha: 1 115 | Autocompute Intensity Bounds: true 116 | Autocompute Value Bounds: 117 | Max Value: 0.21922564506530762 118 | Min Value: -11.99945068359375 119 | Value: true 120 | Axis: Z 121 | Channel Name: intensity 122 | Class: rviz/PointCloud2 123 | Color: 255; 255; 255 124 | Color Transformer: Intensity 125 | Decay Time: 0 126 | Enabled: false 127 | Invert Rainbow: false 128 | Max Color: 255; 255; 255 129 | Max Intensity: 0.9900000095367432 130 | Min Color: 0; 0; 0 131 | Min Intensity: 0 132 | Name: raw_data 133 | Position Transformer: XYZ 134 | Queue Size: 10 135 | Selectable: true 136 | Size (Pixels): 2 137 | Size (m): 0.009999999776482582 138 | Style: Points 139 | Topic: /velodyne_points 140 | Unreliable: false 141 | Use Fixed Frame: true 142 | Use rainbow: true 143 | Value: false 144 | - Alpha: 0.699999988079071 145 | Autocompute Intensity Bounds: true 146 | Autocompute Value Bounds: 147 | Max Value: 8.244071960449219 148 | Min Value: -20.63126564025879 149 | Value: true 150 | Axis: Z 151 | Channel Name: intensity 152 | Class: rviz/PointCloud2 153 | Color: 255; 255; 255 154 | Color Transformer: Intensity 155 | Decay Time: 0 156 | Enabled: true 157 | Invert Rainbow: false 158 | Max Color: 255; 255; 255 159 | Max Intensity: 0.8811999559402466 160 | Min Color: 0; 0; 0 161 | Min Intensity: 0 162 | Name: map 163 | Position Transformer: XYZ 164 | Queue Size: 10 165 | Selectable: true 166 | Size (Pixels): 1 167 | Size (m): 0.3499999940395355 168 | Style: Squares 169 | Topic: /map 170 | Unreliable: false 171 | Use Fixed Frame: true 172 | Use rainbow: true 173 | Value: true 174 | - Alpha: 1 175 | Buffer Length: 1 176 | Class: rviz/Path 177 | Color: 239; 41; 41 178 | Enabled: true 179 | Head Diameter: 0.30000001192092896 180 | Head Length: 0.20000000298023224 181 | Length: 0.30000001192092896 182 | Line Style: Lines 183 | Line Width: 0.029999999329447746 184 | Name: ground_truth 185 | Offset: 186 | X: 0 187 | Y: 0 188 | Z: 0 189 | Pose Color: 255; 85; 255 190 | Pose Style: None 191 | Radius: 0.029999999329447746 192 | Shaft Diameter: 0.10000000149011612 193 | Shaft Length: 0.10000000149011612 194 | Topic: /gt/trajectory 195 | Unreliable: false 196 | Value: true 197 | - Alpha: 1 198 | Buffer Length: 1 199 | Class: rviz/Path 200 | Color: 25; 255; 0 201 | Enabled: true 202 | Head Diameter: 0.30000001192092896 203 | Head Length: 0.20000000298023224 204 | Length: 0.30000001192092896 205 | Line Style: Lines 206 | Line Width: 0.029999999329447746 207 | Name: floam_result 208 | Offset: 209 | X: 0 210 | Y: 0 211 | Z: 0 212 | Pose Color: 255; 85; 255 213 | Pose Style: None 214 | Radius: 0.029999999329447746 215 | Shaft Diameter: 0.10000000149011612 216 | Shaft Length: 0.10000000149011612 217 | Topic: /base_link/trajectory 218 | Unreliable: false 219 | Value: true 220 | Enabled: true 221 | Global Options: 222 | Background Color: 136; 138; 133 223 | Default Light: true 224 | Fixed Frame: base_link 225 | Frame Rate: 30 226 | Name: root 227 | Tools: 228 | - Class: rviz/Interact 229 | Hide Inactive Objects: true 230 | - Class: rviz/MoveCamera 231 | - Class: rviz/Select 232 | - Class: rviz/FocusCamera 233 | - Class: rviz/Measure 234 | - Class: rviz/SetInitialPose 235 | Theta std deviation: 0.2617993950843811 236 | Topic: /initialpose 237 | X std deviation: 0.5 238 | Y std deviation: 0.5 239 | - Class: rviz/SetGoal 240 | Topic: /move_base_simple/goal 241 | - Class: rviz/PublishPoint 242 | Single click: true 243 | Topic: /clicked_point 244 | Value: true 245 | Views: 246 | Current: 247 | Class: rviz/ThirdPersonFollower 248 | Distance: 86.67870330810547 249 | Enable Stereo Rendering: 250 | Stereo Eye Separation: 0.05999999865889549 251 | Stereo Focal Distance: 1 252 | Swap Stereo Eyes: false 253 | Value: false 254 | Focal Point: 255 | X: -35.922298431396484 256 | Y: 0.5339305400848389 257 | Z: 6.388764450093731e-5 258 | Focal Shape Fixed Size: true 259 | Focal Shape Size: 0.05000000074505806 260 | Invert Z Axis: false 261 | Name: Current View 262 | Near Clip Distance: 0.009999999776482582 263 | Pitch: 0.9953982830047607 264 | Target Frame: body 265 | Value: ThirdPersonFollower (rviz) 266 | Yaw: 3.2104086875915527 267 | Saved: ~ 268 | Window Geometry: 269 | Displays: 270 | collapsed: false 271 | Height: 1025 272 | Hide Left Dock: false 273 | Hide Right Dock: false 274 | Image: 275 | collapsed: false 276 | QMainWindow State: 000000ff00000000fd00000004000000000000016a00000363fc020000000afb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d0000029d000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006501000002e0000000c00000001600fffffffb0000000600490053004300000002d9000000c70000000000000000000000010000010f00000363fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d00000363000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000073d0000003efc0100000002fb0000000800540069006d006501000000000000073d000002eb00fffffffb0000000800540069006d00650100000000000004500000000000000000000004b80000036300000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 277 | Selection: 278 | collapsed: false 279 | Time: 280 | collapsed: false 281 | Tool Properties: 282 | collapsed: false 283 | Views: 284 | collapsed: false 285 | Width: 1853 286 | X: 795 287 | Y: 27 288 | -------------------------------------------------------------------------------- /floam/rviz/floam_velodyne.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz/Displays 3 | Help Height: 78 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Grid1 8 | - /TF1/Frames1 9 | - /map1 10 | - /map1/Autocompute Value Bounds1 11 | Splitter Ratio: 0.5 12 | Tree Height: 462 13 | - Class: rviz/Selection 14 | Name: Selection 15 | - Class: rviz/Tool Properties 16 | Expanded: 17 | - /2D Pose Estimate1 18 | - /2D Nav Goal1 19 | - /Publish Point1 20 | Name: Tool Properties 21 | Splitter Ratio: 0.5886790156364441 22 | - Class: rviz/Views 23 | Expanded: 24 | - /Current View1 25 | Name: Views 26 | Splitter Ratio: 0.5 27 | - Class: rviz/Time 28 | Experimental: false 29 | Name: Time 30 | SyncMode: 0 31 | SyncSource: map 32 | Preferences: 33 | PromptSaveOnExit: true 34 | Toolbars: 35 | toolButtonStyle: 2 36 | Visualization Manager: 37 | Class: "" 38 | Displays: 39 | - Alpha: 0.5 40 | Cell Size: 5 41 | Class: rviz/Grid 42 | Color: 160; 160; 164 43 | Enabled: true 44 | Line Style: 45 | Line Width: 0.029999999329447746 46 | Value: Lines 47 | Name: Grid 48 | Normal Cell Count: 0 49 | Offset: 50 | X: 0 51 | Y: 0 52 | Z: 0 53 | Plane: XY 54 | Plane Cell Count: 15 55 | Reference Frame: 56 | Value: true 57 | - Class: rviz/TF 58 | Enabled: true 59 | Frame Timeout: 15 60 | Frames: 61 | All Enabled: false 62 | base_link: 63 | Value: true 64 | map: 65 | Value: true 66 | world: 67 | Value: false 68 | Marker Scale: 3 69 | Name: TF 70 | Show Arrows: false 71 | Show Axes: true 72 | Show Names: true 73 | Tree: 74 | world: 75 | map: 76 | base_link: 77 | {} 78 | Update Interval: 0 79 | Value: true 80 | - Alpha: 1 81 | Autocompute Intensity Bounds: true 82 | Autocompute Value Bounds: 83 | Max Value: 0.21922564506530762 84 | Min Value: -11.99945068359375 85 | Value: true 86 | Axis: Z 87 | Channel Name: intensity 88 | Class: rviz/PointCloud2 89 | Color: 255; 255; 255 90 | Color Transformer: Intensity 91 | Decay Time: 0 92 | Enabled: false 93 | Invert Rainbow: false 94 | Max Color: 255; 255; 255 95 | Max Intensity: 255 96 | Min Color: 0; 0; 0 97 | Min Intensity: 0 98 | Name: raw_data 99 | Position Transformer: XYZ 100 | Queue Size: 10 101 | Selectable: true 102 | Size (Pixels): 1 103 | Size (m): 0.009999999776482582 104 | Style: Points 105 | Topic: /velodyne_points_filtered 106 | Unreliable: false 107 | Use Fixed Frame: true 108 | Use rainbow: true 109 | Value: false 110 | - Alpha: 0.10000000149011612 111 | Autocompute Intensity Bounds: true 112 | Autocompute Value Bounds: 113 | Max Value: 2.685699939727783 114 | Min Value: -1.4920799732208252 115 | Value: false 116 | Axis: Z 117 | Channel Name: intensity 118 | Class: rviz/PointCloud2 119 | Color: 255; 255; 255 120 | Color Transformer: AxisColor 121 | Decay Time: 100 122 | Enabled: true 123 | Invert Rainbow: false 124 | Max Color: 255; 255; 255 125 | Max Intensity: 0.9565645456314087 126 | Min Color: 0; 0; 0 127 | Min Intensity: 0 128 | Name: map 129 | Position Transformer: XYZ 130 | Queue Size: 10 131 | Selectable: true 132 | Size (Pixels): 2 133 | Size (m): 0.05000000074505806 134 | Style: Flat Squares 135 | Topic: /map 136 | Unreliable: false 137 | Use Fixed Frame: true 138 | Use rainbow: true 139 | Value: true 140 | - Alpha: 1 141 | Buffer Length: 1 142 | Class: rviz/Path 143 | Color: 25; 255; 0 144 | Enabled: true 145 | Head Diameter: 0.30000001192092896 146 | Head Length: 0.20000000298023224 147 | Length: 0.30000001192092896 148 | Line Style: Lines 149 | Line Width: 0.029999999329447746 150 | Name: floam_result 151 | Offset: 152 | X: 0 153 | Y: 0 154 | Z: 0 155 | Pose Color: 255; 85; 255 156 | Pose Style: None 157 | Radius: 0.029999999329447746 158 | Shaft Diameter: 0.10000000149011612 159 | Shaft Length: 0.10000000149011612 160 | Topic: /base_link/trajectory 161 | Unreliable: false 162 | Value: true 163 | Enabled: true 164 | Global Options: 165 | Background Color: 0; 0; 0 166 | Default Light: true 167 | Fixed Frame: map 168 | Frame Rate: 30 169 | Name: root 170 | Tools: 171 | - Class: rviz/Interact 172 | Hide Inactive Objects: true 173 | - Class: rviz/MoveCamera 174 | - Class: rviz/Select 175 | - Class: rviz/FocusCamera 176 | - Class: rviz/Measure 177 | - Class: rviz/SetInitialPose 178 | Theta std deviation: 0.2617993950843811 179 | Topic: /initialpose 180 | X std deviation: 0.5 181 | Y std deviation: 0.5 182 | - Class: rviz/SetGoal 183 | Topic: /move_base_simple/goal 184 | - Class: rviz/PublishPoint 185 | Single click: true 186 | Topic: /clicked_point 187 | Value: true 188 | Views: 189 | Current: 190 | Class: jsk_rviz_plugin/TabletViewController 191 | Control Mode: Orbit 192 | Distance: 13.431673049926758 193 | Enable Stereo Rendering: 194 | Stereo Eye Separation: 0.05999999865889549 195 | Stereo Focal Distance: 1 196 | Swap Stereo Eyes: false 197 | Value: false 198 | Eye: 199 | X: -2.6345107555389404 200 | Y: -1.1982510089874268 201 | Z: -7.475274085998535 202 | Focus: 203 | X: 0.5860311985015869 204 | Y: -1.4869129657745361 205 | Z: 5.561393737792969 206 | Invert Z Axis: false 207 | Maintain Vertical Axis: true 208 | Mouse Enabled: true 209 | Name: Current View 210 | Near Clip Distance: 0.009999999776482582 211 | Placement Mouse Point: /rviz/current_mouse_point 212 | Placement Publish Topic: /rviz/current_camera_placement 213 | Placement Topic: /rviz/camera_placement 214 | Target Frame: body 215 | Transition Time: 0.5 216 | Up: 217 | X: 0 218 | Y: 0 219 | Z: 1 220 | Value: TabletViewController (jsk_rviz_plugin) 221 | Saved: ~ 222 | Window Geometry: 223 | Displays: 224 | collapsed: false 225 | Height: 759 226 | Hide Left Dock: false 227 | Hide Right Dock: false 228 | QMainWindow State: 000000ff00000000fd00000004000000000000016a00000259fc020000000afb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d00000259000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006501000002e0000000c00000000000000000fb0000000600490053004300000002d9000000c70000000000000000000000010000010f00000259fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d00000259000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000073d0000003efc0100000002fb0000000800540069006d006501000000000000073d000002eb00fffffffb0000000800540069006d00650100000000000004500000000000000000000004b80000025900000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 229 | Selection: 230 | collapsed: false 231 | Time: 232 | collapsed: false 233 | Tool Properties: 234 | collapsed: false 235 | Views: 236 | collapsed: false 237 | Width: 1853 238 | X: 67 239 | Y: 54 240 | -------------------------------------------------------------------------------- /floam/src/laserMappingClass.cpp: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | 5 | #include "laserMappingClass.h" 6 | 7 | void LaserMappingClass::init(double map_resolution){ 8 | //init map 9 | //init can have real object, but future added block does not need 10 | for(int i=0;i::Ptr>> map_height_temp; 12 | for(int j=0;j::Ptr> map_depth_temp; 14 | for(int k=0;k::Ptr point_cloud_temp(new pcl::PointCloud); 16 | map_depth_temp.push_back(point_cloud_temp); 17 | } 18 | map_height_temp.push_back(map_depth_temp); 19 | } 20 | map.push_back(map_height_temp); 21 | } 22 | 23 | origin_in_map_x = LASER_CELL_RANGE_HORIZONTAL; 24 | origin_in_map_y = LASER_CELL_RANGE_HORIZONTAL; 25 | origin_in_map_z = LASER_CELL_RANGE_VERTICAL; 26 | map_width = LASER_CELL_RANGE_HORIZONTAL*2+1; 27 | map_height = LASER_CELL_RANGE_HORIZONTAL*2+1; 28 | map_depth = LASER_CELL_RANGE_HORIZONTAL*2+1; 29 | 30 | //downsampling size 31 | downSizeFilter.setLeafSize(map_resolution, map_resolution, map_resolution); 32 | } 33 | 34 | void LaserMappingClass::addWidthCellNegative(void){ 35 | std::vector::Ptr>> map_height_temp; 36 | for(int j=0; j < map_height;j++){ 37 | std::vector::Ptr> map_depth_temp; 38 | for(int k=0;k< map_depth;k++){ 39 | pcl::PointCloud::Ptr point_cloud_temp; 40 | map_depth_temp.push_back(point_cloud_temp); 41 | } 42 | map_height_temp.push_back(map_depth_temp); 43 | } 44 | map.insert(map.begin(), map_height_temp); 45 | 46 | origin_in_map_x++; 47 | map_width++; 48 | } 49 | void LaserMappingClass::addWidthCellPositive(void){ 50 | std::vector::Ptr>> map_height_temp; 51 | for(int j=0; j < map_height;j++){ 52 | std::vector::Ptr> map_depth_temp; 53 | for(int k=0;k< map_depth;k++){ 54 | pcl::PointCloud::Ptr point_cloud_temp; 55 | map_depth_temp.push_back(point_cloud_temp); 56 | } 57 | map_height_temp.push_back(map_depth_temp); 58 | } 59 | map.push_back(map_height_temp); 60 | map_width++; 61 | } 62 | void LaserMappingClass::addHeightCellNegative(void){ 63 | for(int i=0; i < map_width;i++){ 64 | std::vector::Ptr> map_depth_temp; 65 | for(int k=0;k::Ptr point_cloud_temp; 67 | map_depth_temp.push_back(point_cloud_temp); 68 | } 69 | map[i].insert(map[i].begin(), map_depth_temp); 70 | } 71 | origin_in_map_y++; 72 | map_height++; 73 | } 74 | void LaserMappingClass::addHeightCellPositive(void){ 75 | for(int i=0; i < map_width;i++){ 76 | std::vector::Ptr> map_depth_temp; 77 | for(int k=0;k::Ptr point_cloud_temp; 79 | map_depth_temp.push_back(point_cloud_temp); 80 | } 81 | map[i].push_back(map_depth_temp); 82 | } 83 | map_height++; 84 | } 85 | void LaserMappingClass::addDepthCellNegative(void){ 86 | for(int i=0; i < map_width;i++){ 87 | for(int j=0;j< map_height;j++){ 88 | pcl::PointCloud::Ptr point_cloud_temp; 89 | map[i][j].insert(map[i][j].begin(), point_cloud_temp); 90 | } 91 | } 92 | origin_in_map_z++; 93 | map_depth++; 94 | } 95 | void LaserMappingClass::addDepthCellPositive(void){ 96 | for(int i=0; i < map_width;i++){ 97 | for(int j=0;j< map_height;j++){ 98 | pcl::PointCloud::Ptr point_cloud_temp; 99 | map[i][j].push_back(point_cloud_temp); 100 | } 101 | } 102 | map_depth++; 103 | } 104 | 105 | //extend map is points exceed size 106 | void LaserMappingClass::checkPoints(int& x, int& y, int& z){ 107 | 108 | while(x + LASER_CELL_RANGE_HORIZONTAL> map_width-1){ 109 | addWidthCellPositive(); 110 | } 111 | while(x-LASER_CELL_RANGE_HORIZONTAL<0){ 112 | addWidthCellNegative(); 113 | x++; 114 | } 115 | while(y + LASER_CELL_RANGE_HORIZONTAL> map_height-1){ 116 | addHeightCellPositive(); 117 | } 118 | while(y-LASER_CELL_RANGE_HORIZONTAL<0){ 119 | addHeightCellNegative(); 120 | y++; 121 | } 122 | while(z + LASER_CELL_RANGE_VERTICAL> map_depth-1){ 123 | addDepthCellPositive(); 124 | } 125 | while(z-LASER_CELL_RANGE_VERTICAL<0){ 126 | addDepthCellNegative(); 127 | z++; 128 | } 129 | 130 | //initialize 131 | //create object if area is null 132 | for(int i=x-LASER_CELL_RANGE_HORIZONTAL;i::Ptr point_cloud_temp(new pcl::PointCloud()); 137 | map[i][j][k] = point_cloud_temp; 138 | } 139 | 140 | } 141 | 142 | } 143 | 144 | } 145 | } 146 | 147 | //update points to map 148 | void LaserMappingClass::updateCurrentPointsToMap(const pcl::PointCloud::Ptr& pc_in, const Eigen::Isometry3d& pose_current){ 149 | 150 | int currentPosIdX = int(std::floor(pose_current.translation().x() / LASER_CELL_WIDTH + 0.5)) + origin_in_map_x; 151 | int currentPosIdY = int(std::floor(pose_current.translation().y() / LASER_CELL_HEIGHT + 0.5)) + origin_in_map_y; 152 | int currentPosIdZ = int(std::floor(pose_current.translation().z() / LASER_CELL_DEPTH + 0.5)) + origin_in_map_z; 153 | 154 | //check is submap is null 155 | checkPoints(currentPosIdX,currentPosIdY,currentPosIdZ); 156 | 157 | pcl::PointCloud::Ptr transformed_pc(new pcl::PointCloud()); 158 | pcl::transformPointCloud(*pc_in, *transformed_pc, pose_current.cast()); 159 | 160 | //save points 161 | for (int i = 0; i < (int)transformed_pc->points.size(); i++) 162 | { 163 | pcl::PointXYZI point_temp = transformed_pc->points[i]; 164 | //for visualization only 165 | point_temp.intensity = std::min(1.0 , std::max(pc_in->points[i].z+2.0, 0.0) / 5); 166 | int currentPointIdX = int(std::floor(point_temp.x / LASER_CELL_WIDTH + 0.5)) + origin_in_map_x; 167 | int currentPointIdY = int(std::floor(point_temp.y / LASER_CELL_HEIGHT + 0.5)) + origin_in_map_y; 168 | int currentPointIdZ = int(std::floor(point_temp.z / LASER_CELL_DEPTH + 0.5)) + origin_in_map_z; 169 | 170 | map[currentPointIdX][currentPointIdY][currentPointIdZ]->push_back(point_temp); 171 | 172 | } 173 | 174 | //filtering points 175 | for(int i=currentPosIdX-LASER_CELL_RANGE_HORIZONTAL;i::Ptr LaserMappingClass::getMap(void){ 189 | pcl::PointCloud::Ptr laserCloudMap = pcl::PointCloud::Ptr(new pcl::PointCloud()); 190 | for (int i = 0; i < map_width; i++){ 191 | for (int j = 0; j < map_height; j++){ 192 | for (int k = 0; k < map_depth; k++){ 193 | if(map[i][j][k]!=NULL){ 194 | *laserCloudMap += *(map[i][j][k]); 195 | } 196 | } 197 | } 198 | } 199 | return laserCloudMap; 200 | } 201 | 202 | LaserMappingClass::LaserMappingClass(){ 203 | 204 | } 205 | 206 | -------------------------------------------------------------------------------- /floam/src/laserMappingNode.cpp: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | 5 | //c++ lib 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | //ros lib 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | //pcl lib 21 | #include 22 | #include 23 | #include 24 | 25 | //local lib 26 | #include "laserMappingClass.h" 27 | #include "lidar.h" 28 | 29 | #define pcd_save_en 1 30 | int pcd_index = 0; 31 | pcl::PointCloud::Ptr pcl_wait_save(new pcl::PointCloud()); 32 | 33 | LaserMappingClass laserMapping; 34 | lidar::Lidar lidar_param; 35 | std::mutex mutex_lock; 36 | std::queue odometryBuf; 37 | std::queue pointCloudBuf; 38 | 39 | ros::Publisher map_pub; 40 | void odomCallback(const nav_msgs::Odometry::ConstPtr &msg) 41 | { 42 | mutex_lock.lock(); 43 | odometryBuf.push(msg); 44 | mutex_lock.unlock(); 45 | } 46 | 47 | void velodyneHandler(const sensor_msgs::PointCloud2ConstPtr &laserCloudMsg) 48 | { 49 | mutex_lock.lock(); 50 | pointCloudBuf.push(laserCloudMsg); 51 | mutex_lock.unlock(); 52 | } 53 | 54 | 55 | void laser_mapping(){ 56 | while(1){ 57 | if(!odometryBuf.empty() && !pointCloudBuf.empty()){ 58 | 59 | //read data 60 | mutex_lock.lock(); 61 | if(!pointCloudBuf.empty() && pointCloudBuf.front()->header.stamp.toSec()header.stamp.toSec()-0.5*lidar_param.scan_period){ 62 | ROS_WARN("time stamp unaligned error and pointcloud discarded, pls check your data --> laser mapping node"); 63 | pointCloudBuf.pop(); 64 | mutex_lock.unlock(); 65 | continue; 66 | } 67 | 68 | if(!odometryBuf.empty() && odometryBuf.front()->header.stamp.toSec() < pointCloudBuf.front()->header.stamp.toSec()-0.5*lidar_param.scan_period){ 69 | odometryBuf.pop(); 70 | ROS_INFO("time stamp unaligned with path final, pls check your data --> laser mapping node"); 71 | mutex_lock.unlock(); 72 | continue; 73 | } 74 | 75 | //if time aligned 76 | pcl::PointCloud::Ptr pointcloud_in(new pcl::PointCloud()); 77 | pcl::fromROSMsg(*pointCloudBuf.front(), *pointcloud_in); 78 | ros::Time pointcloud_time = (pointCloudBuf.front())->header.stamp; 79 | 80 | Eigen::Isometry3d current_pose = Eigen::Isometry3d::Identity(); 81 | current_pose.rotate(Eigen::Quaterniond(odometryBuf.front()->pose.pose.orientation.w,odometryBuf.front()->pose.pose.orientation.x,odometryBuf.front()->pose.pose.orientation.y,odometryBuf.front()->pose.pose.orientation.z)); 82 | current_pose.pretranslate(Eigen::Vector3d(odometryBuf.front()->pose.pose.position.x,odometryBuf.front()->pose.pose.position.y,odometryBuf.front()->pose.pose.position.z)); 83 | pointCloudBuf.pop(); 84 | odometryBuf.pop(); 85 | mutex_lock.unlock(); 86 | 87 | 88 | laserMapping.updateCurrentPointsToMap(pointcloud_in,current_pose); 89 | 90 | pcl::PointCloud::Ptr pc_map = laserMapping.getMap(); 91 | sensor_msgs::PointCloud2 PointsMsg; 92 | pcl::toROSMsg(*pc_map, PointsMsg); 93 | PointsMsg.header.stamp = pointcloud_time; 94 | PointsMsg.header.frame_id = "/map"; 95 | map_pub.publish(PointsMsg); 96 | 97 | // save full pointcloud 98 | if (pcd_save_en) 99 | { 100 | int size = pointcloud_in->points.size(); 101 | pcl::PointCloud::Ptr transformed_pc(new pcl::PointCloud(size, 1)); 102 | pcl::transformPointCloud(*pointcloud_in, *transformed_pc, current_pose.cast()); 103 | 104 | *pcl_wait_save += *transformed_pc; 105 | 106 | // if (pcl_wait_save->size() > 0) 107 | // { 108 | // pcd_index ++; 109 | // std::string all_points_dir(std::string(std::string(ROOT_DIR) + "PCD/scans_") + std::to_string(pcd_index) + std::string(".pcd")); 110 | // pcl::PCDWriter pcd_writer; 111 | // std::cout << "current scan saved to /PCD/" << all_points_dir << std::endl; 112 | // pcd_writer.writeBinary(all_points_dir, *pcl_wait_save); 113 | // pcl_wait_save->clear(); 114 | // } 115 | } 116 | } 117 | 118 | //sleep 2 ms every time 119 | std::chrono::milliseconds dura(2); 120 | std::this_thread::sleep_for(dura); 121 | } 122 | } 123 | 124 | int main(int argc, char **argv) 125 | { 126 | ros::init(argc, argv, "main"); 127 | ros::NodeHandle nh; 128 | 129 | int scan_line = 64; 130 | double vertical_angle = 2.0; 131 | double scan_period= 0.1; 132 | double max_dis = 60.0; 133 | double min_dis = 2.0; 134 | double map_resolution = 0.4; 135 | std::string ROOT_DIR; 136 | 137 | nh.getParam("/scan_period", scan_period); 138 | nh.getParam("/vertical_angle", vertical_angle); 139 | nh.getParam("/max_dis", max_dis); 140 | nh.getParam("/min_dis", min_dis); 141 | nh.getParam("/scan_line", scan_line); 142 | nh.getParam("/map_resolution", map_resolution); 143 | nh.getParam("/map_save_ROOT", ROOT_DIR); 144 | 145 | lidar_param.setScanPeriod(scan_period); 146 | lidar_param.setVerticalAngle(vertical_angle); 147 | lidar_param.setLines(scan_line); 148 | lidar_param.setMaxDistance(max_dis); 149 | lidar_param.setMinDistance(min_dis); 150 | 151 | laserMapping.init(map_resolution); 152 | ros::Subscriber subLaserCloud = nh.subscribe("/velodyne_points_filtered", 100, velodyneHandler); 153 | ros::Subscriber subOdometry = nh.subscribe("/odom", 100, odomCallback); 154 | 155 | map_pub = nh.advertise("/map", 100); 156 | std::thread laser_mapping_process{laser_mapping}; 157 | 158 | ros::spin(); 159 | 160 | // exit the progroam and save the full map 161 | if (pcl_wait_save->size() > 0) 162 | { 163 | // pcd_index ++; 164 | std::string all_points_dir(std::string(std::string(ROOT_DIR) + "/PCD/map_") + std::string(".pcd")); 165 | pcl::PCDWriter pcd_writer; 166 | std::cout << "current scan saved to /PCD/:" << all_points_dir << std::endl; 167 | pcd_writer.writeBinary(all_points_dir, *pcl_wait_save); 168 | pcl_wait_save->clear(); 169 | } 170 | 171 | return 0; 172 | } 173 | -------------------------------------------------------------------------------- /floam/src/laserProcessingClass.cpp: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | #include "laserProcessingClass.h" 5 | 6 | void LaserProcessingClass::init(lidar::Lidar lidar_param_in){ 7 | 8 | lidar_param = lidar_param_in; 9 | 10 | } 11 | 12 | void LaserProcessingClass::featureExtraction(const pcl::PointCloud::Ptr& pc_in, pcl::PointCloud::Ptr& pc_out_edge, pcl::PointCloud::Ptr& pc_out_surf){ 13 | 14 | std::vector indices; 15 | pcl::removeNaNFromPointCloud(*pc_in, indices); 16 | 17 | 18 | int N_SCANS = lidar_param.num_lines; 19 | std::vector::Ptr> laserCloudScans; 20 | for(int i=0;i::Ptr(new pcl::PointCloud())); 22 | } 23 | 24 | for (int i = 0; i < (int) pc_in->points.size(); i++) 25 | { 26 | int scanID=0; 27 | double distance = sqrt(pc_in->points[i].x * pc_in->points[i].x + pc_in->points[i].y * pc_in->points[i].y); 28 | if(distancelidar_param.max_distance) 29 | continue; 30 | double angle = atan(pc_in->points[i].z / distance) * 180 / M_PI; 31 | 32 | if (N_SCANS == 16) 33 | { 34 | scanID = int((angle + 15) / 2 + 0.5); 35 | if (scanID > (N_SCANS - 1) || scanID < 0) 36 | { 37 | continue; 38 | } 39 | } 40 | else if (N_SCANS == 32) 41 | { 42 | scanID = int((angle + 92.0/3.0) * 3.0 / 4.0); 43 | if (scanID > (N_SCANS - 1) || scanID < 0) 44 | { 45 | continue; 46 | } 47 | } 48 | else if (N_SCANS == 64) 49 | { 50 | if (angle >= -8.83) 51 | scanID = int((2 - angle) * 3.0 + 0.5); 52 | else 53 | scanID = N_SCANS / 2 + int((-8.83 - angle) * 2.0 + 0.5); 54 | 55 | if (angle > 2 || angle < -24.33 || scanID > 63 || scanID < 0) 56 | { 57 | continue; 58 | } 59 | } 60 | else 61 | { 62 | printf("wrong scan number\n"); 63 | } 64 | laserCloudScans[scanID]->push_back(pc_in->points[i]); 65 | 66 | } 67 | 68 | for(int i = 0; i < N_SCANS; i++){ 69 | if(laserCloudScans[i]->points.size()<131){ 70 | continue; 71 | } 72 | 73 | std::vector cloudCurvature; 74 | int total_points = laserCloudScans[i]->points.size()-10; 75 | for(int j = 5; j < (int)laserCloudScans[i]->points.size() - 5; j++){ 76 | double diffX = laserCloudScans[i]->points[j - 5].x + laserCloudScans[i]->points[j - 4].x + laserCloudScans[i]->points[j - 3].x + laserCloudScans[i]->points[j - 2].x + laserCloudScans[i]->points[j - 1].x - 10 * laserCloudScans[i]->points[j].x + laserCloudScans[i]->points[j + 1].x + laserCloudScans[i]->points[j + 2].x + laserCloudScans[i]->points[j + 3].x + laserCloudScans[i]->points[j + 4].x + laserCloudScans[i]->points[j + 5].x; 77 | double diffY = laserCloudScans[i]->points[j - 5].y + laserCloudScans[i]->points[j - 4].y + laserCloudScans[i]->points[j - 3].y + laserCloudScans[i]->points[j - 2].y + laserCloudScans[i]->points[j - 1].y - 10 * laserCloudScans[i]->points[j].y + laserCloudScans[i]->points[j + 1].y + laserCloudScans[i]->points[j + 2].y + laserCloudScans[i]->points[j + 3].y + laserCloudScans[i]->points[j + 4].y + laserCloudScans[i]->points[j + 5].y; 78 | double diffZ = laserCloudScans[i]->points[j - 5].z + laserCloudScans[i]->points[j - 4].z + laserCloudScans[i]->points[j - 3].z + laserCloudScans[i]->points[j - 2].z + laserCloudScans[i]->points[j - 1].z - 10 * laserCloudScans[i]->points[j].z + laserCloudScans[i]->points[j + 1].z + laserCloudScans[i]->points[j + 2].z + laserCloudScans[i]->points[j + 3].z + laserCloudScans[i]->points[j + 4].z + laserCloudScans[i]->points[j + 5].z; 79 | Double2d distance(j,diffX * diffX + diffY * diffY + diffZ * diffZ); 80 | cloudCurvature.push_back(distance); 81 | 82 | } 83 | for(int j=0;j<6;j++){ 84 | int sector_length = (int)(total_points/6); 85 | int sector_start = sector_length *j; 86 | int sector_end = sector_length *(j+1)-1; 87 | if (j==5){ 88 | sector_end = total_points - 1; 89 | } 90 | std::vector subCloudCurvature(cloudCurvature.begin()+sector_start,cloudCurvature.begin()+sector_end); 91 | 92 | featureExtractionFromSector(laserCloudScans[i],subCloudCurvature, pc_out_edge, pc_out_surf); 93 | 94 | } 95 | 96 | } 97 | 98 | } 99 | 100 | 101 | void LaserProcessingClass::rsbp_featureExtraction(const pcl::PointCloud::Ptr& pc_in, pcl::PointCloud::Ptr& pc_out_edge, pcl::PointCloud::Ptr& pc_out_surf){ 102 | 103 | std::vector indices; 104 | pcl::removeNaNFromPointCloud(*pc_in, indices); 105 | 106 | int cloudSize = (int) pc_in->points.size(); 107 | int count = cloudSize; 108 | int N_SCANS = lidar_param.num_lines; 109 | std::vector::Ptr> laserCloudScans; 110 | for(int i=0;i::Ptr(new pcl::PointCloud())); 112 | } 113 | 114 | for (int i = 0; i < (int) pc_in->points.size(); i++) 115 | { 116 | int scanID=0; 117 | double distance = sqrt(pc_in->points[i].x * pc_in->points[i].x + pc_in->points[i].y * pc_in->points[i].y); 118 | if(distancelidar_param.max_distance) 119 | continue; 120 | double angle = atan(pc_in->points[i].z / distance) * 180 / M_PI; 121 | 122 | scanID = int((89.5 - angle) / 2.81 + 0.5); 123 | 124 | if (scanID > (32 - 1) || scanID < 0) { 125 | count--; 126 | continue; 127 | } 128 | 129 | laserCloudScans[scanID]->push_back(pc_in->points[i]); 130 | 131 | } 132 | 133 | cloudSize = count; 134 | for(int i = 0; i < N_SCANS; i++){ 135 | if(laserCloudScans[i]->points.size()<131){ 136 | continue; 137 | } 138 | 139 | std::vector cloudCurvature; 140 | int total_points = laserCloudScans[i]->points.size()-10; 141 | for(int j = 5; j < (int)laserCloudScans[i]->points.size() - 5; j++){ 142 | double diffX = laserCloudScans[i]->points[j - 5].x + laserCloudScans[i]->points[j - 4].x + laserCloudScans[i]->points[j - 3].x + laserCloudScans[i]->points[j - 2].x + laserCloudScans[i]->points[j - 1].x - 10 * laserCloudScans[i]->points[j].x + laserCloudScans[i]->points[j + 1].x + laserCloudScans[i]->points[j + 2].x + laserCloudScans[i]->points[j + 3].x + laserCloudScans[i]->points[j + 4].x + laserCloudScans[i]->points[j + 5].x; 143 | double diffY = laserCloudScans[i]->points[j - 5].y + laserCloudScans[i]->points[j - 4].y + laserCloudScans[i]->points[j - 3].y + laserCloudScans[i]->points[j - 2].y + laserCloudScans[i]->points[j - 1].y - 10 * laserCloudScans[i]->points[j].y + laserCloudScans[i]->points[j + 1].y + laserCloudScans[i]->points[j + 2].y + laserCloudScans[i]->points[j + 3].y + laserCloudScans[i]->points[j + 4].y + laserCloudScans[i]->points[j + 5].y; 144 | double diffZ = laserCloudScans[i]->points[j - 5].z + laserCloudScans[i]->points[j - 4].z + laserCloudScans[i]->points[j - 3].z + laserCloudScans[i]->points[j - 2].z + laserCloudScans[i]->points[j - 1].z - 10 * laserCloudScans[i]->points[j].z + laserCloudScans[i]->points[j + 1].z + laserCloudScans[i]->points[j + 2].z + laserCloudScans[i]->points[j + 3].z + laserCloudScans[i]->points[j + 4].z + laserCloudScans[i]->points[j + 5].z; 145 | Double2d distance(j,diffX * diffX + diffY * diffY + diffZ * diffZ); 146 | cloudCurvature.push_back(distance); 147 | 148 | } 149 | for(int j=0;j<6;j++){ 150 | int sector_length = (int)(total_points/6); 151 | int sector_start = sector_length *j; 152 | int sector_end = sector_length *(j+1)-1; 153 | if (j==5){ 154 | sector_end = total_points - 1; 155 | } 156 | std::vector subCloudCurvature(cloudCurvature.begin()+sector_start,cloudCurvature.begin()+sector_end); 157 | 158 | featureExtractionFromSector(laserCloudScans[i],subCloudCurvature, pc_out_edge, pc_out_surf); 159 | 160 | } 161 | 162 | } 163 | 164 | } 165 | 166 | void LaserProcessingClass::featureExtractionFromSector(const pcl::PointCloud::Ptr& pc_in, std::vector& cloudCurvature, pcl::PointCloud::Ptr& pc_out_edge, pcl::PointCloud::Ptr& pc_out_surf){ 167 | 168 | std::sort(cloudCurvature.begin(), cloudCurvature.end(), [](const Double2d & a, const Double2d & b) 169 | { 170 | return a.value < b.value; 171 | }); 172 | 173 | 174 | int largestPickedNum = 0; 175 | std::vector picked_points; 176 | int point_info_count =0; 177 | for (int i = cloudCurvature.size()-1; i >= 0; i--) 178 | { 179 | int ind = cloudCurvature[i].id; 180 | if(std::find(picked_points.begin(), picked_points.end(), ind)==picked_points.end()){ 181 | if(cloudCurvature[i].value <= 0.1){ 182 | break; 183 | } 184 | 185 | largestPickedNum++; 186 | picked_points.push_back(ind); 187 | 188 | if (largestPickedNum <= 20){ 189 | pc_out_edge->push_back(pc_in->points[ind]); 190 | point_info_count++; 191 | }else{ 192 | break; 193 | } 194 | 195 | for(int k=1;k<=5;k++){ 196 | double diffX = pc_in->points[ind + k].x - pc_in->points[ind + k - 1].x; 197 | double diffY = pc_in->points[ind + k].y - pc_in->points[ind + k - 1].y; 198 | double diffZ = pc_in->points[ind + k].z - pc_in->points[ind + k - 1].z; 199 | if (diffX * diffX + diffY * diffY + diffZ * diffZ > 0.05){ 200 | break; 201 | } 202 | picked_points.push_back(ind+k); 203 | } 204 | for(int k=-1;k>=-5;k--){ 205 | double diffX = pc_in->points[ind + k].x - pc_in->points[ind + k + 1].x; 206 | double diffY = pc_in->points[ind + k].y - pc_in->points[ind + k + 1].y; 207 | double diffZ = pc_in->points[ind + k].z - pc_in->points[ind + k + 1].z; 208 | if (diffX * diffX + diffY * diffY + diffZ * diffZ > 0.05){ 209 | break; 210 | } 211 | picked_points.push_back(ind+k); 212 | } 213 | 214 | } 215 | } 216 | 217 | //find flat points 218 | // point_info_count =0; 219 | // int smallestPickedNum = 0; 220 | 221 | // for (int i = 0; i <= (int)cloudCurvature.size()-1; i++) 222 | // { 223 | // int ind = cloudCurvature[i].id; 224 | 225 | // if( std::find(picked_points.begin(), picked_points.end(), ind)==picked_points.end()){ 226 | // if(cloudCurvature[i].value > 0.1){ 227 | // //ROS_WARN("extracted feature not qualified, please check lidar"); 228 | // break; 229 | // } 230 | // smallestPickedNum++; 231 | // picked_points.push_back(ind); 232 | 233 | // if(smallestPickedNum <= 4){ 234 | // //find all points 235 | // pc_surf_flat->push_back(pc_in->points[ind]); 236 | // pc_surf_lessFlat->push_back(pc_in->points[ind]); 237 | // point_info_count++; 238 | // } 239 | // else{ 240 | // break; 241 | // } 242 | 243 | // for(int k=1;k<=5;k++){ 244 | // double diffX = pc_in->points[ind + k].x - pc_in->points[ind + k - 1].x; 245 | // double diffY = pc_in->points[ind + k].y - pc_in->points[ind + k - 1].y; 246 | // double diffZ = pc_in->points[ind + k].z - pc_in->points[ind + k - 1].z; 247 | // if (diffX * diffX + diffY * diffY + diffZ * diffZ > 0.05){ 248 | // break; 249 | // } 250 | // picked_points.push_back(ind+k); 251 | // } 252 | // for(int k=-1;k>=-5;k--){ 253 | // double diffX = pc_in->points[ind + k].x - pc_in->points[ind + k + 1].x; 254 | // double diffY = pc_in->points[ind + k].y - pc_in->points[ind + k + 1].y; 255 | // double diffZ = pc_in->points[ind + k].z - pc_in->points[ind + k + 1].z; 256 | // if (diffX * diffX + diffY * diffY + diffZ * diffZ > 0.05){ 257 | // break; 258 | // } 259 | // picked_points.push_back(ind+k); 260 | // } 261 | 262 | // } 263 | // } 264 | 265 | for (int i = 0; i <= (int)cloudCurvature.size()-1; i++) 266 | { 267 | int ind = cloudCurvature[i].id; 268 | if( std::find(picked_points.begin(), picked_points.end(), ind)==picked_points.end()) 269 | { 270 | pc_out_surf->push_back(pc_in->points[ind]); 271 | } 272 | } 273 | 274 | 275 | 276 | } 277 | LaserProcessingClass::LaserProcessingClass(){ 278 | 279 | } 280 | 281 | Double2d::Double2d(int id_in, double value_in){ 282 | id = id_in; 283 | value =value_in; 284 | }; 285 | 286 | PointsInfo::PointsInfo(int layer_in, double time_in){ 287 | layer = layer_in; 288 | time = time_in; 289 | }; 290 | -------------------------------------------------------------------------------- /floam/src/laserProcessingNode.cpp: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | 5 | //c++ lib 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | //ros lib 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | //pcl lib 23 | #include 24 | #include 25 | #include 26 | 27 | 28 | //local lib 29 | #include "lidar.h" 30 | #include "laserProcessingClass.h" 31 | 32 | 33 | LaserProcessingClass laserProcessing; 34 | std::mutex mutex_lock; 35 | std::queue pointCloudBuf; 36 | lidar::Lidar lidar_param; 37 | 38 | std::string lidar_type; 39 | 40 | ros::Publisher pubEdgePoints; 41 | ros::Publisher pubSurfPoints; 42 | ros::Publisher pubLaserCloudFiltered; 43 | 44 | size_t convertMyLidarToPCLPointCloud(const sensor_msgs::PointCloud2ConstPtr &pcd_msg, pcl::PointCloud &pcd) 45 | { 46 | if (pcd_msg) 47 | { 48 | size_t pt_size = pcd_msg->width * pcd_msg->height; 49 | 50 | // output.points.resize (pcd_msg->width * pcd_msg->height); 51 | // output.channels.resize (pcd_msg->fields.size () - 3); 52 | // Get the x/y/z field offsets 53 | int x_idx = sensor_msgs::getPointCloud2FieldIndex(*pcd_msg, "x"); 54 | int y_idx = sensor_msgs::getPointCloud2FieldIndex(*pcd_msg, "y"); 55 | int z_idx = sensor_msgs::getPointCloud2FieldIndex(*pcd_msg, "z"); 56 | int inten_idx = sensor_msgs::getPointCloud2FieldIndex(*pcd_msg, "intensity"); 57 | 58 | int x_offset = pcd_msg->fields[x_idx].offset; 59 | int y_offset = pcd_msg->fields[y_idx].offset; 60 | int z_offset = pcd_msg->fields[z_idx].offset; 61 | int inten_offset = pcd_msg->fields[inten_idx].offset; 62 | 63 | uint8_t x_datatype = pcd_msg->fields[x_idx].datatype; 64 | uint8_t y_datatype = pcd_msg->fields[y_idx].datatype; 65 | uint8_t z_datatype = pcd_msg->fields[z_idx].datatype; 66 | uint8_t inten_datatype = pcd_msg->fields[inten_idx].datatype; 67 | 68 | // Copy the data points 69 | size_t validCount = 0; 70 | for (size_t cp = 0; cp < pt_size; ++cp) 71 | { 72 | // Copy x/y/z/intensity 73 | pcl::PointXYZI pt; 74 | pt.x = sensor_msgs::readPointCloud2BufferValue(&pcd_msg->data[cp * pcd_msg->point_step + x_offset], x_datatype); 75 | pt.y = sensor_msgs::readPointCloud2BufferValue(&pcd_msg->data[cp * pcd_msg->point_step + y_offset], y_datatype); 76 | pt.z = sensor_msgs::readPointCloud2BufferValue(&pcd_msg->data[cp * pcd_msg->point_step + z_offset], z_datatype); 77 | 78 | if (inten_idx >= 0) 79 | pt.intensity = sensor_msgs::readPointCloud2BufferValue(&pcd_msg->data[cp * pcd_msg->point_step + inten_offset], inten_datatype); 80 | 81 | pcd.push_back(pt); 82 | validCount++; 83 | } 84 | return validCount; 85 | } 86 | return 0; 87 | } 88 | 89 | void velodyneHandler(const sensor_msgs::PointCloud2ConstPtr &laserCloudMsg) 90 | { 91 | mutex_lock.lock(); 92 | pointCloudBuf.push(laserCloudMsg); 93 | mutex_lock.unlock(); 94 | 95 | } 96 | 97 | double total_time =0; 98 | int total_frame=0; 99 | 100 | void laser_processing(){ 101 | while(1){ 102 | if(!pointCloudBuf.empty()){ 103 | //read data 104 | mutex_lock.lock(); 105 | pcl::PointCloud::Ptr pointcloud_in(new pcl::PointCloud()); 106 | if(lidar_type == "rsbpearl") 107 | convertMyLidarToPCLPointCloud(pointCloudBuf.front(), *pointcloud_in); 108 | else 109 | pcl::fromROSMsg(*pointCloudBuf.front(), *pointcloud_in); 110 | 111 | ros::Time pointcloud_time = (pointCloudBuf.front())->header.stamp; 112 | pointCloudBuf.pop(); 113 | mutex_lock.unlock(); 114 | 115 | pcl::PointCloud::Ptr pointcloud_edge(new pcl::PointCloud()); 116 | pcl::PointCloud::Ptr pointcloud_surf(new pcl::PointCloud()); 117 | 118 | std::chrono::time_point start, end; 119 | start = std::chrono::system_clock::now(); 120 | if(lidar_type == "rsbpearl"){ 121 | laserProcessing.rsbp_featureExtraction(pointcloud_in,pointcloud_edge,pointcloud_surf); 122 | }else 123 | laserProcessing.featureExtraction(pointcloud_in,pointcloud_edge,pointcloud_surf); 124 | end = std::chrono::system_clock::now(); 125 | std::chrono::duration elapsed_seconds = end - start; 126 | total_frame++; 127 | float time_temp = elapsed_seconds.count() * 1000; 128 | total_time+=time_temp; 129 | //ROS_INFO("average laser processing time %f ms \n \n", total_time/total_frame); 130 | 131 | sensor_msgs::PointCloud2 laserCloudFilteredMsg; 132 | pcl::PointCloud::Ptr pointcloud_filtered(new pcl::PointCloud()); 133 | *pointcloud_filtered+=*pointcloud_edge; 134 | *pointcloud_filtered+=*pointcloud_surf; 135 | pcl::toROSMsg(*pointcloud_filtered, laserCloudFilteredMsg); 136 | laserCloudFilteredMsg.header.stamp = pointcloud_time; 137 | laserCloudFilteredMsg.header.frame_id = "/base_link"; 138 | pubLaserCloudFiltered.publish(laserCloudFilteredMsg); 139 | 140 | // sensor_msgs::PointCloud2 laserCloudFilteredMsg; 141 | // pcl::toROSMsg(*pointcloud_in, laserCloudFilteredMsg); 142 | // laserCloudFilteredMsg.header.stamp = pointcloud_time; 143 | // laserCloudFilteredMsg.header.frame_id = "/base_link"; 144 | // pubLaserCloudFiltered.publish(laserCloudFilteredMsg); 145 | 146 | sensor_msgs::PointCloud2 edgePointsMsg; 147 | pcl::toROSMsg(*pointcloud_edge, edgePointsMsg); 148 | edgePointsMsg.header.stamp = pointcloud_time; 149 | edgePointsMsg.header.frame_id = "/base_link"; 150 | pubEdgePoints.publish(edgePointsMsg); 151 | 152 | 153 | sensor_msgs::PointCloud2 surfPointsMsg; 154 | pcl::toROSMsg(*pointcloud_surf, surfPointsMsg); 155 | surfPointsMsg.header.stamp = pointcloud_time; 156 | surfPointsMsg.header.frame_id = "/base_link"; 157 | pubSurfPoints.publish(surfPointsMsg); 158 | 159 | } 160 | //sleep 2 ms every time 161 | std::chrono::milliseconds dura(2); 162 | std::this_thread::sleep_for(dura); 163 | } 164 | } 165 | 166 | int main(int argc, char **argv) 167 | { 168 | ros::init(argc, argv, "main"); 169 | ros::NodeHandle nh; 170 | 171 | int scan_line = 64; 172 | double vertical_angle = 2.0; 173 | double scan_period= 0.1; 174 | double max_dis = 60.0; 175 | double min_dis = 2.0; 176 | 177 | nh.getParam("/scan_period", scan_period); 178 | nh.getParam("/vertical_angle", vertical_angle); 179 | nh.getParam("/max_dis", max_dis); 180 | nh.getParam("/min_dis", min_dis); 181 | nh.getParam("/scan_line", scan_line); 182 | nh.getParam("lidar_type",lidar_type); 183 | 184 | lidar_param.setScanPeriod(scan_period); 185 | lidar_param.setVerticalAngle(vertical_angle); 186 | lidar_param.setLines(scan_line); 187 | lidar_param.setMaxDistance(max_dis); 188 | lidar_param.setMinDistance(min_dis); 189 | 190 | laserProcessing.init(lidar_param); 191 | 192 | ros::Subscriber subLaserCloud = nh.subscribe("/velodyne_points", 100, velodyneHandler); 193 | 194 | pubLaserCloudFiltered = nh.advertise("/velodyne_points_filtered", 1000); 195 | 196 | pubEdgePoints = nh.advertise("/laser_cloud_edge", 10000); 197 | 198 | pubSurfPoints = nh.advertise("/laser_cloud_surf", 10000); 199 | 200 | std::thread laser_processing_process{laser_processing}; 201 | 202 | ros::spin(); 203 | 204 | return 0; 205 | } 206 | 207 | -------------------------------------------------------------------------------- /floam/src/lidar.cpp: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | 5 | #include "lidar.h" 6 | 7 | 8 | lidar::Lidar::Lidar(){ 9 | 10 | } 11 | 12 | 13 | void lidar::Lidar::setLines(double num_lines_in){ 14 | num_lines=num_lines_in; 15 | } 16 | 17 | 18 | void lidar::Lidar::setVerticalAngle(double vertical_angle_in){ 19 | vertical_angle = vertical_angle_in; 20 | } 21 | 22 | 23 | void lidar::Lidar::setVerticalResolution(double vertical_angle_resolution_in){ 24 | vertical_angle_resolution = vertical_angle_resolution_in; 25 | } 26 | 27 | 28 | void lidar::Lidar::setScanPeriod(double scan_period_in){ 29 | scan_period = scan_period_in; 30 | } 31 | 32 | 33 | void lidar::Lidar::setMaxDistance(double max_distance_in){ 34 | max_distance = max_distance_in; 35 | } 36 | 37 | void lidar::Lidar::setMinDistance(double min_distance_in){ 38 | min_distance = min_distance_in; 39 | } -------------------------------------------------------------------------------- /floam/src/lidarOptimization.cpp: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | 5 | #include "lidarOptimization.h" 6 | 7 | EdgeAnalyticCostFunction::EdgeAnalyticCostFunction(Eigen::Vector3d curr_point_, Eigen::Vector3d last_point_a_, Eigen::Vector3d last_point_b_) 8 | : curr_point(curr_point_), last_point_a(last_point_a_), last_point_b(last_point_b_){ 9 | 10 | } 11 | 12 | bool EdgeAnalyticCostFunction::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const 13 | { 14 | 15 | Eigen::Map q_last_curr(parameters[0]); 16 | Eigen::Map t_last_curr(parameters[0] + 4); 17 | Eigen::Vector3d lp; 18 | lp = q_last_curr * curr_point + t_last_curr; //new point 19 | Eigen::Vector3d nu = (lp - last_point_a).cross(lp - last_point_b); 20 | Eigen::Vector3d de = last_point_a - last_point_b; 21 | 22 | residuals[0] = nu.x() / de.norm(); 23 | residuals[1] = nu.y() / de.norm(); 24 | residuals[2] = nu.z() / de.norm(); 25 | 26 | if(jacobians != NULL) 27 | { 28 | if(jacobians[0] != NULL) 29 | { 30 | Eigen::Matrix3d skew_lp = skew(lp); 31 | Eigen::Matrix dp_by_so3; 32 | dp_by_so3.block<3,3>(0,0) = -skew_lp; 33 | (dp_by_so3.block<3,3>(0, 3)).setIdentity(); 34 | Eigen::Map > J_se3(jacobians[0]); 35 | J_se3.setZero(); 36 | Eigen::Vector3d re = last_point_b - last_point_a; 37 | Eigen::Matrix3d skew_re = skew(re); 38 | 39 | J_se3.block<3,6>(0,0) = skew_re * dp_by_so3/de.norm(); 40 | 41 | } 42 | } 43 | 44 | return true; 45 | 46 | } 47 | 48 | //surf norm cost 49 | 50 | SurfNormAnalyticCostFunction::SurfNormAnalyticCostFunction(Eigen::Vector3d curr_point_, Eigen::Vector3d plane_unit_norm_, double negative_OA_dot_norm_) 51 | : curr_point(curr_point_), plane_unit_norm(plane_unit_norm_), negative_OA_dot_norm(negative_OA_dot_norm_) { 52 | 53 | } 54 | 55 | bool SurfNormAnalyticCostFunction::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const 56 | { 57 | Eigen::Map q_w_curr(parameters[0]); 58 | Eigen::Map t_w_curr(parameters[0] + 4); 59 | Eigen::Vector3d point_w = q_w_curr * curr_point + t_w_curr; 60 | 61 | residuals[0] = plane_unit_norm.dot(point_w) + negative_OA_dot_norm; 62 | 63 | if(jacobians != NULL) 64 | { 65 | if(jacobians[0] != NULL) 66 | { 67 | Eigen::Matrix3d skew_point_w = skew(point_w); 68 | 69 | Eigen::Matrix dp_by_so3; 70 | dp_by_so3.block<3,3>(0,0) = -skew_point_w; 71 | (dp_by_so3.block<3,3>(0, 3)).setIdentity(); 72 | Eigen::Map > J_se3(jacobians[0]); 73 | J_se3.setZero(); 74 | J_se3.block<1,6>(0,0) = plane_unit_norm.transpose() * dp_by_so3; 75 | 76 | } 77 | } 78 | return true; 79 | 80 | } 81 | 82 | 83 | bool PoseSE3Parameterization::Plus(const double *x, const double *delta, double *x_plus_delta) const 84 | { 85 | Eigen::Map trans(x + 4); 86 | 87 | Eigen::Quaterniond delta_q; 88 | Eigen::Vector3d delta_t; 89 | getTransformFromSe3(Eigen::Map>(delta), delta_q, delta_t); 90 | Eigen::Map quater(x); 91 | Eigen::Map quater_plus(x_plus_delta); 92 | Eigen::Map trans_plus(x_plus_delta + 4); 93 | 94 | quater_plus = delta_q * quater; 95 | trans_plus = delta_q * trans + delta_t; 96 | 97 | return true; 98 | } 99 | 100 | 101 | bool PoseSE3Parameterization::ComputeJacobian(const double *x, double *jacobian) const 102 | { 103 | Eigen::Map> j(jacobian); 104 | (j.topRows(6)).setIdentity(); 105 | (j.bottomRows(1)).setZero(); 106 | 107 | return true; 108 | } 109 | 110 | 111 | void getTransformFromSe3(const Eigen::Matrix& se3, Eigen::Quaterniond& q, Eigen::Vector3d& t){ 112 | Eigen::Vector3d omega(se3.data()); 113 | Eigen::Vector3d upsilon(se3.data()+3); 114 | Eigen::Matrix3d Omega = skew(omega); 115 | 116 | double theta = omega.norm(); 117 | double half_theta = 0.5*theta; 118 | 119 | double imag_factor; 120 | double real_factor = cos(half_theta); 121 | if(theta<1e-10) 122 | { 123 | double theta_sq = theta*theta; 124 | double theta_po4 = theta_sq*theta_sq; 125 | imag_factor = 0.5-0.0208333*theta_sq+0.000260417*theta_po4; 126 | } 127 | else 128 | { 129 | double sin_half_theta = sin(half_theta); 130 | imag_factor = sin_half_theta/theta; 131 | } 132 | 133 | q = Eigen::Quaterniond(real_factor, imag_factor*omega.x(), imag_factor*omega.y(), imag_factor*omega.z()); 134 | 135 | 136 | Eigen::Matrix3d J; 137 | if (theta<1e-10) 138 | { 139 | J = q.matrix(); 140 | } 141 | else 142 | { 143 | Eigen::Matrix3d Omega2 = Omega*Omega; 144 | J = (Eigen::Matrix3d::Identity() + (1-cos(theta))/(theta*theta)*Omega + (theta-sin(theta))/(pow(theta,3))*Omega2); 145 | } 146 | 147 | t = J*upsilon; 148 | } 149 | 150 | Eigen::Matrix skew(Eigen::Matrix& mat_in){ 151 | Eigen::Matrix skew_mat; 152 | skew_mat.setZero(); 153 | skew_mat(0,1) = -mat_in(2); 154 | skew_mat(0,2) = mat_in(1); 155 | skew_mat(1,2) = -mat_in(0); 156 | skew_mat(1,0) = mat_in(2); 157 | skew_mat(2,0) = -mat_in(1); 158 | skew_mat(2,1) = mat_in(0); 159 | return skew_mat; 160 | } 161 | -------------------------------------------------------------------------------- /floam/src/odomEstimationClass.cpp: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | 5 | #include "odomEstimationClass.h" 6 | 7 | void OdomEstimationClass::init(lidar::Lidar lidar_param, double map_resolution){ 8 | //init local map 9 | laserCloudCornerMap = pcl::PointCloud::Ptr(new pcl::PointCloud()); 10 | laserCloudSurfMap = pcl::PointCloud::Ptr(new pcl::PointCloud()); 11 | 12 | //downsampling size 13 | downSizeFilterEdge.setLeafSize(map_resolution, map_resolution, map_resolution); 14 | downSizeFilterSurf.setLeafSize(map_resolution * 2, map_resolution * 2, map_resolution * 2); 15 | 16 | //kd-tree 17 | kdtreeEdgeMap = pcl::KdTreeFLANN::Ptr(new pcl::KdTreeFLANN()); 18 | kdtreeSurfMap = pcl::KdTreeFLANN::Ptr(new pcl::KdTreeFLANN()); 19 | 20 | odom = Eigen::Isometry3d::Identity(); 21 | last_odom = Eigen::Isometry3d::Identity(); 22 | optimization_count=2; 23 | } 24 | 25 | void OdomEstimationClass::initMapWithPoints(const pcl::PointCloud::Ptr& edge_in, const pcl::PointCloud::Ptr& surf_in){ 26 | *laserCloudCornerMap += *edge_in; 27 | *laserCloudSurfMap += *surf_in; 28 | optimization_count=12; 29 | } 30 | 31 | 32 | void OdomEstimationClass::updatePointsToMap(const pcl::PointCloud::Ptr& edge_in, const pcl::PointCloud::Ptr& surf_in){ 33 | 34 | if(optimization_count>2) 35 | optimization_count--; 36 | 37 | Eigen::Isometry3d odom_prediction = odom * (last_odom.inverse() * odom); 38 | last_odom = odom; 39 | odom = odom_prediction; 40 | 41 | q_w_curr = Eigen::Quaterniond(odom.rotation()); 42 | t_w_curr = odom.translation(); 43 | 44 | pcl::PointCloud::Ptr downsampledEdgeCloud(new pcl::PointCloud()); 45 | pcl::PointCloud::Ptr downsampledSurfCloud(new pcl::PointCloud()); 46 | downSamplingToMap(edge_in,downsampledEdgeCloud,surf_in,downsampledSurfCloud); 47 | //ROS_WARN("point nyum%d,%d",(int)downsampledEdgeCloud->points.size(), (int)downsampledSurfCloud->points.size()); 48 | if(laserCloudCornerMap->points.size()>10 && laserCloudSurfMap->points.size()>50){ 49 | kdtreeEdgeMap->setInputCloud(laserCloudCornerMap); 50 | kdtreeSurfMap->setInputCloud(laserCloudSurfMap); 51 | 52 | for (int iterCount = 0; iterCount < optimization_count; iterCount++){ 53 | ceres::LossFunction *loss_function = new ceres::HuberLoss(0.1); 54 | ceres::Problem::Options problem_options; 55 | ceres::Problem problem(problem_options); 56 | 57 | problem.AddParameterBlock(parameters, 7, new PoseSE3Parameterization()); 58 | 59 | addEdgeCostFactor(downsampledEdgeCloud,laserCloudCornerMap,problem,loss_function); 60 | addSurfCostFactor(downsampledSurfCloud,laserCloudSurfMap,problem,loss_function); 61 | 62 | ceres::Solver::Options options; 63 | options.linear_solver_type = ceres::DENSE_QR; 64 | options.max_num_iterations = 4; 65 | options.minimizer_progress_to_stdout = false; 66 | options.check_gradients = false; 67 | options.gradient_check_relative_precision = 1e-4; 68 | ceres::Solver::Summary summary; 69 | 70 | ceres::Solve(options, &problem, &summary); 71 | 72 | } 73 | }else{ 74 | printf("not enough points in map to associate, map error"); 75 | } 76 | odom = Eigen::Isometry3d::Identity(); 77 | odom.linear() = q_w_curr.toRotationMatrix(); 78 | odom.translation() = t_w_curr; 79 | addPointsToMap(downsampledEdgeCloud,downsampledSurfCloud); 80 | 81 | } 82 | 83 | void OdomEstimationClass::pointAssociateToMap(pcl::PointXYZI const *const pi, pcl::PointXYZI *const po) 84 | { 85 | Eigen::Vector3d point_curr(pi->x, pi->y, pi->z); 86 | Eigen::Vector3d point_w = q_w_curr * point_curr + t_w_curr; 87 | po->x = point_w.x(); 88 | po->y = point_w.y(); 89 | po->z = point_w.z(); 90 | po->intensity = pi->intensity; 91 | //po->intensity = 1.0; 92 | } 93 | 94 | void OdomEstimationClass::downSamplingToMap(const pcl::PointCloud::Ptr& edge_pc_in, pcl::PointCloud::Ptr& edge_pc_out, const pcl::PointCloud::Ptr& surf_pc_in, pcl::PointCloud::Ptr& surf_pc_out){ 95 | downSizeFilterEdge.setInputCloud(edge_pc_in); 96 | downSizeFilterEdge.filter(*edge_pc_out); 97 | downSizeFilterSurf.setInputCloud(surf_pc_in); 98 | downSizeFilterSurf.filter(*surf_pc_out); 99 | } 100 | 101 | void OdomEstimationClass::addEdgeCostFactor(const pcl::PointCloud::Ptr& pc_in, const pcl::PointCloud::Ptr& map_in, ceres::Problem& problem, ceres::LossFunction *loss_function){ 102 | int corner_num=0; 103 | for (int i = 0; i < (int)pc_in->points.size(); i++) 104 | { 105 | pcl::PointXYZI point_temp; 106 | pointAssociateToMap(&(pc_in->points[i]), &point_temp); 107 | 108 | std::vector pointSearchInd; 109 | std::vector pointSearchSqDis; 110 | kdtreeEdgeMap->nearestKSearch(point_temp, 5, pointSearchInd, pointSearchSqDis); 111 | if (pointSearchSqDis[4] < 1.0) 112 | { 113 | std::vector nearCorners; 114 | Eigen::Vector3d center(0, 0, 0); 115 | for (int j = 0; j < 5; j++) 116 | { 117 | Eigen::Vector3d tmp(map_in->points[pointSearchInd[j]].x, 118 | map_in->points[pointSearchInd[j]].y, 119 | map_in->points[pointSearchInd[j]].z); 120 | center = center + tmp; 121 | nearCorners.push_back(tmp); 122 | } 123 | center = center / 5.0; 124 | 125 | Eigen::Matrix3d covMat = Eigen::Matrix3d::Zero(); 126 | for (int j = 0; j < 5; j++) 127 | { 128 | Eigen::Matrix tmpZeroMean = nearCorners[j] - center; 129 | covMat = covMat + tmpZeroMean * tmpZeroMean.transpose(); 130 | } 131 | 132 | Eigen::SelfAdjointEigenSolver saes(covMat); 133 | 134 | Eigen::Vector3d unit_direction = saes.eigenvectors().col(2); 135 | Eigen::Vector3d curr_point(pc_in->points[i].x, pc_in->points[i].y, pc_in->points[i].z); 136 | if (saes.eigenvalues()[2] > 3 * saes.eigenvalues()[1]) 137 | { 138 | Eigen::Vector3d point_on_line = center; 139 | Eigen::Vector3d point_a, point_b; 140 | point_a = 0.1 * unit_direction + point_on_line; 141 | point_b = -0.1 * unit_direction + point_on_line; 142 | 143 | ceres::CostFunction *cost_function = new EdgeAnalyticCostFunction(curr_point, point_a, point_b); 144 | problem.AddResidualBlock(cost_function, loss_function, parameters); 145 | corner_num++; 146 | } 147 | } 148 | } 149 | if(corner_num<20){ 150 | printf("not enough correct points"); 151 | } 152 | 153 | } 154 | 155 | void OdomEstimationClass::addSurfCostFactor(const pcl::PointCloud::Ptr& pc_in, const pcl::PointCloud::Ptr& map_in, ceres::Problem& problem, ceres::LossFunction *loss_function){ 156 | int surf_num=0; 157 | for (int i = 0; i < (int)pc_in->points.size(); i++) 158 | { 159 | pcl::PointXYZI point_temp; 160 | pointAssociateToMap(&(pc_in->points[i]), &point_temp); 161 | std::vector pointSearchInd; 162 | std::vector pointSearchSqDis; 163 | kdtreeSurfMap->nearestKSearch(point_temp, 5, pointSearchInd, pointSearchSqDis); 164 | 165 | Eigen::Matrix matA0; 166 | Eigen::Matrix matB0 = -1 * Eigen::Matrix::Ones(); 167 | if (pointSearchSqDis[4] < 1.0) 168 | { 169 | 170 | for (int j = 0; j < 5; j++) 171 | { 172 | matA0(j, 0) = map_in->points[pointSearchInd[j]].x; 173 | matA0(j, 1) = map_in->points[pointSearchInd[j]].y; 174 | matA0(j, 2) = map_in->points[pointSearchInd[j]].z; 175 | } 176 | // find the norm of plane 177 | Eigen::Vector3d norm = matA0.colPivHouseholderQr().solve(matB0); 178 | double negative_OA_dot_norm = 1 / norm.norm(); 179 | norm.normalize(); 180 | 181 | bool planeValid = true; 182 | for (int j = 0; j < 5; j++) 183 | { 184 | // if OX * n > 0.2, then plane is not fit well 185 | if (fabs(norm(0) * map_in->points[pointSearchInd[j]].x + 186 | norm(1) * map_in->points[pointSearchInd[j]].y + 187 | norm(2) * map_in->points[pointSearchInd[j]].z + negative_OA_dot_norm) > 0.2) 188 | { 189 | planeValid = false; 190 | break; 191 | } 192 | } 193 | Eigen::Vector3d curr_point(pc_in->points[i].x, pc_in->points[i].y, pc_in->points[i].z); 194 | if (planeValid) 195 | { 196 | ceres::CostFunction *cost_function = new SurfNormAnalyticCostFunction(curr_point, norm, negative_OA_dot_norm); 197 | problem.AddResidualBlock(cost_function, loss_function, parameters); 198 | 199 | surf_num++; 200 | } 201 | } 202 | 203 | } 204 | if(surf_num<20){ 205 | printf("not enough correct points"); 206 | } 207 | 208 | } 209 | 210 | void OdomEstimationClass::addPointsToMap(const pcl::PointCloud::Ptr& downsampledEdgeCloud, const pcl::PointCloud::Ptr& downsampledSurfCloud){ 211 | 212 | for (int i = 0; i < (int)downsampledEdgeCloud->points.size(); i++) 213 | { 214 | pcl::PointXYZI point_temp; 215 | pointAssociateToMap(&downsampledEdgeCloud->points[i], &point_temp); 216 | laserCloudCornerMap->push_back(point_temp); 217 | } 218 | 219 | for (int i = 0; i < (int)downsampledSurfCloud->points.size(); i++) 220 | { 221 | pcl::PointXYZI point_temp; 222 | pointAssociateToMap(&downsampledSurfCloud->points[i], &point_temp); 223 | laserCloudSurfMap->push_back(point_temp); 224 | } 225 | 226 | double x_min = +odom.translation().x()-100; 227 | double y_min = +odom.translation().y()-100; 228 | double z_min = +odom.translation().z()-100; 229 | double x_max = +odom.translation().x()+100; 230 | double y_max = +odom.translation().y()+100; 231 | double z_max = +odom.translation().z()+100; 232 | 233 | //ROS_INFO("size : %f,%f,%f,%f,%f,%f", x_min, y_min, z_min,x_max, y_max, z_max); 234 | cropBoxFilter.setMin(Eigen::Vector4f(x_min, y_min, z_min, 1.0)); 235 | cropBoxFilter.setMax(Eigen::Vector4f(x_max, y_max, z_max, 1.0)); 236 | cropBoxFilter.setNegative(false); 237 | 238 | pcl::PointCloud::Ptr tmpCorner(new pcl::PointCloud()); 239 | pcl::PointCloud::Ptr tmpSurf(new pcl::PointCloud()); 240 | cropBoxFilter.setInputCloud(laserCloudSurfMap); 241 | cropBoxFilter.filter(*tmpSurf); 242 | cropBoxFilter.setInputCloud(laserCloudCornerMap); 243 | cropBoxFilter.filter(*tmpCorner); 244 | 245 | downSizeFilterEdge.setInputCloud(tmpSurf); 246 | downSizeFilterEdge.filter(*laserCloudSurfMap); 247 | downSizeFilterSurf.setInputCloud(tmpCorner); 248 | downSizeFilterSurf.filter(*laserCloudCornerMap); 249 | 250 | } 251 | 252 | void OdomEstimationClass::getMap(pcl::PointCloud::Ptr& laserCloudMap){ 253 | 254 | *laserCloudMap += *laserCloudSurfMap; 255 | *laserCloudMap += *laserCloudCornerMap; 256 | } 257 | 258 | OdomEstimationClass::OdomEstimationClass(){ 259 | 260 | } -------------------------------------------------------------------------------- /floam/src/odomEstimationNode.cpp: -------------------------------------------------------------------------------- 1 | // Author of FLOAM: Wang Han 2 | // Email wh200720041@gmail.com 3 | // Homepage https://wanghan.pro 4 | 5 | //c++ lib 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | //ros lib 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | //pcl lib 21 | #include 22 | #include 23 | #include 24 | 25 | //local lib 26 | #include "lidar.h" 27 | #include "odomEstimationClass.h" 28 | 29 | OdomEstimationClass odomEstimation; 30 | std::mutex mutex_lock; 31 | std::queue pointCloudEdgeBuf; 32 | std::queue pointCloudSurfBuf; 33 | std::queue pointCloudBuf; 34 | lidar::Lidar lidar_param; 35 | 36 | ros::Publisher pubLaserOdometry; 37 | void velodyneSurfHandler(const sensor_msgs::PointCloud2ConstPtr &laserCloudMsg) 38 | { 39 | mutex_lock.lock(); 40 | pointCloudSurfBuf.push(laserCloudMsg); 41 | mutex_lock.unlock(); 42 | } 43 | void velodyneEdgeHandler(const sensor_msgs::PointCloud2ConstPtr &laserCloudMsg) 44 | { 45 | mutex_lock.lock(); 46 | pointCloudEdgeBuf.push(laserCloudMsg); 47 | mutex_lock.unlock(); 48 | } 49 | void velodyneHandler(const sensor_msgs::PointCloud2ConstPtr &laserCloudMsg) 50 | { 51 | mutex_lock.lock(); 52 | pointCloudBuf.push(laserCloudMsg); 53 | mutex_lock.unlock(); 54 | } 55 | 56 | bool is_odom_inited = false; 57 | double total_time =0; 58 | int total_frame=0; 59 | void odom_estimation(){ 60 | while(1){ 61 | if(!pointCloudEdgeBuf.empty() && !pointCloudSurfBuf.empty()&& !pointCloudBuf.empty()){ 62 | 63 | //read data 64 | mutex_lock.lock(); 65 | if(!pointCloudBuf.empty() && (pointCloudBuf.front()->header.stamp.toSec()header.stamp.toSec()-0.5*lidar_param.scan_period || pointCloudBuf.front()->header.stamp.toSec()header.stamp.toSec()-0.5*lidar_param.scan_period)){ 66 | ROS_WARN("time stamp unaligned error and odom discarded, pls check your data --> odom correction"); 67 | pointCloudBuf.pop(); 68 | mutex_lock.unlock(); 69 | continue; 70 | } 71 | 72 | if(!pointCloudSurfBuf.empty() && (pointCloudSurfBuf.front()->header.stamp.toSec()header.stamp.toSec()-0.5*lidar_param.scan_period || pointCloudSurfBuf.front()->header.stamp.toSec()header.stamp.toSec()-0.5*lidar_param.scan_period)){ 73 | pointCloudSurfBuf.pop(); 74 | ROS_INFO("time stamp unaligned with extra point cloud, pls check your data --> odom correction"); 75 | mutex_lock.unlock(); 76 | continue; 77 | } 78 | 79 | if(!pointCloudEdgeBuf.empty() && (pointCloudEdgeBuf.front()->header.stamp.toSec()header.stamp.toSec()-0.5*lidar_param.scan_period || pointCloudEdgeBuf.front()->header.stamp.toSec()header.stamp.toSec()-0.5*lidar_param.scan_period)){ 80 | pointCloudEdgeBuf.pop(); 81 | ROS_INFO("time stamp unaligned with extra point cloud, pls check your data --> odom correction"); 82 | mutex_lock.unlock(); 83 | continue; 84 | } 85 | //if time aligned 86 | 87 | pcl::PointCloud::Ptr pointcloud_surf_in(new pcl::PointCloud()); 88 | pcl::PointCloud::Ptr pointcloud_edge_in(new pcl::PointCloud()); 89 | pcl::PointCloud::Ptr pointcloud_in(new pcl::PointCloud()); 90 | pcl::fromROSMsg(*pointCloudEdgeBuf.front(), *pointcloud_edge_in); 91 | pcl::fromROSMsg(*pointCloudSurfBuf.front(), *pointcloud_surf_in); 92 | pcl::fromROSMsg(*pointCloudBuf.front(), *pointcloud_in); 93 | ros::Time pointcloud_time = (pointCloudBuf.front())->header.stamp; 94 | pointCloudEdgeBuf.pop(); 95 | pointCloudSurfBuf.pop(); 96 | pointCloudBuf.pop(); 97 | mutex_lock.unlock(); 98 | 99 | if(is_odom_inited == false){ 100 | odomEstimation.initMapWithPoints(pointcloud_edge_in, pointcloud_surf_in); 101 | is_odom_inited = true; 102 | ROS_INFO("odom inited"); 103 | }else{ 104 | std::chrono::time_point start, end; 105 | start = std::chrono::system_clock::now(); 106 | odomEstimation.updatePointsToMap(pointcloud_edge_in, pointcloud_surf_in); 107 | end = std::chrono::system_clock::now(); 108 | std::chrono::duration elapsed_seconds = end - start; 109 | total_frame++; 110 | float time_temp = elapsed_seconds.count() * 1000; 111 | total_time+=time_temp; 112 | ROS_INFO("average odom estimation time %f ms \n \n", total_time/total_frame); 113 | } 114 | 115 | 116 | 117 | Eigen::Quaterniond q_current(odomEstimation.odom.rotation()); 118 | //q_current.normalize(); 119 | Eigen::Vector3d t_current = odomEstimation.odom.translation(); 120 | 121 | static tf::TransformBroadcaster br; 122 | tf::Transform transform; 123 | transform.setOrigin( tf::Vector3(t_current.x(), t_current.y(), t_current.z()) ); 124 | tf::Quaternion q(q_current.x(),q_current.y(),q_current.z(),q_current.w()); 125 | transform.setRotation(q); 126 | br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "map", "base_link")); 127 | 128 | // publish odometry 129 | nav_msgs::Odometry laserOdometry; 130 | laserOdometry.header.frame_id = "/map"; 131 | laserOdometry.child_frame_id = "/base_link"; 132 | laserOdometry.header.stamp = pointcloud_time; 133 | laserOdometry.pose.pose.orientation.x = q_current.x(); 134 | laserOdometry.pose.pose.orientation.y = q_current.y(); 135 | laserOdometry.pose.pose.orientation.z = q_current.z(); 136 | laserOdometry.pose.pose.orientation.w = q_current.w(); 137 | laserOdometry.pose.pose.position.x = t_current.x(); 138 | laserOdometry.pose.pose.position.y = t_current.y(); 139 | laserOdometry.pose.pose.position.z = t_current.z(); 140 | pubLaserOdometry.publish(laserOdometry); 141 | 142 | } 143 | //sleep 2 ms every time 144 | std::chrono::milliseconds dura(2); 145 | std::this_thread::sleep_for(dura); 146 | } 147 | } 148 | 149 | int main(int argc, char **argv) 150 | { 151 | ros::init(argc, argv, "main"); 152 | ros::NodeHandle nh; 153 | 154 | int scan_line = 64; 155 | double vertical_angle = 2.0; 156 | double scan_period= 0.1; 157 | double max_dis = 60.0; 158 | double min_dis = 2.0; 159 | double map_resolution = 0.4; 160 | nh.getParam("/scan_period", scan_period); 161 | nh.getParam("/vertical_angle", vertical_angle); 162 | nh.getParam("/max_dis", max_dis); 163 | nh.getParam("/min_dis", min_dis); 164 | nh.getParam("/scan_line", scan_line); 165 | nh.getParam("/map_resolution", map_resolution); 166 | 167 | lidar_param.setScanPeriod(scan_period); 168 | lidar_param.setVerticalAngle(vertical_angle); 169 | lidar_param.setLines(scan_line); 170 | lidar_param.setMaxDistance(max_dis); 171 | lidar_param.setMinDistance(min_dis); 172 | 173 | odomEstimation.init(lidar_param, map_resolution); 174 | ros::Subscriber subLaserCloud = nh.subscribe("/velodyne_points_filtered", 100, velodyneHandler); 175 | ros::Subscriber subEdgeLaserCloud = nh.subscribe("/laser_cloud_edge", 100, velodyneEdgeHandler); 176 | ros::Subscriber subSurfLaserCloud = nh.subscribe("/laser_cloud_surf", 100, velodyneSurfHandler); 177 | 178 | pubLaserOdometry = nh.advertise("/odom", 100); 179 | std::thread odom_estimation_process{odom_estimation}; 180 | 181 | ros::spin(); 182 | 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /livox_camera_calib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | project(livox_camera_calib) 3 | 4 | ## Compile as C++11, supported in ROS Kinetic and newer 5 | add_compile_options(-std=c++11) 6 | #SET(CMAKE_BUILD_TYPE "Debug") 7 | #SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb") 8 | #SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") 9 | ## Find catkin macros and libraries 10 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) 11 | ## is used, also find other catkin packages 12 | find_package(catkin REQUIRED COMPONENTS 13 | cv_bridge 14 | pcl_conversions 15 | pcl_ros 16 | roscpp 17 | rospy 18 | sensor_msgs 19 | std_msgs 20 | ) 21 | 22 | ## System dependencies are found with CMake's conventions 23 | # find_package(Boost REQUIRED COMPONENTS system) 24 | find_package(PCL REQUIRED) 25 | find_package(OpenCV) 26 | find_package(Threads) 27 | find_package(Ceres REQUIRED) 28 | 29 | set(CMAKE_AUTOMOC ON) 30 | set(CMAKE_AUTOUIC ON) 31 | set(CMAKE_AUTORCC ON) 32 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 33 | 34 | catkin_package( 35 | CATKIN_DEPENDS roscpp rospy std_msgs 36 | ) 37 | 38 | include_directories( 39 | # include 40 | ${catkin_INCLUDE_DIRS} 41 | ${PCL_INCLUDE_DIRS} 42 | ${OpenCV_INCLUDE_DIRS} 43 | ) 44 | 45 | add_executable(lidar_camera_calib src/lidar_camera_calib.cpp include/lidar_camera_calib.hpp) 46 | 47 | 48 | target_link_libraries(lidar_camera_calib 49 | ${catkin_LIBRARIES} ${OpenCV_LIBS} ${PCL_LIBRARIES} ${CERES_LIBRARIES} 50 | ) -------------------------------------------------------------------------------- /livox_camera_calib/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /livox_camera_calib/README.md: -------------------------------------------------------------------------------- 1 | # livox_camera_calib 2 | **livox_camera_calib** is a robust, high accuracy extrinsic calibration tool between high resolution LiDAR (e.g. Livox) and camera in targetless environment. Our algorithm can run in both indoor and outdoor scenes, and only requires edge information in the scene. If the scene is suitable, we can achieve pixel-level accuracy similar to or even beyond the target based method. 3 |
4 | 5 | An example of a outdoor calibration scenario. We color the point cloud with the calibrated extrinsic and compare with actual image. A and C are locally enlarged 6 | views of the point cloud. B and D are parts of the camera image 7 | corresponding to point cloud in A and C. 8 |
9 | 10 | ## Info 11 | New features in next version: 12 | 1. Support spinning LiDAR 13 | 2. Support muti-scenes calibration (more accuracy) 14 | 15 | ## Related paper 16 | Related paper available on arxiv: 17 | [Pixel-level Extrinsic Self Calibration of High Resolution LiDAR and Camera in Targetless Environments](http://arxiv.org/abs/2103.01627) 18 | ## Related video 19 | Related video: https://youtu.be/e6Vkkasc4JI 20 | 21 | ## 1. Prerequisites 22 | ### 1.1 **Ubuntu** and **ROS** 23 | Ubuntu 64-bit 16.04 or 18.04. 24 | ROS Kinetic or Melodic. [ROS Installation](http://wiki.ros.org/ROS/Installation) and its additional ROS pacakge: 25 | 26 | ``` 27 | sudo apt-get install ros-XXX-cv-bridge ros-xxx-pcl-conversions 28 | ``` 29 | 30 | ### 1.2 **Eigen** 31 | Follow [Eigen Installation](http://eigen.tuxfamily.org/index.php?title=Main_Page) 32 | 33 | ### 1.3 **Ceres Solver** 34 | Follow [Ceres Installation](http://ceres-solver.org/installation.html). 35 | 36 | ### 1.4 **PCL** 37 | Follow [PCL Installation](http://www.pointclouds.org/downloads/linux.html). (Our code is tested with PCL1.7) 38 | 39 | ## 2. Build 40 | Clone the repository and catkin_make: 41 | 42 | ``` 43 | cd ~/catkin_ws/src 44 | git clone https://github.com/hku-mars/livox_camera_calib.git 45 | cd ../ 46 | catkin_make 47 | source ~/catkin_ws/devel/setup.bash 48 | ``` 49 | 50 | ## 3. Run our example 51 | Download [Our recorded rosbag](https://drive.google.com/drive/folders/1pBvE_nrg60IUo7PXDRsbBwDI68sq5LS6?usp=sharing) to your local path, and then change the path in **calib.launch** to your data path. Then directly run 52 | ``` 53 | roslaunch livox_camera_calib calib.launch 54 | ``` 55 | If you have trouble in downloading the rosbag files, you can download the same files from Baidu net-disk. 56 | ``` 57 | Link (链接): https://pan.baidu.com/s/197hsjmO42p5OIUjo_l4kkg 58 | Extraction code (提取码): myfm 59 | ``` 60 | ## 4. Run on your own sensor set 61 | ### 4.1 Record data 62 | Record the point cloud and image msg to rosbag (15s or more for avia LiDAR). Then change the topic name in **config_outdoor.yaml** file 63 | ``` 64 | # Topic name in rosbag 65 | PointCloudTopic: "/livox/lidar" 66 | ImageTopic: "/camera/color/image_raw" 67 | ``` 68 | ### 4.2 Modify some params 69 | Modify the camera matrix and distortion coeffs in **camera.yaml** 70 | Modify the initial extrinsic in **config_outdoor.yaml** if needed. 71 | -------------------------------------------------------------------------------- /livox_camera_calib/config/camera.yaml: -------------------------------------------------------------------------------- 1 | %YAML:1.0 2 | 3 | #-------------------------------------------------------------------------------------------- 4 | # Camera Parameters. Adjust them! 5 | #-------------------------------------------------------------------------------------------- 6 | CameraMat: !!opencv-matrix 7 | rows: 3 8 | cols: 3 9 | dt: d 10 | data: [1364.45, 0.0, 958.327, 11 | 0.0, 1366.46, 535.074, 12 | 0.0, 0.0, 1.0 ] 13 | 14 | DistCoeffs: !!opencv-matrix 15 | rows: 5 16 | cols: 1 17 | dt: d 18 | data: [0.0958277, -0.198233, -0.000147133, -0.000430056, 0.000000] 19 | 20 | Camera.width: 1920 21 | Camera.height: 1080 -------------------------------------------------------------------------------- /livox_camera_calib/config/camera_rsbpearl32.yaml: -------------------------------------------------------------------------------- 1 | %YAML:1.0 2 | 3 | #-------------------------------------------------------------------------------------------- 4 | # Camera Parameters. Adjust them! 5 | #-------------------------------------------------------------------------------------------- 6 | CameraMat: !!opencv-matrix 7 | rows: 3 8 | cols: 3 9 | dt: d 10 | data: [1024.7016748143822, 0.0, 622.8605183708166, 11 | 0.0, 1023.1503530251063, 496.6387099662122, 12 | 0.0, 0.0, 1.0 ] 13 | 14 | DistCoeffs: !!opencv-matrix 15 | rows: 5 16 | cols: 1 17 | dt: d 18 | data: [-0.10068352831478207, 0.08447613701201621, -0.001409292470421658, 0.00011165438636820547, 0.00] 19 | 20 | Camera.width: 1280 21 | Camera.height: 1024 -------------------------------------------------------------------------------- /livox_camera_calib/config/camera_velodyne16.yaml: -------------------------------------------------------------------------------- 1 | %YAML:1.0 2 | 3 | #-------------------------------------------------------------------------------------------- 4 | # Camera Parameters. Adjust them! 5 | #-------------------------------------------------------------------------------------------- 6 | CameraMat: !!opencv-matrix 7 | rows: 3 8 | cols: 3 9 | dt: d 10 | data: [1286.4489785410851, 0.0, 332.3412167683782, 11 | 0.0, 1285.641345482317, 248.4184594533393, 12 | 0.0, 0.0, 1.0 ] 13 | 14 | DistCoeffs: !!opencv-matrix 15 | rows: 5 16 | cols: 1 17 | dt: d 18 | data: [-0.46463017999326606, 0.16866292690557916, 0.002553599253969859, 0.004120572503497365, 0.000000] 19 | 20 | Camera.width: 752 21 | Camera.height: 480 -------------------------------------------------------------------------------- /livox_camera_calib/config/camera_velodyne16_r.yaml: -------------------------------------------------------------------------------- 1 | %YAML:1.0 2 | 3 | #-------------------------------------------------------------------------------------------- 4 | # Camera Parameters. Adjust them! 5 | #-------------------------------------------------------------------------------------------- 6 | CameraMat: !!opencv-matrix 7 | rows: 3 8 | cols: 3 9 | dt: d 10 | data: [1286.318605305509, 0.0, 348.2756577700413, 11 | 0.0, 1286.2481372799468, 232.076787756208, 12 | 0.0, 0.0, 1.0 ] 13 | 14 | DistCoeffs: !!opencv-matrix 15 | rows: 5 16 | cols: 1 17 | dt: d 18 | data: [-0.4258521033804439, 0.10879431301912121, 0.0006922985911202098, 0.0028835121456787143, 0.000000] 19 | 20 | Camera.width: 752 21 | Camera.height: 480 -------------------------------------------------------------------------------- /livox_camera_calib/config/config_indoor.yaml: -------------------------------------------------------------------------------- 1 | %YAML:1.0 2 | 3 | # Topic name in rosbag 4 | PointCloudTopic: "/livox/lidar" 5 | ImageTopic: "/camera/color/image_raw" 6 | 7 | # Lidar Data type(custom msg or pointcloud2) 8 | Data.custom_msg: 0 9 | # Initial extrinsic (usually provided by hand measurement or cad design) 10 | 11 | ExtrinsicMat: !!opencv-matrix 12 | rows: 4 13 | cols: 4 14 | dt: d 15 | data: [0.0, -1.0, 0.0, 0.0, 16 | 0.0, 0.0, -1.0, 0.0, 17 | 1.0, 0.0, 0.0, 0.0, 18 | 0.0, 0.0, 0.0, 1.0] 19 | # Params for Canny Edge Extraction 20 | 21 | Canny.gray_threshold: 10 22 | Canny.len_threshold: 100 23 | 24 | # Params for Voxel Cutting & Plane Fitting & Edge Extraction 25 | Voxel.size: 0.5 26 | Voxel.down_sample_size: 0.02 27 | Plane.min_points_size: 30 28 | Plane.normal_theta_min: 30 29 | Plane.normal_theta_max: 150 30 | Plane.max_size: 8 31 | Ransac.dis_threshold: 0.015 32 | Edge.min_dis_threshold: 0.03 33 | Edge.max_dis_threshold: 0.05 34 | AdjustEuler1: -2 35 | AdjustEuler2: -0.5 36 | AdjustEuler3: 2 37 | 38 | # Params for color point clouds 39 | Color.dense: 0.1 40 | Color.intensity_threshold: 5 41 | -------------------------------------------------------------------------------- /livox_camera_calib/config/config_indoor_rsbpearl32.yaml: -------------------------------------------------------------------------------- 1 | %YAML:1.0 2 | 3 | # Topic name in rosbag 4 | PointCloudTopic: "/lidar_pointcloud" 5 | ImageTopic: "/camera_0/image" 6 | 7 | # Lidar Data type(custom msg or pointcloud2) 8 | Data.custom_msg: 0 9 | # Initial extrinsic (usually provided by hand measurement or cad design) 10 | 11 | ExtrinsicMat: !!opencv-matrix 12 | rows: 4 13 | cols: 4 14 | dt: d 15 | data: [0.0, -1.0, 0.0, 0.0, 16 | 1.0, 0.0, 0.0, 0.0, 17 | 0.0, 0.0, 1.0, 0.0, 18 | 0.0, 0.0, 0.0, 1.0] 19 | # Params for Canny Edge Extraction 20 | 21 | Canny.gray_threshold: 10 22 | Canny.len_threshold: 100 23 | 24 | # Params for Voxel Cutting & Plane Fitting & Edge Extraction 25 | Voxel.size: 0.5 26 | Voxel.down_sample_size: 0.02 27 | Plane.min_points_size: 30 28 | Plane.normal_theta_min: 30 29 | Plane.normal_theta_max: 150 30 | Plane.max_size: 8 31 | Ransac.dis_threshold: 0.015 32 | Edge.min_dis_threshold: 0.03 33 | Edge.max_dis_threshold: 0.05 34 | AdjustEuler1: -2 35 | AdjustEuler2: -0.5 36 | AdjustEuler3: 2 37 | 38 | # Params for color point clouds 39 | Color.dense: 0.1 40 | Color.intensity_threshold: 5 41 | -------------------------------------------------------------------------------- /livox_camera_calib/config/config_indoor_velodyne16.yaml: -------------------------------------------------------------------------------- 1 | %YAML:1.0 2 | 3 | # Topic name in rosbag 4 | PointCloudTopic: "/velodyne_points" 5 | ImageTopic: "/camera_0/image" 6 | 7 | # Lidar Data type(custom msg or pointcloud2) 8 | Data.custom_msg: 0 9 | # Initial extrinsic (usually provided by hand measurement or cad design) 10 | 11 | ExtrinsicMat: !!opencv-matrix 12 | rows: 4 13 | cols: 4 14 | dt: d 15 | data: [0.0, -1.0, 0.0, 0.0, 16 | 0.0, 0.0, -1.0, 0.0, 17 | 1.0, 0.0, 0.0, 0.0, 18 | 0.0, 0.0, 0.0, 1.0] 19 | # data: [ 0.0624976,-0.998038,0.0038172,0, 20 | # 0.0310611,-0.00187779,-0.999516,0, 21 | # 0.997562,0.0625859,0.0308828, 0, 22 | # 0,0,0,1] 23 | # Params for Canny Edge Extraction 24 | 25 | Canny.gray_threshold: 10 26 | Canny.len_threshold: 100 27 | 28 | # Params for Voxel Cutting & Plane Fitting & Edge Extraction 29 | Voxel.size: 0.5 30 | Voxel.down_sample_size: 0.02 31 | Plane.min_points_size: 30 32 | Plane.normal_theta_min: 30 33 | Plane.normal_theta_max: 150 34 | Plane.max_size: 8 35 | Ransac.dis_threshold: 0.015 36 | Edge.min_dis_threshold: 0.03 37 | Edge.max_dis_threshold: 0.05 38 | AdjustEuler1: -0.5 39 | AdjustEuler2: -0.5 40 | AdjustEuler3: 0.1 41 | 42 | # Params for color point clouds 43 | Color.dense: 0.1 44 | Color.intensity_threshold: 5 45 | -------------------------------------------------------------------------------- /livox_camera_calib/config/config_indoor_velodyne16_r.yaml: -------------------------------------------------------------------------------- 1 | %YAML:1.0 2 | 3 | # Topic name in rosbag 4 | PointCloudTopic: "/velodyne_points" 5 | ImageTopic: "/camera_1/image" 6 | 7 | # Lidar Data type(custom msg or pointcloud2) 8 | Data.custom_msg: 0 9 | # Initial extrinsic (usually provided by hand measurement or cad design) 10 | 11 | ExtrinsicMat: !!opencv-matrix 12 | rows: 4 13 | cols: 4 14 | dt: d 15 | data: [0.0, -1.0, 0.0, 0.0, 16 | 0.0, 0.0, -1.0, 0.0, 17 | 1.0, 0.0, 0.0, 0.0, 18 | 0.0, 0.0, 0.0, 1.0] 19 | # Params for Canny Edge Extraction 20 | 21 | Canny.gray_threshold: 10 22 | Canny.len_threshold: 100 23 | 24 | # Params for Voxel Cutting & Plane Fitting & Edge Extraction 25 | Voxel.size: 0.5 26 | Voxel.down_sample_size: 0.02 27 | Plane.min_points_size: 30 28 | Plane.normal_theta_min: 30 29 | Plane.normal_theta_max: 150 30 | Plane.max_size: 8 31 | Ransac.dis_threshold: 0.015 32 | Edge.min_dis_threshold: 0.03 33 | Edge.max_dis_threshold: 0.05 34 | AdjustEuler1: -2 35 | AdjustEuler2: -0.5 36 | AdjustEuler3: 2 37 | 38 | # Params for color point clouds 39 | Color.dense: 0.1 40 | Color.intensity_threshold: 5 41 | -------------------------------------------------------------------------------- /livox_camera_calib/config/config_outdoor.yaml: -------------------------------------------------------------------------------- 1 | %YAML:1.0 2 | 3 | # Topic name in rosbag 4 | PointCloudTopic: "/livox/lidar" 5 | ImageTopic: "/camera/color/image_raw" 6 | 7 | # Lidar Data type(custom msg or pointcloud2) 8 | Data.custom_msg: 0 9 | # Initial extrinsic (usually provided by hand measurement or cad design) 10 | 11 | ExtrinsicMat: !!opencv-matrix 12 | rows: 4 13 | cols: 4 14 | dt: d 15 | data: [0.0, -1.0, 0.0, 0.0, 16 | 0.0, 0.0, -1.0, 0.0, 17 | 1.0, 0.0, 0.0, 0.0, 18 | 0.0, 0.0, 0.0, 1.0] 19 | # Params for Canny Edge Extraction 20 | 21 | Canny.gray_threshold: 10 22 | Canny.len_threshold: 200 23 | 24 | # Params for Voxel Cutting & Plane Fitting & Edge Extraction 25 | Voxel.size: 1.0 26 | Voxel.down_sample_size: 0.02 27 | Plane.min_points_size: 60 28 | Plane.normal_theta_min: 30 29 | Plane.normal_theta_max: 150 30 | Plane.max_size: 5 31 | Ransac.dis_threshold: 0.015 32 | Ransac.iter_num: 200 33 | Edge.min_dis_threshold: 0.03 34 | Edge.max_dis_threshold: 0.06 35 | # Add error to initial extrinsic to get a worse initialization 36 | AdjustEuler1: -2 37 | AdjustEuler2: -2 38 | AdjustEuler3: 2 39 | 40 | 41 | # Params for color point clouds 42 | Color.dense: 1 43 | Color.intensity_threshold: 10 -------------------------------------------------------------------------------- /livox_camera_calib/include/CustomMsg.h: -------------------------------------------------------------------------------- 1 | // Generated by gencpp from file livox_ros_driver/CustomMsg.msg 2 | // DO NOT EDIT! 3 | 4 | 5 | #ifndef LIVOX_ROS_DRIVER_MESSAGE_CUSTOMMSG_H 6 | #define LIVOX_ROS_DRIVER_MESSAGE_CUSTOMMSG_H 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include "CustomPoint.h" 20 | 21 | namespace livox_ros_driver 22 | { 23 | template 24 | struct CustomMsg_ 25 | { 26 | typedef CustomMsg_ Type; 27 | 28 | CustomMsg_() 29 | : header() 30 | , timebase(0) 31 | , point_num(0) 32 | , lidar_id(0) 33 | , rsvd() 34 | , points() { 35 | rsvd.assign(0); 36 | } 37 | CustomMsg_(const ContainerAllocator& _alloc) 38 | : header(_alloc) 39 | , timebase(0) 40 | , point_num(0) 41 | , lidar_id(0) 42 | , rsvd() 43 | , points(_alloc) { 44 | (void)_alloc; 45 | rsvd.assign(0); 46 | } 47 | 48 | 49 | 50 | typedef ::std_msgs::Header_ _header_type; 51 | _header_type header; 52 | 53 | typedef uint64_t _timebase_type; 54 | _timebase_type timebase; 55 | 56 | typedef uint32_t _point_num_type; 57 | _point_num_type point_num; 58 | 59 | typedef uint8_t _lidar_id_type; 60 | _lidar_id_type lidar_id; 61 | 62 | typedef boost::array _rsvd_type; 63 | _rsvd_type rsvd; 64 | 65 | typedef std::vector< ::livox_ros_driver::CustomPoint_ , typename ContainerAllocator::template rebind< ::livox_ros_driver::CustomPoint_ >::other > _points_type; 66 | _points_type points; 67 | 68 | 69 | 70 | 71 | 72 | typedef boost::shared_ptr< ::livox_ros_driver::CustomMsg_ > Ptr; 73 | typedef boost::shared_ptr< ::livox_ros_driver::CustomMsg_ const> ConstPtr; 74 | 75 | }; // struct CustomMsg_ 76 | 77 | typedef ::livox_ros_driver::CustomMsg_ > CustomMsg; 78 | 79 | typedef boost::shared_ptr< ::livox_ros_driver::CustomMsg > CustomMsgPtr; 80 | typedef boost::shared_ptr< ::livox_ros_driver::CustomMsg const> CustomMsgConstPtr; 81 | 82 | // constants requiring out of line definition 83 | 84 | 85 | 86 | template 87 | std::ostream& operator<<(std::ostream& s, const ::livox_ros_driver::CustomMsg_ & v) 88 | { 89 | ros::message_operations::Printer< ::livox_ros_driver::CustomMsg_ >::stream(s, "", v); 90 | return s; 91 | } 92 | 93 | } // namespace livox_ros_driver 94 | 95 | namespace ros 96 | { 97 | namespace message_traits 98 | { 99 | 100 | 101 | 102 | // BOOLTRAITS {'IsFixedSize': False, 'IsMessage': True, 'HasHeader': True} 103 | // {'std_msgs': ['/opt/ros/kinetic/share/std_msgs/cmake/../msg'], 'livox_ros_driver': ['/home/dji/Documents/data_set/src/livox_ros_driver/livox_ros_driver/msg']} 104 | 105 | // !!!!!!!!!!! ['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_parsed_fields', 'constants', 'fields', 'full_name', 'has_header', 'header_present', 'names', 'package', 'parsed_fields', 'short_name', 'text', 'types'] 106 | 107 | 108 | 109 | 110 | template 111 | struct IsFixedSize< ::livox_ros_driver::CustomMsg_ > 112 | : FalseType 113 | { }; 114 | 115 | template 116 | struct IsFixedSize< ::livox_ros_driver::CustomMsg_ const> 117 | : FalseType 118 | { }; 119 | 120 | template 121 | struct IsMessage< ::livox_ros_driver::CustomMsg_ > 122 | : TrueType 123 | { }; 124 | 125 | template 126 | struct IsMessage< ::livox_ros_driver::CustomMsg_ const> 127 | : TrueType 128 | { }; 129 | 130 | template 131 | struct HasHeader< ::livox_ros_driver::CustomMsg_ > 132 | : TrueType 133 | { }; 134 | 135 | template 136 | struct HasHeader< ::livox_ros_driver::CustomMsg_ const> 137 | : TrueType 138 | { }; 139 | 140 | 141 | template 142 | struct MD5Sum< ::livox_ros_driver::CustomMsg_ > 143 | { 144 | static const char* value() 145 | { 146 | return "e4d6829bdfe657cb6c21a746c86b21a6"; 147 | } 148 | 149 | static const char* value(const ::livox_ros_driver::CustomMsg_&) { return value(); } 150 | static const uint64_t static_value1 = 0xe4d6829bdfe657cbULL; 151 | static const uint64_t static_value2 = 0x6c21a746c86b21a6ULL; 152 | }; 153 | 154 | template 155 | struct DataType< ::livox_ros_driver::CustomMsg_ > 156 | { 157 | static const char* value() 158 | { 159 | return "livox_ros_driver/CustomMsg"; 160 | } 161 | 162 | static const char* value(const ::livox_ros_driver::CustomMsg_&) { return value(); } 163 | }; 164 | 165 | template 166 | struct Definition< ::livox_ros_driver::CustomMsg_ > 167 | { 168 | static const char* value() 169 | { 170 | return "# Livox publish pointcloud msg format.\n\ 171 | \n\ 172 | Header header # ROS standard message header\n\ 173 | uint64 timebase # The time of first point\n\ 174 | uint32 point_num # Total number of pointclouds\n\ 175 | uint8 lidar_id # Lidar device id number\n\ 176 | uint8[3] rsvd # Reserved use\n\ 177 | CustomPoint[] points # Pointcloud data\n\ 178 | \n\ 179 | \n\ 180 | ================================================================================\n\ 181 | MSG: std_msgs/Header\n\ 182 | # Standard metadata for higher-level stamped data types.\n\ 183 | # This is generally used to communicate timestamped data \n\ 184 | # in a particular coordinate frame.\n\ 185 | # \n\ 186 | # sequence ID: consecutively increasing ID \n\ 187 | uint32 seq\n\ 188 | #Two-integer timestamp that is expressed as:\n\ 189 | # * stamp.sec: seconds (stamp_secs) since epoch (in Python the variable is called 'secs')\n\ 190 | # * stamp.nsec: nanoseconds since stamp_secs (in Python the variable is called 'nsecs')\n\ 191 | # time-handling sugar is provided by the client library\n\ 192 | time stamp\n\ 193 | #Frame this data is associated with\n\ 194 | # 0: no frame\n\ 195 | # 1: global frame\n\ 196 | string frame_id\n\ 197 | \n\ 198 | ================================================================================\n\ 199 | MSG: livox_ros_driver/CustomPoint\n\ 200 | # Livox costom pointcloud format.\n\ 201 | \n\ 202 | uint32 offset_time # offset time relative to the base time\n\ 203 | float32 x # X axis, unit:m\n\ 204 | float32 y # Y axis, unit:m\n\ 205 | float32 z # Z axis, unit:m\n\ 206 | uint8 reflectivity # reflectivity, 0~255\n\ 207 | uint8 tag # livox tag\n\ 208 | uint8 line # laser number in lidar\n\ 209 | \n\ 210 | "; 211 | } 212 | 213 | static const char* value(const ::livox_ros_driver::CustomMsg_&) { return value(); } 214 | }; 215 | 216 | } // namespace message_traits 217 | } // namespace ros 218 | 219 | namespace ros 220 | { 221 | namespace serialization 222 | { 223 | 224 | template struct Serializer< ::livox_ros_driver::CustomMsg_ > 225 | { 226 | template inline static void allInOne(Stream& stream, T m) 227 | { 228 | stream.next(m.header); 229 | stream.next(m.timebase); 230 | stream.next(m.point_num); 231 | stream.next(m.lidar_id); 232 | stream.next(m.rsvd); 233 | stream.next(m.points); 234 | } 235 | 236 | ROS_DECLARE_ALLINONE_SERIALIZER 237 | }; // struct CustomMsg_ 238 | 239 | } // namespace serialization 240 | } // namespace ros 241 | 242 | namespace ros 243 | { 244 | namespace message_operations 245 | { 246 | 247 | template 248 | struct Printer< ::livox_ros_driver::CustomMsg_ > 249 | { 250 | template static void stream(Stream& s, const std::string& indent, const ::livox_ros_driver::CustomMsg_& v) 251 | { 252 | s << indent << "header: "; 253 | s << std::endl; 254 | Printer< ::std_msgs::Header_ >::stream(s, indent + " ", v.header); 255 | s << indent << "timebase: "; 256 | Printer::stream(s, indent + " ", v.timebase); 257 | s << indent << "point_num: "; 258 | Printer::stream(s, indent + " ", v.point_num); 259 | s << indent << "lidar_id: "; 260 | Printer::stream(s, indent + " ", v.lidar_id); 261 | s << indent << "rsvd[]" << std::endl; 262 | for (size_t i = 0; i < v.rsvd.size(); ++i) 263 | { 264 | s << indent << " rsvd[" << i << "]: "; 265 | Printer::stream(s, indent + " ", v.rsvd[i]); 266 | } 267 | s << indent << "points[]" << std::endl; 268 | for (size_t i = 0; i < v.points.size(); ++i) 269 | { 270 | s << indent << " points[" << i << "]: "; 271 | s << std::endl; 272 | s << indent; 273 | Printer< ::livox_ros_driver::CustomPoint_ >::stream(s, indent + " ", v.points[i]); 274 | } 275 | } 276 | }; 277 | 278 | } // namespace message_operations 279 | } // namespace ros 280 | 281 | #endif // LIVOX_ROS_DRIVER_MESSAGE_CUSTOMMSG_H 282 | -------------------------------------------------------------------------------- /livox_camera_calib/include/CustomPoint.h: -------------------------------------------------------------------------------- 1 | // Generated by gencpp from file livox_ros_driver/CustomPoint.msg 2 | // DO NOT EDIT! 3 | 4 | 5 | #ifndef LIVOX_ROS_DRIVER_MESSAGE_CUSTOMPOINT_H 6 | #define LIVOX_ROS_DRIVER_MESSAGE_CUSTOMPOINT_H 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | namespace livox_ros_driver 20 | { 21 | template 22 | struct CustomPoint_ 23 | { 24 | typedef CustomPoint_ Type; 25 | 26 | CustomPoint_() 27 | : offset_time(0) 28 | , x(0.0) 29 | , y(0.0) 30 | , z(0.0) 31 | , reflectivity(0) 32 | , tag(0) 33 | , line(0) { 34 | } 35 | CustomPoint_(const ContainerAllocator& _alloc) 36 | : offset_time(0) 37 | , x(0.0) 38 | , y(0.0) 39 | , z(0.0) 40 | , reflectivity(0) 41 | , tag(0) 42 | , line(0) { 43 | (void)_alloc; 44 | } 45 | 46 | 47 | 48 | typedef uint32_t _offset_time_type; 49 | _offset_time_type offset_time; 50 | 51 | typedef float _x_type; 52 | _x_type x; 53 | 54 | typedef float _y_type; 55 | _y_type y; 56 | 57 | typedef float _z_type; 58 | _z_type z; 59 | 60 | typedef uint8_t _reflectivity_type; 61 | _reflectivity_type reflectivity; 62 | 63 | typedef uint8_t _tag_type; 64 | _tag_type tag; 65 | 66 | typedef uint8_t _line_type; 67 | _line_type line; 68 | 69 | 70 | 71 | 72 | 73 | typedef boost::shared_ptr< ::livox_ros_driver::CustomPoint_ > Ptr; 74 | typedef boost::shared_ptr< ::livox_ros_driver::CustomPoint_ const> ConstPtr; 75 | 76 | }; // struct CustomPoint_ 77 | 78 | typedef ::livox_ros_driver::CustomPoint_ > CustomPoint; 79 | 80 | typedef boost::shared_ptr< ::livox_ros_driver::CustomPoint > CustomPointPtr; 81 | typedef boost::shared_ptr< ::livox_ros_driver::CustomPoint const> CustomPointConstPtr; 82 | 83 | // constants requiring out of line definition 84 | 85 | 86 | 87 | template 88 | std::ostream& operator<<(std::ostream& s, const ::livox_ros_driver::CustomPoint_ & v) 89 | { 90 | ros::message_operations::Printer< ::livox_ros_driver::CustomPoint_ >::stream(s, "", v); 91 | return s; 92 | } 93 | 94 | } // namespace livox_ros_driver 95 | 96 | namespace ros 97 | { 98 | namespace message_traits 99 | { 100 | 101 | 102 | 103 | // BOOLTRAITS {'IsFixedSize': True, 'IsMessage': True, 'HasHeader': False} 104 | // {'std_msgs': ['/opt/ros/kinetic/share/std_msgs/cmake/../msg'], 'livox_ros_driver': ['/home/dji/Documents/data_set/src/livox_ros_driver/livox_ros_driver/msg']} 105 | 106 | // !!!!!!!!!!! ['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_parsed_fields', 'constants', 'fields', 'full_name', 'has_header', 'header_present', 'names', 'package', 'parsed_fields', 'short_name', 'text', 'types'] 107 | 108 | 109 | 110 | 111 | template 112 | struct IsFixedSize< ::livox_ros_driver::CustomPoint_ > 113 | : TrueType 114 | { }; 115 | 116 | template 117 | struct IsFixedSize< ::livox_ros_driver::CustomPoint_ const> 118 | : TrueType 119 | { }; 120 | 121 | template 122 | struct IsMessage< ::livox_ros_driver::CustomPoint_ > 123 | : TrueType 124 | { }; 125 | 126 | template 127 | struct IsMessage< ::livox_ros_driver::CustomPoint_ const> 128 | : TrueType 129 | { }; 130 | 131 | template 132 | struct HasHeader< ::livox_ros_driver::CustomPoint_ > 133 | : FalseType 134 | { }; 135 | 136 | template 137 | struct HasHeader< ::livox_ros_driver::CustomPoint_ const> 138 | : FalseType 139 | { }; 140 | 141 | 142 | template 143 | struct MD5Sum< ::livox_ros_driver::CustomPoint_ > 144 | { 145 | static const char* value() 146 | { 147 | return "109a3cc548bb1f96626be89a5008bd6d"; 148 | } 149 | 150 | static const char* value(const ::livox_ros_driver::CustomPoint_&) { return value(); } 151 | static const uint64_t static_value1 = 0x109a3cc548bb1f96ULL; 152 | static const uint64_t static_value2 = 0x626be89a5008bd6dULL; 153 | }; 154 | 155 | template 156 | struct DataType< ::livox_ros_driver::CustomPoint_ > 157 | { 158 | static const char* value() 159 | { 160 | return "livox_ros_driver/CustomPoint"; 161 | } 162 | 163 | static const char* value(const ::livox_ros_driver::CustomPoint_&) { return value(); } 164 | }; 165 | 166 | template 167 | struct Definition< ::livox_ros_driver::CustomPoint_ > 168 | { 169 | static const char* value() 170 | { 171 | return "# Livox costom pointcloud format.\n\ 172 | \n\ 173 | uint32 offset_time # offset time relative to the base time\n\ 174 | float32 x # X axis, unit:m\n\ 175 | float32 y # Y axis, unit:m\n\ 176 | float32 z # Z axis, unit:m\n\ 177 | uint8 reflectivity # reflectivity, 0~255\n\ 178 | uint8 tag # livox tag\n\ 179 | uint8 line # laser number in lidar\n\ 180 | \n\ 181 | "; 182 | } 183 | 184 | static const char* value(const ::livox_ros_driver::CustomPoint_&) { return value(); } 185 | }; 186 | 187 | } // namespace message_traits 188 | } // namespace ros 189 | 190 | namespace ros 191 | { 192 | namespace serialization 193 | { 194 | 195 | template struct Serializer< ::livox_ros_driver::CustomPoint_ > 196 | { 197 | template inline static void allInOne(Stream& stream, T m) 198 | { 199 | stream.next(m.offset_time); 200 | stream.next(m.x); 201 | stream.next(m.y); 202 | stream.next(m.z); 203 | stream.next(m.reflectivity); 204 | stream.next(m.tag); 205 | stream.next(m.line); 206 | } 207 | 208 | ROS_DECLARE_ALLINONE_SERIALIZER 209 | }; // struct CustomPoint_ 210 | 211 | } // namespace serialization 212 | } // namespace ros 213 | 214 | namespace ros 215 | { 216 | namespace message_operations 217 | { 218 | 219 | template 220 | struct Printer< ::livox_ros_driver::CustomPoint_ > 221 | { 222 | template static void stream(Stream& s, const std::string& indent, const ::livox_ros_driver::CustomPoint_& v) 223 | { 224 | s << indent << "offset_time: "; 225 | Printer::stream(s, indent + " ", v.offset_time); 226 | s << indent << "x: "; 227 | Printer::stream(s, indent + " ", v.x); 228 | s << indent << "y: "; 229 | Printer::stream(s, indent + " ", v.y); 230 | s << indent << "z: "; 231 | Printer::stream(s, indent + " ", v.z); 232 | s << indent << "reflectivity: "; 233 | Printer::stream(s, indent + " ", v.reflectivity); 234 | s << indent << "tag: "; 235 | Printer::stream(s, indent + " ", v.tag); 236 | s << indent << "line: "; 237 | Printer::stream(s, indent + " ", v.line); 238 | } 239 | }; 240 | 241 | } // namespace message_operations 242 | } // namespace ros 243 | 244 | #endif // LIVOX_ROS_DRIVER_MESSAGE_CUSTOMPOINT_H 245 | -------------------------------------------------------------------------------- /livox_camera_calib/include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef LIDAR_CAMERA_COMMON_H 2 | #define LIDAR_CAMERA_COMMON_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | struct PnPData { 10 | double x, y, z, u, v; 11 | }; 12 | 13 | struct VPnPData { 14 | double x, y, z, u, v; 15 | Eigen::Vector2d direction; 16 | Eigen::Vector2d direction_lidar; 17 | int number; 18 | }; 19 | 20 | typedef Eigen::Matrix Vector6d; 21 | //存放每一个点的索引值和其对应的曲率 22 | typedef struct PCURVATURE { 23 | // POINT3F cPoint; 24 | int index; 25 | float curvature; 26 | } PCURVATURE; 27 | 28 | typedef struct Plane { 29 | pcl::PointCloud cloud; 30 | pcl::PointXYZ p_center; 31 | Eigen::Vector3d normal; 32 | int index; 33 | } Plane; 34 | 35 | class VOXEL_LOC { 36 | public: 37 | int64_t x, y, z; 38 | 39 | VOXEL_LOC(int64_t vx = 0, int64_t vy = 0, int64_t vz = 0) 40 | : x(vx), y(vy), z(vz) {} 41 | 42 | bool operator==(const VOXEL_LOC &other) const { 43 | return (x == other.x && y == other.y && z == other.z); 44 | } 45 | }; 46 | 47 | // Hash value 48 | namespace std { 49 | template <> struct hash { 50 | size_t operator()(const VOXEL_LOC &s) const { 51 | using std::size_t; 52 | using std::hash; 53 | return ((hash()(s.x) ^ (hash()(s.y) << 1)) >> 1) ^ 54 | (hash()(s.z) << 1); 55 | } 56 | }; 57 | } 58 | 59 | struct M_POINT { 60 | float xyz[3]; 61 | float intensity; 62 | int count = 0; 63 | }; 64 | 65 | using namespace std; 66 | 67 | template void input(T matrix[4][5]) { 68 | cout << "please input matrix element's data" << endl; 69 | for (int i = 1; i < 4; i++) { 70 | for (int j = 1; j < 5; j++) { 71 | cin >> matrix[i][j]; 72 | } 73 | } 74 | cout << "input ok"; 75 | } 76 | 77 | template void calc(T matrix[4][5], Eigen::Vector3d &solution) { 78 | T base_D = matrix[1][1] * matrix[2][2] * matrix[3][3] + 79 | matrix[2][1] * matrix[3][2] * matrix[1][3] + 80 | matrix[3][1] * matrix[1][2] * matrix[2][3]; //计算行列式 81 | base_D = base_D - (matrix[1][3] * matrix[2][2] * matrix[3][1] + 82 | matrix[1][1] * matrix[2][3] * matrix[3][2] + 83 | matrix[1][2] * matrix[2][1] * matrix[3][3]); 84 | 85 | if (base_D != 0) { 86 | T x_D = matrix[1][4] * matrix[2][2] * matrix[3][3] + 87 | matrix[2][4] * matrix[3][2] * matrix[1][3] + 88 | matrix[3][4] * matrix[1][2] * matrix[2][3]; 89 | x_D = x_D - (matrix[1][3] * matrix[2][2] * matrix[3][4] + 90 | matrix[1][4] * matrix[2][3] * matrix[3][2] + 91 | matrix[1][2] * matrix[2][4] * matrix[3][3]); 92 | T y_D = matrix[1][1] * matrix[2][4] * matrix[3][3] + 93 | matrix[2][1] * matrix[3][4] * matrix[1][3] + 94 | matrix[3][1] * matrix[1][4] * matrix[2][3]; 95 | y_D = y_D - (matrix[1][3] * matrix[2][4] * matrix[3][1] + 96 | matrix[1][1] * matrix[2][3] * matrix[3][4] + 97 | matrix[1][4] * matrix[2][1] * matrix[3][3]); 98 | T z_D = matrix[1][1] * matrix[2][2] * matrix[3][4] + 99 | matrix[2][1] * matrix[3][2] * matrix[1][4] + 100 | matrix[3][1] * matrix[1][2] * matrix[2][4]; 101 | z_D = z_D - (matrix[1][4] * matrix[2][2] * matrix[3][1] + 102 | matrix[1][1] * matrix[2][4] * matrix[3][2] + 103 | matrix[1][2] * matrix[2][1] * matrix[3][4]); 104 | 105 | T x = x_D / base_D; 106 | T y = y_D / base_D; 107 | T z = z_D / base_D; 108 | // cout << "[ x:" << x << "; y:" << y << "; z:" << z << " ]" << endl; 109 | solution[0] = x; 110 | solution[1] = y; 111 | solution[2] = z; 112 | } else { 113 | cout << "【无解】"; 114 | solution[0] = 0; 115 | solution[1] = 0; 116 | solution[2] = 0; 117 | // return DBL_MIN; 118 | } 119 | } 120 | 121 | // Similar with PCL voxelgrid filter 122 | void down_sampling_voxel(pcl::PointCloud &pl_feat, 123 | double voxel_size) { 124 | int intensity = rand() % 255; 125 | if (voxel_size < 0.01) { 126 | return; 127 | } 128 | std::unordered_map feat_map; 129 | uint plsize = pl_feat.size(); 130 | 131 | for (uint i = 0; i < plsize; i++) { 132 | pcl::PointXYZI &p_c = pl_feat[i]; 133 | float loc_xyz[3]; 134 | for (int j = 0; j < 3; j++) { 135 | loc_xyz[j] = p_c.data[j] / voxel_size; 136 | if (loc_xyz[j] < 0) { 137 | loc_xyz[j] -= 1.0; 138 | } 139 | } 140 | 141 | VOXEL_LOC position((int64_t)loc_xyz[0], (int64_t)loc_xyz[1], 142 | (int64_t)loc_xyz[2]); 143 | auto iter = feat_map.find(position); 144 | if (iter != feat_map.end()) { 145 | iter->second.xyz[0] += p_c.x; 146 | iter->second.xyz[1] += p_c.y; 147 | iter->second.xyz[2] += p_c.z; 148 | iter->second.intensity += p_c.intensity; 149 | iter->second.count++; 150 | } else { 151 | M_POINT anp; 152 | anp.xyz[0] = p_c.x; 153 | anp.xyz[1] = p_c.y; 154 | anp.xyz[2] = p_c.z; 155 | anp.intensity = p_c.intensity; 156 | anp.count = 1; 157 | feat_map[position] = anp; 158 | } 159 | } 160 | plsize = feat_map.size(); 161 | pl_feat.clear(); 162 | pl_feat.resize(plsize); 163 | 164 | uint i = 0; 165 | for (auto iter = feat_map.begin(); iter != feat_map.end(); ++iter) { 166 | pl_feat[i].x = iter->second.xyz[0] / iter->second.count; 167 | pl_feat[i].y = iter->second.xyz[1] / iter->second.count; 168 | pl_feat[i].z = iter->second.xyz[2] / iter->second.count; 169 | pl_feat[i].intensity = iter->second.intensity / iter->second.count; 170 | i++; 171 | } 172 | } 173 | 174 | void rgb2grey(const cv::Mat &rgb_image, cv::Mat &grey_img) { 175 | for (int x = 0; x < rgb_image.cols; x++) { 176 | for (int y = 0; y < rgb_image.rows; y++) { 177 | grey_img.at(y, x) = 1.0 / 3.0 * rgb_image.at(y, x)[0] + 178 | 1.0 / 3.0 * rgb_image.at(y, x)[1] + 179 | 1.0 / 3.0 * rgb_image.at(y, x)[2]; 180 | } 181 | } 182 | } 183 | 184 | void mapJet(double v, double vmin, double vmax, uint8_t &r, uint8_t &g, 185 | uint8_t &b) { 186 | r = 255; 187 | g = 255; 188 | b = 255; 189 | 190 | if (v < vmin) { 191 | v = vmin; 192 | } 193 | 194 | if (v > vmax) { 195 | v = vmax; 196 | } 197 | 198 | double dr, dg, db; 199 | 200 | if (v < 0.1242) { 201 | db = 0.504 + ((1. - 0.504) / 0.1242) * v; 202 | dg = dr = 0.; 203 | } else if (v < 0.3747) { 204 | db = 1.; 205 | dr = 0.; 206 | dg = (v - 0.1242) * (1. / (0.3747 - 0.1242)); 207 | } else if (v < 0.6253) { 208 | db = (0.6253 - v) * (1. / (0.6253 - 0.3747)); 209 | dg = 1.; 210 | dr = (v - 0.3747) * (1. / (0.6253 - 0.3747)); 211 | } else if (v < 0.8758) { 212 | db = 0.; 213 | dr = 1.; 214 | dg = (0.8758 - v) * (1. / (0.8758 - 0.6253)); 215 | } else { 216 | db = 0.; 217 | dg = 0.; 218 | dr = 1. - (v - 0.8758) * ((1. - 0.504) / (1. - 0.8758)); 219 | } 220 | 221 | r = (uint8_t)(255 * dr); 222 | g = (uint8_t)(255 * dg); 223 | b = (uint8_t)(255 * db); 224 | } 225 | typedef struct VoxelGrid { 226 | float size = 0.5; 227 | int index; 228 | Eigen::Vector3d origin; 229 | pcl::PointCloud cloud; 230 | } VoxelGrid; 231 | 232 | typedef struct Voxel { 233 | float size; 234 | Eigen::Vector3d voxel_origin; 235 | Eigen::Vector3d voxel_color; 236 | pcl::PointCloud::Ptr cloud; 237 | Voxel(float _size) : size(_size) { 238 | voxel_origin << 0, 0, 0; 239 | cloud = pcl::PointCloud::Ptr( 240 | new pcl::PointCloud); 241 | }; 242 | } Voxel; 243 | 244 | #endif -------------------------------------------------------------------------------- /livox_camera_calib/launch/calib_indoor_livox.launch: -------------------------------------------------------------------------------- 1 | 2 | 11 | 17 | -------------------------------------------------------------------------------- /livox_camera_calib/launch/calib_indoor_rsbpearl32.launch: -------------------------------------------------------------------------------- 1 | 2 | 12 | 18 | -------------------------------------------------------------------------------- /livox_camera_calib/launch/calib_indoor_velodyne16.launch: -------------------------------------------------------------------------------- 1 | 2 | 12 | 18 | -------------------------------------------------------------------------------- /livox_camera_calib/launch/calib_indoor_velodyne16_r.launch: -------------------------------------------------------------------------------- 1 | 2 | 12 | 18 | -------------------------------------------------------------------------------- /livox_camera_calib/launch/calib_outdoor.launch: -------------------------------------------------------------------------------- 1 | 2 | 12 | 18 | -------------------------------------------------------------------------------- /livox_camera_calib/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | livox_camera_calib 4 | 1.0.0 5 | The livox_camera_calib package 6 | 7 | 8 | 9 | 10 | ycj 11 | 12 | 13 | 14 | 15 | 16 | TODO 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | catkin 50 | cv_bridge 51 | libpcl-all-dev 52 | roscpp 53 | rospy 54 | sensor_msgs 55 | std_msgs 56 | cv_bridge 57 | libpcl-all-dev 58 | roscpp 59 | rospy 60 | sensor_msgs 61 | std_msgs 62 | cv_bridge 63 | libpcl-all 64 | roscpp 65 | rospy 66 | sensor_msgs 67 | std_msgs 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /livox_camera_calib/pics/color_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AFEICHINA/extended_lidar_camera_calib/dcb4a852be780104f8f52edf6e684fb0d83076dc/livox_camera_calib/pics/color_cloud.png -------------------------------------------------------------------------------- /livox_camera_calib/result/extrinsic copy.txt: -------------------------------------------------------------------------------- 1 | 0.0624976,-0.998038,0.0038172,0.0175561 2 | 0.0310611,-0.00187779,-0.999516,-0.0703278 3 | 0.997562,0.0625859,0.0308828,0.0234781 4 | 0,0,0,1 5 | 176.415,178.22,-179.892,0,0,0 6 | -------------------------------------------------------------------------------- /livox_camera_calib/result/extrinsic.txt: -------------------------------------------------------------------------------- 1 | 0.0616033,-0.998053,0.00979492,0.0234355 2 | 0.0274342,-0.00811668,-0.999591,-0.045908 3 | 0.997724,0.0618468,0.0268808,0.012567 4 | 0,0,0,1 5 | 176.467,178.428,-179.535,0,0,0 6 | -------------------------------------------------------------------------------- /livox_camera_calib/rviz_cfg/calib.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz/Displays 3 | Help Height: 78 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Global Options1 8 | - /Status1 9 | - /PointCloud21 10 | - /PointCloud22 11 | - /PointCloud23 12 | - /Image1 13 | - /PointCloud24 14 | Splitter Ratio: 0.5 15 | Tree Height: 572 16 | - Class: rviz/Selection 17 | Name: Selection 18 | - Class: rviz/Tool Properties 19 | Expanded: 20 | - /2D Pose Estimate1 21 | - /2D Nav Goal1 22 | - /Publish Point1 23 | Name: Tool Properties 24 | Splitter Ratio: 0.588679016 25 | - Class: rviz/Views 26 | Expanded: 27 | - /Current View1 28 | Name: Views 29 | Splitter Ratio: 0.5 30 | - Class: rviz/Time 31 | Experimental: false 32 | Name: Time 33 | SyncMode: 0 34 | SyncSource: "" 35 | Toolbars: 36 | toolButtonStyle: 2 37 | Visualization Manager: 38 | Class: "" 39 | Displays: 40 | - Alpha: 0.5 41 | Cell Size: 1 42 | Class: rviz/Grid 43 | Color: 160; 160; 164 44 | Enabled: false 45 | Line Style: 46 | Line Width: 0.0299999993 47 | Value: Lines 48 | Name: Grid 49 | Normal Cell Count: 0 50 | Offset: 51 | X: 0 52 | Y: 0 53 | Z: 0 54 | Plane: XY 55 | Plane Cell Count: 10 56 | Reference Frame: 57 | Value: false 58 | - Class: rviz/Axes 59 | Enabled: false 60 | Length: 1 61 | Name: Axes 62 | Radius: 0.100000001 63 | Reference Frame: 64 | Value: false 65 | - Alpha: 1 66 | Autocompute Intensity Bounds: true 67 | Autocompute Value Bounds: 68 | Max Value: 11.993 69 | Min Value: -31.8570004 70 | Value: true 71 | Axis: Z 72 | Channel Name: intensity 73 | Class: rviz/PointCloud2 74 | Color: 255; 255; 255 75 | Color Transformer: RGB8 76 | Decay Time: 10000 77 | Enabled: true 78 | Invert Rainbow: false 79 | Max Color: 255; 255; 255 80 | Max Intensity: 155 81 | Min Color: 0; 0; 0 82 | Min Intensity: 0 83 | Name: PointCloud2 84 | Position Transformer: XYZ 85 | Queue Size: 10 86 | Selectable: true 87 | Size (Pixels): 1 88 | Size (m): 0.00499999989 89 | Style: Flat Squares 90 | Topic: /rgb_cloud 91 | Unreliable: false 92 | Use Fixed Frame: true 93 | Use rainbow: true 94 | Value: true 95 | - Alpha: 0.5 96 | Autocompute Intensity Bounds: true 97 | Autocompute Value Bounds: 98 | Max Value: 10 99 | Min Value: -10 100 | Value: true 101 | Axis: Z 102 | Channel Name: intensity 103 | Class: rviz/PointCloud2 104 | Color: 255; 255; 255 105 | Color Transformer: RGB8 106 | Decay Time: 10000 107 | Enabled: true 108 | Invert Rainbow: false 109 | Max Color: 255; 255; 255 110 | Max Intensity: 4096 111 | Min Color: 0; 0; 0 112 | Min Intensity: 0 113 | Name: PointCloud2 114 | Position Transformer: XYZ 115 | Queue Size: 10 116 | Selectable: true 117 | Size (Pixels): 3 118 | Size (m): 0.00999999978 119 | Style: Flat Squares 120 | Topic: /planner_cloud 121 | Unreliable: false 122 | Use Fixed Frame: true 123 | Use rainbow: true 124 | Value: true 125 | - Alpha: 1 126 | Autocompute Intensity Bounds: true 127 | Autocompute Value Bounds: 128 | Max Value: 10 129 | Min Value: -10 130 | Value: true 131 | Axis: Z 132 | Channel Name: intensity 133 | Class: rviz/PointCloud2 134 | Color: 255; 0; 0 135 | Color Transformer: FlatColor 136 | Decay Time: 10000 137 | Enabled: true 138 | Invert Rainbow: false 139 | Max Color: 255; 255; 255 140 | Max Intensity: 100 141 | Min Color: 0; 0; 0 142 | Min Intensity: 100 143 | Name: PointCloud2 144 | Position Transformer: XYZ 145 | Queue Size: 10 146 | Selectable: true 147 | Size (Pixels): 8 148 | Size (m): 0.0500000007 149 | Style: Flat Squares 150 | Topic: /line_cloud 151 | Unreliable: false 152 | Use Fixed Frame: true 153 | Use rainbow: true 154 | Value: true 155 | - Class: rviz/Image 156 | Enabled: true 157 | Image Topic: /camera_image 158 | Max Value: 1 159 | Median window: 5 160 | Min Value: 0 161 | Name: Image 162 | Normalize Range: true 163 | Queue Size: 2 164 | Transport Hint: raw 165 | Unreliable: false 166 | Value: true 167 | - Alpha: 1 168 | Autocompute Intensity Bounds: true 169 | Autocompute Value Bounds: 170 | Max Value: 10 171 | Min Value: -10 172 | Value: true 173 | Axis: Z 174 | Channel Name: intensity 175 | Class: rviz/PointCloud2 176 | Color: 255; 255; 255 177 | Color Transformer: RGB8 178 | Decay Time: 0 179 | Enabled: false 180 | Invert Rainbow: false 181 | Max Color: 255; 255; 255 182 | Max Intensity: 4096 183 | Min Color: 0; 0; 0 184 | Min Intensity: 0 185 | Name: PointCloud2 186 | Position Transformer: XYZ 187 | Queue Size: 10 188 | Selectable: true 189 | Size (Pixels): 3 190 | Size (m): 0.00999999978 191 | Style: Flat Squares 192 | Topic: /init_rgb_cloud 193 | Unreliable: false 194 | Use Fixed Frame: true 195 | Use rainbow: true 196 | Value: false 197 | Enabled: true 198 | Global Options: 199 | Background Color: 0; 0; 0 200 | Default Light: true 201 | Fixed Frame: livox 202 | Frame Rate: 30 203 | Name: root 204 | Tools: 205 | - Class: rviz/Interact 206 | Hide Inactive Objects: true 207 | - Class: rviz/MoveCamera 208 | - Class: rviz/Select 209 | - Class: rviz/FocusCamera 210 | - Class: rviz/Measure 211 | - Class: rviz/SetInitialPose 212 | Topic: /initialpose 213 | - Class: rviz/SetGoal 214 | Topic: /move_base_simple/goal 215 | - Class: rviz/PublishPoint 216 | Single click: true 217 | Topic: /clicked_point 218 | Value: true 219 | Views: 220 | Current: 221 | Class: rviz/Orbit 222 | Distance: 18.9548416 223 | Enable Stereo Rendering: 224 | Stereo Eye Separation: 0.0599999987 225 | Stereo Focal Distance: 1 226 | Swap Stereo Eyes: false 227 | Value: false 228 | Focal Point: 229 | X: 4.98300934 230 | Y: 1.0172627 231 | Z: 0.278002739 232 | Focal Shape Fixed Size: true 233 | Focal Shape Size: 0.0500000007 234 | Invert Z Axis: false 235 | Name: Current View 236 | Near Clip Distance: 0.00999999978 237 | Pitch: 0.0424800999 238 | Target Frame: 239 | Value: Orbit (rviz) 240 | Yaw: 2.99037695 241 | Saved: 242 | - Class: rviz/Orbit 243 | Distance: 10.4962521 244 | Enable Stereo Rendering: 245 | Stereo Eye Separation: 0.0599999987 246 | Stereo Focal Distance: 1 247 | Swap Stereo Eyes: false 248 | Value: false 249 | Focal Point: 250 | X: 4.3698473 251 | Y: 0.713867247 252 | Z: 1.94276571 253 | Focal Shape Fixed Size: true 254 | Focal Shape Size: 0.0500000007 255 | Invert Z Axis: false 256 | Name: voxel_size 257 | Near Clip Distance: 0.00999999978 258 | Pitch: 0.0947974101 259 | Target Frame: 260 | Value: Orbit (rviz) 261 | Yaw: 3.07537961 262 | - Class: rviz/Orbit 263 | Distance: 10.4962521 264 | Enable Stereo Rendering: 265 | Stereo Eye Separation: 0.0599999987 266 | Stereo Focal Distance: 1 267 | Swap Stereo Eyes: false 268 | Value: false 269 | Focal Point: 270 | X: 4.27507019 271 | Y: 0.0635859966 272 | Z: 1.95490003 273 | Focal Shape Fixed Size: true 274 | Focal Shape Size: 0.0500000007 275 | Invert Z Axis: false 276 | Name: edge 277 | Near Clip Distance: 0.00999999978 278 | Pitch: 0.00247973995 279 | Target Frame: 280 | Value: Orbit (rviz) 281 | Yaw: 3.09037995 282 | - Class: rviz/Orbit 283 | Distance: 9.21914196 284 | Enable Stereo Rendering: 285 | Stereo Eye Separation: 0.0599999987 286 | Stereo Focal Distance: 1 287 | Swap Stereo Eyes: false 288 | Value: false 289 | Focal Point: 290 | X: 4.75317574 291 | Y: -0.375257581 292 | Z: 1.01962817 293 | Focal Shape Fixed Size: true 294 | Focal Shape Size: 0.0500000007 295 | Invert Z Axis: false 296 | Name: varus_voxel_size 297 | Near Clip Distance: 0.00999999978 298 | Pitch: 0.232480064 299 | Target Frame: 300 | Value: Orbit (rviz) 301 | Yaw: 3.09537673 302 | Window Geometry: 303 | Displays: 304 | collapsed: false 305 | Height: 1056 306 | Hide Left Dock: false 307 | Hide Right Dock: true 308 | Image: 309 | collapsed: false 310 | QMainWindow State: 000000ff00000000fd00000004000000000000016a000003dafc020000000afb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006100fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c0061007900730100000028000002cb000000d700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006501000002f9000001090000001600fffffffb0000000a0049006d00610067006501000002f4000000ca0000000000000000000000010000010f000003dafc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a005600690065007700730000000028000003da000000ad00fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000073f0000003efc0100000002fb0000000800540069006d00650200000041000003fa0000073f0000003efb0000000800540069006d00650100000000000004500000000000000000000005cf000003da00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 311 | Selection: 312 | collapsed: false 313 | Time: 314 | collapsed: false 315 | Tool Properties: 316 | collapsed: false 317 | Views: 318 | collapsed: true 319 | Width: 1855 320 | X: 65 321 | Y: 24 322 | -------------------------------------------------------------------------------- /livox_camera_calib/rviz_cfg/calib_indoor.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz/Displays 3 | Help Height: 78 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Global Options1 8 | - /Status1 9 | - /PointCloud23 10 | Splitter Ratio: 0.5 11 | Tree Height: 294 12 | - Class: rviz/Selection 13 | Name: Selection 14 | - Class: rviz/Tool Properties 15 | Expanded: 16 | - /2D Pose Estimate1 17 | - /2D Nav Goal1 18 | - /Publish Point1 19 | Name: Tool Properties 20 | Splitter Ratio: 0.5886790156364441 21 | - Class: rviz/Views 22 | Expanded: 23 | - /Current View1 24 | Name: Views 25 | Splitter Ratio: 0.5 26 | - Class: rviz/Time 27 | Experimental: false 28 | Name: Time 29 | SyncMode: 0 30 | SyncSource: PointCloud2 31 | Preferences: 32 | PromptSaveOnExit: true 33 | Toolbars: 34 | toolButtonStyle: 2 35 | Visualization Manager: 36 | Class: "" 37 | Displays: 38 | - Alpha: 0.5 39 | Cell Size: 1 40 | Class: rviz/Grid 41 | Color: 160; 160; 164 42 | Enabled: false 43 | Line Style: 44 | Line Width: 0.029999999329447746 45 | Value: Lines 46 | Name: Grid 47 | Normal Cell Count: 0 48 | Offset: 49 | X: 0 50 | Y: 0 51 | Z: 0 52 | Plane: XY 53 | Plane Cell Count: 10 54 | Reference Frame: 55 | Value: false 56 | - Class: rviz/Axes 57 | Enabled: false 58 | Length: 1 59 | Name: Axes 60 | Radius: 0.10000000149011612 61 | Reference Frame: 62 | Value: false 63 | - Alpha: 1 64 | Autocompute Intensity Bounds: true 65 | Autocompute Value Bounds: 66 | Max Value: 10 67 | Min Value: -10 68 | Value: true 69 | Axis: Z 70 | Channel Name: intensity 71 | Class: rviz/PointCloud2 72 | Color: 255; 255; 255 73 | Color Transformer: RGB8 74 | Decay Time: 10000 75 | Enabled: true 76 | Invert Rainbow: false 77 | Max Color: 255; 255; 255 78 | Max Intensity: 155 79 | Min Color: 0; 0; 0 80 | Min Intensity: 0 81 | Name: PointCloud2 82 | Position Transformer: XYZ 83 | Queue Size: 10 84 | Selectable: true 85 | Size (Pixels): 1 86 | Size (m): 0.004999999888241291 87 | Style: Flat Squares 88 | Topic: /rgb_cloud 89 | Unreliable: false 90 | Use Fixed Frame: true 91 | Use rainbow: true 92 | Value: true 93 | - Alpha: 1 94 | Autocompute Intensity Bounds: true 95 | Autocompute Value Bounds: 96 | Max Value: 10 97 | Min Value: -10 98 | Value: true 99 | Axis: Z 100 | Channel Name: intensity 101 | Class: rviz/PointCloud2 102 | Color: 255; 255; 255 103 | Color Transformer: RGB8 104 | Decay Time: 100 105 | Enabled: true 106 | Invert Rainbow: false 107 | Max Color: 255; 255; 255 108 | Max Intensity: 4096 109 | Min Color: 0; 0; 0 110 | Min Intensity: 0 111 | Name: PointCloud2 112 | Position Transformer: XYZ 113 | Queue Size: 10 114 | Selectable: true 115 | Size (Pixels): 3 116 | Size (m): 0.009999999776482582 117 | Style: Flat Squares 118 | Topic: /planner_cloud 119 | Unreliable: false 120 | Use Fixed Frame: true 121 | Use rainbow: true 122 | Value: true 123 | - Alpha: 1 124 | Autocompute Intensity Bounds: true 125 | Autocompute Value Bounds: 126 | Max Value: 10 127 | Min Value: -10 128 | Value: true 129 | Axis: Z 130 | Channel Name: intensity 131 | Class: rviz/PointCloud2 132 | Color: 255; 255; 255 133 | Color Transformer: FlatColor 134 | Decay Time: 100 135 | Enabled: true 136 | Invert Rainbow: false 137 | Max Color: 255; 255; 255 138 | Max Intensity: 100 139 | Min Color: 0; 0; 0 140 | Min Intensity: 100 141 | Name: PointCloud2 142 | Position Transformer: XYZ 143 | Queue Size: 10 144 | Selectable: true 145 | Size (Pixels): 8 146 | Size (m): 0.05000000074505806 147 | Style: Flat Squares 148 | Topic: /line_cloud 149 | Unreliable: false 150 | Use Fixed Frame: true 151 | Use rainbow: true 152 | Value: true 153 | - Class: rviz/Image 154 | Enabled: true 155 | Image Topic: /camera_image 156 | Max Value: 1 157 | Median window: 5 158 | Min Value: 0 159 | Name: Image 160 | Normalize Range: true 161 | Queue Size: 2 162 | Transport Hint: raw 163 | Unreliable: false 164 | Value: true 165 | Enabled: true 166 | Global Options: 167 | Background Color: 0; 0; 0 168 | Default Light: true 169 | Fixed Frame: livox 170 | Frame Rate: 30 171 | Name: root 172 | Tools: 173 | - Class: rviz/Interact 174 | Hide Inactive Objects: true 175 | - Class: rviz/MoveCamera 176 | - Class: rviz/Select 177 | - Class: rviz/FocusCamera 178 | - Class: rviz/Measure 179 | - Class: rviz/SetInitialPose 180 | Theta std deviation: 0.2617993950843811 181 | Topic: /initialpose 182 | X std deviation: 0.5 183 | Y std deviation: 0.5 184 | - Class: rviz/SetGoal 185 | Topic: /move_base_simple/goal 186 | - Class: rviz/PublishPoint 187 | Single click: true 188 | Topic: /clicked_point 189 | Value: true 190 | Views: 191 | Current: 192 | Class: rviz/Orbit 193 | Distance: 8.994850158691406 194 | Enable Stereo Rendering: 195 | Stereo Eye Separation: 0.05999999865889549 196 | Stereo Focal Distance: 1 197 | Swap Stereo Eyes: false 198 | Value: false 199 | Focal Point: 200 | X: -0.4866918921470642 201 | Y: -0.43485867977142334 202 | Z: 4.625232696533203 203 | Focal Shape Fixed Size: true 204 | Focal Shape Size: 0.05000000074505806 205 | Invert Z Axis: false 206 | Name: Current View 207 | Near Clip Distance: 0.009999999776482582 208 | Pitch: -1.4647964239120483 209 | Target Frame: 210 | Value: Orbit (rviz) 211 | Yaw: 3.170358180999756 212 | Saved: ~ 213 | Window Geometry: 214 | Displays: 215 | collapsed: false 216 | Height: 759 217 | Hide Left Dock: false 218 | Hide Right Dock: true 219 | Image: 220 | collapsed: false 221 | QMainWindow State: 000000ff00000000fd00000004000000000000016a00000259fc020000000afb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000001b1000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006501000001f4000000a20000001600fffffffb0000000a0049006d00610067006501000002f4000000ca0000000000000000000000010000010f00000259fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d00000259000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004f30000003efc0100000002fb0000000800540069006d00650100000000000004f3000002eb00fffffffb0000000800540069006d00650100000000000004500000000000000000000003830000025900000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 222 | Selection: 223 | collapsed: false 224 | Time: 225 | collapsed: false 226 | Tool Properties: 227 | collapsed: false 228 | Views: 229 | collapsed: true 230 | Width: 1267 231 | X: 160 232 | Y: 75 233 | -------------------------------------------------------------------------------- /livox_camera_calib/src/lidar_camera_calib.cpp: -------------------------------------------------------------------------------- 1 | #include "include/lidar_camera_calib.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "ceres/ceres.h" 7 | #include "include/common.h" 8 | 9 | #define add_error 10 | // instrins matrix 11 | Eigen::Matrix3d inner; 12 | // Distortion coefficient 13 | Eigen::Vector4d distor; 14 | Eigen::Vector4d quaternion; 15 | Eigen::Vector3d transation; 16 | 17 | // Normal pnp solution 18 | class pnp_calib { 19 | public: 20 | pnp_calib(PnPData p) { pd = p; } 21 | template 22 | bool operator()(const T *_q, const T *_t, T *residuals) const { 23 | Eigen::Matrix innerT = inner.cast(); 24 | Eigen::Matrix distorT = distor.cast(); 25 | Eigen::Quaternion q_incre{_q[3], _q[0], _q[1], _q[2]}; 26 | Eigen::Matrix t_incre{_t[0], _t[1], _t[2]}; 27 | Eigen::Matrix p_l(T(pd.x), T(pd.y), T(pd.z)); 28 | Eigen::Matrix p_c = q_incre.toRotationMatrix() * p_l + t_incre; 29 | Eigen::Matrix p_2 = innerT * p_c; 30 | T uo = p_2[0] / p_2[2]; 31 | T vo = p_2[1] / p_2[2]; 32 | const T &fx = innerT.coeffRef(0, 0); 33 | const T &cx = innerT.coeffRef(0, 2); 34 | const T &fy = innerT.coeffRef(1, 1); 35 | const T &cy = innerT.coeffRef(1, 2); 36 | T xo = (uo - cx) / fx; 37 | T yo = (vo - cy) / fy; 38 | T r2 = xo * xo + yo * yo; 39 | T r4 = r2 * r2; 40 | T distortion = 1.0 + distorT[0] * r2 + distorT[1] * r4; 41 | T xd = xo * distortion + (distorT[2] * xo * yo + distorT[2] * xo * yo) + 42 | distorT[3] * (r2 + xo * xo + xo * xo); 43 | T yd = yo * distortion + distorT[3] * xo * yo + distorT[3] * xo * yo + 44 | distorT[2] * (r2 + yo * yo + yo * yo); 45 | T ud = fx * xd + cx; 46 | T vd = fy * yd + cy; 47 | residuals[0] = ud - T(pd.u); 48 | residuals[1] = vd - T(pd.v); 49 | return true; 50 | } 51 | static ceres::CostFunction *Create(PnPData p) { 52 | return ( 53 | new ceres::AutoDiffCostFunction(new pnp_calib(p))); 54 | } 55 | 56 | private: 57 | PnPData pd; 58 | }; 59 | 60 | // pnp calib with direction vector 61 | class vpnp_calib { 62 | public: 63 | vpnp_calib(VPnPData p) { pd = p; } 64 | template 65 | bool operator()(const T *_q, const T *_t, T *residuals) const { 66 | Eigen::Matrix innerT = inner.cast(); 67 | Eigen::Matrix distorT = distor.cast(); 68 | Eigen::Quaternion q_incre{_q[3], _q[0], _q[1], _q[2]}; 69 | Eigen::Matrix t_incre{_t[0], _t[1], _t[2]}; 70 | Eigen::Matrix p_l(T(pd.x), T(pd.y), T(pd.z)); 71 | Eigen::Matrix p_c = q_incre.toRotationMatrix() * p_l + t_incre; 72 | Eigen::Matrix p_2 = innerT * p_c; 73 | T uo = p_2[0] / p_2[2]; 74 | T vo = p_2[1] / p_2[2]; 75 | const T &fx = innerT.coeffRef(0, 0); 76 | const T &cx = innerT.coeffRef(0, 2); 77 | const T &fy = innerT.coeffRef(1, 1); 78 | const T &cy = innerT.coeffRef(1, 2); 79 | T xo = (uo - cx) / fx; 80 | T yo = (vo - cy) / fy; 81 | T r2 = xo * xo + yo * yo; 82 | T r4 = r2 * r2; 83 | T distortion = 1.0 + distorT[0] * r2 + distorT[1] * r4; 84 | T xd = xo * distortion + (distorT[2] * xo * yo + distorT[2] * xo * yo) + 85 | distorT[3] * (r2 + xo * xo + xo * xo); 86 | T yd = yo * distortion + distorT[3] * xo * yo + distorT[3] * xo * yo + 87 | distorT[2] * (r2 + yo * yo + yo * yo); 88 | T ud = fx * xd + cx; 89 | T vd = fy * yd + cy; 90 | if (T(pd.direction(0)) == T(0.0) && T(pd.direction(1)) == T(0.0)) { 91 | residuals[0] = ud - T(pd.u); 92 | residuals[1] = vd - T(pd.v); 93 | } else { 94 | residuals[0] = ud - T(pd.u); 95 | residuals[1] = vd - T(pd.v); 96 | Eigen::Matrix I = 97 | Eigen::Matrix::Identity().cast(); 98 | Eigen::Matrix n = pd.direction.cast(); 99 | Eigen::Matrix nt = pd.direction.transpose().cast(); 100 | Eigen::Matrix V = n * nt; 101 | V = I - V; 102 | Eigen::Matrix R = Eigen::Matrix::Zero().cast(); 103 | R.coeffRef(0, 0) = residuals[0]; 104 | R.coeffRef(1, 1) = residuals[1]; 105 | R = V * R * V.transpose(); 106 | residuals[0] = R.coeffRef(0, 0); 107 | residuals[1] = R.coeffRef(1, 1); 108 | } 109 | return true; 110 | } 111 | static ceres::CostFunction *Create(VPnPData p) { 112 | return (new ceres::AutoDiffCostFunction( 113 | new vpnp_calib(p))); 114 | } 115 | 116 | private: 117 | VPnPData pd; 118 | }; 119 | 120 | void roughCalib(Calibration &calibra, Vector6d &calib_params, 121 | double search_resolution, int max_iter) { 122 | float match_dis = 20; 123 | Eigen::Vector3d fix_adjust_euler(0, 0, 0); 124 | for (int n = 0; n < 2; n++) { 125 | for (int round = 0; round < 3; round++) { 126 | Eigen::Matrix3d rot; 127 | rot = Eigen::AngleAxisd(calib_params[0], Eigen::Vector3d::UnitZ()) * 128 | Eigen::AngleAxisd(calib_params[1], Eigen::Vector3d::UnitY()) * 129 | Eigen::AngleAxisd(calib_params[2], Eigen::Vector3d::UnitX()); 130 | // std::cout << "init rot" << rot << std::endl; 131 | float min_cost = 1000; 132 | for (int iter = 0; iter < max_iter; iter++) { 133 | Eigen::Vector3d adjust_euler = fix_adjust_euler; 134 | adjust_euler[round] = fix_adjust_euler[round] + 135 | pow(-1, iter) * int(iter / 2) * search_resolution; 136 | Eigen::Matrix3d adjust_rotation_matrix; 137 | adjust_rotation_matrix = 138 | Eigen::AngleAxisd(adjust_euler[0], Eigen::Vector3d::UnitZ()) * 139 | Eigen::AngleAxisd(adjust_euler[1], Eigen::Vector3d::UnitY()) * 140 | Eigen::AngleAxisd(adjust_euler[2], Eigen::Vector3d::UnitX()); 141 | Eigen::Matrix3d test_rot = rot * adjust_rotation_matrix; 142 | // std::cout << "adjust_rotation_matrix " << adjust_rotation_matrix 143 | // << std::endl; 144 | Eigen::Vector3d test_euler = test_rot.eulerAngles(2, 1, 0); 145 | // std::cout << "test euler: " << test_euler << std::endl; 146 | Vector6d test_params; 147 | test_params << test_euler[0], test_euler[1], test_euler[2], 148 | calib_params[3], calib_params[4], calib_params[5]; 149 | std::vector pnp_list; 150 | calibra.buildVPnp(test_params, match_dis, false, 151 | calibra.rgb_egde_cloud_, calibra.plane_line_cloud_, 152 | pnp_list); 153 | // std::vector residual_list; 154 | // calibra.calcResidual(test_params, pnp_list, residual_list); 155 | // float mean_residual = 0; 156 | // for (int j = 0; j < residual_list.size(); j++) { 157 | // mean_residual += fabs(residual_list[j]); 158 | // } 159 | // mean_residual = mean_residual / residual_list.size(); 160 | float cost = (calibra.plane_line_cloud_->size() - pnp_list.size()) * 161 | 1.0 / calibra.plane_line_cloud_->size(); 162 | if (cost < min_cost) { 163 | std::cout << "Rough calibration min cost:" << cost << std::endl; 164 | min_cost = cost; 165 | calib_params[0] = test_params[0]; 166 | calib_params[1] = test_params[1]; 167 | calib_params[2] = test_params[2]; 168 | calibra.buildVPnp(calib_params, match_dis, true, 169 | calibra.rgb_egde_cloud_, calibra.plane_line_cloud_, 170 | pnp_list); 171 | cv::Mat projection_img = calibra.getProjectionImg(calib_params); 172 | cv::imshow("Rough Optimization", projection_img); 173 | cv::waitKey(50); 174 | } 175 | } 176 | } 177 | } 178 | } 179 | 180 | int main(int argc, char **argv) { 181 | ros::init(argc, argv, "lidarCamCalib"); 182 | ros::NodeHandle nh; 183 | ros::Rate loop_rate(0.1); 184 | const std::string CameraConfigPath = std::string(argv[1]); 185 | const std::string CalibSettingPath = std::string(argv[2]); 186 | const std::string BagPath = std::string(argv[3]); 187 | const std::string PCDPath = std::string(argv[4]); 188 | const std::string ResultPath = std::string(argv[5]); 189 | std::cout << "---argc num:" << argc << std::endl; 190 | Calibration calibra(CameraConfigPath, CalibSettingPath, BagPath, PCDPath); 191 | Eigen::Vector3d init_euler_angle = 192 | calibra.init_rotation_matrix_.eulerAngles(2, 1, 0); 193 | Eigen::Vector3d init_transation = calibra.init_translation_vector_; 194 | 195 | Vector6d calib_params; 196 | calib_params << init_euler_angle(0), init_euler_angle(1), init_euler_angle(2), 197 | init_transation(0), init_transation(1), init_transation(2); 198 | 199 | std::vector pnp_list; 200 | std::vector vpnp_list; 201 | 202 | ROS_INFO_STREAM("Finish prepare!"); 203 | Eigen::Matrix3d R; 204 | Eigen::Vector3d T; 205 | inner << calibra.fx_, 0.0, calibra.cx_, 0.0, calibra.fy_, calibra.cy_, 0.0, 206 | 0.0, 1.0; 207 | distor << calibra.k1_, calibra.k2_, calibra.p1_, calibra.p2_; 208 | R = calibra.init_rotation_matrix_; 209 | T = calibra.init_translation_vector_; 210 | bool use_vpnp = true; 211 | #ifdef add_error 212 | // add error to the init extrinsic, comment them in real calibration 213 | Eigen::Vector3d error_euler_angle; 214 | error_euler_angle << calibra.adjust_euler_angle_[0], 215 | calibra.adjust_euler_angle_[1], calibra.adjust_euler_angle_[2]; 216 | Eigen::Matrix3d rotation_matrix_error; 217 | rotation_matrix_error = 218 | Eigen::AngleAxisd(error_euler_angle[0], Eigen::Vector3d::UnitZ()) * 219 | Eigen::AngleAxisd(error_euler_angle[1], Eigen::Vector3d::UnitY()) * 220 | Eigen::AngleAxisd(error_euler_angle[2], Eigen::Vector3d::UnitX()); 221 | R = R * rotation_matrix_error; 222 | #endif 223 | Eigen::Vector3d euler = R.eulerAngles(2, 1, 0); 224 | calib_params[0] = euler[0]; 225 | calib_params[1] = euler[1]; 226 | calib_params[2] = euler[2]; 227 | calib_params[3] = T[0]; 228 | calib_params[4] = T[1]; 229 | calib_params[5] = T[2]; 230 | sensor_msgs::PointCloud2 pub_cloud; 231 | pcl::PointCloud::Ptr rgb_cloud( 232 | new pcl::PointCloud); 233 | calibra.colorCloud(calib_params, 1, calibra.rgb_image_, 234 | calibra.raw_lidar_cloud_, rgb_cloud); 235 | pcl::toROSMsg(*rgb_cloud, pub_cloud); 236 | pub_cloud.header.frame_id = "livox"; 237 | calibra.init_rgb_cloud_pub_.publish(pub_cloud); 238 | cv::Mat init_img = calibra.getProjectionImg(calib_params); 239 | cv::imshow("Initial extrinsic", init_img); 240 | cv::waitKey(1000); 241 | 242 | roughCalib(calibra, calib_params, DEG2RAD(0.1), 80); 243 | cv::Mat test_img = calibra.getProjectionImg(calib_params); 244 | cv::imshow("After rough extrinsic", test_img); 245 | cv::waitKey(1000); 246 | int iter = 0; 247 | // Maximum match distance threshold: 15 pixels 248 | // If initial extrinsic lead to error over 15 pixels, the algorithm will not 249 | // work 250 | int dis_threshold = 20; 251 | // Iteratively reducve the matching distance threshold 252 | for (dis_threshold = 20; dis_threshold > 6; dis_threshold -= 1) { 253 | // For each distance, do twice optimization 254 | for (int cnt = 0; cnt < 2; cnt++) { 255 | std::cout << "Iteration:" << iter++ << " Dis:" << dis_threshold 256 | << std::endl; 257 | if (use_vpnp) { 258 | calibra.buildVPnp(calib_params, dis_threshold, true, 259 | calibra.rgb_egde_cloud_, calibra.plane_line_cloud_, 260 | vpnp_list); 261 | } else { 262 | calibra.buildPnp(calib_params, dis_threshold, true, 263 | calibra.rgb_egde_cloud_, calibra.plane_line_cloud_, 264 | pnp_list); 265 | } 266 | cv::Mat projection_img = calibra.getProjectionImg(calib_params); 267 | cv::imshow("Optimization", projection_img); 268 | cv::waitKey(100); 269 | Eigen::Quaterniond q(R); 270 | double ext[7]; 271 | ext[0] = q.x(); 272 | ext[1] = q.y(); 273 | ext[2] = q.z(); 274 | ext[3] = q.w(); 275 | ext[4] = T[0]; 276 | ext[5] = T[1]; 277 | ext[6] = T[2]; 278 | Eigen::Map m_q = Eigen::Map(ext); 279 | Eigen::Map m_t = Eigen::Map(ext + 4); 280 | 281 | ceres::LocalParameterization *q_parameterization = 282 | new ceres::EigenQuaternionParameterization(); 283 | ceres::Problem problem; 284 | 285 | problem.AddParameterBlock(ext, 4, q_parameterization); 286 | problem.AddParameterBlock(ext + 4, 3); 287 | if (use_vpnp) { 288 | for (auto val : vpnp_list) { 289 | ceres::CostFunction *cost_function; 290 | cost_function = vpnp_calib::Create(val); 291 | problem.AddResidualBlock(cost_function, NULL, ext, ext + 4); 292 | } 293 | } else { 294 | for (auto val : pnp_list) { 295 | ceres::CostFunction *cost_function; 296 | cost_function = pnp_calib::Create(val); 297 | problem.AddResidualBlock(cost_function, NULL, ext, ext + 4); 298 | } 299 | } 300 | ceres::Solver::Options options; 301 | options.preconditioner_type = ceres::JACOBI; 302 | options.linear_solver_type = ceres::SPARSE_SCHUR; 303 | options.minimizer_progress_to_stdout = true; 304 | options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT; 305 | 306 | ceres::Solver::Summary summary; 307 | ceres::Solve(options, &problem, &summary); 308 | std::cout << summary.BriefReport() << std::endl; 309 | Eigen::Matrix3d rot = m_q.toRotationMatrix(); 310 | Eigen::Vector3d euler_angle = rot.eulerAngles(2, 1, 0); 311 | // std::cout << rot << std::endl; 312 | // std::cout << m_t << std::endl; 313 | calib_params[0] = euler_angle[0]; 314 | calib_params[1] = euler_angle[1]; 315 | calib_params[2] = euler_angle[2]; 316 | calib_params[3] = m_t(0); 317 | calib_params[4] = m_t(1); 318 | calib_params[5] = m_t(2); 319 | R = rot; 320 | T[0] = m_t(0); 321 | T[1] = m_t(1); 322 | T[2] = m_t(2); 323 | } 324 | } 325 | 326 | ros::Rate loop(0.5); 327 | // roughCalib(calibra, calib_params, DEG2RAD(0.01), 20); 328 | 329 | R = Eigen::AngleAxisd(calib_params[0], Eigen::Vector3d::UnitZ()) * 330 | Eigen::AngleAxisd(calib_params[1], Eigen::Vector3d::UnitY()) * 331 | Eigen::AngleAxisd(calib_params[2], Eigen::Vector3d::UnitX()); 332 | std::string result_file = ResultPath + "/extrinsic.txt"; 333 | std::ofstream outfile(result_file); 334 | for (int i = 0; i < 3; i++) { 335 | outfile << R(i, 0) << "," << R(i, 1) << "," << R(i, 2) << "," << T[i] 336 | << std::endl; 337 | } 338 | outfile << 0 << "," << 0 << "," << 0 << "," << 1 << std::endl; 339 | cv::Mat opt_img = calibra.getProjectionImg(calib_params); 340 | cv::imshow("Optimization result", opt_img); 341 | cv::waitKey(1000); 342 | Eigen::Matrix3d init_rotation; 343 | init_rotation << 0, -1.0, 0, 0, 0, -1.0, 1, 0, 0; 344 | Eigen::Matrix3d adjust_rotation; 345 | adjust_rotation = init_rotation.inverse() * R; 346 | Eigen::Vector3d adjust_euler = adjust_rotation.eulerAngles(2, 1, 0); 347 | outfile << RAD2DEG(adjust_euler[0]) << "," << RAD2DEG(adjust_euler[1]) << "," 348 | << RAD2DEG(adjust_euler[2]) << "," << 0 << "," << 0 << "," << 0 349 | << std::endl; 350 | while (ros::ok()) { 351 | sensor_msgs::PointCloud2 pub_cloud; 352 | pcl::PointCloud::Ptr rgb_cloud( 353 | new pcl::PointCloud); 354 | calibra.colorCloud(calib_params, 1, calibra.rgb_image_, 355 | calibra.raw_lidar_cloud_, rgb_cloud); 356 | pcl::toROSMsg(*rgb_cloud, pub_cloud); 357 | pub_cloud.header.frame_id = "livox"; 358 | calibra.rgb_cloud_pub_.publish(pub_cloud); 359 | sensor_msgs::ImagePtr img_msg = 360 | cv_bridge::CvImage(std_msgs::Header(), "bgr8", calibra.rgb_image_) 361 | .toImageMsg(); 362 | calibra.image_pub_.publish(img_msg); 363 | std::cout << "push enter to publish again" << std::endl; 364 | getchar(); 365 | /* code */ 366 | } 367 | return 0; 368 | } -------------------------------------------------------------------------------- /pics/calib_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AFEICHINA/extended_lidar_camera_calib/dcb4a852be780104f8f52edf6e684fb0d83076dc/pics/calib_result.png -------------------------------------------------------------------------------- /pics/floam_save_pcd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AFEICHINA/extended_lidar_camera_calib/dcb4a852be780104f8f52edf6e684fb0d83076dc/pics/floam_save_pcd.png -------------------------------------------------------------------------------- /pics/map_pcd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AFEICHINA/extended_lidar_camera_calib/dcb4a852be780104f8f52edf6e684fb0d83076dc/pics/map_pcd.png --------------------------------------------------------------------------------