#include "lidar_fusion_node.h" #include "time_sync.h" #include "lidarheart.h" // 读取雷达状态 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // 可视化预测圆柱 #include "visualization.h" // 记录最近一次有效拟合的时间戳与延迟 namespace { std::atomic s_last_c1_stamp_sec{0.0}; std::atomic s_last_c1_delay_sec{0.0}; std::atomic s_last_c2_stamp_sec{0.0}; std::atomic s_last_c2_delay_sec{0.0}; } LidarFusionNode::LidarFusionNode(ros::NodeHandle &pnh, bool wait_for_master, double master_timeout_sec) : pnh_(pnh), c1_thread_running_(true), c2_thread_running_(true), c1_thread_pool_(4), c2_thread_pool_(4), c1_fit_pool_(2), c2_fit_pool_(2), c1_fitter(loadLidarFusionParams(pnh)), c2_fitter(loadLidarFusionParams(pnh)), c1_start_time_(0), c2_start_time_(0) { // 首次加载全部参数 params_ = loadLidarFusionParams(pnh_); // 构建两组雷达的预测器 c1_predictor_ = std::make_unique(); c2_predictor_ = std::make_unique(); if (params_.kf_init_center_speed.size() == 3) { Eigen::Vector3d v0(params_.kf_init_center_speed[0], params_.kf_init_center_speed[1], params_.kf_init_center_speed[2]); c1_predictor_->setInitCenterVelocity(v0); c2_predictor_->setInitCenterVelocity(v0); } c1_predictor_->setAccelThreshold(params_.accel_threshold_mps2); c1_predictor_->setSpeedThreshold(params_.speed_threshold_mps); c1_predictor_->setForceUpdateInterval(params_.force_update_interval_s); c2_predictor_->setAccelThreshold(params_.accel_threshold_mps2); c2_predictor_->setSpeedThreshold(params_.speed_threshold_mps); c2_predictor_->setForceUpdateInterval(params_.force_update_interval_s); // 区分 C1 / C2 拟合器来源,后续可用于选择不同可视化话题 c1_fitter.setSourceId(1); c2_fitter.setSourceId(2); wait_for_master_ = wait_for_master; master_timeout_sec_ = master_timeout_sec; if (wait_for_master_) { waitForMaster(master_timeout_sec_); } std::vector C1T2_to_1_vec, C1T3_to_1_vec, C1T4_to_1_vec, C1T1_to_S_vec, C1S_to_Ship_vec; std::vector C2T2_to_1_vec, C2T3_to_1_vec, C2T4_to_1_vec, C2T1_to_S_vec, C2S_to_Ship_vec; auto params = params_; // 使用缓存的参数 C1T2_to_1_vec = params.C1T2_to_1_vec; // 加载单机1外参 C1T3_to_1_vec = params.C1T3_to_1_vec; C1T4_to_1_vec = params.C1T4_to_1_vec; C1T1_to_S_vec = params.C1T1_to_S_vec; C1S_to_Ship_vec = params.C1S_to_Ship_vec; C2T2_to_1_vec = params.C2T2_to_1_vec; // 加载单机2外参 C2T3_to_1_vec = params.C2T3_to_1_vec; C2T4_to_1_vec = params.C2T4_to_1_vec; C2T1_to_S_vec = params.C2T1_to_S_vec; C2S_to_Ship_vec = params.C2S_to_Ship_vec; Comb1lidar1 = params.Comb1lidar1; // 加载话题名称 Comb1lidar2 = params.Comb1lidar2; Comb1lidar3 = params.Comb1lidar3; Comb1lidar4 = params.Comb1lidar4; Comb1cloud = params.Comb1cloud; Comb2lidar1 = params.Comb2lidar1; Comb2lidar2 = params.Comb2lidar2; Comb2lidar3 = params.Comb2lidar3; Comb2lidar4 = params.Comb2lidar4; Comb2cloud = params.Comb2cloud; std::vector roi1 = params.roi1; // C1 第1块 ROI std::vector roi1_b = params.roi1_b; // C1 第2块 ROI std::vector roi2 = params.roi2; // C2 第1块 ROI std::vector roi2_b = params.roi2_b; // C2 第2块 ROI pixel_size = params.pixel_size; // 加载预处理部分横向滤波纵向滤波的像素大小 // 缓存过滤阈值参数 heightmap_min_diff_ = params.heightmap_min_diff; widthmap_max_diff_ = params.widthmap_max_diff; morph_min_contour_points_ = params.morph_min_contour_points; morph_ratio_min_ = params.morph_ratio_min; morph_ratio_max_ = params.morph_ratio_max; rope_filter_linearity_thresh_ = params_.rope_filter_linearity_thresh; rocket_extract_voxel_ratio_ = params_.rocket_extract_voxel_ratio; rocket_cluster_threshold_ = params_.rocket_cluster_threshold; rocket_cluster_min_size_ = params_.rocket_cluster_min_size; rope_filter_roi_.z_min = static_cast(params_.rope_filter_roi.z_min); rope_filter_roi_.z_max = static_cast(params_.rope_filter_roi.z_max); rope_filter_roi_.x_min = static_cast(params_.rope_filter_roi.x_min); rope_filter_roi_.x_max = static_cast(params_.rope_filter_roi.x_max); rope_filter_roi_.y_min = static_cast(params_.rope_filter_roi.y_min); rope_filter_roi_.y_max = static_cast(params_.rope_filter_roi.y_max); // 组合两块 ROI:前 6 个为 roi_filter1,后 6 个为 roi_filter1_b if (roi1.size() == 6 && roi1_b.size() == 6) { std::copy(roi1.begin(), roi1.end(), roi_1.begin()); // [0..5] std::copy(roi1_b.begin(), roi1_b.end(), roi_1.begin() + 6); // [6..11] } if (roi2.size() == 6 && roi2_b.size() == 6) { std::copy(roi2.begin(), roi2.end(), roi_2.begin()); // [0..5] std::copy(roi2_b.begin(), roi2_b.end(), roi_2.begin() + 6); // [6..11] } C1T2_to_1f_ = vecToAffine3f(C1T2_to_1_vec); // 将单机1外参向量转换为仿射矩阵 C1T3_to_1f_ = vecToAffine3f(C1T3_to_1_vec); C1T4_to_1f_ = vecToAffine3f(C1T4_to_1_vec); C1T1_to_Sf_ = vecToAffine3f(C1T1_to_S_vec); C1Sf_to_Shipf_ = vecToAffine3f(C1S_to_Ship_vec); C2T2_to_1f_ = vecToAffine3f(C2T2_to_1_vec); C2T3_to_1f_ = vecToAffine3f(C2T3_to_1_vec); C2T4_to_1f_ = vecToAffine3f(C2T4_to_1_vec); C2T1_to_Sf_ = vecToAffine3f(C2T1_to_S_vec); C2Sf_to_Shipf_ = vecToAffine3f(C2S_to_Ship_vec); C1T1_to_Shipf_ = mergeTransforms(Eigen::Affine3f::Identity(), C1T1_to_Sf_, C1Sf_to_Shipf_); // 单机1雷达1到船体 C1T2_to_Shipf_ = mergeTransforms(C1T2_to_1f_, C1T1_to_Sf_, C1Sf_to_Shipf_); // 单机1雷达2到船体 C1T3_to_Shipf_ = mergeTransforms(C1T3_to_1f_, C1T1_to_Sf_, C1Sf_to_Shipf_); // 单机1雷达3到船体 C1T4_to_Shipf_ = mergeTransforms(C1T4_to_1f_, C1T1_to_Sf_, C1Sf_to_Shipf_); // 单机1雷达4到船体 C2T1_to_Shipf_ = mergeTransforms(Eigen::Affine3f::Identity(), C2T1_to_Sf_, C2Sf_to_Shipf_); // 单机2雷达1到船体 C2T2_to_Shipf_ = mergeTransforms(C2T2_to_1f_, C2T1_to_Sf_, C2Sf_to_Shipf_); // 单机2雷达2到船体 C2T3_to_Shipf_ = mergeTransforms(C2T3_to_1f_, C2T1_to_Sf_, C2Sf_to_Shipf_); // 单机2雷达3到船体 C2T4_to_Shipf_ = mergeTransforms(C2T4_to_1f_, C2T1_to_Sf_, C2Sf_to_Shipf_); // 单机2雷达4到船体 // 盲区平面转换到 ship 坐标系(仅使用 lidar1_to_shell 与 shell_to_ship,顺序为先 lidar1_to_shell 后 shell_to_ship) auto vec4_to_f = [](const std::vector &v) -> Eigen::Vector4f { if (v.size() != 4) return Eigen::Vector4f::Zero(); return Eigen::Vector4f(static_cast(v[0]), static_cast(v[1]), static_cast(v[2]), static_cast(v[3])); }; auto transform_plane = [](const Eigen::Matrix4f &H, const Eigen::Vector4f &pi) -> Eigen::Vector4f { Eigen::Matrix4f HinvT = H.inverse().transpose(); return HinvT * pi; }; // C1 { Eigen::Matrix4f H = (C1Sf_to_Shipf_ * C1T1_to_Sf_).matrix(); c1_blind_planes_ship_[0] = transform_plane(H, vec4_to_f(params_.C1_blind_34_lidar4_plane)); c1_blind_planes_ship_[1] = transform_plane(H, vec4_to_f(params_.C1_blind_34_lidar3_plane)); c1_blind_planes_ship_[2] = transform_plane(H, vec4_to_f(params_.C1_blind_23_lidar3_plane)); c1_blind_planes_ship_[3] = transform_plane(H, vec4_to_f(params_.C1_blind_23_lidar2_plane)); c1_blind_planes_ship_[4] = transform_plane(H, vec4_to_f(params_.C1_blind_1_lidar1_plane)); } // C2 { Eigen::Matrix4f H = (C2Sf_to_Shipf_ * C2T1_to_Sf_).matrix(); c2_blind_planes_ship_[0] = transform_plane(H, vec4_to_f(params_.C2_blind_34_lidar4_plane)); c2_blind_planes_ship_[1] = transform_plane(H, vec4_to_f(params_.C2_blind_34_lidar3_plane)); c2_blind_planes_ship_[2] = transform_plane(H, vec4_to_f(params_.C2_blind_23_lidar3_plane)); c2_blind_planes_ship_[3] = transform_plane(H, vec4_to_f(params_.C2_blind_23_lidar2_plane)); c2_blind_planes_ship_[4] = transform_plane(H, vec4_to_f(params_.C2_blind_1_lidar1_plane)); } // 订阅雷达点云话题 c1_sub1_ = pnh_.subscribe(Comb1lidar1, 10, &LidarFusionNode::c1_cb1, this, ros::TransportHints().tcpNoDelay()); c1_sub2_ = pnh_.subscribe(Comb1lidar2, 10, &LidarFusionNode::c1_cb2, this, ros::TransportHints().tcpNoDelay()); c1_sub3_ = pnh_.subscribe(Comb1lidar3, 10, &LidarFusionNode::c1_cb3, this, ros::TransportHints().tcpNoDelay()); c1_sub4_ = pnh_.subscribe(Comb1lidar4, 10, &LidarFusionNode::c1_cb4, this, ros::TransportHints().tcpNoDelay()); c2_sub1_ = pnh_.subscribe(Comb2lidar1, 10, &LidarFusionNode::c2_cb1, this, ros::TransportHints().tcpNoDelay()); c2_sub2_ = pnh_.subscribe(Comb2lidar2, 10, &LidarFusionNode::c2_cb2, this, ros::TransportHints().tcpNoDelay()); c2_sub3_ = pnh_.subscribe(Comb2lidar3, 10, &LidarFusionNode::c2_cb3, this, ros::TransportHints().tcpNoDelay()); c2_sub4_ = pnh_.subscribe(Comb2lidar4, 10, &LidarFusionNode::c2_cb4, this, ros::TransportHints().tcpNoDelay()); c1_pub_ = pnh_.advertise(Comb1cloud, 10); c2_pub_ = pnh_.advertise(Comb2cloud, 10); // ===== 注意:先读取参数再启动依赖这些参数的线程,避免线程读取到默认值 ===== // (之前 softsync 线程在参数赋值之前启动,导致 c1_sync_wait_param_ / c2_sync_wait_param_ 仍是默认值) pnh_.param("/common/simulation", simu, 0); pnh_.param("/common/delay_ms", delay_JT_ms, 77); pnh_.param("/common/canid", canid, "0x513"); pnh_.param("/common/sim_path", filename, "1.csv"); pnh_.param("/common/log_path", logpath, "/home/log"); pnh_.param("/common/serverip", sIP, "0xC0A8A0B6"); pnh_.param("/common/serverport", sPORT, 10188); pnh_.param("/common/clientip", cIP, "0xC0A89791"); pnh_.param("/common/clientport", cPORT, 10188); pnh_.param("/common/serverip_bak", sIP_bak, "0xC0A8A0B7"); pnh_.param("/common/serverport_bak", sPORT_bak, 10188); pnh_.param("/common/clientip_bak", cIP_bak, "0xC0A89792"); pnh_.param("/common/clientport_bak", cPORT_bak, 10188); pnh_.param("/coordinate/SheXiang", SheXiang_para, 0.0); pnh_.param("/coordinate/Lever1", Lever_para[0], 0.0); pnh_.param("/coordinate/Lever2", Lever_para[1], 0.0); pnh_.param("/coordinate/Lever3", Lever_para[2], 0.0); pnh_.param("/coordinate/JT1", JT_para[0], 0.0); pnh_.param("/coordinate/JT2", JT_para[1], 0.0); pnh_.param("/coordinate/JT3", JT_para[2], 0.0); pnh_.param("/coordinate/GZatt1", GZ_para[0], 0.0); pnh_.param("/coordinate/GZatt2", GZ_para[1], 0.0); pnh_.param("/coordinate/GZatt3", GZ_para[2], 0.0); printf("%s", canid.c_str()); can_main.setValue_id(std::stoi(canid, nullptr, 16)); can_hpyo.setValue_id(std::stoi(canid, nullptr, 16)); can_main.setValue_can(0); can_hpyo.setValue_can(1); can_main.set_path(logpath); can_hpyo.set_path(logpath); // can_main.setValue_mode(simu); // can_hpyo.setValue_mode(simu); // can_main.setValue_filepath(filename); // can_hpyo.setValue_filepath(filename); if (!readCsvFile(filename, csv_data_list)) { ROS_ERROR("read CSV failed"); } else { ROS_INFO("read file %s success", filename.c_str()); } print_count = csv_data_list.size(); logger.set_path(logpath); logger.start(); can_main.start(); can_hpyo.start(); // logger.start(); udp_client.set_path(logpath); udp_client.InitUDP(std::stoul(sIP, nullptr, 16), (unsigned int)sPORT, std::stoul(cIP, nullptr, 16), (unsigned int)cPORT); udp_client_bak.set_path(logpath); udp_client_bak.InitUDP(std::stoul(sIP_bak, nullptr, 16), (unsigned int)sPORT_bak, std::stoul(cIP_bak, nullptr, 16), (unsigned int)cPORT_bak); c1_sync_wait_param_ = params.c1_sync_wait_sec; c2_sync_wait_param_ = params.c2_sync_wait_sec; c1_SYNC_WAIT_ = ros::Duration(c1_sync_wait_param_); c2_SYNC_WAIT_ = ros::Duration(c2_sync_wait_param_); last_stat_time_ = ros::Time::now(); // 决策等待时间参数 decision_wait_sec_ = params.decision_wait_sec; // 启动结果对策线程 // 线程启动顺序:确保 softsync 线程最后启动,这样其构造的 Inputs 能拿到更新后的 *_sync_wait_param_ c1_merge_thread_ = std::thread(&LidarFusionNode::c1_mergeThreadLoop, this); c2_merge_thread_ = std::thread(&LidarFusionNode::c2_mergeThreadLoop, this); c1_sync_thread_ = std::thread(&LidarFusionNode::c1_doSyncWork, this); c2_sync_thread_ = std::thread(&LidarFusionNode::c2_doSyncWork, this); c1_softsync_thread_ = std::thread(&LidarFusionNode::c1_handleSoftSync, this); c2_softsync_thread_ = std::thread(&LidarFusionNode::c2_handleSoftSync, this); decision_thread_ = std::thread(&LidarFusionNode::decisionThreadLoop, this); double hb_timeout = params.lidar_heartbeat_timeout; int hb_mode = params.lidar_heartbeat_mode; // 0: bit=异常 1: bit=正常 try { LidarHeart::setTimeout(hb_timeout); LidarHeart::setMode(hb_mode); ROS_INFO_STREAM("LidarHeart initialized: timeout=" << hb_timeout << "s mode=" << (hb_mode == 0 ? "bit=abnormal" : "bit=normal")); } catch (const std::exception &e) { ROS_ERROR_STREAM("Failed to initialize LidarHeart: " << e.what()); } // 订阅运行期参数刷新命令 reload_params_sub_ = pnh_.subscribe("/reload_lidar_params", 1, &LidarFusionNode::reloadParamsCallback, this); } // 分割字符串函数 std::vector LidarFusionNode::split(const std::string &s, char delimiter) { std::vector tokens; std::string token; std::istringstream tokenStream(s); while (std::getline(tokenStream, token, delimiter)) { tokens.push_back(token); } return tokens; } // 从CSV文件读取数据 bool LidarFusionNode::readCsvFile(const std::string &file_path, std::vector &data_list) { std::ifstream file(file_path); if (!file.is_open()) { ROS_ERROR("can not open CSV file: %s", file_path.c_str()); return false; } std::string line; // 跳过表头 if (std::getline(file, line)) { ROS_INFO("ignore first line"); } // 读取每一行数据 while (std::getline(file, line)) { std::vector tokens = split(line, ','); // 检查列数是否正确 if (tokens.size() != 21) { ROS_WARN("line data error,jump: %s", line.c_str()); continue; } try { CsvData data; data.index = std::stoi(tokens[0]); data.timestamp = std::stod(tokens[1]); data.x = std::stod(tokens[2]); data.y = std::stod(tokens[3]); data.z = std::stod(tokens[4]); data.a = std::stod(tokens[5]); data.b = std::stod(tokens[6]); data.c = std::stod(tokens[7]); data.yaw = std::stod(tokens[8]); data.pitch = std::stod(tokens[9]); data.point_count = std::stoi(tokens[10]); data.residual_mean = std::stod(tokens[11]); data.is_cross_plane = (std::stoi(tokens[12]) != 0); data.delay = std::stod(tokens[13]); data.is_visible = (std::stoi(tokens[14]) != 0); data.is_prediction = (std::stoi(tokens[15]) != 0); data.radar_state = std::stoi(tokens[16]); data.each_lidarstate = std::stoi(tokens[17]); data.solve_state = std::stoi(tokens[18]); data.data_state = std::stoi(tokens[19]); data_list.push_back(data); // printCsvData(data); } catch (const std::exception &e) { ROS_WARN("data error: %s, jump", e.what()); continue; } } file.close(); ROS_INFO("read file success, %zu data", data_list.size()); return true; } void LidarFusionNode::reloadParamsCallback(const std_msgs::Empty::ConstPtr &) { try { ROS_INFO("Reloading LidarFusionParams from parameter server..."); LidarFusionParams new_params = loadLidarFusionParams(pnh_); params_ = new_params; // 同步运行期可更新的参数 heightmap_min_diff_ = params_.heightmap_min_diff; widthmap_max_diff_ = params_.widthmap_max_diff; morph_min_contour_points_ = params_.morph_min_contour_points; morph_ratio_min_ = params_.morph_ratio_min; morph_ratio_max_ = params_.morph_ratio_max; rope_filter_linearity_thresh_ = params_.rope_filter_linearity_thresh; rocket_extract_voxel_ratio_ = params_.rocket_extract_voxel_ratio; rocket_cluster_threshold_ = params_.rocket_cluster_threshold; rocket_cluster_min_size_ = params_.rocket_cluster_min_size; rope_filter_roi_.z_min = static_cast(params_.rope_filter_roi.z_min); rope_filter_roi_.z_max = static_cast(params_.rope_filter_roi.z_max); rope_filter_roi_.x_min = static_cast(params_.rope_filter_roi.x_min); rope_filter_roi_.x_max = static_cast(params_.rope_filter_roi.x_max); rope_filter_roi_.y_min = static_cast(params_.rope_filter_roi.y_min); rope_filter_roi_.y_max = static_cast(params_.rope_filter_roi.y_max); // ROI 参数实时更新(两块 ROI 组合成 12 元数组) if (params_.roi1.size() == 6 && params_.roi1_b.size() == 6) { std::copy(params_.roi1.begin(), params_.roi1.end(), roi_1.begin()); std::copy(params_.roi1_b.begin(), params_.roi1_b.end(), roi_1.begin() + 6); } if (params_.roi2.size() == 6 && params_.roi2_b.size() == 6) { std::copy(params_.roi2.begin(), params_.roi2.end(), roi_2.begin()); std::copy(params_.roi2_b.begin(), params_.roi2_b.end(), roi_2.begin() + 6); } c1_sync_wait_param_ = params_.c1_sync_wait_sec; c2_sync_wait_param_ = params_.c2_sync_wait_sec; c1_SYNC_WAIT_ = ros::Duration(c1_sync_wait_param_); c2_SYNC_WAIT_ = ros::Duration(c2_sync_wait_param_); decision_wait_sec_ = params_.decision_wait_sec; double hb_timeout = params_.lidar_heartbeat_timeout; int hb_mode = params_.lidar_heartbeat_mode; // 0: bit=异常 1: bit=正常 LidarHeart::setTimeout(hb_timeout); LidarHeart::setMode(hb_mode); // 重新计算盲区平面(ship) auto vec4_to_f = [](const std::vector &v) -> Eigen::Vector4f { if (v.size() != 4) return Eigen::Vector4f::Zero(); return Eigen::Vector4f(static_cast(v[0]), static_cast(v[1]), static_cast(v[2]), static_cast(v[3])); }; auto transform_plane = [](const Eigen::Matrix4f &H, const Eigen::Vector4f &pi) -> Eigen::Vector4f { Eigen::Matrix4f HinvT = H.inverse().transpose(); return HinvT * pi; }; { Eigen::Matrix4f H = (C1Sf_to_Shipf_ * C1T1_to_Sf_).matrix(); c1_blind_planes_ship_[0] = transform_plane(H, vec4_to_f(params_.C1_blind_34_lidar4_plane)); c1_blind_planes_ship_[1] = transform_plane(H, vec4_to_f(params_.C1_blind_34_lidar3_plane)); c1_blind_planes_ship_[2] = transform_plane(H, vec4_to_f(params_.C1_blind_23_lidar3_plane)); c1_blind_planes_ship_[3] = transform_plane(H, vec4_to_f(params_.C1_blind_23_lidar2_plane)); c1_blind_planes_ship_[4] = transform_plane(H, vec4_to_f(params_.C1_blind_1_lidar1_plane)); } { Eigen::Matrix4f H = (C2Sf_to_Shipf_ * C2T1_to_Sf_).matrix(); c2_blind_planes_ship_[0] = transform_plane(H, vec4_to_f(params_.C2_blind_34_lidar4_plane)); c2_blind_planes_ship_[1] = transform_plane(H, vec4_to_f(params_.C2_blind_34_lidar3_plane)); c2_blind_planes_ship_[2] = transform_plane(H, vec4_to_f(params_.C2_blind_23_lidar3_plane)); c2_blind_planes_ship_[3] = transform_plane(H, vec4_to_f(params_.C2_blind_23_lidar2_plane)); c2_blind_planes_ship_[4] = transform_plane(H, vec4_to_f(params_.C2_blind_1_lidar1_plane)); } ROS_INFO("LidarFusionParams reloaded successfully."); } catch (const std::exception &e) { ROS_ERROR("Failed to reload LidarFusionParams: %s", e.what()); } } LidarFusionNode::~LidarFusionNode() { can_main.stop(); can_hpyo.stop(); logger.stop(); shutting_down_.store(true, std::memory_order_relaxed); fitting_enabled_.store(false, std::memory_order_relaxed); // 关闭决策线程 decision_running_.store(false, std::memory_order_relaxed); { std::lock_guard lk(decision_any_mutex_); } decision_any_cv_.notify_all(); c1_thread_pool_.stopAccepting(); c2_thread_pool_.stopAccepting(); c1_fit_pool_.stopAccepting(); c2_fit_pool_.stopAccepting(); c1_fit_pool_.discardPending(); c2_fit_pool_.discardPending(); if (c1_syncTimer_.isValid()) c1_syncTimer_.stop(); if (c2_syncTimer_.isValid()) c2_syncTimer_.stop(); c1_thread_running_.store(false, std::memory_order_relaxed); c2_thread_running_.store(false, std::memory_order_relaxed); c1_sync_thread_running_.store(false, std::memory_order_relaxed); c2_sync_thread_running_.store(false, std::memory_order_relaxed); c1_softsync_running_.store(false, std::memory_order_relaxed); c2_softsync_running_.store(false, std::memory_order_relaxed); { std::lock_guard lk(c1_cv_mutex_); c1_cv_.notify_all(); } { std::lock_guard lk(c2_cv_mutex_); c2_cv_.notify_all(); } if (decision_thread_.joinable()) decision_thread_.join(); if (c1_merge_thread_.joinable()) c1_merge_thread_.join(); if (c2_merge_thread_.joinable()) c2_merge_thread_.join(); if (c1_sync_thread_.joinable()) c1_sync_thread_.join(); if (c2_sync_thread_.joinable()) c2_sync_thread_.join(); if (c1_softsync_thread_.joinable()) c1_softsync_thread_.join(); if (c2_softsync_thread_.joinable()) c2_softsync_thread_.join(); c1_thread_pool_.discardPending(); c2_thread_pool_.discardPending(); c1_thread_pool_.shutdownNow(); c2_thread_pool_.shutdownNow(); c1_fit_pool_.shutdownNow(); c2_fit_pool_.shutdownNow(); { std::lock_guard lg(c1_pending_mutex_); while (!c1_pending_clouds_.empty()) c1_pending_clouds_.pop(); } { std::lock_guard lg(c2_pending_mutex_); while (!c2_pending_clouds_.empty()) c2_pending_clouds_.pop(); } { std::lock_guard lg(c1_pose_mutex_); while (!c1_pose_queue_.empty()) c1_pose_queue_.pop(); } { std::lock_guard lg(c2_pose_mutex_); while (!c2_pose_queue_.empty()) c2_pose_queue_.pop(); } } Eigen::Affine3f LidarFusionNode::vecToAffine3f(const std::vector &vec) { Eigen::Matrix4d mat_d; for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { mat_d(i, j) = vec[i * 4 + j]; } } Eigen::Matrix4f mat_f = mat_d.cast(); return Eigen::Affine3f(mat_f); } // 从frame_id中提取需要计算延迟的起始时间,frame_id格式为:"|