|
@@ -29,7 +29,6 @@ int main(int argc, char* argv[]) {
|
|
|
|
|
|
void hdl_backend::init() {
|
|
void hdl_backend::init() {
|
|
map_frame_id = "map";
|
|
map_frame_id = "map";
|
|
- // map_frame_id = "odom";
|
|
|
|
odom_frame_id = "odom";
|
|
odom_frame_id = "odom";
|
|
map_cloud_resolution = 0.05; // 地图点云分辨率
|
|
map_cloud_resolution = 0.05; // 地图点云分辨率
|
|
trans_odom2map.setIdentity(); // 设置为单位矩阵
|
|
trans_odom2map.setIdentity(); // 设置为单位矩阵
|
|
@@ -55,11 +54,11 @@ void hdl_backend::init() {
|
|
|
|
|
|
read_until_pub = nh.advertise<std_msgs::Header>("/hdl_graph_slam/read_until", 32); // 这种类型的消息通常用于指示读取操作应该持续到何时
|
|
read_until_pub = nh.advertise<std_msgs::Header>("/hdl_graph_slam/read_until", 32); // 这种类型的消息通常用于指示读取操作应该持续到何时
|
|
|
|
|
|
- save_map_service_server = nh.advertiseService("/hdl_graph_slam/save_map", &hdl_backend::save_map_service, this); // 保存地图
|
|
|
|
|
|
+ // save_map_service_server = nh.advertiseService("/hdl_graph_slam/save_map", &hdl_backend::save_map_service, this); // 保存地图
|
|
|
|
|
|
graph_updated = false;
|
|
graph_updated = false;
|
|
|
|
|
|
- double graph_update_interval = 1; // 表示图优化更新的时间间隔,默认值为3.0秒 interval:间隔
|
|
|
|
|
|
+ double graph_update_interval = 1; // 表示图优化更新的时间间隔,默认值为3.0秒 interval:间隔
|
|
double map_cloud_update_interval = 4; // 地图点云更新的时间间隔,默认值为10.0秒
|
|
double map_cloud_update_interval = 4; // 地图点云更新的时间间隔,默认值为10.0秒
|
|
|
|
|
|
optimization_timer = nh.createWallTimer(ros::WallDuration(graph_update_interval), &hdl_backend::optimization_timer_callback, this); // 创建一个定时器,定时器会在每个 graph_update_interval 秒后触发,然后调用 optimization_timer_callback 函数
|
|
optimization_timer = nh.createWallTimer(ros::WallDuration(graph_update_interval), &hdl_backend::optimization_timer_callback, this); // 创建一个定时器,定时器会在每个 graph_update_interval 秒后触发,然后调用 optimization_timer_callback 函数
|
|
@@ -75,13 +74,12 @@ void hdl_backend::init() {
|
|
* @param event // 是ROS提供的定时器时间类,包含信息有:计时器出发的时间戳、上一次出发的时间戳、计时器的周期信息等
|
|
* @param event // 是ROS提供的定时器时间类,包含信息有:计时器出发的时间戳、上一次出发的时间戳、计时器的周期信息等
|
|
*/
|
|
*/
|
|
void hdl_backend::optimization_timer_callback(const ros::WallTimerEvent& event) {
|
|
void hdl_backend::optimization_timer_callback(const ros::WallTimerEvent& event) {
|
|
- LOG(INFO) << "optimization_timer_callback";
|
|
|
|
|
|
+ LOG(INFO) << "进入optimization_timer_callback函数";
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(main_thread_mutex); // 创建了一个锁,用于保护对主线程的访问,确保线程安全
|
|
std::lock_guard<std::mutex> lock(main_thread_mutex); // 创建了一个锁,用于保护对主线程的访问,确保线程安全
|
|
|
|
|
|
// add keyframes and floor coeffs in the queues to the pose graph 将队列中的关键帧和楼层系数添加到姿势图中
|
|
// add keyframes and floor coeffs in the queues to the pose graph 将队列中的关键帧和楼层系数添加到姿势图中
|
|
bool keyframe_updated = flush_keyframe_queue(); // 调用 flush_keyframe_queue 函数,将关键帧队列中的数据添加到位姿图中,并返回是否有关键帧被更新
|
|
bool keyframe_updated = flush_keyframe_queue(); // 调用 flush_keyframe_queue 函数,将关键帧队列中的数据添加到位姿图中,并返回是否有关键帧被更新
|
|
- // LOG(INFO) << keyframe_updated;
|
|
|
|
|
|
|
|
if(!keyframe_updated) { // 如果没有关键帧被更新,则执行大括号内的代码
|
|
if(!keyframe_updated) { // 如果没有关键帧被更新,则执行大括号内的代码
|
|
std_msgs::Header read_until;
|
|
std_msgs::Header read_until;
|
|
@@ -93,9 +91,11 @@ void hdl_backend::optimization_timer_callback(const ros::WallTimerEvent& event)
|
|
}
|
|
}
|
|
if(!keyframe_updated) {
|
|
if(!keyframe_updated) {
|
|
// 检查地板、GPS、IMU等队列,分别调用相应的 flush 函数(例如 flush_floor_queue、flush_gps_queue、flush_imu_queue),如果这些数据没有更新,则函数直接返回,不进行后续操作。
|
|
// 检查地板、GPS、IMU等队列,分别调用相应的 flush 函数(例如 flush_floor_queue、flush_gps_queue、flush_imu_queue),如果这些数据没有更新,则函数直接返回,不进行后续操作。
|
|
- LOG(INFO) << "!keyframe_updated";
|
|
|
|
|
|
+ LOG(INFO) << "关键帧没有更新,退出optimization_timer_callback函数";
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+
|
|
// loop detection 闭环检测
|
|
// loop detection 闭环检测
|
|
std::vector<hdl_graph_slam::Loop::Ptr> loops = loop_detector->detect(keyframes, new_keyframes, *graph_slam);
|
|
std::vector<hdl_graph_slam::Loop::Ptr> loops = loop_detector->detect(keyframes, new_keyframes, *graph_slam);
|
|
|
|
|
|
@@ -106,8 +106,7 @@ void hdl_backend::optimization_timer_callback(const ros::WallTimerEvent& event)
|
|
auto edge = graph_slam->add_se3_edge(loop->key1->node, loop->key2->node, relpose, information_matrix);
|
|
auto edge = graph_slam->add_se3_edge(loop->key1->node, loop->key2->node, relpose, information_matrix);
|
|
graph_slam->add_robust_kernel(edge, "Huber", 1.0);
|
|
graph_slam->add_robust_kernel(edge, "Huber", 1.0);
|
|
}
|
|
}
|
|
- LOG(INFO) << "!111111111";
|
|
|
|
-
|
|
|
|
|
|
+ LOG(INFO) << "完成闭环检测";
|
|
|
|
|
|
std::copy(new_keyframes.begin(), new_keyframes.end(), std::back_inserter(keyframes)); // 将新关键帧 new_keyframes 合并到已有的关键帧列表 keyframes 中
|
|
std::copy(new_keyframes.begin(), new_keyframes.end(), std::back_inserter(keyframes)); // 将新关键帧 new_keyframes 合并到已有的关键帧列表 keyframes 中
|
|
new_keyframes.clear(); // 清空 new_keyframes
|
|
new_keyframes.clear(); // 清空 new_keyframes
|
|
@@ -115,45 +114,46 @@ void hdl_backend::optimization_timer_callback(const ros::WallTimerEvent& event)
|
|
// move the first node anchor position to the current estimate of the first node pose 将第一节点锚点位置移动到第一节点姿态的当前估计值
|
|
// move the first node anchor position to the current estimate of the first node pose 将第一节点锚点位置移动到第一节点姿态的当前估计值
|
|
// so the first node moves freely while trying to stay around the origin 因此,第一个节点在试图停留在原点附近的同时可以自由移动
|
|
// so the first node moves freely while trying to stay around the origin 因此,第一个节点在试图停留在原点附近的同时可以自由移动
|
|
|
|
|
|
- if(anchor_node && true) { // launch文件中,fix_first_node_adaptive 设置为 true
|
|
|
|
|
|
+ if(anchor_node && true) { // launch文件中,fix_first_node_adaptive 设置为 true // 这个能不能设置为false
|
|
Eigen::Isometry3d anchor_target = static_cast<g2o::VertexSE3*>(anchor_edge->vertices()[1])->estimate();
|
|
Eigen::Isometry3d anchor_target = static_cast<g2o::VertexSE3*>(anchor_edge->vertices()[1])->estimate();
|
|
anchor_node->setEstimate(anchor_target);
|
|
anchor_node->setEstimate(anchor_target);
|
|
// 如果启用了自适应固定第一帧的功能(参数 "fix_first_node_adaptive"),则将第一个关键帧的锚点位置更新为当前估计的位置,以允许它自由移动但仍然保持在原点附近。
|
|
// 如果启用了自适应固定第一帧的功能(参数 "fix_first_node_adaptive"),则将第一个关键帧的锚点位置更新为当前估计的位置,以允许它自由移动但仍然保持在原点附近。
|
|
}
|
|
}
|
|
- LOG(INFO) << "!2222222222";
|
|
|
|
// optimize the pose graph
|
|
// optimize the pose graph
|
|
int num_iterations = 512; // launch文件中都是设置成512
|
|
int num_iterations = 512; // launch文件中都是设置成512
|
|
graph_slam->optimize(num_iterations); // 使用 g2o 优化器对位姿图进行优化,优化的迭代次数由参数 "g2o_solver_num_iterations" 控制
|
|
graph_slam->optimize(num_iterations); // 使用 g2o 优化器对位姿图进行优化,优化的迭代次数由参数 "g2o_solver_num_iterations" 控制
|
|
-
|
|
|
|
|
|
+ LOG(INFO) << "完成g2o的图优化";
|
|
|
|
|
|
std::vector<hdl_graph_slam::KeyFrameSnapshot::Ptr> snapshot(keyframes.size());
|
|
std::vector<hdl_graph_slam::KeyFrameSnapshot::Ptr> snapshot(keyframes.size());
|
|
std::transform(keyframes.begin(), keyframes.end(), snapshot.begin(), [=](const hdl_graph_slam::KeyFrame::Ptr& k) { return std::make_shared<hdl_graph_slam::KeyFrameSnapshot>(k); });
|
|
std::transform(keyframes.begin(), keyframes.end(), snapshot.begin(), [=](const hdl_graph_slam::KeyFrame::Ptr& k) { return std::make_shared<hdl_graph_slam::KeyFrameSnapshot>(k); });
|
|
|
|
|
|
- keyframes_snapshot_mutex.lock();
|
|
|
|
|
|
+ // keyframes_snapshot_mutex.lock();
|
|
keyframes_snapshot.swap(snapshot);
|
|
keyframes_snapshot.swap(snapshot);
|
|
- keyframes_snapshot_mutex.unlock();
|
|
|
|
|
|
+ // keyframes_snapshot_mutex.unlock();
|
|
graph_updated = true;
|
|
graph_updated = true;
|
|
- LOG(INFO) << "graph_updated";
|
|
|
|
|
|
+ LOG(INFO) << "准备发布TF变换";
|
|
|
|
|
|
// publish tf 发布位姿变换
|
|
// publish tf 发布位姿变换
|
|
const auto& keyframe = keyframes.back(); // 获取最新的关键帧估计
|
|
const auto& keyframe = keyframes.back(); // 获取最新的关键帧估计
|
|
Eigen::Isometry3d trans = keyframe->node->estimate() * keyframe->odom.inverse();
|
|
Eigen::Isometry3d trans = keyframe->node->estimate() * keyframe->odom.inverse();
|
|
- trans_odom2map_mutex.lock();
|
|
|
|
|
|
+ // trans_odom2map_mutex.lock();
|
|
trans_odom2map = trans.matrix().cast<float>();
|
|
trans_odom2map = trans.matrix().cast<float>();
|
|
- trans_odom2map_mutex.unlock();
|
|
|
|
|
|
+ // trans_odom2map_mutex.unlock();
|
|
|
|
|
|
- if(odom2map_pub.getNumSubscribers()) {
|
|
|
|
- geometry_msgs::TransformStamped ts = hdl_graph_slam::matrix2transform(keyframe->stamp, trans.matrix().cast<float>(), map_frame_id, odom_frame_id);
|
|
|
|
- odom2map_pub.publish(ts); // 发布odom2map_pub话题中
|
|
|
|
- LOG(INFO) << "pub odom2map_pub";
|
|
|
|
- }
|
|
|
|
|
|
+ // if(odom2map_pub.getNumSubscribers()) {
|
|
|
|
+ geometry_msgs::TransformStamped ts = hdl_graph_slam::matrix2transform(keyframe->stamp, trans.matrix().cast<float>(), map_frame_id, odom_frame_id);
|
|
|
|
+ // ts.header.stamp = ros::Time::now(); // ?
|
|
|
|
+ // odom2map_pub.publish(ts); // 发布odom2map_pub话题中/
|
|
|
|
+ LOG(INFO) << "odom2map TF变换发布完成";
|
|
|
|
+ // }
|
|
|
|
|
|
if(markers_pub.getNumSubscribers()) {
|
|
if(markers_pub.getNumSubscribers()) {
|
|
auto markers = create_marker_array(ros::Time::now());
|
|
auto markers = create_marker_array(ros::Time::now());
|
|
markers_pub.publish(markers);
|
|
markers_pub.publish(markers);
|
|
- LOG(INFO) << "pub markers_pub";
|
|
|
|
|
|
+ LOG(INFO) << "markers_pub 发布完成";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -163,36 +163,57 @@ void hdl_backend::optimization_timer_callback(const ros::WallTimerEvent& event)
|
|
void hdl_backend::map_points_publish_timer_callback(const ros::WallTimerEvent& event) {
|
|
void hdl_backend::map_points_publish_timer_callback(const ros::WallTimerEvent& event) {
|
|
// LOG(INFO) << "map_points_publish_timer_callback";
|
|
// LOG(INFO) << "map_points_publish_timer_callback";
|
|
|
|
|
|
- if(!map_points_pub.getNumSubscribers() || !graph_updated) {
|
|
|
|
- // LOG(INFO) << "cloud 222222";
|
|
|
|
|
|
+ // if(!map_points_pub.getNumSubscribers() || !graph_updated) {
|
|
|
|
+ // // LOG(INFO) << "cloud 222222";
|
|
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- // LOG(INFO) << "cloud 33333";
|
|
|
|
|
|
+ // return;
|
|
|
|
+ // }
|
|
|
|
+ // // LOG(INFO) << "cloud 33333";
|
|
|
|
|
|
- std::vector<hdl_graph_slam::KeyFrameSnapshot::Ptr> snapshot;
|
|
|
|
|
|
+ // std::vector<hdl_graph_slam::KeyFrameSnapshot::Ptr> snapshot;
|
|
|
|
|
|
- // keyframes_snapshot_mutex.lock();
|
|
|
|
- snapshot = keyframes_snapshot;
|
|
|
|
- // keyframes_snapshot_mutex.unlock();
|
|
|
|
|
|
+ // // keyframes_snapshot_mutex.lock();
|
|
|
|
+ // snapshot = keyframes_snapshot;
|
|
|
|
+ // // keyframes_snapshot_mutex.unlock();
|
|
|
|
+
|
|
|
|
+ // auto cloud = map_cloud_generator->generate(snapshot, map_cloud_resolution);
|
|
|
|
+ // if(!cloud) {
|
|
|
|
+ // return;
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ // cloud->header.frame_id = map_frame_id;
|
|
|
|
+ // cloud->header.stamp = snapshot.back()->cloud->header.stamp;
|
|
|
|
+
|
|
|
|
+ // sensor_msgs::PointCloud2Ptr cloud_msg(new sensor_msgs::PointCloud2());
|
|
|
|
+ // pcl::toROSMsg(*cloud, *cloud_msg);
|
|
|
|
+
|
|
|
|
+ // // cloud->header.frame_id = "odom"; //
|
|
|
|
+ // LOG(INFO) << cloud_msg->header.frame_id;
|
|
|
|
+
|
|
|
|
+ // map_points_pub.publish(*cloud_msg);
|
|
|
|
+ // cloud->header.frame_id = "map"; //
|
|
|
|
|
|
|
|
+ // 发布点云数据
|
|
|
|
+ LOG(INFO) << "准备发布点云数据";
|
|
|
|
+ std::vector<hdl_graph_slam::KeyFrameSnapshot::Ptr> snapshot;
|
|
|
|
+ snapshot = keyframes_snapshot;
|
|
auto cloud = map_cloud_generator->generate(snapshot, map_cloud_resolution);
|
|
auto cloud = map_cloud_generator->generate(snapshot, map_cloud_resolution);
|
|
|
|
+ LOG(INFO) << "获得点云数据";
|
|
if(!cloud) {
|
|
if(!cloud) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
cloud->header.frame_id = map_frame_id;
|
|
cloud->header.frame_id = map_frame_id;
|
|
cloud->header.stamp = snapshot.back()->cloud->header.stamp;
|
|
cloud->header.stamp = snapshot.back()->cloud->header.stamp;
|
|
|
|
|
|
sensor_msgs::PointCloud2Ptr cloud_msg(new sensor_msgs::PointCloud2());
|
|
sensor_msgs::PointCloud2Ptr cloud_msg(new sensor_msgs::PointCloud2());
|
|
pcl::toROSMsg(*cloud, *cloud_msg);
|
|
pcl::toROSMsg(*cloud, *cloud_msg);
|
|
|
|
|
|
- // cloud->header.frame_id = "odom"; //
|
|
|
|
- LOG(INFO) << cloud_msg->header.frame_id;
|
|
|
|
-
|
|
|
|
map_points_pub.publish(*cloud_msg);
|
|
map_points_pub.publish(*cloud_msg);
|
|
- // cloud->header.frame_id = "map"; //
|
|
|
|
-}
|
|
|
|
|
|
+ LOG(INFO) << "点云数据发布完成";
|
|
|
|
+
|
|
|
|
+ LOG(INFO) << "map_points_publish_timer_callback 函数执行完毕";
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
|
|
/**
|
|
/**
|
|
* @brief 接收点云数据并放到 keyframe_queue 中
|
|
* @brief 接收点云数据并放到 keyframe_queue 中
|
|
@@ -211,7 +232,7 @@ void hdl_backend::cloud_callback(const nav_msgs::OdometryConstPtr& odom_msg, con
|
|
}
|
|
}
|
|
|
|
|
|
// 更新关键帧判断
|
|
// 更新关键帧判断
|
|
- if(!keyframe_updater->update(odom)) { // 根据当前的里程计信息 odom 判断是否需要生成新的关键帧
|
|
|
|
|
|
+ if(!keyframe_updater->update(odom)) { // 根据当前的里程计信息 odom 判断是否需要生成新的关键帧
|
|
// LOG(INFO) << "!keyframe_updater->update(odom)";
|
|
// LOG(INFO) << "!keyframe_updater->update(odom)";
|
|
std::lock_guard<std::mutex> lock(keyframe_queue_mutex); // 这行代码的作用是确保线程安全,防止多个线程同时访问或修改 keyframe_queue 队列
|
|
std::lock_guard<std::mutex> lock(keyframe_queue_mutex); // 这行代码的作用是确保线程安全,防止多个线程同时访问或修改 keyframe_queue 队列
|
|
// std::lock_guard<std::mutex> 是 C++ 标准库中的一个类,用来简化互斥锁(std::mutex)的使用。它的作用是在其作用域内自动对给定的互斥量加锁,并在作用域结束时自动解锁
|
|
// std::lock_guard<std::mutex> 是 C++ 标准库中的一个类,用来简化互斥锁(std::mutex)的使用。它的作用是在其作用域内自动对给定的互斥量加锁,并在作用域结束时自动解锁
|