自动(智能)驾驶 | 时间同步之给工控机授时(PPS+GPRMC)

发布于:2023-01-20 ⋅ 阅读:(2) ⋅ 点赞:(0) ⋅ 评论:(0)

在自动驾驶领域,时间同步是非常重要的,决定着传感器时间戳是否能对齐和后续的算法研究。有很多课程和资料都会给出大致的方向诸如网络时间、GNSS授时(最常见)但实际操作起来又该如何做呢,目前鲜有这方面的资料,本文将给出来龙去脉和具体方法。万字长文感谢大家支持!

目录

0.原理、分析与准备工作

1.PPS的接入

2.GPS设备接入与读取(软件gpsd)

3.同步(将gpsd取得的时间给系统,chrony)

4.拓展内容:ROS的时间机制

5.参考与致谢


0.原理、分析与准备工作

 车载能够提供授时方法的有两种,一种是通过GPS来授时,另外一种则是通过互联网来授时(平常手机电脑的选择网络时间NTP)。前者的主要原理是PPS+NMEA(例如GPRMC)来授时,GPRMC报文提供年月日时分秒信息(一般通过波特率9600串口发送接收,处理时间为毫秒级别)、PPS则通常使用1PPS即频率为1次每秒的秒脉冲,接入工控机,当工控机支持时,设备接收到PPS脉冲后,毫秒及以下位清零重新计算。整体的时间就是与GPRMC报文中的UTC时间整秒相加,得到系统时间。PPS 同步脉冲长度为 20ms 至 200msGPRMC 数据必须在同步脉冲 500ms 内完成;后者则是通过网络NTP得到的。

 我们从系统设计的角度来考虑:

 对于ADU(后续系统的PTP Master)按照图中的意思(实线)是接入了一个NTP time_server来给他做时间同步,也就是它的上层已经将时间同步好了给到它,当然也可以用PTP,同样把时间给到它,ADU作为Stratum的一层,这样是操作起来最为简便的。使用这种方案我们需要什么呢?那就要考虑最顶层时间从何而来,往往其源头仍然是GPS、GNSS。有两种设备(交换机),一种是可以将GNSS的PPS和NMEA接入到交换机上,交换机通过自己的系统处理校准、防干扰等等,构建NTP或者PTP,只需要将设备通过交换机的以太网网口接入到工控机等设备上(相当于它是一个master),再构建NTP或者PTP即可另外一种,对时间要求更高的,引入铷原子钟,有专门的网络时间服务器,它直接接收卫星信号解析时间并能输出PPS,例如下图的机器非广告、来源北斗邦泰T600官网)。

 最终组成的框图:

 这款产品的性能和精度(用了铷原子钟)

 另外一种就是我们今天讨论的中心,是否可以将PPS和GPRMC接入工控机,让他成为Master呢

 现在考虑我们就使用这种pps+GPRMC的时间同步方式,原理似乎很好理解:

1.工控机支持;(不支持的设备可以考虑GPIO)

2.GNSS给出PPS和GPRMC,接入到设备上即可;

实则不然,这个方式其实操作起来要复杂一些,我们的工控机往往采用的都是linux内核的ubuntu系统,例如目前主流使用的Ubuntu18.04以及20.04。那么按照上面的要求我们需要做哪些事情呢?

工控机支持,首先工控机要能够接收到PPS信号以及GPRMC串口信息等,有内部晶振控制授时(这个一般目前的工控机都具备)。那么打开你的设备手册,以我的机器为例:

找COM口的说明(一般通过9针COM接入)

 可以发现4个COM口都是支持RS232 mode的也就是GNSS接入的Mode,这里需要指明的是这四个COM口对应设备的(ttyS0~ttyS3)。可以发现Pin1是可以接收DCD的,也就是这个pin接入PPS,RX为接收口、TX为发出口、5Pin为GND。发现工控机没问题。

接下来查阅GNSS手册,根据要求接线即可。

 临时测试可以购买诸如这种接线测试:

 参考接线方式(需要根据pin定制),将最右边的看作GNSS。

 接线完毕后插入到机箱上!完结撒花!其实这才开始。

首先我们来确认一下是否正常接入了,下载诸如cutecom等串口管理工具来查看。

sudo apt install cutecom

 如果你插入到COM1口:

  点击open device能看到GPRMC的滚动输出说明GPRMC正常接入了。那么如何确认PPS接入了呢?

 在继续下一部分之前,我们有必要了解Ubuntu的时间机制

简短的来说和我们的windows类似,有一个硬件时间(hwclock)以及系统时间(sysclock),来自linux的内核。

具体可以参考这篇文章:Ubuntu命令行修改时间和时区_Smiling_star的博客-CSDN博客_ubuntu 时区设置命令

 下图为NTP原理图:

1.PPS的接入

这里就要说到linux内核了,你是用的是linux,那么PPS如何传到系统层呢?查询ubuntu的系统kernel版本后,在linux doc中:

PPS - Pulse Per Second — The Linux Kernel documentation

可以找到关于PPS的内容,发现LinuxPPS目前已经是内置了,这太好了(之前的老版本有的需要手动编译内核)

那我们来检查一下状态:

grep '^CONFIG_PPS' /boot/config-$(uname -r)

 输出应当为:

CONFIG_PPS=m  (为y也没问题)
CONFIG_PPS_CLIENT_LDISC=m

 说明没问题,接下来我们配置PPS_ldisc.service

将下述etc/systemd/system/pps_ldisc.service 放到/etc/systemd/system下

[Unit]
Description=Modprobe pps_ldisc
After=setserial.service

[Service]
Type=oneshot
ExecStart=/sbin/modprobe pps_core
ExecStart=/sbin/modprobe pps_ldisc

[Install]
WantedBy=gpsd.service

 执行:

systemctl enable pps_ldisc.service  
systemctl start pps_ldisc.service

#第一行使得开机能自启,第二行启动这个service

 (非必要)安装Setserial ,此举是为了低延迟和提升准确率

apt-get install setserial

 安装后,在var/lib/setserial/autoserial.conf 中修改

#
###AUTOSAVE-ONCE###
###AUTOSAVE-ONCE###
###AUTOSAVE###
#
# If you want to configure this file by hand, use 
# dpkg-reconfigure setserial
# and change the configuration mode of the file to MANUAL. If you do not do this, this file may be overwritten automatically the next time you upgrade the
# package.
#

# dpkg-reconfigure setserial   ...  set configuration mode to MANUAL
# Added next line for ttyS1 for Garmin 18 LVC GPS
# From: http://www.rjsystems.nl/en/2100-ntpd-garmin-gps-18-lvc-gpsd.php
# Only needed to add 'low_latency' to default port configuration
# Print out current port configuration:
#   setserial -G /dev/ttyS1
/dev/ttyS1 uart 16550A port 0x02f8 irq 3 baud_base 115200 spd_normal skip_test low_latency

#该文件仅需修改上面的端口映射ttyS1代表COM2口,此地需要根据你真实接入的串口修改, 
#UART越大,内部buffer就越大(16550A有16位的buffer),但是对于本任务来说,不用UART更好,
#因为缓冲区会增加延迟从而降低精度;为了最大限度地降低 CPU 利用率,默认情况下,
串口流量通常会延迟 5 到 10 毫秒。但是,在这种情况下,使用了 low_latency 设置,
#设置高一点的CPU负载是值得的。
#115200是波特率,可以尝试改为9600,如果改动后找不到数据了,就改回来

 然后运行:

dpkg-reconfigure setserial

 点击OK,选择为MANUAL;(可能会有错误提示)

重启该service:

for op in stop start; do systemctl $op setserial.service; done

pps测试:

sudo ppswatch /dev/pps1
#pps1后面的数字根据实际情况来 可使用ppsfind --help

 说明PPS正常,其他测试还有:

ntpshmmon -n 5

-------------------------------------------------------------------------------------------------------

2.GPS设备接入与读取(软件gpsd)

gpsd是一款常用的读取GPS信息的软件,其中有大家熟知的gpsmon等界面。作用是解析NMEA数据及PPS,从而获取当前GNSS的时间

安装:

sudo apt-get install -y gpsd gpsd-clients python-gps pps-tools

 安装后,修改/etc/default/gpsd

# Default settings for gpsd.
# Please do not edit this file directly - use `dpkg-reconfigure gpsd' to
# change the options.
START_DAEMON="true"
GPSD_OPTIONS="-b -n -D1"
DEVICES="/dev/ttyS1 /dev/pps0"
USBAUTO="false"
GPSD_SOCKET="/var/run/gpsd.sock"

#注意DEVICE的一行需要根据实际情况填写,上面为COM2口,默认是pps0
#推荐事先验证并查找对应的端口和pps
#使用 ppsfind --help
#输出示例:pps0: name=serial1 path=/dev/ttyS1   也就代表着我们现在用的是ttyS1以及pps0

 gpsd测试:

gpsd -b -n -N -D3 /dev/ttyS1 /dev/pps0
#同样设备要对应,分别是MNEA来源和pps

 测试NMEA:

#可以使用cgps或者gpsmon
cgps
gpsmon

下图为cgps,可以看到pps和GPRMC正常: 

上图中的time即为根绝GPRMC+PPS推出的时间

------------------------------------------------------------------------------------------------

3.同步(将gpsd取得的时间给系统,chrony)

和ntpd类似,是一种网络时间NTP的实现,但它可以与参考时钟(GNSS)同步、甚至可以手动输入同步。这里就让它与参考时钟同步获取PPS。chrony分为chronyc与chronyd。

关于Chrony有一篇写的非常好的文章推荐:

Linux ❉ Chronyd时间同步服务器详解_wangjie722703的博客-CSDN博客_chronyd

以及chrony官方文档:chrony – Documentation

第一步:安装chrony

sudo apt install chrony

 如果系统也安装了ntpd需要关闭,避免产生冲突:

systemctl disable ntpd
systemctl stop ntpd

 第二步修改etc/chrony/chrony.conf

# This the default chrony.conf file for the Debian chrony package.  After
# editing this file use the command 'invoke-rc.d chrony restart' to make
# your changes take effect.  John Hasler <jhasler@debian.org> 1998-2008

# See www.pool.ntp.org for an explanation of these servers.  Please
# consider joining the project if possible.  If you can't or don't want to
# use these servers I suggest that you try your ISP's nameservers.  We mark
# the servers 'offline' so that chronyd won't try to connect when the link
# is down.  Scripts in /etc/ppp/ip-up.d and /etc/ppp/ip-down.d use chronyc
# commands to switch it on when a dialup link comes up and off when it goes
# down.  Code in /etc/init.d/chrony attempts to determine whether or not
# the link is up at boot time and set the online status accordingly.  If
# you have an always-on connection such as cable omit the 'offline'
# directive and chronyd will default to online.
#
# Note that if Chrony tries to go "online" and dns lookup of the servers
# fails they will be discarded.  Thus under some circumstances it is 
# better to use IP numbers than host names.

# ------------------------------------------------------------------------
# Use a local GPS device and gpsd as Staratum 0 GPS source
#
# Disable systemd-timesyncd.service - conflicts with chrony autostart !
#   for op in stop disable mask; do systemctl $op systemd-timesyncd.service ; done
#
#
# For Globalsat MR-350PS4 PPS GPS on SERIAL port
# From https://wiki.alpinelinux.org/wiki/Chrony_and_GPSD
# Commenting out NEMA and SOCK -:
#   - NEMA is too inaccurate and chrony will not use anyway!
#   - SOCK - does not seem to get any values !
#
#refclock SHM 0  delay 0.5 refid NEMA
refclock SHM 1 offset 0 delay 0.001 refid PPS
#refclock SOCK /var/run/chrony.ttyS1.sock delay 0.0 refid SOCK

# C(20180207) Setings for using USB non-PPS GPS device and GPSD
# NEMA is too inaccurate and chrony will not use anyway!
# Need to fiddle with offset (seconds) to make sure refclock is considered
# watch the output of 'chronyc sources' to see 'Last Sample'
#
# refclock SHM 0 offset 0.05 refid NMEA
#一些NTP的server,可以改成国内的一些如阿里的或者注释掉都可以
server clock.fmt.he.net iburst
server clock.sjc.he.net iburst
server time.cloudflare.com iburst
server ntp1.cloud.aliyuncs.com
server time.nist.gov
server gpstime.la-archdiocese.net

#server time1.google.com iburst
#server time2.google.com iburst
#server time3.google.com iburst
#server time4.google.com iburst
#pool 0.ubuntu.pool.ntp.org iburst
#pool 2.debian.pool.ntp.org offline iburst

# Specify IP address (interface) that chronyd CLIENT will bind to
# The bindacqaddress directive sets the network interface to which
# chronyd will bind its NTP client sockets
# bindacqaddress 0.0.0.0


# Allow other NTP clients in our zone to query time
# allow 10.3/16
# allow 10.30.0/24

# (CHANGED): chronyd version 3.2 commandkey directive is no longer supported
# This directive sets the key ID used for authenticating user commands via the
# 'chronyc' program at run time.
# commandkey 1

# End of customized settings - EXCEPT lines labeled with (CHANGED)
# ------------------------------------------------------------------------

# Look here for the admin password needed for chronyc.  The initial
# password is generated by a random process at install time.  You may
# change it if you wish.

keyfile /etc/chrony/chrony.keys

# I moved the driftfile to /var/lib/chrony to comply with the Debian
# filesystem standard.

driftfile /var/lib/chrony/chrony.drift

# Comment this line out to turn off logging.

log tracking measurements statistics
logdir /var/log/chrony

# Stop bad estimates upsetting machine clock.

maxupdateskew 100.0

# Dump measurements when daemon exits.

dumponexit

# Specify directory for dumping measurements.

dumpdir /var/lib/chrony

# This directive lets 'chronyd' to serve time even if unsynchronised to any
# NTP server.

#local stratum 10

# This directive designates subnets (or nodes) from which NTP clients are allowed
# to access to 'chronyd'.

#allow foo.example.net
#allow 10/8
#allow 0/0 (allow access by any IPv4 node)
#allow ::/0 (allow access by any IPv6 node)

# This directive forces `chronyd' to send a message to syslog if it
# makes a system clock adjustment larger than a threshold value in seconds.

logchange 0.5

# This directive defines an email address to which mail should be sent
# if chronyd applies a correction exceeding a particular threshold to the
# system clock.

# mailonchange root@localhost 0.5

# This directive tells 'chronyd' to parse the 'adjtime' file to find out if the
# real-time clock keeps local time or UTC. It overrides the 'rtconutc' directive.

hwclockfile /etc/adjtime

# This directive enables kernel synchronisation (every 11 minutes) of the
# real-time clock. Note that it can’t be used along with the 'rtcfile' directive.

rtcsync
#rtcsync -指令将启用一个内核模式,在该模式中,系统时间每11分钟会拷贝到实时时钟(RTC)

 文中只需关注一行(PPS):

refclock SHM 1 offset 0 delay 0.001 refid PPS

 根据文档(chrony – Frequently Asked Questions):

 由gpsd作为消息源被指定为SHM 0 参考时钟(SHM是什么意思,推测为共享内存shared memory,也就是与gpsd共享内存),如果有多个接收机则设置为其他偶数。

若基于PPS事件源贼被定义为 SHM 1 参考时钟,或者是其他奇数。或者使用sock, 也就是位于var/run/chrony.DEV.sock ,DEV就是我们之前提到的设备如ttyS0.

由于gpsd和chrony都支持PPS,有三种推荐的选择:

# First option
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS

# Second option
refclock SHM 1 refid GPS

# Third option
refclock PPS /dev/pps0 lock NMEA refid GPS
refclock SHM 0 offset 0.5 delay 0.1 refid NMEA noselect

 第一种就是使用sock,第二种是使用 SHM 1(PPS-based),第三种都用。

那这几种有什么优劣呢?

sock 不用轮训(polling),从而可以比SHM更早获得sample,但是需要在chronyd之后在启动gpsd才能链接到它的socket;

sock及 SHM1 可以比PPS更加准确的(如果gpsd修正了了接收过程中的误差);

PPS可以使用更高的频率(不必是1,可以由rate 给出),但是需要需要第二个参考时钟与秒脉冲匹配(也就是说需要例如GPRMC,并不需要有多准确但是需要参考以确定时间),并且需要正确指定SHM 0偏移量以补偿消息的延迟,而gpsd可应用硬件特定的信息。(如下图)

 上图的意思就是PPS需要一个参考的UTC时间,例如GPRMC,也就是下面的NMEA,可以看出NMEA是不准的,但是没关系,使用PPS校准即可。

如果PPS无法使用,则只能使用

refclock SHM 0 offset 0.5 delay 0.1 refid GPS

 大家可以根据情况调整上面的描述来调整。

第三步,设置开机自启动、开启服务

systemctl enable chronyd
systemctl start chronyd

 验证并查看有多少个节点连接:

chronyc activity

 测试:

chronyc -m 'sources -v' tracking 'sourcestats -v'

 输出结果:

可以看到图上PPS一栏,前面有#*,则代表了现在pps  #代表为local clock *代表为已同步的;下面的reference ID为一串数字括号后是pps,说明来源于PPS,底下给出的时间(system time)为PPS给出的时间——说明同步成功了

解读:

通过-v指令已经输出了解释,但是可能还是比较难懂,这里解读一下。

LastRx: 表示在多久以前从该时间源收到了最后一个好的时间数据。默认时间为秒,还可以是年月日等。

last sample:由三部分组成,方括号中的代表measured offset(即代表实际测得的offset),方框左侧为adjusted offset(也就是调整校正后最终采用的offset),最后面的是erro。

stratum:NTP表示层级;

poll:表示轮序频率,PPS的poll为4,代表每隔2^4=16秒,对该时间进行轮询;

Reach:表示接收到了多少,即可到达性,默认是8进制数,每次从时间源收到或丢包都会更新该值。377表示都接收到了。

Last offset:代表上一个最好的时间偏移量;大概是-15.9微秒;

RMS offset:则代表offset的长期均方根值;

Frequency:代表如果不矫正,系统时间出错率,单位是ppm(百万分之一)

Residual freq:参考时间源测量到的频率与当前使用的频率之间的差,单位ppm;

skew:频率上的估计误差范围

root delay: 表示stratum-1(也就是源)网络路径延迟总和

update interval:最后两次更新的式中间隔;

 至此工控时间同步结束。

tips:

查询目前的service:

service --status-all

 +:代表正在启用的service

-:代表有但是未启用的service

通过该命令方便大家检查是否成功安装了service并且是否是active状态。

4.拓展内容:ROS的时间机制

当完成了时间同步,很多同学需要使用ROS来完成开发,当你使用rviz打开你的激光雷达雷达的时候是否注意到页面有这样的时间显示:

 其中时间有ROS Timewall Time(挂钟时间) 每个时钟的后面还跟着一个xxx Elapsed(时间的流逝量,也就是从打开到现在经历的时间)。可以明显的看到两个时间是不同的时间戳小数点第二位就有问题了,这也就是我们为什么要做同步的原因。未接入GNSS设备时,以及未联网时(无法使用网络NTP),时间来源就是硬件晶振带来的时间。

这里就得说说ROS的时间机制:

请大家阅读官方文档http://wiki.ros.org/Clock

重点内容在这:

 这里明确了,ROS默认是使用电脑(设备)的系统时钟来作为时钟源的,也就是wall-clock。当我们录制完rosbag等回放的时候,此时的ROS Time仍然是我们录制的时间戳,而 Wall Time为彼时的系统时间,通俗地说ROS Time是可变的甚至流逝可以更快,比如你5倍速度播放rosbag,时间增长量会变为五倍,而此时其后面的Elapsed也会为Wall Time的Elapsed的五倍。当然你还可以暂停、往复播放等,但是wall time会一直流逝。Ros Time也叫模拟时钟(simulation time)。这里也提到了,如果在多台设备都去使用wall-time,显然因为硬件等原因这些时钟初始状态一定不是同步且有较大偏移量的,所以ROS推荐可以使用chrony软件通过ntp来完成不同设备间的时间同步。

5.参考与致谢

本文内容参考了以下来源,本文深受他们的影响,在此表示感谢!也推荐大家去阅读他们的文章:

1.GitHub - linuxonly1993/gpsd_pps: NTP time with GPSD and PPS-enabled GPS device

2.(关于chrony非常好的解读!)

Linux ❉ Chronyd时间同步服务器详解_wangjie722703的博客-CSDN博客_chronyd

3.Synchronizing ntpd to a Garmin GPS 18 LVC via gpsd

推荐阅读:

chrony – Frequently Asked Questions

NTP 时间同步指南 Linux(Ubuntu) / Windows_wyk023的博客-CSDN博客_ntp命令同步时间

 Xavier + GPS/PPS + NTP时间设置_恩来贺的博客-CSDN博客_xavier 时间同步

关于GPS的1PPS时间同步功能探索与测试_WindLOR的博客-CSDN博客_1pps

-------------------------------------------------------------------------------------------------------------

感谢大家阅读,欢迎大家讨论与技术分享! 欢迎同样热爱自动驾驶技术的伙伴~会持续分享自动驾驶技术干货与实战!撰文不易,喜欢的朋友可以点赞收藏加关注呀!爱您~