alex 1 неделя назад
Родитель
Сommit
6b223eadcf
1 измененных файлов с 3715 добавлено и 0 удалено
  1. 3715 0
      lidar_fusion_node.cpp

+ 3715 - 0
lidar_fusion_node.cpp

@@ -0,0 +1,3715 @@
+#include "lidar_fusion_node.h"
+#include "time_sync.h"
+#include "lidarheart.h" // 读取雷达状态
+#include <ros/ros.h>
+#include <pcl_conversions/pcl_conversions.h>
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <thread>
+#include <functional>
+#include <chrono>
+#include <map>
+#include <limits>
+#include <params.h>
+#include <atomic>
+#include <std_msgs/Empty.h>
+#include <cmath>
+#include <cstdint>
+// 可视化预测圆柱
+#include "visualization.h"
+
+// 记录最近一次有效拟合的时间戳与延迟
+namespace
+{
+    std::atomic<double> s_last_c1_stamp_sec{0.0};
+    std::atomic<double> s_last_c1_delay_sec{0.0};
+    std::atomic<double> s_last_c2_stamp_sec{0.0};
+    std::atomic<double> 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<PosePredictor>();
+    c2_predictor_ = std::make_unique<PosePredictor>();
+    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<double> C1T2_to_1_vec, C1T3_to_1_vec, C1T4_to_1_vec, C1T1_to_S_vec, C1S_to_Ship_vec;
+    std::vector<double> 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<float> roi1 = params.roi1;     // C1 第1块 ROI
+    std::vector<float> roi1_b = params.roi1_b; // C1 第2块 ROI
+    std::vector<float> roi2 = params.roi2;     // C2 第1块 ROI
+    std::vector<float> 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<float>(params_.rope_filter_roi.z_min);
+    rope_filter_roi_.z_max = static_cast<float>(params_.rope_filter_roi.z_max);
+    rope_filter_roi_.x_min = static_cast<float>(params_.rope_filter_roi.x_min);
+    rope_filter_roi_.x_max = static_cast<float>(params_.rope_filter_roi.x_max);
+    rope_filter_roi_.y_min = static_cast<float>(params_.rope_filter_roi.y_min);
+    rope_filter_roi_.y_max = static_cast<float>(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<double> &v) -> Eigen::Vector4f
+    {
+        if (v.size() != 4)
+            return Eigen::Vector4f::Zero();
+        return Eigen::Vector4f(static_cast<float>(v[0]), static_cast<float>(v[1]), static_cast<float>(v[2]), static_cast<float>(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<sensor_msgs::PointCloud2>(Comb1cloud, 10);
+    c2_pub_ = pnh_.advertise<sensor_msgs::PointCloud2>(Comb2cloud, 10);
+
+    // ===== 注意:先读取参数再启动依赖这些参数的线程,避免线程读取到默认值 =====
+    // (之前 softsync 线程在参数赋值之前启动,导致 c1_sync_wait_param_ / c2_sync_wait_param_ 仍是默认值)
+
+    pnh_.param<int>("/common/simulation", simu, 0);
+    pnh_.param<int>("/common/delay_ms", delay_JT_ms, 77);
+    pnh_.param<string>("/common/canid", canid, "0x513");
+    pnh_.param<string>("/common/sim_path", filename, "1.csv");
+    pnh_.param<string>("/common/log_path", logpath, "/home/log");
+
+    pnh_.param<string>("/common/serverip", sIP, "0xC0A8A0B6");
+    pnh_.param<int>("/common/serverport", sPORT, 10188);
+    pnh_.param<string>("/common/clientip", cIP, "0xC0A89791");
+    pnh_.param<int>("/common/clientport", cPORT, 10188);
+
+    pnh_.param<string>("/common/serverip_bak", sIP_bak, "0xC0A8A0B7");
+    pnh_.param<int>("/common/serverport_bak", sPORT_bak, 10188);
+    pnh_.param<string>("/common/clientip_bak", cIP_bak, "0xC0A89792");
+    pnh_.param<int>("/common/clientport_bak", cPORT_bak, 10188);
+
+    pnh_.param<double>("/coordinate/SheXiang", SheXiang_para, 0.0);
+    pnh_.param<double>("/coordinate/Lever1", Lever_para[0], 0.0);
+    pnh_.param<double>("/coordinate/Lever2", Lever_para[1], 0.0);
+    pnh_.param<double>("/coordinate/Lever3", Lever_para[2], 0.0);
+
+    pnh_.param<double>("/coordinate/JT1", JT_para[0], 0.0);
+    pnh_.param<double>("/coordinate/JT2", JT_para[1], 0.0);
+    pnh_.param<double>("/coordinate/JT3", JT_para[2], 0.0);
+
+    pnh_.param<double>("/coordinate/GZatt1", GZ_para[0], 0.0);
+    pnh_.param<double>("/coordinate/GZatt2", GZ_para[1], 0.0);
+    pnh_.param<double>("/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<std_msgs::Empty>("/reload_lidar_params", 1,
+                                                         &LidarFusionNode::reloadParamsCallback, this);
+}
+
+// 分割字符串函数
+std::vector<std::string> LidarFusionNode::split(const std::string &s, char delimiter)
+{
+    std::vector<std::string> 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<CsvData> &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<std::string> 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<float>(params_.rope_filter_roi.z_min);
+        rope_filter_roi_.z_max = static_cast<float>(params_.rope_filter_roi.z_max);
+        rope_filter_roi_.x_min = static_cast<float>(params_.rope_filter_roi.x_min);
+        rope_filter_roi_.x_max = static_cast<float>(params_.rope_filter_roi.x_max);
+        rope_filter_roi_.y_min = static_cast<float>(params_.rope_filter_roi.y_min);
+        rope_filter_roi_.y_max = static_cast<float>(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<double> &v) -> Eigen::Vector4f
+        {
+            if (v.size() != 4)
+                return Eigen::Vector4f::Zero();
+            return Eigen::Vector4f(static_cast<float>(v[0]), static_cast<float>(v[1]), static_cast<float>(v[2]), static_cast<float>(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<std::mutex> 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<std::mutex> lk(c1_cv_mutex_);
+        c1_cv_.notify_all();
+    }
+    {
+        std::lock_guard<std::mutex> 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<std::mutex> lg(c1_pending_mutex_);
+        while (!c1_pending_clouds_.empty())
+            c1_pending_clouds_.pop();
+    }
+    {
+        std::lock_guard<std::mutex> lg(c2_pending_mutex_);
+        while (!c2_pending_clouds_.empty())
+            c2_pending_clouds_.pop();
+    }
+    {
+        std::lock_guard<std::mutex> lg(c1_pose_mutex_);
+        while (!c1_pose_queue_.empty())
+            c1_pose_queue_.pop();
+    }
+    {
+        std::lock_guard<std::mutex> lg(c2_pose_mutex_);
+        while (!c2_pose_queue_.empty())
+            c2_pose_queue_.pop();
+    }
+}
+
+Eigen::Affine3f LidarFusionNode::vecToAffine3f(const std::vector<double> &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<float>();
+    return Eigen::Affine3f(mat_f);
+}
+
+// 从frame_id中提取需要计算延迟的起始时间,frame_id格式为:"<orig_frame>|<time>"
+double LidarFusionNode::extractFirstPointTime(const sensor_msgs::PointCloud2ConstPtr &msg)
+{
+
+    const std::string &frame_id = msg->header.frame_id;
+
+    // 情况1:frame_id 仅为 "map"(或以斜杠开头的 "/map"),时间戳直接取 header.stamp
+    if (frame_id == "map" || frame_id == "/map")
+    {
+        return ros::Time::now().toSec();
+    }
+
+    // 情况2:frame_id 形如 "<orig_frame>|<first_point_time>"
+    size_t pos = frame_id.find('|');
+    if (pos != std::string::npos && pos + 1 < frame_id.length())
+    {
+        std::string time_str = frame_id.substr(pos + 1);
+        try
+        {
+            double result = std::stod(time_str);
+            return result;
+        }
+        catch (const std::exception &e)
+        {
+            ROS_WARN_THROTTLE(5.0, "Failed to parse first_point_time from frame_id: %s", e.what());
+        }
+    }
+
+    // 无法解析则回退使用 header.stamp
+    double stamp_sec = msg->header.stamp.toSec();
+    return stamp_sec > 0 ? stamp_sec : 0;
+}
+
+// 从frame_id中提取原始frame_id,去除时间信息
+std::string LidarFusionNode::getOriginalFrameId(const std::string &encoded_frame_id)
+{
+    size_t pos = encoded_frame_id.find('|');
+    if (pos != std::string::npos)
+    {
+        return encoded_frame_id.substr(0, pos);
+    }
+    return encoded_frame_id;
+}
+
+// c1_cb1 - c1_cb4: 单机1四个雷达的回调函数
+void LidarFusionNode::c1_cb1(const sensor_msgs::PointCloud2ConstPtr &msg)
+{
+    if (shutting_down_.load(std::memory_order_relaxed))
+        return;
+    {
+        std::lock_guard<std::mutex> lg(c1_in_mutexes_[0]);
+        c1_in_queues_[0].push(msg);
+        // printf("%s %f \n", "c1_cb1 = ", msg->header.stamp.toSec());
+        // printf("%s %f \n", "rosTime1() = ", ros::Time::now().toSec());
+    }
+    c1_cv_.notify_one();
+}
+
+void LidarFusionNode::c1_cb2(const sensor_msgs::PointCloud2ConstPtr &msg)
+{
+    if (shutting_down_.load(std::memory_order_relaxed))
+        return;
+    {
+        std::lock_guard<std::mutex> lg(c1_in_mutexes_[1]);
+        c1_in_queues_[1].push(msg);
+        // printf("%s %f \n", "stamp2 = ", msg->header.stamp.toSec());
+        // printf("%s %f \n", "rosTime2() = ", ros::Time::now().toSec());
+    }
+    c1_cv_.notify_one();
+}
+void LidarFusionNode::c1_cb3(const sensor_msgs::PointCloud2ConstPtr &msg)
+{
+    if (shutting_down_.load(std::memory_order_relaxed))
+        return;
+    {
+        std::lock_guard<std::mutex> lg(c1_in_mutexes_[2]);
+        c1_in_queues_[2].push(msg);
+        // printf("%s %f \n", "stamp3 = ", msg->header.stamp.toSec());
+        // printf("%s %f \n", "rosTime3() = ", ros::Time::now().toSec());
+    }
+    c1_cv_.notify_one();
+}
+void LidarFusionNode::c1_cb4(const sensor_msgs::PointCloud2ConstPtr &msg)
+{
+    if (shutting_down_.load(std::memory_order_relaxed))
+        return;
+    {
+        std::lock_guard<std::mutex> lg(c1_in_mutexes_[3]);
+        c1_in_queues_[3].push(msg);
+        // printf("%s %f \n", "stamp4 = ", msg->header.stamp.toSec());
+        // printf("%s %f \n", "rosTime4() = ", ros::Time::now().toSec());
+    }
+    c1_cv_.notify_one();
+}
+
+void LidarFusionNode::c2_cb1(const sensor_msgs::PointCloud2ConstPtr &msg)
+{
+    if (shutting_down_.load(std::memory_order_relaxed))
+        return;
+    {
+        std::lock_guard<std::mutex> lg(c2_in_mutexes_[0]);
+        c2_in_queues_[0].push(msg);
+    }
+    c2_cv_.notify_one();
+}
+void LidarFusionNode::c2_cb2(const sensor_msgs::PointCloud2ConstPtr &msg)
+{
+    if (shutting_down_.load(std::memory_order_relaxed))
+        return;
+    {
+        std::lock_guard<std::mutex> lg(c2_in_mutexes_[1]);
+        c2_in_queues_[1].push(msg);
+    }
+    c2_cv_.notify_one();
+}
+void LidarFusionNode::c2_cb3(const sensor_msgs::PointCloud2ConstPtr &msg)
+{
+    if (shutting_down_.load(std::memory_order_relaxed))
+        return;
+    {
+        std::lock_guard<std::mutex> lg(c2_in_mutexes_[2]);
+        c2_in_queues_[2].push(msg);
+    }
+    c2_cv_.notify_one();
+}
+void LidarFusionNode::c2_cb4(const sensor_msgs::PointCloud2ConstPtr &msg)
+{
+    if (shutting_down_.load(std::memory_order_relaxed))
+        return;
+    {
+        std::lock_guard<std::mutex> lg(c2_in_mutexes_[3]);
+        c2_in_queues_[3].push(msg);
+    }
+    c2_cv_.notify_one();
+}
+
+// 软同步处理函数:处理逻辑为:从各个雷达的输入队列中取出点云数据,进行时间软同步,并将同步后的数据批次推入同步队列中,供后续处理使用。
+void LidarFusionNode::c1_handleSoftSync()
+{
+    time_sync::Inputs in{
+        &c1_in_queues_,
+        &c1_in_mutexes_,
+        &c1_cv_mutex_,
+        &c1_cv_,
+        &c1_softsync_running_,
+        c1_sync_wait_param_};
+    // 使用 each_mask 的低 4 位(0=正常,1=异常)选择参与同步的雷达通道
+    in.get_active = []()
+    {
+        std::array<bool, 4> act{true, true, true, true};
+        try
+        {
+            uint8_t each_mask = 0;
+            int overall = 0;
+            LidarHeart::getStatus(each_mask, overall);
+            for (int i = 0; i < 4; ++i)
+            {
+                bool abnormal = ((each_mask >> i) & 0x1) != 0; // 1=异常
+                act[i] = !abnormal;                            // 仅正常通道参与
+            }
+        }
+        catch (...)
+        {
+            // 失败则默认全部参与
+        }
+        // act = {false,true,true,true};
+        return act;
+    };
+    auto push_fn = [this](const std::vector<int> &ids,
+                          const std::vector<sensor_msgs::PointCloud2ConstPtr> &msgs)
+    {
+        std::lock_guard<std::mutex> qlg(c1_sync_queue_mutex_);
+        c1_sync_queue_.push(SyncBatch{ids, msgs});
+    };
+    // 实时更新 C1 时间同步状态(0=正常,1=异常)
+    in.set_time_sync_state = [this](bool ok)
+    {
+        c1_time_sync_state_.store(ok ? 0 : 1, std::memory_order_relaxed);
+    };
+    time_sync::two_phase_soft_sync(in, push_fn);
+}
+
+void LidarFusionNode::c2_handleSoftSync()
+{
+    time_sync::Inputs in{
+        &c2_in_queues_,
+        &c2_in_mutexes_,
+        &c2_cv_mutex_,
+        &c2_cv_,
+        &c2_softsync_running_,
+        c2_sync_wait_param_};
+    // 使用 each_mask 的高 4 位(bit[4..7],0=正常,1=异常)选择参与同步的雷达通道
+    in.get_active = []()
+    {
+        std::array<bool, 4> act{true, true, true, true};
+        try
+        {
+            uint8_t each_mask = 0;
+            int overall = 0;
+            LidarHeart::getStatus(each_mask, overall);
+            for (int j = 0; j < 4; ++j)
+            {
+                bool abnormal = ((each_mask >> (4 + j)) & 0x1) != 0; // 1=异常
+                act[j] = !abnormal;                                  // 仅正常通道
+            }
+        }
+        catch (...)
+        {
+            // 失败则默认全部
+        }
+        return act;
+    };
+    auto push_fn = [this](const std::vector<int> &ids,
+                          const std::vector<sensor_msgs::PointCloud2ConstPtr> &msgs)
+    {
+        std::lock_guard<std::mutex> qlg(c2_sync_queue_mutex_);
+        c2_sync_queue_.push(SyncBatch{ids, msgs});
+    };
+    // 实时更新 C2 时间同步状态(0=正常,1=异常)
+    in.set_time_sync_state = [this](bool ok)
+    {
+        c2_time_sync_state_.store(ok ? 0 : 1, std::memory_order_relaxed);
+    };
+    time_sync::two_phase_soft_sync(in, push_fn);
+}
+
+// 同步批次入队函数:将已到达的点云数据打包成同步批次,推入同步队列中,供后续处理使用。
+void LidarFusionNode::c1_enqueueSyncBatch()
+{
+    std::vector<int> ids;
+    std::vector<sensor_msgs::PointCloud2ConstPtr> clouds;
+    {
+        std::lock_guard<std::mutex> lg(c1_mtx_);
+        for (int i = 0; i < 4; ++i)
+        {
+            if (c1_box_[i].arrived)
+            {
+                ids.push_back(i);
+                clouds.push_back(c1_box_[i].cloud);
+                c1_box_[i].arrived = false;
+                c1_box_[i].cloud.reset();
+            }
+        }
+    }
+    if (ids.empty())
+        return;
+    {
+        std::lock_guard<std::mutex> qlg(c1_sync_queue_mutex_);
+        c1_sync_queue_.push(SyncBatch{ids, clouds});
+    }
+}
+
+// 同步工作处理函数:从同步队列中取出同步批次,使用线程池并行处理每个雷达的点云坐标转换和ROI,处理完成后将结果打包成待处理云数据,推入待处理队列中。
+void LidarFusionNode::c1_doSyncWork()
+{
+    while (c1_sync_thread_running_.load(std::memory_order_relaxed) && ros::ok())
+    {
+        SyncBatch batch;
+        bool has = false;
+        // 从同步队列中取出一个批次
+        {
+            std::lock_guard<std::mutex> lg(c1_sync_queue_mutex_);
+            if (!c1_sync_queue_.empty())
+            {
+                batch = c1_sync_queue_.front();
+                c1_sync_queue_.pop();
+                has = true;
+            }
+        }
+        if (!has)
+        {
+            continue;
+        }
+
+        std::vector<CloudType> processed_clouds(4);
+        std::vector<std::future<void>> futures;
+        // 并行处理每个雷达的点云数据
+        for (size_t idx = 0; idx < batch.lidar_ids.size(); ++idx)
+        {
+            int lidar_id = batch.lidar_ids[idx];
+            auto cloud_ptr = batch.clouds[idx];
+            futures.emplace_back(c1_thread_pool_.AddTask([this, lidar_id, cloud_ptr, &processed_clouds]()
+                                                         {
+                try {
+                    switch (lidar_id) {
+                        case 0:
+                            processed_clouds[lidar_id] = this->c1_funcLidar1(cloud_ptr);//ROI和坐标转换
+                            break;
+                        case 1:
+                            processed_clouds[lidar_id] = this->c1_funcLidar2(cloud_ptr);
+                            break;
+                        case 2:
+                            processed_clouds[lidar_id] = this->c1_funcLidar3(cloud_ptr);
+                            break;
+                        case 3:
+                            processed_clouds[lidar_id] = this->c1_funcLidar4(cloud_ptr);
+                            break;
+                    }
+                } catch(const std::exception &e) {
+                    ROS_ERROR("C1 Lidar %d task failed: %s", lidar_id, e.what());
+                } }));
+        }
+        for (auto &f : futures)
+        {
+            if (!f.valid())
+            {
+                continue;
+            }
+            try
+            {
+                f.wait();
+            }
+            catch (const std::exception &e)
+            {
+                ROS_ERROR("Wait C1 future failed: %s", e.what());
+            }
+        }
+        PendingCloud data;
+        data.ready_lidar_ids = batch.lidar_ids;
+        data.header = batch.clouds[0]->header;
+        double earliest_time = std::numeric_limits<double>::max();
+        double lidar_time = std::numeric_limits<double>::max();
+        // 找到所有点云中时间最早的一个作为当前同步批次的时间戳
+        for (auto &cloud_ptr : batch.clouds)
+        {
+            double t = cloud_ptr->header.stamp.toSec();
+            if (t > 0 && t < lidar_time)
+            {
+                lidar_time = t;
+            }
+        }
+        data.header.stamp = ros::Time(lidar_time);
+
+        for (auto &cloud_ptr : batch.clouds)
+        {
+            double t = extractFirstPointTime(cloud_ptr);
+            if (t > 0 && t < earliest_time)
+            {
+                earliest_time = t;
+            }
+        }
+        // i = 0;
+        data.lidar_start_time = (earliest_time != std::numeric_limits<double>::max())
+                                    ? earliest_time
+                                    : 0;
+
+        // // 将该批次的时间戳写入文本文件(追加模式)
+        // try {
+        //     std::ofstream ts_out("/home/ubuntu/c1_batches_timestamps.txt", std::ios::app);
+        //     if (ts_out.is_open()) {
+        //         // 记录批次的统一时间戳(header.stamp)与首点时间(lidar_start_time)以及批次大小
+        //         ts_out << std::fixed << std::setprecision(9)
+        //               << "batch_begin header_stamp_sec=" << data.header.stamp.toSec() << ", "
+        //               << "lidar_start_time=" << data.lidar_start_time << ", "
+        //               << "batch_size=" << batch.clouds.size() << std::endl;
+
+        //         // 逐帧记录:每个通道在该批次中的原始 header.stamp 与首点时间
+        //         for (size_t i = 0; i < batch.clouds.size(); ++i) {
+        //             const auto &msg = batch.clouds[i];
+        //             double stamp_sec = msg->header.stamp.toSec();
+        //             double first_point_time = extractFirstPointTime(msg);
+        //             int lidar_id = (i < batch.lidar_ids.size()) ? batch.lidar_ids[i] : -1;
+        //             ts_out << "  frame lidar_id=" << lidar_id
+        //                    << ", header_stamp_sec=" << stamp_sec
+        //                    << ", first_point_time=" << first_point_time
+        //                    << ", frame_id=\"" << msg->header.frame_id << "\""
+        //                    << std::endl;
+        //         }
+
+        //         ts_out << "batch_end" << std::endl;
+        //     } else {
+        //         ROS_WARN_THROTTLE(5.0, "Failed to open c1_batches_timestamps.txt for writing");
+        //     }
+        // } catch (const std::exception &e) {
+        //     ROS_WARN_THROTTLE(5.0, "Write C1 timestamps failed: %s", e.what());
+        // }
+
+        // 将处理后的点云数据存入待处理云数据结构中
+        for (int id : batch.lidar_ids)
+        {
+            data.ready_clouds[id] = processed_clouds[id];
+        }
+        {
+            std::lock_guard<std::mutex> pending_lg(c1_pending_mutex_);
+            c1_pending_clouds_.push(data);
+        }
+    }
+}
+
+void LidarFusionNode::c2_enqueueSyncBatch()
+{
+    std::vector<int> ids;
+    std::vector<sensor_msgs::PointCloud2ConstPtr> clouds;
+    {
+        std::lock_guard<std::mutex> lg(c2_mtx_);
+        for (int i = 0; i < 4; ++i)
+        {
+            if (c2_box_[i].arrived)
+            {
+                ids.push_back(i);
+                clouds.push_back(c2_box_[i].cloud);
+                c2_box_[i].arrived = false;
+                c2_box_[i].cloud.reset();
+            }
+        }
+    }
+    if (ids.empty())
+        return;
+    {
+        std::lock_guard<std::mutex> qlg(c2_sync_queue_mutex_);
+        c2_sync_queue_.push(SyncBatch{ids, clouds});
+    }
+}
+
+void LidarFusionNode::c2_doSyncWork()
+{
+    while (c2_sync_thread_running_.load(std::memory_order_relaxed) && ros::ok())
+    {
+        SyncBatch batch;
+        bool has = false;
+        {
+            std::lock_guard<std::mutex> lg(c2_sync_queue_mutex_);
+            if (!c2_sync_queue_.empty())
+            {
+                batch = c2_sync_queue_.front();
+                c2_sync_queue_.pop();
+                has = true;
+            }
+        }
+        if (!has)
+        {
+            continue;
+        }
+        std::vector<CloudType> processed_clouds(4);
+        std::vector<std::future<void>> futures;
+        for (size_t idx = 0; idx < batch.lidar_ids.size(); ++idx)
+        {
+            int lidar_id = batch.lidar_ids[idx];
+            auto cloud_ptr = batch.clouds[idx];
+            futures.emplace_back(
+                c2_thread_pool_.AddTask([this, lidar_id, cloud_ptr, &processed_clouds]()
+                                        {
+                    try {
+                        switch (lidar_id) {
+                            case 0:
+                                processed_clouds[lidar_id] = this->c2_funcLidar1(cloud_ptr);
+                                break;
+                            case 1:
+                                processed_clouds[lidar_id] = this->c2_funcLidar2(cloud_ptr);
+                                break;
+                            case 2:
+                                processed_clouds[lidar_id] = this->c2_funcLidar3(cloud_ptr);
+                                break;
+                            case 3:
+                                processed_clouds[lidar_id] = this->c2_funcLidar4(cloud_ptr);
+                                break;
+                        }
+                    } catch (const std::exception &e) {
+                        ROS_ERROR("C2 Lidar %d task failed: %s", lidar_id, e.what());
+                    } }));
+        }
+        for (auto &f : futures)
+        {
+            if (!f.valid())
+            {
+                continue;
+            }
+            try
+            {
+                f.wait();
+            }
+            catch (const std::exception &e)
+            {
+                ROS_ERROR("Wait C2 future failed: %s", e.what());
+            }
+        }
+        PendingCloud data;
+        data.ready_lidar_ids = batch.lidar_ids;
+        data.header = batch.clouds[0]->header;
+        double earliest_time = std::numeric_limits<double>::max();
+        double lidar_time = std::numeric_limits<double>::max();
+        for (auto &cloud_ptr : batch.clouds)
+        {
+            double t = cloud_ptr->header.stamp.toSec();
+            if (t > 0 && t < lidar_time)
+            {
+                lidar_time = t;
+            }
+        }
+        data.header.stamp = ros::Time(lidar_time);
+        for (auto &cloud_ptr : batch.clouds)
+        {
+            double t = extractFirstPointTime(cloud_ptr);
+            if (t > 0 && t < earliest_time)
+            {
+                earliest_time = t;
+            }
+        }
+        data.lidar_start_time = (earliest_time != std::numeric_limits<double>::max()) ? earliest_time : 0;
+
+        // // 将该批次的时间戳写入文本文件(追加模式)
+        // try {
+        //     std::ofstream ts_out("/home/ubuntu/c2_batches_timestamps.txt", std::ios::app);
+        //     if (ts_out.is_open()) {
+        //         ts_out << std::fixed << std::setprecision(9)
+        //               << "batch_begin header_stamp_sec=" << data.header.stamp.toSec() << ", "
+        //               << "lidar_start_time=" << data.lidar_start_time << ", "
+        //               << "batch_size=" << batch.clouds.size() << std::endl;
+        //         for (size_t i = 0; i < batch.clouds.size(); ++i) {
+        //             const auto &msg = batch.clouds[i];
+        //             double stamp_sec = msg->header.stamp.toSec();
+        //             double first_point_time = extractFirstPointTime(msg);
+        //             int lidar_id = (i < batch.lidar_ids.size()) ? batch.lidar_ids[i] : -1;
+        //             ts_out << "  frame lidar_id=" << lidar_id
+        //                    << ", header_stamp_sec=" << stamp_sec
+        //                    << ", first_point_time=" << first_point_time
+        //                    << ", frame_id=\"" << msg->header.frame_id << "\""
+        //                    << std::endl;
+        //         }
+        //         ts_out << "batch_end" << std::endl;
+        //     } else {
+        //         ROS_WARN_THROTTLE(5.0, "Failed to open c2_batches_timestamps.txt for writing");
+        //     }
+        // } catch (const std::exception &e) {
+        //     ROS_WARN_THROTTLE(5.0, "Write C2 timestamps failed: %s", e.what());
+        // }
+
+        // 将处理后的点云数据存入待处理云数据结构中
+        for (int id : batch.lidar_ids)
+        {
+            data.ready_clouds[id] = processed_clouds[id];
+        }
+        {
+            std::lock_guard<std::mutex> pending_lg(c2_pending_mutex_);
+            c2_pending_clouds_.push(data);
+        }
+    }
+}
+
+// 决策函数:根据两个PoseData的有效性和拟合残差选择最佳结果
+PoseData LidarFusionNode::decideTwo(const PoseData &m1, const PoseData &m2)
+{
+    const bool m1_ok = (m1.valid == 0);
+    const bool m2_ok = (m2.valid == 0);
+
+    if (1)
+    {
+        if (m1_ok && m2_ok)
+        {
+            // 若两结果均为测量值,则取延迟小的结果作为输出结果
+            if (m1.IsMeasure == 1 && m2.IsMeasure == 1)
+            {
+                return m1;
+            }
+            else if (m1.IsMeasure == 1 && m2.IsMeasure == 0)
+            {
+                return m1; // 若m1结果为测量值,则返回m1的结果
+            }
+            else if (m1.IsMeasure == 0 && m2.IsMeasure == 1)
+            {
+                return m2; // 若m2结果为测量值,则返回m2的结果
+            }
+            else if (m1.IsMeasure == 0 && m2.IsMeasure == 0)
+            {
+                return m1; // 若两结果均为预测值,则取延迟小的结果作为输出结果
+            }
+        }
+        else if (m1_ok)
+        {
+            return m1;
+        }
+        else if (m2_ok)
+        {
+            return m2;
+        }
+        else
+        {
+            return m1; // 都无效时保持 m1
+        }
+    }
+}
+
+// 决策函数:仅有一个PoseData时,直接返回该结果
+PoseData LidarFusionNode::decideOne(const PoseData &m1)
+{
+    return m1;
+}
+
+// 获取最佳结果函数:从第二个雷达队列中获取数据,并与第一个雷达的数据进行决策,返回最佳结果
+PoseData LidarFusionNode::getBestResult(PoseData &data1)
+{
+    bool has_data2 = false;
+    PoseData data2, result;
+    lidar2_mutex_.lock();
+    if (!lidarCombine2.empty())
+    {
+        while (lidarCombine2.size() > 1)
+            lidarCombine2.pop();
+        data2 = lidarCombine2.front();
+        lidarCombine2.pop();
+        has_data2 = true;
+    }
+    lidar2_mutex_.unlock();
+
+    if (has_data2)
+    {
+        result = decideTwo(data1, data2);
+    }
+    else
+    {
+        result = decideOne(data1);
+    }
+    return result;
+}
+
+// 拼接处理线程函数:从待处理云队列中取出数据,进行点云拼接和姿态拟合,并发布结果
+void LidarFusionNode::c1_mergeThreadLoop()
+{
+    while (c1_thread_running_.load(std::memory_order_relaxed) && ros::ok())
+    {
+
+        PendingCloud data;
+        bool has_data = false;
+        // 从待处理云队列中取出一个数据
+        {
+            std::lock_guard<std::mutex> pending_lg(c1_pending_mutex_);
+            if (!c1_pending_clouds_.empty())
+            {
+                data = c1_pending_clouds_.front();
+                c1_pending_clouds_.pop();
+                has_data = true;
+            }
+        }
+        // 进行点云拼接和姿态拟合
+        if (has_data)
+        {
+            CloudType merged_cloud;
+
+            size_t total_points = 0;
+            // 计算总点数以预分配内存
+            for (int lidar_id : data.ready_lidar_ids)
+            {
+                total_points += data.ready_clouds[lidar_id].size();
+            }
+            merged_cloud.reserve(total_points);
+            // 拼接各雷达点云
+            for (int lidar_id : data.ready_lidar_ids)
+            {
+                const auto &cloud = data.ready_clouds[lidar_id];
+                merged_cloud.insert(merged_cloud.end(), cloud.begin(), cloud.end());
+            }
+            double gps_timestamp = data.header.stamp.toNSec() / 1e9;
+            bool save_pcd = true;
+            if (save_pcd)
+            {
+                // Save debug cloud when timestamp is within 1 ms of the target
+                constexpr double target_ts = 10.100152;
+                constexpr double ts_tolerance = 0.001; // seconds (1 ms)
+                cout << std::fixed << std::setprecision(6) << "target_ts: " << target_ts << endl;
+                cout << std::fixed << std::setprecision(6) << "gps_timestamp: " << gps_timestamp << endl;
+                if (std::abs(gps_timestamp - target_ts) <= ts_tolerance)
+                {
+                    std::ostringstream oss;
+                    oss << "/tmp/processedCloudC1" << std::fixed << std::setprecision(6) << gps_timestamp << ".pcd";
+                    const std::string pcd_path = oss.str();
+                    pcl::io::savePCDFileBinary(pcd_path, merged_cloud);
+                    std::cout << "[CylinderFitter] cp or dir NaN at t=" << std::fixed << std::setprecision(6)
+                              << gps_timestamp << ", saved cloud: " << pcd_path << std::endl;
+                }
+            }
+
+            TicToc t1;
+            // 提取Rocket点云:rocketExtract 仍使用单块 6 维 ROI,这里取 roi_1 的前 6 个元素
+            CloudType rocket_cloud = rocketExtract(
+                merged_cloud,
+                std::array<float, 6>{roi_1[0], roi_1[1],
+                                     roi_1[2], roi_1[3],
+                                     roi_1[10], roi_1[5]},
+                gps_timestamp);
+
+            LOG(INFO) << std::fixed << std::setprecision(6) << "gps_timestamp: " << gps_timestamp << std::endl;
+            LOG(INFO) << "C1 rocket rocketExtract: " << t1.toc() << " ms" << std::endl;
+
+            getTempResult1_ = false;
+            // 提交异步拟合任务
+            if (!shutting_down_.load(std::memory_order_relaxed) && fitting_enabled_.load(std::memory_order_relaxed))
+            {
+                try
+                {
+                    auto fit_task = [this, rocket_cloud, gps_timestamp, lidar_start = data.lidar_start_time]()
+                    {
+                        if (shutting_down_.load(std::memory_order_relaxed) || !fitting_enabled_.load(std::memory_order_relaxed))
+                        {
+                            return PoseData();
+                        }
+
+                        ros::Time fit_start = ros::Time::now();
+                        CloudType::Ptr cloud_ptr(new CloudType(rocket_cloud));
+
+                        // double timestamp = lidar_start > 0.0f ? lidar_start : ros::Time::now().toSec();
+                        TicToc t_fit;
+                        PoseData pose = c1_fitter.fitCylinder(cloud_ptr, gps_timestamp, &c1_blind_planes_ship_);
+                        // 同步状态写入结果(0=正常,1=异常)
+                        LOG(INFO) << "C1 fitCylinder: " << t_fit.toc() << " ms" << std::endl;
+                        pose.time_sync_state = c1_time_sync_state_.load(std::memory_order_relaxed);
+                        if (shutting_down_.load(std::memory_order_relaxed))
+                        {
+                            return pose;
+                        }
+                        ros::Time end_time = ros::Time::now();
+
+                        pose.delay_time = lidar_start;
+
+                        double detection_time = end_time.toSec() - fit_start.toSec();
+                        // 更新预测器历史帧
+                        if (c1_predictor_ && pose.valid == 0 && !pose.bottomCenter.hasNaN() && !pose.bottomCenter.array().isInf().any())
+                        {
+                            c1_predictor_->setBlindZoneActive(c1_fitter.isBlindZoneActive());
+                            Eigen::Vector3f cen = pose.bottomCenter.cast<float>();
+                            Eigen::Vector3f ax = pose.axis.cast<float>();
+                            c1_predictor_->updateHistoryAndFilter(pose.timestamp, cen, ax, pose.fitResidual);
+                            // 记录最近一次时间基准
+                            s_last_c1_stamp_sec.store(pose.timestamp, std::memory_order_relaxed);
+                            s_last_c1_delay_sec.store(pose.delay_time, std::memory_order_relaxed);
+                            posedetect_vis::publishPoint(cen, "posedetect_points", 101, 0.1f, 0.95f, 0.1f, 0.95f, 1.0f);
+                        }
+
+                        decision_any_cv_.notify_all();
+                        // 记录 C1 拟合结果
+                        {
+                            static std::ofstream log_c1_fit("/home/zxy/results/c1_fit_result.log", std::ios::app);
+                            static bool header_written_c1 = false;
+                            if (!header_written_c1 && log_c1_fit.is_open())
+                            {
+                                log_c1_fit << "timestamp delay_time bottomCenter_x bottomCenter_y bottomCenter_z axis_x axis_y axis_z elevation azimuth fitResidual valid cloudNumber is_predict IsMeasure height_source" << std::endl;
+                                log_c1_fit.flush();
+                                header_written_c1 = true;
+                            }
+                            if (log_c1_fit.is_open())
+                            {
+                                log_c1_fit << std::fixed << std::setprecision(6)
+                                           << pose.timestamp << " "
+                                           << pose.delay_time << " "
+                                           << pose.bottomCenter.x() << " " << pose.bottomCenter.y() << " " << pose.bottomCenter.z() << " "
+                                           << pose.axis.x() << " " << pose.axis.y() << " " << pose.axis.z() << " "
+                                           << pose.elevation_angle << " "
+                                           << pose.azimuth_angle << " "
+                                           << pose.fitResidual << " "
+                                           << (int)pose.valid << " "
+                                           << pose.cloudNumber << " "
+                                           << (int)pose.is_predict << " "
+                                           << (int)pose.IsMeasure << " "
+                                           << pose.bottom_height_source << std::endl;
+                                log_c1_fit.flush();
+                            }
+                        }
+                        return pose;
+                    };
+                    c1_fit_tasks_++;
+                    c1_fit_pool_.AddTask(std::move(fit_task));
+                }
+                catch (const std::exception &e)
+                {
+                    ROS_ERROR("c1 async fit task submission failed: %s", e.what());
+                }
+            }
+            // 发布拼接后的点云
+            sensor_msgs::PointCloud2 output_msg;
+            pcl::toROSMsg(rocket_cloud, output_msg);
+            output_msg.header = data.header;
+            output_msg.header.frame_id = "map";
+
+            if (ros::ok())
+            {
+                c1_pub_.publish(output_msg);
+            }
+            c1_frames_++;
+            if ((c1_frames_ % 100 == 0) && ros::Time::now() - last_stat_time_ > ros::Duration(1.0))
+            {
+                // ROS_INFO_STREAM("[STAT] C1 frames=" << c1_frames_.load() << " fit_tasks=" << c1_fit_tasks_.load());
+                last_stat_time_ = ros::Time::now();
+            }
+        }
+    }
+    fitting_enabled_.store(false, std::memory_order_relaxed);
+    ROS_INFO("拼接线程已正常退出");
+}
+
+void LidarFusionNode::c2_mergeThreadLoop()
+{
+    while (c2_thread_running_.load(std::memory_order_relaxed) && ros::ok())
+    {
+        PendingCloud data;
+        bool has_data = false;
+
+        {
+            std::lock_guard<std::mutex> pending_lg(c2_pending_mutex_);
+            if (!c2_pending_clouds_.empty())
+            {
+                data = c2_pending_clouds_.front();
+                c2_pending_clouds_.pop();
+                has_data = true;
+            }
+        }
+        if (has_data)
+        {
+            CloudType merged_cloud;
+            size_t total_points = 0;
+            for (int lidar_id : data.ready_lidar_ids)
+            {
+                total_points += data.ready_clouds[lidar_id].size();
+            }
+            merged_cloud.reserve(total_points);
+            for (int lidar_id : data.ready_lidar_ids)
+            {
+                const auto &cloud = data.ready_clouds[lidar_id];
+                merged_cloud.insert(merged_cloud.end(), cloud.begin(), cloud.end());
+            }
+            double gps_timestamp = data.header.stamp.toNSec() / 1e9;
+            bool save_pcd = false;
+            if (save_pcd)
+            {
+                // Save debug cloud when timestamp is within 1 ms of the target
+                constexpr double target_ts = 5.900152;
+                constexpr double ts_tolerance = 0.001; // seconds (1 ms)
+                cout << std::fixed << std::setprecision(6) << "target_ts: " << target_ts << endl;
+                cout << std::fixed << std::setprecision(6) << "gps_timestamp: " << gps_timestamp << endl;
+                if (std::abs(gps_timestamp - target_ts) <= ts_tolerance)
+                {
+                    std::ostringstream oss;
+                    oss << "/tmp/processedCloudC2" << std::fixed << std::setprecision(6) << gps_timestamp << ".pcd";
+                    const std::string pcd_path = oss.str();
+                    pcl::io::savePCDFileBinary(pcd_path, merged_cloud);
+                    std::cout << "[CylinderFitter] cp or dir NaN at t=" << std::fixed << std::setprecision(6)
+                              << gps_timestamp << ", saved cloud: " << pcd_path << std::endl;
+                }
+            }
+
+            // 提取Rocket点云:rocketExtract 仍使用单块 6 维 ROI,这里取 roi_2 的前 6 个元素
+            ros::Time extract_start_time = ros::Time::now();
+            CloudType rocket_cloud = rocketExtract(
+                merged_cloud,
+                std::array<float, 6>{roi_2[0], roi_2[1],
+                                     roi_2[2], roi_2[3],
+                                     roi_2[10], roi_2[5]},
+                gps_timestamp);
+            ros::Time extract_end_time = ros::Time::now();
+            const double extract_time_ms = (extract_end_time - extract_start_time).toSec() * 1000.0; // ms
+            // ROS_INFO_STREAM("C2 Rocket Extract time: " << std::fixed << std::setprecision(3)
+            //                 << extract_time_ms << " ms");
+            if (!shutting_down_.load(std::memory_order_relaxed) && fitting_enabled_.load(std::memory_order_relaxed))
+            {
+                try
+                {
+                    auto fit_task = [this, rocket_cloud, gps_timestamp, lidar_start = data.lidar_start_time]()
+                    {
+                        if (shutting_down_.load(std::memory_order_relaxed) || !fitting_enabled_.load(std::memory_order_relaxed))
+                            return PoseData();
+                        CloudType::Ptr cloud_ptr(new CloudType(rocket_cloud));
+                        // printf("%s %f \n" , "lidar_start = ", lidar_start);
+                        // printf("%s %d \n" , "cloud_ptr = ", static_cast<double>(cloud_ptr->header.stamp) / 1e9);
+
+                        // double timestamp = lidar_start > 0.0f ? lidar_start : ros::Time::now().toSec();
+                        // std::cout << "gps_timestamp: " << gps_timestamp << std::endl;
+                        PoseData pose = c2_fitter.fitCylinder(cloud_ptr, gps_timestamp, &c2_blind_planes_ship_);
+                        // 同步状态写入结果(0=正常,1=异常)
+                        pose.time_sync_state = c2_time_sync_state_.load(std::memory_order_relaxed);
+                        if (shutting_down_.load(std::memory_order_relaxed))
+                            return pose;
+                        ros::Time end_time = ros::Time::now();
+                        // if (lidar_start > 0) {
+                        //     pose.delay_time = lidar_start;
+                        // } else {
+                        //     pose.delay_time = 0;
+                        // }
+
+                        pose.delay_time = lidar_start;
+
+                        // 更新预测器历史帧
+                        if (c2_predictor_ && pose.valid == 0 && !pose.bottomCenter.hasNaN() && !pose.bottomCenter.array().isInf().any())
+                        {
+                            c2_predictor_->setBlindZoneActive(c2_fitter.isBlindZoneActive());
+                            Eigen::Vector3f cen = pose.bottomCenter.cast<float>();
+                            Eigen::Vector3f ax = pose.axis.cast<float>();
+                            c2_predictor_->updateHistoryAndFilter(pose.timestamp, cen, ax, pose.fitResidual);
+                            // 记录最近一次时间基准(C2)
+                            s_last_c2_stamp_sec.store(pose.timestamp, std::memory_order_relaxed);
+                            s_last_c2_delay_sec.store(pose.delay_time, std::memory_order_relaxed);
+                            // Eigen::Vector3f quan_cen(0.0f, 0.0f, 0.0f);
+                            posedetect_vis::publishPoint(cen, "posedetect_points", 102, 0.1f, 0.95f, 0.1f, 0.95f, 1.0f);
+                        }
+                        decision_any_cv_.notify_all();
+                        ROS_DEBUG_STREAM_THROTTLE(10, "c2 fitCylinder completed");
+                        // 记录 C2 拟合结果
+                        {
+                            static std::ofstream log_c2_fit("/home/zxy/results/c2_fit_result.log", std::ios::app);
+                            static bool header_written_c2 = false;
+                            if (!header_written_c2 && log_c2_fit.is_open())
+                            {
+                                log_c2_fit << "timestamp delay_time bottomCenter_x bottomCenter_y bottomCenter_z axis_x axis_y axis_z elevation azimuth fitResidual valid cloudNumber is_predict IsMeasure height_source" << std::endl;
+                                log_c2_fit.flush();
+                                header_written_c2 = true;
+                            }
+                            if (log_c2_fit.is_open())
+                            {
+                                log_c2_fit << std::fixed << std::setprecision(6)
+                                           << pose.timestamp << " "
+                                           << pose.delay_time << " "
+                                           << pose.bottomCenter.x() << " " << pose.bottomCenter.y() << " " << pose.bottomCenter.z() << " "
+                                           << pose.axis.x() << " " << pose.axis.y() << " " << pose.axis.z() << " "
+                                           << pose.elevation_angle << " "
+                                           << pose.azimuth_angle << " "
+                                           << pose.fitResidual << " "
+                                           << (int)pose.valid << " "
+                                           << pose.cloudNumber << " "
+                                           << (int)pose.is_predict << " "
+                                           << (int)pose.IsMeasure << " "
+                                           << pose.bottom_height_source << std::endl;
+                                log_c2_fit.flush();
+                            }
+                        }
+                        return pose;
+                    };
+                    c2_fit_tasks_++;
+                    c2_fit_pool_.AddTask(std::move(fit_task));
+                }
+                catch (const std::exception &e)
+                {
+                    ROS_ERROR("c2 async fit task submission failed: %s", e.what());
+                }
+            }
+
+            sensor_msgs::PointCloud2 output_msg;
+            pcl::toROSMsg(rocket_cloud, output_msg);
+            output_msg.header = data.header;
+            output_msg.header.frame_id = "map";
+
+            if (ros::ok())
+            {
+                c2_pub_.publish(output_msg);
+            }
+            c2_frames_++;
+            if ((c2_frames_ % 100 == 0) && ros::Time::now() - last_stat_time_ > ros::Duration(1.0))
+            {
+                ROS_INFO_STREAM("[STAT] C2 frames=" << c2_frames_.load() << " fit_tasks=" << c2_fit_tasks_.load());
+                last_stat_time_ = ros::Time::now();
+            }
+        }
+    }
+    fitting_enabled_.store(false, std::memory_order_relaxed);
+    ROS_INFO("拼接线程已正常退出");
+}
+
+// 单机1四个雷达的点云处理函数:包括坐标转换和ROI提取
+CloudType LidarFusionNode::c1_funcLidar1(const sensor_msgs::PointCloud2ConstPtr &cloud)
+{
+    CloudType local_cloud;
+    pcl::fromROSMsg(*cloud, local_cloud);
+
+    TransformPointCloud(local_cloud, local_cloud, C1T1_to_Shipf_);
+
+    extractROI(local_cloud, roi_1);
+
+    return local_cloud;
+}
+
+CloudType LidarFusionNode::c1_funcLidar2(const sensor_msgs::PointCloud2ConstPtr &cloud)
+{
+    CloudType local_cloud;
+    pcl::fromROSMsg(*cloud, local_cloud);
+
+    TransformPointCloud(local_cloud, local_cloud, C1T2_to_Shipf_);
+
+    extractROI(local_cloud, roi_1);
+
+    return local_cloud;
+}
+
+CloudType LidarFusionNode::c1_funcLidar3(const sensor_msgs::PointCloud2ConstPtr &cloud)
+{
+    CloudType local_cloud;
+    pcl::fromROSMsg(*cloud, local_cloud);
+
+    TransformPointCloud(local_cloud, local_cloud, C1T3_to_Shipf_);
+
+    extractROI(local_cloud, roi_1);
+
+    return local_cloud;
+}
+CloudType LidarFusionNode::c1_funcLidar4(const sensor_msgs::PointCloud2ConstPtr &cloud)
+{
+    CloudType local_cloud;
+    pcl::fromROSMsg(*cloud, local_cloud);
+
+    TransformPointCloud(local_cloud, local_cloud, C1T4_to_Shipf_);
+
+    extractROI(local_cloud, roi_1);
+
+    return local_cloud;
+}
+
+CloudType LidarFusionNode::c2_funcLidar1(const sensor_msgs::PointCloud2ConstPtr &cloud)
+{
+    CloudType local_cloud;
+    pcl::fromROSMsg(*cloud, local_cloud);
+
+    TransformPointCloud(local_cloud, local_cloud, C2T1_to_Shipf_);
+
+    extractROI(local_cloud, roi_2);
+
+    return local_cloud;
+}
+
+CloudType LidarFusionNode::c2_funcLidar2(const sensor_msgs::PointCloud2ConstPtr &cloud)
+{
+    CloudType local_cloud;
+    pcl::fromROSMsg(*cloud, local_cloud);
+
+    TransformPointCloud(local_cloud, local_cloud, C2T2_to_Shipf_);
+
+    extractROI(local_cloud, roi_2);
+
+    return local_cloud;
+}
+
+CloudType LidarFusionNode::c2_funcLidar3(const sensor_msgs::PointCloud2ConstPtr &cloud)
+{
+    CloudType local_cloud;
+    pcl::fromROSMsg(*cloud, local_cloud);
+
+    TransformPointCloud(local_cloud, local_cloud, C2T3_to_Shipf_);
+
+    extractROI(local_cloud, roi_2);
+
+    return local_cloud;
+}
+CloudType LidarFusionNode::c2_funcLidar4(const sensor_msgs::PointCloud2ConstPtr &cloud)
+{
+    CloudType local_cloud;
+    pcl::fromROSMsg(*cloud, local_cloud);
+
+    TransformPointCloud(local_cloud, local_cloud, C2T4_to_Shipf_);
+
+    extractROI(local_cloud, roi_2);
+
+    return local_cloud;
+}
+
+// Rocket点云提取函数:对合并后的点云进行Rocket点云提取处理
+CloudType LidarFusionNode::rocketExtract(const CloudType &cloud, const std::array<float, 6> &roi, double gps_timestamp)
+{
+
+    bool useFilter = true;
+    if (useFilter)
+    {
+
+        // TicToc t_filter;
+        // 先利用网格降采样
+        float rocketRadius = params_.rocket_radius;
+        pcl::PointCloud<PointType> tempCloud = cloud;
+        // cout<<"rocket_extract_voxel_ratio_ "<< rocket_extract_voxel_ratio_<<endl;
+        voxel_filter(tempCloud, rocketRadius * static_cast<float>(rocket_extract_voxel_ratio_));
+        // std::cout<<"Rocket Extract voxel_filter: "<<t_filter.toc()<<" ms"<<std::endl;
+        (void)gps_timestamp; // unused in clustering path, retained for interface compatibility
+
+        // TicToc t_rope;
+        // 绳索滤除:在指定 ROI 内用 PCA 线性度去除绳索点云
+        CloudType result_cloud;
+        if (1)
+        {
+
+            if (!cloud.empty())
+            {
+                result_cloud = FilterRopeByLinearity(
+                    tempCloud,
+                    rope_filter_roi_,
+                    /*cluster_tolerance=*/0.3f,
+                    /*min_cluster_size=*/5,
+                    /*linearity_thresh=*/static_cast<float>(rope_filter_linearity_thresh_),
+                    /*max_cross_radius=*/0.15f);
+            }
+        }
+        // std::cout<<"Rocket Extract FilterRopeByLinearity: "<<t_rope.toc()<<" ms"<<std::endl;
+
+        // TicToc t_cluster;
+        // 2. 聚类并保留点数最多的点云块
+        const double cluster_threshold = rocket_cluster_threshold_; // 欧式聚类距离阈值(米)
+        const int min_cluster_size = rocket_cluster_min_size_;      // 最小有效聚类点数
+        // cout<<"cluster threshold "<< cluster_threshold<< "  min_cluster_size"<< min_cluster_size<<endl;
+
+        result_cloud = clusterKeepLargest(result_cloud, cluster_threshold, min_cluster_size);
+        if (result_cloud.empty())
+        {
+            return CloudType();
+        }
+
+        // std::cout<<"Rocket Extract clusterKeepLargest: "<<t_cluster.toc()<<" ms"<<std::endl;
+        return result_cloud;
+    }
+    else
+    {
+        return cloud;
+    }
+}
+
+// 决策线程函数:从两个雷达的姿态队列中取出数据,进行决策并输出结果
+void LidarFusionNode::decisionThreadLoop()
+{
+    int csvCount = 0, simuCount = 0, realCount = 0, testCount = 0;
+
+    unsigned int recursion_time = 0, recursion_overflag = 0;
+    double COM_ZL[3] = {0}, pos_bottom_N[3] = {0};
+    double COM_V[3] = {0}, vel[3] = {0};
+    double COM_att[3] = {0}, att_hook[3] = {0};
+
+    double SheXiang = SheXiang_para;
+    double att[3] = {0}, att_net[3] = {0};
+    double Lever[3] = {Lever_para[0], Lever_para[1], Lever_para[2]};
+    double JBottom2JM_JT[3] = {JT_para[0], JT_para[1], JT_para[2]};
+
+    ros::Rate rate(100.0);
+    while (decision_running_.load(std::memory_order_relaxed) && ros::ok())
+    {
+        // 直接进行两组预测
+        PoseData result;
+        bool have_any = false;
+        const double now_sec = ros::Time::now().toSec();
+        // 在预测前,按时间窗口裁剪历史,避免长期无更新导致历史积压预测无法停止
+        // 窗口选择:2s
+        double hist_max_age = 2;
+        if (c1_predictor_)
+            c1_predictor_->pruneHistoryByTime(now_sec, hist_max_age);
+        if (c2_predictor_)
+            c2_predictor_->pruneHistoryByTime(now_sec, hist_max_age);
+        // 以“最新帧timestamp + (now - 最新delay_time)”作为各自的预测时间
+        double c1_last_ts = s_last_c1_stamp_sec.load(std::memory_order_relaxed);
+        double c2_last_ts = s_last_c2_stamp_sec.load(std::memory_order_relaxed);
+        double c1_last_delay = s_last_c1_delay_sec.load(std::memory_order_relaxed);
+        double c2_last_delay = s_last_c2_delay_sec.load(std::memory_order_relaxed);
+        double c1_delay = now_sec - c1_last_delay;
+        double c2_delay = now_sec - c2_last_delay;
+        double t_pred_c1 = (c1_last_ts > 0.0 && c1_last_delay > 0.0) ? (c1_last_ts + (now_sec - c1_last_delay)) : now_sec;
+        double t_pred_c2 = (c2_last_ts > 0.0 && c2_last_delay > 0.0) ? (c2_last_ts + (now_sec - c2_last_delay)) : now_sec;
+        // std::cout << "c1_last_ts = "<< std::fixed << std::setprecision(6) << c1_last_ts <<std::endl;
+        // std::cout << "c1_last_delay = "<< std::fixed << std::setprecision(6) << c1_last_delay <<std::endl;
+        // std::cout << "c1_delay = "<< std::fixed << std::setprecision(6) << c1_delay <<std::endl;
+        // 由 center/axis 构建 PoseData(预测值)改为调用头文件中的内联方法
+
+        PoseData c1_pred, c2_pred;
+        bool has_c1 = false;
+        bool has_c2 = false;
+
+        if (simu == 1)
+        {
+            // ROS_INFO("CAN%d csvCount = %d",this->can_interface_,csvCount);
+            const auto &data = csv_data_list[csvCount];
+            csvCount++;
+            if (csvCount >= print_count)
+            {
+                csvCount = 0;
+            }
+
+            // 仿真数据输出到 can_main & can_hpyo
+            can_main.setValue_coor_x(data.x);
+            can_main.setValue_coor_y(data.y);
+            can_main.setValue_coor_z(data.z);
+            can_main.setValue_projection_B(data.pitch);
+            can_main.setValue_projection_A(data.yaw);
+            can_main.setValue_output_delay(data.delay);
+            can_main.setValue_state((char)(data.radar_state));
+            can_main.setValue_timestate(1);
+            can_main.setValue_visible((char)(data.is_visible));
+            can_main.setValue_valid((char)(data.solve_state));
+            can_main.setValue_timestamp(data.timestamp * 1e3);
+            can_main.setValue_state_lidar((char)(data.each_lidarstate));
+            can_main.setValue_transplane((char)(data.is_cross_plane));
+            can_main.setValue_predict((char)(data.is_prediction));
+            can_main.setValue_state_value(0);
+            can_main.setValue_count(simuCount);
+            can_main.setValue_datasource((char)(data.data_state));
+            
+            can_hpyo.setValue_coor_x(data.x);
+            can_hpyo.setValue_coor_y(data.y);
+            can_hpyo.setValue_coor_z(data.z);
+            can_hpyo.setValue_projection_B(data.pitch);
+            can_hpyo.setValue_projection_A(data.yaw);
+            can_hpyo.setValue_output_delay(data.delay);
+            can_hpyo.setValue_state((char)(data.radar_state));
+            can_hpyo.setValue_timestate(1);
+            can_hpyo.setValue_visible((char)(data.is_visible));
+            can_hpyo.setValue_valid((char)(data.solve_state));
+            can_hpyo.setValue_timestamp(data.timestamp * 1e3);
+            can_hpyo.setValue_state_lidar((char)(data.each_lidarstate));
+            can_hpyo.setValue_transplane((char)(data.is_cross_plane));
+            can_hpyo.setValue_predict((char)(data.is_prediction));
+            can_hpyo.setValue_state_value(0);
+            can_hpyo.setValue_count(simuCount);
+            can_hpyo.setValue_datasource((char)(data.data_state));
+
+            simuCount++;
+            if (simuCount > 255)
+            {
+                simuCount = 0;
+            }
+        }
+        else if (simu == 2)
+        {
+            // double COM_ZL[3] = {-100.0, -200.0, 300.0};    // 箭下传数据,单位:米
+            // double SheXiang = 130.0;               // 射向 单位:度
+            // double att[3] = {5.0, -3.0, 100.0};     // 惯组输出的网系姿态,单位:度
+            // double Lever[3] = {1, -60, 1};     // 摇心在网系坐标系下的杆臂,单位:米
+            PoseData result_out;
+            result_out.ProjectedCenter.x() = 0;
+            result_out.ProjectedCenter.y() = 0;
+            result_out.axis.x() = 0.0;
+            result_out.axis.y() = 0.0;
+            result_out.axis.z() = 0.0;
+
+            result_out.cloudNumber = 0;
+            result_out.fitResidual = 0.0;
+            result_out.is_predict = 0.0;
+
+            // double COM_ZL[3] = {0};
+            // double COM_V[3] = {0};
+            // double COM_att[3] = {0};
+            // double SheXiang = SheXiang_para;
+            // double att[3] = {0};
+            // double Lever[3] = {Lever_para[0], Lever_para[1], Lever_para[2]};
+            // double JBottom2JM_JT[3] = {JT_para[0], JT_para[1],JT_para[2]};
+            recursion_time++;
+            if (udp_client.get_update_flag())
+            {
+                udp_client.set_update_flag(0);
+                recursion_time = 0;
+                recursion_overflag = 0;
+
+                COM_V[0] = udp_client.get_vx_value();
+                COM_V[1] = udp_client.get_vy_value();
+                COM_V[2] = udp_client.get_vz_value();
+
+                COM_ZL[0] = udp_client.get_x_value() + COM_V[0] * delay_JT_ms / 1000.0;
+                COM_ZL[1] = udp_client.get_y_value() + COM_V[1] * delay_JT_ms / 1000.0;
+                COM_ZL[2] = udp_client.get_z_value() + COM_V[2] * delay_JT_ms / 1000.0;
+
+                COM_att[0] = udp_client.get_pitch_value();
+                COM_att[1] = udp_client.get_yaw_value();
+                COM_att[2] = udp_client.get_roll_value();
+
+                can_main.setValue_predict(0x01);
+                can_hpyo.setValue_predict(0x01);
+                result_out.IsMeasure = 0x01;
+            }
+            else if (udp_client_bak.get_update_flag())
+            {
+                udp_client_bak.set_update_flag(0);
+                recursion_time = 0;
+                recursion_overflag = 0;
+
+                COM_V[0] = udp_client_bak.get_vx_value();
+                COM_V[1] = udp_client_bak.get_vy_value();
+                COM_V[2] = udp_client_bak.get_vz_value();
+
+                COM_ZL[0] = udp_client_bak.get_x_value() + COM_V[0] * delay_JT_ms / 1000.0;
+                COM_ZL[1] = udp_client_bak.get_y_value() + COM_V[1] * delay_JT_ms / 1000.0;
+                COM_ZL[2] = udp_client_bak.get_z_value() + COM_V[2] * delay_JT_ms / 1000.0;
+
+                COM_att[0] = udp_client_bak.get_pitch_value();
+                COM_att[1] = udp_client_bak.get_yaw_value();
+                COM_att[2] = udp_client_bak.get_roll_value();
+
+                can_main.setValue_predict(0x01);
+                can_hpyo.setValue_predict(0x01);
+                result_out.IsMeasure = 0x01;
+            }
+            else
+            {
+                if (recursion_time < 200)
+                {
+                    recursion_overflag = 0;
+                    // 递推
+                    COM_ZL[0] += COM_V[0] * 0.01;
+                    COM_ZL[1] += COM_V[1] * 0.01;
+                    COM_ZL[2] += COM_V[2] * 0.01;
+
+                    can_main.setValue_predict(0x00);
+                    can_hpyo.setValue_predict(0x00);
+                    result_out.IsMeasure = 0x00;
+                }
+                else
+                {
+                    recursion_overflag = 1;
+                }
+
+            }
+
+            double ZL_distance = sqrt(COM_ZL[0] * COM_ZL[0] + COM_ZL[1] * COM_ZL[1] + COM_ZL[2] * COM_ZL[2]);
+            double COM_att_norm = sqrt(COM_att[0] * COM_att[0] + COM_att[1] * COM_att[1] + COM_att[2] * COM_att[2]);
+
+            if (can_main.get_rec_flag())
+            {
+                att[0] = can_main.get_pitch_value();
+                att[1] = can_main.get_roll_value();
+                att[2] = can_main.get_yaw_value();                 
+            }
+            else if (can_hpyo.get_rec_flag())
+            {
+                att[0] = can_hpyo.get_pitch_value();
+                att[1] = can_hpyo.get_roll_value();
+                att[2] = can_hpyo.get_yaw_value();
+            }
+            else
+            {
+                att[0] = GZ_para[0];
+                att[1] = GZ_para[1];
+                att[2] = GZ_para[2];
+            }
+
+            double COM_ZL_processed[3] = {COM_ZL[0], COM_ZL[1], COM_ZL[2]};
+            double JBottom_ZL[3] = {0};
+            calculate_ZL_WX(COM_ZL_processed, COM_att, JBottom2JM_JT, JBottom_ZL);
+
+            double CM_WX[3] = {0};
+            calculate_CM_WX(COM_ZL_processed, SheXiang, att, Lever, CM_WX);
+
+            double JBottom_WX[3] = {0};
+            calculate_CM_WX(JBottom_ZL, SheXiang, att, Lever, JBottom_WX);
+
+            double CM_WX_15s[3] = {CM_WX[0], -CM_WX[2], CM_WX[1]};
+            double JBottom_WX_15s[3] = {JBottom_WX[0], -JBottom_WX[2], JBottom_WX[1]};
+
+            double rocks[3];
+            double rock[3];
+
+            rocks[0] = CM_WX_15s[0] - JBottom_WX_15s[0];
+            rocks[1] = CM_WX_15s[1] - JBottom_WX_15s[1];
+            rocks[2] = CM_WX_15s[2] - JBottom_WX_15s[2];
+
+            double rock_norm = sqrt(rocks[0] * rocks[0] + rocks[1] * rocks[1] + rocks[2] * rocks[2]);
+            if (fabs(rock_norm) < 1e-9)
+            {
+                rock[0] = rock[1] = rock[2] = 0.0;
+            }
+            else
+            {
+                rock[0] = rocks[0] / rock_norm;
+                rock[1] = rocks[1] / rock_norm;
+                rock[2] = rocks[2] / rock_norm;
+            }
+
+            double azimuth_deg, elevation_deg, theta;
+            azimuth_deg = atan2(rock[1], rock[0]) * 180.0f / PI;
+            if (azimuth_deg < 0)
+            {
+                azimuth_deg += 360.0;
+            }
+
+            theta = acos(rock[2]) * 180.0f / PI;
+            elevation_deg = 90.0 - theta;
+            if (elevation_deg < 0)
+            {
+                elevation_deg = 0.0;
+            }
+
+            char flag_chuanwang = 0;
+            double J0_WX_15s[3] = {0.0, 0.0, 0.0};
+
+            if (JBottom_WX[1] < 0)
+            {
+                flag_chuanwang = 1;
+
+                double J0_WX[3];
+                double diff[3] = {
+                    JBottom_WX[0] - CM_WX[0],
+                    JBottom_WX[1] - CM_WX[1],
+                    JBottom_WX[2] - CM_WX[2]};
+
+                double denominator = fabs(JBottom_WX[1]) + CM_WX[1];
+                if (fabs(denominator) < 1e-9)
+                {
+                    J0_WX[0] = CM_WX[0];
+                    J0_WX[1] = CM_WX[1];
+                    J0_WX[2] = CM_WX[2];
+                }
+                else
+                {
+                    double scalar = CM_WX[1] / denominator;
+                    J0_WX[0] = diff[0] * scalar + CM_WX[0];
+                    J0_WX[1] = diff[1] * scalar + CM_WX[1];
+                    J0_WX[2] = diff[2] * scalar + CM_WX[2];
+                }
+
+                J0_WX_15s[0] = J0_WX[0];
+                J0_WX_15s[1] = -J0_WX[2];
+                J0_WX_15s[2] = JBottom_WX[1];
+
+                if ((ZL_distance < 1500.0) && (ZL_distance > 0.01) && (COM_att_norm > 0.0001))
+                {
+                    can_main.setValue_visible(0x00);
+                    can_main.setValue_valid(0x00);
+                    can_main.setValue_transplane(flag_chuanwang);
+
+                    can_hpyo.setValue_visible(0x00);
+                    can_hpyo.setValue_valid(0x00);
+                    can_hpyo.setValue_transplane(flag_chuanwang);
+
+                    result_out.valid = 0x00;
+                    result_out.Isvisible = 0x00;
+                    result_out.TransPlane = flag_chuanwang;
+                }
+                else
+                {
+                    can_main.setValue_visible(0x01);
+                    can_main.setValue_valid(0x01);
+                    can_main.setValue_transplane(0x00);
+
+                    can_hpyo.setValue_visible(0x01);
+                    can_hpyo.setValue_valid(0x01);
+                    can_hpyo.setValue_transplane(0x00);
+
+                    result_out.valid = 0x01;
+                    result_out.Isvisible = 0x01;
+                    result_out.TransPlane = 0x00;
+                }
+
+                can_main.setValue_coor_x((float)J0_WX_15s[0]);
+                can_main.setValue_coor_y((float)J0_WX_15s[1]);
+                can_main.setValue_coor_z((float)J0_WX_15s[2]);
+                // can_main.setValue_transplane(flag_chuanwang);
+
+                can_hpyo.setValue_coor_x((float)J0_WX_15s[0]);
+                can_hpyo.setValue_coor_y((float)J0_WX_15s[1]);
+                can_hpyo.setValue_coor_z((float)J0_WX_15s[2]);
+                // can_hpyo.setValue_transplane(flag_chuanwang);
+
+                result_out.bottomCenter.x() = (float)J0_WX_15s[0];
+                result_out.bottomCenter.y() = (float)J0_WX_15s[1];
+                result_out.bottomCenter.z() = (float)J0_WX_15s[2];
+                // result_out.TransPlane = flag_chuanwang;
+            }
+            else
+            {
+                if ((ZL_distance < 1500.0) && (ZL_distance > 0.01) && (COM_att_norm > 0.0001))
+                {
+                    can_main.setValue_visible(0x00);
+                    can_main.setValue_valid(0x00);
+
+                    can_hpyo.setValue_visible(0x00);
+                    can_hpyo.setValue_valid(0x00);
+
+                    result_out.valid = 0x00;
+                    result_out.Isvisible = 0x00;
+                }
+                else
+                {
+                    can_main.setValue_visible(0x01);
+                    can_main.setValue_valid(0x01);
+
+                    can_hpyo.setValue_visible(0x01);
+                    can_hpyo.setValue_valid(0x01);
+
+                    result_out.valid = 0x01;
+                    result_out.Isvisible = 0x01;
+                }
+
+                can_main.setValue_coor_x((float)JBottom_WX_15s[0]);
+                can_main.setValue_coor_y((float)JBottom_WX_15s[1]);
+                can_main.setValue_coor_z((float)JBottom_WX_15s[2]);
+                can_main.setValue_transplane(flag_chuanwang);
+
+                can_hpyo.setValue_coor_x((float)JBottom_WX_15s[0]);
+                can_hpyo.setValue_coor_y((float)JBottom_WX_15s[1]);
+                can_hpyo.setValue_coor_z((float)JBottom_WX_15s[2]);
+                can_hpyo.setValue_transplane(flag_chuanwang);
+
+                result_out.bottomCenter.x() = (float)JBottom_WX_15s[0];
+                result_out.bottomCenter.y() = (float)JBottom_WX_15s[1];
+                result_out.bottomCenter.z() = (float)JBottom_WX_15s[2];
+                result_out.TransPlane = flag_chuanwang;
+            }
+
+            // printf("azimuth_deg=%.6f\n", azimuth_deg);
+            // printf("elevation_deg=%.6f\n", elevation_deg);
+
+            // printf("穿网前底部中心点(网系坐标系下)结果:%.6f %.6f %.6f\n",
+            //     JBottom_WX_15s[0], JBottom_WX_15s[1], JBottom_WX_15s[2]);
+
+            // if (flag_chuanwang == 1) {
+            //     printf("穿网后火箭截面与网系的交点结果:%.6f %.6f %.6f\n",
+            //         J0_WX_15s[0], J0_WX_15s[1], J0_WX_15s[2]);
+            // } else {
+            //     printf("未穿网\n");
+            // }
+            if(recursion_overflag == 1)
+            {
+                can_main.setValue_visible(0x01);
+                can_main.setValue_valid(0x01);
+
+                can_hpyo.setValue_visible(0x01);
+                can_hpyo.setValue_valid(0x01);
+
+                result_out.valid = 0x01;
+                result_out.Isvisible = 0x01;
+            }
+
+            can_main.setValue_output_delay(0.0);
+            can_main.setValue_state(0x00);
+            can_main.setValue_timestate(0x00);
+            can_main.setValue_timestamp(0);
+            can_main.setValue_state_lidar(0x00);
+            can_main.setValue_state_value(0x00);
+            can_main.setValue_projection_B((float)elevation_deg);
+            can_main.setValue_projection_A((float)azimuth_deg);
+            can_main.setValue_count(testCount);
+
+            can_hpyo.setValue_output_delay(0.0);
+            can_hpyo.setValue_state(0x00);
+            can_hpyo.setValue_timestate(0x00);
+            can_hpyo.setValue_timestamp(0);
+            can_hpyo.setValue_state_lidar(0x00);
+            can_hpyo.setValue_state_value(0x00);
+            can_hpyo.setValue_projection_B((float)elevation_deg);
+            can_hpyo.setValue_projection_A((float)azimuth_deg);
+            can_hpyo.setValue_count(testCount);
+
+            result_out.timestamp = 0;
+            result_out.elevation_angle = (float)elevation_deg;
+            result_out.azimuth_angle = (float)azimuth_deg;
+            result_out.delay_time = 0.0;
+            result_out.angle_zero_state = 0x00;
+            result_out.each_lidar_status = 0x00;
+            result_out.time_sync_state = 0x00;
+            result_out.lidar_status = 0x00;
+
+            testCount++;
+            if (testCount > 255)
+            {
+                testCount = 0;
+            }
+
+            result_out.dataState = 1;
+            can_main.setValue_datasource((char)(result_out.dataState));
+            can_hpyo.setValue_datasource((char)(result_out.dataState));
+
+            auto now = std::chrono::system_clock::now();
+            // 转换为自1970年以来的毫秒数
+            uint64_t timestamp_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
+                                        now.time_since_epoch())
+                                        .count();
+
+            // 日志记录
+            logger.setValue_result(result_out.timestamp,
+                                   result_out.bottomCenter.x(), result_out.bottomCenter.y(), result_out.bottomCenter.z(),
+                                   result_out.axis.x(), result_out.axis.y(), result_out.axis.z(),
+                                   result_out.azimuth_angle, result_out.elevation_angle,
+                                   result_out.cloudNumber, result_out.fitResidual, result_out.TransPlane, result_out.delay_time,
+                                   result_out.Isvisible, result_out.IsMeasure, result_out.lidar_status, (int)(result_out.each_lidar_status), result_out.valid, result_out.dataState, timestamp_ms);
+        }
+        else if (simu == 3)
+        {
+
+            if (c1_predictor_)
+            {
+                Eigen::Vector3f c, a;
+                bool predict_success = false;
+
+                // 情况1:尝试预测(仅在延迟<=30秒时)
+                if (c1_delay <= 30.0 && c1_predictor_->predictOnly(t_pred_c1, c, a))
+                {
+                    c1_pred = makePoseFromPredict(c, a, 1, t_pred_c1);
+                    // if(c1_time_sync_state_.load(std::memory_order_relaxed))
+                    // {
+                    //     c1_pred.time_sync_state = 1;
+                    // }
+                    // else
+                    // {
+                    //     c1_pred.time_sync_state = 0;
+                    // }
+                    predict_success = true;
+
+                    // 根据时间逻辑设置状态
+                    double delay_ms = c1_delay * 1000.0;
+                    c1_pred.delay_time = delay_ms;
+                    if (delay_ms <= 50.0)
+                    {
+                        c1_pred.IsMeasure = 1;
+                        c1_pred.valid = 0; // 有效
+                    }
+                    else if (delay_ms <= 1000.0)
+                    {
+                        c1_pred.IsMeasure = 0;
+                        c1_pred.valid = 0; // 有效(延迟在50ms到1秒之间)
+                    }
+                    else if (c1_delay <= 30.0)
+                    {
+                        c1_pred.IsMeasure = 0;
+                        c1_pred.valid = 1; // 无效(延迟在1秒到30秒之间)
+                    }
+                    else
+                    {
+                        // 这里理论上不会执行,因为上面已经判断c1_delay <= 30.0
+                        c1_pred.valid = 1; // 无效
+                        c1_pred.IsMeasure = 0;
+                    }
+                }
+
+                // 处理预测结果
+                if (predict_success)
+                {
+                    if (c1_predictor_->time_first < 1e-9)
+                    {
+                        c1_predictor_->time_first = now_sec;
+                    }
+                    /*如果超过1s或者更新次数超过10次,就开始输出*/
+                    if (c1_pred.valid == 0 && (now_sec - c1_predictor_->time_first > 1.0 || c1_predictor_->num_inter_Update >= 100))
+                    {
+                        // 当前预测有效:更新上一次有效结果
+                        last_valid_result_c1_ = c1_pred;
+                        has_c1 = true;
+                    }
+                    else
+                    {
+                        // 当前预测无效:使用上一次有效结果
+                        if (last_valid_result_c1_.valid == 0)
+                        { // 确保上一次结果本身有效
+                            c1_pred = last_valid_result_c1_;
+                            c1_pred.timestamp = t_pred_c1; // 更新时间戳为当前时刻
+                            c1_pred.valid = 1;             // 标记为无效(因为当前预测无效)
+                            c1_pred.IsMeasure = 0;         // 不是实时测量
+                            has_c1 = true;
+                        }
+                        // 如果上一次结果也无效,则不输出任何结果
+                    }
+                }
+                else
+                {
+                    // 预测失败或延迟超过30秒:使用上一次有效结果
+                    if (last_valid_result_c1_.valid == 0)
+                    { // 确保上一次结果本身有效
+                        c1_pred = last_valid_result_c1_;
+                        c1_pred.timestamp = t_pred_c1; // 更新时间戳为当前时刻
+                        c1_pred.valid = 1;             // 标记为无效(因为当前没有有效预测)
+                        c1_pred.IsMeasure = 0;         // 不是实时测量
+                        has_c1 = true;
+                    }
+                    // 如果上一次结果无效,则不输出任何结果
+                }
+
+                // 可视化
+                if (has_c1)
+                {
+                    posedetect_vis::publishPredictCylinder(
+                        c1_pred.bottomCenter.cast<float>(),
+                        c1_pred.axis.cast<float>(),
+                        static_cast<float>(params_.rocket_radius),
+                        static_cast<float>(params_.rocket_length),
+                        1);
+                }
+            }
+            if (c2_predictor_)
+            {
+                Eigen::Vector3f c, a;
+                bool predict_success = false;
+
+                // 情况1:尝试预测(仅在延迟<=30秒时)
+                if (c2_delay <= 30.0 && c2_predictor_->predictOnly(t_pred_c2, c, a))
+                {
+                    c2_pred = makePoseFromPredict(c, a, 2, t_pred_c2);
+                    // if(c2_time_sync_state_.load(std::memory_order_relaxed))
+                    // {
+                    //     c2_pred.time_sync_state = 1;
+                    // }
+                    // else
+                    // {
+                    //     c2_pred.time_sync_state = 0;
+                    // }
+                    predict_success = true;
+
+                    // 根据时间逻辑设置状态
+                    double delay_ms = c2_delay * 1000.0;
+                    c2_pred.delay_time = delay_ms;
+                    if (delay_ms <= 50.0)
+                    {
+                        c2_pred.IsMeasure = 1;
+                        c2_pred.valid = 0; // 有效
+                    }
+                    else if (delay_ms <= 1000.0)
+                    {
+                        c2_pred.IsMeasure = 0;
+                        c2_pred.valid = 0; // 有效(延迟在50ms到1秒之间)
+                    }
+                    else if (c2_delay <= 30.0)
+                    {
+                        c2_pred.IsMeasure = 0;
+                        c2_pred.valid = 1; // 无效(延迟在1秒到30秒之间)
+                    }
+                    else
+                    {
+                        // 这里理论上不会执行,因为上面已经判断c2_delay <= 30.0
+                        c2_pred.valid = 1; // 无效
+                        c2_pred.IsMeasure = 0;
+                    }
+                }
+
+                // 处理预测结果
+                if (predict_success)
+                {
+                    if (c2_predictor_->time_first < 1e-9)
+                    {
+                        c2_predictor_->time_first = now_sec;
+                    }
+                    if (c2_pred.valid == 0 && (now_sec - c2_predictor_->time_first > 1.0 || c2_predictor_->num_inter_Update >= 100))
+                    {
+                        // 当前预测有效:更新上一次有效结果
+                        last_valid_result_c2_ = c2_pred;
+                        has_c2 = true;
+                    }
+                    else
+                    {
+                        // 当前预测无效:使用上一次有效结果
+                        if (last_valid_result_c2_.valid == 0)
+                        { // 确保上一次结果本身有效
+                            c2_pred = last_valid_result_c2_;
+                            c2_pred.timestamp = t_pred_c2; // 更新时间戳为当前时刻
+                            c2_pred.valid = 1;             // 标记为无效(因为当前预测无效)
+                            c2_pred.IsMeasure = 0;         // 不是实时测量
+                            has_c2 = true;
+                        }
+                        // 如果上一次结果也无效,则不输出任何结果
+                    }
+                }
+                else
+                {
+                    // 预测失败或延迟超过30秒:使用上一次有效结果
+                    if (last_valid_result_c2_.valid == 0)
+                    { // 确保上一次结果本身有效
+                        c2_pred = last_valid_result_c2_;
+                        c2_pred.timestamp = t_pred_c2; // 更新时间戳为当前时刻
+                        c2_pred.valid = 1;             // 标记为无效(因为当前没有有效预测)
+                        c2_pred.IsMeasure = 0;         // 不是实时测量
+                        has_c2 = true;
+                    }
+                    // 如果上一次结果无效,则不输出任何结果
+                }
+
+                // 预测结果可视化(单机2)
+                if (has_c2)
+                {
+                    posedetect_vis::publishPredictCylinder(
+                        c2_pred.bottomCenter.cast<float>(),
+                        c2_pred.axis.cast<float>(),
+                        static_cast<float>(params_.rocket_radius),
+                        static_cast<float>(params_.rocket_length),
+                        2);
+                }
+            }
+
+            if (has_c1 || has_c2)
+            {
+                have_any = true;
+                if (has_c1 && has_c2)
+                {
+                    result = decideTwo(c1_pred, c2_pred);
+                }
+                else if (has_c1)
+                {
+                    result = decideOne(c1_pred);
+                }
+                else
+                {
+                    result = decideOne(c2_pred);
+                }
+
+                // 输出调试日志:c1_pred、c2_pred、result 到文件
+                {
+                    static std::ofstream log_c1("/home/zxy/results/c1_pred.log", std::ios::app);
+                    static std::ofstream log_c2("/home/zxy/results/c2_pred.log", std::ios::app);
+                    static std::ofstream log_result("/home/zxy/results/result.log", std::ios::app);
+                    static bool header_written = false;
+
+                    if (!header_written)
+                    {
+                        if (log_c1.is_open())
+                        {
+                            log_c1 << "timestamp bottomCenter_x bottomCenter_y bottomCenter_z axis_x axis_y axis_z azimuth elevation IsMeasure valid delay" << std::endl;
+                            log_c1.flush();
+                        }
+                        if (log_c2.is_open())
+                        {
+                            log_c2 << "timestamp bottomCenter_x bottomCenter_y bottomCenter_z axis_x axis_y axis_z azimuth elevation IsMeasure valid delay" << std::endl;
+                            log_c2.flush();
+                        }
+                        if (log_result.is_open())
+                        {
+                            log_result << "timestamp bottomCenter_x bottomCenter_y bottomCenter_z axis_x axis_y axis_z IsMeasure valid azimuth elevation" << std::endl;
+                            log_result.flush();
+                        }
+                        header_written = true;
+                    }
+
+                    if (has_c1 && log_c1.is_open())
+                    {
+                        log_c1 << std::fixed << std::setprecision(6)
+                               << c1_pred.timestamp << " "
+                               << c1_pred.bottomCenter.x() << " " << c1_pred.bottomCenter.y() << " " << c1_pred.bottomCenter.z() << " "
+                               << c1_pred.axis.x() << " " << c1_pred.axis.y() << " " << c1_pred.axis.z() << " "
+                               << c1_pred.azimuth_angle << " " << c1_pred.elevation_angle << " "
+                               << (int)c1_pred.IsMeasure << " "
+                               << (int)c1_pred.valid << " "
+                               << c1_delay << std::endl;
+                        log_c1.flush();
+                    }
+
+                    if (has_c2 && log_c2.is_open())
+                    {
+                        log_c2 << std::fixed << std::setprecision(6)
+                               << c2_pred.timestamp << " "
+                               << c2_pred.bottomCenter.x() << " " << c2_pred.bottomCenter.y() << " " << c2_pred.bottomCenter.z() << " "
+                               << c2_pred.axis.x() << " " << c2_pred.axis.y() << " " << c2_pred.axis.z() << " "
+                               << c2_pred.azimuth_angle << " " << c2_pred.elevation_angle << " "
+                               << (int)c2_pred.IsMeasure << " "
+                               << (int)c2_pred.valid << " "
+                               << c2_delay << std::endl;
+                        log_c2.flush();
+                    }
+
+                    if (have_any && log_result.is_open())
+                    {
+                        log_result << std::fixed << std::setprecision(6)
+                                   << result.timestamp << " "
+                                   << result.bottomCenter.x() << " " << result.bottomCenter.y() << " " << result.bottomCenter.z() << " "
+                                   << result.axis.x() << " " << result.axis.y() << " " << result.axis.z() << " "
+                                   << (int)result.IsMeasure << " "
+                                   << (int)result.valid << " "
+                                   << result.azimuth_angle << " "
+                                   << result.elevation_angle << std::endl;
+                        log_result.flush();
+                    }
+                }
+
+                // 输出到 can_main & can_hpyo
+                // printf("%s %f \n", "delay_time = ", result.delay_time);
+                // printf("%s %f \n", "timestamp = ", result.timestamp);
+                can_main.setValue_coor_x(result.bottomCenter.x());
+                can_main.setValue_coor_y(result.bottomCenter.y());
+                can_main.setValue_coor_z(result.bottomCenter.z());
+                can_main.setValue_projection_B(result.elevation_angle);
+                can_main.setValue_projection_A(result.azimuth_angle);
+                can_main.setValue_output_delay(result.delay_time);
+                // can_main.setValue_state((char)(result.lidar_status));
+                // can_main.setValue_timestate((char)(result.time_sync_state));
+                can_main.setValue_visible((char)(result.Isvisible));
+                can_main.setValue_valid((char)(result.valid));
+                can_main.setValue_timestamp(result.timestamp * 1e3);
+                // can_main.setValue_state_lidar((char)(result.each_lidar_status));
+                can_main.setValue_transplane((char)(result.TransPlane));
+                can_main.setValue_predict((char)(result.IsMeasure));
+                can_main.setValue_state_value((char)(result.angle_zero_state));
+
+                can_hpyo.setValue_coor_x(result.bottomCenter.x());
+                can_hpyo.setValue_coor_y(result.bottomCenter.y());
+                can_hpyo.setValue_coor_z(result.bottomCenter.z());
+                can_hpyo.setValue_projection_B(result.elevation_angle);
+                can_hpyo.setValue_projection_A(result.azimuth_angle);
+                can_hpyo.setValue_output_delay(result.delay_time);
+                // can_hpyo.setValue_state((char)(result.lidar_status));
+                // can_hpyo.setValue_timestate((char)(result.time_sync_state));
+                can_hpyo.setValue_visible((char)(result.Isvisible));
+                can_hpyo.setValue_valid((char)(result.valid));
+                can_hpyo.setValue_timestamp(result.timestamp * 1e3);
+                // can_hpyo.setValue_state_lidar((char)(result.each_lidar_status));
+                can_hpyo.setValue_transplane((char)(result.TransPlane));
+                can_hpyo.setValue_predict((char)(result.IsMeasure));
+                can_hpyo.setValue_state_value((char)(result.angle_zero_state));
+            }
+
+            uint8_t each_mask = 0xFF;
+            int overall = 1;
+            try
+            {
+                LidarHeart::getStatus(each_mask, overall);
+                result.each_lidar_status = each_mask;
+                result.lidar_status = overall;
+            }
+            catch (const std::exception &e)
+            {
+                ROS_WARN_THROTTLE(5.0, "LidarHeart getStatus failed: %s", e.what());
+            }
+
+            if ((!c1_time_sync_state_.load(std::memory_order_relaxed)) && (!c2_time_sync_state_.load(std::memory_order_relaxed)))
+            {
+                result.time_sync_state = 0;
+            }
+            else
+            {
+                result.time_sync_state = 1;
+            }
+
+            result.dataState = 0;
+            can_main.setValue_datasource((char)(result.dataState));
+            can_hpyo.setValue_datasource((char)(result.dataState));
+
+            can_main.setValue_timestate((char)(result.time_sync_state));
+            can_main.setValue_state((char)(result.lidar_status));
+            can_main.setValue_state_lidar((char)(result.each_lidar_status));
+            can_main.setValue_count(realCount);
+
+            can_hpyo.setValue_timestate((char)(result.time_sync_state));
+            can_hpyo.setValue_state((char)(result.lidar_status));
+            can_hpyo.setValue_state_lidar((char)(result.each_lidar_status));
+
+            can_hpyo.setValue_count(realCount);
+
+            realCount++;
+            if (realCount > 255)
+            {
+                realCount = 0;
+            }
+
+            auto now_lidar = std::chrono::system_clock::now();
+            // 转换为自1970年以来的毫秒数
+            uint64_t timestamp_ms_lidar = std::chrono::duration_cast<std::chrono::milliseconds>(
+                                              now_lidar.time_since_epoch())
+                                              .count();
+
+            // 日志记录
+            logger.setValue_result(result.timestamp,
+                                   result.bottomCenter.x(), result.bottomCenter.y(), result.bottomCenter.z(),
+                                   result.axis.x(), result.axis.y(), result.axis.z(),
+                                   result.azimuth_angle, result.elevation_angle,
+                                   result.cloudNumber, result.fitResidual, result.TransPlane, result.delay_time,
+                                   result.Isvisible, result.IsMeasure, result.lidar_status, (int)(result.each_lidar_status), result.valid, result.dataState, timestamp_ms_lidar);
+        }
+        else
+        {   // *****************实际测量数据处理 决策逻辑开始******************
+            PoseData result_out;
+            result_out.ProjectedCenter.x() = 0;
+            result_out.ProjectedCenter.y() = 0;
+            result_out.axis.x() = 0.0;
+            result_out.axis.y() = 0.0;
+            result_out.axis.z() = 0.0;
+
+            result_out.cloudNumber = 0;
+            result_out.fitResidual = 0.0;
+            result_out.is_predict = 0.0;
+
+            // double COM_ZL[3] = {0};
+            // double COM_V[3] = {0};
+            // double COM_att[3] = {0};
+            // double SheXiang = SheXiang_para;
+            // double att[3] = {0};
+            // double Lever[3] = {Lever_para[0], Lever_para[1], Lever_para[2]};
+            // double JBottom2JM_JT[3] = {JT_para[0], JT_para[1],JT_para[2]};
+            recursion_time++;
+            if (udp_client.get_update_flag())
+            {
+                udp_client.set_update_flag(0);
+                recursion_time = 0;
+                recursion_overflag = 0;
+
+                COM_V[0] = udp_client.get_vx_value();
+                COM_V[1] = udp_client.get_vy_value();
+                COM_V[2] = udp_client.get_vz_value();
+
+                COM_ZL[0] = udp_client.get_x_value() + COM_V[0] * delay_JT_ms / 1000.0;
+                COM_ZL[1] = udp_client.get_y_value() + COM_V[1] * delay_JT_ms / 1000.0;
+                COM_ZL[2] = udp_client.get_z_value() + COM_V[2] * delay_JT_ms / 1000.0;
+
+                COM_att[0] = udp_client.get_pitch_value();
+                COM_att[1] = udp_client.get_yaw_value();
+                COM_att[2] = udp_client.get_roll_value();
+
+                att_hook[0] = udp_client.get_pitch_value();
+                att_hook[1] = udp_client.get_yaw_value();
+                att_hook[2] = udp_client.get_roll_value();
+
+                vel[0] = udp_client.get_vx_value();
+                vel[1] = udp_client.get_vy_value();
+                vel[2] = udp_client.get_vz_value();
+                
+                can_main.setValue_predict(0x01);
+                can_hpyo.setValue_predict(0x01);
+                result_out.IsMeasure = 0x01;
+            }
+            else if (udp_client_bak.get_update_flag())
+            {
+                udp_client_bak.set_update_flag(0);
+                recursion_time = 0;
+                recursion_overflag = 0;
+
+                COM_V[0] = udp_client_bak.get_vx_value();
+                COM_V[1] = udp_client_bak.get_vy_value();
+                COM_V[2] = udp_client_bak.get_vz_value();
+
+                COM_ZL[0] = udp_client_bak.get_x_value() + COM_V[0] * delay_JT_ms / 1000.0;
+                COM_ZL[1] = udp_client_bak.get_y_value() + COM_V[1] * delay_JT_ms / 1000.0;
+                COM_ZL[2] = udp_client_bak.get_z_value() + COM_V[2] * delay_JT_ms / 1000.0;
+
+                COM_att[0] = udp_client_bak.get_pitch_value();
+                COM_att[1] = udp_client_bak.get_yaw_value();
+                COM_att[2] = udp_client_bak.get_roll_value();
+
+                att_hook[0] = udp_client_bak.get_pitch_value();
+                att_hook[1] = udp_client_bak.get_yaw_value();
+                att_hook[2] = udp_client_bak.get_roll_value();
+
+                vel[0] = udp_client_bak.get_vx_value();
+                vel[1] = udp_client_bak.get_vy_value();
+                vel[2] = udp_client_bak.get_vz_value();
+
+                can_main.setValue_predict(0x01);
+                can_hpyo.setValue_predict(0x01);
+                result_out.IsMeasure = 0x01;
+            }
+            else
+            {
+                if (recursion_time < 200)
+                {
+                    recursion_overflag = 0;
+                    // 递推
+                    COM_ZL[0] += COM_V[0] * 0.01;
+                    COM_ZL[1] += COM_V[1] * 0.01;
+                    COM_ZL[2] += COM_V[2] * 0.01;
+
+                    can_main.setValue_predict(0x00);
+                    can_hpyo.setValue_predict(0x00);
+                    result_out.IsMeasure = 0x00;
+                }
+                else
+                {
+                    recursion_overflag = 1;
+                }
+
+            }
+
+            double ZL_distance = sqrt(COM_ZL[0] * COM_ZL[0] + COM_ZL[1] * COM_ZL[1] + COM_ZL[2] * COM_ZL[2]);
+            double COM_att_norm = sqrt(COM_att[0] * COM_att[0] + COM_att[1] * COM_att[1] + COM_att[2] * COM_att[2]);
+
+            if (can_main.get_rec_flag())
+            {
+                att[0] = can_main.get_pitch_value();
+                att[1] = can_main.get_roll_value();
+                att[2] = can_main.get_yaw_value();
+                                               
+                att_net[0] = can_main.get_pitch_value();
+                att_net[1] = can_main.get_roll_value();
+                att_net[2] = can_main.get_yaw_value();
+            }
+            else if (can_hpyo.get_rec_flag())
+            {
+                att[0] = can_hpyo.get_pitch_value();
+                att[1] = can_hpyo.get_roll_value();
+                att[2] = can_hpyo.get_yaw_value();
+
+                att_net[0] = can_hpyo.get_pitch_value();
+                att_net[1] = can_hpyo.get_roll_value();
+                att_net[2] = can_hpyo.get_yaw_value();
+            }
+            else
+            {
+                att[0] = GZ_para[0];
+                att[1] = GZ_para[1];
+                att[2] = GZ_para[2];
+
+                att_net[0] = GZ_para[0];
+                att_net[1] = GZ_para[1];
+                att_net[2] = GZ_para[2];
+            }
+
+            double COM_ZL_processed[3] = {COM_ZL[0], COM_ZL[1], COM_ZL[2]};
+            double JBottom_ZL[3] = {0};
+            calculate_ZL_WX(COM_ZL_processed, COM_att, JBottom2JM_JT, JBottom_ZL);
+
+            double CM_WX[3] = {0};
+            calculate_CM_WX(COM_ZL_processed, SheXiang, att, Lever, CM_WX);
+
+            double JBottom_WX[3] = {0};
+            calculate_CM_WX(JBottom_ZL, SheXiang, att, Lever, JBottom_WX);
+
+            double CM_WX_15s[3] = {CM_WX[0], -CM_WX[2], CM_WX[1]};
+            double JBottom_WX_15s[3] = {JBottom_WX[0], -JBottom_WX[2], JBottom_WX[1]};
+            
+
+            double rocks[3];
+            double rock[3];
+
+            rocks[0] = CM_WX_15s[0] - JBottom_WX_15s[0];
+            rocks[1] = CM_WX_15s[1] - JBottom_WX_15s[1];
+            rocks[2] = CM_WX_15s[2] - JBottom_WX_15s[2];
+
+            double rock_norm = sqrt(rocks[0] * rocks[0] + rocks[1] * rocks[1] + rocks[2] * rocks[2]);
+            if (fabs(rock_norm) < 1e-9)
+            {
+                rock[0] = rock[1] = rock[2] = 0.0;
+            }
+            else
+            {
+                rock[0] = rocks[0] / rock_norm;
+                rock[1] = rocks[1] / rock_norm;
+                rock[2] = rocks[2] / rock_norm;
+            }
+
+            double azimuth_deg, elevation_deg, theta;
+            azimuth_deg = atan2(rock[1], rock[0]) * 180.0f / PI;
+            if (azimuth_deg < 0)
+            {
+                azimuth_deg += 360.0;
+            }
+
+            theta = acos(rock[2]) * 180.0f / PI;
+            elevation_deg = 90.0 - theta;
+            if (elevation_deg < 0)
+            {
+                elevation_deg = 0.0;
+            }
+
+            char flag_chuanwang = 0;
+            double J0_WX_15s[3] = {0.0, 0.0, 0.0};
+
+            if (JBottom_WX[1] < 0)
+            {
+                flag_chuanwang = 1;
+
+                double J0_WX[3];
+                double diff[3] = {
+                    JBottom_WX[0] - CM_WX[0],
+                    JBottom_WX[1] - CM_WX[1],
+                    JBottom_WX[2] - CM_WX[2]};
+
+                double denominator = fabs(JBottom_WX[1]) + CM_WX[1];
+                if (fabs(denominator) < 1e-9)
+                {
+                    J0_WX[0] = CM_WX[0];
+                    J0_WX[1] = CM_WX[1];
+                    J0_WX[2] = CM_WX[2];
+                }
+                else
+                {
+                    double scalar = CM_WX[1] / denominator;
+                    J0_WX[0] = diff[0] * scalar + CM_WX[0];
+                    J0_WX[1] = diff[1] * scalar + CM_WX[1];
+                    J0_WX[2] = diff[2] * scalar + CM_WX[2];
+                }
+
+                J0_WX_15s[0] = J0_WX[0];
+                J0_WX_15s[1] = -J0_WX[2];
+                J0_WX_15s[2] = JBottom_WX[1];
+
+                if ((ZL_distance < 1500.0) && (ZL_distance > 0.01) && (COM_att_norm > 0.0001))
+                {
+                    result_out.valid = 0x00;
+                    result_out.Isvisible = 0x00;
+                    result_out.TransPlane = flag_chuanwang;
+                }
+                else
+                {
+                    result_out.valid = 0x01;
+                    result_out.Isvisible = 0x01;
+                    result_out.TransPlane = 0x00;
+                }
+                result_out.bottomCenter.x() = (float)J0_WX_15s[0];
+                result_out.bottomCenter.y() = (float)J0_WX_15s[1];
+                result_out.bottomCenter.z() = (float)J0_WX_15s[2];
+                // result_out.TransPlane = flag_chuanwang;
+            }
+            else
+            {
+                if ((ZL_distance < 1500.0) && (ZL_distance > 0.01) && (COM_att_norm > 0.0001))
+                {
+                    result_out.valid = 0x00;
+                    result_out.Isvisible = 0x00;
+                }
+                else
+                {
+                    can_main.setValue_visible(0x01);
+                    can_main.setValue_valid(0x01);
+
+                    can_hpyo.setValue_visible(0x01);
+                    can_hpyo.setValue_valid(0x01);
+
+                    result_out.valid = 0x01;
+                    result_out.Isvisible = 0x01;
+                }
+                result_out.bottomCenter.x() = (float)JBottom_WX_15s[0];
+                result_out.bottomCenter.y() = (float)JBottom_WX_15s[1];
+                result_out.bottomCenter.z() = (float)JBottom_WX_15s[2];
+                result_out.TransPlane = flag_chuanwang;
+            }
+
+            if(recursion_overflag == 1)
+            {
+                can_main.setValue_visible(0x01);
+                can_main.setValue_valid(0x01);
+
+                can_hpyo.setValue_visible(0x01);
+                can_hpyo.setValue_valid(0x01);
+
+                result_out.valid = 0x01;
+                result_out.Isvisible = 0x01;
+            }
+
+            result_out.timestamp = 0;
+            result_out.elevation_angle = (float)elevation_deg;
+            result_out.azimuth_angle = (float)azimuth_deg;
+            result_out.delay_time = 0.0;
+            result_out.angle_zero_state = 0x00;
+            result_out.each_lidar_status = 0x00;
+            result_out.time_sync_state = 0x00;
+            result_out.lidar_status = 0x00;
+
+            testCount++;
+            if (testCount > 255)
+            {
+                testCount = 0;
+            }
+
+            if (c1_predictor_)
+            {
+                Eigen::Vector3f c, a;
+                bool predict_success = false;
+
+                if (c1_delay <= 30.0 && c1_predictor_->predictOnly(t_pred_c1, c, a))
+                {
+                    c1_pred = makePoseFromPredict(c, a, 1, t_pred_c1);
+                    predict_success = true;
+
+                    double delay_ms = c1_delay * 1000.0;
+                    c1_pred.delay_time = delay_ms;
+                    if (delay_ms <= 50.0)
+                    {
+                        c1_pred.IsMeasure = 1;
+                        c1_pred.valid = 0;
+                    }
+                    else if (delay_ms <= 1000.0)
+                    {
+                        c1_pred.IsMeasure = 0;
+                        c1_pred.valid = 0;
+                    }
+                    else if (c1_delay <= 30.0)
+                    {
+                        c1_pred.IsMeasure = 0;
+                        c1_pred.valid = 1;
+                    }
+                    else
+                    {
+                        c1_pred.valid = 1;
+                        c1_pred.IsMeasure = 0;
+                    }
+                }
+
+                if (predict_success)
+                {
+                    if (c1_predictor_->time_first < 1e-9)
+                    {
+                        c1_predictor_->time_first = now_sec;
+                    }
+                    /*如果超过1s或者更新次数超过10次,就开始输出*/
+                    if (c1_pred.valid == 0 && (now_sec - c1_predictor_->time_first > 1.0 || c1_predictor_->num_inter_Update >= 100))
+                    {
+                        // 当前预测有效:更新上一次有效结果
+                        last_valid_result_c1_ = c1_pred;
+                        has_c1 = true;
+                    }
+                    else
+                    {
+                        if (last_valid_result_c1_.valid == 0)
+                        {
+                            c1_pred = last_valid_result_c1_;
+                            c1_pred.timestamp = t_pred_c1; // 更新时间戳为当前时刻
+                            c1_pred.valid = 1;             // 标记为无效(因为当前预测无效)
+                            c1_pred.IsMeasure = 0;         // 不是实时测量
+                            has_c1 = true;
+                        }
+                    }
+                }
+                else
+                {
+                    if (last_valid_result_c1_.valid == 0)
+                    {
+                        c1_pred = last_valid_result_c1_;
+                        c1_pred.timestamp = t_pred_c1; // 更新时间戳为当前时刻
+                        c1_pred.valid = 1;             // 标记为无效(因为当前没有有效预测)
+                        c1_pred.IsMeasure = 0;         // 不是实时测量
+                        has_c1 = true;
+                    }
+                }
+
+                if (has_c1)
+                {
+                    posedetect_vis::publishPredictCylinder(
+                        c1_pred.bottomCenter.cast<float>(),
+                        c1_pred.axis.cast<float>(),
+                        static_cast<float>(params_.rocket_radius),
+                        static_cast<float>(params_.rocket_length),
+                        1);
+                }
+            }
+            if (c2_predictor_)
+            {
+                Eigen::Vector3f c, a;
+                bool predict_success = false;
+
+                if (c2_delay <= 30.0 && c2_predictor_->predictOnly(t_pred_c2, c, a))
+                {
+                    c2_pred = makePoseFromPredict(c, a, 2, t_pred_c2);
+                    predict_success = true;
+
+                    double delay_ms = c2_delay * 1000.0;
+                    c2_pred.delay_time = delay_ms;
+                    if (delay_ms <= 50.0)
+                    {
+                        c2_pred.IsMeasure = 1;
+                        c2_pred.valid = 0;
+                    }
+                    else if (delay_ms <= 1000.0)
+                    {
+                        c2_pred.IsMeasure = 0;
+                        c2_pred.valid = 0;
+                    }
+                    else if (c2_delay <= 30.0)
+                    {
+                        c2_pred.IsMeasure = 0;
+                        c2_pred.valid = 1;
+                    }
+                    else
+                    {
+                        c2_pred.valid = 1;
+                        c2_pred.IsMeasure = 0;
+                    }
+                }
+
+                if (predict_success)
+                {
+                    if (c2_predictor_->time_first < 1e-9)
+                    {
+                        c2_predictor_->time_first = now_sec;
+                    }
+                    if (c2_pred.valid == 0 && (now_sec - c2_predictor_->time_first > 1.0 || c2_predictor_->num_inter_Update >= 100))
+                    {
+                        // 当前预测有效:更新上一次有效结果
+                        last_valid_result_c2_ = c2_pred;
+                        has_c2 = true;
+                    }
+                    else
+                    {
+                        if (last_valid_result_c2_.valid == 0)
+                        {
+                            c2_pred = last_valid_result_c2_;
+                            c2_pred.timestamp = t_pred_c2;
+                            c2_pred.valid = 1;
+                            c2_pred.IsMeasure = 0;
+                            has_c2 = true;
+                        }
+                    }
+                }
+                else
+                {
+                    if (last_valid_result_c2_.valid == 0)
+                    {
+                        c2_pred = last_valid_result_c2_;
+                        c2_pred.timestamp = t_pred_c2;
+                        c2_pred.valid = 1;
+                        c2_pred.IsMeasure = 0;
+                        has_c2 = true;
+                    }
+                }
+
+                if (has_c2)
+                {
+                    posedetect_vis::publishPredictCylinder(
+                        c2_pred.bottomCenter.cast<float>(),
+                        c2_pred.axis.cast<float>(),
+                        static_cast<float>(params_.rocket_radius),
+                        static_cast<float>(params_.rocket_length),
+                        2);
+                }
+            }
+
+            if (has_c1 || has_c2)
+            {
+                have_any = true;
+                if (has_c1 && has_c2)
+                {
+                    result = decideTwo(c1_pred, c2_pred);
+                }
+                else if (has_c1)
+                {
+                    result = decideOne(c1_pred);
+                }
+                else
+                {
+                    result = decideOne(c2_pred);
+                }
+            }
+
+            uint8_t each_mask = 0xFF;
+            int overall = 1;
+            try
+            {
+                LidarHeart::getStatus(each_mask, overall);
+                result.each_lidar_status = each_mask;
+                result.lidar_status = overall;
+            }
+            catch (const std::exception &e)
+            {
+                ROS_WARN_THROTTLE(5.0, "LidarHeart getStatus failed: %s", e.what());
+            }
+
+            if ((!c1_time_sync_state_.load(std::memory_order_relaxed)) && (!c2_time_sync_state_.load(std::memory_order_relaxed)))
+            {
+                result.time_sync_state = 0;
+            }
+            else
+            {
+                result.time_sync_state = 1;
+            }
+
+            realCount++;
+            if (realCount > 255)
+            {
+                realCount = 0;
+            }
+
+            // fusion New code
+            result_out.dataState = 1;
+            result.dataState = 0;
+            
+//            pos_bottom_N[0] = result.bottomCenter.x();
+//            pos_bottom_N[1] = result.bottomCenter.y();
+//            pos_bottom_N[2] = result.bottomCenter.z();
+
+            static PoseData result_fusion_out;
+            static float update_h = 200.0; //必须比较高
+            static int valid_cnt = 0;
+            static float old_result_xyz[3] = {0.0, 0.0, 0.0};
+            static double ipos_bottom_N[3] = {0.0, 0.0, 200.0};
+
+            //更新update_h高度.默认值200
+            if(result.valid == 0)
+            {
+               update_h = result.bottomCenter.z(); 
+            }else if(result_out.valid == 0)
+            {
+               update_h = result_out.bottomCenter.z(); 
+            }
+
+            result_fusion_out = result_out;//默认使用J上下传数据结果
+            if( (update_h<1500) && (update_h>110)){
+                if(result_out.valid == 0){
+                    result_fusion_out = result_out;
+                    ipos_bottom_N[0] = result_out.bottomCenter.x();
+                    ipos_bottom_N[1] = result_out.bottomCenter.y();
+                    ipos_bottom_N[2] = result_out.bottomCenter.z();
+                    update_h = result_out.bottomCenter.z();
+                }
+            }else if(update_h <= 110){
+                result_fusion_out.valid = 1; //默认状态无效
+                if (result.valid == 0) //一旦有新的有效的位姿数据就更新ipos_bottom_N
+                {
+                    valid_cnt = 1;
+                    result_fusion_out = result;
+                    result_fusion_out.valid = 0;
+                    update_h = result_fusion_out.bottomCenter.z();
+                    ipos_bottom_N[0] = result.bottomCenter.x();
+                    ipos_bottom_N[1] = result.bottomCenter.y();
+                    ipos_bottom_N[2] = result.bottomCenter.z();
+                }
+                else
+                {
+                 //   if (valid_cnt == 1)  不论如何也要递推计算
+                    {
+                        //result_fusion_out = result;
+                        //result_fusion_out.valid = 0;
+
+                        // 递推  在pos_bottom_N基础上递推
+                        //double CM_WX_15s[3] = {CM_WX[0], -CM_WX[2], CM_WX[1]};
+                        double pos_hook[3] = {0}, pos_hook_N_new[3] = {0}, pos_bottom_N_new[3] = {0};
+                        double pos_hook_N_new_1b[3] = {0}, pos_bottom_N_new_1b[3] = {0};
+                        double  ipos_bottom_N_1b[3] = {ipos_bottom_N[0], ipos_bottom_N[2], -ipos_bottom_N[1]};
+                        posbottomN2poshookL(ipos_bottom_N_1b, att_hook, att_net, pos_hook);
+
+                        pos_hook[0] += vel[0] * 0.01;
+                        pos_hook[1] += vel[1] * 0.01;
+                        pos_hook[2] += vel[2] * 0.01;
+
+                        poshookL2posbottomN(pos_hook, att_hook, att_net, pos_bottom_N_new_1b, pos_hook_N_new_1b);
+                        pos_bottom_N_new = {pos_bottom_N_new_1b[0], -pos_bottom_N_new_1b[2], pos_bottom_N_new_1b[1]};
+                        pos_hook_N_new = {pos_hook_N_new_1b[0], -pos_hook_N_new_1b[2], pos_hook_N_new_1b[1]};
+
+                        ipos_bottom_N[0] = pos_bottom_N_new[0];
+                        ipos_bottom_N[1] = pos_bottom_N_new[1];
+                        ipos_bottom_N[2] = pos_bottom_N_new[2]; 
+
+                        getRockDirectionAndFlags(pos_hook_N_new, pos_bottom_N_new,
+                                                 &azimuth_deg, &elevation_deg, J0_WX_15s,
+                                                 &flag_chuanwang);
+                        if (flag_chuanwang == 0)
+                        {
+                            result_fusion_out.bottomCenter.x() = pos_bottom_N_new[0];
+                            result_fusion_out.bottomCenter.y() = pos_bottom_N_new[1];
+                            result_fusion_out.bottomCenter.z() = pos_bottom_N_new[2];
+                        }
+                        else
+                        {
+                            result_fusion_out.bottomCenter.x() = J0_WX_15s[0];
+                            result_fusion_out.bottomCenter.y() = J0_WX_15s[1];
+                            result_fusion_out.bottomCenter.z() = J0_WX_15s[2];
+                        }
+                        result_fusion_out.azimuth_angle = azimuth_deg;
+                        result_fusion_out.elevation_angle = elevation_deg;
+                        result_fusion_out.TransPlane = flag_chuanwang;
+
+                        update_h = result_fusion_out.bottomCenter.z();
+                    }
+                }
+
+                if (update_h <= 60 )
+                {
+                    //result_fusion_out = result_out;
+                    result_fusion_out.valid = 0;
+                    //update_h = result_fusion_out.bottomCenter.z();
+                }
+                else
+                {
+                    //result_fusion_out = result_out;
+                    result_fusion_out.valid = 1;
+                    //update_h = result_fusion_out.bottomCenter.z();
+                }
+            }
+
+           
+            can_main.setValue_coor_x(result_fusion_out.bottomCenter.x());
+            can_main.setValue_coor_y(result_fusion_out.bottomCenter.y());
+            can_main.setValue_coor_z(result_fusion_out.bottomCenter.z());
+            can_main.setValue_projection_B(result_fusion_out.elevation_angle);
+            can_main.setValue_projection_A(result_fusion_out.azimuth_angle);
+            can_main.setValue_output_delay(result_fusion_out.delay_time);
+            can_main.setValue_state((char)(result_fusion_out.lidar_status));
+            can_main.setValue_timestate((char)(result_fusion_out.time_sync_state));
+            can_main.setValue_visible((char)(result_fusion_out.Isvisible));
+            can_main.setValue_valid((char)(result_fusion_out.valid));
+            can_main.setValue_timestamp(result_fusion_out.timestamp * 1e3);
+            can_main.setValue_state_lidar((char)(result_fusion_out.each_lidar_status));
+            can_main.setValue_transplane((char)(result_fusion_out.TransPlane));
+            can_main.setValue_predict((char)(result_fusion_out.IsMeasure));
+            can_main.setValue_state_value((char)(result_fusion_out.angle_zero_state));
+            can_main.setValue_datasource((char)(result_fusion_out.dataState));
+            can_main.setValue_count(testCount);
+
+            can_hpyo.setValue_coor_x(result_fusion_out.bottomCenter.x());
+            can_hpyo.setValue_coor_y(result_fusion_out.bottomCenter.y());
+            can_hpyo.setValue_coor_z(result_fusion_out.bottomCenter.z());
+            can_hpyo.setValue_projection_B(result_fusion_out.elevation_angle);
+            can_hpyo.setValue_projection_A(result_fusion_out.azimuth_angle);
+            can_hpyo.setValue_output_delay(result_fusion_out.delay_time);
+            can_hpyo.setValue_state((char)(result_fusion_out.lidar_status));
+            can_hpyo.setValue_timestate((char)(result_fusion_out.time_sync_state));
+            can_hpyo.setValue_visible((char)(result_fusion_out.Isvisible));
+            can_hpyo.setValue_valid((char)(result_fusion_out.valid));
+            can_hpyo.setValue_timestamp(result_fusion_out.timestamp * 1e3);
+            can_hpyo.setValue_state_lidar((char)(result_fusion_out.each_lidar_status));
+            can_hpyo.setValue_transplane((char)(result_fusion_out.TransPlane));
+            can_hpyo.setValue_predict((char)(result_fusion_out.IsMeasure));
+            can_hpyo.setValue_state_value((char)(result_fusion_out.angle_zero_state));
+            can_hpyo.setValue_datasource((char)(result_fusion_out.dataState));
+            can_main.setValue_count(testCount);
+
+            auto now_fusion_lidar = std::chrono::system_clock::now();
+            // 转换为自1970年以来的毫秒数
+            uint64_t timestamp_ms_fusion = std::chrono::duration_cast<std::chrono::milliseconds>(
+                                               now_fusion_lidar.time_since_epoch())
+                                               .count();
+
+            // 日志记录
+            logger.setValue_result(result_fusion_out.timestamp,
+                                   result_fusion_out.bottomCenter.x(), result_fusion_out.bottomCenter.y(), result_fusion_out.bottomCenter.z(),
+                                   result_fusion_out.axis.x(), result_fusion_out.axis.y(), result_fusion_out.axis.z(),
+                                   result_fusion_out.azimuth_angle, result_fusion_out.elevation_angle,
+                                   result_fusion_out.cloudNumber, result_fusion_out.fitResidual, result_fusion_out.TransPlane, result_fusion_out.delay_time,
+                                   result_fusion_out.Isvisible, result_fusion_out.IsMeasure, result_fusion_out.lidar_status, (int)(result_fusion_out.each_lidar_status), result_fusion_out.valid, result_fusion_out.dataState, timestamp_ms_fusion);
+        }
+
+        // 实时模式下触发 CAN 发送(实时模式不会启动回放线程)
+        can_main.triggerSendRealtime();
+        can_hpyo.triggerSendRealtime();
+
+        ros::spinOnce();
+        rate.sleep();
+    }
+}
+
+void LidarFusionNode::getRockDirectionAndFlags(double CM_WX_15s[3], double JBottom_WX_15s[3], 
+    double* rt_azimuth_deg, double* rt_elevation_deg,double J0_WX_15s[3],
+    char*  rt_flag_chuanwang)
+{
+            double rocks[3];
+            double rock[3];
+
+            rocks[0] = CM_WX_15s[0] - JBottom_WX_15s[0];
+            rocks[1] = CM_WX_15s[1] - JBottom_WX_15s[1];
+            rocks[2] = CM_WX_15s[2] - JBottom_WX_15s[2];
+
+            double rock_norm = sqrt(rocks[0] * rocks[0] + rocks[1] * rocks[1] + rocks[2] * rocks[2]);
+            if (fabs(rock_norm) < 1e-9)
+            {
+                rock[0] = rock[1] = rock[2] = 0.0;
+            }
+            else
+            {
+                rock[0] = rocks[0] / rock_norm;
+                rock[1] = rocks[1] / rock_norm;
+                rock[2] = rocks[2] / rock_norm;
+            }
+
+            double azimuth_deg, elevation_deg, theta;
+            azimuth_deg = atan2(rock[1], rock[0]) * 180.0f / PI;
+            if (azimuth_deg < 0)
+            {
+                azimuth_deg += 360.0;
+            }
+
+            theta = acos(rock[2]) * 180.0f / PI;
+            elevation_deg = 90.0 - theta;
+            if (elevation_deg < 0)
+            {
+                elevation_deg = 0.0;
+            }
+
+            *rt_azimuth_deg = azimuth_deg;
+            *rt_elevation_deg = elevation_deg;
+
+            char flag_chuanwang = 0;
+            double J0_WX_15s[3] = {0.0, 0.0, 0.0};
+
+            //double CM_WX_15s[3] = {CM_WX[0], -CM_WX[2], CM_WX[1]};
+            //double JBottom_WX_15s[3] = {JBottom_WX[0], -JBottom_WX[2], JBottom_WX[1]};
+            double CM_WX[3] = {CM_WX_15s[0], CM_WX_15s[2], -CM_WX_15s[1]};
+            double JBottom_WX[3] = {JBottom_WX_15s[0], JBottom_WX_15s[2], -JBottom_WX_15s[1]};
+
+
+            if (JBottom_WX_15s[2] < 0)
+            {
+                flag_chuanwang = 1;
+
+                double J0_WX[3];
+                double diff[3] = {
+                    JBottom_WX[0] - CM_WX[0],
+                    JBottom_WX[1] - CM_WX[1],
+                    JBottom_WX[2] - CM_WX[2]};
+
+                double denominator = fabs(JBottom_WX[1]) + CM_WX[1];
+                if (fabs(denominator) < 1e-9)
+                {
+                    J0_WX[0] = CM_WX[0];
+                    J0_WX[1] = CM_WX[1];
+                    J0_WX[2] = CM_WX[2];
+                }
+                else
+                {
+                    double scalar = CM_WX[1] / denominator;
+                    J0_WX[0] = diff[0] * scalar + CM_WX[0];
+                    J0_WX[1] = diff[1] * scalar + CM_WX[1];
+                    J0_WX[2] = diff[2] * scalar + CM_WX[2];
+                }
+
+                J0_WX_15s[0] = J0_WX[0];
+                J0_WX_15s[1] = -J0_WX[2];
+                J0_WX_15s[2] = JBottom_WX[1];
+            }else
+            {
+                flag_chuanwang = 0;
+                J0_WX_15s[0] = JBottom_WX[0];
+                J0_WX_15s[1] = JBottom_WX[1];
+                J0_WX_15s[2] = JBottom_WX[2];
+            }
+            *rt_flag_chuanwang = flag_chuanwang;
+}
+
+
+double LidarFusionNode::deg2rad(double deg)
+{
+    return deg * PI / 180.0;
+}
+
+double LidarFusionNode::cosd(double deg)
+{
+    return cos(deg2rad(deg));
+}
+
+double LidarFusionNode::sind(double deg)
+{
+    return sin(deg2rad(deg));
+}
+
+void LidarFusionNode::mat33_mult(const double A[3][3], const double B[3][3], double C[3][3])
+{
+    for (int i = 0; i < 3; i++)
+    {
+        for (int j = 0; j < 3; j++)
+        {
+            C[i][j] = 0.0;
+            for (int k = 0; k < 3; k++)
+            {
+                C[i][j] += A[i][k] * B[k][j];
+            }
+        }
+    }
+}
+
+void LidarFusionNode::mat33_transpose(const double A[3][3], double AT[3][3])
+{
+    for (int i = 0; i < 3; i++)
+    {
+        for (int j = 0; j < 3; j++)
+        {
+            AT[i][j] = A[j][i];
+        }
+    }
+}
+
+void LidarFusionNode::mat33_vec3_mult(const double A[3][3], const double a[3], double b[3])
+{
+    for (int i = 0; i < 3; i++)
+    {
+        b[i] = 0.0;
+        for (int k = 0; k < 3; k++)
+        {
+            b[i] += A[i][k] * a[k];
+        }
+    }
+}
+
+void LidarFusionNode::vec3_sub(const double a[3], const double b[3], double c[3])
+{
+    for (int i = 0; i < 3; i++)
+    {
+        c[i] = a[i] - b[i];
+    }
+}
+
+void LidarFusionNode::vec3_add(const double a[3], const double b[3], double c[3])
+{
+    for (int i = 0; i < 3; i++)
+    {
+        c[i] = a[i] + b[i];
+    }
+}
+
+void LidarFusionNode::calculate_CM_WX(const double COM_ZL[3], double SheXiang, const double att[3], const double Lever[3], double CM_WX[3])
+{
+    // 提取姿态角
+    double fuyang = att[0];
+    double henggun = att[1];
+    double heading = att[2];
+
+    // 定义变换矩阵T = [0 1 0; 0 0 1; 1 0 0]
+    double T[3][3] = {
+        {0.0, 1.0, 0.0},
+        {0.0, 0.0, 1.0},
+        {1.0, 0.0, 0.0}};
+
+    double T0[3][3] = {0};
+    mat33_transpose(T, T0);
+
+    double temp_COM_ZL[3] = {0};
+    double temp_Lever[3] = {0};
+    mat33_vec3_mult(T0, COM_ZL, temp_COM_ZL);
+    mat33_vec3_mult(T0, Lever, temp_Lever);
+
+    // 1. 计算C_ZL2N矩阵 (箭系转网系)
+    double C_ZL2N[3][3] = {0};
+    C_ZL2N[0][0] = cosd(SheXiang);
+    C_ZL2N[0][1] = sind(SheXiang);
+    C_ZL2N[0][2] = 0.0;
+
+    C_ZL2N[1][0] = -sind(SheXiang);
+    C_ZL2N[1][1] = cosd(SheXiang);
+    C_ZL2N[1][2] = 0.0;
+
+    C_ZL2N[2][0] = 0.0;
+    C_ZL2N[2][1] = 0.0;
+    C_ZL2N[2][2] = 1.0;
+
+    // 2. 计算各姿态旋转矩阵
+    // 偏航角(heading)旋转矩阵 C3_heading
+    double C3_heading[3][3] = {0};
+    C3_heading[0][0] = cosd(heading);
+    C3_heading[0][1] = sind(heading);
+    C3_heading[0][2] = 0.0;
+
+    C3_heading[1][0] = -sind(heading);
+    C3_heading[1][1] = cosd(heading);
+    C3_heading[1][2] = 0.0;
+
+    C3_heading[2][0] = 0.0;
+    C3_heading[2][1] = 0.0;
+    C3_heading[2][2] = 1.0;
+
+    // 俯仰角(fuyang)旋转矩阵 C1_fuyang (修正原MATLAB代码的笔误O)
+    double C1_fuyang[3][3] = {0};
+    C1_fuyang[0][0] = 1.0;
+    C1_fuyang[0][1] = 0.0;
+    C1_fuyang[0][2] = 0.0;
+
+    C1_fuyang[1][0] = 0.0;
+    C1_fuyang[1][1] = cosd(fuyang);
+    C1_fuyang[1][2] = sind(fuyang);
+
+    C1_fuyang[2][0] = 0.0;
+    C1_fuyang[2][1] = -sind(fuyang);
+    C1_fuyang[2][2] = cosd(fuyang);
+
+    // 横滚角(henggun)旋转矩阵 C2_henggun (修正原MATLAB代码的fuyang笔误)
+    double C2_henggun[3][3] = {0};
+    C2_henggun[0][0] = cosd(henggun);
+    C2_henggun[0][1] = 0.0;
+    C2_henggun[0][2] = -sind(henggun);
+
+    C2_henggun[1][0] = 0.0;
+    C2_henggun[1][1] = 1.0;
+    C2_henggun[1][2] = 0.0;
+
+    C2_henggun[2][0] = sind(henggun); // 原MATLAB代码此处写错为fuyang,已修正
+    C2_henggun[2][1] = 0.0;
+    C2_henggun[2][2] = cosd(henggun);
+
+    // 计算C_N2WX = C2_henggun * C1_fuyang * C3_heading
+    double C1C3[3][3] = {0};
+    mat33_mult(C1_fuyang, C3_heading, C1C3);
+
+    double C_N2WX[3][3] = {0};
+    mat33_mult(C2_henggun, C1C3, C_N2WX);
+
+    // 计算C_ZL2WX = C_N2WX * C_ZL2N
+    double C_ZL2WX[3][3] = {0};
+    mat33_mult(C_N2WX, C_ZL2N, C_ZL2WX);
+
+    // 计算C_WX02WX = C2_henggun * C1_fuyang
+    double C_WX02WX[3][3] = {0};
+    mat33_mult(C2_henggun, C1_fuyang, C_WX02WX);
+
+    // 计算C_WX2WX0 = C_WX02WX的转置
+    double C_WX2WX0[3][3] = {0};
+    mat33_transpose(C_WX02WX, C_WX2WX0);
+
+    // 计算C_WX2WX0 * Lever
+    double temp_vec1[3] = {0};
+    mat33_vec3_mult(C_WX2WX0, temp_Lever, temp_vec1);
+
+    // 计算(C_WX2WX0*Lever - Lever)
+    double temp_vec2[3] = {0};
+    vec3_sub(temp_vec1, temp_Lever, temp_vec2);
+
+    // 计算CCO_WX = C_WX02WX * temp_vec2
+    double CCO_WX[3] = {0};
+    mat33_vec3_mult(C_WX02WX, temp_vec2, CCO_WX);
+
+    // 计算C_ZL2WX * COM_ZL
+    double temp_vec3[3] = {0};
+    mat33_vec3_mult(C_ZL2WX, temp_COM_ZL, temp_vec3);
+
+    // 计算C_ZL2WX*COM_ZL + CCO_WX
+    double temp_vec4[3] = {0};
+    vec3_add(temp_vec3, CCO_WX, temp_vec4);
+
+    // 最终计算CM_WX = T * temp_vec4
+    mat33_vec3_mult(T, temp_vec4, CM_WX);
+}
+
+void LidarFusionNode::calculate_ZL_WX(const double COM_ZL_processed[3], const double COM_att[3], const double JBottom2JM_JT[3], double JBottom_ZL[3])
+{
+    // 提取姿态角
+    double fuyang = COM_att[0];
+    double henggun = COM_att[1];
+    double heading = COM_att[2];
+
+    double C_J3[3][3] = {0};
+    C_J3[0][0] = cosd(fuyang);
+    C_J3[0][1] = sind(fuyang);
+    C_J3[0][2] = 0.0;
+
+    C_J3[1][0] = -sind(fuyang);
+    C_J3[1][1] = cosd(fuyang);
+    C_J3[1][2] = 0.0;
+
+    C_J3[2][0] = 0.0;
+    C_J3[2][1] = 0.0;
+    C_J3[2][2] = 1.0;
+
+    double C_J2[3][3] = {0};
+    C_J2[0][0] = cosd(henggun);
+    C_J2[0][1] = 0.0;
+    C_J2[0][2] = -sind(henggun);
+
+    C_J2[1][0] = 0.0;
+    C_J2[1][1] = 1.0;
+    C_J2[1][2] = 0.0;
+
+    C_J2[2][0] = sind(henggun);
+    C_J2[2][1] = 0.0;
+    C_J2[2][2] = cosd(henggun);
+
+    double C_J1[3][3] = {0};
+    C_J1[0][0] = 1.0;
+    C_J1[0][1] = 0.0;
+    C_J1[0][2] = 0.0;
+
+    C_J1[1][0] = 0.0;
+    C_J1[1][1] = cosd(heading);
+    C_J1[1][2] = sind(heading);
+
+    C_J1[2][0] = 0.0;
+    C_J1[2][1] = -sind(heading);
+    C_J1[2][2] = cosd(heading);
+
+    double C_ZL2JT_temp[3][3] = {0};
+    mat33_mult(C_J2, C_J3, C_ZL2JT_temp);
+
+    double C_ZL2JT[3][3] = {0};
+    mat33_mult(C_J1, C_ZL2JT_temp, C_ZL2JT);
+
+    double C_ZL2JT0[3][3] = {0};
+    mat33_transpose(C_ZL2JT, C_ZL2JT0);
+
+    double JBottom2JM_ZL_temp[3] = {0};
+    mat33_vec3_mult(C_ZL2JT0, JBottom2JM_JT, JBottom2JM_ZL_temp);
+
+    vec3_sub(COM_ZL_processed, JBottom2JM_ZL_temp, JBottom_ZL);
+}
+
+// 姿态角转旋转矩阵 Cnb(载体->导航)
+void LidarFusionNode::a2cnb(const double att[3], double Cnb[3][3])
+{
+    double si = sin(att[0]);
+    double ci = cos(att[0]);
+    double sj = sin(att[1]);
+    double cj = cos(att[1]);
+    double sk = sin(att[2]);
+    double ck = cos(att[2]);
+
+    Cnb[0][0] = cj * ck - si * sj * sk;
+    Cnb[0][1] = -ci * sk;
+    Cnb[0][2] = sj * ck + si * cj * sk;
+
+    Cnb[1][0] = cj * sk + si * sj * ck;
+    Cnb[1][1] = ci * ck;
+    Cnb[1][2] = sj * sk - si * cj * ck;
+
+    Cnb[2][0] = -ci * sj;
+    Cnb[2][1] = si;
+    Cnb[2][2] = ci * cj;
+}
+
+// 3x3矩阵转置
+void LidarFusionNode::mat3_transpose(const double m[3][3], double res[3][3])
+{
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            res[i][j] = m[j][i];
+        }
+    }
+}
+
+// 3x3矩阵乘法:a * b
+void LidarFusionNode::mat3_mult(const double a[3][3], const double b[3][3], double res[3][3])
+{
+    double tmp[3][3] = {0};
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            tmp[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j] + a[i][2] * b[2][j];
+        }
+    }
+    memcpy(res, tmp, sizeof(tmp));
+}
+
+// 3x3矩阵 * 3x1向量
+void LidarFusionNode::mat3_vec_mult(const double m[3][3], const double v[3], double res[3])
+{
+    res[0] = m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2];
+    res[1] = m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2];
+    res[2] = m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2];
+}
+
+//======================== 函数1:挂钩L坐标 → 箭底N / 挂钩N ========================
+// 输入:pos_hook[3], att_hook[3], att_net[3]
+// 输出:pos_bottom_N[3], pos_hook_N[3]
+void LidarFusionNode::poshookL2posbottomN(
+    const double pos_hook[3],
+    const double att_hook[3],
+    const double att_net[3],
+    double pos_bottom_N[3],
+    double pos_hook_N[3])
+{
+    const double glv_deg = M_PI / 180.0;
+    const double dir = 130.0 * glv_deg;
+    const double yaw_0 = att_net[2];
+
+    double lever_os[3] = {0.36, -(65.2 - 3.56), 0.0};
+    double lever_hook[3] = {32.498, 0.0, 0.0};
+
+    // 坐标系变换
+    const double c_rfu_fur[3][3] = {{0,0,1}, {1,0,0}, {0,1,0}};
+    double c_fur_rfu[3][3];
+    mat3_transpose(c_rfu_fur, c_fur_rfu);
+
+    double lever_os_t[3], lever_hook_t[3], pos_hook_t[3];
+    mat3_vec_mult(c_rfu_fur, lever_os, lever_os_t);
+    mat3_vec_mult(c_rfu_fur, lever_hook, lever_hook_t);
+    mat3_vec_mult(c_rfu_fur, pos_hook, pos_hook_t);
+
+    // 计算 cLR
+    double att1[3] = {att_hook[0], 0, 0};
+    double att2[3] = {0, 0, att_hook[1]};
+    double att3[3] = {0, att_hook[2], 0};
+    double m1[3][3], m2[3][3], m3[3][3], tmp[3][3], cLR[3][3];
+    a2cnb(att1, m1); a2cnb(att2, m2); a2cnb(att3, m3);
+    mat3_mult(m1, m2, tmp);
+    mat3_mult(tmp, m3, cLR);
+
+    // cGL / cLG
+    double attGL[3] = {0, 0, -dir};
+    double cGL[3][3], cLG[3][3];
+    a2cnb(attGL, cGL);
+    mat3_transpose(cGL, cLG);
+
+    // cGN
+    double cGN[3][3];
+    a2cnb(att_net, cGN);
+
+    // cNL
+    double cGN_T[3][3], cNL[3][3];
+    mat3_transpose(cGN, cGN_T);
+    mat3_mult(cGN_T, cGL, cNL);
+
+    // cGA / cAG
+    double attGA[3] = {0, 0, yaw_0};
+    double cGA[3][3], cAG[3][3];
+    a2cnb(attGA, cGA);
+    mat3_transpose(cGA, cAG);
+
+    // cAN / cLA
+    double cAN[3][3], cLA[3][3];
+    mat3_mult(cAG, cGN, cAN);
+    mat3_mult(cLG, cGA, cLA);
+
+    // pos_bottom
+    double cLR_lev[3], pos_bottom[3];
+    mat3_vec_mult(cLR, lever_hook_t, cLR_lev);
+    vec3_sub(pos_hook_t, cLR_lev, pos_bottom);
+
+    // 公共项
+    double cAN_lev[3], temp[3], cLA_temp[3];
+    mat3_vec_mult(cAN, lever_os_t, cAN_lev);
+    vec3_sub(cAN_lev, lever_os_t, temp);
+    mat3_vec_mult(cLA, temp, cLA_temp);
+    // 计算输出
+    double vec1[3], vec2[3];
+    vec3_add(pos_bottom, cLA_temp, vec1);
+    vec3_add(pos_hook_t, cLA_temp, vec2);
+
+    mat3_vec_mult(cNL, vec1, pos_bottom_N);
+    mat3_vec_mult(cNL, vec2, pos_hook_N);
+
+    // 最终坐标转换
+    double tmp1[3], tmp2[3];
+    mat3_vec_mult(c_fur_rfu, pos_bottom_N, pos_bottom_N);
+    mat3_vec_mult(c_fur_rfu, pos_hook_N, pos_hook_N);
+}
+
+//======================== 函数2:箭底N坐标 → 挂钩L坐标 ========================
+// 输入:pos_bottom_N[3], att_hook[3], att_net[3]
+// 输出:pos_hook[3]
+void LidarFusionNode::posbottomN2poshookL(
+    const double pos_bottom_N[3],
+    const double att_hook[3],
+    const double att_net[3],
+    double pos_hook[3])
+{
+    const double glv_deg = M_PI / 180.0;
+    const double dir = 130.0 * glv_deg;
+    const double yaw_0 = att_net[2];
+
+    double lever_os[3] = {0.36, -(65.2 - 3.56), 0.0};
+    double lever_hook[3] = {32.498, 0.0, 0.0};
+
+    // 坐标系变换
+    const double c_rfu_fur[3][3] = {{0,0,1}, {1,0,0}, {0,1,0}};
+    double c_fur_rfu[3][3];
+    mat3_transpose(c_rfu_fur, c_fur_rfu);
+
+    double lever_os_t[3], lever_hook_t[3], pos_N_t[3];
+    mat3_vec_mult(c_rfu_fur, lever_os, lever_os_t);
+    mat3_vec_mult(c_rfu_fur, lever_hook, lever_hook_t);
+    mat3_vec_mult(c_rfu_fur, pos_bottom_N, pos_N_t);
+
+    double att1[3] = {att_hook[0], 0, 0};
+    double att2[3] = {0, 0, att_hook[1]};
+    double att3[3] = {0, att_hook[2], 0};
+
+    double m1[3][3], m2[3][3], m3[3][3], tmp[3][3], cLR[3][3];
+    a2cnb(att1, m1);
+    a2cnb(att2, m2);
+    a2cnb(att3, m3);
+    mat3_mult(m1, m2, tmp);
+    mat3_mult(tmp, m3, cLR);
+
+    double attGL[3] = {0, 0, -dir};
+    double cGL[3][3];
+    a2cnb(attGL, cGL);
+
+    double cGN[3][3];
+    a2cnb(att_net, cGN);
+
+    double cGN_T[3][3], cNL[3][3], cLN[3][3];
+    mat3_transpose(cGN, cGN_T);
+    mat3_mult(cGN_T, cGL, cNL);
+    mat3_transpose(cNL, cLN);
+
+    double attGA[3] = {0, 0, yaw_0};
+    double cGA[3][3], cAG[3][3];
+    a2cnb(attGA, cGA);
+    mat3_transpose(cGA, cAG);
+
+    double cAN[3][3], cNA[3][3];
+    mat3_mult(cAG, cGN, cAN);
+    mat3_transpose(cAN, cNA);
+
+    double cNA_lev[3], tmp_sub[3], tmp_vec[3], pos_bottom[3];
+    mat3_vec_mult(cNA, lever_os_t, cNA_lev);
+    vec3_sub(lever_os_t, cNA_lev, tmp_sub);
+    vec3_sub(pos_N_t, tmp_sub, tmp_vec);
+    mat3_vec_mult(cLN, tmp_vec, pos_bottom);
+
+    // pos_hook
+    double cLR_lev[3], pos_hook_mid[3];
+    mat3_vec_mult(cLR, lever_hook_t, cLR_lev);
+    vec3_add(pos_bottom, cLR_lev, pos_hook_mid);
+
+    // 最终坐标转换
+    mat3_vec_mult(c_fur_rfu, pos_hook_mid, pos_hook);
+}