|
@@ -70,12 +70,13 @@ public:
|
|
|
HdlGraphSlamNodelet() {}
|
|
|
virtual ~HdlGraphSlamNodelet() {}
|
|
|
|
|
|
- virtual void onInit() {
|
|
|
+ virtual void onInit() {
|
|
|
+
|
|
|
nh = getNodeHandle();
|
|
|
mt_nh = getMTNodeHandle();
|
|
|
private_nh = getPrivateNodeHandle();
|
|
|
|
|
|
-
|
|
|
+
|
|
|
published_odom_topic = private_nh.param<std::string>("published_odom_topic", "/odom");
|
|
|
map_frame_id = private_nh.param<std::string>("map_frame_id", "map");
|
|
|
odom_frame_id = private_nh.param<std::string>("odom_frame_id", "odom");
|
|
@@ -84,80 +85,100 @@ public:
|
|
|
|
|
|
max_keyframes_per_update = private_nh.param<int>("max_keyframes_per_update", 10);
|
|
|
|
|
|
-
|
|
|
- anchor_node = nullptr;
|
|
|
+
|
|
|
+ anchor_node = nullptr;
|
|
|
anchor_edge = nullptr;
|
|
|
floor_plane_node = nullptr;
|
|
|
- graph_slam.reset(new GraphSLAM(private_nh.param<std::string>("g2o_solver_type", "lm_var")));
|
|
|
+ graph_slam.reset(new GraphSLAM(private_nh.param<std::string>("g2o_solver_type", "lm_var")));
|
|
|
keyframe_updater.reset(new KeyframeUpdater(private_nh));
|
|
|
loop_detector.reset(new LoopDetector(private_nh));
|
|
|
map_cloud_generator.reset(new MapCloudGenerator());
|
|
|
inf_calclator.reset(new InformationMatrixCalculator(private_nh));
|
|
|
nmea_parser.reset(new NmeaSentenceParser());
|
|
|
|
|
|
- gps_time_offset = private_nh.param<double>("gps_time_offset", 0.0);
|
|
|
- gps_edge_stddev_xy = private_nh.param<double>("gps_edge_stddev_xy", 10000.0);
|
|
|
- gps_edge_stddev_z = private_nh.param<double>("gps_edge_stddev_z", 10.0);
|
|
|
- floor_edge_stddev = private_nh.param<double>("floor_edge_stddev", 10.0);
|
|
|
+
|
|
|
+
|
|
|
+ gps_time_offset = private_nh.param<double>("gps_time_offset", 0.0);
|
|
|
+ gps_edge_stddev_xy = private_nh.param<double>("gps_edge_stddev_xy", 10000.0);
|
|
|
+ gps_edge_stddev_z = private_nh.param<double>("gps_edge_stddev_z", 10.0);
|
|
|
+ floor_edge_stddev = private_nh.param<double>("floor_edge_stddev", 10.0);
|
|
|
|
|
|
- imu_time_offset = private_nh.param<double>("imu_time_offset", 0.0);
|
|
|
- enable_imu_orientation = private_nh.param<bool>("enable_imu_orientation", false);
|
|
|
- enable_imu_acceleration = private_nh.param<bool>("enable_imu_acceleration", false);
|
|
|
- imu_orientation_edge_stddev = private_nh.param<double>("imu_orientation_edge_stddev", 0.1);
|
|
|
- imu_acceleration_edge_stddev = private_nh.param<double>("imu_acceleration_edge_stddev", 3.0);
|
|
|
+ imu_time_offset = private_nh.param<double>("imu_time_offset", 0.0);
|
|
|
+ enable_imu_orientation = private_nh.param<bool>("enable_imu_orientation", false);
|
|
|
+ enable_imu_acceleration = private_nh.param<bool>("enable_imu_acceleration", false);
|
|
|
+ imu_orientation_edge_stddev = private_nh.param<double>("imu_orientation_edge_stddev", 0.1);
|
|
|
+ imu_acceleration_edge_stddev = private_nh.param<double>("imu_acceleration_edge_stddev", 3.0);
|
|
|
|
|
|
- points_topic = private_nh.param<std::string>("points_topic", "/velodyne_points");
|
|
|
+ points_topic = private_nh.param<std::string>("points_topic", "/velodyne_points");
|
|
|
|
|
|
|
|
|
- odom_sub.reset(new message_filters::Subscriber<nav_msgs::Odometry>(mt_nh, published_odom_topic, 256));
|
|
|
- cloud_sub.reset(new message_filters::Subscriber<sensor_msgs::PointCloud2>(mt_nh, "/filtered_points", 32));
|
|
|
- sync.reset(new message_filters::Synchronizer<ApproxSyncPolicy>(ApproxSyncPolicy(32), *odom_sub, *cloud_sub));
|
|
|
- sync->registerCallback(boost::bind(&HdlGraphSlamNodelet::cloud_callback, this, _1, _2));
|
|
|
- imu_sub = nh.subscribe("/gpsimu_driver/imu_data", 1024, &HdlGraphSlamNodelet::imu_callback, this);
|
|
|
- floor_sub = nh.subscribe("/floor_detection/floor_coeffs", 1024, &HdlGraphSlamNodelet::floor_coeffs_callback, this);
|
|
|
-
|
|
|
- if(private_nh.param<bool>("enable_gps", true)) {
|
|
|
- gps_sub = mt_nh.subscribe("/gps/geopoint", 1024, &HdlGraphSlamNodelet::gps_callback, this);
|
|
|
- nmea_sub = mt_nh.subscribe("/gpsimu_driver/nmea_sentence", 1024, &HdlGraphSlamNodelet::nmea_callback, this);
|
|
|
- navsat_sub = mt_nh.subscribe("/gps/navsat", 1024, &HdlGraphSlamNodelet::navsat_callback, this);
|
|
|
+
|
|
|
+ odom_sub.reset(new message_filters::Subscriber<nav_msgs::Odometry>(mt_nh, published_odom_topic, 256));
|
|
|
+ cloud_sub.reset(new message_filters::Subscriber<sensor_msgs::PointCloud2>(mt_nh, "/filtered_points", 32));
|
|
|
+
|
|
|
+ sync.reset(new message_filters::Synchronizer<ApproxSyncPolicy>(ApproxSyncPolicy(32), *odom_sub, *cloud_sub));
|
|
|
+ sync->registerCallback(boost::bind(&HdlGraphSlamNodelet::cloud_callback, this, _1, _2));
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ imu_sub = nh.subscribe("/gpsimu_driver/imu_data", 1024, &HdlGraphSlamNodelet::imu_callback, this);
|
|
|
+ floor_sub = nh.subscribe("/floor_detection/floor_coeffs", 1024, &HdlGraphSlamNodelet::floor_coeffs_callback, this);
|
|
|
+
|
|
|
+ if(private_nh.param<bool>("enable_gps", true)) {
|
|
|
+ gps_sub = mt_nh.subscribe("/gps/geopoint", 1024, &HdlGraphSlamNodelet::gps_callback, this);
|
|
|
+ nmea_sub = mt_nh.subscribe("/gpsimu_driver/nmea_sentence", 1024, &HdlGraphSlamNodelet::nmea_callback, this);
|
|
|
+ navsat_sub = mt_nh.subscribe("/gps/navsat", 1024, &HdlGraphSlamNodelet::navsat_callback, this);
|
|
|
}
|
|
|
|
|
|
|
|
|
- markers_pub = mt_nh.advertise<visualization_msgs::MarkerArray>("/hdl_graph_slam/markers", 16);
|
|
|
- odom2map_pub = mt_nh.advertise<geometry_msgs::TransformStamped>("/hdl_graph_slam/odom2pub", 16);
|
|
|
- map_points_pub = mt_nh.advertise<sensor_msgs::PointCloud2>("/hdl_graph_slam/map_points", 1, true);
|
|
|
- read_until_pub = mt_nh.advertise<std_msgs::Header>("/hdl_graph_slam/read_until", 32);
|
|
|
-
|
|
|
- load_service_server = mt_nh.advertiseService("/hdl_graph_slam/load", &HdlGraphSlamNodelet::load_service, this);
|
|
|
- dump_service_server = mt_nh.advertiseService("/hdl_graph_slam/dump", &HdlGraphSlamNodelet::dump_service, this);
|
|
|
+
|
|
|
+ markers_pub = mt_nh.advertise<visualization_msgs::MarkerArray>("/hdl_graph_slam/markers", 16);
|
|
|
+ odom2map_pub = mt_nh.advertise<geometry_msgs::TransformStamped>("/hdl_graph_slam/odom2pub", 16);
|
|
|
+ map_points_pub = mt_nh.advertise<sensor_msgs::PointCloud2>("/hdl_graph_slam/map_points", 1, true);
|
|
|
+ read_until_pub = mt_nh.advertise<std_msgs::Header>("/hdl_graph_slam/read_until", 32);
|
|
|
+
|
|
|
+
|
|
|
+ load_service_server = mt_nh.advertiseService("/hdl_graph_slam/load", &HdlGraphSlamNodelet::load_service, this);
|
|
|
+ dump_service_server = mt_nh.advertiseService("/hdl_graph_slam/dump", &HdlGraphSlamNodelet::dump_service, this);
|
|
|
save_map_service_server = mt_nh.advertiseService("/hdl_graph_slam/save_map", &HdlGraphSlamNodelet::save_map_service, this);
|
|
|
|
|
|
- graph_updated = false;
|
|
|
- double graph_update_interval = private_nh.param<double>("graph_update_interval", 3.0);
|
|
|
- double map_cloud_update_interval = private_nh.param<double>("map_cloud_update_interval", 10.0);
|
|
|
- optimization_timer = mt_nh.createWallTimer(ros::WallDuration(graph_update_interval), &HdlGraphSlamNodelet::optimization_timer_callback, this);
|
|
|
- map_publish_timer = mt_nh.createWallTimer(ros::WallDuration(map_cloud_update_interval), &HdlGraphSlamNodelet::map_points_publish_timer_callback, this);
|
|
|
+ graph_updated = false;
|
|
|
+ double graph_update_interval = private_nh.param<double>("graph_update_interval", 3.0);
|
|
|
+ double map_cloud_update_interval = private_nh.param<double>("map_cloud_update_interval", 10.0);
|
|
|
+ optimization_timer = mt_nh.createWallTimer(ros::WallDuration(graph_update_interval), &HdlGraphSlamNodelet::optimization_timer_callback, this);
|
|
|
+ map_publish_timer = mt_nh.createWallTimer(ros::WallDuration(map_cloud_update_interval), &HdlGraphSlamNodelet::map_points_publish_timer_callback, this);
|
|
|
+
|
|
|
+
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
|
|
|
* @brief received point clouds are pushed to #keyframe_queue
|
|
|
- * @param odom_msg
|
|
|
- * @param cloud_msg
|
|
|
+ * @param odom_msg
|
|
|
+ * @param cloud_msg
|
|
|
*/
|
|
|
void cloud_callback(const nav_msgs::OdometryConstPtr& odom_msg, const sensor_msgs::PointCloud2::ConstPtr& cloud_msg) {
|
|
|
- const ros::Time& stamp = cloud_msg->header.stamp;
|
|
|
- Eigen::Isometry3d odom = odom2isometry(odom_msg);
|
|
|
+ const ros::Time& stamp = cloud_msg->header.stamp;
|
|
|
+ Eigen::Isometry3d odom = odom2isometry(odom_msg);
|
|
|
|
|
|
- pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>());
|
|
|
- pcl::fromROSMsg(*cloud_msg, *cloud);
|
|
|
+ pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>());
|
|
|
+ pcl::fromROSMsg(*cloud_msg, *cloud);
|
|
|
if(base_frame_id.empty()) {
|
|
|
- base_frame_id = cloud_msg->header.frame_id;
|
|
|
- }
|
|
|
-
|
|
|
- if(!keyframe_updater->update(odom)) {
|
|
|
- std::lock_guard<std::mutex> lock(keyframe_queue_mutex);
|
|
|
- if(keyframe_queue.empty()) {
|
|
|
+ base_frame_id = cloud_msg->header.frame_id;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if(!keyframe_updater->update(odom)) {
|
|
|
+ std::lock_guard<std::mutex> lock(keyframe_queue_mutex);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if(keyframe_queue.empty()) {
|
|
|
std_msgs::Header read_until;
|
|
|
read_until.stamp = stamp + ros::Duration(10, 0);
|
|
|
read_until.frame_id = points_topic;
|
|
@@ -166,16 +187,19 @@ private:
|
|
|
read_until_pub.publish(read_until);
|
|
|
}
|
|
|
|
|
|
- return;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- double accum_d = keyframe_updater->get_accum_distance();
|
|
|
- KeyFrame::Ptr keyframe(new KeyFrame(stamp, odom, accum_d, cloud));
|
|
|
+ double accum_d = keyframe_updater->get_accum_distance();
|
|
|
+ KeyFrame::Ptr keyframe(new KeyFrame(stamp, odom, accum_d, cloud));
|
|
|
|
|
|
- std::lock_guard<std::mutex> lock(keyframe_queue_mutex);
|
|
|
- keyframe_queue.push_back(keyframe);
|
|
|
+ std::lock_guard<std::mutex> lock(keyframe_queue_mutex);
|
|
|
+ keyframe_queue.push_back(keyframe);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
* @brief this method adds all the keyframes in #keyframe_queue to the pose graph (odometry edges)
|
|
|
* @return if true, at least one keyframe was added to the pose graph
|
|
@@ -192,7 +216,7 @@ private:
|
|
|
Eigen::Isometry3d odom2map(trans_odom2map.cast<double>());
|
|
|
trans_odom2map_mutex.unlock();
|
|
|
|
|
|
- std::cout << "flush_keyframe_queue - keyframes len:"<< keyframes.size() << std::endl;
|
|
|
+ std::cout << "flush_keyframe_queue - keyframes len:" << keyframes.size() << std::endl;
|
|
|
int num_processed = 0;
|
|
|
for(int i = 0; i < std::min<int>(keyframe_queue.size(), max_keyframes_per_update); i++) {
|
|
|
num_processed = i;
|
|
@@ -208,7 +232,6 @@ private:
|
|
|
|
|
|
|
|
|
if(keyframes.empty() && new_keyframes.size() == 1) {
|
|
|
-
|
|
|
if(private_nh.param<bool>("fix_first_node", false)) {
|
|
|
Eigen::MatrixXd inf = Eigen::MatrixXd::Identity(6, 6);
|
|
|
std::stringstream sst(private_nh.param<std::string>("fix_first_node_stddev", "1 1 1 1 1 1"));
|
|
@@ -808,7 +831,6 @@ private:
|
|
|
return markers;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
|
|
|
* @brief load all data from a directory
|
|
|
* @param req
|
|
@@ -824,13 +846,12 @@ private:
|
|
|
|
|
|
|
|
|
graph_slam->load(directory + "/graph.g2o");
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
|
|
|
boost::filesystem::directory_iterator begin(directory), end;
|
|
|
- int max_directory_count = std::count_if(begin, end,
|
|
|
- [](const boost::filesystem::directory_entry & d) {
|
|
|
- return boost::filesystem::is_directory(d.path());
|
|
|
+ int max_directory_count = std::count_if(begin, end, [](const boost::filesystem::directory_entry& d) {
|
|
|
+ return boost::filesystem::is_directory(d.path());
|
|
|
});
|
|
|
|
|
|
|
|
@@ -847,8 +868,8 @@ private:
|
|
|
KeyFrame::Ptr keyframe(new KeyFrame(key_frame_directory, graph_slam->graph.get()));
|
|
|
keyframes.push_back(keyframe);
|
|
|
}
|
|
|
- std::cout << "loaded " << keyframes.size() << " keyframes" <<std::endl;
|
|
|
-
|
|
|
+ std::cout << "loaded " << keyframes.size() << " keyframes" << std::endl;
|
|
|
+
|
|
|
|
|
|
std::ifstream ifs(directory + "/special_nodes.csv");
|
|
|
if(!ifs) {
|
|
@@ -863,21 +884,21 @@ private:
|
|
|
anchor_node = static_cast<g2o::VertexSE3*>(graph_slam->graph->vertex(id));
|
|
|
} else if(token == "anchor_edge") {
|
|
|
int id = 0;
|
|
|
- ifs >> id;
|
|
|
+ ifs >> id;
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
- if(anchor_node){
|
|
|
+ if(anchor_node) {
|
|
|
auto edges = anchor_node->edges();
|
|
|
|
|
|
for(auto e : edges) {
|
|
|
- int edgeID = e->id();
|
|
|
- if (edgeID == id){
|
|
|
+ int edgeID = e->id();
|
|
|
+ if(edgeID == id) {
|
|
|
anchor_edge = static_cast<g2o::EdgeSE3*>(e);
|
|
|
|
|
|
break;
|
|
|
}
|
|
|
- }
|
|
|
+ }
|
|
|
}
|
|
|
} else if(token == "floor_node") {
|
|
|
int id = 0;
|
|
@@ -900,7 +921,7 @@ private:
|
|
|
if(floor_plane_node->id()) {
|
|
|
std::cout << " floor_node: " << floor_plane_node->id();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
std::cout << std::endl;
|
|
|
}
|
|
@@ -917,12 +938,11 @@ private:
|
|
|
|
|
|
res.success = true;
|
|
|
|
|
|
- std::cout << "snapshot updated" << std::endl << "loading successful" <<std::endl;
|
|
|
+ std::cout << "snapshot updated" << std::endl << "loading successful" << std::endl;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
|
|
|
* @brief dump all data to the current directory
|
|
|
* @param req
|
|
@@ -948,7 +968,7 @@ private:
|
|
|
}
|
|
|
|
|
|
std::cout << "dumping data to:" << directory << std::endl;
|
|
|
-
|
|
|
+
|
|
|
graph_slam->save(directory + "/graph.g2o");
|
|
|
|
|
|
|
|
@@ -1012,11 +1032,11 @@ private:
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-private:
|
|
|
+private:
|
|
|
|
|
|
- ros::NodeHandle nh;
|
|
|
- ros::NodeHandle mt_nh;
|
|
|
- ros::NodeHandle private_nh;
|
|
|
+ ros::NodeHandle nh;
|
|
|
+ ros::NodeHandle mt_nh;
|
|
|
+ ros::NodeHandle private_nh;
|
|
|
ros::WallTimer optimization_timer;
|
|
|
ros::WallTimer map_publish_timer;
|
|
|
|
|
@@ -1098,7 +1118,9 @@ private:
|
|
|
std::vector<KeyFrame::Ptr> keyframes;
|
|
|
std::unordered_map<ros::Time, KeyFrame::Ptr, RosTimeHash> keyframe_hash;
|
|
|
|
|
|
- std::unique_ptr<GraphSLAM> graph_slam;
|
|
|
+
|
|
|
+
|
|
|
+ std::unique_ptr<GraphSLAM> graph_slam;
|
|
|
std::unique_ptr<LoopDetector> loop_detector;
|
|
|
std::unique_ptr<KeyframeUpdater> keyframe_updater;
|
|
|
std::unique_ptr<NmeaSentenceParser> nmea_parser;
|