成都分销网站建设,内蒙古建设厅网站删除,网站打开速度慢,iis8 wordpress前言#xff1a;
本文是基于IGNAV的PPP/INS紧组合学习#xff0c;在此之前需要具备GNSS/INS松组合知识#xff0c;武汉大学的i2nav实验室的KF-GINS项目可以作为学习模板。可以参考这篇优秀博文#xff0c;链接#xff1a;KF-GINS源码阅读_李郑骁学导航的博客-CSDN博客 IG…前言
本文是基于IGNAV的PPP/INS紧组合学习在此之前需要具备GNSS/INS松组合知识武汉大学的i2nav实验室的KF-GINS项目可以作为学习模板。可以参考这篇优秀博文链接KF-GINS源码阅读_李郑骁学导航的博客-CSDN博客 IGNAV是基于RTKLIB进行二次开发的因此熟悉RTKLIB源代码的学习起来会容易一些。我的这篇博文也大概介绍了RTKLIB中PPP模块可做简略学习RTKLIB学习二–2、PPP代码分析-CSDN博客 在学习一个项目之前对算法的学习至关重要对算法的理解程度很大程度上决定了你对代码的理解能力因此IGNAV项目中man文件夹内的theory.pdf。算法文件中6.3.2-GNSS 伪距与载波观测信息辅助INS的过程推到务必花时间理解。我的这篇文章总结了i2nav团队关于PPP/INS紧组合学习知识也可作为算法学习参考PPP/INS紧组合算法-CSDN博客 本人学习能力有限不足的地方请批评指正。
一、IGNAV的PPP/INS流程
下图是IGNAV中PPP/INS紧组合流程。其中rtksvrthread是定位计算线程tcigpos是紧组合入口函数也是本文主要学习对象。对文件读取输出的内容不做介绍! updatetimediff() /* ins tightly coupled position mode */if (svr-rtk.opt.modePMODE_INS_TGNSS) {if (syn-nisyn-nr) {//IMU和流动站时间对齐索引均不为0syn-dt[0]time2gpst(syn-time[2],NULL)-//IMU时间-流动站观测值时间time2gpst(syn-time[0],NULL);}if (syn-nisyn-nb) {//基站模式syn-dt[1]time2gpst(syn-time[2],NULL)-time2gpst(syn-time[1],NULL);}if (svr-rtk.opt.insopt.pose_aid||svr-rtk.opt.insopt.align_dualants) {//双天线模式if (syn-npsyn-ni) {syn-dt[2]time2gpst(syn-time[5],NULL)-time2gpst(syn-time[2],NULL);}}}在PPP/INS紧组合中主要更新**dt[0]**当前历元的IMU时间-流动站时间
timealign()
此处进入紧组合时间对齐
if (svr-rtk.opt.modePMODE_INS_TGNSS) return imuobsalign(svr);imuobsalign() int i,j,k,n; double sow1,sow2,sow3;obs_t *p1NULL,*p2NULL;syn_t *psynsvr-syn;/* observation and imu data time alignment */npsyn-of[2]?MAXIMUBUF:psyn-ni;//n为IMU观测历元数p1svr-obs[0];//流动站观测值p2svr-obs[1];//基站观测值在navlib.h中查看syn_t结构体(存储了各类数据的索引)
typedef struct { /* time synchronization index in buffer */int ni,nip,imu; /* current/precious number and sync index of imu measurement data */int nr,nrp,rover; /* current/precious number and sync index of observation data */int ns,nsp,pvt; /* current/precious number and sync index of pvt solution data */int nb,nbp,base; /* current/precious number and sync index of base observation data */int nm,nmp,img; /* current/precious number and sync index of image raw data */int np,npp,pose; /* current/precious number and sync index of pose measurement */int of[6]; /* overflow flag (rover,base,imu,pvt,image,pose) */unsigned int tali[6];/* time alignment for data (rover-base,pvt-imu,rover-base-imu,imu-image,pose-imu) */unsigned int ws; /* search window size */double dt[6]; /* time difference between input streams */gtime_t time[6]; /* current time of rover,base,imu,pvt,image and pose measurement */
} syn_t;在imuobsalign()中时间对齐只能匹配一个IMU和GNSS观测值且对齐方式与KF_GINS的区别还是挺大的。
/* observation and imu data time alignment */npsyn-of[2]?MAXIMUBUF:psyn-ni;//n为IMU观测历元数p1svr-obs[0];//流动站观测值窗口p2svr-obs[1];//基站观测值窗口for (i0;insvr-syn.tali[2]!2;i) { /* start time alignment 从窗口内第一个IMU观测值开始*/sow1time2gpst(svr-imu[i].time,NULL);//将imu时间转换为周内秒/* match rover observation匹配流动站观测值 */for (j0;j(psyn-of[0]?MAXOBSBUF:psyn-nr);j) {//循环遍历流动站每一个历元观测值sow2time2gpst(p1[j].data[0].time,NULL);//将流动站观测值时间转换为周内秒if (p1[j].n) {//流动站观测值存在if (fabs(sow1-sow2)DTTOL) continue;//流动站观测值时间不在IMU时间阈值0.0025s内}psyn-imu i;//记录第i个IMU匹配到相应的流动站观测值psyn-rover j;//记录第j个流动站观测值匹配到相应的IMU观测值psyn-tali[2]1;//完成标识break;}/* match base observation 基站同上*/if (psyn-tali[2]1) {for (k0;k(psyn-of[1]?MAXOBSBUF:psyn-nb);k) {sow3time2gpst(p2[k].data[0].time,NULL);if (p2[k].n) {if (fabs(sow2-sow3)DTTOLM) continue;}else continue;psyn-base k;psyn-tali[2]2;break;}}if (psyn-tali[2]2) {//匹配完一个GNSS观测值就退出tracet(3,imu and rover/base align ok\n);return 1;}else psyn-tali[2]0; /* fail */inputobstc() if ((obsd.ninputobstc(svr,imus.data[i].time,obsd.data))) {jINSUPD_MEAS;}**rtksvr_t *srv:**RTK server type囊括所有原始数据中间数据结果数据参数设置等
**imus.data[i].time:**imu数据窗口第i个数据的时间
**obsd.data:**GNSS观测值窗口内某个历元的观测数据为空 const obs_t *robssvr-obs[0],*bobssvr-obs[1];//流动站与基站观测数据窗口/* match rover observation data 匹配流动站观测值数据*/for (i0,dt0.0,nNS(svr-syn.rover,svr-syn.nr,MAXOBSBUF);in1svr-syn.nr;i) {jsvr-syn.roveri;//timealign()匹配的观测值所在历元索引iif (jMAXOBSBUF) jj%MAXOBSBUF;//超出历元窗口时余上窗口大小if (dtfabs(dt)fabs(timediff(time,robs[j].data[0].time))) {break;//第j个历元的时间与目标IMU测量时间大于大于指定阈值}if (fabs((dttimediff(time,robs[j].data[0].time)))DTTOL//当前观测值历元时间与指定IMU时间小于阈值时robs[j].n!0) {//该GNSS历元内观测值数不为0for (k0,m0;krobs[j].n;k) obs[m]robs[j].data[k];//将时间对齐的数据输入到obs[]svr-syn.roverj; trobs[0].time;//记录流动站观测值历元索引和历元时间flag1; break;//流动站配置完毕}}inputimu()
/* input imu measurement data输入IMU数据并确保正确的时间对齐。-----------*/
static int inputimu(rtksvr_t *svr,imud_t *data)
{int i,j,k,n0;tracet(3,inputimu:\n);/* check time alignment of input streams */if (!svr-syn.tali[1]!svr-syn.tali[2]!svr-syn.tali[3]) {trace(2,check time alignment fail\n);return 0;}for (i0,k0,nNS(svr-syn.imu,svr-syn.ni,MAXIMUBUF),jsvr-syn.imu;in1;i) {//j为IMU时间对齐索引jsvr-syn.imui;//从对齐索引的数据开始if (jMAXIMUBUF) jj%MAXIMUBUF;//超出MAXIMUBUF时j取模循环if (kMAXIMU) data[k]svr-imu[j]; else break;//提取数据}svr-syn.imu(j1)%MAXIMUBUF;//重置时间对齐索引svr-syn.imureturn k;//返回k的值表示处理的IMU数据历元数。
}其中
n((((svr-syn.ni)-1)%(1000)-(svr-syn.imu))0?(((svr-syn.ni)-1)%(1000)-(svr-syn.imu)(1000)):(((svr-syn.ni)-1)%(1000)-(svr-syn.imu)))计算了IMU当前历元编号和IMU同步索引之间的历元数。如果结果为负数它会加上1000以确保得到正确的差值。
insinirtobs() double vr[3]{0}; insstate_t *inssvr-rtk.ins;// 初始化速度为零/* global variables for rtk positioning */static int first1,i;static prcopt_t poptsvr-rtk.opt;static rtk_t rtk{0};// 初始化 RTK 结构体static sol_t sols[MAXSOL]{0};// 保存位置解的数组trace(3,insinirtobs: n%d\n,n);svr-rtk.ins.statINSS_INIT;if (n0) {trace(2,no observation data to initial\n); return 0;}/* initial gps position options */if (first) {//如果是首次调用初始化RTK定位选项initrtkpos(rtk,popt); first0;}rtkpos(rtk,obs,n,svr-nav);// GNSS计算定位结果/* save position solution to buffer将最新的位置解保存到缓冲区 */for (i0;iMAXSOL-1;i) sols[i]sols[i1]; sols[i]rtk.sol;for (i0;iMAXSOL;i) {//检查解的状态确保解是有效的if (sols[i].statpopt.insopt.iisu||sols[i].statSOLQ_NONE) {trace(2,check solution status fail\n);return 0;}}//检查解的时间差异确保时间差异在 MAXDIFF10s 内for (i0;iMAXSOL-1;i) {if (timediff(sols[i1].time,sols[i].time)MAXDIFF) {return 0;}}/* compute velocity from solutions 从解中计算速度*/matcpy(vr,sols[MAXSOL-1].rr3,1,3);if (norm(vr,3)0.0) {sol2vel(solsMAXSOL-1,solsMAXSOL-2,vr);}//检查陀螺仪测量值和速度的范数是否在合理的范围内if (norm(imu-gyro,3)MAXGYRO||norm(vr,3)MINVEL) {return 0;}/* initialize ins states初始化 INS 状态 */initinsrt(svr);if (!ant2inins(sols[MAXSOL-1].time,sols[MAXSOL-1].rr,vr,popt.insopt,NULL,ins,NULL)) {//将位置解和速度传递给INS进行初始化return 0;}ins-timesols[MAXSOL-1].time;/* update ins state in n-frame n系(导航系下)更新INS状态 */update_ins_state_n(ins);
二、tcigpos() trace(3,tcigpos: update%d,time%s\n,upd,time_str(imu-time,4));#if CHKNUMERIC/* check numeric of estimate state检查状态估计数字 */for (i0;i3;i) {if (isnan(ins-re[i])||isnan(ins-ve[i])||isnan(ins-ae[i])||isinf(ins-re[i])||isinf(ins-ve[i])||isinf(ins-ae[i])) {fprintf(stderr,check numeric error: nan or inf\n);return 0;}}
#endifins-statINSS_NONE; /* start ins mechanization开始机械编排 */if (
#if 0/* update ins states based on llh position mechanization */!updateinsn(insopt,ins,imu);
#else/* update ins states in e-frame */!updateins(insopt,ins,imu)) {
#endiftrace(2,ins mechanization update fail\n);return 0;}Pzeros(nx,nx);/* propagate ins states 传播INS状态到GNSS历元时刻*/propinss(ins,insopt,ins-dt,ins-x,ins-P);/* check variance of estimated position检查位置方差 */chkpcov(nx,insopt,ins-P);/* updates ins states using imu or observation data开始更新INS状态 */if (updINSUPD_TIME) {ins-statINSS_TIME;info1;}else {for (i0;i6;i) rtk-sol.pqr[i]rtk-sol.qr[i];rtk-sol.pstatrtk-sol.stat;ins-gstatSOLQ_NONE;ins-ns0;
#if REBOOT/* reboot tightly coupled if need 重启紧组合*/if ((flagrebootc(ins,opt,obs,n,imu,nav))) {if (flag1) {trace(2,ins tightly coupled still reboot\n);info0; goto EXIT;}trace(3,ins tightly coupled reboot ok\n);ins-statINSS_REBOOT; info1;goto EXIT;}
#endif/* updates by measurement data通过测量数据更新 */if (obsimun) {/* count rover/base station observations 记录流动站/基站观测数*/for (nu0;nu nobs[nu ].rcv1;nu) ;for (nr0;nunrnobs[nunr].rcv2;nr) ;dttimediff(obs[0].time,ins-time);/* check synchronization 检查同步*/if (fabs(dt)3.0) {trace(2,observation and imu sync error\n);info0;}/* tightly coupled进入紧组合 */if (info) {infortkpos(rtk,obs,nunr,nav);}}else info0;/* update coupled solution status 更新组合状态解/反馈*/if (info) {ins-ptctins-time;ins-statins-statINSS_REBOOT?INSS_REBOOT:INSS_TCUD;trace(3,tightly couple ok\n);/* lack satellites but tightly-coupled run 缺少卫星的*/if (ins-ns4) {ins-statINSS_LACK;}/* save precious epoch gps measurement 保存GPS精密?历元测量值*/savegmeas(ins,rtk-sol,NULL);
#if 1/* recheck attitude检查姿态 */rechkatt(ins,imu);
#endif/* ins state in n-frame 导航坐标系下的ins状态*/update_ins_state_n(ins);}else {trace(2,tightly coupled fail\n);info0;}}
EXIT:free(P); return info;其中机械编排updateins()状态传播propinss()紧组合rtkpos(),函数是重要学习内容。
三、机械编排updateins()
通过结合陀螺仪和加速度计的测量值更新姿态、速度和位置信息 /* save precious epoch ins states保存上一个历元的 INS 状态 */savepins(ins,data);//当前IMU测量时间 data-time与上一个INS状态的时间ins-time之间的时间差if ((dttimediff(data-time,ins-time))MAXDT||fabs(dt)1E-6) {/* update time information */ins-dttimediff(data-time,ins-time);//时间间隔ins-ptimeins-time;//上一个历元解算时间ins-time data-time;//最新IMU测量时间trace(2,time difference too large: %.0fs\n,dt);return 0;}1、IMU测量值校正 for (i0;i3;i) {ins-omgb0[i]data-gyro[i];ins-fb0 [i]data-accl[i];if (insopt-exinserr) {//ins测量值校正。ins_errmodel(data-accl,data-gyro,ins-fb,ins-omgb,ins);}else {//否则直接更新陀螺仪和加速度计的测量值。ins-omgb[i]data-gyro[i]-ins-bg[i];ins-fb [i]data-accl[i]-ins-ba[i];}}用ins_errmodel()函数对IMU测量值角速率比力进行改正否则直接更新陀螺仪和加速度计的测量值。
2、更新姿态
首先用**rotscull_corr()**函数进行旋转和划桨运动校正
**rvec2quat(domgb,dqb)*旋转向量domgb[]-四元数dqb[]
**quat2dcmx(dqe,dCe)*四元数dqe[]-转换矩阵dCe[]
dcm2quatx(ins-Cbe,qk_1):
**quatmulx(qk_1,dqb,qtmp)*四元数乘法
**normquat(qk)*规范化四元数
**quat2dcmx(qk,ins-Cbe)*四元数qk-转换矩阵ins-Cbe
3、更新速度
**gravity(ins-re,ge)**根据地心地固坐标re[]和重力模型求这点的重力ge[] for (i0;i3;i) {ins-ve[i]dvfk[i](ge[i]-2.0*wv[i])*dt;//更新速度}4、更新位置 /* update position */matcpy(vek_1,ins-ve,1,3);for (i0;i3;i) {ins-re[i]0.5*(vek_1[i]ins-ve[i])*dt;}理论应该是位置增量前一历元速度当前历元速度*dt *0.5建模成匀加速运动但是在此处vek_1[]获取的值还是当前的历元速度实际上建模成了匀速运动在短时间内效果基本一样。
**updinsn(ins)**更新INS在n下系的坐标之前的更新都是在e系下的
四、propinss()
进行惯导递推(将INS状态和协方差递推到当前IMU测量时刻)。
updstst()
此函数更新状态和协方差为后面的扩展卡尔曼滤波做基础 /* determine approximate system noise covariance matrix确定近似系统噪声协方差矩阵 */opt-exprn?getprn(ins,opt,dt,Q):getQ(opt,dt,Q);/* determine transition matrix确定转移矩阵* using last epoch ins states (first-order approx)使用最后历元ins状态一阶近似值*/opt-exphi?precPhi(opt,dt,ins-Cbe,ins-re,ins-omgb,ins-fb,phi):getPhi1(opt,dt,ins-Cbe,ins-re,ins-omgb,ins-fb,phi);#if UPD_IN_EULERgetphi_euler(opt,dt,ins-Cbe,ins-re,ins-omgb,ins-fb,phi);
#endif/* propagate state estimation error covariance 传播状态估计误差协方差*/if (fabs(dt)MAXUPDTIMEINT) {//如果时间步长大于等于MAXUPDTIMEINTgetP0(opt,P); //则直接调用函数getP0来获取初始状态估计误差协方差矩阵}else {//通过转移矩阵和协方差矩阵来预测下一个时间步的状态估计误差协方差矩阵propP(opt,Q,phi,P0,P);}/* propagate state estimates noting that* all states are zero due to close-loop correction */if (x) propx(opt,x0,x);//预测状态估计值/* predict info. */if (ins-P0) matcpy(ins-P0,P ,ins-nx,ins-nx);if (ins-F ) matcpy(ins-F ,phi,ins-nx,ins-nx);之后在chkpcov()检查INS状态协方差矩阵
int i; double var0.0;for (ixiP(opt);ixiP(opt)3;i) varSQRT(P[ii*nx]);if ((var/3)MAXVAR) if (P) getP0(opt,P);如果P矩阵位置参数对应的对角阵阵上的均值大于阈值MAXVAR就初始化P
五、rtkpos()、pppos() prcopt_t *optrtk-opt;insopt_t *insoptopt-insopt;sol_t solb{{0}};gtime_t time;static obsd_t obsd[MAXOBS];int fi0,fj1,fk2;int i,j,nu,nr,stat0,tcs0,tcp0;char msg[128];trace(3,rtkpos : time%s n%d\n,time_str(obs[0].time,3),n);trace(4,obs\n); traceobs(4,obs,n);trace(5,nav\n); tracenav(5,nav);/* check tc-mode */tcsopt-modePMODE_INS_TGNSSinsopt-tcINSTC_SINGLE;//spp_ins紧组合tcpopt-modePMODE_INS_TGNSSinsopt-tcINSTC_PPK;//ppp_ins紧组合if (n0) return 0;/* set base staion position 基站位置*/if (opt-refposPOSOPT_RINEXopt-mode!PMODE_SINGLEopt-mode!PMODE_MOVEB) {for (i0;i6;i) rtk-rb[i]i3?opt-rb[i]:0.0;}/* count rover/base station observations */for (nu0;nu nobs[nu ].rcv1;nu) ;//流动站观测值计数for (nr0;nunrnobs[nunr].rcv2;nr) ;//基站观测值计数/* for rover and base observation data */for (i0;inunropt-adjobs;i) {memcpy(obsd[i],obs[i],sizeof(obsd_t));if (adjsind(opt,obs[i],fi,fj,fk)) {//调整观测数据trace(4,adjust observation data signal index ok\n);}/* here just adjust three frequency */for (j0;j3;j) {obsd[i].LLI[j]obs[i].LLI[j0?fi:j1?fj:fk];obsd[i].SNR[j]obs[i].SNR[j0?fi:j1?fj:fk];obsd[i].P[j]obs[i].P[j0?fi:j1?fj:fk];obsd[i].L[j]obs[i].L[j0?fi:j1?fj:fk];obsd[i].D[j]obs[i].D[j0?fi:j1?fj:fk];}/* index of frequency */fi0; fj1; fk2;}if (opt-adjobs) {trace(4,adjust obs\n); traceobs(4,obsd,n);}/* previous epoch */timertk-sol.time;/* rover position by single point positioning */if (!pntpos(opt-adjobs?obsd:obs,nu,nav,rtk-opt,rtk-sol,tcs?rtk-ins:NULL,NULL,rtk-ssat,msg)) {errmsg(rtk,point pos error (%s)\n,msg);if (!rtk-opt.dynamics) {outsolstat(rtk);return 0;}}if (time.time!0) rtk-tttimediff(rtk-sol.time,time);if (fabs(rtk-tt)DTTOLopt-modePMODE_FIXED) return stat;/* single point positioning spp或spp_ins紧组合*/if (opt-modePMODE_SINGLE||tcs) {outsolstat(rtk);return 1;}/* suppress output of single solution抑制单个解的输出 */if (!opt-outsingle) {rtk-sol.statSOLQ_NONE;}/* precise point positioning ppp或ppp_ins紧组合*/if ((opt-modePMODE_PPP_KINEMAopt-modePMODE_INS_UPDATE)||tcp) {pppos(rtk,opt-adjobs?obsd:obs,nu,nav);outsolstat(rtk);return 1;}//检查该历元流动站观测时间和基准站观测时间是否对应/* check number of data of base station and age of differential */if (nr0) {errmsg(rtk,no base station observation data for rtk\n);outsolstat(rtk);return 1;}//动基线与其他差分定位方式动基线的基站坐标需要随时间同步变化所以需要计算出变化速率if (opt-modePMODE_MOVEB) { /* moving baseline *///解释了为什么第二步除了单点定位动基线也不参与基站解算动基线在这里单独解算/* estimate position/velocity of base station */if (!pntpos(opt-adjobs?obsd:obsnu,nr,nav,rtk-opt,solb,NULL,NULL,NULL,msg)) {errmsg(rtk,base station position error (%s)\n,msg);return 0;}rtk-sol.age(float)timediff(rtk-sol.time,solb.time);if (fabs(rtk-sol.age)TTOL_MOVEB) {errmsg(rtk,time sync error for moving-base (age%.1f)\n,rtk-sol.age);return 0;}for (i0;i6;i) rtk-rb[i]solb.rr[i];/* time-synchronized position of base station */for (i0;i3;i) {rtk-rb[i]rtk-rb[i3]*rtk-sol.age;}}else {rtk-sol.age(float)timediff(obs[0].time,obs[nu].time);if (fabs(rtk-sol.age)opt-maxtdiff) {errmsg(rtk,age of differential error (age%.1f)\n,rtk-sol.age);outsolstat(rtk);return 1;}}/* relative positioning */statrelpos(rtk,opt-adjobs?obsd:obs,nu,nr,nav);#if DEGRADETC/* degrade to dgps-tc mode if rtk-tc fail */if (stat0opt-modePMODE_INS_TGNSS) {insopt-tcINSTC_DGPS;statrelpos(rtk,opt-adjobs?obsd:obs,nu,nr,nav);insopt-tcINSTC_RTK;if (stat) goto exit;}/* degrade to single-tc mode if dgps-tc fail */if (stat0opt-modePMODE_INS_TGNSS) {statpntpos(opt-adjobs?obsd:obs,nu,nav,rtk-opt,rtk-sol,rtk-ins,NULL,NULL,msg);goto exit;}
exit:
#endifoutsolstat(rtk);return stat;需要注意到的是此处状态参数x中的位置参数是IMU坐标系下的而rr[]存储的是地心地固下的坐标 。这里也是spp/ins,rtk/ins紧组合入口函数注意区分。这里只介绍PPP/INS紧组合入口函数pppos()。pppos()代码在下面
extern void pppos(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav)
{const prcopt_t *optrtk-opt;double *rs,*dts,*var,*v,*H,*R,*azel,*xp,*Pp,dr[3]{0},std[3];double *x,*P,rr[3];char str[32];int i,j,nv,info,svh[MAXOBS],exc[MAXOBS]{0},statSOLQ_SINGLE,tc;int nx;insstate_t insp;time2str(obs[0].time,str,2);trace(3,pppos : time%s nx%d n%d\n,str,rtk-nx,n);rsmat(6,n); dtsmat(2,n); varmat(1,n); azelzeros(2,n);for (i0;iMAXSAT;i) for (j0;jopt-nf;j) rtk-ssat[i].fix[j]0;/* temporal update of ekf states */udstate_ppp(rtk,obs,n,nav);/* satellite positions and clocks */satposs(obs[0].time,obs,n,nav,rtk-opt.sateph,rs,dts,var,svh);//排除被遮住的卫星(地影)/* exclude measurements of eclipsing satellite (block IIA) */if (rtk-opt.posopt[3]) {testeclipse(obs,n,nav,rs);}/* earth tides correction地球潮汐校正 */if (opt-tidecorr) {tidedisp(gpst2utc(obs[0].time),rtk-x,opt-tidecorr1?1:7,nav-erp,opt-odisp[0],dr);}nvn*rtk-opt.nf*2MAXSAT3;xpzeros(rtk-nx,1); Ppzeros(rtk-nx,rtk-nx);vmat(nv,1); Hmat(rtk-nx,nv); Rmat(nv,nv);/* tc0: common rtk mode* tc1: tightly coupled mode* */tcopt-modePMODE_INS_TGNSS?1:0;xtc?rtk-ins.x:rtk-x;//通过INS解算的状态先验值Ptc?rtk-ins.P:rtk-P;//通过INS解算的参数协方差//若为紧组合模式nx为紧组合模型状态参数nxtc?rtk-ins.nx:rtk-nx;/* backup ins states ins状态参数反馈矩阵*/if (tc) {memcpy(insp,rtk-ins,sizeof(insstate_t));}for (i0;iMAX_ITER;i) {//迭代//若为组合模式将IMU位置坐标转换到GNSS天线位置tc?insp2antp(insp,rr):matcpy(rr,xp,1,3);matcpy(xp,x,nx,1);matcpy(Pp,P,nx,nx);/* prefit residuals先验残差 */if (!(nvppp_res(0,obs,n,rs,dts,var,svh,dr,exc,nav,xp,rtk,v,H,R,azel,rr))) {trace(2,%s ppp (%d) no valid obs data\n,str,i1);break;}/* measurement update of ekf states */if ((infofilter(xp,Pp,H,v,R,nx,nv))) {trace(2,%s ppp (%d) filter error info%d\n,str,i1,info);break;}/* postfit residuals后验残差 */if (ppp_res(i1,obs,n,rs,dts,var,svh,dr,exc,nav,xp,rtk,v,H,R,azel,rr)) {matcpy(x,xp,nx,1);matcpy(P,Pp,nx,nx);statSOLQ_PPP;if (tc) {clp(insp,opt-insopt,xp);for (j0;jxnCl(opt-insopt);j) xp[j]0.0;}break;}}if (iMAX_ITER) {trace(2,%s ppp (%d) iteration overflows\n,str,i);}if (statSOLQ_PPP) {/* update ins states if solution is ok */if (tc) {upd_ins_states(rtk,insp);}tc?insp2antp(insp,rr):matcpy(rr,xp,1,3);/* todo: add ppp-ar fix ambiguity *//* ambiguity resolution in ppp */if (ppp_ar(rtk,obs,n,exc,nav,azel,xp,Pp)ppp_res(9,obs,n,rs,dts,var,svh,dr,exc,nav,xp,rtk,v,H,R,azel,rr)) {double *xa,*Pa;xatc?rtk-ins.xb:rtk-xa;Patc?rtk-ins.Pb:rtk-Pa;for (i0;i3;i) std[i]sqrt(Pp[ii*nx]);if (norm(std,3)MAX_STD_FIX) statSOLQ_FIX;if (statSOLQ_FIX) {if (tc) {clp(insp,opt-insopt,xp);for (j0;jxnCl(opt-insopt);j) xp[j]0.0;/* update ins states if solution is ok */if (tc) {upd_ins_states(rtk,insp);}}matcpy(xa,xp,nx, 1);matcpy(Pa,Pp,nx,nx);}}else {rtk-nfix0;}/* update solution status */update_stat(rtk,obs,n,stat);/* hold fixed ambiguities */if (statSOLQ_FIXtest_hold_amb(rtk)) {matcpy(x,xp,nx,1);matcpy(P,Pp,nx,nx);trace(2,%s hold ambiguity\n,str);rtk-nfix0;}}free(rs); free(dts); free(var); free(azel);free(xp); free(Pp); free(v); free(H); free(R);
}
其中udstate_ppp()、satposs()、testeclipe()、tidedisp()、pppar()、update_stat()、update_stat()并没有多大改变不作阐述。
1、先验残差ppp_res() const double *lam;prcopt_t *optrtk-opt;insopt_t *insoptopt-insopt;double y,r,cdtr,bias,C,rr[3],pos[3],e[3],dtdx[3],L[NFREQ],P[NFREQ],Lc,Pc;double var[MAXOBS*2],dtrp0.0,dion0.0,vart0.0,vari0.0,dcb;double dantr[NFREQ]{0},dants[NFREQ]{0},uddp[3],udda[3],uddl[3];double ve[MAXOBS*2*NFREQ]{0},vmax0;char str[32];int ne0,obsi[MAXOBS*2*NFREQ]{0},frqi[MAXOBS*2*NFREQ],maxobs,maxfrq,rej;int i,j,k,sat,sys,nv0,nxrtk-nx,stat1,tc;/* tc0: common rtk mode* tc1: tightly coupled mode* */tcopt-modePMODE_INS_TGNSS?1:0;time2str(obs[0].time,str,2);//将第一个观测历元的时间转换为字符串存储在str中。//将 rtk-ssat 中的卫星状态标记为未使用for (i0;iMAXSAT;i) for (j0;jopt-nf;j) rtk-ssat[i].vsat[j]0;for (i0;i3;i) rr[i]rpos[i]dr[i];//dr[]为地球潮汐对坐标改正量ecef2pos(rr,pos);for (i0;iniMAXOBS;i) {satobs[i].sat;lamnav-lam[sat-1];//波长if (lam[j/2]0.0||lam[0]0.0) continue;if ((rgeodist(rsi*6,rr,e))0.0||satazel(pos,e,azeli*2)opt-elmin) {exc[i]1;continue;}if (!(syssatsys(sat,NULL))||!rtk-ssat[sat-1].vs||satexclude(obs[i].sat,svh[i],opt)||exc[i]) {exc[i]1;continue;}/* tropospheric and ionospheric model */if (!model_trop(obs[i].time,pos,azeli*2,opt,x,dtdx,nav,dtrp,vart)||!model_iono(obs[i].time,pos,azeli*2,opt,sat,x,nav,dion,vari)) {continue;}/* satellite and receiver antenna model */if (opt-posopt[0]) satantpcv(rsi*6,rr,nav-pcvssat-1,dants);antmodel(opt-pcvr,opt-antdel[0],azeli*2,opt-posopt[1],dantr);/* phase windup model */if (!model_phw(rtk-sol.time,sat,nav-pcvs[sat-1].type,opt-posopt[2]?2:0,rsi*6,rr,rtk-ssat[sat-1].phw)) {continue;}/* corrected phase and code measurements */corr_meas(obsi,nav,azeli*2,rtk-opt,dantr,dants,rtk-ssat[sat-1].phw,L,P,Lc,Pc);/* stack phase and code residuals {L1,P1,L2,P2,...} */for (j0;j2*NF(opt);j) {dcbbias0.0;if (opt-ionooptIONOOPT_IFLC) {if ((yj%20?Lc:Pc)0.0) continue;}else {if ((yj%20?L[j/2]:P[j/2])0.0) continue;/* receiver DCB correction for P2 */if (j/21) dcb-nav-rbias[0][sysSYS_GLO?1:0][0];}CSQR(lam[j/2]/lam[0])*ionmapf(pos,azeli*2)*(j%20?-1.0:1.0);if (tc) {//IMU位置状态参数---对观测方程xcom对应的雅克比矩阵jacob_ud_dp(e,uddp);//e[]为卫星到接收机的向量除以几何距离H[xiP(insopt)0]uddp[0];H[xiP(insopt)1]uddp[1];H[xiP(insopt)2]uddp[2];}else {for (k0;knx;k) H[knx*nv]k3?-e[k]:0.0;}/* partial derivation by ins attitude error利用ins姿态误差进行部分推导 */if (Htc) {jacob_ud_da(e,rtk-ins.lever,rtk-ins.Cbe,udda);H[xiA(insopt)0]udda[0];H[xiA(insopt)1]udda[1];H[xiA(insopt)2]udda[2];}/* partial derivation by lever arm 杆臂偏导数*/if (Htc) {jacob_ud_la(e,rtk-ins.Cbe,uddl);H[xiLa(insopt)0]uddl[0];H[xiLa(insopt)1]uddl[1];H[xiLa(insopt)2]uddl[2];}/* receiver clock接收机钟差雅克比矩阵系数 */ksysSYS_GLO?1:(sysSYS_GAL?2:(sysSYS_CMP?3:0));/* xiRc(insopt)0: GPS receiver clock* xiRc(insopt)1: GLO receiver clock* xiRc(insopt)2: GAL receiver clock* xiRc(insopt)3: BDS receiver clock* */cdtrx[tc?xiRc(insopt)k:IC(k,opt)];H[tc?xiRc(insopt)k:IC(k,opt)nx*nv]1.0;/* todo: ppp/ins tightly coupled */if (opt-tropoptTROPOPT_EST||opt-tropoptTROPOPT_ESTG) {for (k0;k(opt-tropoptTROPOPT_ESTG?3:1);k) {H[tc?xiTr(insopt,k):IT(opt)knx*nv]dtdx[k];//对流层湿延迟雅克比矩阵系数}}if (opt-ionooptIONOOPT_EST) {//电离层估计int iitc?xiIo(insopt,sat):II(sat,opt);if (rtk-x[ii]0.0) continue;H[iinx*nv]C;}if (j/22j%21) { /* L5-receiver-dcb */dcbrtk-x[tc?xiDl(insopt):ID(opt)];H[tc?xiDl(insopt):ID(opt)nx*nv]1.0;}if (j%20) { /* phase bias 相位偏差*/int ibtc?xiBs(insopt,sat,j/2):IB(sat,j/2,opt);if ((biasx[ib])0.0) continue;H[ibnx*nv]1.0;}/* residual y是GNSS观测值*/v[nv]y-(rcdtr-CLIGHT*dts[i*2]dtrpC*diondcbbias);if (j%20) rtk-ssat[sat-1].resc[j/2]v[nv];else rtk-ssat[sat-1].resp[j/2]v[nv];/* variance */var[nv]varerr(obs[i].sat,sys,azel[1i*2],j/2,j%2,opt)vartSQR(C)*varivar_rs[i];if (sysSYS_GLOj%21) var[nv]VAR_GLO_IFB;trace(4,%s sat%2d %s%d res%9.4f sig%9.4f el%4.1f\n,str,sat,j%2?P:L,j/21,v[nv],sqrt(var[nv]),azel[1i*2]*R2D);/* reject satellite by pre-fit residuals用先验残差排除卫星 */if (!postopt-maxinno0.0fabs(v[nv])opt-maxinno) {trace(2,outlier (%d) rejected %s sat%2d %s%d res%9.4f el%4.1f\n,post,str,sat,j%2?P:L,j/21,v[nv],azel[1i*2]*R2D);exc[i]1; rtk-ssat[sat-1].rejc[j%2];continue;}/* record large post-fit residuals记录大的后验残差 */if (postfabs(v[nv])sqrt(var[nv])*THRES_REJECT) {obsi[ne]i; frqi[ne]j; ve[ne]v[nv]; ne;}if (j%20) rtk-ssat[sat-1].vsat[j/2]1;nv;}}/* reject satellite with large and max post-fit residual 排除卫星*/if (postne0) {vmaxve[0]; maxobsobsi[0]; maxfrqfrqi[0]; rej0;for (j1;jne;j) {if (fabs(vmax)fabs(ve[j])) continue;vmaxve[j]; maxobsobsi[j]; maxfrqfrqi[j]; rejj;}satobs[maxobs].sat;trace(2,outlier (%d) rejected %s sat%2d %s%d res%9.4f el%4.1f\n,post,str,sat,maxfrq%2?P:L,maxfrq/21,vmax,azel[1maxobs*2]*R2D);exc[maxobs]1; rtk-ssat[sat-1].rejc[maxfrq%2]; stat0;ve[rej]0;}/* constraint to local correction 局部校正约束*/nvconst_corr(obs,n,exc,nav,x,pos,azel,rtk,vnv,Hnv*rtk-nx,varnv);for (i0;inv;i) for (j0;jnv;j) {R[ij*nv]ij?var[i]:0.0;//观测值方差}return post?stat:nv;如果对自己的要求比较高需要了解雅克比矩阵H中矩阵块赋值情况建议对i2nav团队上传在bilibili上的组合导航最后一章PPP/INS紧组合进行详细的学习。注意在细节上此项目与视频有差异。此项目貌似并没有使用一步解法存疑。
2、测量更新扩展卡尔曼滤波
算法详情;RTKLIB学习(二)–1、PPP方程和扩展卡尔曼滤波等算法详解-CSDN博客
extern int filter(double *x, double *P, const double *H, const double *v,const double *R, int n, int m)需要注意到的是此处输入的*x为ins解算出来的先验状态参数解算出来的状态参数x中的位置参数是IMU坐标系下的。但是在KF-GINS中扩展卡尔曼滤波中的状态参数是改正量。
3、后验残差
if (ppp_res(i1,obs,n,rs,dts,var,svh,dr,exc,nav,xp,rtk,v,H,R,azel,rr))ppp_res()是通过第一个传入的参数区分先验还是后验。在后验函数中主要做了检验工作并排除故障卫星。