|
| 1 | +#pragma once |
| 2 | +#ifndef _UTILITY_LIDAR_ODOMETRY_H_ |
| 3 | +#define _UTILITY_LIDAR_ODOMETRY_H_ |
| 4 | + |
| 5 | +#include <ros/ros.h> |
| 6 | + |
| 7 | +#include <std_msgs/Header.h> |
| 8 | +#include <std_msgs/Bool.h> |
| 9 | +#include <std_msgs/Float64MultiArray.h> |
| 10 | +#include <std_msgs/Float64.h> |
| 11 | +#include <sensor_msgs/Imu.h> |
| 12 | +#include <sensor_msgs/PointCloud2.h> |
| 13 | +#include <sensor_msgs/NavSatFix.h> |
| 14 | +#include <nav_msgs/Odometry.h> |
| 15 | +#include <nav_msgs/Path.h> |
| 16 | +#include <visualization_msgs/Marker.h> |
| 17 | +#include <visualization_msgs/MarkerArray.h> |
| 18 | + |
| 19 | +#include <opencv/cv.h> |
| 20 | + |
| 21 | +#include <pcl/point_cloud.h> |
| 22 | +#include <pcl/point_types.h> |
| 23 | +#include <pcl/search/impl/search.hpp> |
| 24 | +#include <pcl/range_image/range_image.h> |
| 25 | +#include <pcl/kdtree/kdtree_flann.h> |
| 26 | +#include <pcl/common/common.h> |
| 27 | +#include <pcl/common/transforms.h> |
| 28 | +#include <pcl/registration/icp.h> |
| 29 | +#include <pcl/io/pcd_io.h> |
| 30 | +#include <pcl/filters/filter.h> |
| 31 | +#include <pcl/filters/voxel_grid.h> |
| 32 | +#include <pcl/filters/crop_box.h> |
| 33 | +#include <pcl_conversions/pcl_conversions.h> |
| 34 | + |
| 35 | +#include <tf/LinearMath/Quaternion.h> |
| 36 | +#include <tf/transform_listener.h> |
| 37 | +#include <tf/transform_datatypes.h> |
| 38 | +#include <tf/transform_broadcaster.h> |
| 39 | + |
| 40 | +#include <vector> |
| 41 | +#include <cmath> |
| 42 | +#include <algorithm> |
| 43 | +#include <queue> |
| 44 | +#include <deque> |
| 45 | +#include <iostream> |
| 46 | +#include <fstream> |
| 47 | +#include <ctime> |
| 48 | +#include <cfloat> |
| 49 | +#include <iterator> |
| 50 | +#include <sstream> |
| 51 | +#include <string> |
| 52 | +#include <limits> |
| 53 | +#include <iomanip> |
| 54 | +#include <array> |
| 55 | +#include <thread> |
| 56 | +#include <mutex> |
| 57 | +using namespace std; |
| 58 | + |
| 59 | +typedef pcl::PointXYZI PointType; |
| 60 | + |
| 61 | +enum class SensorType { VELODYNE, OUSTER, Pandar40P }; |
| 62 | + |
| 63 | +class ParamServer |
| 64 | +{ |
| 65 | +public: |
| 66 | + |
| 67 | + ros::NodeHandle nh; |
| 68 | + std::string PROJECT_NAME; |
| 69 | + std::string robot_id; |
| 70 | + |
| 71 | + //Topics |
| 72 | + string pointCloudTopic; |
| 73 | + string imuTopic; |
| 74 | + string odomTopic; |
| 75 | + string gpsTopic; |
| 76 | + string gpsRawTopic; |
| 77 | + string odomResetTopic; |
| 78 | + //Frames |
| 79 | + string lidarFrame; |
| 80 | + string baselinkFrame; |
| 81 | + string odometryFrame; |
| 82 | + string mapFrame; |
| 83 | + |
| 84 | + // GPS Settings |
| 85 | + std::string gpsFixTopic; |
| 86 | + bool useGpsData; |
| 87 | + bool useGpsPoseinterpolation; |
| 88 | + bool useImuHeadingInitialization; |
| 89 | + bool useGpsElevation; |
| 90 | + float gpsCovThreshold; |
| 91 | + float poseCovThreshold; |
| 92 | + bool saveTrajectory; |
| 93 | + // Save pcd |
| 94 | + bool savePCD; |
| 95 | + string savePCDDirectory; |
| 96 | + |
| 97 | + // Lidar Sensor Configuration |
| 98 | + SensorType sensor; |
| 99 | + int N_SCAN; |
| 100 | + int Horizon_SCAN; |
| 101 | + int downsampleRate; |
| 102 | + float lidarMinRange; |
| 103 | + float lidarMaxRange; |
| 104 | + |
| 105 | + // IMU |
| 106 | + bool imuRotEqualBase; |
| 107 | + float imuAccNoise; |
| 108 | + float imuGyrNoise; |
| 109 | + float imuAccBiasN; |
| 110 | + float imuGyrBiasN; |
| 111 | + float imuGravity; |
| 112 | + float imuRPYWeight; |
| 113 | + float imuRate; |
| 114 | + vector<double> extRotV; |
| 115 | + vector<double> extRPYV; |
| 116 | + vector<double> extTransV; |
| 117 | + vector<double> extTransVL; |
| 118 | + vector<double> extROTVI; |
| 119 | + |
| 120 | + Eigen::Matrix3d extRot; |
| 121 | + Eigen::Matrix3d extRotImu2Base; |
| 122 | + Eigen::Matrix3d extRPY; |
| 123 | + Eigen::Matrix3d extRPYInv; |
| 124 | + Eigen::Vector3d extTransLidar2Base; |
| 125 | + Eigen::Vector3d extTrans; |
| 126 | + Eigen::Quaterniond extQRPY; |
| 127 | + Eigen::Quaterniond extQImu2Base; |
| 128 | + |
| 129 | + float errorThreshold; |
| 130 | + |
| 131 | + // LOAM |
| 132 | + float edgeThreshold; |
| 133 | + float surfThreshold; |
| 134 | + int edgeFeatureMinValidNum; |
| 135 | + int surfFeatureMinValidNum; |
| 136 | + |
| 137 | + // voxel filter paprams |
| 138 | + float odometrySurfLeafSize; |
| 139 | + float mappingCornerLeafSize; |
| 140 | + float mappingSurfLeafSize ; |
| 141 | + float mappingLeafSize; |
| 142 | + |
| 143 | + float z_tollerance; |
| 144 | + float rotation_tollerance; |
| 145 | + |
| 146 | + // CPU Params |
| 147 | + int numberOfCores; |
| 148 | + double mappingProcessInterval; |
| 149 | + |
| 150 | + // Surrounding map |
| 151 | + float surroundingkeyframeAddingDistThreshold; |
| 152 | + float surroundingkeyframeAddingAngleThreshold; |
| 153 | + float surroundingKeyframeDensity; |
| 154 | + float surroundingKeyframeSearchRadius; |
| 155 | + |
| 156 | + // Loop closure |
| 157 | + bool loopClosureEnableFlag; |
| 158 | + float loopClosureFrequency; |
| 159 | + int surroundingKeyframeSize; |
| 160 | + float historyKeyframeSearchRadius; |
| 161 | + float historyKeyframeSearchTimeDiff; |
| 162 | + int historyKeyframeSearchNum; |
| 163 | + float historyKeyframeFitnessScore; |
| 164 | + |
| 165 | + // global map visualization radius |
| 166 | + float globalMapVisualizationSearchRadius; |
| 167 | + float globalMapVisualizationPoseDensity; |
| 168 | + float globalMapVisualizationLeafSize; |
| 169 | + |
| 170 | + ParamServer() |
| 171 | + { |
| 172 | + nh.param<std::string>("/PROJECT_NAME", PROJECT_NAME, "sam"); |
| 173 | + nh.param<std::string>("/robot_id", robot_id, "roboat"); |
| 174 | + |
| 175 | + nh.param<std::string>(PROJECT_NAME + "/pointCloudTopic", pointCloudTopic, "points_raw"); |
| 176 | + nh.param<std::string>(PROJECT_NAME + "/imuTopic", imuTopic, "imu_raw"); |
| 177 | + nh.param<std::string>(PROJECT_NAME + "/odomTopic", odomTopic, "odometry/imu"); |
| 178 | + nh.param<std::string>(PROJECT_NAME + "/gpsTopic", gpsTopic, "odometry/gps"); |
| 179 | + nh.param<std::string>(PROJECT_NAME + "/gpsRawTopic", gpsRawTopic, "/localization/odom"); |
| 180 | + nh.param<std::string>(PROJECT_NAME + "/odomResetTopic", odomResetTopic, "/reset_lidar_osometry"); |
| 181 | + nh.param<std::string>(PROJECT_NAME + "/lidarFrame", lidarFrame, "base_link"); |
| 182 | + nh.param<std::string>(PROJECT_NAME + "/baselinkFrame", baselinkFrame, "base_link"); |
| 183 | + nh.param<std::string>(PROJECT_NAME + "/odometryFrame", odometryFrame, "odom"); |
| 184 | + nh.param<std::string>(PROJECT_NAME + "/mapFrame", mapFrame, "map"); |
| 185 | + |
| 186 | + nh.param<std::string>(PROJECT_NAME + "/gpsFixTopic", gpsFixTopic, "/cgi610/nav_fix"); |
| 187 | + |
| 188 | + nh.param<bool>(PROJECT_NAME + "/saveTrajectory", saveTrajectory, false); |
| 189 | + nh.param<bool>(PROJECT_NAME + "/useGpsData", useGpsData, false); |
| 190 | + nh.param<bool>(PROJECT_NAME + "/useGpsPoseinterpolation", useGpsPoseinterpolation, false); |
| 191 | + nh.param<bool>(PROJECT_NAME + "/useImuHeadingInitialization", useImuHeadingInitialization, false); |
| 192 | + nh.param<bool>(PROJECT_NAME + "/useGpsElevation", useGpsElevation, false); |
| 193 | + nh.param<float>(PROJECT_NAME + "/gpsCovThreshold", gpsCovThreshold, 2.0); |
| 194 | + nh.param<float>(PROJECT_NAME + "/poseCovThreshold", poseCovThreshold, 25.0); |
| 195 | + |
| 196 | + nh.param<bool>(PROJECT_NAME + "/savePCD", savePCD, false); |
| 197 | + nh.param<std::string>(PROJECT_NAME + "/savePCDDirectory", savePCDDirectory, "/Downloads/LOAM/"); |
| 198 | + |
| 199 | + std::string sensorStr; |
| 200 | + nh.param<std::string>(PROJECT_NAME + "/sensor", sensorStr, ""); |
| 201 | + if (sensorStr == "velodyne") |
| 202 | + { |
| 203 | + sensor = SensorType::VELODYNE; |
| 204 | + } |
| 205 | + else if (sensorStr == "ouster") |
| 206 | + { |
| 207 | + sensor = SensorType::OUSTER; |
| 208 | + } |
| 209 | + else if(sensorStr == "pandar40p") |
| 210 | + { |
| 211 | + sensor = SensorType::Pandar40P; |
| 212 | + } |
| 213 | + else |
| 214 | + { |
| 215 | + ROS_ERROR_STREAM( |
| 216 | + "Invalid sensor type (must be either 'velodyne' or 'ouster'): " << sensorStr); |
| 217 | + ros::shutdown(); |
| 218 | + } |
| 219 | + |
| 220 | + nh.param<int>(PROJECT_NAME + "/N_SCAN", N_SCAN, 16); |
| 221 | + nh.param<int>(PROJECT_NAME + "/Horizon_SCAN", Horizon_SCAN, 1800); |
| 222 | + nh.param<int>(PROJECT_NAME + "/downsampleRate", downsampleRate, 1); |
| 223 | + nh.param<float>(PROJECT_NAME + "/lidarMinRange", lidarMinRange, 1.0); |
| 224 | + nh.param<float>(PROJECT_NAME + "/lidarMaxRange", lidarMaxRange, 1000.0); |
| 225 | + |
| 226 | + nh.param<bool>(PROJECT_NAME + "/imuRotEqualBase", imuRotEqualBase, false); |
| 227 | + nh.param<float>(PROJECT_NAME + "/imuAccNoise", imuAccNoise, 0.01); |
| 228 | + nh.param<float>(PROJECT_NAME + "/imuGyrNoise", imuGyrNoise, 0.001); |
| 229 | + nh.param<float>(PROJECT_NAME + "/imuAccBiasN", imuAccBiasN, 0.0002); |
| 230 | + nh.param<float>(PROJECT_NAME + "/imuGyrBiasN", imuGyrBiasN, 0.00003); |
| 231 | + nh.param<float>(PROJECT_NAME + "/imuGravity", imuGravity, 9.80511); |
| 232 | + nh.param<float>(PROJECT_NAME + "/imuRPYWeight", imuRPYWeight, 0.01); |
| 233 | + nh.param<float>(PROJECT_NAME + "/imuRate", imuRate, 100); |
| 234 | + |
| 235 | + nh.param<vector<double>>(PROJECT_NAME + "/extrinsicRot", extRotV, vector<double>()); |
| 236 | + nh.param<vector<double>>(PROJECT_NAME + "/extrinsicRPY", extRPYV, vector<double>()); |
| 237 | + nh.param<vector<double>>(PROJECT_NAME + "/extrinsicTrans", extTransV, vector<double>()); |
| 238 | + nh.param<vector<double>>(PROJECT_NAME + "/extrinsicTransLidar2Base", extTransVL, vector<double>()); |
| 239 | + nh.param<vector<double>>(PROJECT_NAME + "/extrinsicRotImu2Base", extROTVI, vector<double>()); |
| 240 | + extTransLidar2Base = Eigen::Map<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>>(extTransVL.data(), 3, 1); |
| 241 | + extRotImu2Base = Eigen::Map<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>>(extROTVI.data(), 3, 3); |
| 242 | + extRot = Eigen::Map<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>>(extRotV.data(), 3, 3); |
| 243 | + extRPY = Eigen::Map<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>>(extRPYV.data(), 3, 3); |
| 244 | + extTrans = Eigen::Map<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>>(extTransV.data(), 3, 1); |
| 245 | + extQRPY = Eigen::Quaterniond(extRPY); |
| 246 | + extRPYInv = extRPY.inverse(); |
| 247 | + extQImu2Base = Eigen::Quaterniond(extRotImu2Base); |
| 248 | + |
| 249 | + nh.param<float>(PROJECT_NAME + "/errorThreshold", errorThreshold, 2.0); |
| 250 | + nh.param<float>(PROJECT_NAME + "/edgeThreshold", edgeThreshold, 0.1); |
| 251 | + nh.param<float>(PROJECT_NAME + "/surfThreshold", surfThreshold, 0.1); |
| 252 | + nh.param<int>(PROJECT_NAME + "/edgeFeatureMinValidNum", edgeFeatureMinValidNum, 10); |
| 253 | + nh.param<int>(PROJECT_NAME + "/surfFeatureMinValidNum", surfFeatureMinValidNum, 100); |
| 254 | + |
| 255 | + nh.param<float>(PROJECT_NAME + "/odometrySurfLeafSize", odometrySurfLeafSize, 0.5); |
| 256 | + nh.param<float>(PROJECT_NAME + "/mappingCornerLeafSize", mappingCornerLeafSize, 0.5); |
| 257 | + nh.param<float>(PROJECT_NAME + "/mappingSurfLeafSize", mappingSurfLeafSize, 0.5); |
| 258 | + nh.param<float>(PROJECT_NAME + "/mappingLeafSize", mappingLeafSize, 0.5); |
| 259 | + |
| 260 | + nh.param<float>(PROJECT_NAME + "/z_tollerance", z_tollerance, FLT_MAX); |
| 261 | + nh.param<float>(PROJECT_NAME + "/rotation_tollerance", rotation_tollerance, FLT_MAX); |
| 262 | + |
| 263 | + nh.param<int>(PROJECT_NAME + "/numberOfCores", numberOfCores, 2); |
| 264 | + nh.param<double>(PROJECT_NAME + "/mappingProcessInterval", mappingProcessInterval, 0.15); |
| 265 | + |
| 266 | + nh.param<float>(PROJECT_NAME + "/surroundingkeyframeAddingDistThreshold", surroundingkeyframeAddingDistThreshold, 1.0); |
| 267 | + nh.param<float>(PROJECT_NAME + "/surroundingkeyframeAddingAngleThreshold", surroundingkeyframeAddingAngleThreshold, 0.2); |
| 268 | + nh.param<float>(PROJECT_NAME + "/surroundingKeyframeDensity", surroundingKeyframeDensity, 1.0); |
| 269 | + nh.param<float>(PROJECT_NAME + "/surroundingKeyframeSearchRadius", surroundingKeyframeSearchRadius, 50.0); |
| 270 | + |
| 271 | + nh.param<bool>(PROJECT_NAME + "/loopClosureEnableFlag", loopClosureEnableFlag, false); |
| 272 | + nh.param<float>(PROJECT_NAME + "/loopClosureFrequency", loopClosureFrequency, 1.0); |
| 273 | + nh.param<int>(PROJECT_NAME + "/surroundingKeyframeSize", surroundingKeyframeSize, 50); |
| 274 | + nh.param<float>(PROJECT_NAME + "/historyKeyframeSearchRadius", historyKeyframeSearchRadius, 10.0); |
| 275 | + nh.param<float>(PROJECT_NAME + "/historyKeyframeSearchTimeDiff", historyKeyframeSearchTimeDiff, 30.0); |
| 276 | + nh.param<int>(PROJECT_NAME + "/historyKeyframeSearchNum", historyKeyframeSearchNum, 25); |
| 277 | + nh.param<float>(PROJECT_NAME + "/historyKeyframeFitnessScore", historyKeyframeFitnessScore, 0.3); |
| 278 | + |
| 279 | + nh.param<float>(PROJECT_NAME + "/globalMapVisualizationSearchRadius", globalMapVisualizationSearchRadius, 1e3); |
| 280 | + nh.param<float>(PROJECT_NAME + "/globalMapVisualizationPoseDensity", globalMapVisualizationPoseDensity, 10.0); |
| 281 | + nh.param<float>(PROJECT_NAME + "/globalMapVisualizationLeafSize", globalMapVisualizationLeafSize, 1.0); |
| 282 | + |
| 283 | + usleep(100); |
| 284 | + } |
| 285 | + |
| 286 | + sensor_msgs::Imu imuConverter(const sensor_msgs::Imu& imu_in) |
| 287 | + { |
| 288 | + sensor_msgs::Imu imu_out = imu_in; |
| 289 | + |
| 290 | + // rotate acceleration |
| 291 | + Eigen::Vector3d acc(imu_in.linear_acceleration.x, imu_in.linear_acceleration.y, imu_in.linear_acceleration.z); |
| 292 | + acc = extRotImu2Base * acc; |
| 293 | + imu_out.linear_acceleration.x = acc.x(); |
| 294 | + imu_out.linear_acceleration.y = acc.y(); |
| 295 | + imu_out.linear_acceleration.z = acc.z(); |
| 296 | + Eigen::Vector3d gyr(imu_in.angular_velocity.x, imu_in.angular_velocity.y, imu_in.angular_velocity.z); |
| 297 | + gyr = extRotImu2Base * gyr; |
| 298 | + imu_out.angular_velocity.x = gyr.x(); |
| 299 | + imu_out.angular_velocity.y = gyr.y(); |
| 300 | + imu_out.angular_velocity.z = gyr.z(); |
| 301 | + if(!imuRotEqualBase){ |
| 302 | + // rotate roll pitch yaw |
| 303 | + Eigen::Quaterniond q_from(imu_in.orientation.w, imu_in.orientation.x, imu_in.orientation.y, imu_in.orientation.z); |
| 304 | + Eigen::Quaterniond q_final = q_from * extQImu2Base; |
| 305 | + imu_out.orientation.x = q_final.x(); |
| 306 | + imu_out.orientation.y = q_final.y(); |
| 307 | + imu_out.orientation.z = q_final.z(); |
| 308 | + imu_out.orientation.w = q_final.w(); |
| 309 | + |
| 310 | + if (sqrt(q_final.x()*q_final.x() + q_final.y()*q_final.y() + q_final.z()*q_final.z() + q_final.w()*q_final.w()) < 0.1) |
| 311 | + { |
| 312 | + ROS_ERROR("Invalid quaternion, please use a 9-axis IMU!"); |
| 313 | + ros::shutdown(); |
| 314 | + } |
| 315 | + } |
| 316 | + return imu_out; |
| 317 | + } |
| 318 | +}; |
| 319 | + |
| 320 | + |
| 321 | +sensor_msgs::PointCloud2 publishCloud(ros::Publisher *thisPub, pcl::PointCloud<PointType>::Ptr thisCloud, ros::Time thisStamp, std::string thisFrame) |
| 322 | +{ |
| 323 | + sensor_msgs::PointCloud2 tempCloud; |
| 324 | + pcl::toROSMsg(*thisCloud, tempCloud); |
| 325 | + tempCloud.header.stamp = thisStamp; |
| 326 | + tempCloud.header.frame_id = thisFrame; |
| 327 | + if (thisPub->getNumSubscribers() != 0) |
| 328 | + thisPub->publish(tempCloud); |
| 329 | + return tempCloud; |
| 330 | +} |
| 331 | + |
| 332 | +template<typename T> |
| 333 | +double ROS_TIME(T msg) |
| 334 | +{ |
| 335 | + return msg->header.stamp.toSec(); |
| 336 | +} |
| 337 | + |
| 338 | + |
| 339 | +template<typename T> |
| 340 | +void imuAngular2rosAngular(sensor_msgs::Imu *thisImuMsg, T *angular_x, T *angular_y, T *angular_z) |
| 341 | +{ |
| 342 | + *angular_x = thisImuMsg->angular_velocity.x; |
| 343 | + *angular_y = thisImuMsg->angular_velocity.y; |
| 344 | + *angular_z = thisImuMsg->angular_velocity.z; |
| 345 | +} |
| 346 | + |
| 347 | + |
| 348 | +template<typename T> |
| 349 | +void imuAccel2rosAccel(sensor_msgs::Imu *thisImuMsg, T *acc_x, T *acc_y, T *acc_z) |
| 350 | +{ |
| 351 | + *acc_x = thisImuMsg->linear_acceleration.x; |
| 352 | + *acc_y = thisImuMsg->linear_acceleration.y; |
| 353 | + *acc_z = thisImuMsg->linear_acceleration.z; |
| 354 | +} |
| 355 | + |
| 356 | + |
| 357 | +template<typename T> |
| 358 | +void imuRPY2rosRPY(sensor_msgs::Imu *thisImuMsg, T *rosRoll, T *rosPitch, T *rosYaw) |
| 359 | +{ |
| 360 | + double imuRoll, imuPitch, imuYaw; |
| 361 | + tf::Quaternion orientation; |
| 362 | + tf::quaternionMsgToTF(thisImuMsg->orientation, orientation); |
| 363 | + tf::Matrix3x3(orientation).getRPY(imuRoll, imuPitch, imuYaw); |
| 364 | + |
| 365 | + *rosRoll = imuRoll; |
| 366 | + *rosPitch = imuPitch; |
| 367 | + *rosYaw = imuYaw; |
| 368 | +} |
| 369 | + |
| 370 | + |
| 371 | +float pointDistance(PointType p) |
| 372 | +{ |
| 373 | + return sqrt(p.x*p.x + p.y*p.y + p.z*p.z); |
| 374 | +} |
| 375 | + |
| 376 | + |
| 377 | +float pointDistance(PointType p1, PointType p2) |
| 378 | +{ |
| 379 | + return sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y) + (p1.z-p2.z)*(p1.z-p2.z)); |
| 380 | +} |
| 381 | + |
| 382 | +#endif |
0 commit comments