本篇博文的目的是:对RTKLIB
中LAMBDA
固定整周模糊度的算法实现做一个尽量详尽的总结。由于笔者水平有限,不当之处还望不吝赐教。
LAMBDA 全称 Least-square AMBiguity Decorrelation Adjustment,最小二乘降相关平差。主要分为以下两步:(1)为降低模糊度参数之间相关性而进行的多维整数变换;(2)在转换后的空间内进行模糊度搜索,然后再将结果转换回模糊度空间中,进而求得模糊度整数解。详细的原理可以参看[2],本文主要介绍 RTKLIB 中有关 LAMBDA 搜索的实现,并附以一个示例进行验证。
RTKLIB 中的 LAMBDA 实现是在lambda.c
文件中的,里面主要的函数有
下面是几个主要的函数传参
extern int lambda(int n, int m, const double *a, const double *Q, double *F,double *s)
n: @param[in]
待固定的模糊度个数m: @param[in]
待搜索的候选模糊度向量组个数a: @param[in]
实数模糊度向量n*1
Q: @param[in]
方差-协方差矩阵n*n
F: @param[out]
候选模糊度组n*m
s: @param[out]
整数模糊度向量与实数模糊向量的距离(二次方残差)1*m
RTKLIB 中的 lambda
函数内容如下:
extern int lambda(int n, int m, const double *a, const double *Q, double *F,
double *s)
{
int info;
double *L,*D,*Z,*z,*E;
if (n<=0||m<=0) return -1;
L=zeros(n,n); D=mat(n,1); Z=eye(n); z=mat(n,1); E=mat(n,m);
/* LD factorization */
if (!(info=LD(n,Q,L,D))) {
/* lambda reduction */
reduction(n,L,D,Z);
matmul("TN",n,1,n,1.0,Z,a,0.0,z); /* z=Z'*a */
/* mlambda search */
if (!(info=search(n,m,L,D,z,E,s))) {
// F 搜出来的备选模糊度
info=solve("T",Z,E,n,m,F); /* F=Z'\E */
}
}
free(L); free(D); free(Z); free(z); free(E);
return info;
}
各个步骤很清晰的呈现在代码中,牛的。
static int LD(int n, const double *Q, double *L, double *D)
@note
Q = U D U T Q=UDU^T Q=UDUTn: @param[in]
待固定的模糊度个数Q: @param[in]
方差-协方差矩阵n*n
L: @param[out]
对Q
分解得到的上三角矩阵n*n
,实际上用U
表示更合适,因为是上三角阵D: @param[out]
对Q
分解得到的对角矩阵,只保留了对角线元素1*n
static void reduction(int n, double *L, double *D, double *Z)
n: @param[in]
待固定的模糊度个数L: @param[in/out]
对Q
分解得到的上三角矩阵n*n
,传出前后会发生变化D: @param[in/out]
对Q
分解得到的对角矩阵,只保留了对角线元素1*n
,传出前后会发生变化Z: @param[out]
降相关矩阵Z
阵,n*n
,Z
的所有元素都是整数,并且其行列式为1@note
:reduction
函数中调用了gauss
和perm
,对它们的理解还需进一步加强。static int search(int n, int m, const double *L, const double *D, const double *zs, double *zn, double *s)
n: @param[in]
待固定的模糊度个数m: @param[in]
待搜索的候选模糊度向量组个数L: @param[in]
对Q
分解得到的上三角矩阵n*n
,并且经过reduction
处理D: @param[in]
对Q
分解得到的对角矩阵,只保留了对角线元素1*n
,并且经过reduction
处理,目前存在如下关系: Z Q a ^ Z T = L D L T ZQ_{\hat a}Z^T=LDL^T ZQa^ZT=LDLTzs: @param[in]
降相关后的浮点模糊度 z ^ \hat z z^ n*1
zn: @param[out]
搜索到的候选模糊度向量组 n*m
s: @param[out]
各候选模糊度向量到浮点模糊度向量的距离 m*1
笔者将RTKLIB
有关LAMBDA
搜索的程序移植到C++程序中,写了如下的测试代码:
void t_gtest::RLAMBDA_test() {
t_glambda2* lambda = new t_glambda2();
int n = 3, m = 7, iN = n, iMaxCan = m;
double a[3] = { 5.45,3.10,2.97 };
double Q[9] = { 6.290,5.978,0.544, 5.978,6.292,2.340, 0.544,2.340,6.288 };
int piA[3] = { 0 };
/*for (int i = 0; i < iN; i++) {
piA[i] = round(pdA[i]);
pdA[i] -= piA[i];
}*/
cout << "原始方差-协方差矩阵:" << endl;
printArr(Q, iN, iN);
cout << "浮点模糊度:" << endl;
printArr(a, 1, iN);
cout << "-----------------------------------" << endl;
double s[8] = { 0 };
double* F = new double[iN * iMaxCan]{ 0 };
int info;
double* L, * D, * Z, * z, * E;
L = lambda->zeros(n, n); D = lambda->mat(n, 1); Z = lambda->eye(n);
z = lambda->mat(n, 1); E = lambda->mat(n, m);
/* LD factorization */
if (!(info = lambda->LD(n, Q, L, D))) {
cout << "-----------------------------------" << endl;
cout << "LD 之后 L 阵:" << endl;
printArr(L, iN, iN);
cout << "LD 之后 D 阵:" << endl;
printArr(D, 1, iN);
/* lambda reduction */
lambda->reduction(n, L, D, Z);
cout << "-----------------------------------" << endl;
cout << "reduction 之后 L 阵:" << endl;
printArr(L, iN, iN);
cout << "reduction 之后 D 阵:" << endl;
printArr(D, 1, iN);
cout << "reduction 之后 Z 阵:" << endl;
printArr(Z, iN, iN);
lambda->matmul("TN", n, 1, n, 1.0, Z, a, 0.0, z); /* z=Z'*a */
cout << "-----------------------------------" << endl;
cout << "matmul 之后 z 阵:" << endl;
printArr(z, 1, iN);
cout << "matmul 之后 a 阵:" << endl;
printArr(a, 1, iN);
cout << "matmul 之后 Z 阵:" << endl;
printArr(Z, iN, iN);
/* mlambda search */
if (!(info = lambda->search(n, m, L, D, z, E, s))) {
cout << "-----------------------------------" << endl;
cout << "search 之后 E 阵:" << endl;
printArr(E, m, n);
cout << "search 之后 s 阵:" << endl;
printArr(s, 1, m);
cout << "search 之后 z 阵:" << endl;
printArr(z, 1, iN);
info = lambda->solve("T", Z, E, n, m, F); /* F=Z'\E */
cout << "-----------------------------------" << endl;
cout << "solve 之后 F 阵:" << endl;
printArr(F, m, n);
cout << "solve 之后 E 阵:" << endl;
printArr(E, m, n);
cout << "solve 之后 Z 阵:" << endl;
printArr(Z, n, n);
}
}
free(L); free(D); free(Z); free(z); free(E); free(F);
if (lambda != NULL)
{
delete lambda;
lambda = NULL;
}
}
程序运行之后有如下输出:
原始方差-协方差矩阵:
6.29 5.978 0.544
5.978 6.292 2.34
0.544 2.34 6.288
浮点模糊度:
5.45 3.1 2.97
-----------------------------------
-----------------------------------
LD 之后 L 阵:
1 1.06537 0.086514
0 1 0.372137
0 0 1
LD 之后 D 阵:
0.0898576 5.4212 6.288
-----------------------------------
reduction 之后 L 阵:
1 0.267668 0.367412
0 1 0.13099
0 0 1
reduction 之后 D 阵:
4.31016 1.13526 0.626
reduction 之后 Z 阵:
-2 3 -1
3 -3 1
1 -1 0
-----------------------------------
matmul 之后 z 阵:
-4.57 10.02 2.35
matmul 之后 a 阵:
5.45 3.1 2.97
matmul 之后 Z 阵:
-2 3 -1
3 -3 1
1 -1 0
-----------------------------------
search 之后 E 阵:
-5 10 2
-4 10 2
-6 10 2
-4 10 3
-5 10 3
-3 10 2
-5 9 2
search 之后 s 阵:
0.218331 0.307273 0.59341 0.714614 0.77989 0.860234 1.03198
search 之后 z 阵:
-4.57 10.02 2.35
-----------------------------------
solve 之后 F 阵:
5 3 4
6 4 4
4 2 4
6 3 1
5 2 1
7 5 4
4 2 3
solve 之后 E 阵:
-5 10 2
-4 10 2
-6 10 2
-4 10 3
-5 10 3
-3 10 2
-5 9 2
solve 之后 Z 阵:
-2 3 -1
3 -3 1
1 -1 0
结合程序输出,对结果用 Matlab 进行了验证
验证代码如下:
%% -------- RTKLIB 检验 --------
clc;clear
a_hat=[5.45,3.10,2.97]';
Q=[6.290 5.978 0.544;5.978 6.292 2.340;0.544 2.340 6.288];
% 原始模糊度方差与相关系数,要看的话记得打断点,后面会覆盖掉
a_11=sqrt(Q(1,1)); ro_12=1/sqrt(Q(1,1)*Q(2,2)/(Q(1,2)*Q(1,2)));
a_22=sqrt(Q(2,2)); ro_13=1/sqrt(Q(1,1)*Q(3,3)/(Q(1,3)*Q(1,3)));
a_33=sqrt(Q(3,3)); ro_23=1/sqrt(Q(2,2)*Q(3,3)/(Q(2,3)*Q(2,3)));
Z2=[-2 3 -1;3 -3 1;1 -1 0]; det(Z2); % 行列式为 1
U2=[1 1.06537 0.086514; 0 1 0.372137;0 0 1]; % 上三角
D2=diag([0.0898576 5.4212 6.288]);
Q-U2*D2*U2' % UDUT 分解正确性检验
z_hat=Z2*a_hat; % 变换之后的浮点模糊度
Q_z=Z2*Q*Z2'; % 变换之后的协方差矩阵
% Z变换后的模糊度方差与相关系数
a_11=sqrt(Q_z(1,1)); ro_12=1/sqrt(Q_z(1,1)*Q_z(2,2)/(Q_z(1,2)*Q_z(1,2)));
a_22=sqrt(Q_z(2,2)); ro_13=1/sqrt(Q_z(1,1)*Q_z(3,3)/(Q_z(1,3)*Q_z(1,3)));
a_33=sqrt(Q_z(3,3)); ro_23=1/sqrt(Q_z(2,2)*Q_z(3,3)/(Q_z(2,3)*Q_z(2,3)));
% RTKLIB reduction 函数之后 L 和 D 变为
L_z=[1 0.267668 0.367412;0 1 0.13099;0 0 1 ];
D_z=diag([4.31016 1.13526 0.626]);
Q_z-L_z*D_z*L_z' % Z变换之后正确性检验
% 求整数解和浮点解之间的距离
a_bar=[5 3 4]'; % 最后得到整数解
z_bar=[-5 10 2]';
(z_bar-z_hat)'*inv(Q_z)*(z_bar-z_hat) % RTKLIB 吐出的是它
(a_bar-a_hat)'*inv(Q_z)*(a_bar-a_hat)
比较有意思的一点是变换前后搜索空间与各模糊度相关系数的变化,笔者觉得这才是 LAMBDA 的灵魂和精髓。
下面是用 Matlab 绘制的三维搜索空间的变化(注意坐标轴刻度和椭球形状)