├── README.md
├── config
├── mapfusion.yaml
└── mapfusion_k.yaml
├── doc
├── pipeline.jpeg
└── pipeline.pdf
├── mapFusion.cpp
└── third_parties
├── fast_max-clique_finder
├── COPYRIGHT
├── README.md
└── src
│ ├── findClique.cpp
│ ├── findClique.h
│ ├── findCliqueHeu.cpp
│ ├── graphIO.cpp
│ ├── graphIO.h
│ └── utils.cpp
├── nanoflann.hpp
├── nanoflann_pcl.h
└── scanContext
├── .Rhistory
├── scanContext.cpp
└── scanContext.h
/README.md:
--------------------------------------------------------------------------------
1 | # DiSCo-SLAM
2 |
3 | **DiSCo-SLAM is a novel framework for distributed, multi-robot SLAM intended for use with 3D LiDAR observations. The framework is the first to use the lightweight Scan Context descriptor for multi-robot SLAM, permitting a data-efficient exchange of LiDAR observations among robots. Additionally, our framework includes a two-stage global and local optimization framework for distributed multi- robot SLAM which provides stable localization results that are resilient to the unknown initial conditions that typify the search for inter-robot loop closures.**
4 |
5 |

6 |
7 | ##
8 | - Here we provide a distributed multi-robot SLAM example for 3 robots, intended for use with the two datasets provided below.
9 | - The local SLAM used in our project is [LIO-SAM](https://github.com/TixiaoShan/LIO-SAM), please download [the modified version of LIO-SAM](https://github.com/yeweihuang/LIO-SAM.git), and add the DiSCo-SLAM folder into ```LIO-SAM\src```.
10 | ```
11 | ├──LIO-SAM
12 | ├── ...
13 | ├── src
14 | │ ├── ...
15 | │ └── DiSCo-SLAM # Folder for multi-robot SLAM
16 | └── ...
17 | ```
18 | ```
19 | git clone https://github.com/yeweihuang/LIO-SAM.git
20 | cd LIO-SAM/src
21 | git clone git@github.com:RobustFieldAutonomyLab/DiSCo-SLAM.git
22 | ```
23 | - Code from [Scan Context](https://github.com/irapkaist/scancontext) is used for feature description.
24 | - We use code from [PCM](https://github.com/lajoiepy/robust_distributed_mapper/tree/d609f59658956e1b7fe06c786ed7d07776ecb426/cpp/src/pairwise_consistency_maximization)
25 | for outlier detection.
26 |
27 |
28 | ## Dependencies
29 | - Same dependencies as [LIO-SAM](https://github.com/TixiaoShan/LIO-SAM):
30 | - [ROS Melodic](http://wiki.ros.org/melodic#Installation)
31 | - [gtsam 4.0.2](https://github.com/borglab/gtsam/releases) (Georgia Tech Smoothing and Mapping library)
32 | - Dependency for [Scan Context](https://github.com/irapkaist/scancontext):
33 | - [libnabo 1.0.7](https://github.com/ethz-asl/libnabo/releases)
34 |
35 |
36 | ## Datasets
37 |
38 | - [The Park Dataset](https://huggingface.co/datasets/yeweihuang/DiSCo-SLAM-Example-Dataset)
39 | - [KITTI 08 Dataset](https://huggingface.co/datasets/yeweihuang/DiSCo-SLAM-Example-Dataset)
40 |
41 | To run the KITTI08 dataset, change line 9 & 10 in launch/run.launch from
42 | ```
43 |
44 |
45 | ```
46 | to
47 | ```
48 |
49 |
50 | ```
51 |
52 | ## How to use
53 |
54 | ```
55 | cd ~/catkin_ws/src
56 | git clone
57 | cd ..
58 | catkin_make
59 | ```
60 |
61 | ```
62 | roslaunch lio_sam run.launch
63 | rosbag play your_bag_name.bag
64 | ```
65 |
66 |
--------------------------------------------------------------------------------
/config/mapfusion.yaml:
--------------------------------------------------------------------------------
1 | mapfusion:
2 | scancontext:
3 | knn_feature_dim: 16
4 | max_range: 30
5 | num_sector: 60
6 | num_nearest_matches: 50
7 | num_match_candidates: 1
8 | #vertial_axis: #should be x/y/z
9 |
10 | interRobot:
11 | #topic
12 | sc_topic: "context"
13 | sc_frame: "base_link"
14 | local_topic: "lio_sam/mapping/cloud_info"
15 |
16 | #thres
17 | loop_threshold: 0.2
18 | pcm_threshold: 20
19 | icp_threshold: 3
20 | loop_frame_threshold: 10 #frames
21 | pcm_start_threshold: 5 # 5 for best result, 2 for work in harsh environment
22 |
23 | robot_initial: jackal0
24 | use_position_search: false #use position search may introduce more global loop closure, but spend more time for candidate checking
25 |
--------------------------------------------------------------------------------
/config/mapfusion_k.yaml:
--------------------------------------------------------------------------------
1 | mapfusion:
2 | scancontext:
3 | knn_feature_dim: 64
4 | max_range: 30
5 | num_sector: 60
6 | num_nearest_matches: 50
7 | num_match_candidates: 1
8 | #vertial_axis: #should be x/y/z
9 |
10 | interRobot:
11 | #topic
12 | sc_topic: "context"
13 | sc_frame: "base_link"
14 | local_topic: "lio_sam/mapping/cloud_info"
15 |
16 | #thres
17 | loop_threshold: 0.2
18 | pcm_threshold: 100
19 | icp_threshold: 5
20 | pcm_start_threshold: 5 # 5 for best result, 2 for work in harsh environment
21 |
22 | robot_initial: jackal0
23 | use_position_search: false #use position search may introduce more global loop closure, but spend more time for candidate checking
24 |
--------------------------------------------------------------------------------
/doc/pipeline.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobustFieldAutonomyLab/DiSCo-SLAM/772755702840a029f4fd0eaedd11bab82ac1e3fd/doc/pipeline.jpeg
--------------------------------------------------------------------------------
/doc/pipeline.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobustFieldAutonomyLab/DiSCo-SLAM/772755702840a029f4fd0eaedd11bab82ac1e3fd/doc/pipeline.pdf
--------------------------------------------------------------------------------
/mapFusion.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by yewei on 8/31/20.
3 | //
4 |
5 | //msg
6 | #include "lio_sam/cloud_info.h"
7 | #include "lio_sam/context_info.h"
8 |
9 | //third party
10 | #include "scanContext/scanContext.h"
11 | #include "fast_max-clique_finder/src/findClique.h"
12 |
13 | #include "nabo/nabo.h"
14 |
15 | //ros
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include
22 | #include
23 |
24 | //gtsam
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | //expression graph
32 | #include
33 | #include
34 | #include
35 |
36 | //factor graph
37 | #include
38 | #include
39 | #include
40 |
41 | //pcl
42 | #include
43 | #include
44 | #include
45 |
46 | #include
47 | #include
48 | #include
49 |
50 | inline gtsam::Pose3_ transformTo(const gtsam::Pose3_& x, const gtsam::Pose3_& p) {
51 | return gtsam::Pose3_(x, >sam::Pose3::transform_pose_to, p);
52 | }
53 |
54 | sensor_msgs::PointCloud2 publishCloud(ros::Publisher *thisPub, pcl::PointCloud::Ptr thisCloud, ros::Time thisStamp, std::string thisFrame)
55 | {
56 | sensor_msgs::PointCloud2 tempCloud;
57 | pcl::toROSMsg(*thisCloud, tempCloud);
58 | tempCloud.header.stamp = thisStamp;//
59 | tempCloud.header.frame_id = thisFrame;
60 | if (thisPub->getNumSubscribers() != 0)
61 | thisPub->publish(tempCloud);
62 | return tempCloud;
63 | }
64 |
65 | class MapFusion{
66 |
67 | private:
68 | ros::NodeHandle nh;//global node handler for publishing everthing
69 |
70 | ros::Subscriber _sub_laser_cloud_info;
71 | ros::Subscriber _sub_scan_context_info;
72 | ros::Subscriber _sub_odom_trans;
73 | ros::Subscriber _sub_loop_info_global;
74 |
75 | ros::Subscriber _sub_communication_signal;
76 | ros::Subscriber _sub_signal_1;
77 | ros::Subscriber _sub_signal_2;
78 |
79 |
80 | std::string _signal_id_1;
81 | std::string _signal_id_2;
82 |
83 | ros::Publisher _pub_context_info;
84 | ros::Publisher _pub_loop_info;
85 | ros::Publisher _pub_cloud;
86 |
87 | ros::Publisher _pub_trans_odom2map;
88 | ros::Publisher _pub_trans_odom2odom;
89 |
90 | ros::Publisher _pub_loop_info_global;
91 |
92 | //parameters
93 | std::string _robot_id;
94 | std::string _robot_this;//robot id which the thread is now processing
95 | std::string _sc_topic;
96 | std::string _sc_frame;
97 |
98 | std::string _local_topic;
99 |
100 | bool _communication_signal;
101 | bool _signal_1;
102 | bool _signal_2;
103 | bool _use_position_search;
104 |
105 | int _num_bin;
106 | int _robot_id_th;
107 | int _robot_this_th;
108 |
109 | int _max_range;
110 | int _num_sectors;
111 | int _knn_feature_dim;
112 |
113 | int _num_nearest_matches;
114 | int _num_match_candidates;
115 |
116 | int _pcm_start_threshold;
117 |
118 | float _loop_thres;
119 | float _pcm_thres;
120 | float _icp_thres;
121 | int _loop_frame_thres;
122 |
123 | std::mutex mtx_publish_1;
124 | std::mutex mtx_publish_2;
125 | std::mutex mtx;
126 |
127 | std::string _robot_initial;
128 | std::string _pcm_matrix_folder;
129 |
130 | lio_sam::cloud_info _cloud_info;
131 | // lio_sam::context_info _context_info;
132 | std::vector _context_list_to_publish_1;
133 | std::vector _context_list_to_publish_2;
134 |
135 | pcl::KdTreeFLANN::Ptr _kdtree_pose_to_publish;
136 | pcl::PointCloud::Ptr _cloud_pose_to_publish;
137 |
138 | pcl::KdTreeFLANN::Ptr _kdtree_pose_to_search;
139 | pcl::PointCloud::Ptr _cloud_pose_to_search_this;//3D
140 | pcl::PointCloud::Ptr _cloud_pose_to_search_other;//3D
141 |
142 | pcl::KdTreeFLANN::Ptr _kdtree_loop_to_search;
143 | pcl::PointCloud::Ptr _cloud_loop_to_search;
144 |
145 | pcl::VoxelGrid _downsize_filter_icp;
146 |
147 | std::pair _initial_loop;
148 | int _id_bin_last;
149 |
150 | lio_sam::context_info _loop_info;
151 | std_msgs::Header _cloud_header;
152 |
153 | pcl::PointCloud::Ptr _laser_cloud_sum;
154 | pcl::PointCloud::Ptr _laser_cloud_feature;
155 | pcl::PointCloud::Ptr _laser_cloud_corner;
156 | pcl::PointCloud::Ptr _laser_cloud_surface;
157 |
158 | //global variables for sc
159 | Nabo::NNSearchF* _nns = NULL; //KDtree
160 | Eigen::MatrixXf _target_matrix;
161 | ScanContext *_scan_context_factory;
162 |
163 | std::vector _robot_received_list;
164 | std::vector> _idx_nearest_list;
165 | std::unordered_map _bin_with_id;
166 |
167 | //for pcm & graph
168 | //first: source pose; second: source pose in target; third: icp fitness score;
169 | std::unordered_map< int, std::vector< std::tuple > > _pose_queue;
170 | //first: target, second: source, third: relative transform;
171 | std::unordered_map< int, std::vector< std::tuple > > _loop_queue;
172 |
173 | std::unordered_map< std::string, std::vector > _global_odom_trans;
174 |
175 | //first: robot pair id, second: effective loop id in _loop_queue
176 | std::unordered_map< int, std::vector > _loop_accept_queue;
177 |
178 | std::unordered_map< int, std::vector > _global_map_trans;
179 | std::unordered_map< int, PointTypePose> _global_map_trans_optimized;
180 |
181 | int number_print;
182 |
183 | PointTypePose _trans_to_publish;
184 |
185 | std::vector> _processing_time_list;
186 | public:
187 |
188 | MapFusion(){
189 | ParamLoader();
190 | initialization();
191 |
192 | _sub_communication_signal = nh.subscribe(_robot_id + "/lio_sam/signal",
193 | 100, &MapFusion::communicationSignalHandler, this, ros::TransportHints().tcpNoDelay());
194 |
195 | _sub_signal_1 = nh.subscribe(_signal_id_1 + "/lio_sam/signal",
196 | 100, &MapFusion::signalHandler1, this, ros::TransportHints().tcpNoDelay());
197 | _sub_signal_2 = nh.subscribe(_signal_id_2 + "/lio_sam/signal",
198 | 100, &MapFusion::signalHandler2, this, ros::TransportHints().tcpNoDelay());
199 |
200 | _sub_laser_cloud_info = nh.subscribe(_robot_id + "/" + _local_topic,
201 | 1, &MapFusion::laserCloudInfoHandler, this, ros::TransportHints().tcpNoDelay());
202 |
203 | _sub_loop_info_global = nh.subscribe(_sc_topic + "/loop_info_global",
204 | 100, &MapFusion::globalLoopInfoHandler, this, ros::TransportHints().tcpNoDelay());
205 |
206 |
207 | if(_robot_id != _robot_initial){
208 | _sub_scan_context_info = nh.subscribe(_sc_topic + "/context_info",
209 | 20, &MapFusion::scanContextInfoHandler, this, ros::TransportHints().tcpNoDelay());//number of buffer may differs for different robot numbers
210 | _sub_odom_trans = nh.subscribe(_sc_topic + "/trans_odom",
211 | 20, &MapFusion::OdomTransHandler, this, ros::TransportHints().tcpNoDelay());
212 |
213 | }
214 |
215 |
216 |
217 | _pub_context_info = nh.advertise (_sc_topic + "/context_info", 1);
218 | _pub_loop_info = nh.advertise (_robot_id + "/" + _sc_topic + "/loop_info", 1);
219 | _pub_cloud = nh.advertise (_robot_id + "/" + _sc_topic + "/cloud", 1);
220 | _pub_trans_odom2map = nh.advertise ( _robot_id + "/" + _sc_topic + "/trans_map", 1);
221 | _pub_trans_odom2odom = nh.advertise ( _sc_topic + "/trans_odom", 1);
222 | _pub_loop_info_global = nh.advertise(_sc_topic + "/loop_info_global", 1);
223 |
224 | }
225 |
226 | void publishContextInfoThread(){
227 | int signal_id_th_1 = robotID2Number(_signal_id_1);
228 | int signal_id_th_2 = robotID2Number(_signal_id_2);
229 | while (ros::ok())
230 | {
231 | if (_communication_signal && _signal_1 && _robot_id_th < signal_id_th_1){
232 | if (_context_list_to_publish_1.empty())
233 | continue;
234 | //publish scan context info to other robots
235 | mtx_publish_1.lock();
236 | ScanContextBin bin = _context_list_to_publish_1.back();
237 | _context_list_to_publish_1.pop_back();
238 | mtx_publish_1.unlock();
239 | publishContextInfo(bin, _signal_id_1);
240 | }
241 | if (_communication_signal && _signal_2 && _robot_id_th < signal_id_th_2){
242 | if (_context_list_to_publish_2.empty())
243 | continue;
244 | //publish scan context info to other robots
245 | mtx_publish_2.lock();
246 | ScanContextBin bin = _context_list_to_publish_2.back();
247 | _context_list_to_publish_2.pop_back();
248 | mtx_publish_2.unlock();
249 | publishContextInfo(bin, _signal_id_2);
250 |
251 | }
252 | }
253 | }
254 |
255 | private:
256 | void ParamLoader(){
257 | ros::NodeHandle n("~");//local node handler
258 | n.param("robot_id", _robot_id, "jackal0");
259 | n.param("id_1", _signal_id_1, "jackal1");
260 | n.param("id_2", _signal_id_2, "jackal2");
261 | n.param("no", number_print, 100);
262 | n.param("pcm_matrix_folder", _pcm_matrix_folder, "aaa");
263 |
264 | nh.getParam("/mapfusion/scancontext/knn_feature_dim", _knn_feature_dim);
265 | nh.getParam("/mapfusion/scancontext/max_range", _max_range);
266 | nh.getParam("/mapfusion/scancontext/num_sector", _num_sectors);
267 | nh.getParam("/mapfusion/scancontext/num_nearest_matches", _num_nearest_matches);
268 | nh.getParam("/mapfusion/scancontext/num_match_candidates", _num_match_candidates);
269 |
270 | nh.getParam("/mapfusion/interRobot/loop_threshold", _loop_thres);
271 | nh.getParam("/mapfusion/interRobot/pcm_threshold",_pcm_thres);
272 | nh.getParam("/mapfusion/interRobot/icp_threshold",_icp_thres);
273 | nh.getParam("/mapfusion/interRobot/robot_initial",_robot_initial);
274 | nh.getParam("/mapfusion/interRobot/loop_frame_threshold", _loop_frame_thres);
275 |
276 | nh.getParam("/mapfusion/interRobot/sc_topic", _sc_topic);
277 | nh.getParam("/mapfusion/interRobot/sc_frame", _sc_frame);
278 | nh.getParam("/mapfusion/interRobot/local_topic", _local_topic);
279 | nh.getParam("/mapfusion/interRobot/pcm_start_threshold", _pcm_start_threshold);
280 | nh.getParam("/mapfusion/interRobot/use_position_search", _use_position_search);
281 |
282 | }
283 |
284 | void initialization(){
285 | _laser_cloud_sum.reset(new pcl::PointCloud());
286 | _laser_cloud_feature.reset(new pcl::PointCloud());
287 | _laser_cloud_corner.reset(new pcl::PointCloud());
288 | _laser_cloud_surface.reset(new pcl::PointCloud());
289 |
290 | _scan_context_factory = new ScanContext(_max_range, _knn_feature_dim, _num_sectors);
291 |
292 | _kdtree_pose_to_publish.reset(new pcl::KdTreeFLANN());
293 | _cloud_pose_to_publish.reset(new pcl::PointCloud());
294 |
295 | _kdtree_pose_to_search.reset(new pcl::KdTreeFLANN());
296 | _cloud_pose_to_search_this.reset(new pcl::PointCloud());
297 | _cloud_pose_to_search_other.reset(new pcl::PointCloud());
298 |
299 | _kdtree_loop_to_search.reset(new pcl::KdTreeFLANN());
300 | _cloud_loop_to_search.reset(new pcl::PointCloud());
301 |
302 | _downsize_filter_icp.setLeafSize(0.4, 0.4, 0.4);
303 |
304 | _initial_loop.first = -1;
305 |
306 | _robot_id_th = robotID2Number(_robot_id);
307 |
308 | _trans_to_publish.intensity = 0;
309 |
310 | _num_bin = 0;//
311 |
312 | _communication_signal = true;
313 | _signal_1 = true;
314 | _signal_2 = true;
315 |
316 |
317 | }
318 |
319 | int robotID2Number(std::string robo){
320 | return robo.back() - '0';
321 | }
322 |
323 | void laserCloudInfoHandler(const lio_sam::cloud_infoConstPtr& msgIn)
324 | {
325 | _laser_cloud_sum->clear();
326 | _laser_cloud_feature->clear();
327 | _laser_cloud_corner->clear();
328 | _laser_cloud_surface->clear();
329 |
330 | //load newest data
331 | _cloud_info = *msgIn; // new cloud info
332 | _cloud_header = msgIn->header; // new cloud header
333 | pcl::fromROSMsg(msgIn->cloud_deskewed, *_laser_cloud_sum);
334 | pcl::fromROSMsg(msgIn->cloud_corner, *_laser_cloud_corner);
335 | pcl::fromROSMsg(msgIn->cloud_surface, *_laser_cloud_surface);
336 | *_laser_cloud_feature += *_laser_cloud_corner;
337 | *_laser_cloud_feature += *_laser_cloud_surface;
338 |
339 | //do scancontext
340 | ScanContextBin bin = _scan_context_factory->ptcloud2bin(_laser_cloud_sum);
341 | bin.robotname = _robot_id;
342 | bin.time = _cloud_header.stamp.toSec();
343 | bin.pose.x = _cloud_info.initialGuessX;
344 | bin.pose.y = _cloud_info.initialGuessY;
345 | bin.pose.z = _cloud_info.initialGuessZ;
346 | bin.pose.roll = _cloud_info.initialGuessRoll;
347 | bin.pose.pitch = _cloud_info.initialGuessPitch;
348 | bin.pose.yaw = _cloud_info.initialGuessYaw;
349 | bin.pose.intensity = _cloud_info.imuAvailable;
350 |
351 | bin.cloud->clear();
352 | pcl::copyPointCloud(*_laser_cloud_feature, *bin.cloud);
353 |
354 | //push the bin info into the wait list
355 | mtx_publish_1.lock();
356 | _context_list_to_publish_1.push_back(bin);
357 | mtx_publish_1.unlock();
358 | mtx_publish_2.lock();
359 | _context_list_to_publish_2.push_back(bin);
360 | mtx_publish_2.unlock();
361 |
362 |
363 | publishContextInfo(bin, _robot_id);
364 |
365 | }
366 |
367 | void communicationSignalHandler(const std_msgs::Bool::ConstPtr& msg){
368 | _communication_signal = msg->data;
369 | }
370 |
371 | void signalHandler1(const std_msgs::Bool::ConstPtr& msg){
372 | _signal_1 = msg->data;
373 | }
374 |
375 | void signalHandler2(const std_msgs::Bool::ConstPtr& msg){
376 | _signal_2 = msg->data;
377 | }
378 |
379 | void publishContextInfo( ScanContextBin bin , std::string robot_to){
380 | lio_sam::context_info context_info;
381 | context_info.robotID = _robot_id;
382 |
383 | context_info.numRing = _knn_feature_dim;
384 | context_info.numSector = _num_sectors;
385 |
386 | context_info.scanContextBin.assign(_knn_feature_dim * _num_sectors,0);
387 | context_info.ringKey.assign(_knn_feature_dim, 0);
388 | context_info.header = _cloud_header;
389 |
390 | int cnt = 0;
391 | for (int i = 0; i < _knn_feature_dim; i++){
392 | context_info.ringKey[i] = bin.ringkey(i);
393 | for (int j = 0; j < _num_sectors; j++){
394 | context_info.scanContextBin[cnt] = bin.bin(i,j);
395 | ++cnt;
396 | }
397 | }
398 |
399 | context_info.robotIDReceive = robot_to;
400 | context_info.poseX = bin.pose.x;
401 | context_info.poseY = bin.pose.y;
402 | context_info.poseZ = bin.pose.z;
403 | context_info.poseRoll = bin.pose.roll;
404 | context_info.posePitch = bin.pose.pitch;
405 | context_info.poseYaw = bin.pose.yaw;
406 | context_info.poseIntensity = bin.pose.intensity;
407 |
408 | context_info.scanCloud = publishCloud(&_pub_cloud, bin.cloud, ros::Time(bin.time), _robot_id + "/" + _sc_frame);
409 | mtx.lock();
410 | _pub_context_info.publish(context_info);
411 | mtx.unlock();
412 | // context_info.scanContextBin.
413 | }
414 |
415 | void OdomTransHandler(const nav_msgs::Odometry::ConstPtr& odomMsg){
416 | std::string robot_publish = odomMsg->header.frame_id;
417 | if( robot_publish == _robot_id)
418 | return;//skip info publish by the node itself
419 | std::string robot_child = odomMsg->child_frame_id;
420 | std::string index = robot_child + robot_publish;
421 | PointTypePose pose;
422 | pose.x = odomMsg->pose.pose.position.x;
423 | pose.y = odomMsg->pose.pose.position.y;
424 | pose.z = odomMsg->pose.pose.position.z;
425 | tf::Quaternion orientation;
426 | tf::quaternionMsgToTF(odomMsg->pose.pose.orientation, orientation);
427 | double roll, pitch, yaw;
428 | tf::Matrix3x3(orientation).getRPY(roll, pitch, yaw);
429 | pose.roll = roll; pose.pitch = pitch; pose.yaw = yaw;
430 |
431 | //intensity also serves as a index
432 | pose.intensity = robotID2Number(robot_child);
433 |
434 | auto ite = _global_odom_trans.find(index);
435 | if(ite == _global_odom_trans.end())//receive a new trans
436 | {
437 | std::vector tmp_pose_list;
438 | tmp_pose_list.push_back(pose);
439 | _global_odom_trans.emplace(std::make_pair(index, tmp_pose_list));
440 | }
441 | else//else add a new association;
442 | _global_odom_trans[index].push_back(pose);
443 |
444 | gtsamFactorGraph();
445 | sendMapOutputMessage();
446 |
447 | }
448 |
449 | void globalLoopInfoHandler(const lio_sam::context_infoConstPtr& msgIn){
450 | // return;
451 | if (msgIn->robotID != _robot_id)
452 | return;
453 | _pub_loop_info.publish(*msgIn);
454 | sendMapOutputMessage();
455 |
456 | }
457 |
458 | void gtsamFactorGraph(){
459 | if (_global_map_trans.size() == 0 && _global_odom_trans.size() == 0)
460 | return;
461 | gtsam::Vector Vector6(6);
462 | gtsam::NonlinearFactorGraph graph;
463 | gtsam::Values initialEstimate;
464 |
465 | std::vector id_received = _robot_received_list;
466 | std::vector> trans_list;
467 |
468 | if (_global_map_trans.size() != 0)
469 | id_received.push_back(_robot_id_th);
470 |
471 | //set initial
472 | Vector6 << 1e-8, 1e-8, 1e-8, 1e-8, 1e-8, 1e-8;
473 | auto odometryNoise0 = gtsam::noiseModel::Diagonal::Variances(Vector6);
474 | graph.add( gtsam::PriorFactor(gtsam::PriorFactor(0, gtsam::Pose3( gtsam::Rot3::RzRyRx(0, 0, 0), gtsam::Point3(0, 0, 0) ) , odometryNoise0) ) );
475 | initialEstimate.insert(0, gtsam::Pose3( gtsam::Rot3::RzRyRx(0, 0, 0), gtsam::Point3(0, 0, 0)));
476 |
477 | bool ill_posed = true;
478 | //add local constraints
479 | for(auto ite : _global_map_trans){
480 | int id_0 = std::min(ite.first, _robot_id_th);
481 | int id_1 = std::max(ite.first, _robot_id_th);
482 |
483 | for(auto ite_measure : ite.second){
484 | PointTypePose pclpose = ite_measure;
485 | gtsam::Pose3 measurement = gtsam::Pose3 (gtsam::Rot3::RzRyRx(pclpose.roll, pclpose.pitch, pclpose.yaw),
486 | gtsam::Point3(pclpose.x, pclpose.y, pclpose.z) );
487 | Vector6 << 1, 1, 1, 1, 1, 1;
488 | auto odometryNoise = gtsam::noiseModel::Diagonal::Variances(Vector6);
489 | graph.add( gtsam::BetweenFactor(id_0, id_1, measurement, odometryNoise) );
490 | }
491 |
492 | PointTypePose pclpose = ite.second[ite.second.size() - 1];
493 | gtsam::Pose3 measurement_latest = gtsam::Pose3 (gtsam::Rot3::RzRyRx(pclpose.roll, pclpose.pitch, pclpose.yaw),
494 | gtsam::Point3(pclpose.x, pclpose.y, pclpose.z));
495 | if(id_0 == 0){
496 | initialEstimate.insert(id_1, measurement_latest);
497 | ill_posed = false;
498 | }
499 | else
500 | trans_list.emplace_back(std::make_tuple(id_0, id_1, measurement_latest));
501 | }
502 |
503 | for(auto ite: _global_odom_trans){
504 | int id_publish = robotID2Number(ite.first);
505 | int id_child = ite.second[0].intensity;
506 | int id_0 = std::min(id_publish, id_child);
507 | int id_1 = std::max(id_publish, id_child);
508 |
509 | for(auto ite_measure: ite.second){
510 |
511 | PointTypePose pclpose = ite_measure;
512 | gtsam::Pose3 measurement = gtsam::Pose3 (gtsam::Rot3::RzRyRx(pclpose.roll, pclpose.pitch, pclpose.yaw),
513 | gtsam::Point3(pclpose.x, pclpose.y, pclpose.z));
514 | Vector6 << 1, 1, 1, 1, 1, 1;
515 | auto odometryNoise = gtsam::noiseModel::Diagonal::Variances(Vector6);
516 | graph.add( gtsam::BetweenFactor(id_0, id_1, measurement, odometryNoise) );
517 | }
518 |
519 | PointTypePose pclpose = ite.second[ite.second.size() - 1];
520 | gtsam::Pose3 measurement_latest = gtsam::Pose3 (gtsam::Rot3::RzRyRx(pclpose.roll, pclpose.pitch, pclpose.yaw),
521 | gtsam::Point3(pclpose.x, pclpose.y, pclpose.z));
522 |
523 | if(id_0 == 0){
524 | initialEstimate.insert(id_1, measurement_latest);
525 | ill_posed = false;
526 | }
527 | else
528 | trans_list.emplace_back(std::make_tuple(id_0, id_1, measurement_latest));
529 |
530 | if(find(id_received.begin(), id_received.end(), id_0) == id_received.end())
531 | id_received.push_back(id_0);
532 |
533 | if(find(id_received.begin(), id_received.end(), id_1) == id_received.end())
534 | id_received.push_back(id_1);
535 |
536 | }
537 |
538 | if (find(id_received.begin(), id_received.end(), _robot_id_th) == id_received.end()){
539 | return;
540 | }
541 | if (ill_posed)
542 | return;
543 |
544 | bool terminate_signal = false;
545 | while (!terminate_signal){
546 | if (id_received.size() == 0)
547 | break;
548 | terminate_signal = true;
549 | for(auto id = id_received.begin(); id != id_received.end();)
550 | {
551 | int id_this = *id;
552 | if(initialEstimate.exists(id_this)){
553 | id = id_received.erase(id);
554 | continue;
555 | }
556 | else
557 | ++id;
558 |
559 | auto it = std::find_if(trans_list.begin(), trans_list.end(),
560 | [id_this](auto& e)
561 | {return std::get<0>(e) == id_this || std::get<1>(e) == id_this;});
562 |
563 | if(it == trans_list.end())
564 | continue;
565 |
566 | int id_t = get<0>(*it) + get<1>(*it) - id_this;
567 |
568 | if(!initialEstimate.exists(id_t))
569 | continue;
570 | gtsam::Pose3 pose_t = initialEstimate.at(id_t);
571 | if ( id_this == get<1>(*it)){
572 | gtsam::Pose3 pose_f = pose_t * get<2>(*it);
573 | initialEstimate.insert(id_this, pose_f);
574 | terminate_signal = false;
575 |
576 | }
577 | if ( id_this == get<0>(*it)){
578 | gtsam::Pose3 pose_f = pose_t * get<2>(*it).inverse();
579 | initialEstimate.insert(id_this, pose_f);
580 | terminate_signal = false;
581 | }
582 | }
583 | }
584 |
585 | for (auto it : id_received){
586 | initialEstimate.insert(it, gtsam::Pose3( gtsam::Rot3::RzRyRx(0, 0, 0), gtsam::Point3(0, 0, 0) ));
587 | }
588 |
589 | id_received.clear();
590 | trans_list.clear();
591 |
592 | gtsam::Values result = gtsam::LevenbergMarquardtOptimizer(graph, initialEstimate).optimize();
593 |
594 | initialEstimate.clear();
595 | graph.resize(0);
596 |
597 | gtsam::Pose3 est = result.at(_robot_id_th);
598 |
599 | _trans_to_publish.x = est.translation().x();
600 | _trans_to_publish.y = est.translation().y();
601 | _trans_to_publish.z = est.translation().z();
602 | _trans_to_publish.roll = est.rotation().roll();
603 | _trans_to_publish.pitch = est.rotation().pitch();
604 | _trans_to_publish.yaw = est.rotation().yaw();
605 | if (_trans_to_publish.x != 0 || _trans_to_publish.y != 0 || _trans_to_publish.z != 0)
606 | _trans_to_publish.intensity = 1;
607 |
608 | if (_trans_to_publish.intensity == 1){
609 | int robot_id_initial = robotID2Number(_robot_initial);
610 | if (_global_map_trans_optimized.find(robot_id_initial) == _global_map_trans_optimized.end()){
611 | _global_map_trans_optimized.emplace(std::make_pair(robot_id_initial, _trans_to_publish));
612 | }
613 |
614 |
615 | else{
616 | _global_map_trans[robot_id_initial].push_back(_trans_to_publish);
617 | _global_map_trans_optimized[robot_id_initial] = _trans_to_publish;
618 | }
619 | }
620 | }
621 |
622 | void scanContextInfoHandler(const lio_sam::context_infoConstPtr& msgIn){
623 | lio_sam::context_info context_info_input = *msgIn;
624 | //load the data received
625 | if (!_communication_signal)
626 | return;
627 | if (msgIn->robotIDReceive != _robot_id)
628 | return;
629 |
630 | ScanContextBin bin;
631 | bin.robotname = msgIn->robotID;
632 | bin.time = msgIn->header.stamp.toSec();
633 |
634 | bin.pose.x = msgIn->poseX;
635 | bin.pose.y = msgIn->poseY;
636 | bin.pose.z = msgIn->poseZ;
637 | bin.pose.roll = msgIn->poseRoll;
638 | bin.pose.pitch = msgIn->posePitch;
639 | bin.pose.yaw = msgIn->poseYaw;
640 | bin.pose.intensity = msgIn->poseIntensity;
641 |
642 | bin.cloud.reset(new pcl::PointCloud());
643 | pcl::fromROSMsg(msgIn->scanCloud, *bin.cloud);
644 |
645 | bin.bin = Eigen::MatrixXf::Zero(_knn_feature_dim, _num_sectors);
646 | bin.ringkey = Eigen::VectorXf::Zero(_knn_feature_dim);
647 | int cnt = 0;
648 | for (int i=0; inumRing; i++){
649 | bin.ringkey(i) = msgIn->ringKey[i];
650 | for (int j=0; jnumSector; j++){
651 | bin.bin(i,j) = msgIn->scanContextBin[cnt];
652 | ++cnt;
653 | }
654 | }
655 |
656 | run(bin);
657 |
658 | }
659 |
660 | void run(ScanContextBin bin){
661 | //build
662 |
663 | buildKDTree(bin);
664 | KNNSearch(bin);
665 |
666 | if(_idx_nearest_list.empty() && _use_position_search)
667 | distanceSearch(bin);
668 |
669 | if(!getInitialGuesses(bin)){
670 | return;
671 | }
672 |
673 | if(!incrementalPCM()){
674 | return;
675 | }
676 |
677 | //perform optimization
678 | gtsamExpressionGraph();
679 |
680 | //send out transform
681 | sendOdomOutputMessage();
682 | }
683 |
684 | void distanceSearch(ScanContextBin bin){
685 | int id_this = robotID2Number(bin.robotname);
686 | if(bin.robotname != _robot_id){
687 | if(_global_map_trans_optimized.find(id_this) == _global_map_trans_optimized.end() )
688 | return;
689 | PointType pt_query;
690 | std::vector idx_list;
691 | std::vector dist_list;
692 | PointTypePose pose_this = _global_map_trans_optimized[id_this];
693 | gtsam::Pose3 T_pose_this = gtsam::Pose3(gtsam::Rot3::RzRyRx(pose_this.roll, pose_this.pitch, pose_this.yaw),
694 | gtsam::Point3(pose_this.x, pose_this.y, pose_this.z));
695 | auto T_query = gtsam::Pose3(gtsam::Rot3::RzRyRx(bin.pose.roll, bin.pose.pitch, bin.pose.yaw),
696 | gtsam::Point3(bin.pose.x, bin.pose.y, bin.pose.z));
697 | T_query = T_pose_this.inverse() * T_query;
698 |
699 | pt_query.x = T_query.translation().x(); pt_query.y = T_query.translation().y(); pt_query.z = T_query.translation().z();
700 |
701 | _kdtree_pose_to_search->setInputCloud(_cloud_pose_to_search_this);
702 | _kdtree_pose_to_search->radiusSearch(pt_query, 5, idx_list, dist_list, 0);
703 | if (!idx_list.empty()){
704 | int tmp_id = _cloud_pose_to_search_this->points[idx_list[0]].intensity;
705 | _idx_nearest_list.emplace_back(std::make_pair(tmp_id, 0));
706 | }
707 | }
708 | else{
709 | PointType pt_query;
710 | std::vector idx_list;
711 | std::vector dist_list;
712 | pcl::PointCloud::Ptr cloud_pose_to_search_other_copy(new pcl::PointCloud());
713 | for (unsigned int i = 0; i < _cloud_pose_to_search_other->size(); i++){
714 | PointType tmp = _cloud_pose_to_search_other->points[i];
715 | int id_this = robotID2Number(_bin_with_id[tmp.intensity].robotname);
716 | if(_global_map_trans_optimized.find(id_this) == _global_map_trans_optimized.end() )
717 | continue;
718 | PointTypePose pose_this = _global_map_trans_optimized[id_this];
719 | gtsam::Pose3 T_pose_this = gtsam::Pose3(gtsam::Rot3::RzRyRx(pose_this.roll, pose_this.pitch, pose_this.yaw),
720 | gtsam::Point3(pose_this.x, pose_this.y, pose_this.z));
721 | auto T_this = gtsam::Point3(tmp.x,tmp.y,tmp.z);
722 | T_this = T_pose_this.inverse() * T_this;
723 | tmp.x = T_this.x();
724 | tmp.y = T_this.y();
725 | tmp.z = T_this.z();
726 | cloud_pose_to_search_other_copy->push_back(tmp);
727 | }
728 | pt_query.x = bin.pose.x;
729 | pt_query.y = bin.pose.y;
730 | pt_query.z = bin.pose.z;
731 | if (cloud_pose_to_search_other_copy->empty())
732 | return;
733 |
734 | _kdtree_pose_to_search->setInputCloud(cloud_pose_to_search_other_copy);
735 | _kdtree_pose_to_search->radiusSearch(pt_query, 10, idx_list, dist_list, 0);
736 | if (!idx_list.empty()){
737 | for (unsigned int i = 0; i< cloud_pose_to_search_other_copy->size(); i++){
738 | int tmp_id = cloud_pose_to_search_other_copy->points[idx_list[i]].intensity;
739 | if(tmp_id == _num_bin)
740 | continue;
741 | _idx_nearest_list.emplace_back(std::make_pair(tmp_id, 0));
742 | break;
743 | }
744 | }
745 | cloud_pose_to_search_other_copy->clear();
746 |
747 | }//
748 |
749 |
750 | }
751 |
752 | void buildKDTree(ScanContextBin bin){
753 | _num_bin++;
754 | //store data received
755 | _bin_with_id.emplace( std::make_pair(_num_bin-1, bin) );
756 |
757 | PointType tmp_pose;
758 | tmp_pose.x = bin.pose.x; tmp_pose.y = bin.pose.y; tmp_pose.z = bin.pose.z;
759 | tmp_pose.intensity = _num_bin - 1;
760 |
761 | if (bin.robotname == _robot_id)
762 | _cloud_pose_to_search_this->push_back(tmp_pose);
763 | else
764 | _cloud_pose_to_search_other->push_back(tmp_pose);
765 |
766 | //add the latest ringkey
767 | _target_matrix.conservativeResize(_knn_feature_dim, _num_bin);
768 | _target_matrix.block(0, _num_bin-1, _knn_feature_dim, 1) =
769 | bin.ringkey.block(0, 0, _knn_feature_dim, 1);
770 | //add the target matrix to nns
771 | _nns = Nabo::NNSearchF::createKDTreeLinearHeap(_target_matrix);
772 | }
773 |
774 | void KNNSearch(ScanContextBin bin){
775 | if (_num_nearest_matches >= _num_bin){
776 | return;//if not enough candidates, return
777 | }
778 |
779 | int num_neighbors = _num_nearest_matches;
780 |
781 | //search n nearest neighbors
782 | Eigen::VectorXi indices(num_neighbors);
783 | Eigen::VectorXf dists2(num_neighbors);
784 |
785 | _nns->knn(bin.ringkey, indices, dists2, num_neighbors);
786 |
787 | int idx_candidate, rot_idx;
788 | float distance_to_query;
789 | //first: dist, second: idx in bin, third: rot_idx
790 | std::vector> idx_list;
791 | for (int i = 0; i < std::min( num_neighbors, int(indices.size()) ); ++i){
792 | //check if the searching work normally
793 | if ( indices.sum() == 0)
794 | continue;
795 |
796 | idx_candidate = indices[i];
797 | if ( idx_candidate >= _num_bin)
798 | continue;
799 |
800 | // if the candidate & source belong to same robot, skip
801 | if ( bin.robotname == _bin_with_id.at(idx_candidate).robotname)
802 | continue;
803 |
804 | // if the matching pair have nothing to do with the present robot, skip
805 | if ( bin.robotname != _robot_id && _bin_with_id.at(idx_candidate).robotname != _robot_id)
806 | continue;
807 |
808 | if( robotID2Number(bin.robotname) >= _robot_id_th && robotID2Number(_bin_with_id.at(idx_candidate).robotname) >= _robot_id_th)
809 | continue;
810 |
811 | //compute the dist with full scancontext info
812 | distance_to_query = distBtnScanContexts(bin.bin, _bin_with_id.at(idx_candidate).bin, rot_idx);
813 |
814 | if( distance_to_query > _loop_thres)
815 | continue;
816 |
817 | //add to idx list
818 | idx_list.emplace_back( std::make_tuple(distance_to_query, idx_candidate, rot_idx) );
819 | }
820 |
821 | _idx_nearest_list.clear();
822 |
823 | if (idx_list.size() == 0)
824 | return;
825 |
826 | //find nearest scan contexts
827 | std::sort(idx_list.begin(), idx_list.end());
828 | for (int i = 0; i < std::min( _num_match_candidates, int(idx_list.size()) ); i++){
829 | std::tie(distance_to_query, idx_candidate, rot_idx) = idx_list[i];
830 | _idx_nearest_list.emplace_back(std::make_pair(idx_candidate, rot_idx));
831 | }
832 | idx_list.clear();
833 | }
834 |
835 | bool getInitialGuesses(ScanContextBin bin){
836 | if(_idx_nearest_list.size() == 0){
837 | return false;
838 | }
839 | bool new_candidate_signal = false;
840 | for (auto it: _idx_nearest_list){
841 | new_candidate_signal = getInitialGuess(bin, it.first, it.second);
842 | }
843 | return new_candidate_signal;
844 | }
845 |
846 | bool getInitialGuess(ScanContextBin bin, int idx_nearest, int min_idx){
847 |
848 | int id0 = idx_nearest, id1 = _num_bin - 1;
849 |
850 | ScanContextBin bin_nearest;
851 | PointTypePose source_pose_initial, target_pose;
852 |
853 | float sc_pitch = (min_idx+1) * M_PI * 2 /_num_sectors;
854 | if (sc_pitch > M_PI)
855 | sc_pitch -= (M_PI * 2);
856 |
857 | int robot_id_this = robotID2Number(bin.robotname);
858 |
859 | auto robot_id_this_ite = std::find(_robot_received_list.begin(), _robot_received_list.end(), robot_id_this);
860 |
861 | //record all received robot id from other robots
862 | if (robot_id_this_ite == _robot_received_list.end() && robot_id_this != _robot_id_th)
863 | _robot_received_list.push_back(robot_id_this);
864 |
865 | // exchange if source has a prior robot id (the last character of the robot name is smaller) (first > second)
866 | if (robot_id_this < _robot_id_th){
867 | bin_nearest = bin;
868 | bin = _bin_with_id.at(idx_nearest);
869 |
870 | id0 = _num_bin - 1;
871 | id1 = idx_nearest;
872 |
873 | sc_pitch = -sc_pitch;
874 | }
875 | else
876 | bin_nearest = _bin_with_id.at(idx_nearest);
877 |
878 | _robot_this = bin_nearest.robotname;
879 | _robot_this_th = robotID2Number(_robot_this);
880 |
881 | //get initial guess from scancontext
882 | target_pose = bin_nearest.pose;
883 |
884 | //find the pose constrain
885 | if (_global_map_trans_optimized.find(_robot_this_th) != _global_map_trans_optimized.end()){
886 | PointTypePose trans_to_that = _global_map_trans_optimized[_robot_this_th];
887 | Eigen::Affine3f t_source2target = pcl::getTransformation(trans_to_that.x, trans_to_that.y, trans_to_that.z,
888 | trans_to_that.roll, trans_to_that.pitch, trans_to_that.yaw);
889 | Eigen::Affine3f t_source = pcl::getTransformation(bin.pose.x, bin.pose.y, bin.pose.z, bin.pose.roll, bin.pose.pitch, bin.pose.yaw);
890 | Eigen::Affine3f t_initial_source = t_source2target * t_source;
891 | pcl::getTranslationAndEulerAngles(t_initial_source, source_pose_initial.x, source_pose_initial.y, source_pose_initial.z,
892 | source_pose_initial.roll, source_pose_initial.pitch, source_pose_initial.yaw);
893 | //if too far away, return false
894 |
895 | }
896 | else if(abs(sc_pitch) < 0.3){
897 | source_pose_initial = target_pose;
898 | }
899 | else{
900 | Eigen::Affine3f sc_initial = pcl::getTransformation(0, 0, 0,
901 | 0, 0, sc_pitch);
902 | Eigen::Affine3f t_target = pcl::getTransformation(target_pose.x, target_pose.y, target_pose.z,
903 | target_pose.roll, target_pose.pitch, target_pose.yaw);
904 | Eigen::Affine3f t_initial_source = sc_initial * t_target;
905 | // pre-multiplying -> successive rotation about a fixed frame
906 |
907 | pcl::getTranslationAndEulerAngles(t_initial_source, source_pose_initial.x, source_pose_initial.y, source_pose_initial.z,
908 | source_pose_initial.roll, source_pose_initial.pitch, source_pose_initial.yaw);
909 | source_pose_initial.x = target_pose.x;
910 | source_pose_initial.y = target_pose.y;
911 | source_pose_initial.z = target_pose.z;
912 | }
913 |
914 | PointTypePose pose_source_lidar = icpRelativeMotion(transformPointCloud(bin.cloud, &source_pose_initial),
915 | transformPointCloud(bin_nearest.cloud, &target_pose), source_pose_initial);
916 |
917 | if (pose_source_lidar.intensity == -1 || pose_source_lidar.intensity > _icp_thres)
918 | return false;
919 |
920 | //1: jackal0, 2: jackal1
921 | gtsam::Pose3 pose_from =
922 | gtsam::Pose3(gtsam::Rot3::RzRyRx(bin.pose.roll, bin.pose.pitch, bin.pose.yaw),
923 | gtsam::Point3(bin.pose.x, bin.pose.y, bin.pose.z));
924 |
925 | gtsam::Pose3 pose_to =
926 | gtsam::Pose3(gtsam::Rot3::RzRyRx(pose_source_lidar.roll, pose_source_lidar.pitch, pose_source_lidar.yaw),
927 | gtsam::Point3(pose_source_lidar.x, pose_source_lidar.y, pose_source_lidar.z));
928 |
929 | gtsam::Pose3 pose_target =
930 | gtsam::Pose3(gtsam::Rot3::RzRyRx(target_pose.roll, target_pose.pitch, target_pose.yaw),
931 | gtsam::Point3(target_pose.x, target_pose.y, target_pose.z));
932 |
933 | auto ite = _pose_queue.find(_robot_this_th);
934 | if(ite == _pose_queue.end()){
935 | std::vector< std::tuple > new_pose_queue;
936 | std::vector< std::tuple > new_loop_queue;
937 | _pose_queue.emplace( std::make_pair(_robot_this_th, new_pose_queue) );
938 | _loop_queue.emplace( std::make_pair(_robot_this_th, new_loop_queue) );
939 | }
940 |
941 |
942 | _pose_queue[_robot_this_th].emplace_back(std::make_tuple(pose_from, pose_to, pose_source_lidar.intensity));
943 | _loop_queue[_robot_this_th].emplace_back(std::make_tuple(id0, id1, pose_to.between(pose_target)));
944 |
945 | return true;
946 | }
947 |
948 | float distBtnScanContexts(Eigen::MatrixXf bin1, Eigen::MatrixXf bin2, int & idx){
949 | Eigen::VectorXf sim_for_each_cols(_num_sectors);
950 | for (int i = 0; i<_num_sectors; i++) {
951 |
952 | //shift
953 | int one_step = 1;
954 | Eigen::MatrixXf bint = circShift(bin1, one_step);
955 | bin1 = bint;
956 |
957 | //compare
958 | float sum_of_cos_sim = 0;
959 | int num_col_engaged = 0;
960 |
961 | for (int j = 0; j < _num_sectors; j++) {
962 | Eigen::VectorXf col_j_1(_knn_feature_dim);
963 | Eigen::VectorXf col_j_2(_knn_feature_dim);
964 | col_j_1.block(0, 0, _knn_feature_dim, 1) = bin1.block(0, j, _knn_feature_dim, 1);
965 | col_j_2.block(0, 0, _knn_feature_dim, 1) = bin2.block(0, j, _knn_feature_dim, 1);
966 |
967 | if(col_j_1.isZero() || col_j_2.isZero())
968 | continue;
969 |
970 | //calc sim
971 | float cos_similarity = col_j_1.dot(col_j_2) / col_j_1.norm() / col_j_2.norm();
972 | sum_of_cos_sim += cos_similarity;
973 |
974 | num_col_engaged++;
975 | }
976 | //devided by num_col_engaged: So, even if there are many columns that are excluded from the calculation, we can get high scores if other columns are well fit.
977 | sim_for_each_cols(i) = sum_of_cos_sim / float(num_col_engaged);
978 |
979 | }
980 | Eigen::VectorXf::Index idx_max;
981 | float sim = sim_for_each_cols.maxCoeff(& idx_max);
982 | idx = idx_max;
983 | //get the corresponding angle of the maxcoeff
984 | float dist = 1-sim;
985 | return dist;
986 |
987 | }
988 |
989 | pcl::PointCloud::Ptr transformPointCloud(pcl::PointCloud::Ptr cloudIn, PointTypePose* transformIn)
990 | {
991 | pcl::PointCloud::Ptr cloudOut(new pcl::PointCloud());
992 |
993 | PointType *pointFrom;
994 |
995 | int cloudSize = cloudIn->size();
996 | cloudOut->resize(cloudSize);
997 |
998 | Eigen::Affine3f transCur = pcl::getTransformation(transformIn->x, transformIn->y, transformIn->z, transformIn->roll, transformIn->pitch, transformIn->yaw);
999 |
1000 | #pragma omp parallel for num_threads(numberOfCores)
1001 | for (int i = 0; i < cloudSize; ++i)
1002 | {
1003 | pointFrom = &cloudIn->points[i];
1004 | cloudOut->points[i].x = transCur(0,0) * pointFrom->x + transCur(0,1) * pointFrom->y + transCur(0,2) * pointFrom->z + transCur(0,3);
1005 | cloudOut->points[i].y = transCur(1,0) * pointFrom->x + transCur(1,1) * pointFrom->y + transCur(1,2) * pointFrom->z + transCur(1,3);
1006 | cloudOut->points[i].z = transCur(2,0) * pointFrom->x + transCur(2,1) * pointFrom->y + transCur(2,2) * pointFrom->z + transCur(2,3);
1007 | cloudOut->points[i].intensity = pointFrom->intensity;
1008 | }
1009 | return cloudOut;
1010 | }
1011 |
1012 | PointTypePose icpRelativeMotion(pcl::PointCloud::Ptr source,
1013 | pcl::PointCloud::Ptr target,
1014 | PointTypePose pose_source)
1015 | {
1016 | // ICP Settings
1017 | pcl::IterativeClosestPoint icp;
1018 | icp.setMaxCorrespondenceDistance(100);
1019 | icp.setMaximumIterations(100);
1020 | icp.setTransformationEpsilon(1e-6);
1021 | icp.setEuclideanFitnessEpsilon(1e-6);
1022 | icp.setRANSACIterations(0);
1023 |
1024 | pcl::PointCloud::Ptr cloud_temp(new pcl::PointCloud());
1025 | _downsize_filter_icp.setInputCloud(source);
1026 | _downsize_filter_icp.filter(*cloud_temp);
1027 | *source = *cloud_temp;
1028 |
1029 | _downsize_filter_icp.setInputCloud(target);
1030 | _downsize_filter_icp.filter(*cloud_temp);
1031 | *target = *cloud_temp;
1032 |
1033 | //Align clouds
1034 | icp.setInputSource(source);
1035 | icp.setInputTarget(target);
1036 | pcl::PointCloud::Ptr unused_result(
1037 | new pcl::PointCloud());
1038 | icp.align(*unused_result);
1039 | PointTypePose pose_from;
1040 |
1041 | if (icp.hasConverged() == false){
1042 | pose_from.intensity = -1;
1043 | return pose_from;
1044 | }
1045 |
1046 | float x, y, z, roll, pitch, yaw;
1047 | Eigen::Affine3f correctionLidarFrame;
1048 |
1049 | correctionLidarFrame = icp.getFinalTransformation(); // get transformation in camera frame
1050 |
1051 | pcl::getTranslationAndEulerAngles(correctionLidarFrame, x, y, z, roll, pitch,yaw);
1052 |
1053 | // if(std::min(_robot_id_th, _robot_this_th) == 1 && std::max(_robot_id_th, _robot_this_th) == 2){
1054 | // publishCloud(&_pub_target_cloud, target, _cloud_header.stamp, "/jackal1/odom");
1055 | //
1056 | // PointTypePose ptp;
1057 | // ptp.x = x; ptp.y = y; ptp.z = z; ptp.roll = roll; ptp.yaw = yaw; ptp.pitch = pitch;
1058 | // publishCloud(&_pub_match_cloud, transformPointCloud(source, &ptp), _cloud_header.stamp, "/jackal1/odom");
1059 | // }
1060 |
1061 |
1062 | // transform from world origin to wrong pose
1063 | Eigen::Affine3f tWrong = pcl::getTransformation(pose_source.x, pose_source.y, pose_source.z,
1064 | pose_source.roll, pose_source.pitch, pose_source.yaw);
1065 | // transform from world origin to corrected pose
1066 | Eigen::Affine3f tCorrect = correctionLidarFrame * tWrong;
1067 |
1068 | // pre-multiplying -> successive rotation about a fixed frame
1069 | pcl::getTranslationAndEulerAngles(tCorrect, x, y, z, roll, pitch, yaw);
1070 |
1071 | pose_from.x = x;
1072 | pose_from.y = y;
1073 | pose_from.z = z;
1074 |
1075 | pose_from.yaw = yaw;
1076 | pose_from.roll = roll;
1077 | pose_from.pitch = pitch;
1078 |
1079 | pose_from.intensity = icp.getFitnessScore();
1080 |
1081 | return pose_from;
1082 |
1083 | }
1084 |
1085 | bool incrementalPCM() {
1086 | if (_pose_queue[_robot_this_th].size() < _pcm_start_threshold)
1087 | return false;
1088 |
1089 | //perform pcm for all robot matches
1090 |
1091 | Eigen::MatrixXi consistency_matrix = computePCMMatrix(_loop_queue[_robot_this_th]);//, _pose_queue[_robot_this_th]);
1092 | std::string consistency_matrix_file = _pcm_matrix_folder + "/consistency_matrix" + _robot_id + ".clq.mtx";
1093 | printPCMGraph(consistency_matrix, consistency_matrix_file);
1094 | // Compute maximum clique
1095 | FMC::CGraphIO gio;
1096 | gio.readGraph(consistency_matrix_file);
1097 | int max_clique_size = 0;
1098 | std::vector max_clique_data;
1099 |
1100 | max_clique_size = FMC::maxCliqueHeu(gio, max_clique_data);
1101 |
1102 | std::sort(max_clique_data.begin(), max_clique_data.end());
1103 |
1104 | auto loop_accept_queue_this = _loop_accept_queue.find(_robot_this_th);
1105 | if (loop_accept_queue_this == _loop_accept_queue.end()){
1106 | _loop_accept_queue.emplace(std::make_pair(_robot_this_th, max_clique_data));
1107 | return true;
1108 | }
1109 |
1110 | if(max_clique_data == loop_accept_queue_this->second)
1111 | return false;
1112 |
1113 | _loop_accept_queue[_robot_this_th].clear();
1114 | _loop_accept_queue[_robot_this_th] = max_clique_data;
1115 | return true;
1116 | }
1117 |
1118 | Eigen::MatrixXi computePCMMatrix( std::vector< std::tuple > loop_queue_this){
1119 | Eigen::MatrixXi PCMMat;
1120 | PCMMat.setZero(loop_queue_this.size(), loop_queue_this.size());
1121 | int id_0, id_1;
1122 | gtsam::Pose3 z_aj_bk, z_ai_bl;
1123 | gtsam::Pose3 z_ai_aj, z_bk_bl;
1124 | gtsam::Pose3 t_ai, t_aj, t_bk, t_bl;
1125 |
1126 | for (unsigned int i = 0; i < loop_queue_this.size(); i++){
1127 | std::tie(id_0, id_1, z_aj_bk) = loop_queue_this[i];
1128 | PointTypePose tmp_pose_0 = _bin_with_id.at(id_0).pose;
1129 | PointTypePose tmp_pose_1 = _bin_with_id.at(id_1).pose;
1130 | t_aj = gtsam::Pose3(gtsam::Rot3::RzRyRx(tmp_pose_0.roll, tmp_pose_0.pitch, tmp_pose_0.yaw),
1131 | gtsam::Point3(tmp_pose_0.x, tmp_pose_0.y, tmp_pose_0.z));
1132 | t_bk = gtsam::Pose3(gtsam::Rot3::RzRyRx(tmp_pose_1.roll, tmp_pose_1.pitch, tmp_pose_1.yaw),
1133 | gtsam::Point3(tmp_pose_1.x, tmp_pose_1.y, tmp_pose_1.z));
1134 |
1135 | for (unsigned int j = i + 1; j < loop_queue_this.size(); j++){
1136 | std::tie(id_0, id_1, z_ai_bl) = loop_queue_this[j];
1137 | PointTypePose tmp_pose_0 = _bin_with_id.at(id_0).pose;
1138 | PointTypePose tmp_pose_1 = _bin_with_id.at(id_1).pose;
1139 | t_ai = gtsam::Pose3(gtsam::Rot3::RzRyRx(tmp_pose_0.roll, tmp_pose_0.pitch, tmp_pose_0.yaw),
1140 | gtsam::Point3(tmp_pose_0.x, tmp_pose_0.y, tmp_pose_0.z));
1141 | t_bl = gtsam::Pose3(gtsam::Rot3::RzRyRx(tmp_pose_1.roll, tmp_pose_1.pitch, tmp_pose_1.yaw),
1142 | gtsam::Point3(tmp_pose_1.x, tmp_pose_1.y, tmp_pose_1.z));
1143 | z_ai_aj = t_ai.between(t_aj);
1144 | z_bk_bl = t_bk.between(t_bl);
1145 | float resi = residualPCM(z_aj_bk, z_ai_bl, z_ai_aj, z_bk_bl, 1);
1146 | if (resi < _pcm_thres)
1147 | PCMMat(i,j) = 1;
1148 | else
1149 | PCMMat(i,j) = 0;
1150 | }
1151 | }
1152 | return PCMMat;
1153 | }
1154 |
1155 | float residualPCM(gtsam::Pose3 inter_jk, gtsam::Pose3 inter_il, gtsam::Pose3 inner_ij, gtsam::Pose3 inner_kl, float intensity){
1156 | gtsam::Pose3 inter_il_inv = inter_il.inverse();
1157 | gtsam::Pose3 res_pose = inner_ij * inter_jk * inner_kl * inter_il_inv;
1158 | gtsam::Vector6 res_vec = gtsam::Pose3::Logmap(res_pose);
1159 |
1160 | Eigen::Matrix< double, 6, 1> v ;
1161 | v << intensity, intensity, intensity,
1162 | intensity, intensity, intensity;
1163 | Eigen::Matrix< double, 6, 6> m_cov = v.array().matrix().asDiagonal();
1164 |
1165 | return sqrt(res_vec.transpose()* m_cov * res_vec);
1166 | }
1167 |
1168 | void printPCMGraph(Eigen::MatrixXi pcm_matrix, std::string file_name) {
1169 | // Intialization
1170 | int nb_consistent_measurements = 0;
1171 |
1172 | // Format edges.
1173 | std::stringstream ss;
1174 | for (int i = 0; i < pcm_matrix.rows(); i++) {
1175 | for (int j = i; j < pcm_matrix.cols(); j++) {
1176 | if (pcm_matrix(i,j) == 1) {
1177 | ss << i+1 << " " << j+1 << std::endl;
1178 | nb_consistent_measurements++;
1179 | }
1180 | }
1181 | }
1182 |
1183 | // Write to file
1184 | std::ofstream output_file;
1185 | output_file.open(file_name);
1186 | output_file << "%%MatrixMarket matrix coordinate pattern symmetric" << std::endl;
1187 | output_file << pcm_matrix.rows() << " " << pcm_matrix.cols() << " " << nb_consistent_measurements << std::endl;
1188 | output_file << ss.str();
1189 | output_file.close();
1190 | }
1191 |
1192 | void gtsamExpressionGraph(){
1193 | if (_loop_accept_queue[_robot_this_th].size()<2)
1194 | return;
1195 |
1196 | gtsam::Vector Vector6(6);
1197 |
1198 | gtsam::Pose3 initial_pose_0, initial_pose_1;
1199 | initial_pose_0 = std::get<0>(_pose_queue[_robot_this_th][ _loop_accept_queue[_robot_this_th][_loop_accept_queue[_robot_this_th].size() -1] ]);
1200 | initial_pose_1 = std::get<1>(_pose_queue[_robot_this_th][ _loop_accept_queue[_robot_this_th][_loop_accept_queue[_robot_this_th].size() -1] ]);
1201 |
1202 | gtsam::Values initial;
1203 | gtsam::ExpressionFactorGraph graph;
1204 |
1205 | gtsam::Pose3_ trans(0);
1206 |
1207 | initial.insert(0, initial_pose_1 * initial_pose_0.inverse());
1208 | //initial.print();
1209 |
1210 | gtsam::Pose3 p, measurement;
1211 | float noiseScore;
1212 |
1213 | for (auto i:_loop_accept_queue[_robot_this_th]){
1214 | std::tie(measurement, p, noiseScore) = _pose_queue[_robot_this_th][i];
1215 |
1216 | if(noiseScore == 0)// 0 indicates a inter robot outlier
1217 | continue;
1218 |
1219 | gtsam::Pose3_ predicted = transformTo(trans,p);
1220 |
1221 | Vector6 << noiseScore, noiseScore, noiseScore, noiseScore, noiseScore,
1222 | noiseScore;
1223 | auto measurementNoise = gtsam::noiseModel::Diagonal::Variances(Vector6);
1224 |
1225 | // Add the Pose3 expression variable, an initial estimate, and the measurement noise.
1226 | graph.addExpressionFactor(predicted, measurement, measurementNoise);
1227 | }
1228 | gtsam::Values result = gtsam::LevenbergMarquardtOptimizer(graph, initial).optimize();
1229 |
1230 | gtsam::Pose3 est = result.at(0);
1231 |
1232 | PointTypePose map_trans_this;
1233 |
1234 | //float x, y, z, roll, pitch, yaw;
1235 | map_trans_this.x = est.translation().x();
1236 | map_trans_this.y = est.translation().y();
1237 | map_trans_this.z = est.translation().z();
1238 | map_trans_this.roll = est.rotation().roll();
1239 | map_trans_this.pitch = est.rotation().pitch();
1240 | map_trans_this.yaw = est.rotation().yaw();
1241 |
1242 | auto ite = _global_map_trans.find(_robot_this_th);
1243 | if(ite == _global_map_trans.end()){
1244 | std::vector tmp_pose_list;
1245 | tmp_pose_list.push_back(map_trans_this);
1246 | _global_map_trans.emplace(std::make_pair( _robot_this_th, tmp_pose_list ) );
1247 | _global_map_trans_optimized.emplace(std::make_pair( _robot_this_th, map_trans_this));
1248 | }
1249 | else{
1250 | _global_map_trans[_robot_this_th].push_back(map_trans_this);
1251 | _global_map_trans_optimized[_robot_this_th] = map_trans_this;
1252 | }
1253 |
1254 |
1255 | if (_global_odom_trans.size() != 0)
1256 | gtsamFactorGraph();
1257 |
1258 | if (_trans_to_publish.intensity == 0){
1259 | ite = _global_map_trans.find(0);
1260 | if(ite == _global_map_trans.end())
1261 | return;
1262 | _global_map_trans_optimized[0].intensity = 1;
1263 | _trans_to_publish = _global_map_trans_optimized[0];
1264 | }
1265 |
1266 | if (_global_map_trans.size() == 1 && _global_odom_trans.size() == 0)
1267 | _trans_to_publish = _global_map_trans_optimized[0];
1268 |
1269 | graph.resize(0);
1270 |
1271 | }
1272 |
1273 | void sendMapOutputMessage(){
1274 | if (_trans_to_publish.intensity == 0)
1275 | return;
1276 |
1277 | //publish transformation to the SLAM node
1278 | nav_msgs::Odometry odom2map;
1279 | odom2map.header.stamp = _cloud_header.stamp;
1280 | odom2map.header.frame_id = _robot_id + "/" + _sc_frame;
1281 | odom2map.child_frame_id = _robot_id + "/" + _sc_frame + "/odom2map";
1282 | odom2map.pose.pose.position.x = _trans_to_publish.x;
1283 | odom2map.pose.pose.position.y = _trans_to_publish.y;
1284 | odom2map.pose.pose.position.z = _trans_to_publish.z;
1285 | odom2map.pose.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw
1286 | (_trans_to_publish.roll, _trans_to_publish.pitch, _trans_to_publish.yaw);
1287 | _pub_trans_odom2map.publish(odom2map);
1288 | }
1289 |
1290 | void sendGlobalLoopMessageKDTree(){
1291 |
1292 | auto loop_list = _loop_queue[_robot_this_th];
1293 | int len_loop_list = loop_list.size() - 1;
1294 | auto loop_this = loop_list[len_loop_list];
1295 | int id_bin_this = std::get<1>(loop_this);
1296 |
1297 | if (_initial_loop.first == -1){
1298 | //if not enough time distance
1299 | _initial_loop.first = _robot_this_th;
1300 | _initial_loop.second = len_loop_list;
1301 | //initialize last point
1302 | _id_bin_last = id_bin_this;
1303 | return;
1304 | }
1305 |
1306 | if (!compare_timestamp(id_bin_this, _id_bin_last)){
1307 | //if not enough history point
1308 | return;
1309 | }
1310 |
1311 | int tmp_robot_id_th = _initial_loop.first;
1312 | int tmp_len_loop_list = _initial_loop.second;
1313 | auto loop_that = _loop_queue[tmp_robot_id_th][tmp_len_loop_list];
1314 | int id_bin_that = std::get<1>(loop_that);
1315 | sendLoopThis(_robot_this_th, tmp_robot_id_th, len_loop_list, tmp_len_loop_list);
1316 | sendLoopThat(_robot_this_th, tmp_robot_id_th, len_loop_list, tmp_len_loop_list);
1317 |
1318 | _id_bin_last = id_bin_this;
1319 |
1320 | }
1321 |
1322 |
1323 | bool compare_timestamp(int id_0, int id_1){
1324 | if(abs(_bin_with_id[id_0].pose.intensity - _bin_with_id[id_1].pose.intensity) > _loop_frame_thres)
1325 | return true;
1326 | else
1327 | return false;
1328 | }
1329 |
1330 | void sendLoopThis(int robot_id_this, int robot_id_that, int id_loop_this, int id_loop_that){
1331 | auto loop_list_this = _loop_queue[robot_id_this];
1332 | auto loop_list_that = _loop_queue[robot_id_that];
1333 | auto loop_this = loop_list_this[id_loop_this];
1334 | auto loop_that = loop_list_that[id_loop_that];
1335 |
1336 | int id_bin_this = std::get<1>(loop_this);
1337 | int id_bin_last = std::get<1>(loop_that);
1338 |
1339 | auto pose_this = _pose_queue[robot_id_this][id_loop_this];
1340 | auto pose_that = _pose_queue[robot_id_that][id_loop_that];
1341 |
1342 | gtsam::Pose3 pose_to_this, pose_to_that;
1343 | if(robot_id_this == robot_id_that){
1344 | pose_to_this = std::get<1>(pose_this);
1345 | pose_to_that = std::get<1>(pose_that);
1346 | }
1347 | else{
1348 | if (_global_map_trans_optimized.find(robot_id_this) == _global_map_trans_optimized.end() ||
1349 | _global_map_trans_optimized.find(robot_id_that) == _global_map_trans_optimized.end() )
1350 | return;
1351 | PointTypePose trans_this = _global_map_trans_optimized[robot_id_this];
1352 | gtsam::Pose3 trans_this3 = gtsam::Pose3( gtsam::Rot3::RzRyRx(trans_this.roll, trans_this.pitch, trans_this.yaw),
1353 | gtsam::Point3(trans_this.x, trans_this.y, trans_this.z) );
1354 |
1355 | PointTypePose trans_that = _global_map_trans_optimized[robot_id_that];
1356 | gtsam::Pose3 trans_that3 = gtsam::Pose3( gtsam::Rot3::RzRyRx(trans_that.roll, trans_that.pitch, trans_that.yaw),
1357 | gtsam::Point3(trans_that.x, trans_that.y, trans_that.z) );
1358 |
1359 | pose_to_this = trans_this3.inverse() * std::get<1>(pose_this);
1360 | pose_to_that = trans_that3.inverse() * std::get<1>(pose_that);
1361 | }
1362 |
1363 | float tmp_intensity = (std::get<2>(pose_this) + std::get<2>(pose_that) ) / 2.0;
1364 |
1365 | auto dpose = pose_to_that.between(pose_to_this);
1366 | auto thisp = _bin_with_id[id_bin_this].pose;
1367 | auto thatp = _bin_with_id[id_bin_last].pose;
1368 | auto pose_to_this1 = gtsam::Pose3( gtsam::Rot3::RzRyRx(thisp.roll, thisp.pitch, thisp.yaw),
1369 | gtsam::Point3(thisp.x, thisp.y, thisp.z) );
1370 | auto pose_to_that1 = gtsam::Pose3( gtsam::Rot3::RzRyRx(thatp.roll, thatp.pitch, thatp.yaw),
1371 | gtsam::Point3(thatp.x, thatp.y, thatp.z) );
1372 | auto dpose1 = pose_to_that1.between(pose_to_this1);
1373 |
1374 | update_loop_info(id_bin_last, id_bin_this, pose_to_that, pose_to_this, tmp_intensity);
1375 |
1376 | _pub_loop_info.publish(_loop_info);
1377 | }
1378 |
1379 | void sendLoopThat(int robot_id_this,int robot_id_that, int id_loop_this, int id_loop_that){
1380 | if(robot_id_this != robot_id_that)
1381 | return;
1382 | auto loop_list = _loop_queue[robot_id_this];
1383 | auto loop_this = loop_list[id_loop_this];
1384 | auto loop_that = loop_list[id_loop_that];
1385 |
1386 | int id_bin_this = std::get<0>(loop_this);
1387 | int id_bin_last = std::get<0>(loop_that);
1388 |
1389 | auto pose_this = _pose_queue[robot_id_this][id_loop_this];
1390 | auto pose_that = _pose_queue[robot_id_this][id_loop_that];
1391 | auto pose_to_this = std::get<0>(pose_this) * std::get<2>(loop_this) ;
1392 | auto pose_to_that = std::get<0>(pose_that) * std::get<2>(loop_that) ;
1393 | float tmp_intensity = (std::get<2>(pose_this) + std::get<2>(pose_that) ) / 2.0;
1394 | update_loop_info(id_bin_last, id_bin_this, pose_to_that, pose_to_this, tmp_intensity);
1395 |
1396 | _pub_loop_info_global.publish(_loop_info);
1397 | }
1398 |
1399 | void update_loop_info(int id_bin_last, int id_bin_this, gtsam::Pose3 pose_to_last, gtsam::Pose3 pose_to_this, float intensity){
1400 | //relative translation btwn 2 poses
1401 | auto dpose = pose_to_last.between(pose_to_this);
1402 | _loop_info.robotID = _bin_with_id[id_bin_last].robotname;
1403 | _loop_info.numRing = _bin_with_id[id_bin_last].pose.intensity;//from
1404 | _loop_info.numSector = _bin_with_id[id_bin_this].pose.intensity;//to
1405 |
1406 | _loop_info.poseX = dpose.translation().x();
1407 | _loop_info.poseY = dpose.translation().y();
1408 | _loop_info.poseZ = dpose.translation().z();
1409 | _loop_info.poseRoll = dpose.rotation().roll();
1410 | _loop_info.posePitch = dpose.rotation().pitch();
1411 | _loop_info.poseYaw = dpose.rotation().yaw();
1412 | _loop_info.poseIntensity = intensity;
1413 |
1414 | }
1415 |
1416 | void sendOdomOutputMessage(){
1417 |
1418 | sendMapOutputMessage();
1419 |
1420 | //publish relative transformation to other robots
1421 | nav_msgs::Odometry odom2odom;
1422 | odom2odom.header.stamp = _cloud_header.stamp;
1423 | odom2odom.header.frame_id = _robot_id;
1424 | odom2odom.child_frame_id = _robot_this;
1425 | odom2odom.pose.pose.position.x = _global_map_trans_optimized[_robot_this_th].x;
1426 | odom2odom.pose.pose.position.y = _global_map_trans_optimized[_robot_this_th].y;
1427 | odom2odom.pose.pose.position.z = _global_map_trans_optimized[_robot_this_th].z;
1428 | odom2odom.pose.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw
1429 | (_global_map_trans_optimized[_robot_this_th].roll,
1430 | _global_map_trans_optimized[_robot_this_th].pitch,
1431 | _global_map_trans_optimized[_robot_this_th].yaw);
1432 | _pub_trans_odom2odom.publish(odom2odom);
1433 |
1434 | sendGlobalLoopMessageKDTree();
1435 |
1436 | }
1437 | };
1438 |
1439 | int main(int argc, char** argv)
1440 | {
1441 | ros::init(argc, argv, "fushion");
1442 | MapFusion MapF;
1443 |
1444 | ROS_INFO("\033[1;32m----> Map Fushion Started.\033[0m");
1445 |
1446 | std::thread publishThread(&MapFusion::publishContextInfoThread, &MapF);
1447 |
1448 | ros::spin();
1449 |
1450 | publishThread.join();
1451 |
1452 | return 0;
1453 | }
1454 |
--------------------------------------------------------------------------------
/third_parties/fast_max-clique_finder/COPYRIGHT:
--------------------------------------------------------------------------------
1 | Copyright (C) 2014, Northwestern University
2 |
3 | Portions of this software were developed by Northwestern University.
4 |
5 | Access and use of this software shall impose the following obligations
6 | and understandings on the user. The user is granted the right, without
7 | any fee or cost, to use, copy, modify, alter, enhance and distribute
8 | this software, and any derivative works thereof, and its supporting
9 | documentation for any purpose whatsoever, provided that this entire
10 | notice appears in all copies of the software, derivative works and
11 | supporting documentation. Further, Northwestern University requests
12 | that the user credit Northwestern University in any publications that
13 | result from the use of this software or in any product that includes
14 | this software. The names Northwestern University, however, may not be
15 | used in any advertising or publicity to endorse or promote any products
16 | or commercial entity unless specific written permission is obtained from
17 | Northwestern University. The user also understands that Northwestern
18 | University is not obligated to provide the user with any support,
19 | consulting, training or assistance of any kind with regard to the use,
20 | operation and performance of this software nor to provide the user with
21 | any updates, revisions, new versions or "bug fixes."
22 |
23 | THIS SOFTWARE IS PROVIDED BY NORTHWESTERN UNIVERSITY "AS IS" AND ANY
24 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NORTHWESTERN UNIVERSITY BE
27 | LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
28 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
29 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
30 | OR IN CONNECTION WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
31 |
--------------------------------------------------------------------------------
/third_parties/fast_max-clique_finder/README.md:
--------------------------------------------------------------------------------
1 | You should have received this Fast Max-Clique Finder version along with robust\_multirobot\_map\_merging (https://github.com/lajoiepy/robust\_multirobot\_map\_merging).
2 | See the original Fast Max-Clique Finder library at: http://cucis.ece.northwestern.edu/projects/MAXCLIQUE/
3 |
--------------------------------------------------------------------------------
/third_parties/fast_max-clique_finder/src/findClique.cpp:
--------------------------------------------------------------------------------
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 | /* Description: a library for finding the maximum clique of a graph */
3 | /* */
4 | /* */
5 | /* Authors: Bharath Pattabiraman and Md. Mostofa Ali Patwary */
6 | /* EECS Department, Northwestern University */
7 | /* email: {bpa342,mpatwary}@eecs.northwestern.edu */
8 | /* */
9 | /* Copyright, 2014, Northwestern University */
10 | /* See COPYRIGHT notice in top-level directory. */
11 | /* */
12 | /* Please site the following publication if you use this package: */
13 | /* Bharath Pattabiraman, Md. Mostofa Ali Patwary, Assefaw H. Gebremedhin2, */
14 | /* Wei-keng Liao, and Alok Choudhary. */
15 | /* "Fast Algorithms for the Maximum Clique Problem on Massive Graphs with */
16 | /* Applications to Overlapping Community Detection" */
17 | /* http://arxiv.org/abs/1411.7460 */
18 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
19 |
20 | #include "findClique.h"
21 |
22 | namespace FMC {
23 | int pruned1;
24 | int pruned2;
25 | int pruned3;
26 | int pruned5;
27 |
28 | /* Algorithm 2: CLIQUE: Recursive Subroutine of algorithm 1. */
29 | void maxCliqueHelper( CGraphIO& gio, vector* U, int sizeOfClique, int& maxClq, vector& max_clique_data_inter )
30 | {
31 | int iPos, index = 0, maxClq_prev;
32 | vector * ptrVertex = gio.GetVerticesPtr();
33 | vector * ptrEdge = gio.GetEdgesPtr();
34 | vector U_new;
35 | U_new.reserve(gio.GetVertexCount());
36 |
37 | if( U->size() == 0 )
38 | {
39 | if( sizeOfClique > maxClq )
40 | {
41 | maxClq = sizeOfClique;
42 | max_clique_data_inter.clear();
43 | }
44 | return;
45 | }
46 |
47 | while( U->size() > 0 )
48 | {
49 | //Old Pruning
50 | if( sizeOfClique + U->size() <= maxClq )
51 | return;
52 |
53 | index = U->back();
54 | U->pop_back();
55 |
56 | // Loop over neighbrs of v_index.
57 | for(int j = (*ptrVertex)[index]; j < (*ptrVertex)[index + 1]; j++ )
58 | //Pruning 5
59 | if( getDegree(ptrVertex, (*ptrEdge)[j]) >= maxClq )
60 | {
61 | // Loop over U.
62 | for(int i = 0; i < U->size(); i++)
63 | {
64 | if( (*ptrEdge)[j] == (*U)[i] )
65 | U_new.push_back( (*ptrEdge)[j]);
66 | }
67 | }
68 | else
69 | pruned3++;
70 |
71 | maxClq_prev = maxClq;
72 |
73 | maxCliqueHelper( gio, &U_new, sizeOfClique + 1, maxClq, max_clique_data_inter );
74 |
75 | if(maxClq > maxClq_prev)
76 | max_clique_data_inter.push_back(index);
77 |
78 | U_new.clear();
79 | }
80 | }
81 |
82 | /* Algorithm 1: MAXCLIQUE: Finds maximum clique of the given graph */
83 | int maxClique( CGraphIO& gio, int l_bound, vector& max_clique_data )
84 | {
85 | vector * ptrVertex = gio.GetVerticesPtr();
86 | vector * ptrEdge = gio.GetEdgesPtr();
87 | vector U;
88 | U.reserve(gio.GetVertexCount());
89 | vector max_clique_data_inter;
90 | max_clique_data_inter.reserve(gio.GetVertexCount());
91 | max_clique_data.reserve(gio.GetVertexCount());
92 | int maxClq = l_bound;
93 | int prev_maxClq;
94 |
95 | //cout << "Computing Max Clique... with lower bound " << maxClq << endl;
96 | pruned1 = 0;
97 | pruned2 = 0;
98 | pruned3 = 0;
99 | pruned5 = 0;
100 |
101 | //Bit Vector to track if vertex has been considered previously.
102 | int *bitVec = new int[gio.GetVertexCount()];
103 | memset(bitVec, 0, gio.GetVertexCount() * sizeof(int));
104 |
105 | for(int i = gio.GetVertexCount()-1; i >= 0; i--)
106 | {
107 | bitVec[i] = 1;
108 | prev_maxClq = maxClq;
109 |
110 | U.clear();
111 | //Pruning 1
112 | if( getDegree(ptrVertex, i) < maxClq)
113 | {
114 | pruned1++;
115 | continue;
116 | }
117 |
118 | for( int j = (*ptrVertex)[i]; j < (*ptrVertex)[i + 1]; j++ )
119 | {
120 | //Pruning 2
121 | if(bitVec[(*ptrEdge)[j]] != 1)
122 | {
123 | //Pruning 3
124 | if( getDegree(ptrVertex, (*ptrEdge)[j]) >= maxClq )
125 | U.push_back((*ptrEdge)[j]);
126 | else
127 | pruned3++;
128 | }
129 | else
130 | pruned2++;
131 | }
132 |
133 | maxCliqueHelper( gio, &U, 1, maxClq, max_clique_data_inter );
134 |
135 | if(maxClq > prev_maxClq)
136 | {
137 | max_clique_data_inter.push_back(i);
138 | max_clique_data = max_clique_data_inter;
139 | }
140 | max_clique_data_inter.clear();
141 | }
142 |
143 | delete [] bitVec;
144 | max_clique_data_inter.clear();
145 |
146 | #ifdef _DEBUG
147 | cout << "Pruning 1 = " << pruned1 << endl;
148 | cout << "Pruning 2 = " << pruned2 << endl;
149 | cout << "Pruning 3 = " << pruned3 << endl;
150 | cout << "Pruning 5 = " << pruned5 << endl;
151 | #endif
152 |
153 | return maxClq;
154 | }
155 |
156 | void print_max_clique(vector& max_clique_data)
157 | {
158 | //cout << "Maximum clique: ";
159 | for(int i = 0; i < max_clique_data.size(); i++)
160 | cout << max_clique_data[i] + 1 << " ";
161 | cout << endl;
162 | }
163 | }
--------------------------------------------------------------------------------
/third_parties/fast_max-clique_finder/src/findClique.h:
--------------------------------------------------------------------------------
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 | /* Description: a library for finding the maximum clique of a graph */
3 | /* */
4 | /* */
5 | /* Authors: Bharath Pattabiraman and Md. Mostofa Ali Patwary */
6 | /* EECS Department, Northwestern University */
7 | /* email: {bpa342,mpatwary}@eecs.northwestern.edu */
8 | /* */
9 | /* Copyright, 2014, Northwestern University */
10 | /* See COPYRIGHT notice in top-level directory. */
11 | /* */
12 | /* Please site the following publication if you use this package: */
13 | /* Bharath Pattabiraman, Md. Mostofa Ali Patwary, Assefaw H. Gebremedhin2, */
14 | /* Wei-keng Liao, and Alok Choudhary. */
15 | /* "Fast Algorithms for the Maximum Clique Problem on Massive Graphs with */
16 | /* Applications to Overlapping Community Detection" */
17 | /* http://arxiv.org/abs/1411.7460 */
18 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
19 |
20 | #ifndef FINDCLIQUE_H_INCLUDED
21 | #define FINDCLIQUE_H_INCLUDED
22 |
23 | #include "graphIO.h"
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | using namespace std;
32 |
33 | #ifdef _DEBUG
34 | int DEBUG=1;
35 | #endif
36 |
37 | namespace FMC {
38 |
39 | //Function Definitions
40 | bool fexists(const char *filename);
41 | double wtime();
42 | void usage(char *argv0);
43 | int getDegree(vector* ptrVtx, int idx);
44 | void print_max_clique(vector& max_clique_data);
45 |
46 | int maxClique( CGraphIO& gio, int l_bound, vector& max_clique_data );
47 | void maxCliqueHelper( CGraphIO& gio, vector* U, int sizeOfClique, int& maxClq, vector& max_clique_data_inter );
48 |
49 | int maxCliqueHeu( CGraphIO& gio );
50 | int maxCliqueHeu(CGraphIO& gio, vector& max_clique_data);
51 | void maxCliqueHelperHeu( CGraphIO& gio, vector* U, int sizeOfClique, int& maxClq, vector& max_clique_data_inter );
52 |
53 | }
54 | #endif
55 |
--------------------------------------------------------------------------------
/third_parties/fast_max-clique_finder/src/findCliqueHeu.cpp:
--------------------------------------------------------------------------------
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 | /* Description: a library for finding the maximum clique from a graph */
3 | /* */
4 | /* */
5 | /* Authors: Md. Mostofa Ali Patwary and Bharath Pattabiraman */
6 | /* EECS Department, Northwestern University */
7 | /* email: {mpatwary,bpa342}@eecs.northwestern.edu */
8 | /* */
9 | /* Copyright, 2014, Northwestern University */
10 | /* See COPYRIGHT notice in top-level directory. */
11 | /* */
12 | /* Please site the following publication if you use this package: */
13 | /* Bharath Pattabiraman, Md. Mostofa Ali Patwary, Assefaw H. Gebremedhin2, */
14 | /* Wei-keng Liao, and Alok Choudhary. */
15 | /* "Fast Algorithms for the Maximum Clique Problem on Massive Graphs with */
16 | /* Applications to Overlapping Community Detection" */
17 | /* http://arxiv.org/abs/1411.7460 */
18 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
19 |
20 | #include "findClique.h"
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | namespace FMC {
27 | int maxDegree, maxClq;
28 |
29 | /* Algorithm 2: MaxCliqueHeu: A heuristic to find maximum clique */
30 | int maxCliqueHeu(CGraphIO& gio)
31 | {
32 | vector * p_v_i_Vertices = gio.GetVerticesPtr();
33 | vector * p_v_i_Edges = gio.GetEdgesPtr();
34 |
35 | //srand(time(NULL));
36 |
37 | maxDegree = gio.GetMaximumVertexDegree();
38 | int maxClq = - 1, u, icc;
39 | vector < int > v_i_S;
40 | vector < int > v_i_S1;
41 | v_i_S.resize(maxDegree + 1, 0);
42 | v_i_S1.resize(maxDegree + 1, 0);
43 |
44 | int iPos, iPos1, iCount, iCount1;
45 |
46 | int notComputed = 0;
47 |
48 | // compute the max clique for each vertex
49 | for(int iCandidateVertex = 0; iCandidateVertex < p_v_i_Vertices->size() - 1; iCandidateVertex++)
50 | {
51 | // Pruning 1
52 | if(maxClq > ((*p_v_i_Vertices)[iCandidateVertex + 1] - (*p_v_i_Vertices)[iCandidateVertex]))
53 | {
54 | notComputed++;
55 | continue;
56 | }
57 |
58 | iPos = 0;
59 | v_i_S[iPos++] = iCandidateVertex;
60 | int z, imdv = iCandidateVertex, imd = (*p_v_i_Vertices)[iCandidateVertex + 1] - (*p_v_i_Vertices)[iCandidateVertex];
61 |
62 | int iLoopCount = (*p_v_i_Vertices)[iCandidateVertex + 1];
63 | for(int j = (*p_v_i_Vertices)[iCandidateVertex]; j < iLoopCount; j++)
64 | {
65 | // Pruning 3
66 | if(maxClq <= ((*p_v_i_Vertices)[(*p_v_i_Edges)[j] + 1] - (*p_v_i_Vertices)[(*p_v_i_Edges)[j]]))
67 | v_i_S[iPos++] = (*p_v_i_Edges)[j];
68 | }
69 |
70 | icc = 0;
71 |
72 | while(iPos > 0)
73 | {
74 | int imdv1 = -1, imd1 = -1;
75 |
76 | icc++;
77 |
78 | // generate a random number x from 0 to iPos -1
79 | // aasign that imdv = x and imd = d(x)
80 | //imdv = v_i_S[rand() % iPos];
81 | imdv = v_i_S[iPos-1];
82 | imd = (*p_v_i_Vertices)[imdv + 1] - (*p_v_i_Vertices)[imdv];
83 |
84 | iPos1 = 0;
85 |
86 | for(int j = 0; j < iPos; j++)
87 | {
88 | iLoopCount = (*p_v_i_Vertices)[imdv + 1];
89 | for(int k = (*p_v_i_Vertices)[imdv]; k < iLoopCount; k++)
90 | {
91 | // Pruning 5
92 | if(v_i_S[j] == (*p_v_i_Edges)[k] && maxClq <= ((*p_v_i_Vertices)[(*p_v_i_Edges)[k] + 1] - (*p_v_i_Vertices)[(*p_v_i_Edges)[k]]))
93 | {
94 | v_i_S1[iPos1++] = v_i_S[j]; // calculate the max degree vertex here
95 |
96 | break;
97 | }
98 | }
99 | }
100 |
101 | for(int j = 0; j < iPos1; j++)
102 | v_i_S[j] = v_i_S1[j];
103 |
104 | iPos = iPos1;
105 |
106 | imdv = imdv1;
107 | imd = imd1;
108 | }
109 |
110 | if(maxClq < icc)
111 | maxClq = icc;
112 |
113 | }
114 |
115 | return maxClq;
116 | }
117 |
118 | /* Algorithm 2: MaxCliqueHeu: A heuristic to find maximum clique */
119 | /* Modified by Josh Mangelson to return possible Max clique */
120 | int maxCliqueHeu(CGraphIO& gio, vector& max_clique_data)
121 | {
122 | vector * p_v_i_Vertices = gio.GetVerticesPtr();
123 | vector * p_v_i_Edges = gio.GetEdgesPtr();
124 |
125 | vector max_clique_data_inter;
126 | max_clique_data_inter.reserve(gio.GetVertexCount());
127 | max_clique_data.reserve(gio.GetVertexCount());
128 |
129 | //srand(time(NULL));
130 |
131 | maxDegree = gio.GetMaximumVertexDegree();
132 | int maxClq = - 1, u, icc;
133 | vector < int > v_i_S;
134 | vector < int > v_i_S1;
135 | v_i_S.resize(maxDegree + 1, 0);
136 | v_i_S1.resize(maxDegree + 1, 0);
137 |
138 | int iPos, iPos1, iCount, iCount1;
139 |
140 | int notComputed = 0;
141 |
142 | // compute the max clique for each vertex
143 | for(int iCandidateVertex = 0; iCandidateVertex < p_v_i_Vertices->size() - 1; iCandidateVertex++)
144 | {
145 | max_clique_data_inter.clear ();
146 |
147 | // std::cout << iCandidateVertex << " ---------------\n";
148 | // Pruning 1
149 | if(maxClq > ((*p_v_i_Vertices)[iCandidateVertex + 1] - (*p_v_i_Vertices)[iCandidateVertex]))
150 | {
151 | notComputed++;
152 | continue;
153 | }
154 |
155 | iPos = 0;
156 | v_i_S[iPos++] = iCandidateVertex;
157 | int z, imdv = iCandidateVertex, imd = (*p_v_i_Vertices)[iCandidateVertex + 1] - (*p_v_i_Vertices)[iCandidateVertex];
158 |
159 | int iLoopCount = (*p_v_i_Vertices)[iCandidateVertex + 1];
160 | for(int j = (*p_v_i_Vertices)[iCandidateVertex]; j < iLoopCount; j++)
161 | {
162 | // Pruning 3
163 | if(maxClq <= ((*p_v_i_Vertices)[(*p_v_i_Edges)[j] + 1] - (*p_v_i_Vertices)[(*p_v_i_Edges)[j]]))
164 | v_i_S[iPos++] = (*p_v_i_Edges)[j];
165 | }
166 |
167 | // std::cout << iPos << std::endl;
168 | // for(int x = 0; x < v_i_S.size(); x++)
169 | // std::cout << v_i_S[x] << ", ";
170 | // std::cout << std::endl << "------------" << std::endl;
171 |
172 |
173 | icc = 0;
174 | max_clique_data_inter.push_back(iCandidateVertex);
175 |
176 | while(iPos > 0)
177 | {
178 | int imdv1 = -1, imd1 = -1;
179 |
180 | icc++;
181 |
182 | // generate a random number x from 0 to iPos -1
183 | // aasign that imdv = x and imd = d(x)
184 | //imdv = v_i_S[rand() % iPos];
185 | imdv = v_i_S[iPos-1];
186 | imd = (*p_v_i_Vertices)[imdv + 1] - (*p_v_i_Vertices)[imdv];
187 |
188 | iPos1 = 0;
189 |
190 | for(int j = 0; j < iPos; j++)
191 | {
192 | iLoopCount = (*p_v_i_Vertices)[imdv + 1];
193 | for(int k = (*p_v_i_Vertices)[imdv]; k < iLoopCount; k++)
194 | {
195 | // Pruning 5
196 | if(v_i_S[j] == (*p_v_i_Edges)[k] && maxClq <= ((*p_v_i_Vertices)[(*p_v_i_Edges)[k] + 1] - (*p_v_i_Vertices)[(*p_v_i_Edges)[k]]))
197 | {
198 | v_i_S1[iPos1++] = v_i_S[j]; // calculate the max degree vertex here
199 |
200 | break;
201 | }
202 | }
203 | }
204 |
205 |
206 | // for(int x = 0; x < v_i_S.size(); x++)
207 | // std::cout << v_i_S[x] << ", ";
208 | // std::cout << std::endl;
209 | // std::cout << "v_i_S1: ";
210 | // for(int x = 0; x < iPos1; x++)
211 | // std::cout << v_i_S1[x] << ", ";
212 | // std::cout << std::endl;
213 | // std::cout << imdv << ", " << imd << std::endl;
214 | // std::cout << iPos << ", " << iPos1 << std::endl;
215 |
216 | for(int j = 0; j < iPos1; j++)
217 | v_i_S[j] = v_i_S1[j];
218 |
219 | if (iPos1 != 0) {
220 | // max_clique_data_inter.push_back(iPos);
221 | max_clique_data_inter.push_back(imdv);;
222 | }
223 |
224 | // for(int x=0; x < max_clique_data_inter.size();x++){
225 | // std::cout << max_clique_data_inter[x] << ", ";
226 | // }
227 | // std::cout << std::endl;
228 |
229 |
230 | iPos = iPos1;
231 |
232 | imdv = imdv1;
233 | imd = imd1;
234 | }
235 |
236 | if(maxClq < icc){
237 | maxClq = icc;
238 | max_clique_data = max_clique_data_inter;
239 | }
240 | }
241 |
242 | return maxClq;
243 | }
244 |
245 | template
246 | vector sort_indexes(const vector &v)
247 | {
248 | vector idx(v.size());
249 | iota(idx.begin(), idx.end(), 0);
250 |
251 | sort(idx.begin(), idx.end(),
252 | [&v](size_t i1, size_t i2) {return v[i1] > v[i2];});
253 |
254 | return idx;
255 | }
256 |
257 | }
--------------------------------------------------------------------------------
/third_parties/fast_max-clique_finder/src/graphIO.cpp:
--------------------------------------------------------------------------------
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 | /* Description: an I/O library for reading a graph */
3 | /* */
4 | /* */
5 | /* Authors: Md. Mostofa Ali Patwary and Bharath Pattabiraman */
6 | /* EECS Department, Northwestern University */
7 | /* email: {mpatwary,bpa342}@eecs.northwestern.edu */
8 | /* */
9 | /* Copyright, 2014, Northwestern University */
10 | /* See COPYRIGHT notice in top-level directory. */
11 | /* */
12 | /* Please site the following publication if you use this package: */
13 | /* Bharath Pattabiraman, Md. Mostofa Ali Patwary, Assefaw H. Gebremedhin2, */
14 | /* Wei-keng Liao, and Alok Choudhary. */
15 | /* "Fast Algorithms for the Maximum Clique Problem on Massive Graphs with */
16 | /* Applications to Overlapping Community Detection" */
17 | /* http://arxiv.org/abs/1411.7460 */
18 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
19 |
20 | #include "graphIO.h"
21 |
22 | namespace FMC {
23 | CGraphIO::~CGraphIO()
24 | {
25 | m_vi_Vertices.clear();
26 | m_vi_OrderedVertices.clear();
27 | m_vi_Edges.clear();
28 | m_vd_Values.clear();
29 | }
30 |
31 | bool CGraphIO::readGraph(string s_InputFile, float connStrength)
32 | {
33 | string fileExtension = getFileExtension(s_InputFile);
34 | if(fileExtension == "mtx")
35 | {
36 | // matrix market format
37 | return ReadMatrixMarketAdjacencyGraph(s_InputFile, connStrength);
38 | }
39 | else if(fileExtension == "gr")
40 | {
41 | // gr format
42 | return ReadMeTiSAdjacencyGraph(s_InputFile);
43 | }
44 | else
45 | return false;
46 | }
47 |
48 | bool CGraphIO::ReadMatrixMarketAdjacencyGraph(string s_InputFile, float connStrength)
49 | {
50 | istringstream in2;
51 | string line="";
52 | map > nodeList;
53 | map > valueList;
54 | int col=0, row=0, rowIndex=0, colIndex=0;
55 | int entry_counter = 0, num_of_entries = 0;
56 | double value;
57 |
58 | ifstream in (s_InputFile.c_str());
59 | if(!in)
60 | {
61 | cout<0&&line[0]=='%') //ignore comment line
93 | getline(in,line);
94 |
95 | in2.str(line);
96 | in2 >> row >> col >> num_of_entries;
97 |
98 | if(row!=col)
99 | {
100 | cout<<"* WARNING: GraphInputOutput::ReadMatrixMarketAdjacencyGraph()"<> rowIndex >> colIndex >> value;
116 | rowIndex--;
117 | colIndex--;
118 |
119 | if(rowIndex < 0 || rowIndex >= row)
120 | cout << "Something wrong rowIndex " << rowIndex << " row " << row << endl;
121 |
122 | if(colIndex < 0 || colIndex >= col)
123 | cout << "Something wrong colIndex " << colIndex << " col " << col << endl;
124 |
125 | if(rowIndex == colIndex)
126 | {
127 | continue;
128 | }
129 |
130 | // This is to handle directed graphs. If the edge is already present, skip. If not add.
131 | int exists=0;
132 | for(int k=0; k connStrength)
145 | {
146 | nodeList[rowIndex].push_back(colIndex);
147 | nodeList[colIndex].push_back(rowIndex);
148 | }
149 | }
150 | else
151 | {
152 | nodeList[rowIndex].push_back(colIndex);
153 | nodeList[colIndex].push_back(rowIndex);
154 | }
155 |
156 | if(b_getValue && value > connStrength)
157 | {
158 | valueList[rowIndex].push_back(value);
159 | valueList[colIndex].push_back(value);
160 | }
161 | }
162 | }
163 | }
164 |
165 | //cout << "No. of upper triangular pruned: " << num_upper_triangular << endl;
166 | m_vi_Vertices.push_back(m_vi_Edges.size());
167 |
168 | for(int i=0;i < row; i++)
169 | {
170 | m_vi_Edges.insert(m_vi_Edges.end(),nodeList[i].begin(),nodeList[i].end());
171 | m_vi_Vertices.push_back(m_vi_Edges.size());
172 | }
173 |
174 | if(b_getValue)
175 | {
176 | for(int i=0;i i_VertexDegree)
215 | {
216 | m_i_MinimumVertexDegree = i_VertexDegree;
217 | }
218 | }
219 |
220 | m_d_AverageVertexDegree = (double)m_vi_Edges.size()/i_VertexCount;
221 |
222 | return;
223 | }
224 |
225 | string CGraphIO::getFileExtension(string fileName)
226 | {
227 | string::size_type result;
228 | string fileExtension = "";
229 |
230 | //1. see if the fileName is given in full path
231 | /*result = fileName.rfind("/", fileName.size() - 1);
232 | if(result != string::npos)
233 | {
234 | //found the path (file prefix)
235 | //get the path, including the last DIR_SEPARATOR
236 | path = fileName.substr(0,result+1);
237 | //remove the path from the fileName
238 | fileName = fileName.substr(result+1);
239 | }
240 | */
241 |
242 | //2. see if the fileName has file extension. For example ".mtx"
243 | result = fileName.rfind('.', fileName.size() - 1);
244 | if(result != string::npos)
245 | {
246 | //found the fileExtension
247 | //get the fileExtension excluding the '.'
248 | fileExtension = fileName.substr(result+1);
249 | //remove the fileExtension from the fileName
250 | //fileName = fileName.substr(0,result);
251 | }
252 |
253 | //3. get the name of the input file
254 | //name = fileName;
255 |
256 | return fileExtension;
257 | }
258 | }
--------------------------------------------------------------------------------
/third_parties/fast_max-clique_finder/src/graphIO.h:
--------------------------------------------------------------------------------
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 | /* Description: an I/O library for reading a graph */
3 | /* */
4 | /* */
5 | /* Authors: Md. Mostofa Ali Patwary and Bharath Pattabiraman */
6 | /* EECS Department, Northwestern University */
7 | /* email: {mpatwary,bpa342}@eecs.northwestern.edu */
8 | /* */
9 | /* Copyright, 2014, Northwestern University */
10 | /* See COPYRIGHT notice in top-level directory. */
11 | /* */
12 | /* Please site the following publication if you use this package: */
13 | /* Bharath Pattabiraman, Md. Mostofa Ali Patwary, Assefaw H. Gebremedhin2, */
14 | /* Wei-keng Liao, and Alok Choudhary. */
15 | /* "Fast Algorithms for the Maximum Clique Problem on Massive Graphs with */
16 | /* Applications to Overlapping Community Detection" */
17 | /* http://arxiv.org/abs/1411.7460 */
18 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
19 |
20 | #ifndef _graphIO_
21 | #define _graphIO_
22 |
23 | #include
24 | #include
25 | #include