GNSS接收机设计:(6)基于最小二乘法的伪距单点定位(缓慢更新中)

由软件接收机的leastSquarePos.m实现。

原理解释

1.牛顿迭代法 (作用是非线性方程组线性化)

一元非线性方程

我们知道,伪距定位法列出的方程组是非线性的四元方程,所以接下来就要研究这类方程的求解方法。
首先考虑一元非线性的情况:
f ( x ) = 0 f(x)=0 f(x)=0
此方程没有求解的通法,于是考虑迭代。为了能够逼近最终的值,需要构建如下形式的迭代:
x k + 1 = φ ( x k ) x_{k+1}=\varphi(x_k) xk+1=φ(xk)
这样有了一个初始值 x k x_k xk,就能计算出 x k + 1 x_{k+1} xk+1,以此类推,一直逼近到最后的值。
这种方法的精度由迭代次数决定,太少的话结果不够精确,但也不是越多越好,需要具体考虑。此外还要注意迭代收敛的问题,如果不能收敛,说明需要重新构建关系式,或选择其它的迭代方法。
然而,因为我们的原式是非线性的,因此无法通过直接变形将 x k + 1 x_{k+1} xk+1剥离出来,所以自然想到利用泰勒级数展开,得到在某点近似的线性方程
具体操作时,由根的初始估计值 x k x_k xk,将 f ( x ) f(x) f(x) x k x_k xk处进行泰勒展开,并忽略高阶余项,得到
f ( x ) ≈ f ( x k ) + f ′ ( x k ) ⋅ ( x − x k ) f(x)\approx f(x_{k})+f^{\prime}(x_{k})\cdot(x-x_{k}) f(x)f(xk)+f(xk)(xxk)
再根据 f ( x ) = 0 f(x)=0 f(x)=0,稍加变形,得到如下的线性方程
f ( x k ) + f ′ ( x k ) ⋅ ( x − x k ) = 0 f(x_{k})+f^{\prime}(x_{k})\cdot(x-x_{k})=0 f(xk)+f(xk)(xxk)=0
将此方程的解作为 x x x的下一个值,也就是 x k + 1 x_{k+1} xk+1,故有
x k + 1 = x k − f ( x k ) f ′ ( x k ) x_{k+1}=x_{k}-\frac{f(x_{k})}{f^{\prime}(x_{k})} xk+1=xkf(xk)f(xk)
这就是我们构建出的迭代式子 x k + 1 = φ ( x k ) x_{k+1}=\varphi(x_k) xk+1=φ(xk),接下来只需要重复以上步骤,直到求解精度满足要求即可。

多元非线性方程

将上面的思路进行推广。例如,对于下式
v = f ( x , y , z , u ) v=f(x,y,z,u) v=f(x,y,z,u)
其中, u u u是系统输入, v v v是系统输出, x x x y y y z z z是待求的参数,为确定它们的值,我们对系统进行了 n n n次测试,得到多元非线性方程组
{ v 1 = f ( x , y , z , u 1 ) v 2 = f ( x , y , z , u 2 ) ⋯ v N = f ( x , y , z , u N ) \begin{cases}v_1=f(x,y,z,u_1)\\v_2=f(x,y,z,u_2)\\\cdots\\v_N=f(x,y,z,u_N)\end{cases} v1=f(x,y,z,u1)v2=f(x,y,z,u2)vN=f(x,y,z,uN)
仍沿用上述牛顿迭代的思想,并有初始值 ( x k , y k , z k ) (x_k,y_k,z_k) (xk,yk,zk),因此,可在该点处通过泰勒展开对上述方程组进行线性化。对于第 n n n个方程,有
ν n ≈ f ( x k , y k , z k , u n ) + ∂ f ( x k , y k , z k , u n ) ∂ x ( x − x k ) + ∂ f ( x k , y k , z k , u n ) ∂ y ( y − y k ) + ∂ f ( x k , y k , z k , u n ) ∂ z ( z − z k ) \nu_{n}\approx f(x_{k},y_{k},z_{k},u_{n})+\frac{\partial f(x_{k},y_{k},z_{k},u_{n})}{\partial x}(x-x_{k})+\frac{\partial f(x_{k},y_{k},z_{k},u_{n})}{\partial y}(y-y_{k})+\frac{\partial f(x_{k},y_{k},z_{k},u_{n})}{\partial z}(z-z_{k}) νnf(xk,yk,zk,un)+xf(xk,yk,zk,un)(xxk)+yf(xk,yk,zk,un)(yyk)+zf(xk,yk,zk,un)(zzk)
变形得
ν n − f ( x k , y k , z k , u n ) = ∂ f ( x k , y k , z k , u n ) ∂ x ( x − x k ) + ∂ f ( x k , y k , z k , u n ) ∂ y ( y − y k ) + ∂ f ( x k , y k , z k , u n ) ∂ z ( z − z k ) \nu_{n}- f(x_{k},y_{k},z_{k},u_{n})=\frac{\partial f(x_{k},y_{k},z_{k},u_{n})}{\partial x}(x-x_{k})+\frac{\partial f(x_{k},y_{k},z_{k},u_{n})}{\partial y}(y-y_{k})+\frac{\partial f(x_{k},y_{k},z_{k},u_{n})}{\partial z}(z-z_{k}) νnf(xk,yk,zk,un)=xf(xk,yk,zk,un)(xxk)+yf(xk,yk,zk,un)(yyk)+zf(xk,yk,zk,un)(zzk)
考虑方程组整体,就可以用矩阵形式来表达:
G ⋅ Δ x = b \boldsymbol{G}\cdot\Delta \boldsymbol{x}=\boldsymbol{b} GΔx=b

其中,
G = [ ∂ f ( x k , y k , z k , u 1 ) ∂ x ∂ f ( x k , y k , z k , u 1 ) ∂ y ∂ f ( x k , y k , z k , u 1 ) ∂ z ∂ f ( x k , y k , z k , u 2 ) ∂ x ∂ f ( x k , y k , z k , u 2 ) ∂ y ∂ f ( x k , y k , z k , u 2 ) ∂ z ⋯ ⋯ ⋯ ∂ f ( x k , y k , z k , u N ) ∂ x ∂ f ( x k , y k , z k , u N ) ∂ y ∂ f ( x k , y k , z k , u N ) ∂ z ] G=\begin{bmatrix}\frac{\partial f(x_{k},y_{k},z_{k},u_1)}{\partial x}&\frac{\partial f(x_{k},y_{k},z_{k},u_1)}{\partial y}&\frac{\partial f(x_{k},y_{k},z_{k},u_1)}{\partial z}\\\frac{\partial f(x_{k},y_{k},z_{k},u_2)}{\partial x}&\frac{\partial f(x_{k},y_{k},z_{k},u_2)}{\partial y}&\frac{\partial f(x_{k},y_{k},z_{k},u_2)}{\partial z}\\\cdots&\cdots&\cdots\\\frac{\partial f(x_{k},y_{k},z_{k},u_N)}{\partial x}&\frac{\partial f(x_{k},y_{k},z_{k},u_N)}{\partial y}&\frac{\partial f(x_{k},y_{k},z_{k},u_N)}{\partial z}\end{bmatrix} G= xf(xk,yk,zk,u1)xf(xk,yk,zk,u2)xf(xk,yk,zk,uN)yf(xk,yk,zk,u1)yf(xk,yk,zk,u2)yf(xk,yk,zk,uN)zf(xk,yk,zk,u1)zf(xk,yk,zk,u2)zf(xk,yk,zk,uN)
称为雅可比矩阵.

b = [ ν 1 − f ( x k , y k , z k , u 1 ) ν 2 − f ( x k , y k , z k , u 2 ) . . . ν N − f ( x k , y k , z k , u N ) ] \left.\boldsymbol{b}=\left[\begin{array}{c}\nu_1-f(x_{k},y_{k},z_{k},u_1)\\\nu_2-f(x_{k},y_{k},z_{k},u_2)\\...\\\\\nu_N-f(x_{k},y_{k},z_{k},u_N)\end{array}\right.\right] b= ν1f(xk,yk,zk,u1)ν2f(xk,yk,zk,u2)...νNf(xk,yk,zk,uN)

Δ x = x − x k = [ x y z ] − [ x k y k z k ] \left.\Delta x=x-x_{k}=\left[\begin{array}{c}x\\y\\z\end{array}\right.\right]-\left[\begin{array}{c}x_{k}\\y_{k}\\z_{k}\end{array}\right] Δx=xxk= xyz xkykzk
所以迭代式为
x k + 1 = x k + Δ x x_{k+1}=x_k+\Delta x xk+1=xk+Δx
也就是说,因为 x k x_k xk已知,只要再求出 Δ x \Delta x Δx,就可以得到更新后的 x k + 1 x_{k+1} xk+1.
顺便一提,除了牛顿法以外,还有其它常用的迭代方法,如梯度下降法,它需要构建的形式是
x k + 1 = x k − Δ ( x k ) x_{k+1}=x_k-\Delta(x_k) xk+1=xkΔ(xk)

2.最小二乘法 (作用是对超定线性方程组求近似解)

在得到线性方程组之后,考虑GNSS定位的实际情况。显然在绝大多数时间内可见星都大于4颗(这也是接收机最希望的工作状态)。此时伪距定位方程组是超定的,也就是有效方程的个数>未知数的个数,在线性代数的体系中是无解的,所以需要使用最小二乘法,得到方程组的近似解 Δ x \Delta{x} Δx,从而更新定位结果。
所谓最小二乘解,也就是说此时的 Δ x \Delta{x} Δx会使计算值 f ( x , y , z , u ) f(x,y,z,u) f(x,y,z,u)与实际观测的输出 v v v之间的差的平方和最小。

代码实现

因为matlab中有求解矩阵方程的运算符,因此就不需要再另外写最小二乘的代码。
调用格式为:x=A\B ; 要求矩阵A、B必须具有相同的行数。

对矩阵A可以分成如下三种情况:

  1. 如果A是标量,那么A\B就等同于A.\B
  2. 如果A是n×n方阵,B是n行矩阵,那么A\B就是方程A*X=B的解
  3. 如果A是m×n矩阵(m≠n),B是m行矩阵,那么A\B返回方程组A*X=B的最小二乘解

在下面代码中,显然符合第三种情况,所以会直接返回最小二乘解,十分方便。

function [pos, el, az, dop] = leastSquarePos(satpos, obs, settings)
%接收机位置的最小二乘解

%=== Initialization =======================================================
nmbOfIterations = 7;  %迭代次数=7

dtr     = pi/180;        %角度和弧度的转换因子
pos     = zeros(4, 1);   %初始化用户位置结果结构体,包括三维位置和钟差
X       = satpos;        %X存入卫星位置
nmbOfSatellites = size(satpos, 2);  %因为satpos每一列存的是一颗卫星的数据,所以计算列数就得到卫星数量

A       = zeros(nmbOfSatellites, 4);
omc     = zeros(nmbOfSatellites, 1);    %残差
az      = zeros(1, nmbOfSatellites);    %方位角
el      = az;   %仰角

%=== 开始迭代 ===================================
for iter = 1:nmbOfIterations

    for i = 1:nmbOfSatellites
        if iter == 1
            Rot_X = X(:, i);    %第一次迭代时,Rot_X记录卫星初始位置
            trop = 2;   %初始化对流层延迟修正
        else
            % 如果不是第一次迭代...
            rho2 = (X(1, i) - pos(1))^2 + (X(2, i) - pos(2))^2 + ...
                   (X(3, i) - pos(3))^2;   
            %rho2就是伪距的平方,pos123分别代表XYZ
            traveltime = sqrt(rho2) / settings.c ;  % 传播时间=伪距/光速

            % 调用e_r_corr,根据卫星信号传播时间和地球自转速度,对卫星位置进行修正
            Rot_X = e_r_corr(traveltime, X(:, i));

            % ---调用topocent函数,计算卫星的方位角、仰角和距离
            [az(i), el(i), dist] = topocent(pos(1:3, :), Rot_X - pos(1:3, :));
					 % 至此,四个输出结果中的el、az就求完了
					 
            if (settings.useTropCorr == 1)   %如果使用对流层延迟修正:
                
                trop = tropo(sin(el(i) * dtr), ...
                             0.0, 1013.0, 293.0, 50.0, 0.0, 0.0, 0.0);
            else
                % 否则就令=0,不修正
                trop = 0;
            end
        end 

下面就是进行矩阵方程的求解,把第一部分原理解释中的牛顿迭代法应用到GNSS定位场景,就能得到以下:
G ⋅ Δ x = b \boldsymbol{G}\cdot\Delta \boldsymbol{x}=\boldsymbol{b} GΔx=b
①对这个方程,首先算b:
b = [ ν 1 − f ( x k , y k , z k , u 1 ) ν 2 − f ( x k , y k , z k , u 2 ) . . . ν N − f ( x k , y k , z k , u N ) ] \left.\boldsymbol{b}=\left[\begin{array}{c}\nu_1-f(x_{k},y_{k},z_{k},u_1)\\\nu_2-f(x_{k},y_{k},z_{k},u_2)\\...\\\\\nu_N-f(x_{k},y_{k},z_{k},u_N)\end{array}\right.\right] b= ν1f(xk,yk,zk,u1)ν2f(xk,yk,zk,u2)...νNf(xk,yk,zk,uN)
v v v是观测到的伪距,即输入的obs; f f f是计算得到的距离,其中还包含钟差和对流层延迟修正。
故相应代码为:

omc(i) = (obs(i) - norm(Rot_X - pos(1:3), 'fro') - pos(4) - trop);
% norm( 'fro'):意思是求矩阵二范数
% pos(4)是钟差
% trop是对流层延迟修正

②然后算G,在程序里是矩阵A:
在这里插入图片描述以第一列元素为例,
在这里插入图片描述分母:观测矢量 r r r x x x方向上的分量再反向,而 r r r是由接收机指向卫星,故
r r r = 卫星的 x x x位置-用户的 x x x位置 = Rot_X(1) - pos(1)
分子:观测矢量 r r r的模,也就是伪距obs(i)
因此A的代码为:

 A(i, :) =  [ (-(Rot_X(1) - pos(1))) / obs(i) ...
                     (-(Rot_X(2) - pos(2))) / obs(i) ...
                     (-(Rot_X(3) - pos(3))) / obs(i) ...
                     1 ];
    end

③求完之后,需要判断A的情况
若A不满秩,则有无穷多解,我们对这种情况不感兴趣,所以pos直接返回0矩阵。否则可往下进行。

    if rank(A) ~= 4         %若矩阵A不满秩
        pos     = zeros(1, 4);
        return
    end

至此,G、b都已得到,因此矩阵方程已经建立,下面就是求解。

    x   = A \ omc;          %计算位置更新量x  

    pos = pos + x;          %更新位置
    
end % for iter = 1:nmbOfIterations

pos = pos';

到这里,第三个输出pos也得到了。最后一个输出是精度因子DOP。

先求出权系数阵Q:
Q = ( A T A ) − 1 Q=(A^T A)^{-1} Q=(ATA)1

原本,定位误差就是测量误差,但是在最小二乘法求解的过程中,测量误差的方差被矩阵Q放大了,成为新的定位误差。而DOP表征的正是误差的放大倍数,所以它由Q阵求得,代码如下:

%=== 计算DOP ======================================
if nargout  == 4
    %--- Initialize output ------------------------------------------------
    dop     = zeros(1, 5);
    
    %--- Calculate DOP ----------------------------------------------------
    Q       = inv(A'*A);     % Q=(A*A转置)求逆
    
    dop(1)  = sqrt(trace(Q));                       % GDOP    
    dop(2)  = sqrt(Q(1,1) + Q(2,2) + Q(3,3));       % PDOP
    dop(3)  = sqrt(Q(1,1) + Q(2,2));                % HDOP
    dop(4)  = sqrt(Q(3,3));                         % VDOP
    dop(5)  = sqrt(Q(4,4));                         % TDOP
end

其中,
GDOP:三维坐标与时间精度因子;所以所有对角线元素都要用到,也就是矩阵的迹
PDOP:位置精度因子;只与三维位置坐标有关
HDOP:水平精度因子;只与xy位置有关
VDOP:垂直精度因子;只与z有关
TDOP:时间精度因子;只与钟差有关

  • 53
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现基于北斗BDS-3的星历文件的gnss伪距单点定位程序,你需要掌握以下几个方面的知识: 1. 北斗BDS-3卫星系统的基本原理和星历数据格式; 2. gnss伪距单点定位的基本原理和算法; 3. 编程语言和开发环境。 下面是一个基于Python语言的北斗BDS-3星历文件的gnss伪距单点定位程序示例: ```python import numpy as np # 读取北斗BDS-3星历文件 def read_bds3_nav_file(file_path): with open(file_path, 'r') as f: lines = f.readlines() nav_data = {} for i in range(0, len(lines), 8): prn = int(lines[i][2:4]) nav_data[prn] = {} nav_data[prn]['toc'] = float(lines[i][4:23]) nav_data[prn]['af0'] = float(lines[i][23:42]) nav_data[prn]['af1'] = float(lines[i][42:61]) nav_data[prn]['af2'] = float(lines[i][61:80]) nav_data[prn]['iode'] = int(lines[i+1][4:23]) nav_data[prn]['crs'] = float(lines[i+1][23:42]) nav_data[prn]['deltan'] = float(lines[i+1][42:61]) nav_data[prn]['m0'] = float(lines[i+1][61:80]) nav_data[prn]['cuc'] = float(lines[i+2][4:23]) nav_data[prn]['ecc'] = float(lines[i+2][23:42]) nav_data[prn]['cus'] = float(lines[i+2][42:61]) nav_data[prn]['sqrtA'] = float(lines[i+2][61:80]) nav_data[prn]['toe'] = float(lines[i+3][4:23]) nav_data[prn]['cic'] = float(lines[i+3][23:42]) nav_data[prn]['omega0'] = float(lines[i+3][42:61]) nav_data[prn]['cis'] = float(lines[i+3][61:80]) nav_data[prn]['i0'] = float(lines[i+4][4:23]) nav_data[prn]['crc'] = float(lines[i+4][23:42]) nav_data[prn]['omega'] = float(lines[i+4][42:61]) nav_data[prn]['omegadot'] = float(lines[i+4][61:80]) nav_data[prn]['idot'] = float(lines[i+5][4:23]) nav_data[prn]['l2code'] = int(lines[i+5][23:42]) nav_data[prn]['week'] = int(lines[i+5][42:61]) nav_data[prn]['l2pflag'] = int(lines[i+5][61:80]) nav_data[prn]['accuracy'] = int(lines[i+6][4:23]) nav_data[prn]['health'] = int(lines[i+6][23:42]) nav_data[prn]['tgd1'] = float(lines[i+6][42:61]) nav_data[prn]['tgd2'] = float(lines[i+6][61:80]) nav_data[prn]['iodc'] = int(lines[i+7][4:23]) nav_data[prn]['transmit_time'] = float(lines[i+7][23:42]) nav_data[prn]['fit_interval'] = int(lines[i+7][42:61]) return nav_data # 计算卫星位置 def calculate_satellite_position(nav_data, prn, transmit_time): t = transmit_time - nav_data[prn]['toe'] a = nav_data[prn]['sqrtA'] ** 2 n0 = 7.2921151467e-5 n = n0 + nav_data[prn]['deltan'] M = nav_data[prn]['m0'] + n * t E = M for i in range(10): E = M + nav_data[prn]['ecc'] * np.sin(E) v = np.arctan2(np.sqrt(1 - nav_data[prn]['ecc'] ** 2) * np.sin(E), np.cos(E) - nav_data[prn]['ecc']) phi = v + nav_data[prn]['omega'] u = phi + nav_data[prn]['cus'] * np.sin(2 * phi) + nav_data[prn]['cuc'] * np.cos(2 * phi) r = a * (1 - nav_data[prn]['ecc'] * np.cos(E)) + nav_data[prn]['crc'] * np.cos(2 * phi) + nav_data[prn]['crs'] * np.sin(2 * phi) i = nav_data[prn]['i0'] + nav_data[prn]['idot'] * t + nav_data[prn]['cis'] * np.sin(2 * phi) + nav_data[prn]['cic'] * np.cos(2 * phi) x = r * np.cos(u) y = r * np.sin(u) z = 0 xe = x * np.cos(n * t) - y * np.cos(i) * np.sin(n * t) ye = x * np.sin(n * t) + y * np.cos(i) * np.cos(n * t) ze = y * np.sin(i) return np.array([xe, ye, ze]) # 计算接收机位置 def calculate_receiver_position(satellite_positions, pseudoranges): A = np.zeros((len(satellite_positions) - 1, 4)) b = np.zeros((len(satellite_positions) - 1, 1)) for i in range(1, len(satellite_positions)): A[i-1, :] = np.append(satellite_positions[i, :] - satellite_positions[0, :], 1) b[i-1, 0] = pseudoranges[i] - pseudoranges[0] + np.dot(satellite_positions[0, :], satellite_positions[0, :]) - np.dot(satellite_positions[i, :], satellite_positions[i, :]) x = np.linalg.lstsq(A, b, rcond=None)[0] receiver_position = x[0:3, 0] receiver_clock_error = x[3, 0] return receiver_position, receiver_clock_error # 主程序 if __name__ == '__main__': # 读取北斗BDS-3星历文件 nav_data = read_bds3_nav_file('bds3_nav.txt') # 读取伪距数据 pseudoranges = [22326531.20, 21570022.07, 24881521.91, 23717318.44, 22394411.94] # 计算卫星位置 satellite_positions = np.zeros((len(pseudoranges), 3)) transmit_time = nav_data[1]['transmit_time'] for i in range(len(pseudoranges)): satellite_positions[i, :] = calculate_satellite_position(nav_data, i+1, transmit_time) # 计算接收机位置 receiver_position, receiver_clock_error = calculate_receiver_position(satellite_positions, pseudoranges) # 输出结果 print('Receiver position: x=%f, y=%f, z=%f' % (receiver_position[0], receiver_position[1], receiver_position[2])) print('Receiver clock error: %f' % receiver_clock_error) ``` 这段代码实现了北斗BDS-3星历文件的读取、卫星位置计算、接收机位置计算和结果输出等功能。其,read_bds3_nav_file函数用于读取星历文件,calculate_satellite_position函数用于计算卫星位置,calculate_receiver_position函数用于计算接收机位置。主程序给出了一个示例,包括伪距数据和结果输出。需要注意的是,这段代码只考虑了单频伪距测量,因此计算出的接收机位置精度较低,如果需要提高精度需要考虑多频伪距测量、载波相位测量等因素。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值