手机 RTK/PPK-地面平面有多重要


手机 RTK/PPK-地面平面有多重要?

原文来自于cell-phone-rtk-ppk-how-important-is-a-ground-plane,作者rtklibexplorer,这里只是做翻译记录,版权归原作者。

11-17 文章没有翻译完整,第一稿
I often stress in my posts the importance of using a ground plane under the antenna when collecting observations for precision GNSS solutions to reduce the effect of multipath. This is true for traditional ceramic patch antennas but is even more important when collecting data with a cell phone.

在我过去的文章中,我经常强调,为了减小多路径的影响以提高定位解算结果的精度,在收集观测数据时使用一个屏蔽面放在天线底下很重要。这对传统的陶瓷贴片天线来说当然正确,但在用手机收集数据时(为了减少多路径的影响)也很重要。

A ceramic patch antenna typically used with a low cost GNSS receiver has some built in protection against multipath. It is directional with maximum gain in the vertical direction and is also circularly polarized which helps because the reflected signals have the opposite polarization of the direct signals. In this case, adding a ground plane under the antenna reduces multipath by attenuating signals from low elevation angles and eliminating signals from below the horizon, but it is only part of the multipath mitigation.

通常与低成本 GNSS 接收机一起使用的陶瓷贴片天线具有一些内置的防多径保护。它在垂直方向上具有最大增益的方向性,而且还具有圆极化特性,这是由于反射信号与直射信号的极化相反所致。在这种情况下,在天线下面增加一个屏蔽面,通过减弱低仰角信号和消除地平线以下的信号来减少多径,但这只是多径抑制的一部分。

A cell phone antenna on the other hand is omni-directional and most likely linearly polarized. Hence it has little or no built-in protection against the reflected signals. The only defense in this case against multipath is a ground plane, and so it becomes that much more critical.

另一方面,手机天线是全方位的,极有可能是线性极化的。因此,它对反射信号几乎没有或根本没有内置的保护。在这种情况下,对抗多路径的唯一防御是地面,因此它变得更加关键。

I recently stumbled across a very nice cell phone data set shared online by the same authors of the data I used in my very first post on cell phone solutions from a couple years ago. It is taken with an Xiaomi Mi8 phone and includes 40 days of static measurements, each measurement between 10 and 15 minutes long. There are measurements each day from three locations with varying sky visibility; open sky, partially forested, and forested. What makes this data particularly interesting is that for half of the 40 days, the measurements were taken with a ground plane underneath the phone and the other half were taken without a ground plane. The authors then went on to show that the solutions derived from the ground plane data were significantly more accurate than those derived from the data without ground planes. Unfortunately their paper is not available without logging into a service, but a short summary is available here.

最近,我偶然发现了一个非常不错的手机数据集,这个数据集是由几年前我第一篇关于手机解决方案的文章中使用的数据的同一个作者在网上共享的。这张照片是用小米8手机拍摄的,包括40天的静态测量,每次测量长度在10到15分钟之间。每天都有来自三个地点的测量数据,这三个地点的天空能见度不同: 开放的天空,部分森林覆盖,以及森林覆盖。这些数据之所以特别有趣,是因为在这40天中,有一半的时间是用手机下方的地面飞机进行测量的,而另一半时间是在没有地面飞机的情况下进行的。然后作者继续表明,从地面数据得出的解决方案比那些从没有地面数据得出的解决方案要精确得多。不幸的是,他们的论文在没有登录服务的情况下是不可用的,但是这里有一个简短的摘要。

In this post I will try to repeat their analysis using their data to confirm I get the same result. They actually used an earlier version of the demo5 RTKLIB for their analysis but I will use the latest version of the code (b34d) and a more recent configuration file. For this exercise I will analyze only the open sky measurements and will leave the more challenging measurements for another post.

在这篇文章中,我将尝试使用他们的数据重复他们的分析,以确认我得到了相同的结果。他们实际上使用了 demo5 RTKLIB 的早期版本进行分析,但我将使用最新版本的代码(b34d)和更新的配置文件。对于这个练习,我将只分析开放天空的测量,并将更具挑战性的测量留给另一篇文章。

The data set is very complete and includes observations from a nearby base station as well as downloaded broadcast navigation data. This last item is important, because as I’ve noted before, the Mi8 Galileo navigation data either has a bug in it or is just incompatible with RTKLIB. For this reason, I ran all solutions with the downloaded broadcast navigation data rather than the navigation data collected by the phone.

数据集是非常完整的,包括从附近基站的观测以及下载的广播导航数据。最后一点很重要,因为正如我之前提到的,Mi8 Galileo 的导航数据要么存在 bug,要么与 RTKLIB 不兼容。出于这个原因,我使用下载的广播导航数据而不是手机收集的导航数据运行所有解决方案。

I started with the config file I described in my last cell phone post but made a few changes. I have appended the contents of the final config file I ended up with to the end of this post. I’ll describe the changes I made below.

我从上一篇文章中描述的配置文件开始,但是做了一些修改。我在这篇文章的最后添加了最终配置文件的内容。我将在下面描述我所做的改变。

First of all, this data is static, unlike the previous data, so I changed the solution mode from “kinematic” to “static”.

首先,这些数据是静态的,与以前的数据不同,因此我将解决方案模式从“运动学”改为“静态”。

Next, looking at the residuals from an initial solution plotted with RTKPLOT, I noticed a significant difference in the magnitude of the pseudorange residuals between L1 and L5. In the plots below, the left plot shows the L1 residuals and the right plot shows the L5 residuals. The carrier phase residuals are similar but the pseudorange residuals are much smaller for L5. This is not unexpected because the newer L5 signal has improved structure and higher power. The details are explained in this article.

接下来,观察用 RTKPLOT 绘制的初始解的残差,我注意到 l1和 l5之间伪橙残差的大小有显著差异。在下面的图表中,左边的图表显示 l1残差,右边的图表显示 l5残差。载波相位残差相似,但 l5的伪橙残差要小得多。这并不意外,因为较新的 l5信号已经改善了结构和更高的功率。详细情况在本文中进行了说明。

Solution residuals for L1(left) and L5(right) observations L1(左)和 L5(右)观测的解残差

To take advantage of this difference, I adjusted the code/carrier phase error ratio for L1 (stats-eratio1) from 300 to 1500 and left the L5 error ratio (stats-eratio5) at 300. This will cause the kalman filter to weight the L5 pseudorange observations more heavily than the L1 observations. In RTKPOST, this parameter is set in the Statistics tab of the Options menu. Note that prior to the b34d code, the L5 error ratio parameter existed but was not configurable through the GUI interface in RTKPOST, but that is fixed now.

为了利用这种差异,我调整了 L1(stats-eratio1)的代码/载波相位错误率,从300调整到1500,并将 l5的错误率(stats-eratio5)保持在300。这将导致卡尔曼滤波器对 l5伪距观测的权重比 l1观测更大。在 RTKPOST,这个参数是在“选项”菜单的“统计”选项卡中设置的。注意,在 b34d 代码之前,l 5错误比率参数是存在的,但是不能通过 RTKPOST 的 GUI 界面进行配置,但是现在已经修复了。

Next, based on the SNR of the observations, plotted below, I chose to reduce the minimum SNR threshold from 34 dbHz to 24 dbHz. This is a somewhat arbitrary setting and may not be optimal, but leaving it at 34 dbHz for this data set would have thrown out too many usable satellites. It could also have been adjusted separately for L1 and L5 but I did not choose to do this. It is interesting that the L5 observations do not appear to have higher SNR than L1 despite the increased signal power mentioned above. This may be due to limitations of the antenna.

接下来,根据下面绘制的观测值的信噪比,我选择将最小信噪比阈值从34dbhz 降低到24dbhz。这是一个有点随意的设置,可能并不是最佳的,但是对于这个数据集来说,将它保持在34dbhz 会抛出太多可用的卫星。它也可以分别调整 l1和 L5,但我没有选择这样做。有趣的是,尽管上面提到的信号功率增加了,但是 l5的观测信噪比并没有高于 L1。这可能是由于天线的局限性。

SNR of observations, L1 (left) and L5(right) 观测信噪比,L1(左)和 L5(右)

The last change I chose to make was to change the solution type (Filter mode in the RTKPOST options menu) from “Combined to “Combined-no phase reset”. This is a relatively new option in the demo5 code. The official 2.4.3 code always resets the phase bias estimates between the forward and backward solutions and this is a more conventional approach. Earlier versions of the demo5 code reset the phase estimates if ambiguity resolution was set to fix-and-hold but did not reset them otherwise. The reason to reset them is to insure the forward and backward solutions independent, but especially for shorter data sets, it can be advantageous to use the converged estimates as a starting point for the backwards solution. Since, in this experiment, the measurements are fairly short (10-15 minutes), I chose to not reset the biases.

我选择的最后一个更改是将解决方案类型(RTKPOST 选项菜单中的 Filter 模式)从“ Combined”更改为“ Combined-no phase reset”。这是 demo5代码中一个相对较新的选项。官方的2.4.3代码总是重置前向和后向解之间的相位偏差估计,这是一个更常规的方法。如果歧义解析被设置为修复并保持,早期版本的 demo5代码重置了相位估计,但没有重置它们。重置它们的原因是为了确保前向和后向解的独立性,但对于较短的数据集,使用聚合估计作为后向解的起点是有利的。因为在这个实验中,测量时间相当短(10-15分钟) ,所以我选择不重置偏差。

The next step was to run the solution on all 40 days of data. RTKPOST is nice for working with a single data set and experimenting with different parameter settings, but to run many solutions quickly, it is easier to batch process them with the RNX2RTKP command line version of RTKPOST. I used a simple python script to run RNX2RTKP ( the CLI version of RTKPOST) in multiple simultaneous threads to make this process faster since a single instance of an RTKLIB app will only use a single processor . I’ll discuss this script in more detail in my next post.

下一步是对所有40天的数据运行解决方案。对于处理单个数据集和试验不同的参数设置是很好的,但是为了快速运行许多解决方案,使用 RNX2RTKP 命令行版本的 RTKPOST 更容易批处理它们。我使用一个简单的 python 脚本在多个并发线程中运行 RNX2RTKP (RTKPOST 的 CLI 版本) ,使这个过程更快,因为一个 RTKLIB 应用程序的单个实例将只使用一个处理器。我将在下一篇文章中更详细地讨论这个脚本。

I then used python to plot the error in the solutions, 40 points in all, 2o with ground planes, 20 without. To calculate the errors, I subtracted the ground truth included with the data set from each solution position.

然后我用 python 来绘制解答中的错误,总共40个点,地平面20个,没有20个。为了计算误差,我从每个解决方案位置减去包含在数据集中的地面真值。

Static PPK solutions for open sky data, with and without ground plane 静态 PPK 解决方案的开放天空数据,有和没有地平面

Note that the ground truth was generated from a survey grade receiver but is only approximate because the exact location of the antenna within the phone was unknown. Cell phone antennas can also have large offsets between the mechanical center of the antenna (ARP) and the electrical center of the antenna (APC). No attempt was made to compensate for these.

请注意,地面真相是从一个调查等级的接收器产生的,但只是近似的,因为天线在手机内的确切位置是未知的。手机天线也可以有大偏移量之间的机械中心的天线(ARP)和电气中心的天线(APC)。没有人试图弥补这些损失。

As expected, the ground plane solutions were significantly more accurate than the no-ground plane solutions. In fact, 19 out of the 20 measurements solutions for data using a ground plane were within a few centimeters of each other. Only 7 of the 20 measurements without ground planes met the same criteria. Overall, I would say this data makes quite a compelling argument for using a ground plane. I understand it’s not always possible in a cell phone application to do this, but it’s important to at least understand the significance of this choice.

正如预期的那样,地平面解比非地平面解更加精确。事实上,使用地平面的20个测量方案中有19个相距不到几厘米。在没有地面飞机的20次测量中,只有7次符合同样的标准。总的来说,我认为这些数据为使用地面提供了一个非常有说服力的论据。我知道在手机应用程序中并不总是可以做到这一点,但是至少理解这个选择的意义是很重要的。

The results were also consistent enough to suggest that cell phones, despite their low performance antennas, are becoming a viable option for practical measurements, at least in open sky environments. It’s worth noting as well, that, although the errors were much larger without the ground plane, even the errors in these measurements were consistently well below a meter.

研究结果也足以表明,尽管手机的天线性能较差,但至少在开放的天空环境中,手机正成为一种可行的实用测量方法。同样值得注意的是,虽然没有地平面的情况下误差要大得多,但即使是这些测量中的误差也一直远低于一米。

It is important to remember that this data was all taken with an open sky view and that you should not expect such good results in more challenging conditions. It’s also still true that low cost receivers with higher quality antennas will still give much more robust and reliable solutions.

重要的是要记住,这些数据都是在开放的天空下获得的,你不应该期望在更具挑战性的条件下获得如此好的结果。低成本的接收机配有高质量的天线仍然可以提供更加健壮和可靠的解决方案,这也是事实。

There’s plenty more data in this data set that I didn’t look at in this post but hope to take a closer look at in the future.

在这个数据集中还有很多数据,我在这篇文章中没有看到,但是希望在未来能够进一步了解。

Config file used for this experiment:

用于这个实验的配置文件:

\# rtkpost options for b34d code, static Mi8 cell phone
pos1-posmode       =static     # (0:single,1:dgps,2:kinematic,3:static,4:static-start,5:movingbase,6:fixed,7:ppp-kine,8:ppp-static,9:ppp-fixed)
pos1-frequency     =l1+l2+l5   # (1:l1,2:l1+l2,3:l1+l2+l5,4:l1+l2+l5+l6)
pos1-soltype       =combined-nophasereset # (0:forward,1:backward,2:combined,3:combined-nophasereset)
pos1-elmask        =15         # (deg)
pos1-snrmask\_r     =on         # (0:off,1:on)
pos1-snrmask\_b     =on         # (0:off,1:on)
pos1-snrmask\_L1    =24,24,24,24,24,24,24,24,24
pos1-snrmask\_L2    =34,34,34,34,34,34,34,34,34
pos1-snrmask\_L5    =24,24,24,24,24,24,24,24,24
pos1-dynamics      =on         # (0:off,1:on)
pos1-tidecorr      =off        # (0:off,1:on,2:otl)
pos1-ionoopt       =brdc       # (0:off,1:brdc,2:sbas,3:dual-freq,4:est-stec,5:ionex-tec,6:qzs-brdc)
pos1-tropopt       =saas       # (0:off,1:saas,2:sbas,3:est-ztd,4:est-ztdgrad)
pos1-sateph        =brdc       # (0:brdc,1:precise,2:brdc+sbas,3:brdc+ssrapc,4:brdc+ssrcom)
pos1-posopt1       =off        # (0:off,1:on)
pos1-posopt2       =off        # (0:off,1:on)
pos1-posopt3       =off        # (0:off,1:on,2:precise)
pos1-posopt4       =off        # (0:off,1:on)
pos1-posopt5       =off        # (0:off,1:on)
pos1-posopt6       =off        # (0:off,1:on)
pos1-exclsats      =           # (prn ...)
pos1-navsys        =45         # (1:gps+2:sbas+4:glo+8:gal+16:qzs+32:bds+64:navic)
pos2-armode        =fix-and-hold # (0:off,1:continuous,2:instantaneous,3:fix-and-hold)
pos2-gloarmode     =fix-and-hold # (0:off,1:on,2:autocal,3:fix-and-hold)
pos2-bdsarmode     =on         # (0:off,1:on)
pos2-arfilter      =on         # (0:off,1:on)
pos2-arthres       =3
pos2-arthresmin    =3
pos2-arthresmax    =3
pos2-arthres1      =0.1
pos2-arthres2      =0
pos2-arthres3      =1e-09
pos2-arthres4      =1e-05
pos2-varholdamb    =0.1        # (cyc^2)
pos2-gainholdamb   =0.01
pos2-arlockcnt     =5
pos2-minfixsats    =4
pos2-minholdsats   =5
pos2-mindropsats   =10
pos2-rcvstds       =off        # (0:off,1:on)
pos2-arelmask      =15         # (deg)
pos2-arminfix      =10
pos2-armaxiter     =1
pos2-elmaskhold    =15         # (deg)
pos2-aroutcnt      =20
pos2-maxage        =30         # (s)
pos2-syncsol       =off        # (0:off,1:on)
pos2-slipthres     =0.05       # (m)
pos2-rejionno      =1          # (m)
pos2-rejgdop       =30
pos2-niter         =1
pos2-baselen       =0          # (m)
pos2-basesig       =0          # (m)
out-solformat      =llh        # (0:llh,1:xyz,2:enu,3:nmea)
out-outhead        =on         # (0:off,1:on)
out-outopt         =on         # (0:off,1:on)
out-outvel         =off        # (0:off,1:on)
out-timesys        =gpst       # (0:gpst,1:utc,2:jst)
out-timeform       =hms        # (0:tow,1:hms)
out-timendec       =3
out-degform        =deg        # (0:deg,1:dms)
out-fieldsep       =
out-outsingle      =off        # (0:off,1:on)
out-maxsolstd      =0          # (m)
out-height         =ellipsoidal # (0:ellipsoidal,1:geodetic)
out-geoid          =internal   # (0:internal,1:egm96,2:egm08\_2.5,3:egm08\_1,4:gsi2000)
out-solstatic      =all        # (0:all,1:single)
out-nmeaintv1      =0          # (s)
out-nmeaintv2      =0          # (s)
out-outstat        =residual   # (0:off,1:state,2:residual)
stats-weightmode   =elevation  # (0:elevation,1:snr)
stats-eratio1      =1500
stats-eratio2      =300
stats-eratio5      =300
stats-errphase     =0.006      # (m)
stats-errphaseel   =0.006      # (m)
stats-errphasebl   =0          # (m/10km)
stats-errdoppler   =1          # (Hz)
stats-snrmax       =52         # (dB.Hz)
stats-stdbias      =30         # (m)
stats-stdiono      =0.03       # (m)
stats-stdtrop      =0.3        # (m)
stats-prnaccelh    =3          # (m/s^2)
stats-prnaccelv    =1          # (m/s^2)
stats-prnbias      =0.001      # (m)
stats-prniono      =0.001      # (m)
stats-prntrop      =0.0001     # (m)
stats-prnpos       =0          # (m)
stats-clkstab      =5e-12      # (s/s)
ant1-postype       =llh        # (0:llh,1:xyz,2:single,3:posfile,4:rinexhead,5:rtcm,6:raw)
ant1-pos1          =0          # (deg|m)
ant1-pos2          =0          # (deg|m)
ant1-pos3          =0          # (m|m)
ant1-anttype       =
ant1-antdele       =0          # (m)
ant1-antdeln       =0          # (m)
ant1-antdelu       =0          # (m)
ant2-postype       =rinexhead  # (0:llh,1:xyz,2:single,3:posfile,4:rinexhead,5:rtcm,6:raw)
ant2-pos1          =0          # (deg|m)
ant2-pos2          =0          # (deg|m)
ant2-pos3          =0          # (m|m)
ant2-anttype       =
ant2-antdele       =0          # (m)
ant2-antdeln       =0          # (m)
ant2-antdelu       =0          # (m)
ant2-maxaveep      =1
ant2-initrst       =on         # (0:off,1:on)
misc-timeinterp    =on         # (0:off,1:on)
misc-sbasatsel     =0          # (0:all)