GVINS源码解析

GVINS是基于VINS-MONO写的,视觉、IMU部分与VINS-MONO类似,可参考我的前一篇文章VINS-MONO学习
这篇文章主要解析与GNSS有关的部分。
持续更新中

estimator_node.cpp

main()

    ros::Subscriber sub_imu = n.subscribe(IMU_TOPIC, 2000, imu_callback, ros::TransportHints().tcpNoDelay());  // 订阅IMU数据
    ros::Subscriber sub_feature = n.subscribe("/gvins_feature_tracker/feature", 2000, feature_callback);       // 订阅图像特征点
    ros::Subscriber sub_restart = n.subscribe("/gvins_feature_tracker/restart", 2000, restart_callback);       // 订阅重启

    ros::Subscriber sub_ephem, sub_glo_ephem, sub_gnss_meas, sub_gnss_iono_params;
    ros::Subscriber sub_gnss_time_pluse_info, sub_local_trigger_info;
    if (GNSS_ENABLE)
    {
        sub_ephem = n.subscribe(GNSS_EPHEM_TOPIC, 100, gnss_ephem_callback);                           // 订阅星历
        sub_glo_ephem = n.subscribe(GNSS_GLO_EPHEM_TOPIC, 100, gnss_glo_ephem_callback);               // 订阅GLONASS星历
        sub_gnss_meas = n.subscribe(GNSS_MEAS_TOPIC, 100, gnss_meas_callback);                         // 订阅GNSS观测
        sub_gnss_iono_params = n.subscribe(GNSS_IONO_PARAMS_TOPIC, 100, gnss_iono_params_callback);    // 订阅电离层参数(Klobuchar模型)

        if (GNSS_LOCAL_ONLINE_SYNC)                                                                           // GNSS与本地在线时间同步 
        {
            sub_gnss_time_pluse_info = n.subscribe(GNSS_TP_INFO_TOPIC, 100, gnss_tp_info_callback);           // 订阅GNSS脉冲信息
            sub_local_trigger_info = n.subscribe(LOCAL_TRIGGER_INFO_TOPIC, 100, local_trigger_info_callback); // 订阅相机触发信息
        }
        else
        {
            time_diff_gnss_local = GNSS_LOCAL_TIME_DIFF;               // 截至23年3月 GPST-UTC=18s
            estimator_ptr->inputGNSSTimeDiff(time_diff_gnss_local);
            time_diff_valid = true;
        }
    }
}

gnss_tp_info_callback()

/* UBX-18010854-R08 P157 GNSS脉冲信息 */
void gnss_tp_info_callback(const GnssTimePulseInfoMsgConstPtr &tp_msg)
{
    gtime_t tp_time = gpst2time(tp_msg->time.week, tp_msg->time.tow);  // 转为计算机时间
    if (tp_msg->utc_based || tp_msg->time_sys == SYS_GLO)              // UTC->GPST   tp_msg->utc_based  1:UTC 0:GNSS
        tp_time = utc2gpst(tp_time);
    else if (tp_msg->time_sys == SYS_GAL)
        tp_time = gst2time(tp_msg->time.week, tp_msg->time.tow);
    else if (tp_msg->time_sys == SYS_BDS)
        tp_time = bdt2time(tp_msg->time.week, tp_msg->time.tow);
    else if (tp_msg->time_sys == SYS_NONE)
    {
        std::cerr << "Unknown time system in GNSSTimePulseInfoMsg.\n";
        return;
    }
    double gnss_ts = time2sec(tp_time);

    std::lock_guard<std::mutex> lg(m_time);
    next_pulse_time = gnss_ts;                  // 下一次脉冲时间
    next_pulse_time_valid = true;
}

local_trigger_info_callback()

/* 在线时间同步--------------------------------------------------------------------------------------------------
*  用rqt_bag查看了一下GVINS配套的体育场数据包,相机触发事件5s一次,GNSS脉冲信息连续5s给的信息是一致的,
*  GNSS脉冲信息给的是下一次脉冲的时间next_pulse_time(可准确转换为GPST),减去相机触发的本地时间戳,就是time_diff_gnss_local
*-------------------------------------------------------------------------------------------------------------*/
void local_trigger_info_callback(const gvins::LocalSensorExternalTriggerConstPtr &trigger_msg)
{
    std::lock_guard<std::mutex> lg(m_time);

    if (next_pulse_time_valid)
    {
        time_diff_gnss_local = next_pulse_time - trigger_msg->header.stamp.toSec();
        estimator_ptr->inputGNSSTimeDiff(time_diff_gnss_local);
        if (!time_diff_valid)       // just get calibrated
            std::cout << "time difference between GNSS and VI-Sensor got calibrated: "
                << std::setprecision(15) << time_diff_gnss_local << " s\n";
        time_diff_valid = true;
    }
}

feature_callback()

void feature_callback(const sensor_msgs::PointCloudConstPtr &feature_msg)
{
    ++ feature_msg_counter;
    
    // 在时间同步后的前两帧决定skip_parameter  
    if (skip_parameter < 0 && time_diff_valid)
    {
        const double this_feature_ts = feature_msg->header.stamp.toSec()+time_diff_gnss_local;
        if (latest_gnss_time > 0 && tmp_last_feature_time > 0)
        {
            if (abs(this_feature_ts - latest_gnss_time) > abs(tmp_last_feature_time - latest_gnss_time))
                skip_parameter = feature_msg_counter%2;         // skip this frame and afterwards
            else
                skip_parameter = 1 - (feature_msg_counter%2);   // skip next frame and afterwards
        }
        cerr << "feature counter is " << feature_msg_counter << ", skip parameter is " << int(skip_parameter) << endl;
        tmp_last_feature_time = this_feature_ts;
    }
    // feature_msg是按照图像原始数据频率发布  猜测:1:控制处理频率(20hz->10hz) 2:得到的图像时间戳更贴近GNSS观测时间戳
    // 查看数据包后 GNSS 10hz 这样控制频率后 图像也是10hz左右
    if (skip_parameter >= 0 && int(feature_msg_counter%2) != skip_parameter)
    {
        m_buf.lock();
        feature_buf.push(feature_msg);
        m_buf.unlock();
        con.notify_one();
    }
}

getMeasurements()

    if (GNSS_ENABLE)
    {
        front_feature_ts += time_diff_gnss_local;                                                // 转到GNSS时间戳
        double front_gnss_ts = time2sec(gnss_meas_buf.front()[0]->time);
        while (!gnss_meas_buf.empty() && front_gnss_ts < front_feature_ts-MAX_GNSS_CAMERA_DELAY) // 删除旧的gnss数据
        {
            ROS_WARN("throw gnss, only should happen at the beginning");
            gnss_meas_buf.pop();
            if (gnss_meas_buf.empty()) return false;
            front_gnss_ts = time2sec(gnss_meas_buf.front()[0]->time);
        }
        if (gnss_meas_buf.empty())
        {
            ROS_WARN("wait for gnss...");
            return false;
        }
        else if (abs(front_gnss_ts-front_feature_ts) < MAX_GNSS_CAMERA_DELAY)                     // 添加
        {
            gnss_msg = gnss_meas_buf.front();
            gnss_meas_buf.pop();
        }
    }

estimator.cpp

processGNSS()

void Estimator::processGNSS(const std::vector<ObsPtr> &gnss_meas)
{
    std::vector<ObsPtr> valid_meas;
    std::vector<EphemBasePtr> valid_ephems;
    for (auto obs : gnss_meas)           
    {
        // filter according to system
        uint32_t sys = satsys(obs->sat, NULL);
        if (sys != SYS_GPS && sys != SYS_GLO && sys != SYS_GAL && sys != SYS_BDS)
            continue;

        // if not got cooresponding ephemeris yet
        if (sat2ephem.count(obs->sat) == 0)
            continue;
        
        if (obs->freqs.empty())    continue;       // no valid signal measurement
        int freq_idx = -1;
        L1_freq(obs, &freq_idx);
        if (freq_idx < 0)   continue;              // no L1 observation
        
        double obs_time = time2sec(obs->time);     // GPST
        std::map<double, size_t> time2index = sat2time_index.at(obs->sat); // <toe(广播星历参考时间), 星历索引>
        double ephem_time = EPH_VALID_SECONDS;
        size_t ephem_index = -1;
        for (auto ti : time2index)                          // 遍历该卫星的所有星历
        {
            if (std::abs(ti.first - obs_time) < ephem_time) // 找距离本次观测时间最近的星历
            {
                ephem_time = std::abs(ti.first - obs_time);
                ephem_index = ti.second;
            }
        }
        if (ephem_time >= EPH_VALID_SECONDS)                // obs_time超出星历参考时刻以外2h 星历无效
        {
            cerr << "ephemeris not valid anymore\n";
            continue;
        }
        const EphemBasePtr &best_ephem = sat2ephem.at(obs->sat).at(ephem_index);

        // filter by tracking status
        LOG_IF(FATAL, freq_idx < 0) << "No L1 observation found.\n";
        if (obs->psr_std[freq_idx]  > GNSS_PSR_STD_THRES ||  // 观测质量不合格
            obs->dopp_std[freq_idx] > GNSS_DOPP_STD_THRES)
        {
            sat_track_status[obs->sat] = 0;
            continue;
        }
        else                                                 // 增加该星的跟踪次数
        {
            if (sat_track_status.count(obs->sat) == 0)
                sat_track_status[obs->sat] = 0;
            ++ sat_track_status[obs->sat];
        }
        if (sat_track_status[obs->sat] < GNSS_TRACK_NUM_THRES) 
            continue;           // not being tracked for enough epochs

        // filter by elevation angle 高度角(仰角)
        if (gnss_ready)  // GNSS与VIO初始化后
        {
            Eigen::Vector3d sat_ecef;
            if (sys == SYS_GLO)                              // 计算粗略卫星位置,计算方位角、高度角
                sat_ecef = geph2pos(obs->time, std::dynamic_pointer_cast<GloEphem>(best_ephem), NULL);
            else
                sat_ecef = eph2pos(obs->time, std::dynamic_pointer_cast<Ephem>(best_ephem), NULL);
            double azel[2] = {0, M_PI/2.0};
            sat_azel(ecef_pos, sat_ecef, azel);              
            if (azel[1] < GNSS_ELEVATION_THRES*M_PI/180.0)
                continue;
        }
        valid_meas.push_back(obs);                           // 存入
        valid_ephems.push_back(best_ephem);
    }
    
    gnss_meas_buf[frame_count] = valid_meas;
    gnss_ephem_buf[frame_count] = valid_ephems;
}

GNSSVIAlign()

bool Estimator::GNSSVIAlign()
{
    if (solver_flag == INITIAL)     // visual-inertial not initialized
        return false;
    
    if (gnss_ready)                 // GNSS-VI already initialized
        return true;
    
    for (uint32_t i = 0; i < (WINDOW_SIZE+1); ++i)   // 检查窗口内GNSS数据
    {
        if (gnss_meas_buf[i].empty() || gnss_meas_buf[i].size() < 10)
            return false;
    }

    // check horizontal velocity excitation
    Eigen::Vector2d avg_hor_vel(0.0, 0.0);
    for (uint32_t i = 0; i < (WINDOW_SIZE+1); ++i)
        avg_hor_vel += Vs[i].head<2>().cwiseAbs();
    avg_hor_vel /= (WINDOW_SIZE+1);
    if (avg_hor_vel.norm() < 0.3)
    {
        std::cerr << "velocity excitation not enough for GNSS-VI alignment.\n";
        return false;
    }

    std::vector<std::vector<ObsPtr>> curr_gnss_meas_buf;
    std::vector<std::vector<EphemBasePtr>> curr_gnss_ephem_buf;
    for (uint32_t i = 0; i < (WINDOW_SIZE+1); ++i)
    {
        curr_gnss_meas_buf.push_back(gnss_meas_buf[i]);
        curr_gnss_ephem_buf.push_back(gnss_ephem_buf[i]);
    }

    GNSSVIInitializer gnss_vi_initializer(curr_gnss_meas_buf, curr_gnss_ephem_buf, latest_gnss_iono_params);

    // 1. get a rough global location  获得接收机在ECEF系下粗略坐标、接收机钟差
    Eigen::Matrix<double, 7, 1> rough_xyzt;
    rough_xyzt.setZero();
    if (!gnss_vi_initializer.coarse_localization(rough_xyzt))
    {
        std::cerr << "Fail to obtain a coarse location.\n";
        return false;
    }

    // 2. perform yaw alignment        w系和当地ENU系对齐 w系的z轴和ENU的U平行,绕z轴转动对齐(航向)
    std::vector<Eigen::Vector3d> local_vs;
    for (uint32_t i = 0; i < (WINDOW_SIZE+1); ++i)
        local_vs.push_back(Vs[i]);                            // b系在w系下的速度
    Eigen::Vector3d rough_anchor_ecef = rough_xyzt.head<3>(); // 接收机在ECEF下的粗糙位置
    double aligned_yaw = 0;
    double aligned_rcv_ddt = 0;
    if (!gnss_vi_initializer.yaw_alignment(local_vs, rough_anchor_ecef, aligned_yaw, aligned_rcv_ddt))
    {
        std::cerr << "Fail to align ENU and local frames.\n";
        return false;
    }
    // std::cout << "aligned_yaw is " << aligned_yaw*180.0/M_PI << '\n';

    // 3. perform anchor refinement    锚点重定义
    std::vector<Eigen::Vector3d> local_ps;
    for (uint32_t i = 0; i < (WINDOW_SIZE+1); ++i)
        local_ps.push_back(Ps[i]);                            // b系在w系下的位置
    Eigen::Matrix<double, 7, 1> refined_xyzt;
    refined_xyzt.setZero();
    if (!gnss_vi_initializer.anchor_refinement(local_ps, aligned_yaw, 
        aligned_rcv_ddt, rough_xyzt, refined_xyzt))
    {
        std::cerr << "Fail to refine anchor point.\n";
        return false;
    }
    // std::cout << "refined anchor point is " << std::setprecision(20) 
    //           << refined_xyzt.head<3>().transpose() << '\n';

    // restore GNSS states
    uint32_t one_observed_sys = static_cast<uint32_t>(-1);
    for (uint32_t k = 0; k < 4; ++k)
    {
        if (rough_xyzt(k+3) != 0)
        {
            one_observed_sys = k;
            break;
        }
    }
    for (uint32_t i = 0; i < (WINDOW_SIZE+1); ++i)
    {
        para_rcv_ddt[i] = aligned_rcv_ddt;
        for (uint32_t k = 0; k < 4; ++k)
        {
            if (rough_xyzt(k+3) == 0)
                para_rcv_dt[i*4+k] = refined_xyzt(3+one_observed_sys) + aligned_rcv_ddt * i;
            else
                para_rcv_dt[i*4+k] = refined_xyzt(3+k) + aligned_rcv_ddt * i;
        }
    }
    anc_ecef = refined_xyzt.head<3>();
    R_ecef_enu = ecef2rotation(anc_ecef);

    yaw_enu_local = aligned_yaw;

    return true;
}

GNSS与VIO初始化参考
1、参考1:含中文解析代码
2、参考2

yaw_alignment()

/* w系与ENU系对齐 */ 
bool GNSSVIInitializer::yaw_alignment(const std::vector<Eigen::Vector3d> &local_vs, 
    const Eigen::Vector3d &rough_anchor_ecef, double &aligned_yaw, double &rcv_ddt)
{
    aligned_yaw = 0;
    rcv_ddt = 0;

    double est_yaw = 0;
    double est_rcv_ddt = 0;

    Eigen::Matrix3d rough_R_ecef_enu = ecef2rotation(rough_anchor_ecef);  // ENU到ECEF的旋转矩阵
    uint32_t align_iter = 0;
    double align_dx_norm = 1.0;
    while (align_iter < MAX_ITERATION && align_dx_norm > CONVERGENCE_EPSILON) // 迭代最小二乘求解接收机时钟钟差变化率和偏航角
    {
        Eigen::MatrixXd align_G(num_all_meas, 2);         // (观测(残差)方程数量、参数数量)
        align_G.setZero();
        align_G.col(1).setOnes();
        Eigen::VectorXd align_b(num_all_meas);
        align_b.setZero();
        Eigen::Matrix3d align_R_enu_local(Eigen::AngleAxisd(est_yaw, Eigen::Vector3d::UnitZ())); // enu系<-w系初始值:绕Z轴转est_yaw的轴角 转 旋转矩阵
        Eigen::Matrix3d align_tmp_M;                      //  绕Z轴旋转est_yaw角度的旋转矩阵 求导
        align_tmp_M << -sin(est_yaw), -cos(est_yaw), 0,
                        cos(est_yaw), -sin(est_yaw), 0,
                        0       , 0        , 0;
        
        // 构建多普勒测量的残差和雅可比矩阵
        uint32_t align_counter = 0;
        for (uint32_t i = 0; i < gnss_meas_buf.size(); ++i)
        {
            Eigen::Matrix<double, 4, 1> ecef_vel_ddt;         
            ecef_vel_ddt.head<3>() = rough_R_ecef_enu * align_R_enu_local * local_vs[i];    // 得到接收机在ECEF系下的速度
            ecef_vel_ddt(3) = est_rcv_ddt;                                                  // 接收机时钟漂移率
            Eigen::VectorXd epoch_res;                                                      // 多普勒测量残差
            Eigen::MatrixXd epoch_J;                                                        // 多普勒测量雅可比
            dopp_res(ecef_vel_ddt, rough_anchor_ecef, gnss_meas_buf[i], all_sat_states[i], epoch_res, epoch_J);
            align_b.segment(align_counter, gnss_meas_buf[i].size()) = epoch_res;

            /* dopp_res中的雅可比矩阵J是对速度求导数,优化变量实际为航向角,G矩阵要乘以速度对yaw的导数
            *  align_tmp_M = ENU<-LOCAL WORLD  d()/d(yaw)   
            *  align_tmp_M*local_vs[i] = ENU系下 d(v)/d(yaw)
            *  rough_R_ecef_enu*align_tmp_M*local_vs[i] = ECEF系下 d(v)/d(yaw)           */ 
            align_G.block(align_counter, 0, gnss_meas_buf[i].size(), 1) = 
                epoch_J.leftCols(3)*rough_R_ecef_enu*align_tmp_M*local_vs[i];  
            align_counter += gnss_meas_buf[i].size();
        }
        Eigen::VectorXd dx = -(align_G.transpose()*align_G).inverse() * align_G.transpose() * align_b;
        est_yaw += dx(0);
        est_rcv_ddt += dx(1);
        align_dx_norm = dx.norm();
        ++ align_iter;
    }

    if (align_iter > MAX_ITERATION)
    {
        std::cerr << "Fail to initialize yaw offset.\n";
        return false;
    }

    // 调整航向角到[-180, 180]
    aligned_yaw = est_yaw;
    if (aligned_yaw > M_PI)
        aligned_yaw -= floor(est_yaw/(2.0*M_PI) + 0.5) * (2.0*M_PI);
    else if (aligned_yaw < -M_PI)
        aligned_yaw -=  ceil(est_yaw/(2.0*M_PI) - 0.5) * (2.0*M_PI);

    rcv_ddt = est_rcv_ddt;

    return true;
}

多普勒测速原理参考
1、参考1
2、参考2

anchor_refinement()

bool GNSSVIInitializer::anchor_refinement(const std::vector<Eigen::Vector3d> &local_ps, 
    const double aligned_yaw, const double aligned_ddt, 
    const Eigen::Matrix<double, 7, 1> &rough_ecef_dt, Eigen::Matrix<double, 7, 1> &refined_ecef_dt)
{
    refined_ecef_dt.setZero();

    Eigen::Matrix3d aligned_R_enu_local(Eigen::AngleAxisd(aligned_yaw, Eigen::Vector3d::UnitZ())); // w系到enu系的转换矩阵;

    // refine anchor point and receiver clock bias
    Eigen::Vector3d refine_anchor = rough_ecef_dt.head<3>();
    Eigen::Vector4d refine_dt = rough_ecef_dt.tail<4>();
    uint32_t refine_iter = 0;
    double refine_dx_norm = 1.0;
    std::vector<uint32_t> unobserved_sys;
    for (uint32_t k = 0; k < 4; ++k)
    {
        if (rough_ecef_dt(3+k) == 0)
            unobserved_sys.push_back(k);
    }

    while (refine_iter < MAX_ITERATION && refine_dx_norm > CONVERGENCE_EPSILON)
    {
        Eigen::MatrixXd refine_G(num_all_meas+unobserved_sys.size(), 7);
        Eigen::VectorXd refine_b(num_all_meas+unobserved_sys.size());
        refine_G.setZero();
        refine_b.setZero();
        uint32_t refine_counter = 0;
        Eigen::Matrix3d refine_R_ecef_enu = ecef2rotation(refine_anchor);                  // enu系到ecef系的旋转矩阵
        Eigen::Matrix3d refine_R_ecef_local = refine_R_ecef_enu * aligned_R_enu_local;     // w系到ecef系的旋转矩阵
        for (uint32_t i = 0; i < gnss_meas_buf.size(); ++i)
        {
            Eigen::Matrix<double, 7, 1> ecef_xyz_dt;
            ecef_xyz_dt.head<3>() = refine_R_ecef_local * local_ps[i] + refine_anchor;     // 锚点与w系原点重合,计算结果应该是每帧b系在ECEF系的位置?
            ecef_xyz_dt.tail<4>() = refine_dt + aligned_ddt * i * Eigen::Vector4d::Ones(); // 钟差

            Eigen::VectorXd epoch_res;
            Eigen::MatrixXd epoch_J;
            std::vector<Eigen::Vector2d> tmp_atmos_delay, tmp_sv_azel;
            psr_res(ecef_xyz_dt, gnss_meas_buf[i], all_sat_states[i], iono_params, 
                epoch_res, epoch_J, tmp_atmos_delay, tmp_sv_azel);
            refine_b.segment(refine_counter, gnss_meas_buf[i].size()) = epoch_res;
            refine_G.middleRows(refine_counter, gnss_meas_buf[i].size()) = epoch_J;
            refine_counter += gnss_meas_buf[i].size();
        }
        for (uint32_t k : unobserved_sys)
        {
            refine_b(refine_counter) = 0;
            refine_G(refine_counter, k+3) = 1.0;
            ++ refine_counter;
        }

        Eigen::VectorXd dx = -(refine_G.transpose()*refine_G).inverse() * refine_G.transpose() * refine_b;
        refine_anchor += dx.head<3>();
        refine_dt += dx.tail<4>();
        refine_dx_norm = dx.norm();
        ++ refine_iter;
    }

    if (refine_iter > MAX_ITERATION)
    {
        std::cerr << "Fail to perform anchor refinement.\n";
        return false;
    }

    refined_ecef_dt.head<3>() = refine_anchor;
    refined_ecef_dt.tail<4>() = refine_dt;

    return true;
}

updateGNSSStatistics()

// GNSS初始化完成后,更新参数
void Estimator::updateGNSSStatistics()
{
    R_enu_local = Eigen::AngleAxisd(yaw_enu_local, Eigen::Vector3d::UnitZ());  // w系到enu系的旋转矩阵
    enu_pos = R_enu_local * Ps[WINDOW_SIZE];                                   // 令enu为参考系
    enu_vel = R_enu_local * Vs[WINDOW_SIZE];
    enu_ypr = Utility::R2ypr(R_enu_local*Rs[WINDOW_SIZE]);                     // 欧拉角组(deg)
    ecef_pos = anc_ecef + R_ecef_enu * enu_pos;                                // b系在ECEF下的位置
}
}

optimization()

A. 优化

	...
    // gnss相关参数块 显式定义
    if (gnss_ready)
    {
        problem.AddParameterBlock(para_yaw_enu_local, 1);   // w系到enu系的航向角
        Eigen::Vector2d avg_hor_vel(0.0, 0.0);
        for (uint32_t i = 0; i <= WINDOW_SIZE; ++i)
            avg_hor_vel += Vs[i].head<2>().cwiseAbs();
        avg_hor_vel /= (WINDOW_SIZE+1);
        // cerr << "avg_hor_vel is " << avg_vel << endl;
        if (avg_hor_vel.norm() < 0.3)                       // 速度不够,不优化航向角
        {
            // std::cerr << "velocity excitation not enough, fix yaw angle.\n";
            problem.SetParameterBlockConstant(para_yaw_enu_local);
        }

        for (uint32_t i = 0; i <= WINDOW_SIZE; ++i)
        {
            if (gnss_meas_buf[i].size() < 10)               // GNSS观测数据不够,不优化航向角
                problem.SetParameterBlockConstant(para_yaw_enu_local); 
        }
        
        problem.AddParameterBlock(para_anc_ecef, 3);        // 锚点(w系与ENU系的共同原点)
        // problem.SetParameterBlockConstant(para_anc_ecef);

        for (uint32_t i = 0; i <= WINDOW_SIZE; ++i)
        {
            for (uint32_t k = 0; k < 4; ++k)
                problem.AddParameterBlock(para_rcv_dt+i*4+k, 1); // 钟差
            problem.AddParameterBlock(para_rcv_ddt+i, 1);        // 钟漂
        }
    }

    TicToc t_whole, t_prepare;
    vector2double();

    // 猜测:在优化中固定初始化后的第一帧b系在w系下的位姿(由于绝对X、Y、Z、YAW不可观)
    if (first_optimization)
    {
        std::vector<double> anchor_value;
        for (uint32_t k = 0; k < 7; ++k)
            anchor_value.push_back(para_Pose[0][k]);
        PoseAnchorFactor *pose_anchor_factor = new PoseAnchorFactor(anchor_value);
        problem.AddResidualBlock(pose_anchor_factor, NULL, para_Pose[0]);
        first_optimization = false;
    }
    ...
    // gnss相关残差和雅可比
    if (gnss_ready)
    {
        for(int i = 0; i <= WINDOW_SIZE; ++i)                                 
        {
            // cerr << "size of gnss_meas_buf[" << i << "] is " << gnss_meas_buf[i].size() << endl;
            const std::vector<ObsPtr> &curr_obs = gnss_meas_buf[i];               // 观测数据
            const std::vector<EphemBasePtr> &curr_ephem = gnss_ephem_buf[i];      // 星历

            for (uint32_t j = 0; j < curr_obs.size(); ++j)
            {
                const uint32_t sys = satsys(curr_obs[j]->sat, NULL);              // 系统
                const uint32_t sys_idx = gnss_comm::sys2idx.at(sys);              // 索引

                int lower_idx = -1;
                const double obs_local_ts = time2sec(curr_obs[j]->time) - diff_t_gnss_local; // 图像时间戳
                if (Headers[i].stamp.toSec() > obs_local_ts)                      // 这帧图像时间戳比gnss大
                    lower_idx = (i==0? 0 : i-1);
                else                                                              // 这帧图像时间戳比gnss小
                    lower_idx = (i==WINDOW_SIZE? WINDOW_SIZE-1 : i);
                const double lower_ts = Headers[lower_idx].stamp.toSec();    // 一般:lower_ts < obs_local_ts < upper_ts 
                const double upper_ts = Headers[lower_idx+1].stamp.toSec();  // 特殊:obs_local_ts < lower_ts < upper_ts < obs_local_ts

                const double ts_ratio = (upper_ts-obs_local_ts) / (upper_ts-lower_ts); 
                // 构造代价函数 计算残差和雅可比
                GnssPsrDoppFactor *gnss_factor = new GnssPsrDoppFactor(curr_obs[j],   
                    curr_ephem[j], latest_gnss_iono_params, ts_ratio);
                problem.AddResidualBlock(gnss_factor, NULL, para_Pose[lower_idx], 
                    para_SpeedBias[lower_idx], para_Pose[lower_idx+1], para_SpeedBias[lower_idx+1],
                    para_rcv_dt+i*4+sys_idx, para_rcv_ddt+i, para_yaw_enu_local, para_anc_ecef);
            }
        }

        // build relationship between rcv_dt and rcv_ddt
        for (size_t k = 0; k < 4; ++k)
        {
            for (uint32_t i = 0; i < WINDOW_SIZE; ++i)
            {
                const double gnss_dt = Headers[i+1].stamp.toSec() - Headers[i].stamp.toSec();
                DtDdtFactor *dt_ddt_factor = new DtDdtFactor(gnss_dt);
                problem.AddResidualBlock(dt_ddt_factor, NULL, para_rcv_dt+i*4+k, 
                    para_rcv_dt+(i+1)*4+k, para_rcv_ddt+i, para_rcv_ddt+i+1);
            }
        }

        // add rcv_ddt smooth factor
        for (int i = 0; i < WINDOW_SIZE; ++i)
        {
            DdtSmoothFactor *ddt_smooth_factor = new DdtSmoothFactor(GNSS_DDT_WEIGHT);
            problem.AddResidualBlock(ddt_smooth_factor, NULL, para_rcv_ddt+i, para_rcv_ddt+i+1);
        }
    }
PoseAnchorFactor::Evaluate()
bool PoseAnchorFactor::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const
{
    Eigen::Map<const Eigen::Matrix<double, 7, 1>> pose(parameters[0]);
    Eigen::Map<Eigen::Matrix<double, 6, 1>> res(residuals);

    res.head<3>() = pose.head<3>() - _anchor_point.head<3>();      // 位置残差
    const Eigen::Quaterniond curr_q(pose.tail<4>());
    const Eigen::Quaterniond anchor_q(_anchor_point.tail<4>());
    res.tail<3>() = 2.0 * (curr_q*anchor_q.inverse()).vec();       // 旋转残差
    res *= sqrt_info;                                              // 大权重
    if (jacobians && jacobians[0])
    {
        Eigen::Map<Eigen::Matrix<double, 6, 7, Eigen::RowMajor>> J(jacobians[0]);
        J.setZero();
        J.topLeftCorner<3, 3>().setIdentity();   // res = (x_p-anchor_p)*sqrt_info

        Eigen::Quaterniond anchor_q_inv = anchor_q.inverse();
        Eigen::Matrix3d J_q;                     // res = 2*(q_x*q_anchor_q_inv)xyz*sqrt_info 
        J_q << anchor_q_inv.w(),  anchor_q_inv.z(), -anchor_q_inv.y(),  
              -anchor_q_inv.z(),  anchor_q_inv.w(),  anchor_q_inv.x(),
               anchor_q_inv.y(), -anchor_q_inv.x(),  anchor_q_inv.w();
        J.block<3, 3>(3, 3) = J_q;
        J *= 2.0*sqrt_info;                                        // 为什么平移部分也要乘2 ????
    }
    return true;
}

思考:这一段中雅可比J全部乘2不是很理解,知道的朋友求解惑!

GnssPsrDoppFactor::Evaluate()

GnssPsrDoppFactor 相关参数
para[0]: position and orientation at time k
para[1]: velocity and acc/gyro bias at time k
para[2]: position and orientation at time k+1
para[3]: velocity and acc/gyro bias at time k+1
para[4]: receiver clock bias in light travelling distance (m)
para[5]: receiver clock bias change rate in clock bias light travelling distance per second (m/s)
para[6]: yaw difference between ENU and local coordinate (rad)
para[7]: anchor point’s ECEF coordinate

bool GnssPsrDoppFactor::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const
{
    Eigen::Vector3d Pi(parameters[0][0], parameters[0][1], parameters[0][2]);
    Eigen::Vector3d Vi(parameters[1][0], parameters[1][1], parameters[1][2]);
    Eigen::Vector3d Pj(parameters[2][0], parameters[2][1], parameters[2][2]);
    Eigen::Vector3d Vj(parameters[3][0], parameters[3][1], parameters[3][2]);
    double rcv_dt = parameters[4][0];
    double rcv_ddt = parameters[5][0];
    double yaw_diff = parameters[6][0];
    Eigen::Vector3d ref_ecef(parameters[7][0], parameters[7][1], parameters[7][2]);

    const Eigen::Vector3d local_pos = ratio*Pi + (1.0-ratio)*Pj;     // 通过相邻两帧图像的PV线性插值出对齐GNSS数据时刻的PV
    const Eigen::Vector3d local_vel = ratio*Vi + (1.0-ratio)*Vj;

    double sin_yaw_diff = std::sin(yaw_diff);
    double cos_yaw_diff = std::cos(yaw_diff);
    Eigen::Matrix3d R_enu_local;
    R_enu_local << cos_yaw_diff, -sin_yaw_diff, 0,
                   sin_yaw_diff,  cos_yaw_diff, 0,
                   0           ,  0           , 1;
    Eigen::Matrix3d R_ecef_enu = ecef2rotation(ref_ecef);
    Eigen::Matrix3d R_ecef_local = R_ecef_enu * R_enu_local;

    Eigen::Vector3d P_ecef = R_ecef_local * local_pos + ref_ecef;
    Eigen::Vector3d V_ecef = R_ecef_local * local_vel;

    double ion_delay = 0, tro_delay = 0;
    double azel[2] = {0, M_PI/2.0};
    if (P_ecef.norm() > 0)
    {
        sat_azel(P_ecef, sv_pos, azel);                                              // 方位角、高度角
        Eigen::Vector3d rcv_lla = ecef2geo(P_ecef);                                  
        tro_delay = calculate_trop_delay(obs->time, rcv_lla, azel);                  // 对流层延迟
        ion_delay = calculate_ion_delay(obs->time, iono_paras, rcv_lla, azel);       // 电离层延迟
    }
    double sin_el = sin(azel[1]);
    double sin_el_2 = sin_el*sin_el;
    double pr_weight = sin_el_2 / pr_uura * relative_sqrt_info;                      // 伪距权重
    double dp_weight = sin_el_2 / dp_uura * relative_sqrt_info * PSR_TO_DOPP_RATIO;  // 多普勒权重

    Eigen::Vector3d rcv2sat_ecef = sv_pos - P_ecef;
    Eigen::Vector3d rcv2sat_unit = rcv2sat_ecef.normalized();                        

    const double psr_sagnac = EARTH_OMG_GPS*(sv_pos(0)*P_ecef(1)-sv_pos(1)*P_ecef(0))/LIGHT_SPEED; // 地球自转
    double psr_estimated = rcv2sat_ecef.norm() + psr_sagnac + rcv_dt - svdt*LIGHT_SPEED +        
                                ion_delay + tro_delay + tgd*LIGHT_SPEED;
    
    residuals[0] = (psr_estimated - obs->psr[freq_idx]) * pr_weight;                 // 伪距残差

    const double dopp_sagnac = EARTH_OMG_GPS/LIGHT_SPEED*(sv_vel(0)*P_ecef(1)+
            sv_pos(0)*V_ecef(1) - sv_vel(1)*P_ecef(0) - sv_pos(1)*V_ecef(0));
    double dopp_estimated = (sv_vel - V_ecef).dot(rcv2sat_unit) + dopp_sagnac + rcv_ddt - svddt*LIGHT_SPEED;
    const double wavelength = LIGHT_SPEED / freq;
    residuals[1] = (dopp_estimated + obs->dopp[freq_idx]*wavelength) * dp_weight;    // 多普勒残差

    if (jacobians)  // 对各个优化量求取雅可比
    {
        // J_Pi  i时刻位姿
        if (jacobians[0])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> J_Pi(jacobians[0]);
            J_Pi.setZero();
            J_Pi.topLeftCorner<1, 3>() = -rcv2sat_unit.transpose() * R_ecef_local * pr_weight * ratio; // residuals[0]对Pi求一阶导

            const double norm3 = pow(rcv2sat_ecef.norm(), 3);   // 卫地距的3次方
            const double norm2 = rcv2sat_ecef.squaredNorm();    // 卫地距的2次方
            Eigen::Matrix3d unit2rcv_pos;
            for (size_t i = 0; i < 3; ++i)
            {
                for (size_t j = 0; j < 3; ++j)
                {
                    if (i == j)
                        unit2rcv_pos(i, j) = (norm2-rcv2sat_ecef(i)*rcv2sat_ecef(i))/norm3;
                    else
                        unit2rcv_pos(i, j) = (-rcv2sat_ecef(i)*rcv2sat_ecef(j))/norm3;
                }
            }
            unit2rcv_pos *= -1;
            J_Pi.bottomLeftCorner<1, 3>() = (sv_vel-V_ecef).transpose() * unit2rcv_pos * 
                R_ecef_local * dp_weight * ratio;                                                      //  residuals[1]对Pi求一阶导
        }

        // J_Vi  i时刻速度
        if (jacobians[1])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 9, Eigen::RowMajor>> J_Vi(jacobians[1]);
            J_Vi.setZero();
            J_Vi.bottomLeftCorner<1, 3>() = rcv2sat_unit.transpose() * (-1.0) * 
                R_ecef_local * dp_weight * ratio;
        }

        // J_Pj
        if (jacobians[2])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> J_Pj(jacobians[2]);
            J_Pj.setZero();
            J_Pj.topLeftCorner<1, 3>() = -rcv2sat_unit.transpose() * R_ecef_local * pr_weight * (1.0-ratio);

            const double norm3 = pow(rcv2sat_ecef.norm(), 3);
            const double norm2 = rcv2sat_ecef.squaredNorm();
            Eigen::Matrix3d unit2rcv_pos;
            for (size_t i = 0; i < 3; ++i)
            {
                for (size_t j = 0; j < 3; ++j)
                {
                    if (i == j)
                        unit2rcv_pos(i, j) = (norm2-rcv2sat_ecef(i)*rcv2sat_ecef(i))/norm3;
                    else
                        unit2rcv_pos(i, j) = (-rcv2sat_ecef(i)*rcv2sat_ecef(j))/norm3;
                }
            }
            unit2rcv_pos *= -1;
            J_Pj.bottomLeftCorner<1, 3>() = (sv_vel-V_ecef).transpose() * unit2rcv_pos * 
                R_ecef_local * dp_weight * (1.0-ratio);
        }

        // J_Vj
        if (jacobians[3])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 9, Eigen::RowMajor>> J_Vj(jacobians[3]);
            J_Vj.setZero();
            J_Vj.bottomLeftCorner<1, 3>() = rcv2sat_unit.transpose() * (-1.0) * 
                R_ecef_local * dp_weight * (1.0-ratio);
        }

        // J_rcv_dt
        if (jacobians[4])
        {
            jacobians[4][0] = 1.0 * pr_weight;
            jacobians[4][1] = 0;
        }

        // J_rcv_ddt
        if (jacobians[5])
        {
            jacobians[5][0] = 0;
            jacobians[5][1] = 1.0 * dp_weight;
        }

        // J_yaw_diff
        if (jacobians[6])
        {
            Eigen::Matrix3d d_yaw;
            // 绕Z轴旋转yaw 的导数
            d_yaw << -sin_yaw_diff, -cos_yaw_diff, 0, 
                      cos_yaw_diff, -sin_yaw_diff, 0, 
                      0           ,  0           , 0;
            jacobians[6][0] = -rcv2sat_unit.dot(R_ecef_enu * d_yaw * local_pos) * pr_weight;
            jacobians[6][1] = -rcv2sat_unit.dot(R_ecef_enu * d_yaw * local_vel) * dp_weight;
        }

        // J_ref_ecef, approximation for simplicity
        if (jacobians[7])
        {
            Eigen::Map<Eigen::Matrix<double, 2, 3, Eigen::RowMajor>> J_ref_ecef(jacobians[7]);
            J_ref_ecef.setZero();
            J_ref_ecef.row(0) = -rcv2sat_unit.transpose() * pr_weight;
        }
    }
    return true;
}
多普勒残差对位置的雅可比推导

在这里插入图片描述

DtDdtFactor::Evaluate()

para[0]: rev_dt (t) in light travelling distance (m)
para[1]: rev_dt (t+1) in light travelling distance (m)
para[2]: rev_ddt(t) in light travelling distance per second (m/s)
para[3]: rev_ddt(t+1) in light travelling distance per second (m/s)

DdtSmoothFactor::Evaluate()

para[0]: rev_ddt (t) in light travelling distance (m)
para[1]: rev_ddt (t+1) in light travelling distance (m)

和钟差、钟漂相关的约束也不是很明白 论文中 钟漂建模为随机游走过程是怎么体现的

B. 边缘化(类似)

  • 9
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值