|
@@ -60,6 +60,20 @@
|
|
|
#include <g2o/edge_se3_priorvec.hpp>
|
|
|
#include <g2o/edge_se3_priorquat.hpp>
|
|
|
|
|
|
+
|
|
|
+后端的作用(GPT):
|
|
|
+1. 构建位资图:
|
|
|
+ 后端根据前端生成的关键帧位姿和相对位姿变换,构建位姿图(Pose Graph),每个关键帧对应图中的一个节点,节点之间的相对运动构成边。
|
|
|
+
|
|
|
+2. 闭环检测
|
|
|
+ 后端通过检测闭环(即机器人经过某个已经访问过的地方)来减少累积误差。闭环检测通过计算当前帧和之前帧之间的相似度来发现闭环位置,并将其加入位姿图中,形成闭环约束边。
|
|
|
+
|
|
|
+3. 图优化
|
|
|
+ 利用优化算法(如g2o或Ceres)对整个位姿图进行优化。目标是调整位姿图中所有关键帧的位姿,
|
|
|
+ 使之符合所有的约束(包括相邻关键帧的相对位姿约束以及闭环检测生成的约束),从而减少传感器噪声和漂移,生成全局一致的地图
|
|
|
+
|
|
|
+ */
|
|
|
+
|
|
|
namespace hdl_graph_slam {
|
|
|
|
|
|
class HdlGraphSlamNodelet : public nodelet::Nodelet {
|
|
@@ -110,12 +124,29 @@ public:
|
|
|
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");
|
|
|
+
|
|
|
+
|
|
|
+ (slam_env) mj@mj-Lenovo-Legion-R9000P2021H:~$ rostopic info /velodyne_points
|
|
|
+ Type: sensor_msgs/PointCloud2
|
|
|
+
|
|
|
+ Publishers:
|
|
|
+ * /player (http:
|
|
|
+ 这个发布方是 rosbag 呀,是没处理过的点云信息,后端怎么会订阅没处理过的点云数据?
|
|
|
+ 也不是用来订阅的,这个文件是用这个话题来发布的,但是发布的内容又只有时间戳和frameid? 为什么这么奇怪?
|
|
|
+ 结合 read_until_pub 的解释来看,似乎是用来指示操作应该持续到何时,所以这个应该不用管
|
|
|
+
|
|
|
+ Subscribers:
|
|
|
+ * /velodyne_nodelet_manager (http:
|
|
|
+ * /rviz_1729770290778318983 (http:
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
@@ -137,21 +168,24 @@ public:
|
|
|
|
|
|
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);
|
|
|
+ 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);
|
|
|
+ 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);
|
|
|
|
|
|
- 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:
|
|
@@ -161,23 +195,25 @@ private:
|
|
|
* @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::fromROSMsg(*cloud_msg, *cloud);
|
|
|
if(base_frame_id.empty()) {
|
|
|
- base_frame_id = cloud_msg->header.frame_id;
|
|
|
- }
|
|
|
+ base_frame_id = cloud_msg->header.frame_id;
|
|
|
+ }
|
|
|
|
|
|
|
|
|
- if(!keyframe_updater->update(odom)) {
|
|
|
- std::lock_guard<std::mutex> lock(keyframe_queue_mutex);
|
|
|
+ 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);
|
|
@@ -187,28 +223,27 @@ 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));
|
|
|
|
|
|
- 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
|
|
|
+ * @brief this method adds all the keyframes in #keyframe_queue to the pose graph (odometry edges) 此方法将#keyframe_queue中的所有关键帧添加到姿势图中(里程计边)
|
|
|
+ * @return if true, at least one keyframe was added to the pose graph 如果为真,则至少有一个关键帧被添加到姿势图中
|
|
|
* 将队列中的关键帧添加到位姿图中,并返回是否至少有一个关键帧被添加
|
|
|
+ *
|
|
|
+ * pose graph 是什么?
|
|
|
*/
|
|
|
bool flush_keyframe_queue() {
|
|
|
- std::lock_guard<std::mutex> lock(keyframe_queue_mutex);
|
|
|
+ std::lock_guard<std::mutex> lock(keyframe_queue_mutex);
|
|
|
|
|
|
- if(keyframe_queue.empty()) {
|
|
|
+ if(keyframe_queue.empty()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -473,7 +508,7 @@ private:
|
|
|
return updated;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
* @brief received floor coefficients are added to #floor_coeffs_queue
|
|
|
* @param floor_coeffs_msg
|
|
|
*/
|
|
@@ -486,9 +521,11 @@ private:
|
|
|
floor_coeffs_queue.push_back(floor_coeffs_msg);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
* @brief this methods associates floor coefficients messages with registered keyframes, and then adds the associated coeffs to the pose graph
|
|
|
+ * @brief 该方法将地板系数消息与注册的关键帧相关联,然后将关联的系数添加到姿势图中。
|
|
|
* @return if true, at least one floor plane edge is added to the pose graph
|
|
|
+ * @return 如果为真,则至少有一个地板平面边被添加到姿势图中
|
|
|
*/
|
|
|
bool flush_floor_queue() {
|
|
|
std::lock_guard<std::mutex> lock(floor_coeffs_queue_mutex);
|
|
@@ -564,52 +601,71 @@ private:
|
|
|
|
|
|
|
|
|
* @brief this methods adds all the data in the queues to the pose graph, and then optimizes the pose graph
|
|
|
- * @param event
|
|
|
+ * @brief 此方法将队列中的所有数据添加到姿势图中,然后优化姿势图
|
|
|
+ * @param event
|
|
|
*/
|
|
|
void optimization_timer_callback(const ros::WallTimerEvent& event) {
|
|
|
- std::lock_guard<std::mutex> lock(main_thread_mutex);
|
|
|
+ std::lock_guard<std::mutex> lock(main_thread_mutex);
|
|
|
|
|
|
-
|
|
|
- bool keyframe_updated = flush_keyframe_queue();
|
|
|
+
|
|
|
+ bool keyframe_updated = flush_keyframe_queue();
|
|
|
|
|
|
- if(!keyframe_updated) {
|
|
|
+ if(!keyframe_updated) {
|
|
|
std_msgs::Header read_until;
|
|
|
- read_until.stamp = ros::Time::now() + ros::Duration(30, 0);
|
|
|
+ read_until.stamp = ros::Time::now() + ros::Duration(30, 0);
|
|
|
read_until.frame_id = points_topic;
|
|
|
read_until_pub.publish(read_until);
|
|
|
read_until.frame_id = "/filtered_points";
|
|
|
- read_until_pub.publish(read_until);
|
|
|
+ read_until_pub.publish(read_until);
|
|
|
}
|
|
|
|
|
|
if(!keyframe_updated & !flush_floor_queue() & !flush_gps_queue() & !flush_imu_queue()) {
|
|
|
+
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
std::vector<Loop::Ptr> loops = loop_detector->detect(keyframes, new_keyframes, *graph_slam);
|
|
|
- for(const auto& loop : loops) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ for(const auto& loop : loops) {
|
|
|
Eigen::Isometry3d relpose(loop->relative_pose.cast<double>());
|
|
|
Eigen::MatrixXd information_matrix = inf_calclator->calc_information_matrix(loop->key1->cloud, loop->key2->cloud, relpose);
|
|
|
auto edge = graph_slam->add_se3_edge(loop->key1->node, loop->key2->node, relpose, information_matrix);
|
|
|
graph_slam->add_robust_kernel(edge, private_nh.param<std::string>("loop_closure_edge_robust_kernel", "NONE"), private_nh.param<double>("loop_closure_edge_robust_kernel_size", 1.0));
|
|
|
}
|
|
|
|
|
|
- std::copy(new_keyframes.begin(), new_keyframes.end(), std::back_inserter(keyframes));
|
|
|
- new_keyframes.clear();
|
|
|
+ std::copy(new_keyframes.begin(), new_keyframes.end(), std::back_inserter(keyframes));
|
|
|
+ new_keyframes.clear();
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- if(anchor_node && private_nh.param<bool>("fix_first_node_adaptive", true)) {
|
|
|
+ if(anchor_node && private_nh.param<bool>("fix_first_node_adaptive", true)) {
|
|
|
Eigen::Isometry3d anchor_target = static_cast<g2o::VertexSE3*>(anchor_edge->vertices()[1])->estimate();
|
|
|
anchor_node->setEstimate(anchor_target);
|
|
|
+
|
|
|
}
|
|
|
|
|
|
|
|
|
- int num_iterations = private_nh.param<int>("g2o_solver_num_iterations", 1024);
|
|
|
- graph_slam->optimize(num_iterations);
|
|
|
+ int num_iterations = private_nh.param<int>("g2o_solver_num_iterations", 1024);
|
|
|
+ graph_slam->optimize(num_iterations);
|
|
|
|
|
|
-
|
|
|
- const auto& keyframe = keyframes.back();
|
|
|
+
|
|
|
+ const auto& keyframe = keyframes.back();
|
|
|
Eigen::Isometry3d trans = keyframe->node->estimate() * keyframe->odom.inverse();
|
|
|
trans_odom2map_mutex.lock();
|
|
|
trans_odom2map = trans.matrix().cast<float>();
|
|
@@ -625,7 +681,7 @@ private:
|
|
|
|
|
|
if(odom2map_pub.getNumSubscribers()) {
|
|
|
geometry_msgs::TransformStamped ts = matrix2transform(keyframe->stamp, trans.matrix().cast<float>(), map_frame_id, odom_frame_id);
|
|
|
- odom2map_pub.publish(ts);
|
|
|
+ odom2map_pub.publish(ts);
|
|
|
}
|
|
|
|
|
|
if(markers_pub.getNumSubscribers()) {
|
|
@@ -1047,7 +1103,6 @@ private:
|
|
|
ros::Subscriber gps_sub;
|
|
|
ros::Subscriber nmea_sub;
|
|
|
ros::Subscriber navsat_sub;
|
|
|
-
|
|
|
ros::Subscriber imu_sub;
|
|
|
ros::Subscriber floor_sub;
|
|
|
|
|
@@ -1057,24 +1112,24 @@ private:
|
|
|
std::string map_frame_id;
|
|
|
std::string odom_frame_id;
|
|
|
|
|
|
- std::mutex trans_odom2map_mutex;
|
|
|
- Eigen::Matrix4f trans_odom2map;
|
|
|
- ros::Publisher odom2map_pub;
|
|
|
+ std::mutex trans_odom2map_mutex;
|
|
|
+ Eigen::Matrix4f trans_odom2map;
|
|
|
+ ros::Publisher odom2map_pub;
|
|
|
|
|
|
- std::string points_topic;
|
|
|
+ std::string points_topic;
|
|
|
ros::Publisher read_until_pub;
|
|
|
ros::Publisher map_points_pub;
|
|
|
|
|
|
- tf::TransformListener tf_listener;
|
|
|
+ tf::TransformListener tf_listener;
|
|
|
|
|
|
- ros::ServiceServer load_service_server;
|
|
|
- ros::ServiceServer dump_service_server;
|
|
|
- ros::ServiceServer save_map_service_server;
|
|
|
+ ros::ServiceServer load_service_server;
|
|
|
+ ros::ServiceServer dump_service_server;
|
|
|
+ ros::ServiceServer save_map_service_server;
|
|
|
|
|
|
|
|
|
std::string base_frame_id;
|
|
|
- std::mutex keyframe_queue_mutex;
|
|
|
- std::deque<KeyFrame::Ptr> keyframe_queue;
|
|
|
+ std::mutex keyframe_queue_mutex;
|
|
|
+ std::deque<KeyFrame::Ptr> keyframe_queue;
|
|
|
|
|
|
|
|
|
double gps_time_offset;
|
|
@@ -1085,13 +1140,13 @@ private:
|
|
|
std::deque<geographic_msgs::GeoPointStampedConstPtr> gps_queue;
|
|
|
|
|
|
|
|
|
- double imu_time_offset;
|
|
|
- bool enable_imu_orientation;
|
|
|
- double imu_orientation_edge_stddev;
|
|
|
- bool enable_imu_acceleration;
|
|
|
- double imu_acceleration_edge_stddev;
|
|
|
- std::mutex imu_queue_mutex;
|
|
|
- std::deque<sensor_msgs::ImuConstPtr> imu_queue;
|
|
|
+ double imu_time_offset;
|
|
|
+ bool enable_imu_orientation;
|
|
|
+ double imu_orientation_edge_stddev;
|
|
|
+ bool enable_imu_acceleration;
|
|
|
+ double imu_acceleration_edge_stddev;
|
|
|
+ std::mutex imu_queue_mutex;
|
|
|
+ std::deque<sensor_msgs::ImuConstPtr> imu_queue;
|
|
|
|
|
|
|
|
|
double floor_edge_stddev;
|
|
@@ -1099,24 +1154,28 @@ private:
|
|
|
std::deque<hdl_graph_slam::FloorCoeffsConstPtr> floor_coeffs_queue;
|
|
|
|
|
|
|
|
|
- std::atomic_bool graph_updated;
|
|
|
- double map_cloud_resolution;
|
|
|
- std::mutex keyframes_snapshot_mutex;
|
|
|
- std::vector<KeyFrameSnapshot::Ptr> keyframes_snapshot;
|
|
|
- std::unique_ptr<MapCloudGenerator> map_cloud_generator;
|
|
|
+ std::atomic_bool graph_updated;
|
|
|
+ double map_cloud_resolution;
|
|
|
+ std::mutex keyframes_snapshot_mutex;
|
|
|
+ std::vector<KeyFrameSnapshot::Ptr> keyframes_snapshot;
|
|
|
+ std::unique_ptr<MapCloudGenerator> map_cloud_generator;
|
|
|
|
|
|
|
|
|
|
|
|
- std::mutex main_thread_mutex;
|
|
|
-
|
|
|
- int max_keyframes_per_update;
|
|
|
- std::deque<KeyFrame::Ptr> new_keyframes;
|
|
|
-
|
|
|
- g2o::VertexSE3* anchor_node;
|
|
|
- g2o::EdgeSE3* anchor_edge;
|
|
|
- g2o::VertexPlane* floor_plane_node;
|
|
|
- std::vector<KeyFrame::Ptr> keyframes;
|
|
|
- std::unordered_map<ros::Time, KeyFrame::Ptr, RosTimeHash> keyframe_hash;
|
|
|
+ std::mutex main_thread_mutex;
|
|
|
+
|
|
|
+ int max_keyframes_per_update;
|
|
|
+ std::deque<KeyFrame::Ptr> new_keyframes;
|
|
|
+
|
|
|
+ g2o::VertexSE3* anchor_node;
|
|
|
+
|
|
|
+ g2o::EdgeSE3* anchor_edge;
|
|
|
+
|
|
|
+ g2o::VertexPlane* floor_plane_node;
|
|
|
+ std::vector<KeyFrame::Ptr> keyframes;
|
|
|
+ std::unordered_map<ros::Time, KeyFrame::Ptr, RosTimeHash> keyframe_hash;
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
|