laserMapping.cpp 104 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455
  1. #include <omp.h>
  2. #include <mutex>
  3. #include <math.h>
  4. #include <thread>
  5. #include <fstream>
  6. #include <csignal>
  7. #include <unistd.h>
  8. #include <Python.h>
  9. #include <so3_math.h>
  10. #include <ros/ros.h>
  11. #include <Eigen/Core>
  12. #include "IMU_Processing.hpp"
  13. #include <nav_msgs/Odometry.h>
  14. #include <nav_msgs/Path.h>
  15. #include <visualization_msgs/Marker.h>
  16. #include <pcl_conversions/pcl_conversions.h>
  17. #include <pcl/point_cloud.h>
  18. #include <pcl/point_types.h>
  19. #include <pcl/filters/voxel_grid.h>
  20. #include <pcl/io/pcd_io.h>
  21. #include <sensor_msgs/PointCloud2.h>
  22. #include <tf/transform_datatypes.h>
  23. #include <tf/transform_broadcaster.h>
  24. #include <geometry_msgs/Vector3.h>
  25. #include <geometry_msgs/PoseWithCovarianceStamped.h> //PoseWithCovarianceStamped
  26. #include <livox_ros_driver/CustomMsg.h>
  27. #include "preprocess.h"
  28. #include <ikd-Tree/ikd_Tree.h>
  29. #include <std_msgs/Header.h>
  30. #include <std_msgs/Float64MultiArray.h>
  31. #include <sensor_msgs/Imu.h>
  32. #include <sensor_msgs/PointCloud2.h>
  33. #include <sensor_msgs/NavSatFix.h>
  34. #include <nav_msgs/Odometry.h>
  35. #include <nav_msgs/Path.h>
  36. #include <visualization_msgs/Marker.h>
  37. #include <visualization_msgs/MarkerArray.h>
  38. #include <pcl/search/impl/search.hpp>
  39. #include <pcl/range_image/range_image.h>
  40. #include <pcl/kdtree/kdtree_flann.h>
  41. #include <pcl/common/common.h>
  42. #include <pcl/common/transforms.h>
  43. #include <pcl/registration/icp.h>
  44. #include <pcl/registration/ndt.h>
  45. #include <pcl/io/pcd_io.h>
  46. #include <pcl/filters/filter.h>
  47. #include <pcl/filters/crop_box.h>
  48. #include <pcl_conversions/pcl_conversions.h>
  49. #include "scancontext/Scancontext.h"
  50. // gstam
  51. #include <gtsam/geometry/Rot3.h>
  52. #include <gtsam/geometry/Pose3.h>
  53. #include <gtsam/slam/PriorFactor.h>
  54. #include <gtsam/slam/BetweenFactor.h>
  55. #include <gtsam/navigation/GPSFactor.h>
  56. #include <gtsam/navigation/ImuFactor.h>
  57. #include <gtsam/navigation/CombinedImuFactor.h>
  58. #include <gtsam/nonlinear/NonlinearFactorGraph.h>
  59. #include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
  60. #include <gtsam/nonlinear/Marginals.h>
  61. #include <gtsam/nonlinear/Values.h>
  62. #include <gtsam/inference/Symbol.h>
  63. #include <gtsam/nonlinear/ISAM2.h>
  64. #include "keyFramesManager.h"
  65. /*
  66. // gnss
  67. #include "GNSS_Processing.hpp"
  68. #include "sensor_msgs/NavSatFix.h"
  69. */
  70. // save map
  71. #include "fast_lio_sam/save_map.h"
  72. #include "fast_lio_sam/save_pose.h"
  73. // save data in kitti format
  74. #include <sstream>
  75. #include <fstream>
  76. #include <iomanip>
  77. // using namespace gtsam;
  78. #define INIT_TIME (0.1)
  79. #define LASER_POINT_COV (0.001)
  80. #define MAXN (720000)
  81. #define PUBFRAME_PERIOD (20)
  82. /*** Time Log Variables ***/
  83. double kdtree_incremental_time = 0.0, kdtree_search_time = 0.0, kdtree_delete_time = 0.0;
  84. double T1[MAXN], s_plot[MAXN], s_plot2[MAXN], s_plot3[MAXN], s_plot4[MAXN], s_plot5[MAXN], s_plot6[MAXN], s_plot7[MAXN], s_plot8[MAXN], s_plot9[MAXN], s_plot10[MAXN], s_plot11[MAXN];
  85. double match_time = 0, solve_time = 0, solve_const_H_time = 0;
  86. int kdtree_size_st = 0, kdtree_size_end = 0, add_point_size = 0, kdtree_delete_counter = 0;
  87. bool runtime_pos_log = false, pcd_save_en = false, time_sync_en = false, extrinsic_est_en = true, path_en = true;
  88. bool locate_mode=false;
  89. /**************************/
  90. float res_last[100000] = {0.0}; //残差,点到面距离平方和
  91. float DET_RANGE = 300.0f;
  92. const float MOV_THRESHOLD = 1.5f;
  93. mutex mtx_buffer;
  94. condition_variable sig_buffer;
  95. string root_dir = ROOT_DIR;
  96. string map_file_path, lid_topic, imu_topic;
  97. double res_mean_last = 0.05, total_residual = 0.0;
  98. double last_timestamp_lidar = 0, last_timestamp_imu = -1.0;
  99. double gyr_cov = 0.1, acc_cov = 0.1, b_gyr_cov = 0.0001, b_acc_cov = 0.0001;
  100. double filter_size_corner_min = 0, filter_size_surf_min = 0, filter_size_map_min = 0, fov_deg = 0;
  101. double cube_len = 0, HALF_FOV_COS = 0, FOV_DEG = 0, total_distance = 0, lidar_end_time = 0, first_lidar_time = 0.0;
  102. int effct_feat_num = 0, time_log_counter = 0, scan_count = 0, publish_count = 0;
  103. int iterCount = 0, feats_down_size = 0, NUM_MAX_ITERATIONS = 0, laserCloudValidNum = 0, pcd_save_interval = -1, pcd_index = 0;
  104. bool point_selected_surf[100000] = {0}; // 是否为平面特征点
  105. bool lidar_pushed, flg_first_scan = true, flg_exit = false, flg_EKF_inited;
  106. bool scan_pub_en = false, dense_pub_en = false, scan_body_pub_en = false;
  107. vector<vector<int>> pointSearchInd_surf;
  108. vector<BoxPointType> cub_needrm; // ikd-tree中,地图需要移除的包围盒序列
  109. vector<PointVector> Nearest_Points;
  110. vector<double> extrinT(3, 0.0);
  111. vector<double> extrinR(9, 0.0);
  112. deque<double> time_buffer; // 记录lidar时间
  113. deque<PointCloudXYZI::Ptr> lidar_buffer; //记录特征提取或间隔采样后的lidar(特征)数据
  114. deque<sensor_msgs::Imu::ConstPtr> imu_buffer;
  115. PointCloudXYZI::Ptr featsFromMap(new PointCloudXYZI());
  116. PointCloudXYZI::Ptr feats_undistort(new PointCloudXYZI());
  117. PointCloudXYZI::Ptr feats_down_body(new PointCloudXYZI()); //畸变纠正后降采样的单帧点云,lidar系
  118. PointCloudXYZI::Ptr feats_down_world(new PointCloudXYZI()); //畸变纠正后降采样的单帧点云,w系
  119. PointCloudXYZI::Ptr normvec(new PointCloudXYZI(100000, 1)); //特征点在地图中对应点的,局部平面参数,w系
  120. PointCloudXYZI::Ptr laserCloudOri(new PointCloudXYZI(100000, 1));
  121. PointCloudXYZI::Ptr corr_normvect(new PointCloudXYZI(100000, 1)); //对应点法相量?
  122. PointCloudXYZI::Ptr _featsArray; // ikd-tree中,map需要移除的点云
  123. pcl::VoxelGrid<PointType> downSizeFilterSurf; //单帧内降采样使用voxel grid
  124. pcl::VoxelGrid<PointType> downSizeFilterMap; //未使用
  125. KD_TREE ikdtree;
  126. V3F XAxisPoint_body(LIDAR_SP_LEN, 0.0, 0.0);
  127. V3F XAxisPoint_world(LIDAR_SP_LEN, 0.0, 0.0);
  128. V3D euler_cur;
  129. V3D position_last(Zero3d);
  130. V3D Lidar_T_wrt_IMU(Zero3d); // T lidar to imu (imu = r * lidar + t)
  131. M3D Lidar_R_wrt_IMU(Eye3d); // R lidar to imu (imu = r * lidar + t)
  132. /*** EKF inputs and output ***/
  133. std::mutex ikf_lock_;
  134. MeasureGroup Measures;
  135. esekfom::esekf<state_ikfom, 12, input_ikfom> ikfom_; // 状态,噪声维度,输入
  136. state_ikfom state_point;
  137. state_ikfom last_state_point;
  138. vect3 pos_lid; // world系下lidar坐标
  139. nav_msgs::Path path;
  140. nav_msgs::Odometry odomAftMapped;
  141. geometry_msgs::Quaternion geoQuat;
  142. geometry_msgs::PoseStamped msg_body_pose;
  143. shared_ptr<Preprocess> p_pre(new Preprocess());
  144. shared_ptr<ImuProcess> p_imu(new ImuProcess());
  145. /*back end*/
  146. vector<pcl::PointCloud<PointType>::Ptr> cornerCloudKeyFrames; // 历史所有关键帧的角点集合(降采样)
  147. vector<pcl::PointCloud<PointType>::Ptr> surfCloudKeyFrames; // 历史所有关键帧的平面点集合(降采样)
  148. pcl::PointCloud<PointType>::Ptr cloudKeyPoses3D(new pcl::PointCloud<PointType>()); // 历史关键帧位姿(位置)
  149. pcl::PointCloud<PointTypePose>::Ptr cloudKeyPoses6D(new pcl::PointCloud<PointTypePose>()); // 历史关键帧位姿
  150. pcl::PointCloud<PointType>::Ptr copy_cloudKeyPoses3D(new pcl::PointCloud<PointType>());
  151. pcl::PointCloud<PointTypePose>::Ptr copy_cloudKeyPoses6D(new pcl::PointCloud<PointTypePose>());
  152. pcl::PointCloud<PointTypePose>::Ptr fastlio_unoptimized_cloudKeyPoses6D(new pcl::PointCloud<PointTypePose>()); // 存储fastlio 未优化的位姿
  153. // voxel filter paprams
  154. float odometrySurfLeafSize;
  155. float mappingCornerLeafSize;
  156. float mappingSurfLeafSize;
  157. float z_tollerance;
  158. float rotation_tollerance;
  159. // CPU Params
  160. int numberOfCores = 4;
  161. double mappingProcessInterval;
  162. /*loop clousre*/
  163. bool startFlag = true;
  164. bool loopClosureEnableFlag;
  165. float loopClosureFrequency; // 回环检测频率
  166. int surroundingKeyframeSize;
  167. float historyKeyframeSearchRadius; // 回环检测 radius kdtree搜索半径
  168. float historyKeyframeSearchTimeDiff; // 帧间时间阈值
  169. int historyKeyframeSearchNum; // 回环时多少个keyframe拼成submap
  170. float historyKeyframeFitnessScore; // icp 匹配阈值
  171. bool potentialLoopFlag = false;
  172. ros::Publisher pubHistoryKeyFrames; // 发布 loop history keyframe submap
  173. ros::Publisher pubIcpKeyFrames;
  174. ros::Publisher pubRecentKeyFrames;
  175. ros::Publisher pubRecentKeyFrame;
  176. ros::Publisher pubCloudRegisteredRaw;
  177. ros::Publisher pubLoopConstraintEdge;
  178. bool aLoopIsClosed = false;
  179. map<int, int> loopIndexContainer; // from new to old
  180. vector<pair<int, int>> loopIndexQueue;
  181. vector<gtsam::Pose3> loopPoseQueue;
  182. vector<gtsam::noiseModel::Diagonal::shared_ptr> loopNoiseQueue;
  183. deque<std_msgs::Float64MultiArray> loopInfoVec;
  184. nav_msgs::Path globalPath;
  185. // 局部关键帧构建的map点云,对应kdtree,用于scan-to-map找相邻点
  186. pcl::KdTreeFLANN<PointType>::Ptr kdtreeCornerFromMap(new pcl::KdTreeFLANN<PointType>());
  187. pcl::KdTreeFLANN<PointType>::Ptr kdtreeSurfFromMap(new pcl::KdTreeFLANN<PointType>());
  188. pcl::KdTreeFLANN<PointType>::Ptr kdtreeSurroundingKeyPoses(new pcl::KdTreeFLANN<PointType>());
  189. pcl::KdTreeFLANN<PointType>::Ptr kdtreeHistoryKeyPoses(new pcl::KdTreeFLANN<PointType>());
  190. // 降采样
  191. pcl::VoxelGrid<PointType> downSizeFilterCorner;
  192. // pcl::VoxelGrid<PointType> downSizeFilterSurf;
  193. pcl::VoxelGrid<PointType> downSizeFilterICP;
  194. pcl::VoxelGrid<PointType> downSizeFilterSurroundingKeyPoses; // for surrounding key poses of scan-to-map optimization
  195. float transformTobeMapped[6]; // 当前帧的位姿(world系下)
  196. std::mutex mtx;
  197. std::mutex mtxLoopInfo;
  198. // Surrounding map
  199. float surroundingkeyframeAddingDistThreshold; // 判断是否为关键帧的距离阈值
  200. float surroundingkeyframeAddingAngleThreshold; // 判断是否为关键帧的角度阈值
  201. float surroundingKeyframeDensity;
  202. float surroundingKeyframeSearchRadius;
  203. // gtsam
  204. gtsam::NonlinearFactorGraph gtSAMgraph;
  205. gtsam::Values initialEstimate;
  206. gtsam::Values optimizedEstimate;
  207. gtsam::ISAM2 *isam;
  208. gtsam::Values isamCurrentEstimate;
  209. Eigen::MatrixXd poseCovariance;
  210. ros::Publisher pubLaserCloudSurround;
  211. ros::Publisher pubOptimizedGlobalMap ; // 发布最后优化的地图
  212. bool recontructKdTree = false;
  213. int updateKdtreeCount = 0 ; // 每100次更新一次
  214. bool visulize_IkdtreeMap = false; // visual iktree submap
  215. // global map visualization radius
  216. float globalMapVisualizationSearchRadius;
  217. float globalMapVisualizationPoseDensity;
  218. float globalMapVisualizationLeafSize;
  219. bool savePCD; // 是否保存地图
  220. string savePCDDirectory; // 保存路径
  221. bool ndt_loop=false;
  222. /*
  223. * scancontext 回环检测
  224. */
  225. SCManager scManager;
  226. double scDistThres=0.2;
  227. double scMaximumRadius=80;
  228. /**
  229. * 更新里程计轨迹
  230. */
  231. void updatePath(const PointTypePose &pose_in)
  232. {
  233. string odometryFrame = "camera_init";
  234. geometry_msgs::PoseStamped pose_stamped;
  235. pose_stamped.header.stamp = ros::Time().fromSec(pose_in.time);
  236. pose_stamped.header.frame_id = odometryFrame;
  237. pose_stamped.pose.position.x = pose_in.x;
  238. pose_stamped.pose.position.y = pose_in.y;
  239. pose_stamped.pose.position.z = pose_in.z;
  240. tf::Quaternion q = tf::createQuaternionFromRPY(pose_in.roll, pose_in.pitch, pose_in.yaw);
  241. pose_stamped.pose.orientation.x = q.x();
  242. pose_stamped.pose.orientation.y = q.y();
  243. pose_stamped.pose.orientation.z = q.z();
  244. pose_stamped.pose.orientation.w = q.w();
  245. globalPath.poses.push_back(pose_stamped);
  246. }
  247. /**
  248. * 对点云cloudIn进行变换transformIn,返回结果点云, 修改liosam, 考虑到外参的表示
  249. */
  250. pcl::PointCloud<PointType>::Ptr transformPointCloud(pcl::PointCloud<PointType>::Ptr cloudIn, PointTypePose *transformIn)
  251. {
  252. pcl::PointCloud<PointType>::Ptr cloudOut(new pcl::PointCloud<PointType>());
  253. int cloudSize = cloudIn->size();
  254. cloudOut->resize(cloudSize);
  255. // 注意:lio_sam 中的姿态用的euler表示,而fastlio存的姿态角是旋转矢量。而 pcl::getTransformation是将euler_angle 转换到rotation_matrix 不合适,注释
  256. // Eigen::Affine3f transCur = pcl::getTransformation(transformIn->x, transformIn->y, transformIn->z, transformIn->roll, transformIn->pitch, transformIn->yaw);
  257. Eigen::Isometry3d T_b_lidar(state_point.offset_R_L_I ); // 获取 body2lidar 外参
  258. T_b_lidar.pretranslate(state_point.offset_T_L_I);
  259. Eigen::Affine3f T_w_b_ = pcl::getTransformation(transformIn->x, transformIn->y, transformIn->z, transformIn->roll, transformIn->pitch, transformIn->yaw);
  260. Eigen::Isometry3d T_w_b ; // world2body
  261. T_w_b.matrix() = T_w_b_.matrix().cast<double>();
  262. Eigen::Isometry3d T_w_lidar = T_w_b * T_b_lidar ; // T_w_lidar 转换矩阵
  263. Eigen::Isometry3d transCur = T_w_lidar;
  264. #pragma omp parallel for num_threads(numberOfCores)
  265. for (int i = 0; i < cloudSize; ++i)
  266. {
  267. const auto &pointFrom = cloudIn->points[i];
  268. cloudOut->points[i].x = transCur(0, 0) * pointFrom.x + transCur(0, 1) * pointFrom.y + transCur(0, 2) * pointFrom.z + transCur(0, 3);
  269. cloudOut->points[i].y = transCur(1, 0) * pointFrom.x + transCur(1, 1) * pointFrom.y + transCur(1, 2) * pointFrom.z + transCur(1, 3);
  270. cloudOut->points[i].z = transCur(2, 0) * pointFrom.x + transCur(2, 1) * pointFrom.y + transCur(2, 2) * pointFrom.z + transCur(2, 3);
  271. cloudOut->points[i].intensity = pointFrom.intensity;
  272. }
  273. return cloudOut;
  274. }
  275. /**
  276. * 位姿格式变换
  277. */
  278. gtsam::Pose3 pclPointTogtsamPose3(PointTypePose thisPoint)
  279. {
  280. return gtsam::Pose3(gtsam::Rot3::RzRyRx(double(thisPoint.roll), double(thisPoint.pitch), double(thisPoint.yaw)),
  281. gtsam::Point3(double(thisPoint.x), double(thisPoint.y), double(thisPoint.z)));
  282. }
  283. /**
  284. * 位姿格式变换
  285. */
  286. gtsam::Pose3 trans2gtsamPose(float transformIn[])
  287. {
  288. return gtsam::Pose3(gtsam::Rot3::RzRyRx(transformIn[0], transformIn[1], transformIn[2]),
  289. gtsam::Point3(transformIn[3], transformIn[4], transformIn[5]));
  290. }
  291. /**
  292. * Eigen格式的位姿变换
  293. */
  294. Eigen::Affine3f pclPointToAffine3f(PointTypePose thisPoint)
  295. {
  296. return pcl::getTransformation(thisPoint.x, thisPoint.y, thisPoint.z, thisPoint.roll, thisPoint.pitch, thisPoint.yaw);
  297. }
  298. /**
  299. * Eigen格式的位姿变换
  300. */
  301. Eigen::Affine3f trans2Affine3f(float transformIn[])
  302. {
  303. return pcl::getTransformation(transformIn[3], transformIn[4], transformIn[5], transformIn[0], transformIn[1], transformIn[2]);
  304. }
  305. /**
  306. * 位姿格式变换
  307. */
  308. PointTypePose trans2PointTypePose(float transformIn[])
  309. {
  310. PointTypePose thisPose6D;
  311. thisPose6D.x = transformIn[3];
  312. thisPose6D.y = transformIn[4];
  313. thisPose6D.z = transformIn[5];
  314. thisPose6D.roll = transformIn[0];
  315. thisPose6D.pitch = transformIn[1];
  316. thisPose6D.yaw = transformIn[2];
  317. return thisPose6D;
  318. }
  319. /**
  320. * 发布thisCloud,返回thisCloud对应msg格式
  321. */
  322. sensor_msgs::PointCloud2 publishCloud(ros::Publisher *thisPub, pcl::PointCloud<PointType>::Ptr thisCloud, ros::Time thisStamp, std::string thisFrame)
  323. {
  324. sensor_msgs::PointCloud2 tempCloud;
  325. pcl::toROSMsg(*thisCloud, tempCloud);
  326. tempCloud.header.stamp = thisStamp;
  327. tempCloud.header.frame_id = thisFrame;
  328. if (thisPub->getNumSubscribers() != 0)
  329. thisPub->publish(tempCloud);
  330. return tempCloud;
  331. }
  332. /**
  333. * 点到坐标系原点距离
  334. */
  335. float pointDistance(PointType p)
  336. {
  337. return sqrt(p.x * p.x + p.y * p.y + p.z * p.z);
  338. }
  339. /**
  340. * 两点之间距离
  341. */
  342. float pointDistance(PointType p1, PointType p2)
  343. {
  344. 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));
  345. }
  346. // eulerAngle 2 Quaterniond
  347. Eigen::Quaterniond EulerToQuat(float roll_, float pitch_, float yaw_)
  348. {
  349. Eigen::Quaterniond q ; // 四元数 q 和 -q 是相等的
  350. Eigen::AngleAxisd roll(double(roll_), Eigen::Vector3d::UnitX());
  351. Eigen::AngleAxisd pitch(double(pitch_), Eigen::Vector3d::UnitY());
  352. Eigen::AngleAxisd yaw(double(yaw_), Eigen::Vector3d::UnitZ());
  353. q = yaw * pitch * roll ;
  354. q.normalize();
  355. return q ;
  356. }
  357. // 将更新的pose赋值到 transformTobeMapped
  358. void getCurPose(state_ikfom cur_state)
  359. {
  360. // 欧拉角是没有群的性质,所以从SO3还是一般的rotation matrix 转换过来的结果一样
  361. Eigen::Vector3d eulerAngle = cur_state.rot.matrix().eulerAngles(2,1,0); // yaw pitch roll 单位:弧度
  362. // V3D eulerAngle = SO3ToEuler(cur_state.rot)/57.3 ; // fastlio 自带 roll pitch yaw 单位: 度,旋转顺序 zyx
  363. // transformTobeMapped[0] = eulerAngle(0); // roll 使用 SO3ToEuler 方法时,顺序是 rpy
  364. // transformTobeMapped[1] = eulerAngle(1); // pitch
  365. // transformTobeMapped[2] = eulerAngle(2); // yaw
  366. transformTobeMapped[0] = eulerAngle(2); // roll 使用 eulerAngles(2,1,0) 方法时,顺序是 ypr
  367. transformTobeMapped[1] = eulerAngle(1); // pitch
  368. transformTobeMapped[2] = eulerAngle(0); // yaw
  369. transformTobeMapped[3] = cur_state.pos(0); // x
  370. transformTobeMapped[4] = cur_state.pos(1); // y
  371. transformTobeMapped[5] = cur_state.pos(2); // z
  372. }
  373. /**
  374. * rviz展示闭环边
  375. */
  376. void visualizeLoopClosure()
  377. {
  378. ros::Time timeLaserInfoStamp = ros::Time().fromSec(lidar_end_time); // 时间戳
  379. string odometryFrame = "camera_init";
  380. if (loopIndexContainer.empty())
  381. return;
  382. visualization_msgs::MarkerArray markerArray;
  383. // 闭环顶点
  384. visualization_msgs::Marker markerNode;
  385. markerNode.header.frame_id = odometryFrame;
  386. markerNode.header.stamp = timeLaserInfoStamp;
  387. markerNode.action = visualization_msgs::Marker::ADD;
  388. markerNode.type = visualization_msgs::Marker::SPHERE_LIST;
  389. markerNode.ns = "loop_nodes";
  390. markerNode.id = 0;
  391. markerNode.pose.orientation.w = 1;
  392. markerNode.scale.x = 0.3;
  393. markerNode.scale.y = 0.3;
  394. markerNode.scale.z = 0.3;
  395. markerNode.color.r = 0;
  396. markerNode.color.g = 0.8;
  397. markerNode.color.b = 1;
  398. markerNode.color.a = 1;
  399. // 闭环边
  400. visualization_msgs::Marker markerEdge;
  401. markerEdge.header.frame_id = odometryFrame;
  402. markerEdge.header.stamp = timeLaserInfoStamp;
  403. markerEdge.action = visualization_msgs::Marker::ADD;
  404. markerEdge.type = visualization_msgs::Marker::LINE_LIST;
  405. markerEdge.ns = "loop_edges";
  406. markerEdge.id = 1;
  407. markerEdge.pose.orientation.w = 1;
  408. markerEdge.scale.x = 0.1;
  409. markerEdge.color.r = 0.9;
  410. markerEdge.color.g = 0.9;
  411. markerEdge.color.b = 0;
  412. markerEdge.color.a = 1;
  413. // 遍历闭环
  414. for (auto it = loopIndexContainer.begin(); it != loopIndexContainer.end(); ++it)
  415. {
  416. int key_cur = it->first;
  417. int key_pre = it->second;
  418. geometry_msgs::Point p;
  419. p.x = copy_cloudKeyPoses6D->points[key_cur].x;
  420. p.y = copy_cloudKeyPoses6D->points[key_cur].y;
  421. p.z = copy_cloudKeyPoses6D->points[key_cur].z;
  422. markerNode.points.push_back(p);
  423. markerEdge.points.push_back(p);
  424. p.x = copy_cloudKeyPoses6D->points[key_pre].x;
  425. p.y = copy_cloudKeyPoses6D->points[key_pre].y;
  426. p.z = copy_cloudKeyPoses6D->points[key_pre].z;
  427. markerNode.points.push_back(p);
  428. markerEdge.points.push_back(p);
  429. }
  430. markerArray.markers.push_back(markerNode);
  431. markerArray.markers.push_back(markerEdge);
  432. pubLoopConstraintEdge.publish(markerArray);
  433. }
  434. /**
  435. * 计算当前帧与前一帧位姿变换,如果变化太小,不设为关键帧,反之设为关键帧
  436. */
  437. bool saveFrame()
  438. {
  439. if (cloudKeyPoses3D->points.empty())
  440. return true;
  441. // 前一帧位姿
  442. Eigen::Affine3f transStart = pclPointToAffine3f(cloudKeyPoses6D->back());
  443. // 当前帧位姿
  444. Eigen::Affine3f transFinal = trans2Affine3f(transformTobeMapped);
  445. // Eigen::Affine3f transFinal = pcl::getTransformation(transformTobeMapped[3], transformTobeMapped[4], transformTobeMapped[5],
  446. // transformTobeMapped[0], transformTobeMapped[1], transformTobeMapped[2]);
  447. // 位姿变换增量
  448. Eigen::Affine3f transBetween = transStart.inverse() * transFinal;
  449. float x, y, z, roll, pitch, yaw;
  450. pcl::getTranslationAndEulerAngles(transBetween, x, y, z, roll, pitch, yaw); // 获取上一帧 相对 当前帧的 位姿
  451. // 旋转和平移量都较小,当前帧不设为关键帧
  452. if (abs(roll) < surroundingkeyframeAddingAngleThreshold &&
  453. abs(pitch) < surroundingkeyframeAddingAngleThreshold &&
  454. abs(yaw) < surroundingkeyframeAddingAngleThreshold &&
  455. sqrt(x * x + y * y + z * z) < surroundingkeyframeAddingDistThreshold)
  456. return false;
  457. return true;
  458. }
  459. /**
  460. * 添加激光里程计因子
  461. */
  462. void addOdomFactor()
  463. {
  464. if (cloudKeyPoses3D->points.empty())
  465. {
  466. // 第一帧初始化先验因子
  467. gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances((gtsam::Vector(6) <<1e-12, 1e-12, 1e-12, 1e-12, 1e-12, 1e-12).finished()); // rad*rad, meter*meter // indoor 1e-12, 1e-12, 1e-12, 1e-12, 1e-12, 1e-12 // 1e-2, 1e-2, M_PI*M_PI, 1e8, 1e8, 1e8
  468. gtSAMgraph.add(gtsam::PriorFactor<gtsam::Pose3>(0, trans2gtsamPose(transformTobeMapped), priorNoise));
  469. // 变量节点设置初始值
  470. initialEstimate.insert(0, trans2gtsamPose(transformTobeMapped));
  471. }
  472. else
  473. {
  474. // 添加激光里程计因子
  475. gtsam::noiseModel::Diagonal::shared_ptr odometryNoise = gtsam::noiseModel::Diagonal::Variances((gtsam::Vector(6) << 1e-6, 1e-6, 1e-6, 1e-4, 1e-4, 1e-4).finished());
  476. gtsam::Pose3 poseFrom = pclPointTogtsamPose3(cloudKeyPoses6D->points.back()); /// pre
  477. gtsam::Pose3 poseTo = trans2gtsamPose(transformTobeMapped); // cur
  478. // 参数:前一帧id,当前帧id,前一帧与当前帧的位姿变换(作为观测值),噪声协方差
  479. gtSAMgraph.add(gtsam::BetweenFactor<gtsam::Pose3>(cloudKeyPoses3D->size() - 1, cloudKeyPoses3D->size(), poseFrom.between(poseTo), odometryNoise));
  480. // 变量节点设置初始值
  481. initialEstimate.insert(cloudKeyPoses3D->size(), poseTo);
  482. }
  483. }
  484. /**
  485. * 添加闭环因子
  486. */
  487. void addLoopFactor()
  488. {
  489. if (loopIndexQueue.empty())
  490. return;
  491. // 闭环队列
  492. for (int i = 0; i < (int)loopIndexQueue.size(); ++i)
  493. {
  494. // 闭环边对应两帧的索引
  495. int indexFrom = loopIndexQueue[i].first; // cur
  496. int indexTo = loopIndexQueue[i].second; // pre
  497. // 闭环边的位姿变换
  498. gtsam::Pose3 poseBetween = loopPoseQueue[i];
  499. gtsam::noiseModel::Diagonal::shared_ptr noiseBetween = loopNoiseQueue[i];
  500. gtSAMgraph.add(gtsam::BetweenFactor<gtsam::Pose3>(indexFrom, indexTo, poseBetween, noiseBetween));
  501. printf(" Add loop factor between %d -- %d\n",indexFrom,indexTo);
  502. }
  503. loopIndexQueue.clear();
  504. loopPoseQueue.clear();
  505. loopNoiseQueue.clear();
  506. aLoopIsClosed = true;
  507. }
  508. /**
  509. * 提取key索引的关键帧前后相邻若干帧的关键帧特征点集合,降采样
  510. */
  511. void loopFindNearKeyframes(pcl::PointCloud<PointType>::Ptr &nearKeyframes, const int &key, const int &searchNum)
  512. {
  513. // 提取key索引的关键帧前后相邻若干帧的关键帧特征点集合
  514. nearKeyframes->clear();
  515. int cloudSize = copy_cloudKeyPoses6D->size();
  516. auto surfcloud_keyframes_size = surfCloudKeyFrames.size() ;
  517. for (int i = -searchNum; i <= searchNum; ++i)
  518. {
  519. int keyNear = key + i;
  520. if (keyNear < 0 || keyNear >= cloudSize)
  521. continue;
  522. if (keyNear < 0 || keyNear >= surfcloud_keyframes_size)
  523. continue;
  524. // *nearKeyframes += *transformPointCloud(cornerCloudKeyFrames[keyNear], &copy_cloudKeyPoses6D->points[keyNear]);
  525. // 注意:cloudKeyPoses6D 存储的是 T_w_b , 而点云是lidar系下的,构建icp的submap时,需要通过外参数T_b_lidar 转换 , 参考pointBodyToWorld 的转换
  526. *nearKeyframes += *transformPointCloud(surfCloudKeyFrames[keyNear], &copy_cloudKeyPoses6D->points[keyNear]); // fast-lio 没有进行特征提取,默认点云就是surf
  527. }
  528. if (nearKeyframes->empty())
  529. return;
  530. // 降采样
  531. pcl::PointCloud<PointType>::Ptr cloud_temp(new pcl::PointCloud<PointType>());
  532. downSizeFilterICP.setInputCloud(nearKeyframes);
  533. downSizeFilterICP.filter(*cloud_temp);
  534. *nearKeyframes = *cloud_temp;
  535. }
  536. void performLoopClosure() {
  537. ros::Time timeLaserInfoStamp = ros::Time().fromSec(lidar_end_time); // 时间戳
  538. string odometryFrame = "camera_init";
  539. if (cloudKeyPoses3D->points.empty() == true) {
  540. return;
  541. }
  542. mtx.lock();
  543. *copy_cloudKeyPoses3D = *cloudKeyPoses3D;
  544. *copy_cloudKeyPoses6D = *cloudKeyPoses6D;
  545. mtx.unlock();
  546. // 当前关键帧索引,候选闭环匹配帧索引
  547. static int LastKeyFrameSize=0;//;
  548. if(LastKeyFrameSize>=surfCloudKeyFrames.size()) //没有更新的keyframe 无需检测
  549. return ;
  550. int loopKeyCur;
  551. int loopKeyPre;
  552. // 在历史关键帧中查找与当前关键帧距离最近的关键帧集合,选择时间相隔较远的一帧作为候选闭环帧
  553. /*if (detectLoopClosureDistance(&loopKeyCur, &loopKeyPre) == false)
  554. {
  555. return;
  556. }*/
  557. if( int(cloudKeyPoses3D->size()) < scManager.NUM_EXCLUDE_RECENT) // do not try too early
  558. return;
  559. auto detectResult = scManager.detectLoopClosureID(); // first: nn index, second: yaw diff
  560. LastKeyFrameSize=surfCloudKeyFrames.size();
  561. int SCclosestHistoryFrameID = detectResult.first;
  562. if( SCclosestHistoryFrameID != -1 ) {
  563. loopKeyPre = SCclosestHistoryFrameID;
  564. loopKeyCur = cloudKeyPoses3D->size() - 1; // because cpp starts 0 and ends n-1
  565. //cout << "Loop detected! - between " << loopKeyPre << " and " << loopKeyCur << "" << endl;
  566. }else{
  567. return;
  568. }
  569. // 提取
  570. pcl::PointCloud<PointType>::Ptr cureKeyframeCloud(new pcl::PointCloud<PointType>()); // cue keyframe
  571. pcl::PointCloud<PointType>::Ptr prevKeyframeCloud(new pcl::PointCloud<PointType>()); // history keyframe submap
  572. {
  573. // 提取当前关键帧特征点集合,降采样
  574. loopFindNearKeyframes(cureKeyframeCloud, loopKeyCur, 0); // 将cur keyframe 转换到world系下
  575. // 提取闭环匹配关键帧前后相邻若干帧的关键帧特征点集合,降采样
  576. loopFindNearKeyframes(prevKeyframeCloud, loopKeyPre,
  577. historyKeyframeSearchNum); // 选取historyKeyframeSearchNum个keyframe拼成submap
  578. // 如果特征点较少,返回
  579. // if (cureKeyframeCloud->size() < 300 || prevKeyframeCloud->size() < 1000)
  580. // return;
  581. // 发布闭环匹配关键帧局部map
  582. if (pubHistoryKeyFrames.getNumSubscribers() != 0)
  583. publishCloud(&pubHistoryKeyFrames, prevKeyframeCloud, timeLaserInfoStamp, odometryFrame);
  584. }
  585. double icp_t1 = omp_get_wtime();
  586. float noiseScore;
  587. Eigen::Affine3f correctionLidarFrame;
  588. float x, y, z, roll, pitch, yaw;
  589. /*PointTypePose current = copy_cloudKeyPoses6D->points[loopKeyCur];
  590. PointTypePose target = copy_cloudKeyPoses6D->points[loopKeyPre];
  591. gtsam::Pose3 poseSrc(gtsam::Rot3::RzRyRx(double(current.roll), double(current.pitch), double(current.yaw)),
  592. gtsam::Point3(double(current.x), double(current.y), double(current.z)));
  593. gtsam::Pose3 poseTar(gtsam::Rot3::RzRyRx(double(target.roll), double(target.pitch), double(target.yaw)),
  594. gtsam::Point3(double(target.x), double(target.y), double(target.z)));*/
  595. // ICP Settings
  596. if(ndt_loop==false) {
  597. pcl::IterativeClosestPoint<PointType, PointType> icp;
  598. icp.setMaxCorrespondenceDistance(150); // giseop , use a value can cover 2*historyKeyframeSearchNum range in meter
  599. icp.setMaximumIterations(100);
  600. icp.setTransformationEpsilon(1e-6);
  601. icp.setEuclideanFitnessEpsilon(1e-6);
  602. icp.setRANSACIterations(0);
  603. // scan-to-map,调用icp匹配
  604. icp.setInputSource(cureKeyframeCloud);
  605. icp.setInputTarget(prevKeyframeCloud);
  606. pcl::PointCloud<PointType>::Ptr unused_result(new pcl::PointCloud<PointType>());
  607. icp.align(*unused_result);
  608. noiseScore =icp.getFitnessScore(); // loop_clousre noise from ndt
  609. correctionLidarFrame = icp.getFinalTransformation();
  610. double icp_t2 = omp_get_wtime();
  611. pcl::getTranslationAndEulerAngles(correctionLidarFrame, x, y, z, roll, pitch, yaw);
  612. if (icp.hasConverged() == false || icp.getFitnessScore() > historyKeyframeFitnessScore) {
  613. printf("icp failed,score:%f from %d to %d ,(%f %f %f) (%f %f %f) time:%f\n",
  614. icp.getFitnessScore(),loopKeyCur, loopKeyPre,x, y, z, roll, pitch, yaw,icp_t2 - icp_t1);
  615. return;
  616. }
  617. printf("icp success,score:%f from %d to %d ,(%f %f %f) (%f %f %f) time:%f\n",
  618. icp.getFitnessScore(),loopKeyCur, loopKeyPre,x, y, z, roll, pitch, yaw,icp_t2 - icp_t1);
  619. }else {
  620. pcl::NormalDistributionsTransform<PointType, PointType> ndt;
  621. ndt.setTransformationEpsilon(0.001);
  622. // Setting maximum step size for More-Thuente line search.
  623. ndt.setStepSize(0.1);
  624. //Setting Resolution of NDT grid structure (VoxelGridCovariance).
  625. ndt.setResolution(0.5);
  626. // Setting max number of registration iterations.
  627. ndt.setMaximumIterations(50);
  628. // Setting point cloud to be aligned.
  629. ndt.setInputSource(cureKeyframeCloud);
  630. // Setting point cloud to be aligned to.
  631. ndt.setInputTarget(prevKeyframeCloud);
  632. pcl::PointCloud<PointType>::Ptr unused_result(new pcl::PointCloud<PointType>());
  633. ndt.align(*unused_result, Eigen::Matrix4f::Identity());
  634. double icp_t2 = omp_get_wtime();
  635. noiseScore =ndt.getFitnessScore(); // loop_clousre noise from ndt
  636. correctionLidarFrame = ndt.getFinalTransformation();
  637. pcl::getTranslationAndEulerAngles(correctionLidarFrame, x, y, z, roll, pitch, yaw);
  638. if (ndt.hasConverged() == false || ndt.getFitnessScore() > historyKeyframeFitnessScore) {
  639. printf("ndt failed,score:%f from %d to %d ,(%f %f %f) (%f %f %f) time:%f\n",
  640. ndt.getFitnessScore(),loopKeyCur, loopKeyPre,x, y, z, roll, pitch, yaw,icp_t2 - icp_t1);
  641. return;
  642. }
  643. printf("ndt success,score:%f from %d to %d ,(%f %f %f) (%f %f %f) time:%f\n",
  644. ndt.getFitnessScore(),loopKeyCur, loopKeyPre,x, y, z, roll, pitch, yaw,icp_t2 - icp_t1);
  645. }
  646. // 发布当前关键帧经过闭环优化后的位姿变换之后的特征点云
  647. if (pubIcpKeyFrames.getNumSubscribers() != 0) {
  648. pcl::PointCloud<PointType>::Ptr closed_cloud(new pcl::PointCloud<PointType>());
  649. pcl::transformPointCloud(*cureKeyframeCloud, *closed_cloud, correctionLidarFrame);
  650. publishCloud(&pubIcpKeyFrames, closed_cloud, timeLaserInfoStamp, odometryFrame);
  651. }
  652. // 闭环优化得到的当前关键帧与闭环关键帧之间的位姿变换
  653. // 闭环优化前当前帧位姿
  654. Eigen::Affine3f tWrong = pclPointToAffine3f(copy_cloudKeyPoses6D->points[loopKeyCur]);
  655. // 闭环优化后当前帧位姿
  656. Eigen::Affine3f tCorrect = correctionLidarFrame * tWrong;
  657. pcl::getTranslationAndEulerAngles(tCorrect, x, y, z, roll, pitch, yaw); // 获取上一帧匹配后的位姿
  658. gtsam::Pose3 poseFrom = gtsam::Pose3(gtsam::Rot3::RzRyRx(roll, pitch, yaw), gtsam::Point3(x, y, z));
  659. // 闭环匹配帧的位姿
  660. gtsam::Pose3 poseTo = pclPointTogtsamPose3(copy_cloudKeyPoses6D->points[loopKeyPre]);
  661. gtsam::Vector Vector6(6);
  662. Vector6 << noiseScore, noiseScore, noiseScore, noiseScore, noiseScore, noiseScore;
  663. gtsam::noiseModel::Diagonal::shared_ptr constraintNoise = gtsam::noiseModel::Diagonal::Variances(Vector6);
  664. std::cout << "loopNoiseQueue = " << noiseScore << std::endl;
  665. // 添加闭环因子需要的数据
  666. mtx.lock();
  667. loopIndexQueue.push_back(make_pair(loopKeyCur, loopKeyPre));
  668. loopPoseQueue.push_back(poseFrom.between(poseTo));
  669. loopNoiseQueue.push_back(constraintNoise);
  670. mtx.unlock();
  671. loopIndexContainer[loopKeyCur] = loopKeyPre; // 使用hash map 存储回环对
  672. }
  673. void saveKeyFramesAndFactor()
  674. {
  675. // 计算当前帧与前一帧位姿变换,如果变化太小,不设为关键帧,反之设为关键帧
  676. if (saveFrame() == false)
  677. return;
  678. // 激光里程计因子(from fast-lio), 输入的是frame_relative pose 帧间位姿(body 系下)
  679. addOdomFactor();
  680. scManager.makeAndSaveScancontextAndKeys(*feats_undistort);
  681. performLoopClosure(); //闭环检测
  682. visualizeLoopClosure();//展示闭环边
  683. // 闭环因子 (rs-loop-detect) 基于欧氏距离的检测
  684. addLoopFactor();
  685. // 执行优化
  686. isam->update(gtSAMgraph, initialEstimate);
  687. isam->update();
  688. if (aLoopIsClosed == true) // 有回环因子,多update几次
  689. {
  690. isam->update();
  691. isam->update();
  692. isam->update();
  693. isam->update();
  694. isam->update();
  695. printf(" isam update \n");
  696. }
  697. // update之后要清空一下保存的因子图,注:历史数据不会清掉,ISAM保存起来了
  698. gtSAMgraph.resize(0);
  699. initialEstimate.clear();
  700. PointType thisPose3D;
  701. PointTypePose thisPose6D;
  702. gtsam::Pose3 latestEstimate;
  703. // 优化结果
  704. isamCurrentEstimate = isam->calculateBestEstimate();
  705. // 当前帧位姿结果
  706. latestEstimate = isamCurrentEstimate.at<gtsam::Pose3>(isamCurrentEstimate.size() - 1);
  707. // cloudKeyPoses3D加入当前帧位置
  708. thisPose3D.x = latestEstimate.translation().x();
  709. thisPose3D.y = latestEstimate.translation().y();
  710. thisPose3D.z = latestEstimate.translation().z();
  711. // 索引
  712. thisPose3D.intensity = cloudKeyPoses3D->size(); // 使用intensity作为该帧点云的index
  713. cloudKeyPoses3D->push_back(thisPose3D); // 新关键帧帧放入队列中
  714. // cloudKeyPoses6D加入当前帧位姿
  715. thisPose6D.x = thisPose3D.x;
  716. thisPose6D.y = thisPose3D.y;
  717. thisPose6D.z = thisPose3D.z;
  718. thisPose6D.intensity = thisPose3D.intensity;
  719. thisPose6D.roll = latestEstimate.rotation().roll();
  720. thisPose6D.pitch = latestEstimate.rotation().pitch();
  721. thisPose6D.yaw = latestEstimate.rotation().yaw();
  722. thisPose6D.time = lidar_end_time;
  723. cloudKeyPoses6D->push_back(thisPose6D);
  724. // 位姿协方差
  725. poseCovariance = isam->marginalCovariance(isamCurrentEstimate.size() - 1);
  726. // ESKF状态和方差 更新
  727. state_ikfom state_updated = ikfom_.get_x(); // 获取cur_pose (还没修正)
  728. Eigen::Vector3d pos(latestEstimate.translation().x(), latestEstimate.translation().y(), latestEstimate.translation().z());
  729. Eigen::Quaterniond q = EulerToQuat(latestEstimate.rotation().roll(), latestEstimate.rotation().pitch(), latestEstimate.rotation().yaw());
  730. // 更新状态量
  731. state_updated.pos = pos;
  732. state_updated.rot = q;
  733. state_point = state_updated; // 对state_point进行更新,state_point可视化用到
  734. // if(aLoopIsClosed == true )
  735. ikfom_.change_x(state_updated); // 对cur_pose 进行isam2优化后的修正
  736. // TODO: P的修正有待考察,按照yanliangwang的做法,修改了p,会跑飞
  737. // esekfom::esekf<state_ikfom, 12, input_ikfom>::cov P_updated = kf.get_P(); // 获取当前的状态估计的协方差矩阵
  738. // P_updated.setIdentity();
  739. // P_updated(6, 6) = P_updated(7, 7) = P_updated(8, 8) = 0.00001;
  740. // P_updated(9, 9) = P_updated(10, 10) = P_updated(11, 11) = 0.00001;
  741. // P_updated(15, 15) = P_updated(16, 16) = P_updated(17, 17) = 0.0001;
  742. // P_updated(18, 18) = P_updated(19, 19) = P_updated(20, 20) = 0.001;
  743. // P_updated(21, 21) = P_updated(22, 22) = 0.00001;
  744. // kf.change_P(P_updated);
  745. // 当前帧激光角点、平面点,降采样集合
  746. // pcl::PointCloud<PointType>::Ptr thisCornerKeyFrame(new pcl::PointCloud<PointType>());
  747. pcl::PointCloud<PointType>::Ptr thisSurfKeyFrame(new pcl::PointCloud<PointType>());
  748. // pcl::copyPointCloud(*feats_undistort, *thisCornerKeyFrame);
  749. pcl::copyPointCloud(*feats_undistort, *thisSurfKeyFrame); // 存储关键帧,没有降采样的点云
  750. // 保存特征点降采样集合
  751. // cornerCloudKeyFrames.push_back(thisCornerKeyFrame);
  752. surfCloudKeyFrames.push_back(thisSurfKeyFrame);
  753. //sc关键帧保存降采样的点云
  754. /*pcl::PointCloud<PointType>::Ptr cloud_temp(new pcl::PointCloud<PointType>());
  755. downSizeFilterICP.setInputCloud(thisSurfKeyFrame);
  756. downSizeFilterICP.filter(*cloud_temp);*/
  757. updatePath(thisPose6D); // 可视化update后的path
  758. }
  759. void recontructIKdTree(){
  760. if(recontructKdTree && updateKdtreeCount > 0){
  761. /*** if path is too large, the rvis will crash ***/
  762. pcl::KdTreeFLANN<PointType>::Ptr kdtreeGlobalMapPoses(new pcl::KdTreeFLANN<PointType>());
  763. pcl::PointCloud<PointType>::Ptr subMapKeyPoses(new pcl::PointCloud<PointType>());
  764. pcl::PointCloud<PointType>::Ptr subMapKeyPosesDS(new pcl::PointCloud<PointType>());
  765. pcl::PointCloud<PointType>::Ptr subMapKeyFrames(new pcl::PointCloud<PointType>());
  766. pcl::PointCloud<PointType>::Ptr subMapKeyFramesDS(new pcl::PointCloud<PointType>());
  767. // kdtree查找最近一帧关键帧相邻的关键帧集合
  768. std::vector<int> pointSearchIndGlobalMap;
  769. std::vector<float> pointSearchSqDisGlobalMap;
  770. mtx.lock();
  771. kdtreeGlobalMapPoses->setInputCloud(cloudKeyPoses3D);
  772. kdtreeGlobalMapPoses->radiusSearch(cloudKeyPoses3D->back(), globalMapVisualizationSearchRadius, pointSearchIndGlobalMap, pointSearchSqDisGlobalMap, 0);
  773. mtx.unlock();
  774. for (int i = 0; i < (int)pointSearchIndGlobalMap.size(); ++i)
  775. subMapKeyPoses->push_back(cloudKeyPoses3D->points[pointSearchIndGlobalMap[i]]); // subMap的pose集合
  776. // 降采样
  777. pcl::VoxelGrid<PointType> downSizeFilterSubMapKeyPoses;
  778. downSizeFilterSubMapKeyPoses.setLeafSize(globalMapVisualizationPoseDensity, globalMapVisualizationPoseDensity, globalMapVisualizationPoseDensity); // for global map visualization
  779. downSizeFilterSubMapKeyPoses.setInputCloud(subMapKeyPoses);
  780. downSizeFilterSubMapKeyPoses.filter(*subMapKeyPosesDS); // subMap poses downsample
  781. // 提取局部相邻关键帧对应的特征点云
  782. for (int i = 0; i < (int)subMapKeyPosesDS->size(); ++i)
  783. {
  784. // 距离过大
  785. if (pointDistance(subMapKeyPosesDS->points[i], cloudKeyPoses3D->back()) > globalMapVisualizationSearchRadius)
  786. continue;
  787. int thisKeyInd = (int)subMapKeyPosesDS->points[i].intensity;
  788. // *globalMapKeyFrames += *transformPointCloud(cornerCloudKeyFrames[thisKeyInd], &cloudKeyPoses6D->points[thisKeyInd]);
  789. *subMapKeyFrames += *transformPointCloud(surfCloudKeyFrames[thisKeyInd], &cloudKeyPoses6D->points[thisKeyInd]); // fast_lio only use surfCloud
  790. }
  791. // 降采样,发布
  792. pcl::VoxelGrid<PointType> downSizeFilterGlobalMapKeyFrames; // for global map visualization
  793. downSizeFilterGlobalMapKeyFrames.setLeafSize(globalMapVisualizationLeafSize, globalMapVisualizationLeafSize, globalMapVisualizationLeafSize); // for global map visualization
  794. downSizeFilterGlobalMapKeyFrames.setInputCloud(subMapKeyFrames);
  795. downSizeFilterGlobalMapKeyFrames.filter(*subMapKeyFramesDS);
  796. std::cout << "subMapKeyFramesDS sizes = " << subMapKeyFramesDS->points.size() << std::endl;
  797. ikdtree.reconstruct(subMapKeyFramesDS->points);
  798. updateKdtreeCount = 0;
  799. ROS_INFO("Reconstructed ikdtree ");
  800. int featsFromMapNum = ikdtree.validnum();
  801. kdtree_size_st = ikdtree.size();
  802. std::cout << "featsFromMapNum = " << featsFromMapNum << "\t" << " kdtree_size_st = " << kdtree_size_st << std::endl;
  803. }
  804. updateKdtreeCount ++ ;
  805. }
  806. /**
  807. * 更新因子图中所有变量节点的位姿,也就是所有历史关键帧的位姿,更新里程计轨迹
  808. */
  809. void correctPoses()
  810. {
  811. if (cloudKeyPoses3D->points.empty())
  812. return;
  813. if (aLoopIsClosed == true)
  814. {
  815. // 清空里程计轨迹
  816. globalPath.poses.clear();
  817. // 更新因子图中所有变量节点的位姿,也就是所有历史关键帧的位姿
  818. int numPoses = isamCurrentEstimate.size();
  819. for (int i = 0; i < numPoses; ++i)
  820. {
  821. cloudKeyPoses3D->points[i].x = isamCurrentEstimate.at<gtsam::Pose3>(i).translation().x();
  822. cloudKeyPoses3D->points[i].y = isamCurrentEstimate.at<gtsam::Pose3>(i).translation().y();
  823. cloudKeyPoses3D->points[i].z = isamCurrentEstimate.at<gtsam::Pose3>(i).translation().z();
  824. cloudKeyPoses6D->points[i].x = cloudKeyPoses3D->points[i].x;
  825. cloudKeyPoses6D->points[i].y = cloudKeyPoses3D->points[i].y;
  826. cloudKeyPoses6D->points[i].z = cloudKeyPoses3D->points[i].z;
  827. cloudKeyPoses6D->points[i].roll = isamCurrentEstimate.at<gtsam::Pose3>(i).rotation().roll();
  828. cloudKeyPoses6D->points[i].pitch = isamCurrentEstimate.at<gtsam::Pose3>(i).rotation().pitch();
  829. cloudKeyPoses6D->points[i].yaw = isamCurrentEstimate.at<gtsam::Pose3>(i).rotation().yaw();
  830. // 更新里程计轨迹
  831. updatePath(cloudKeyPoses6D->points[i]);
  832. }
  833. // 清空局部map, reconstruct ikdtree submap
  834. recontructIKdTree();
  835. ROS_INFO("ISMA2 Update");
  836. aLoopIsClosed = false;
  837. }
  838. }
  839. //回环检测三大要素
  840. // 1.设置最小时间差,太近没必要
  841. // 2.控制回环的频率,避免频繁检测,每检测一次,就做一次等待
  842. // 3.根据当前最小距离重新计算等待时间
  843. bool detectLoopClosureDistance(int *latestID, int *closestID)
  844. {
  845. // 当前关键帧帧
  846. int loopKeyCur = copy_cloudKeyPoses3D->size() - 1; // 当前关键帧索引
  847. int loopKeyPre = -1;
  848. // 当前帧已经添加过闭环对应关系,不再继续添加
  849. auto it = loopIndexContainer.find(loopKeyCur);
  850. if (it != loopIndexContainer.end())
  851. return false;
  852. // 在历史关键帧中查找与当前关键帧距离最近的关键帧集合
  853. std::vector<int> pointSearchIndLoop; // 候选关键帧索引
  854. std::vector<float> pointSearchSqDisLoop; // 候选关键帧距离
  855. kdtreeHistoryKeyPoses->setInputCloud(copy_cloudKeyPoses3D); // 历史帧构建kdtree
  856. kdtreeHistoryKeyPoses->radiusSearch(copy_cloudKeyPoses3D->back(), historyKeyframeSearchRadius, pointSearchIndLoop, pointSearchSqDisLoop, 0);
  857. // 在候选关键帧集合中,找到与当前帧时间相隔较远的帧,设为候选匹配帧
  858. for (int i = 0; i < (int)pointSearchIndLoop.size(); ++i)
  859. {
  860. int id = pointSearchIndLoop[i];
  861. if (abs(copy_cloudKeyPoses6D->points[id].time - lidar_end_time) > historyKeyframeSearchTimeDiff)
  862. {
  863. loopKeyPre = id;
  864. break;
  865. }
  866. }
  867. if (loopKeyPre == -1 || loopKeyCur == loopKeyPre)
  868. return false;
  869. *latestID = loopKeyCur;
  870. *closestID = loopKeyPre;
  871. ROS_INFO("Find loop clousre frame ");
  872. return true;
  873. }
  874. //回环检测线程
  875. void loopClosureThread()
  876. {
  877. /*if (loopClosureEnableFlag == false)
  878. {
  879. std::cout << "loopClosureEnableFlag == false " << endl;
  880. return;
  881. }
  882. ros::Rate rate(loopClosureFrequency); // 回环频率
  883. while (ros::ok() && startFlag)
  884. {
  885. rate.sleep();
  886. performLoopClosure(); // 回环检测
  887. visualizeLoopClosure(); // rviz展示闭环边
  888. }*/
  889. }
  890. void SigHandle(int sig)
  891. {
  892. flg_exit = true;
  893. ROS_WARN("catch sig %d", sig);
  894. sig_buffer.notify_all();
  895. }
  896. inline void dump_lio_state_to_log(FILE *fp)
  897. {
  898. V3D rot_ang(Log(state_point.rot.toRotationMatrix()));
  899. fprintf(fp, "%lf ", Measures.lidar_beg_time - first_lidar_time);
  900. fprintf(fp, "%lf %lf %lf ", rot_ang(0), rot_ang(1), rot_ang(2)); // Angle
  901. fprintf(fp, "%lf %lf %lf ", state_point.pos(0), state_point.pos(1), state_point.pos(2)); // Pos
  902. fprintf(fp, "%lf %lf %lf ", 0.0, 0.0, 0.0); // omega
  903. fprintf(fp, "%lf %lf %lf ", state_point.vel(0), state_point.vel(1), state_point.vel(2)); // Vel
  904. fprintf(fp, "%lf %lf %lf ", 0.0, 0.0, 0.0); // Acc
  905. fprintf(fp, "%lf %lf %lf ", state_point.bg(0), state_point.bg(1), state_point.bg(2)); // Bias_g
  906. fprintf(fp, "%lf %lf %lf ", state_point.ba(0), state_point.ba(1), state_point.ba(2)); // Bias_a
  907. fprintf(fp, "%lf %lf %lf ", state_point.grav[0], state_point.grav[1], state_point.grav[2]); // Bias_a
  908. fprintf(fp, "\r\n");
  909. fflush(fp);
  910. }
  911. void pointBodyToWorld_ikfom(PointType const *const pi, PointType *const po, state_ikfom &s)
  912. {
  913. V3D p_body(pi->x, pi->y, pi->z);
  914. V3D p_global(s.rot * (s.offset_R_L_I * p_body + s.offset_T_L_I) + s.pos);
  915. po->x = p_global(0);
  916. po->y = p_global(1);
  917. po->z = p_global(2);
  918. po->intensity = pi->intensity;
  919. }
  920. //按当前body(lidar)的状态,将局部点转换到世界系下
  921. void pointBodyToWorld(PointType const *const pi, PointType *const po)
  922. {
  923. V3D p_body(pi->x, pi->y, pi->z);
  924. // world <-- imu <-- lidar
  925. V3D p_global(state_point.rot * (state_point.offset_R_L_I * p_body + state_point.offset_T_L_I) + state_point.pos);
  926. po->x = p_global(0);
  927. po->y = p_global(1);
  928. po->z = p_global(2);
  929. po->intensity = pi->intensity;
  930. }
  931. template <typename T>
  932. void pointBodyToWorld(const Matrix<T, 3, 1> &pi, Matrix<T, 3, 1> &po)
  933. {
  934. V3D p_body(pi[0], pi[1], pi[2]);
  935. V3D p_global(state_point.rot * (state_point.offset_R_L_I * p_body + state_point.offset_T_L_I) + state_point.pos);
  936. po[0] = p_global(0);
  937. po[1] = p_global(1);
  938. po[2] = p_global(2);
  939. }
  940. void RGBpointBodyToWorld(PointType const *const pi, PointType *const po) // lidar2world
  941. {
  942. V3D p_body(pi->x, pi->y, pi->z);
  943. V3D p_global(state_point.rot * (state_point.offset_R_L_I * p_body + state_point.offset_T_L_I) + state_point.pos);
  944. po->x = p_global(0);
  945. po->y = p_global(1);
  946. po->z = p_global(2);
  947. po->intensity = pi->intensity;
  948. }
  949. void test_RGBpointBodyToWorld(PointType const *const pi, PointType *const po,Eigen::Vector3d pos, Eigen::Matrix3d rotation) // lidar2world
  950. {
  951. V3D p_body(pi->x, pi->y, pi->z);
  952. V3D p_global(rotation * (state_point.offset_R_L_I * p_body + state_point.offset_T_L_I) + pos);
  953. po->x = p_global(0);
  954. po->y = p_global(1);
  955. po->z = p_global(2);
  956. po->intensity = pi->intensity;
  957. }
  958. void RGBpointBodyLidarToIMU(PointType const *const pi, PointType *const po)
  959. {
  960. V3D p_body_lidar(pi->x, pi->y, pi->z);
  961. V3D p_body_imu(state_point.offset_R_L_I * p_body_lidar + state_point.offset_T_L_I);
  962. po->x = p_body_imu(0);
  963. po->y = p_body_imu(1);
  964. po->z = p_body_imu(2);
  965. po->intensity = pi->intensity;
  966. }
  967. void points_cache_collect()
  968. {
  969. PointVector points_history;
  970. ikdtree.acquire_removed_points(points_history);
  971. for (int i = 0; i < points_history.size(); i++)
  972. _featsArray->push_back(points_history[i]);
  973. }
  974. //根据lidar的FoV分割场景
  975. BoxPointType LocalMap_Points; // ikd-tree中,局部地图的包围盒角点
  976. bool Localmap_Initialized = false;
  977. void lasermap_fov_segment()
  978. {
  979. cub_needrm.clear(); // 清空需要移除的区域
  980. kdtree_delete_counter = 0;
  981. kdtree_delete_time = 0.0;
  982. pointBodyToWorld(XAxisPoint_body, XAxisPoint_world); // X轴分界点转换到w系下
  983. V3D pos_LiD = pos_lid; // global系lidar位置
  984. //初始化局部地图包围盒角点,以为w系下lidar位置为中心
  985. if (!Localmap_Initialized)
  986. {
  987. for (int i = 0; i < 3; i++)
  988. {
  989. LocalMap_Points.vertex_min[i] = pos_LiD(i) - cube_len / 2.0;
  990. LocalMap_Points.vertex_max[i] = pos_LiD(i) + cube_len / 2.0;
  991. }
  992. Localmap_Initialized = true;
  993. return;
  994. }
  995. float dist_to_map_edge[3][2]; //各个方向与局部地图边界的距离
  996. bool need_move = false;
  997. for (int i = 0; i < 3; i++)
  998. {
  999. dist_to_map_edge[i][0] = fabs(pos_LiD(i) - LocalMap_Points.vertex_min[i]);
  1000. dist_to_map_edge[i][1] = fabs(pos_LiD(i) - LocalMap_Points.vertex_max[i]);
  1001. //与某个方向上的边界距离太小,标记需要移除need_move
  1002. if (dist_to_map_edge[i][0] <= MOV_THRESHOLD * DET_RANGE || dist_to_map_edge[i][1] <= MOV_THRESHOLD * DET_RANGE)
  1003. need_move = true;
  1004. }
  1005. //不需要移除则直接返回
  1006. if (!need_move)
  1007. return;
  1008. BoxPointType New_LocalMap_Points, tmp_boxpoints;
  1009. New_LocalMap_Points = LocalMap_Points; // 新的局部地图角点
  1010. float mov_dist = max((cube_len - 2.0 * MOV_THRESHOLD * DET_RANGE) * 0.5 * 0.9, double(DET_RANGE * (MOV_THRESHOLD - 1)));
  1011. for (int i = 0; i < 3; i++)
  1012. {
  1013. tmp_boxpoints = LocalMap_Points;
  1014. //与包围盒最小值角点距离
  1015. if (dist_to_map_edge[i][0] <= MOV_THRESHOLD * DET_RANGE)
  1016. {
  1017. New_LocalMap_Points.vertex_max[i] -= mov_dist;
  1018. New_LocalMap_Points.vertex_min[i] -= mov_dist;
  1019. tmp_boxpoints.vertex_min[i] = LocalMap_Points.vertex_max[i] - mov_dist;
  1020. cub_needrm.push_back(tmp_boxpoints); // 移除较远包围盒
  1021. }
  1022. else if (dist_to_map_edge[i][1] <= MOV_THRESHOLD * DET_RANGE)
  1023. {
  1024. New_LocalMap_Points.vertex_max[i] += mov_dist;
  1025. New_LocalMap_Points.vertex_min[i] += mov_dist;
  1026. tmp_boxpoints.vertex_max[i] = LocalMap_Points.vertex_min[i] + mov_dist;
  1027. cub_needrm.push_back(tmp_boxpoints);
  1028. }
  1029. }
  1030. LocalMap_Points = New_LocalMap_Points;
  1031. points_cache_collect();
  1032. double delete_begin = omp_get_wtime();
  1033. if (cub_needrm.size() > 0)
  1034. kdtree_delete_counter = ikdtree.Delete_Point_Boxes(cub_needrm);
  1035. kdtree_delete_time = omp_get_wtime() - delete_begin;
  1036. }
  1037. void standard_pcl_cbk(const sensor_msgs::PointCloud2::ConstPtr &msg)
  1038. {
  1039. mtx_buffer.lock();
  1040. scan_count++;
  1041. double preprocess_start_time = omp_get_wtime();
  1042. if (msg->header.stamp.toSec() < last_timestamp_lidar)
  1043. {
  1044. ROS_ERROR("lidar loop back, clear buffer");
  1045. lidar_buffer.clear();
  1046. }
  1047. PointCloudXYZI::Ptr ptr(new PointCloudXYZI());
  1048. p_pre->process(msg, ptr);
  1049. lidar_buffer.push_back(ptr);
  1050. time_buffer.push_back(msg->header.stamp.toSec());
  1051. last_timestamp_lidar = msg->header.stamp.toSec();
  1052. s_plot11[scan_count] = omp_get_wtime() - preprocess_start_time;
  1053. mtx_buffer.unlock();
  1054. sig_buffer.notify_all();
  1055. }
  1056. double timediff_lidar_wrt_imu = 0.0;
  1057. bool timediff_set_flg = false; // 标记是否已经进行了时间补偿
  1058. void livox_pcl_cbk(const livox_ros_driver::CustomMsg::ConstPtr &msg)
  1059. {
  1060. mtx_buffer.lock();
  1061. double preprocess_start_time = omp_get_wtime();
  1062. scan_count++;
  1063. if (msg->header.stamp.toSec() < last_timestamp_lidar)
  1064. {
  1065. ROS_ERROR("lidar loop back, clear buffer");
  1066. lidar_buffer.clear();
  1067. }
  1068. last_timestamp_lidar = msg->header.stamp.toSec();
  1069. if (!time_sync_en && abs(last_timestamp_imu - last_timestamp_lidar) > 10.0 && !imu_buffer.empty() && !lidar_buffer.empty())
  1070. {
  1071. printf("IMU and LiDAR not Synced, IMU time: %lf, lidar header time: %lf \n", last_timestamp_imu, last_timestamp_lidar);
  1072. }
  1073. if (time_sync_en && !timediff_set_flg && abs(last_timestamp_lidar - last_timestamp_imu) > 1 && !imu_buffer.empty())
  1074. {
  1075. timediff_set_flg = true;
  1076. timediff_lidar_wrt_imu = last_timestamp_lidar + 0.1 - last_timestamp_imu; //????
  1077. printf("Self sync IMU and LiDAR, time diff is %.10lf \n", timediff_lidar_wrt_imu);
  1078. }
  1079. PointCloudXYZI::Ptr ptr(new PointCloudXYZI());
  1080. // 特征提取或间隔采样
  1081. p_pre->process(msg, ptr);
  1082. lidar_buffer.push_back(ptr); //储存处理后的lidar特征
  1083. time_buffer.push_back(last_timestamp_lidar);
  1084. s_plot11[scan_count] = omp_get_wtime() - preprocess_start_time;
  1085. mtx_buffer.unlock();
  1086. sig_buffer.notify_all();
  1087. }
  1088. void imu_cbk(const sensor_msgs::Imu::ConstPtr &msg_in)
  1089. {
  1090. publish_count++;
  1091. // cout<<"IMU got at: "<<msg_in->header.stamp.toSec()<<endl;
  1092. sensor_msgs::Imu::Ptr msg(new sensor_msgs::Imu(*msg_in));
  1093. // lidar 和 imu时间差过大,且开启 时间同步, 纠正当前输入imu的时间
  1094. if (abs(timediff_lidar_wrt_imu) > 0.1 && time_sync_en)
  1095. {
  1096. // 对输入imu时间,纠正为 时间差 + 原始时间
  1097. msg->header.stamp =
  1098. ros::Time().fromSec(timediff_lidar_wrt_imu + msg_in->header.stamp.toSec());
  1099. }
  1100. double timestamp = msg->header.stamp.toSec();
  1101. mtx_buffer.lock();
  1102. if (timestamp < last_timestamp_imu)
  1103. {
  1104. ROS_WARN("imu loop back, clear buffer");
  1105. imu_buffer.clear();
  1106. }
  1107. last_timestamp_imu = timestamp; // update imu time
  1108. imu_buffer.push_back(msg);
  1109. mtx_buffer.unlock();
  1110. sig_buffer.notify_all();
  1111. }
  1112. double lidar_mean_scantime = 0.0;
  1113. int scan_num = 0;
  1114. bool sync_packages(MeasureGroup &meas)
  1115. {
  1116. if (lidar_buffer.empty() || imu_buffer.empty())
  1117. {
  1118. return false;
  1119. }
  1120. /*** push a lidar scan ***/
  1121. if (!lidar_pushed)
  1122. {
  1123. meas.lidar = lidar_buffer.front(); // lidar指针指向最旧的lidar数据
  1124. meas.lidar_beg_time = time_buffer.front(); //记录最早时间
  1125. //更新结束时刻的时间
  1126. if (meas.lidar->points.size() <= 1) // time too little 时间太短,点数不足
  1127. {
  1128. lidar_end_time = meas.lidar_beg_time + lidar_mean_scantime; // 记录lidar结束时间为 起始时间 + 单帧扫描时间
  1129. ROS_WARN("Too few input point cloud!\n");
  1130. }
  1131. else if (meas.lidar->points.back().curvature / double(1000) < 0.5 * lidar_mean_scantime) //最后一个点的时间 小于 单帧扫描时间的一半
  1132. {
  1133. lidar_end_time = meas.lidar_beg_time + lidar_mean_scantime; // 记录lidar结束时间为 起始时间 + 单帧扫描时间
  1134. }
  1135. else
  1136. {
  1137. scan_num++;
  1138. lidar_end_time = meas.lidar_beg_time + meas.lidar->points.back().curvature / double(1000); //结束时间设置为 起始时间 + 最后一个点的时间(相对)
  1139. // 动态更新每帧lidar数据平均扫描时间
  1140. lidar_mean_scantime += (meas.lidar->points.back().curvature / double(1000) - lidar_mean_scantime) / scan_num;
  1141. }
  1142. meas.lidar_end_time = lidar_end_time;
  1143. lidar_pushed = true;
  1144. }
  1145. if (last_timestamp_imu < lidar_end_time)
  1146. {
  1147. return false;
  1148. }
  1149. /*** push imu data, and pop from imu buffer ***/
  1150. double imu_time = imu_buffer.front()->header.stamp.toSec(); // 最旧IMU时间
  1151. meas.imu.clear();
  1152. while ((!imu_buffer.empty()) && (imu_time < lidar_end_time)) //记录imu数据,imu时间小于当前帧lidar结束时间
  1153. {
  1154. imu_time = imu_buffer.front()->header.stamp.toSec();
  1155. if (imu_time > lidar_end_time)
  1156. break;
  1157. meas.imu.push_back(imu_buffer.front()); //记录当前lidar帧内的imu数据到meas.imu
  1158. imu_buffer.pop_front();
  1159. }
  1160. lidar_buffer.pop_front();
  1161. time_buffer.pop_front();
  1162. lidar_pushed = false;
  1163. return true;
  1164. }
  1165. int process_increments = 0;
  1166. void map_incremental()
  1167. {
  1168. PointVector PointToAdd; //需要加入到ikd-tree中的点云
  1169. PointVector PointNoNeedDownsample; //加入ikd-tree时,不需要降采样的点云
  1170. PointToAdd.reserve(feats_down_size);
  1171. PointNoNeedDownsample.reserve(feats_down_size);
  1172. //根据点与所在包围盒中心点的距离,分类是否需要降采样
  1173. for (int i = 0; i < feats_down_size; i++)
  1174. {
  1175. /* transform to world frame */
  1176. pointBodyToWorld(&(feats_down_body->points[i]), &(feats_down_world->points[i]));
  1177. /* decide if need add to map */
  1178. if (!Nearest_Points[i].empty() && flg_EKF_inited)
  1179. {
  1180. const PointVector &points_near = Nearest_Points[i];
  1181. bool need_add = true;
  1182. BoxPointType Box_of_Point;
  1183. PointType downsample_result, mid_point;
  1184. mid_point.x = floor(feats_down_world->points[i].x / filter_size_map_min) * filter_size_map_min + 0.5 * filter_size_map_min;
  1185. mid_point.y = floor(feats_down_world->points[i].y / filter_size_map_min) * filter_size_map_min + 0.5 * filter_size_map_min;
  1186. mid_point.z = floor(feats_down_world->points[i].z / filter_size_map_min) * filter_size_map_min + 0.5 * filter_size_map_min;
  1187. float dist = calc_dist(feats_down_world->points[i], mid_point); //当前点与box中心的距离
  1188. //判断最近点在x、y、z三个方向上,与中心的距离,判断是否加入时需要降采样
  1189. if (fabs(points_near[0].x - mid_point.x) > 0.5 * filter_size_map_min && fabs(points_near[0].y - mid_point.y) > 0.5 * filter_size_map_min && fabs(points_near[0].z - mid_point.z) > 0.5 * filter_size_map_min)
  1190. {
  1191. PointNoNeedDownsample.push_back(feats_down_world->points[i]); //若三个方向距离都大于地图珊格半轴长,无需降采样
  1192. continue;
  1193. }
  1194. //判断当前点的 NUM_MATCH_POINTS 个邻近点与包围盒中心的范围
  1195. for (int readd_i = 0; readd_i < NUM_MATCH_POINTS; readd_i++)
  1196. {
  1197. if (points_near.size() < NUM_MATCH_POINTS)
  1198. break;
  1199. if (calc_dist(points_near[readd_i], mid_point) < dist) // 如果邻近点到中心的距离 小于 当前点到中心的距离,则不需要添加当前点
  1200. {
  1201. need_add = false;
  1202. break;
  1203. }
  1204. }
  1205. if (need_add)
  1206. PointToAdd.push_back(feats_down_world->points[i]);
  1207. }
  1208. else
  1209. {
  1210. PointToAdd.push_back(feats_down_world->points[i]);
  1211. }
  1212. }
  1213. double st_time = omp_get_wtime();
  1214. add_point_size = ikdtree.Add_Points(PointToAdd, true); //加入点时需要降采样
  1215. ikdtree.Add_Points(PointNoNeedDownsample, false); //加入点时不需要降采样
  1216. add_point_size = PointToAdd.size() + PointNoNeedDownsample.size();
  1217. kdtree_incremental_time = omp_get_wtime() - st_time;
  1218. }
  1219. PointCloudXYZI::Ptr pcl_wait_pub(new PointCloudXYZI(500000, 1));
  1220. PointCloudXYZI::Ptr pcl_wait_save(new PointCloudXYZI());
  1221. void publish_frame_world(const ros::Publisher &pubLaserCloudFull) // 将稠密点云从 imu convert to world
  1222. {
  1223. if (scan_pub_en)
  1224. {
  1225. PointCloudXYZI::Ptr laserCloudFullRes(dense_pub_en ? feats_undistort : feats_down_body);
  1226. int size = laserCloudFullRes->points.size();
  1227. PointCloudXYZI::Ptr laserCloudWorld(
  1228. new PointCloudXYZI(size, 1));
  1229. for (int i = 0; i < size; i++)
  1230. {
  1231. RGBpointBodyToWorld(&laserCloudFullRes->points[i],
  1232. &laserCloudWorld->points[i]);
  1233. }
  1234. sensor_msgs::PointCloud2 laserCloudmsg;
  1235. pcl::toROSMsg(*laserCloudWorld, laserCloudmsg);
  1236. laserCloudmsg.header.stamp = ros::Time().fromSec(lidar_end_time);
  1237. laserCloudmsg.header.frame_id = "camera_init";
  1238. pubLaserCloudFull.publish(laserCloudmsg);
  1239. publish_count -= PUBFRAME_PERIOD;
  1240. }
  1241. /**************** save map ****************/
  1242. /* 1. make sure you have enough memories
  1243. /* 2. noted that pcd save will influence the real-time performences **/
  1244. if (pcd_save_en)
  1245. {
  1246. int size = feats_undistort->points.size();
  1247. PointCloudXYZI::Ptr laserCloudWorld(
  1248. new PointCloudXYZI(size, 1));
  1249. for (int i = 0; i < size; i++)
  1250. {
  1251. RGBpointBodyToWorld(&feats_undistort->points[i],
  1252. &laserCloudWorld->points[i]);
  1253. }
  1254. *pcl_wait_save += *laserCloudWorld;
  1255. static int scan_wait_num = 0;
  1256. scan_wait_num++;
  1257. if (pcl_wait_save->size() > 0 && pcd_save_interval > 0 && scan_wait_num >= pcd_save_interval)
  1258. {
  1259. pcd_index++;
  1260. string all_points_dir(string(string(ROOT_DIR) + "PCD/scans_") + to_string(pcd_index) + string(".pcd"));
  1261. pcl::PCDWriter pcd_writer;
  1262. cout << "current scan saved to /PCD/" << all_points_dir << endl;
  1263. pcd_writer.writeBinary(all_points_dir, *pcl_wait_save);
  1264. pcl_wait_save->clear();
  1265. scan_wait_num = 0;
  1266. }
  1267. }
  1268. }
  1269. void publish_frame_body(const ros::Publisher &pubLaserCloudFull_body) // 发布body系(imu)下的点云
  1270. {
  1271. int size = feats_undistort->points.size();
  1272. PointCloudXYZI::Ptr laserCloudIMUBody(new PointCloudXYZI(size, 1));
  1273. for (int i = 0; i < size; i++)
  1274. {
  1275. RGBpointBodyLidarToIMU(&feats_undistort->points[i],
  1276. &laserCloudIMUBody->points[i]);
  1277. }
  1278. sensor_msgs::PointCloud2 laserCloudmsg;
  1279. pcl::toROSMsg(*laserCloudIMUBody, laserCloudmsg);
  1280. laserCloudmsg.header.stamp = ros::Time().fromSec(lidar_end_time);
  1281. laserCloudmsg.header.frame_id = "body";
  1282. pubLaserCloudFull_body.publish(laserCloudmsg);
  1283. publish_count -= PUBFRAME_PERIOD;
  1284. }
  1285. void publish_effect_world(const ros::Publisher &pubLaserCloudEffect)
  1286. {
  1287. PointCloudXYZI::Ptr laserCloudWorld(
  1288. new PointCloudXYZI(effct_feat_num, 1));
  1289. for (int i = 0; i < effct_feat_num; i++)
  1290. {
  1291. RGBpointBodyToWorld(&laserCloudOri->points[i],
  1292. &laserCloudWorld->points[i]);
  1293. }
  1294. sensor_msgs::PointCloud2 laserCloudFullRes3;
  1295. pcl::toROSMsg(*laserCloudWorld, laserCloudFullRes3);
  1296. laserCloudFullRes3.header.stamp = ros::Time().fromSec(lidar_end_time);
  1297. laserCloudFullRes3.header.frame_id = "camera_init";
  1298. pubLaserCloudEffect.publish(laserCloudFullRes3);
  1299. }
  1300. void publish_map(const ros::Publisher &pubLaserCloudMap)
  1301. {
  1302. sensor_msgs::PointCloud2 laserCloudMap;
  1303. pcl::toROSMsg(*featsFromMap, laserCloudMap);
  1304. laserCloudMap.header.stamp = ros::Time().fromSec(lidar_end_time);
  1305. laserCloudMap.header.frame_id = "camera_init";
  1306. pubLaserCloudMap.publish(laserCloudMap);
  1307. }
  1308. template <typename T>
  1309. void set_posestamp(T &out)
  1310. {
  1311. out.pose.position.x = state_point.pos(0);
  1312. out.pose.position.y = state_point.pos(1);
  1313. out.pose.position.z = state_point.pos(2);
  1314. out.pose.orientation.x = geoQuat.x;
  1315. out.pose.orientation.y = geoQuat.y;
  1316. out.pose.orientation.z = geoQuat.z;
  1317. out.pose.orientation.w = geoQuat.w;
  1318. }
  1319. void publish_odometry(const ros::Publisher &pubOdomAftMapped)
  1320. {
  1321. odomAftMapped.header.frame_id = "camera_init";
  1322. odomAftMapped.child_frame_id = "body";
  1323. odomAftMapped.header.stamp = ros::Time().fromSec(lidar_end_time); // ros::Time().fromSec(lidar_end_time);
  1324. set_posestamp(odomAftMapped.pose);
  1325. pubOdomAftMapped.publish(odomAftMapped);
  1326. auto P = ikfom_.get_P();
  1327. for (int i = 0; i < 6; i++)
  1328. {
  1329. int k = i < 3 ? i + 3 : i - 3;
  1330. odomAftMapped.pose.covariance[i * 6 + 0] = P(k, 3);
  1331. odomAftMapped.pose.covariance[i * 6 + 1] = P(k, 4);
  1332. odomAftMapped.pose.covariance[i * 6 + 2] = P(k, 5);
  1333. odomAftMapped.pose.covariance[i * 6 + 3] = P(k, 0);
  1334. odomAftMapped.pose.covariance[i * 6 + 4] = P(k, 1);
  1335. odomAftMapped.pose.covariance[i * 6 + 5] = P(k, 2);
  1336. }
  1337. static tf::TransformBroadcaster br;
  1338. tf::Transform transform;
  1339. tf::Quaternion q;
  1340. transform.setOrigin(tf::Vector3(odomAftMapped.pose.pose.position.x,
  1341. odomAftMapped.pose.pose.position.y,
  1342. odomAftMapped.pose.pose.position.z));
  1343. q.setW(odomAftMapped.pose.pose.orientation.w);
  1344. q.setX(odomAftMapped.pose.pose.orientation.x);
  1345. q.setY(odomAftMapped.pose.pose.orientation.y);
  1346. q.setZ(odomAftMapped.pose.pose.orientation.z);
  1347. transform.setRotation(q);
  1348. br.sendTransform(tf::StampedTransform(transform, odomAftMapped.header.stamp, "camera_init", "body"));
  1349. }
  1350. void publish_path(const ros::Publisher pubPath)
  1351. {
  1352. set_posestamp(msg_body_pose);
  1353. msg_body_pose.header.stamp = ros::Time().fromSec(lidar_end_time);
  1354. msg_body_pose.header.frame_id = "camera_init";
  1355. /*** if path is too large, the rvis will crash ***/
  1356. static int jjj = 0;
  1357. jjj++;
  1358. if (jjj % 10 == 0)
  1359. {
  1360. path.poses.push_back(msg_body_pose);
  1361. pubPath.publish(path);
  1362. // save unoptimized pose
  1363. V3D rot_ang(Log( state_point.rot.toRotationMatrix())); // 旋转向量
  1364. PointTypePose thisPose6D;
  1365. thisPose6D.x = msg_body_pose.pose.position.x ;
  1366. thisPose6D.y = msg_body_pose.pose.position.y ;
  1367. thisPose6D.z = msg_body_pose.pose.position.z ;
  1368. thisPose6D.roll = rot_ang(0) ;
  1369. thisPose6D.pitch = rot_ang(1) ;
  1370. thisPose6D.yaw = rot_ang(2) ;
  1371. fastlio_unoptimized_cloudKeyPoses6D->push_back(thisPose6D);
  1372. }
  1373. }
  1374. void publish_path_update(const ros::Publisher pubPath)
  1375. {
  1376. ros::Time timeLaserInfoStamp = ros::Time().fromSec(lidar_end_time); // 时间戳
  1377. string odometryFrame = "camera_init";
  1378. if (pubPath.getNumSubscribers() != 0)
  1379. {
  1380. /*** if path is too large, the rvis will crash ***/
  1381. static int kkk = 0;
  1382. kkk++;
  1383. if (kkk % 10 == 0)
  1384. {
  1385. // path.poses.push_back(globalPath);
  1386. globalPath.header.stamp = timeLaserInfoStamp;
  1387. globalPath.header.frame_id = odometryFrame;
  1388. pubPath.publish(globalPath);
  1389. }
  1390. }
  1391. }
  1392. /*定义pose结构体*/
  1393. struct pose
  1394. {
  1395. Eigen::Vector3d t ;
  1396. Eigen::Matrix3d R;
  1397. };
  1398. bool CreateFile(std::ofstream& ofs, std::string file_path) {
  1399. ofs.open(file_path, std::ios::out); // 使用std::ios::out 可实现覆盖
  1400. if(!ofs)
  1401. {
  1402. std::cout << "open csv file error " << std::endl;
  1403. return false;
  1404. }
  1405. return true;
  1406. }
  1407. /* write2txt format KITTI*/
  1408. void WriteText(std::ofstream& ofs, pose data){
  1409. ofs << std::fixed << data.R(0,0) << " " << data.R(0,1) << " "<< data.R(0,2) << " " << data.t[0] << " "
  1410. << data.R(1,0) << " " << data.R(1,1) <<" " << data.R(1,2) << " " << data.t[1] << " "
  1411. << data.R(2,0) << " " << data.R(2,1) <<" " << data.R(2,2) << " " << data.t[2] << std::endl;
  1412. }
  1413. bool savePoseService(fast_lio_sam::save_poseRequest& req, fast_lio_sam::save_poseResponse& res)
  1414. {
  1415. pose pose_gnss ;
  1416. pose pose_optimized ;
  1417. pose pose_without_optimized ;
  1418. //std::ofstream file_pose_gnss ;
  1419. std::ofstream file_pose_optimized ;
  1420. std::ofstream file_pose_without_optimized ;
  1421. string savePoseDirectory;
  1422. cout << "****************************************************" << endl;
  1423. cout << "Saving poses to pose files ..." << endl;
  1424. if(req.destination.empty()) savePoseDirectory = std::getenv("HOME") + savePCDDirectory;
  1425. else savePoseDirectory = std::getenv("HOME") + req.destination;
  1426. cout << "Save destination: " << savePoseDirectory << endl;
  1427. // create file
  1428. // CreateFile(file_pose_gnss, savePoseDirectory + "/gnss_pose.txt");
  1429. CreateFile(file_pose_optimized, savePoseDirectory + "/optimized_pose.txt");
  1430. CreateFile(file_pose_without_optimized, savePoseDirectory + "/without_optimized_pose.txt");
  1431. // save optimize data
  1432. for(int i = 0; i < cloudKeyPoses6D->size(); i++){
  1433. pose_optimized.t = Eigen::Vector3d(cloudKeyPoses6D->points[i].x, cloudKeyPoses6D->points[i].y, cloudKeyPoses6D->points[i].z );
  1434. pose_optimized.R = Exp(double(cloudKeyPoses6D->points[i].roll), double(cloudKeyPoses6D->points[i].pitch), double(cloudKeyPoses6D->points[i].yaw) );
  1435. WriteText(file_pose_optimized, pose_optimized);
  1436. }
  1437. cout << "Sucess global optimized poses to pose files ..." << endl;
  1438. for(int i = 0; i < fastlio_unoptimized_cloudKeyPoses6D->size(); i++){
  1439. pose_without_optimized.t = Eigen::Vector3d(fastlio_unoptimized_cloudKeyPoses6D->points[i].x, fastlio_unoptimized_cloudKeyPoses6D->points[i].y, fastlio_unoptimized_cloudKeyPoses6D->points[i].z );
  1440. pose_without_optimized.R = Exp(double(fastlio_unoptimized_cloudKeyPoses6D->points[i].roll), double(fastlio_unoptimized_cloudKeyPoses6D->points[i].pitch), double(fastlio_unoptimized_cloudKeyPoses6D->points[i].yaw) );
  1441. WriteText(file_pose_without_optimized, pose_without_optimized);
  1442. }
  1443. cout << "Sucess unoptimized poses to pose files ..." << endl;
  1444. //file_pose_gnss.close();
  1445. file_pose_optimized.close();
  1446. file_pose_without_optimized.close();
  1447. return true ;
  1448. }
  1449. /**
  1450. * 保存全局关键帧特征点集合
  1451. */
  1452. bool saveMapService(fast_lio_sam::save_mapRequest& req, fast_lio_sam::save_mapResponse& res)
  1453. {
  1454. string saveMapDirectory;
  1455. cout << "****************************************************" << endl;
  1456. cout << "Saving map to pcd files ..." << endl;
  1457. if(req.destination.empty()) saveMapDirectory = std::getenv("HOME") + savePCDDirectory;
  1458. else saveMapDirectory = std::getenv("HOME") + req.destination;
  1459. cout << "Save destination: " << saveMapDirectory << endl;
  1460. // 这个代码太坑了!!注释掉
  1461. // int unused = system((std::string("exec rm -r ") + saveMapDirectory).c_str());
  1462. // unused = system((std::string("mkdir -p ") + saveMapDirectory).c_str());
  1463. // 保存历史关键帧位姿
  1464. pcl::io::savePCDFileBinary(saveMapDirectory + "/trajectory.pcd", *cloudKeyPoses3D); // 关键帧位置
  1465. pcl::io::savePCDFileBinary(saveMapDirectory + "/transformations.pcd", *cloudKeyPoses6D); // 关键帧位姿
  1466. // 提取历史关键帧点集合
  1467. pcl::PointCloud<PointType>::Ptr globalSurfCloud(new pcl::PointCloud<PointType>());
  1468. pcl::PointCloud<PointType>::Ptr globalSurfCloudDS(new pcl::PointCloud<PointType>());
  1469. pcl::PointCloud<PointType>::Ptr globalMapCloud(new pcl::PointCloud<PointType>());
  1470. // 注意:拼接地图时,keyframe是lidar系,而fastlio更新后的存到的cloudKeyPoses6D 关键帧位姿是body系下的,需要把
  1471. //cloudKeyPoses6D 转换为T_world_lidar 。 T_world_lidar = T_world_body * T_body_lidar , T_body_lidar 是外参
  1472. for (int i = 0; i < (int)cloudKeyPoses6D->size(); i++) {
  1473. // *globalCornerCloud += *transformPointCloud(cornerCloudKeyFrames[i], &cloudKeyPoses6D->points[i]);
  1474. *globalSurfCloud += *transformPointCloud(surfCloudKeyFrames[i], &cloudKeyPoses6D->points[i]);
  1475. cout << "\r" << std::flush << "Processing feature cloud " << i << " of " << cloudKeyPoses6D->size() << " ...";
  1476. }
  1477. if(req.resolution != 0)
  1478. {
  1479. cout << "\n\nSave resolution: " << req.resolution << endl;
  1480. // 降采样
  1481. downSizeFilterSurf.setInputCloud(globalSurfCloud);
  1482. downSizeFilterSurf.setLeafSize(req.resolution, req.resolution, req.resolution);
  1483. downSizeFilterSurf.filter(*globalSurfCloudDS);
  1484. pcl::io::savePCDFileBinary(saveMapDirectory + "/SurfMap.pcd", *globalSurfCloudDS);
  1485. }
  1486. else
  1487. {
  1488. // downSizeFilterCorner.setLeafSize(mappingCornerLeafSize, mappingCornerLeafSize, mappingCornerLeafSize);
  1489. downSizeFilterSurf.setInputCloud(globalSurfCloud);
  1490. downSizeFilterSurf.setLeafSize(mappingSurfLeafSize, mappingSurfLeafSize, mappingSurfLeafSize);
  1491. downSizeFilterSurf.filter(*globalSurfCloudDS);
  1492. // pcl::io::savePCDFileBinary(saveMapDirectory + "/CornerMap.pcd", *globalCornerCloud);
  1493. // pcl::io::savePCDFileBinary(saveMapDirectory + "/SurfMap.pcd", *globalSurfCloud); // 稠密点云地图
  1494. }
  1495. // 保存到一起,全局关键帧特征点集合
  1496. // *globalMapCloud += *globalCornerCloud;
  1497. *globalMapCloud += *globalSurfCloud;
  1498. pcl::io::savePCDFileBinary(saveMapDirectory + "/filterGlobalMap.pcd", *globalSurfCloudDS); // 滤波后地图
  1499. int ret = pcl::io::savePCDFileBinary(saveMapDirectory + "/GlobalMap.pcd", *globalMapCloud); // 稠密地图
  1500. res.success = ret == 0;
  1501. cout << "****************************************************" << endl;
  1502. cout << "Saving map to pcd files completed\n" << endl;
  1503. // visial optimize global map on viz
  1504. ros::Time timeLaserInfoStamp = ros::Time().fromSec(lidar_end_time);
  1505. string odometryFrame = "camera_init";
  1506. publishCloud(&pubOptimizedGlobalMap, globalSurfCloudDS, timeLaserInfoStamp, odometryFrame);
  1507. return true;
  1508. }
  1509. void saveMap()
  1510. {
  1511. fast_lio_sam::save_mapRequest req;
  1512. fast_lio_sam::save_mapResponse res;
  1513. // 保存全局关键帧特征点集合
  1514. if(!saveMapService(req, res)){
  1515. cout << "Fail to save map" << endl;
  1516. }
  1517. }
  1518. /**
  1519. * 发布局部关键帧map的特征点云
  1520. */
  1521. void publishGlobalMap()
  1522. {
  1523. /*** if path is too large, the rvis will crash ***/
  1524. ros::Time timeLaserInfoStamp = ros::Time().fromSec(lidar_end_time);
  1525. string odometryFrame = "camera_init";
  1526. if (pubLaserCloudSurround.getNumSubscribers() == 0)
  1527. return;
  1528. if (cloudKeyPoses3D->points.empty() == true)
  1529. return;
  1530. pcl::KdTreeFLANN<PointType>::Ptr kdtreeGlobalMap(new pcl::KdTreeFLANN<PointType>());
  1531. ;
  1532. pcl::PointCloud<PointType>::Ptr globalMapKeyPoses(new pcl::PointCloud<PointType>());
  1533. pcl::PointCloud<PointType>::Ptr globalMapKeyPosesDS(new pcl::PointCloud<PointType>());
  1534. pcl::PointCloud<PointType>::Ptr globalMapKeyFrames(new pcl::PointCloud<PointType>());
  1535. pcl::PointCloud<PointType>::Ptr globalMapKeyFramesDS(new pcl::PointCloud<PointType>());
  1536. // kdtree查找最近一帧关键帧相邻的关键帧集合
  1537. std::vector<int> pointSearchIndGlobalMap;
  1538. std::vector<float> pointSearchSqDisGlobalMap;
  1539. mtx.lock();
  1540. kdtreeGlobalMap->setInputCloud(cloudKeyPoses3D);
  1541. kdtreeGlobalMap->radiusSearch(cloudKeyPoses3D->back(), globalMapVisualizationSearchRadius, pointSearchIndGlobalMap, pointSearchSqDisGlobalMap, 0);
  1542. mtx.unlock();
  1543. for (int i = 0; i < (int)pointSearchIndGlobalMap.size(); ++i)
  1544. globalMapKeyPoses->push_back(cloudKeyPoses3D->points[pointSearchIndGlobalMap[i]]);
  1545. // 降采样
  1546. pcl::VoxelGrid<PointType> downSizeFilterGlobalMapKeyPoses;
  1547. downSizeFilterGlobalMapKeyPoses.setLeafSize(globalMapVisualizationPoseDensity, globalMapVisualizationPoseDensity, globalMapVisualizationPoseDensity); // for global map visualization
  1548. downSizeFilterGlobalMapKeyPoses.setInputCloud(globalMapKeyPoses);
  1549. downSizeFilterGlobalMapKeyPoses.filter(*globalMapKeyPosesDS);
  1550. // 提取局部相邻关键帧对应的特征点云
  1551. for (int i = 0; i < (int)globalMapKeyPosesDS->size(); ++i)
  1552. {
  1553. // 距离过大
  1554. if (pointDistance(globalMapKeyPosesDS->points[i], cloudKeyPoses3D->back()) > globalMapVisualizationSearchRadius)
  1555. continue;
  1556. int thisKeyInd = (int)globalMapKeyPosesDS->points[i].intensity;
  1557. // *globalMapKeyFrames += *transformPointCloud(cornerCloudKeyFrames[thisKeyInd], &cloudKeyPoses6D->points[thisKeyInd]);
  1558. *globalMapKeyFrames += *transformPointCloud(surfCloudKeyFrames[thisKeyInd], &cloudKeyPoses6D->points[thisKeyInd]); // fast_lio only use surfCloud
  1559. }
  1560. // 降采样,发布
  1561. pcl::VoxelGrid<PointType> downSizeFilterGlobalMapKeyFrames; // for global map visualization
  1562. downSizeFilterGlobalMapKeyFrames.setLeafSize(globalMapVisualizationLeafSize, globalMapVisualizationLeafSize, globalMapVisualizationLeafSize); // for global map visualization
  1563. downSizeFilterGlobalMapKeyFrames.setInputCloud(globalMapKeyFrames);
  1564. downSizeFilterGlobalMapKeyFrames.filter(*globalMapKeyFramesDS);
  1565. publishCloud(&pubLaserCloudSurround, globalMapKeyFramesDS, timeLaserInfoStamp, odometryFrame);
  1566. }
  1567. //构造H矩阵
  1568. void h_share_model(state_ikfom &s, esekfom::dyn_share_datastruct<double> &ekfom_data)
  1569. {
  1570. double match_start = omp_get_wtime();
  1571. laserCloudOri->clear();
  1572. corr_normvect->clear();
  1573. total_residual = 0.0;
  1574. /** closest surface search and residual computation **/
  1575. #ifdef MP_EN
  1576. omp_set_num_threads(MP_PROC_NUM);
  1577. #pragma omp parallel for
  1578. #endif
  1579. for (int i = 0; i < feats_down_size; i++) //判断每个点的对应邻域是否符合平面点的假设
  1580. {
  1581. PointType &point_body = feats_down_body->points[i]; // lidar系下坐标
  1582. PointType &point_world = feats_down_world->points[i]; // lidar数据点在world系下坐标
  1583. /* transform to world frame */
  1584. V3D p_body(point_body.x, point_body.y, point_body.z); // lidar系下坐标
  1585. V3D p_global(s.rot * (s.offset_R_L_I * p_body + s.offset_T_L_I) + s.pos); // w系下坐标
  1586. point_world.x = p_global(0);
  1587. point_world.y = p_global(1);
  1588. point_world.z = p_global(2);
  1589. point_world.intensity = point_body.intensity;
  1590. vector<float> pointSearchSqDis(NUM_MATCH_POINTS);
  1591. auto &points_near = Nearest_Points[i];
  1592. if (ekfom_data.converge) // 如果收敛了
  1593. {
  1594. /** Find the closest surfaces in the map **/
  1595. // world系下从ikdtree找5个最近点用于平面拟合
  1596. ikdtree.Nearest_Search(point_world, NUM_MATCH_POINTS, points_near, pointSearchSqDis);
  1597. //最近点数大于NUM_MATCH_POINTS,且最大距离小于等于5,point_selected_surf设置为true
  1598. point_selected_surf[i] = points_near.size() < NUM_MATCH_POINTS ? false : pointSearchSqDis[NUM_MATCH_POINTS - 1] > 5 ? false
  1599. : true;
  1600. }
  1601. //不符合平面特征
  1602. if (!point_selected_surf[i])
  1603. continue;
  1604. VF(4) pabcd; // plane 参数 a b c d
  1605. point_selected_surf[i] = false; //二次筛选平面点
  1606. //拟合局部平面,返回:是否有内点大于距离阈值
  1607. if (esti_plane(pabcd, points_near, 0.1f))
  1608. {
  1609. // plane distance
  1610. float pd2 = pabcd(0) * point_world.x + pabcd(1) * point_world.y + pabcd(2) * point_world.z + pabcd(3);
  1611. float s = 1 - 0.9 * fabs(pd2) / sqrt(p_body.norm()); //筛选条件 1 - 0.9 * (点到平面距离 / 点到lidar原点距离)
  1612. if (s > 0.9)
  1613. {
  1614. point_selected_surf[i] = true;
  1615. normvec->points[i].x = pabcd(0);
  1616. normvec->points[i].y = pabcd(1);
  1617. normvec->points[i].z = pabcd(2);
  1618. normvec->points[i].intensity = pd2; //以intensity记录点到面残差
  1619. res_last[i] = abs(pd2); // 残差,距离
  1620. }
  1621. }
  1622. }
  1623. effct_feat_num = 0; //有效匹配点数
  1624. for (int i = 0; i < feats_down_size; i++)
  1625. {
  1626. if (point_selected_surf[i])
  1627. {
  1628. laserCloudOri->points[effct_feat_num] = feats_down_body->points[i]; // body系 平面特征点
  1629. corr_normvect->points[effct_feat_num] = normvec->points[i]; // world系 平面参数
  1630. total_residual += res_last[i]; // 残差和
  1631. effct_feat_num++;
  1632. }
  1633. }
  1634. if (effct_feat_num < 1)
  1635. {
  1636. ekfom_data.valid = false;
  1637. ROS_WARN("No Effective Points! \n");
  1638. return;
  1639. }
  1640. res_mean_last = total_residual / effct_feat_num; // 残差均值 (距离)
  1641. match_time += omp_get_wtime() - match_start;
  1642. double solve_start_ = omp_get_wtime();
  1643. /*** Computation of Measuremnt Jacobian matrix H and measurents vector ***/
  1644. ekfom_data.h_x = MatrixXd::Zero(effct_feat_num, 12); //定义H维度
  1645. ekfom_data.h.resize(effct_feat_num); //有效方程个数
  1646. for (int i = 0; i < effct_feat_num; i++)
  1647. {
  1648. const PointType &laser_p = laserCloudOri->points[i]; // lidar系 平面特征点
  1649. V3D point_this_be(laser_p.x, laser_p.y, laser_p.z);
  1650. M3D point_be_crossmat;
  1651. point_be_crossmat << SKEW_SYM_MATRX(point_this_be);
  1652. V3D point_this = s.offset_R_L_I * point_this_be + s.offset_T_L_I; // 当前状态imu系下 点坐标
  1653. M3D point_crossmat;
  1654. point_crossmat << SKEW_SYM_MATRX(point_this); // 当前状态imu系下 点坐标反对称矩阵
  1655. /*** get the normal vector of closest surface/corner ***/
  1656. const PointType &norm_p = corr_normvect->points[i];
  1657. V3D norm_vec(norm_p.x, norm_p.y, norm_p.z); //对应局部法相量, world系下
  1658. /*** calculate the Measuremnt Jacobian matrix H ***/
  1659. V3D C(s.rot.conjugate() * norm_vec); // 将对应局部法相量旋转到imu系下 corr_normal_I
  1660. V3D A(point_crossmat * C); //残差对角度求导系数 P(IMU)^ [R(imu <-- w) * normal_w]
  1661. //添加数据到矩阵
  1662. if (extrinsic_est_en)
  1663. {
  1664. // B = lidar_p^ R(L <-- I) * corr_normal_I
  1665. // B = lidar_p^ R(L <-- I) * R(I <-- W) * normal_W
  1666. V3D B(point_be_crossmat * s.offset_R_L_I.conjugate() * C); // s.rot.conjugate()*norm_vec);
  1667. ekfom_data.h_x.block<1, 12>(i, 0) << norm_p.x, norm_p.y, norm_p.z, VEC_FROM_ARRAY(A), VEC_FROM_ARRAY(B), VEC_FROM_ARRAY(C);
  1668. }
  1669. else
  1670. {
  1671. ekfom_data.h_x.block<1, 12>(i, 0) << norm_p.x, norm_p.y, norm_p.z, VEC_FROM_ARRAY(A), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0;
  1672. }
  1673. /*** Measuremnt: distance to the closest surface/corner ***/
  1674. ekfom_data.h(i) = -norm_p.intensity;
  1675. }
  1676. solve_time += omp_get_wtime() - solve_start_;
  1677. }
  1678. void init_pose_cbk(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr &msg){
  1679. double x=msg->pose.pose.position.x;
  1680. double y=msg->pose.pose.position.y;
  1681. printf(" received init pose:%f %f \n",x,y);
  1682. state_ikfom state_inited;
  1683. Eigen::Vector3d pos(x,y, 0);
  1684. Eigen::Quaterniond q(msg->pose.pose.orientation.w,msg->pose.pose.orientation.x,
  1685. msg->pose.pose.orientation.y,msg->pose.pose.orientation.z);
  1686. // 更新状态量
  1687. state_inited.pos = pos;
  1688. state_inited.rot = q;
  1689. double epsi[23] = {0.005};
  1690. fill(epsi, epsi + 23, 0.005);
  1691. ///初始化,其中h_share_model定义了·平面搜索和残差计算
  1692. esekfom::esekf<state_ikfom, 12, input_ikfom> new_ikfom; // 状态,噪声维度,输入
  1693. new_ikfom.init_dyn_share(get_f, df_dx, df_dw, h_share_model, NUM_MAX_ITERATIONS, epsi);
  1694. new_ikfom.change_x(state_inited);
  1695. ikf_lock_.lock();
  1696. ikfom_=new_ikfom;
  1697. state_point=state_inited;
  1698. last_state_point=state_inited;
  1699. p_imu->Reset();
  1700. flg_first_scan=true;
  1701. ikf_lock_.unlock();
  1702. }
  1703. int main(int argc, char **argv) {
  1704. // allocateMemory();
  1705. for (int i = 0; i < 6; ++i) {
  1706. transformTobeMapped[i] = 0;
  1707. }
  1708. ros::init(argc, argv, "laserMapping");
  1709. ros::NodeHandle nh;
  1710. nh.param<bool>("publish/path_en", path_en, true);
  1711. nh.param<bool>("publish/scan_publish_en", scan_pub_en, true);
  1712. nh.param<bool>("publish/dense_publish_en", dense_pub_en, true);
  1713. nh.param<bool>("publish/scan_bodyframe_pub_en", scan_body_pub_en, true);
  1714. nh.param<int>("max_iteration", NUM_MAX_ITERATIONS, 10);
  1715. nh.param<string>("map_file_path", map_file_path, "");
  1716. nh.param<string>("common/lid_topic", lid_topic, "/livox/lidar");
  1717. nh.param<string>("common/imu_topic", imu_topic, "/livox/imu");
  1718. nh.param<bool>("common/time_sync_en", time_sync_en, false);
  1719. nh.param<double>("filter_size_corner", filter_size_corner_min, 0.5);
  1720. nh.param<double>("filter_size_surf", filter_size_surf_min, 0.5);
  1721. nh.param<double>("filter_size_map", filter_size_map_min, 0.5);
  1722. nh.param<double>("cube_side_length", cube_len, 200);
  1723. nh.param<float>("mapping/det_range", DET_RANGE, 300.f);
  1724. nh.param<double>("mapping/fov_degree", fov_deg, 180);
  1725. nh.param<double>("mapping/gyr_cov", gyr_cov, 0.1);
  1726. nh.param<double>("mapping/acc_cov", acc_cov, 0.1);
  1727. nh.param<double>("mapping/b_gyr_cov", b_gyr_cov, 0.0001);
  1728. nh.param<double>("mapping/b_acc_cov", b_acc_cov, 0.0001);
  1729. nh.param<double>("preprocess/blind", p_pre->blind, 0.01);
  1730. nh.param<int>("preprocess/lidar_type", p_pre->lidar_type, AVIA);
  1731. nh.param<int>("preprocess/scan_line", p_pre->N_SCANS, 16);
  1732. nh.param<int>("preprocess/scan_rate", p_pre->SCAN_RATE, 10);
  1733. nh.param<int>("point_filter_num", p_pre->point_filter_num, 1);
  1734. nh.param<bool>("feature_extract_enable", p_pre->feature_enabled, false);
  1735. nh.param<bool>("runtime_pos_log_enable", runtime_pos_log, 0);
  1736. nh.param<bool>("mapping/extrinsic_est_en", extrinsic_est_en, true);
  1737. nh.param<bool>("pcd_save/pcd_save_en", pcd_save_en, false);
  1738. nh.param<int>("pcd_save/interval", pcd_save_interval, -1);
  1739. nh.param<vector<double>>("mapping/extrinsic_T", extrinT, vector<double>());
  1740. nh.param<vector<double>>("mapping/extrinsic_R", extrinR, vector<double>());
  1741. cout << "p_pre->lidar_type " << p_pre->lidar_type << endl;
  1742. nh.param<float>("odometrySurfLeafSize", odometrySurfLeafSize, 0.2);
  1743. nh.param<float>("mappingCornerLeafSize", mappingCornerLeafSize, 0.2);
  1744. nh.param<float>("mappingSurfLeafSize", mappingSurfLeafSize, 0.2);
  1745. nh.param<float>("z_tollerance", z_tollerance, FLT_MAX);
  1746. nh.param<float>("rotation_tollerance", rotation_tollerance, FLT_MAX);
  1747. nh.param<int>("numberOfCores", numberOfCores, 2);
  1748. nh.param<double>("mappingProcessInterval", mappingProcessInterval, 0.15);
  1749. // save keyframes
  1750. nh.param<float>("surroundingkeyframeAddingDistThreshold", surroundingkeyframeAddingDistThreshold, 20.0);
  1751. nh.param<float>("surroundingkeyframeAddingAngleThreshold", surroundingkeyframeAddingAngleThreshold, 0.2);
  1752. nh.param<float>("surroundingKeyframeDensity", surroundingKeyframeDensity, 1.0);
  1753. nh.param<float>("surroundingKeyframeSearchRadius", surroundingKeyframeSearchRadius, 50.0);
  1754. // loop clousre
  1755. nh.param<bool>("loopClosureEnableFlag", loopClosureEnableFlag, false);
  1756. nh.param<float>("loopClosureFrequency", loopClosureFrequency, 1.0);
  1757. nh.param<int>("surroundingKeyframeSize", surroundingKeyframeSize, 50);
  1758. nh.param<float>("historyKeyframeSearchRadius", historyKeyframeSearchRadius, 10.0);
  1759. nh.param<float>("historyKeyframeSearchTimeDiff", historyKeyframeSearchTimeDiff, 30.0);
  1760. nh.param<int>("historyKeyframeSearchNum", historyKeyframeSearchNum, 25);
  1761. nh.param<float>("historyKeyframeFitnessScore", historyKeyframeFitnessScore, 0.3);
  1762. // Visualization
  1763. nh.param<float>("globalMapVisualizationSearchRadius", globalMapVisualizationSearchRadius, 1e3);
  1764. nh.param<float>("globalMapVisualizationPoseDensity", globalMapVisualizationPoseDensity, 10.0);
  1765. nh.param<float>("globalMapVisualizationLeafSize", globalMapVisualizationLeafSize, 1.0);
  1766. // visual ikdtree map
  1767. nh.param<bool>("locate_mode", locate_mode, false);
  1768. vector<double> init_pos(3, 0.0);
  1769. vector<double> init_rpy(3, 0.0);
  1770. nh.param<vector<double>>("locate/init_pos", init_pos, vector<double>());
  1771. nh.param<vector<double>>("locate/init_rpy", init_rpy, vector<double>());
  1772. nh.param<bool>("visulize_IkdtreeMap", visulize_IkdtreeMap, false);
  1773. nh.param<bool>("ndtLoopMatch", ndt_loop, false);
  1774. // reconstruct ikdtree
  1775. nh.param<bool>("recontructKdTree", recontructKdTree, false);
  1776. // savMap
  1777. nh.param<bool>("savePCD", savePCD, false);
  1778. nh.param<std::string>("savePCDDirectory", savePCDDirectory, "/Downloads/LOAM/");
  1779. downSizeFilterCorner.setLeafSize(mappingCornerLeafSize, mappingCornerLeafSize, mappingCornerLeafSize);
  1780. // downSizeFilterSurf.setLeafSize(mappingSurfLeafSize, mappingSurfLeafSize, mappingSurfLeafSize);
  1781. downSizeFilterICP.setLeafSize(mappingSurfLeafSize, mappingSurfLeafSize, mappingSurfLeafSize);
  1782. downSizeFilterSurroundingKeyPoses.setLeafSize(surroundingKeyframeDensity, surroundingKeyframeDensity,
  1783. surroundingKeyframeDensity); // for surrounding key poses of scan-to-map optimization
  1784. /*
  1785. * SC回环参数
  1786. */
  1787. scManager.setSCdistThres(scDistThres);
  1788. scManager.setMaximumRadius(scMaximumRadius);
  1789. // ISAM2参数
  1790. gtsam::ISAM2Params parameters;
  1791. parameters.relinearizeThreshold = 0.01;
  1792. parameters.relinearizeSkip = 1;
  1793. isam = new gtsam::ISAM2(parameters);
  1794. path.header.stamp = ros::Time::now();
  1795. path.header.frame_id = "camera_init";
  1796. /*** variables definition ***/
  1797. int effect_feat_num = 0, frame_num = 0;
  1798. double deltaT, deltaR, aver_time_consu = 0, aver_time_icp = 0, aver_time_match = 0, aver_time_incre = 0, aver_time_solve = 0, aver_time_const_H_time = 0;
  1799. bool flg_EKF_converged, EKF_stop_flg = 0;
  1800. FOV_DEG = (fov_deg + 10.0) > 179.9 ? 179.9 : (fov_deg + 10.0);
  1801. HALF_FOV_COS = cos((FOV_DEG) * 0.5 * PI_M / 180.0);
  1802. _featsArray.reset(new PointCloudXYZI());
  1803. memset(point_selected_surf, true, sizeof(point_selected_surf));
  1804. memset(res_last, -1000.0f, sizeof(res_last));
  1805. downSizeFilterSurf.setLeafSize(filter_size_surf_min, filter_size_surf_min, filter_size_surf_min);
  1806. downSizeFilterMap.setLeafSize(filter_size_map_min, filter_size_map_min, filter_size_map_min);
  1807. memset(point_selected_surf, true, sizeof(point_selected_surf)); //重复?
  1808. memset(res_last, -1000.0f, sizeof(res_last));
  1809. //设置imu和lidar外参和imu参数等
  1810. Lidar_T_wrt_IMU << VEC_FROM_ARRAY(extrinT);
  1811. Lidar_R_wrt_IMU << MAT_FROM_ARRAY(extrinR);
  1812. p_imu->set_extrinsic(Lidar_T_wrt_IMU, Lidar_R_wrt_IMU);
  1813. p_imu->set_gyr_cov(V3D(gyr_cov, gyr_cov, gyr_cov));
  1814. p_imu->set_acc_cov(V3D(acc_cov, acc_cov, acc_cov)); // 加速度协方差
  1815. p_imu->set_gyr_bias_cov(V3D(b_gyr_cov, b_gyr_cov, b_gyr_cov));
  1816. p_imu->set_acc_bias_cov(V3D(b_acc_cov, b_acc_cov, b_acc_cov));
  1817. double epsi[23] = {0.001};
  1818. fill(epsi, epsi + 23, 0.001);
  1819. ///初始化,其中h_share_model定义了·平面搜索和残差计算
  1820. ikfom_.init_dyn_share(get_f, df_dx, df_dw, h_share_model, NUM_MAX_ITERATIONS, epsi);
  1821. /*** debug record ***/
  1822. FILE *fp;
  1823. string pos_log_dir = root_dir + "/Log/pos_log.txt";
  1824. fp = fopen(pos_log_dir.c_str(), "w");
  1825. ofstream fout_pre, fout_out, fout_dbg;
  1826. fout_pre.open(DEBUG_FILE_DIR("mat_pre.txt"), ios::out);
  1827. fout_out.open(DEBUG_FILE_DIR("mat_out.txt"), ios::out);
  1828. fout_dbg.open(DEBUG_FILE_DIR("dbg.txt"), ios::out);
  1829. if (fout_pre && fout_out)
  1830. cout << "~~~~" << ROOT_DIR << " file opened" << endl;
  1831. else
  1832. cout << "~~~~" << ROOT_DIR << " doesn't exist" << endl;
  1833. /*** ROS subscribe initialization ***/
  1834. ros::Subscriber sub_pcl =
  1835. p_pre->lidar_type == AVIA ? nh.subscribe(lid_topic, 200000, livox_pcl_cbk) : nh.subscribe(lid_topic, 200000,
  1836. standard_pcl_cbk);
  1837. ros::Subscriber sub_imu = nh.subscribe(imu_topic, 200000, imu_cbk);
  1838. ros::Publisher pubLaserCloudFull = nh.advertise<sensor_msgs::PointCloud2>("/cloud_registered",
  1839. 100000); // world系下稠密点云
  1840. ros::Publisher pubLaserCloudFull_body = nh.advertise<sensor_msgs::PointCloud2>("/cloud_registered_body",
  1841. 100000); // body系下稠密点云
  1842. ros::Publisher pubLaserCloudEffect = nh.advertise<sensor_msgs::PointCloud2>("/cloud_effected",
  1843. 100000); // no used
  1844. ros::Publisher pubLaserCloudMap = nh.advertise<sensor_msgs::PointCloud2>("/Laser_map",
  1845. 100000); // no used
  1846. ros::Publisher pubOdomAftMapped = nh.advertise<nav_msgs::Odometry>("/Odometry", 100000);
  1847. ros::Publisher pubPath = nh.advertise<nav_msgs::Path>("/path", 1e00000);
  1848. ros::Publisher pubPathUpdate = nh.advertise<nav_msgs::Path>("fast_lio_sam/path_update",
  1849. 100000); // isam更新后的path
  1850. pubLaserCloudSurround = nh.advertise<sensor_msgs::PointCloud2>("fast_lio_sam/mapping/keyframe_submap",
  1851. 1); // 发布局部关键帧map的特征点云
  1852. pubOptimizedGlobalMap = nh.advertise<sensor_msgs::PointCloud2>("fast_lio_sam/mapping/map_global_optimized",
  1853. 1); // 发布局部关键帧map的特征点云
  1854. // loop clousre
  1855. // 发布闭环匹配关键帧局部map
  1856. pubHistoryKeyFrames = nh.advertise<sensor_msgs::PointCloud2>("fast_lio_sam/mapping/icp_loop_closure_history_cloud",
  1857. 1);
  1858. // 发布当前关键帧经过闭环优化后的位姿变换之后的特征点云
  1859. pubIcpKeyFrames = nh.advertise<sensor_msgs::PointCloud2>("fast_lio_sam/mapping/icp_loop_closure_corrected_cloud", 1);
  1860. // 发布闭环边,rviz中表现为闭环帧之间的连线
  1861. pubLoopConstraintEdge = nh.advertise<visualization_msgs::MarkerArray>(
  1862. "/fast_lio_sam/mapping/loop_closure_constraints", 1);
  1863. ros::Subscriber sub_init_pose;
  1864. // 回环检测线程
  1865. std::thread *loopthread = nullptr;
  1866. if (locate_mode == false) {
  1867. ros::ServiceServer srvSaveMap;
  1868. ros::ServiceServer srvSavePose;
  1869. // saveMap 发布地图保存服务
  1870. srvSaveMap = nh.advertiseService("/save_map", &saveMapService);
  1871. // savePose 发布轨迹保存服务
  1872. srvSavePose = nh.advertiseService("/save_pose", &savePoseService);
  1873. loopthread = new std::thread(&loopClosureThread);
  1874. } else {
  1875. PointCloudXYZI map_cloud;
  1876. std::string map_file = std::getenv("HOME") + savePCDDirectory + "/filterGlobalMap.pcd";
  1877. if (pcl::io::loadPCDFile<PointType>(map_file, map_cloud) == -1) //* 读入PCD格式的文件,如果文件不存在,返回-1
  1878. {
  1879. PCL_ERROR ("Couldn't read file test_pcd.pcd \n"); //文件不存在时,返回错误,终止程序。
  1880. return (-1);
  1881. }
  1882. printf(" Read map point num:%d\n", map_cloud.size());
  1883. ikdtree.Build(map_cloud.points);
  1884. sub_init_pose = nh.subscribe("/initialpose", 1000, init_pose_cbk);
  1885. state_ikfom state_inited;
  1886. Eigen::Vector3d pos(init_pos[0], init_pos[1], init_pos[2]);
  1887. Eigen::Quaterniond q = EulerToQuat(init_rpy[0], init_rpy[1], init_rpy[2]);
  1888. // 更新状态量
  1889. state_inited.pos = pos;
  1890. state_inited.rot = q;
  1891. ikfom_.change_x(state_inited);
  1892. last_state_point = state_inited;
  1893. usleep(1000 * 1000);
  1894. PointVector().swap(ikdtree.PCL_Storage);
  1895. ikdtree.flatten(ikdtree.Root_Node, ikdtree.PCL_Storage, NOT_RECORD);
  1896. featsFromMap->clear();
  1897. featsFromMap->points = ikdtree.PCL_Storage;
  1898. publish_map(pubLaserCloudMap);
  1899. }
  1900. signal(SIGINT, SigHandle);
  1901. ros::Rate rate(5000);
  1902. bool status = ros::ok();
  1903. while (status) {
  1904. if (flg_exit)
  1905. break;
  1906. ros::spinOnce();
  1907. /// 在Measure内,储存当前lidar数据及lidar扫描时间内对应的imu数据序列
  1908. if (sync_packages(Measures)) {
  1909. ikf_lock_.lock();
  1910. //第一帧lidar数据
  1911. if (flg_first_scan) {
  1912. first_lidar_time = Measures.lidar_beg_time; //记录第一帧绝对时间
  1913. p_imu->first_lidar_time = first_lidar_time; //记录第一帧绝对时间
  1914. flg_first_scan = false;
  1915. ikf_lock_.unlock();
  1916. continue;
  1917. }
  1918. double t0, t1, t2, t3, t4, t5, match_start, solve_start, svd_time;
  1919. match_time = 0;
  1920. kdtree_search_time = 0.0;
  1921. solve_time = 0;
  1922. solve_const_H_time = 0;
  1923. svd_time = 0;
  1924. t0 = omp_get_wtime();
  1925. //根据imu数据序列和lidar数据,向前传播纠正点云的畸变, 此前已经完成间隔采样或特征提取
  1926. // feats_undistort 为畸变纠正之后的点云,lidar系
  1927. p_imu->Process(Measures, ikfom_, feats_undistort);
  1928. state_point = ikfom_.get_x(); // 前向传播后body的状态预测值
  1929. pos_lid = state_point.pos + state_point.rot * state_point.offset_T_L_I; // global系 lidar位置
  1930. if (feats_undistort->empty() || (feats_undistort == NULL)) {
  1931. ROS_WARN("No point, skip this scan!\n");
  1932. ikf_lock_.unlock();
  1933. continue;
  1934. }
  1935. // 检查当前lidar数据时间,与最早lidar数据时间是否足够,两帧间隔0.1s
  1936. flg_EKF_inited = (Measures.lidar_beg_time - first_lidar_time) < INIT_TIME ? false : true;
  1937. /*** Segment the map in lidar FOV ***/
  1938. if (locate_mode == false)
  1939. lasermap_fov_segment(); // 根据lidar在W系下的位置,重新确定局部地图的包围盒角点,移除远端的点
  1940. else {
  1941. /*** downsample the feature points in a scan ***/
  1942. double ndt_beg = omp_get_wtime();
  1943. downSizeFilterSurf.setInputCloud(feats_undistort);
  1944. downSizeFilterSurf.filter(*feats_down_body);
  1945. pcl::NormalDistributionsTransform<PointType, PointType> ndt;
  1946. ndt.setTransformationEpsilon(0.001);
  1947. // Setting maximum step size for More-Thuente line search.
  1948. ndt.setStepSize(0.1);
  1949. //Setting Resolution of NDT grid structure (VoxelGridCovariance).
  1950. ndt.setResolution(0.5);
  1951. // Setting max number of registration iterations.
  1952. ndt.setMaximumIterations(50);
  1953. // Setting point cloud to be aligned.
  1954. ndt.setInputSource(feats_down_body);
  1955. // Setting point cloud to be aligned to.
  1956. ndt.setInputTarget(featsFromMap);
  1957. pcl::PointCloud<PointType>::Ptr unused_result(new pcl::PointCloud<PointType>());
  1958. ndt.align(*unused_result, Eigen::Matrix4f::Identity());
  1959. double ndt_end = omp_get_wtime();
  1960. printf(" ndt time :%f , size(%d %d)\n", ndt_end - ndt_beg, feats_down_body->size(), featsFromMap->size());
  1961. }
  1962. t1 = omp_get_wtime();
  1963. feats_down_size = feats_down_body->points.size(); //当前帧降采样后点数
  1964. /*** initialize the map kdtree ***/
  1965. if (locate_mode == false) {
  1966. if (ikdtree.Root_Node == nullptr) {
  1967. if (feats_down_size > 5) {
  1968. ikdtree.set_downsample_param(filter_size_map_min);
  1969. feats_down_world->resize(feats_down_size);
  1970. for (int i = 0; i < feats_down_size; i++) {
  1971. pointBodyToWorld(&(feats_down_body->points[i]), &(feats_down_world->points[i])); // point转到world系下
  1972. }
  1973. // world系下对当前帧降采样后的点云,初始化lkd-tree
  1974. ikdtree.Build(feats_down_world->points);
  1975. }
  1976. ikf_lock_.unlock();
  1977. continue;
  1978. }
  1979. }
  1980. int featsFromMapNum = ikdtree.validnum();
  1981. kdtree_size_st = ikdtree.size();
  1982. // cout<<"[ mapping ]: In num: "<<feats_undistort->points.size()<<" downsamp "<<feats_down_size<<" Map num: "<<featsFromMapNum<<"effect num:"<<effct_feat_num<<endl;
  1983. /*** ICP and iterated Kalman filter update ***/
  1984. if (feats_down_size < 5) {
  1985. ROS_WARN("No point, skip this scan!\n");
  1986. ikf_lock_.unlock();
  1987. continue;
  1988. }
  1989. normvec->resize(feats_down_size);
  1990. feats_down_world->resize(feats_down_size);
  1991. // lidar --> imu
  1992. V3D ext_euler = SO3ToEuler(state_point.offset_R_L_I);
  1993. fout_pre << setw(20) << Measures.lidar_beg_time - first_lidar_time << " " << euler_cur.transpose() << " "
  1994. << state_point.pos.transpose() << " " << ext_euler.transpose() << " "
  1995. << state_point.offset_T_L_I.transpose() << " " << state_point.vel.transpose()
  1996. << " " << state_point.bg.transpose() << " " << state_point.ba.transpose() << " " << state_point.grav
  1997. << endl;
  1998. pointSearchInd_surf.resize(feats_down_size);
  1999. Nearest_Points.resize(feats_down_size);
  2000. int rematch_num = 0;
  2001. bool nearest_search_en = true; //
  2002. t2 = omp_get_wtime();
  2003. /*** iterated state estimation ***/
  2004. double t_update_start = omp_get_wtime();
  2005. double solve_H_time = 0;
  2006. bool matched = ikfom_.update_iterated_dyn_share_modified(LASER_POINT_COV, solve_H_time);//预测、更新
  2007. state_point = ikfom_.get_x();
  2008. last_state_point = state_point;
  2009. /*if (matched==false && locate_mode==true) {
  2010. printf(" ikfom state update failed ...continue...\n");
  2011. ikfom_.change_x(last_state_point);//恢复到前一帧的数据
  2012. state_point = last_state_point;
  2013. }*/
  2014. euler_cur = SO3ToEuler(state_point.rot);
  2015. pos_lid = state_point.pos + state_point.rot * state_point.offset_T_L_I; // world系下lidar坐标
  2016. geoQuat.x = state_point.rot.coeffs()[0]; // world系下当前imu的姿态四元数
  2017. geoQuat.y = state_point.rot.coeffs()[1];
  2018. geoQuat.z = state_point.rot.coeffs()[2];
  2019. geoQuat.w = state_point.rot.coeffs()[3];
  2020. double t_update_end = omp_get_wtime();
  2021. getCurPose(state_point); // 更新transformTobeMapped
  2022. // Publish points
  2023. if (scan_pub_en) {
  2024. publish_frame_world(pubLaserCloudFull); // 发布world系下的点云
  2025. }
  2026. /******* Publish odometry *******/
  2027. publish_odometry(pubOdomAftMapped);
  2028. ikf_lock_.unlock();
  2029. /*if (matched == false && locate_mode==true) {
  2030. continue;
  2031. }*/
  2032. /*back end*/
  2033. // 1.计算当前帧与前一帧位姿变换,如果变化太小,不设为关键帧,反之设为关键帧
  2034. // 2.添加激光里程计因子、GPS因子、闭环因子
  2035. // 3.执行因子图优化
  2036. // 4.得到当前帧优化后的位姿,位姿协方差
  2037. // 5.添加cloudKeyPoses3D,cloudKeyPoses6D,更新transformTobeMapped,添加当前关键帧的角点、平面点集合
  2038. if (locate_mode == false) {
  2039. saveKeyFramesAndFactor();
  2040. // 更新因子图中所有变量节点的位姿,也就是所有历史关键帧的位姿,更新里程计轨迹, 重构ikdtree
  2041. correctPoses();
  2042. }
  2043. double tloop = omp_get_wtime();
  2044. /*printf(" match time:%fs,kdtree points:%d save KeyFrame loop time:%fs\n", t_update_end - t_update_start,
  2045. ikdtree.validnum(), tloop - t_update_end);*/
  2046. /*** add the feature points to map kdtree ***/
  2047. if (locate_mode == false) {
  2048. t3 = omp_get_wtime();
  2049. map_incremental();
  2050. t5 = omp_get_wtime();
  2051. }
  2052. if (path_en) {
  2053. publish_path(pubPath);
  2054. publish_path_update(pubPathUpdate); // 发布经过isam2优化后的路径
  2055. static int jjj = 0;
  2056. jjj++;
  2057. if (jjj % 100 == 0) {
  2058. publishGlobalMap(); // 发布局部点云特征地图
  2059. }
  2060. }
  2061. if (scan_pub_en && scan_body_pub_en)
  2062. publish_frame_body(pubLaserCloudFull_body); // 发布imu系下的点云
  2063. /*
  2064. //Debug variables
  2065. if (runtime_pos_log)
  2066. {
  2067. frame_num++;
  2068. kdtree_size_end = ikdtree.size();
  2069. aver_time_consu = aver_time_consu * (frame_num - 1) / frame_num + (t5 - t0) / frame_num;
  2070. aver_time_icp = aver_time_icp * (frame_num - 1) / frame_num + (t_update_end - t_update_start) / frame_num;
  2071. aver_time_match = aver_time_match * (frame_num - 1) / frame_num + (match_time) / frame_num;
  2072. aver_time_incre = aver_time_incre * (frame_num - 1) / frame_num + (kdtree_incremental_time) / frame_num;
  2073. aver_time_solve = aver_time_solve * (frame_num - 1) / frame_num + (solve_time + solve_H_time) / frame_num;
  2074. aver_time_const_H_time = aver_time_const_H_time * (frame_num - 1) / frame_num + solve_time / frame_num;
  2075. T1[time_log_counter] = Measures.lidar_beg_time;
  2076. s_plot[time_log_counter] = t5 - t0;
  2077. s_plot2[time_log_counter] = feats_undistort->points.size();
  2078. s_plot3[time_log_counter] = kdtree_incremental_time;
  2079. s_plot4[time_log_counter] = kdtree_search_time;
  2080. s_plot5[time_log_counter] = kdtree_delete_counter;
  2081. s_plot6[time_log_counter] = kdtree_delete_time;
  2082. s_plot7[time_log_counter] = kdtree_size_st;
  2083. s_plot8[time_log_counter] = kdtree_size_end;
  2084. s_plot9[time_log_counter] = aver_time_consu;
  2085. s_plot10[time_log_counter] = add_point_size;
  2086. time_log_counter++;
  2087. printf("[ mapping ]: time: IMU + Map + Input Downsample: %0.6f ave match: %0.6f ave solve: %0.6f ave ICP: %0.6f map incre: %0.6f ave total: %0.6f icp: %0.6f construct H: %0.6f \n", t1 - t0, aver_time_match, aver_time_solve, t3 - t1, t5 - t3, aver_time_consu, aver_time_icp, aver_time_const_H_time);
  2088. ext_euler = SO3ToEuler(state_point.offset_R_L_I);
  2089. fout_out << setw(20) << Measures.lidar_beg_time - first_lidar_time << " " << euler_cur.transpose() << " " << state_point.pos.transpose() << " " << ext_euler.transpose() << " " << state_point.offset_T_L_I.transpose() << " " << state_point.vel.transpose()
  2090. << " " << state_point.bg.transpose() << " " << state_point.ba.transpose() << " " << state_point.grav << " " << feats_undistort->points.size() << endl;
  2091. dump_lio_state_to_log(fp);
  2092. }*/
  2093. }
  2094. status = ros::ok();
  2095. rate.sleep();
  2096. }
  2097. /**************** save map ****************/
  2098. /* 1. make sure you have enough memories
  2099. /* 2. pcd save will largely influence the real-time performences **/
  2100. if (pcl_wait_save->size() > 0 && pcd_save_en) {
  2101. string file_name = string("scans.pcd");
  2102. string all_points_dir(string(string(ROOT_DIR) + "PCD/") + file_name);
  2103. pcl::PCDWriter pcd_writer;
  2104. cout << "current scan saved to /PCD/" << file_name << endl;
  2105. pcd_writer.writeBinary(all_points_dir, *pcl_wait_save);
  2106. }
  2107. fout_out.close();
  2108. fout_pre.close();
  2109. if (runtime_pos_log) {
  2110. vector<double> t, s_vec, s_vec2, s_vec3, s_vec4, s_vec5, s_vec6, s_vec7;
  2111. FILE *fp2;
  2112. string log_dir = root_dir + "/Log/fast_lio_time_log.csv";
  2113. fp2 = fopen(log_dir.c_str(), "w");
  2114. fprintf(fp2,
  2115. "time_stamp, total time, scan point size, incremental time, search time, delete size, delete time, tree size st, tree size end, add point size, preprocess time\n");
  2116. for (int i = 0; i < time_log_counter; i++) {
  2117. fprintf(fp2, "%0.8f,%0.8f,%d,%0.8f,%0.8f,%d,%0.8f,%d,%d,%d,%0.8f\n", T1[i], s_plot[i], int(s_plot2[i]),
  2118. s_plot3[i], s_plot4[i], int(s_plot5[i]), s_plot6[i], int(s_plot7[i]), int(s_plot8[i]), int(s_plot10[i]),
  2119. s_plot11[i]);
  2120. t.push_back(T1[i]);
  2121. s_vec.push_back(s_plot9[i]);
  2122. s_vec2.push_back(s_plot3[i] + s_plot6[i]);
  2123. s_vec3.push_back(s_plot4[i]);
  2124. s_vec5.push_back(s_plot[i]);
  2125. }
  2126. fclose(fp2);
  2127. }
  2128. startFlag = false;
  2129. if (locate_mode == false) {
  2130. loopthread->join(); // 分离线程
  2131. }
  2132. delete loopthread;
  2133. return 0;
  2134. }