理论:(2)“RCTM” 和 “AIME” 故障检测与修复方法 (理论和C++代码)

本文介绍了 GNSS/INS 紧组合导航系统的故障检测与修复方法,重点讨论了RCTM和AIME算法。AIME方法包括基于检验统计量和新息协方差矩阵的故障检测,对阶跃和渐变故障有不同响应速度。文中还提及了代码实现,特别是故障修复/隔离算法,并预告将开源全部代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//======================================================================//

GNSS/INS紧组合导航系统完好性监测(理论和c++代码)专栏,后续会开源全部代码

https://blog.csdn.net/hltt3838/category_12207970.html?spm=1001.2014.3001.5482

//======================================================================//

目录

一、故障检测算法

二、故障修复/隔离算法

2.1 基于检验统计量

2.2 基于新息协方差矩阵

三、代码部分

一、故障检测算法

 备注:

  • 理论部分还是比较容易理解的,代码实现也比较简单,见后续 !
  • 该方法的优点是 对阶越故障相应快,渐变故障检测慢 !
  • 对于初学者需要注意的是 卡方检验的相关概念和求法,比较详细的介绍博客参考下面的连接 !

状态分布函数 详细介绍_他人是一面镜子,保持谦虚的态度的博客-CSDN博客_虚警概率与卡方分布

 

上述的AIME并非 原论文中的AIME方法,更像是改进。源论文方法如下:

 备注:

  • 理论部分理解相对复杂,代码实现稍微复杂,理论图如下

  • 该方法的优点是 对渐变故障检测相对快 !

二、故障修复/隔离算法

2.1 基于检验统计量

 

 参考论文:

INS/GNSS 松组合导航故障检测与修复算法(苗岳旺 1 , 孙付平 1 , 李海峰 1 , 李飞 2)

2.2 基于新息协方差矩阵

 参考论文:

GNSS/INS 紧组合导航接收机自主完好性监测技术研究 
 

三、代码部分


double TC_Filter::RCTM_test_statistic(Eigen::VectorXd &rk, Eigen::MatrixXd &Ak)
{
   double Ts = 0;
   Ts = rk.transpose() * Ak.inverse() * rk;
  // std::cout<<"--------------------------- Ts_RCTM ="<<Ts<<std::endl;
   
   return Ts;
}


double TC_Filter::AIME_test_statistic(Eigen::Matrix<double, NSTATE, 1> &mdx_, Eigen::Matrix<double, NSTATE, NSTATE> &m_Phi_, Eigen::MatrixXd &Hk_, Eigen::Matrix<double, NSTATE, NSTATE> &Pk_, Eigen::VectorXd &Zk_,Eigen::MatrixXd &Rk_)
{
   double Ts = 0;
   
   if( mdx_vec.size() < M_Td )
   {
      mdx_vec.push_back(mdx_);
      m_Phi_vec.push_back(m_Phi_);
      Hk_vec.push_back(Hk_);
      Pk_vec.push_back(Pk_);  
      Zk_vec.push_back(Zk_);
      Rk_vec.push_back(Rk_);
   }
   
   if( mdx_vec.size() >= M_Td )
   {
      Eigen::VectorXd Xks_, Zks_; 
      Eigen::VectorXd r_i; 
      Eigen::MatrixXd A_i,Aavg_inv;
      
      Eigen::VectorXd beta_i, r_avg; 
      
      Xks_ =  mdx_vec[0];
      
      for(int i = 0; i< mdx_vec.size(); i++)
      {
         A_i.noalias() = Hk_vec[i] * Pk_vec[i] * Hk_vec[i].transpose() + Rk_vec[i]; 
         
         Aavg_inv.setZero(A_i.rows(), A_i.cols());
         Aavg_inv.noalias() += A_i.inverse();
      }
    
     
      for(int i = 0; i < mdx_vec.size(); i++)
      {
         for(int j = 0; j <= i; j++)
            Xks_.noalias() = m_Phi_vec[j] * Xks_;
        
         Zks_.noalias() = Hk_vec[i] * Xks_;
         r_i = Zk_vec[i] - Zks_;  
         A_i.noalias() = Hk_vec[i] * Pk_vec[i] * Hk_vec[i].transpose() + Rk_vec[i]; 
         
         beta_i.setZero(A_i.rows());
         beta_i += A_i.inverse() * r_i;
      }
     
      r_avg.noalias() = Aavg_inv.inverse() * beta_i;  
      Ts = r_avg.transpose() * Aavg_inv * r_avg;
       
      // delete the first  
      mdx_vec.erase(mdx_vec.begin());
      m_Phi_vec.erase(m_Phi_vec.begin());
      Hk_vec.erase(Hk_vec.begin());
      Pk_vec.erase(Pk_vec.begin());
      Zk_vec.erase(Zk_vec.begin());
      Rk_vec.erase(Rk_vec.begin());
   }
   
   
   std::cout<<"--------------------------- mdx_vec.size() ="<<mdx_vec.size()<<std::endl; 
   
   return Ts;
}





// 原版 方法
void FUSION_KF::AIME_test_statistic( Eigen::VectorXd &Zk_p,Eigen::MatrixXd &Vk_p, double &Ts_AIME)
{
   if( Zk_vec.size() < M_Td )
   {
      Zk_vec.push_back(Zk_p);
      Vk_vec.push_back(Vk_p);
   }
   
    
   if( Zk_vec.size() >= M_Td )
   {
       Eigen::MatrixXd Vavg_inv, Vavg_inv_inv;
       Eigen::VectorXd beta, r_avg;  
       
       // 1、为了解决不同时刻矩阵维度不一致问题
       int max_rows,max_cols;
       max_rows = 0; max_cols = 0;
       for(int i = 0; i< Zk_vec.size(); i++)
       {
          if( Vk_vec[i].rows() > max_rows)
              max_rows = Vk_vec[i].rows();
              
          if( Vk_vec[i].cols() > max_cols)
              max_cols = Vk_vec[i].cols(); 
       }
       std::cout <<"--max_rows - max_cols 1= \n"<<max_rows<<","<<max_cols<<std::endl;
       
       
       // 2、求Vavg
       Vavg_inv.setZero(max_rows,max_cols);
       
       for(int i = 0; i< Zk_vec.size(); i++)
       {
         Eigen::MatrixXd Vi_inv,Vi_inv_mid;
         Vi_inv_mid.setZero(max_rows,max_cols);
         
         Vi_inv.setZero(Vk_vec[i].rows(), Vk_vec[i].cols());
         Vi_inv = Vk_vec[i].inverse();
         
         Vi_inv_mid.topLeftCorner(Vk_vec[i].rows(), Vk_vec[i].cols())  = Vi_inv;
         
         Vavg_inv += Vi_inv_mid;
       }
        
        
      // 3、
      beta.setZero(max_rows);
      for(int i = 0; i < Zk_vec.size(); i++) 
      {
         Eigen::VectorXd vkinv_rk,vkinv_rk_mid; 
         vkinv_rk_mid.setZero(max_rows);
         
         vkinv_rk.noalias() = Vk_vec[i].inverse() * Zk_vec[i];
         vkinv_rk_mid.head(vkinv_rk.rows()) = vkinv_rk;
         beta += vkinv_rk_mid;
      }
      
      // 4、
      r_avg.noalias() = Vavg_inv.inverse() * beta;
      Ts_AIME = r_avg.transpose() * Vavg_inv * r_avg ;
    
      Ts_AIME = sqrt(Ts_AIME);

      if(abs(Ts_AIME) > 500.0)
         Ts_AIME = 2.0;  
      
      std::cout <<"-----------------------Ts_AIME = \n"<<Ts_AIME<<std::endl;
      
    
      // 4、delete the first  
      Zk_vec.erase(Zk_vec.begin());
      Vk_vec.erase(Vk_vec.begin());
   }
}

备注:

  • M_Td 是设置的外推周期 !这个是可以根据不同的应用场景而改动 !
  • 函数放在 组合导航系统EKF更新函数中,后续会开源全部代码 !
  • 反是代码有问题,烦请指出,大家共同学习和进步,谢谢 !

抗差 代码(上述理论对应)

       // =============== added by Jiang
       double Td = 10.83;
       Eigen::MatrixXd Ak,Ak_inv,MAX_mid;
       MAX_mid.setIdentity( Rmat.rows(),Rmat.cols() );
       Ak.noalias() = Hmat * covar_Th + Rmat;
       Ak_inv = Ak.inverse();
       
       for(int i = 0; i < Rmat.rows(); i++)
       {
          double Tsi = 0.0;
          Tsi = Zmat[i] * Ak_inv(i,i) * Zmat[i];
          
          if( Tsi > Td)
          {
            MAX_mid(i,i) = Td / Tsi;  
            std::cout<<"---------------Tsi= \n"<<Tsi<<std::endl;  
          }
          else
            MAX_mid(i,i) = 1.0;   
       }

三、题外话

我们在调试代码的时候可能会遇到一个奇怪的问题,组合导航结果在某一时间段 出现很大的定位误差,但是抗差对其无用。

       正常人的思维方式都司卫星观测值不好,其实还会有一种情况,那便是IMU的参数不对 !很多人都会忽略IMU问题,但是确实存在相应参数不准确的情况,遇到改问题,分两步处理!

  •  单点定位 看看GNSS的定位结果,若发现无异常,则是IMU问题;
  • IMU参数 调大即可 (一般把零偏不确定性设为之前的五倍左右)

<think>好的,我现在需要帮用户解决在Unreal Engine中使用UF打断曲线的问题。首先,用户提到的“UF”可能是指Unreal Engine中的某种功能或操作,但需要先明确UF的具体含义。在UE的上下文中,UF通常指Unreal Framework或者蓝图函数(User Function)的标记,比如UFUNCTION宏。但结合“打断曲线”,可能涉及曲线编辑、动画曲线或样条组件。 接下来,我需要回忆Unreal Engine中处理曲线的相关模块。比如在蓝图或C++中,曲线常用于动画、时间轴、材质等。用户可能是在编辑样条曲线时想要打断或分割曲线,或者在动画曲线编辑器中调整关键帧。此外,样条组件(Spline Component)是UE中处理曲线的重要部分,用于创建路径、轨道等。 查阅相关资料,UE的样条组件提供了一系列函数来操作样条点,如添加、删除、移动点。打断曲线可能指在特定位置分割样条,这可能需要插入新的样条点并调整切线。另外,动画曲线中的打断可能涉及关键帧的分割或曲线的断开连接。 考虑到用户可能不熟悉术语,我需要涵盖几种可能性。首先检查样条组件的相关方法,比如在指定位置插入点,这可能实现曲线的分割。其次,动画曲线编辑器中,断开关键帧的切线手柄也可以视为打断曲线。此外,蓝图或Python脚本操作曲线的可能性也不能排除。 参考站内引用,特别是引用[2][4]提到UE的学习曲线功能复杂性,说明用户可能需要具体的步骤指导。因此,回答应分情况讨论,并提供操作步骤代码示例,如使用Spline Component的AddSplinePointAtIndex函数,或修改Curve的键值。 最后,生成相关问题,帮助用户深入理解,例如询问样条组件的其他操作或动画曲线的优化方法。</think>在Unreal Engine中,“打断曲线”通常涉及对样条(Spline)或动画曲线的操作。以下是两种常见场景的解决方案: ### 一、样条组件(Spline Component)打断曲线 1. **插入样条点实现分割** - 在目标位置插入新点,将原曲线分为两段: ```cpp // C++示例 USplineComponent* Spline = ...; // 获取样条组件 int32 TargetIndex = 1; // 目标插入位置 FVector NewPointLocation = Spline->GetLocationAtSplinePoint(TargetIndex, ESplineCoordinateSpace::World); Spline->AddSplinePointAtIndex(NewPointLocation, TargetIndex + 1, ESplineCoordinateSpace::World, true); ``` - 通过调整新点的切线(`SetTangentsAtSplinePoint`)控制曲线形状[^2][^4]。 2. **蓝图操作** - 使用`Add Spline Point at Index`节点动态插入点 - 通过`Get Location at Distance Along Spline`计算分割位置 ### 二、动画曲线编辑器中打断曲线 1. **关键帧切线操作** - 右键点击关键帧 → 选择`Break`断开切线手柄 - 独立调整入/出切线方向,实现曲线形状的突变 2. **曲线资产编程修改** ```cpp // 修改UCurveFloat UCurveFloat* Curve = ...; FKeyHandle KeyHandle = Curve->FloatCurve.AddKey(0.5f, 0.8f); Curve->FloatCurve.SetKeyInterpMode(KeyHandle, RCIM_Cubic); Curve->FloatCurve.SetKeyTangentMode(KeyHandle, RCTM_Break); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

他人是一面镜子,保持谦虚的态度

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值