激光雷达与相机时间同步问题的低成本完整解决方案

        激光雷达和相机的时间同步是做自动驾驶感知方面实验的基础,目前网络上给出的各种解决方案都不是很完整,博主也走了不少弯路,最后探索出一个较为完整的低成本解决方案(重点是低成本,可靠性一般)。这是第一次在CSDN上写作,写得不好还请各位看官批评指正哈哈!

总体技术路线

        激光雷达和相机的时间同步是多传感器融合是先决条件,但是精确的时间同步需要涉及到软件和硬件的相关知识,因此也是不大容易实现的。下面是目前网络上已有的资料(截止到2021.03.21):

        其中苏笑云大佬的回答是比较清晰。大佬提到:更彻底的解决方法是选择可以触发拍摄的相机(必须是硬件线控),根据激光雷达的帧周期同步触发相机的拍摄,实现雷达和相机的完全帧同步,实现完全的毫秒级同步。 本文也是根据这个解决方案来做的。所谓激光雷达与相机时间同步其实涉及到两个问题:一个是激光雷达和相机的内部时钟要同步;一个是要对采集出来的点云和图像进行时间对齐。因此本文也是从这两个方面来进行技术路线的描述的。本文的技术路线也参考了上述的论文《Camera-LIDAR_Detection_Fusion》,有一说一,这篇本科毕业论文写得很不错!

设备选型

  • GPS,能够输出PPS和NMEA串口信号,淘宝有很多款可以支持,不过买回来需要用示波器测试一下,博主就踩过劣质GPS的坑…
  • 树莓派,作为网络授时主时钟,应该随便一款应该都行,博主使用的是树莓派4B+
  • 相机,要能够支持外部硬件触发,要能支持PTP时间同步功能,要能支持输出图像曝光时相机的内部时间戳,博主使用的是Basler的工业相机
  • 激光雷达,要能支持基于PPS和NMEA的时间同步(或者PTP时间同步,虽然博主没有调通这个功能),能够支持输出点云采集时激光雷达内部的时间戳,博主使用的是速腾激光雷达
  • 单片机,把GPS输出的PPS信号转发为同相位的10Hz方波,给相机做硬件触发,要求单片机支持定时器,中断,GPIO,博主还没来得及实现单片机的功能,目测STM32就可以了。

激光雷达和相机的内部时钟同步

激光雷达和相机时钟同步


激光雷达和相机的内部时钟同步示意图

GPS到激光雷达的时间同步

        这个需要参考的激光雷达说明文档啦,像速腾激光雷达,还对PPS和NMEA串口信号的格式做个限制,所以需要提前用示波器看买来的GPS模块是否符合要求。这一步成功之后,在RSVIEW的web端的GPS Data一栏会显示串口信息、GPS Status和PPS Status一栏Locked。

GPS到树莓派的时间同步/树莓派做PTP授时服务器

        这一步比较麻烦。这里主要使用的是gpsd、chronyd、ptp4l这几个软件。博主假设树莓派已经刷了官方系统,接好了各种外设。

树莓派串口配置

        由于树莓派外设一共包含两个串口,一个称之为硬件串口(/dev/ttyAMA0),一个称之为mini串口(/dev/ttyS0)。硬件串口由硬件实现,有单独的波特率时钟源,性能高、可靠。树莓派(3/4代)板载蓝牙模块,默认的硬件串口是分配给蓝牙模块使用的,而性能较差的mini串口是分配给GPIO串口 TXD0、RXD0。但是我们不能使用mini串口,因此得把硬件串口分配给GPIO串口 TXD0、RXD0。具体的方法在网上可以查到很多资源,这里推荐树莓派的官方教程。这一步做完后,接好线,然后使用串口工具cutecom就可以查到GPS发过来的串口信号了。也可以使用gpsmon对GPS信号进行解析。

开启树莓派pps-gpio

        为什么精确授时需要pps和NMEA缺一不可呢?这是因此设备处理NMEA信息(通常是串口信号)需要较长时间,处理完就已经早就错过NMEA信息所描述的那个时刻了。而PPS是一个1hz的方波,方波的上升沿可以触发设备的硬件中断功能,同时记录下这个时刻,这样处理完NMEA信息的时候,就知道对应的是哪个时刻了。开启树莓派pps-gpio功能的教程是这个。配置完成后,重启树莓派,使用ppstest去测试。

关闭树莓派ntpd功能

        因为我们需要让树莓派跟gps同步,因此需要关闭树莓派ntpd功能,避免树莓派通过网络时间同步。在命令行输入rcconf,安装提示操作就可以了,很简单。

使用gpsd和chronyd对树莓派进行授时,使用ptp4l把树莓派配置为授时服务器

        终于到重点了,这里先对这三个软件的功能做一个介绍。
        gpsd的功能是解析NMEA信息和pps信号,获取当前GPS时间,但是它没有办法给树莓派系统授时;chronyd能够给树莓派系统授时,但是需要从gpsd获取当前GPS时间,chronyd和gpsd的通讯是通过共享内存实现的,这需要对chronyd做一定的配置;ptp4l是PTP同步,即精确网络时间同步协议的软件,它需要从chronyd获取系统当前时间,然后发布到局域网,给相机和工控机授时,ptp4l与chronyd的通讯也是通过共享内存。下面分别介绍怎么配置这三个软件:

gpsd配置

        其实并不需要配置,直接开启就完事了哈哈。

sudo killall -9 gpsd chronyd
sudo gpsd -n -G /dev/ttyAMA0

        但是开启之后,需要用一些软件来测试一下有没有开启成功。如下面所示:

gpsmon
----------------------------------------------------------
/dev/ttyAMA0                  u-blox>
┌──────────────────────────┐┌─────────────────────────────────────────────────┐ or":12}
│Ch PRN  Az  El S/N Flag U ││ECEF Pos: -2324739.37m +5387509.44m +2492042.31m │ ver":"u-blox","subtype":"SW ROM CORE 3.01 (107888),HW 00080000,FWVER=SPG 3.01,PROTVER=18","activated":"20
│ 0   3 224   6  28 060f Y ││ECEF Vel:     +0.02m/s     +0.03m/s     +0.01m/s │ 1,"cycle":1.00,"mincycle":0.25}]}
│ 1   4 262  62  26 060f Y ││                                                 │ false,"timing":false,"split24":false,"pps":true}
│ 2   7 313   8  30 070f Y ││LTP Pos:  23.150042180° 113.340458154°    96.91m │
│ 3   8 208  56  28 060f Y ││LTP Vel:    0.03m/s 272.8°   0.02m/s             │
│ 4   9 303  36  28 070f Y ││                                                 │
│ 5  11   0 165  29 0710   ││Time: 51 15:33:40.00                             │
│ 6  16  21  43  28 070f Y ││Time GPS: 2150+446242.000     Day: 5             │
│ 7  18  51   3  25 040c   ││                                                 │
│ 8  21 179   8   0 010c   ││Est Pos Err  11.51m Est Vel Err   0.00m/s        │
│ 9  22 201   3   0 010c   ││PRNs:  6 PDOP:  2.6 Fix 0x03 Flags 0xdf          │
│10  26  48  26   0 010c   │└─────────────────── NAV_SOL ─────────────────────┘
│11  27  75  81  35 070f Y │┌─────────────────────────────────────────────────┐
│12  31 109  21  43 070c   ││DOP [H]  1.8 [V]  2.0 [P]  2.6 [T]  1.6 [G]  3.1 │
│13 127 256  21   0 0004   │└─────────────────── NAV_DOP ─────────────────────┘
│14 128 236  47  39 070c   │┌─────────────────────────────────────────────────┐
│15 132 223  55   0 0104   ││TOFF:  0.095508862       PPS: -0.000000278       │
└────── NAV_SVINFO ────────┘└─────────────────────────────────────────────────┘
------------------- PPS offset:  0.000000096 ------
(60) b56201063400300d991aa5640300660803df98ba24f2c8af1c20278eda0e740400000200000003000000030000002200000009010306e04a0300214c
(26) b56201041200300d991a36010901a000c500b2004c00a1004ca3
(24) b56201201000300d991aa56403006608120745000000f938
------------------- PPS offset:  0.000000368 ------
sudo ntpshmon
----------------------------------------------------------
pi@raspberrypi:~ $ sudo ntpshmmon
ntpshmmon version 1
#      Name Seen@                Clock                Real                 L Prec
sample NTP0 1616731155.261334631 1616731155.090788722 1616731155.000000000 0 -20
sample NTP1 1616731155.261480981 1616731154.999999776 1616731155.000000000 0 -30
sample NTP1 1616731156.001005646 1616731155.999999600 1616731156.000000000 0 -30
sample NTP0 1616731156.091572631 1616731156.090540716 1616731156.000000000 0 -20
sample NTP1 1616731157.000122016 1616731156.999999202 1616731157.000000000 0 -30
sample NTP0 1616731157.090672594 1616731157.090357912 1616731157.000000000 0 -20
sample NTP1 1616731158.000105988 1616731157.999998915 1616731158.000000000 0 -30
sample NTP0 1616731158.090650363 1616731158.090550734 1616731158.000000000 0 -20
sample NTP1 1616731159.000270238 1616731158.999998408 1616731159.000000000 0 -30
sample NTP0 1616731159.091943158 1616731159.090915761 1616731159.000000000 0 -20

chronyd配置

        chronyd有一个默认配置文件,修改配置文件即可。下面是整个配置文件,复制黏贴保存为conf即可。其中最后三行代码是重点,配置了跟gpsd和ptp4l的共享内存参数。

# Welcome to the chrony configuration file. See chrony.conf(5) for more
# information about usuable directives.
pool 2.debian.pool.ntp.org iburst

# This directive specify the location of the file containing ID/key pairs for
# NTP authentication.
keyfile /etc/chrony/chrony.keys

# This directive specify the file into which chronyd will store the rate
# information.
driftfile /var/lib/chrony/chrony.drift

# Uncomment the following line to turn logging on.
#log tracking measurements statistics

# Log files location.
logdir /var/log/chrony

# Stop bad estimates upsetting machine clock.
maxupdateskew 100.0

# 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

# Step the system clock instead of slewing it if the adjustment is larger than
# one second, but only in the first three clock updates.
makestep 1 3


server 0.cn.pool.ntp.org
server 1.cn.pool.ntp.org
server 2.cn.pool.ntp.org
server 3.cn.pool.ntp.org

driftfile /var/lib/chrony/drift

allow

refclock SHM 0 refid GPS precision 1e-1 offset 0.9999 delay 0.2
refclock SHM 1 refid PPS precision 1e-7
refclock SHM 2 refid PTP precision 1e-7

配置文件保存后,开启chronyd。

sudo chronyd -f /path/to/mychrony.conf
chronyc sources -v

        等待半分钟,应该会显示系统已经与pps同步,如下:

pi@raspberrypi:~ $ chronyc sources -v
210 Number of sources = 11

  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = current synced, '+' = combined , '-' = not combined,
| /   '?' = unreachable, 'x' = time may be in error, '~' = time too variable.
||                                                 .- xxxx [ yyyy ] +/- zzzz
||      Reachability register (octal) -.           |  xxxx = adjusted offset,
||      Log2(Polling interval) --.      |          |  yyyy = measured offset,
||                                \     |          |  zzzz = estimated error.
||                                 |    |           \
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
#x GPS                           0   4   377    18   -909ms[ -909ms] +/-  200ms
#* PPS                           0   4   377    18   -470ns[ -580ns] +/- 1129ns
#? PTP                           0   4     0     -     +0ns[   +0ns] +/-    0ns
^- 203.107.6.88                  2   7   377    89  -1073us[-1074us] +/-   36ms
^- ntp1.ams1.nl.leaseweb.net     3   6   377    17    -21ms[  -21ms] +/-  252ms
^? time.cloudflare.com           0   6     0     -     +0ns[   +0ns] +/-    0ns
^- 119.28.206.193                2   7   277    82    +13ms[  +13ms] +/-   42ms
^- ntp6.flashdance.cx            2   7   363    21    -10ms[  -10ms] +/-  205ms
^- 139.199.215.251               2   7   277    94  -2922us[-2923us] +/-   46ms
^- 139.199.214.202               2   7   375   221  -2571us[-2571us] +/-   26ms
^- ntp7.flashdance.cx            2   7   377    85    -22ms[  -22ms] +/-  215ms

Known Issue:有时候树莓派需要联网才能跟pps同步。。。有哪位大佬知道为啥的麻烦指导一波

PTP4l配置

        配置文件如下。其中重点是这几个参数priority1、logAnnounceInterval、logSyncInterval、time stamping、ntpshm_segment。前四者是为了保证相机能够通过PTP与树莓派时间同步(具体解释见《Camera-LIDAR_Detection_Fusion》这篇本科毕业论文,或者见Basler官方文档),最后一个是为了是ptp4l能跟chronyd通讯。

[global]
#
# Default Data Set
#
twoStepFlag		1
slaveOnly		0
# priority1		128
priority1		127
priority2		128
domainNumber		0
#utc_offset		37
clockClass		248
clockAccuracy		0xFE
offsetScaledLogVariance	0xFFFF
free_running		0
freq_est_interval	1
dscp_event		0
dscp_general		0
#
# Port Data Set
#
logAnnounceInterval	1
# logSyncInterval		0
logSyncInterval		-1
logMinDelayReqInterval	0
logMinPdelayReqInterval	0
announceReceiptTimeout	3
syncReceiptTimeout	0
delayAsymmetry		0
fault_reset_interval	4
neighborPropDelayThresh	20000000
#
# Run time options
#
assume_two_step		0
logging_level		6
path_trace_enabled	0
follow_up_info		0
hybrid_e2e		0
net_sync_monitor	0
tx_timestamp_timeout	1
use_syslog		1
# verbose			0
verbose			1
summary_interval	0
kernel_leap		1
check_fup_sync		0
#
# Servo Options
#
pi_proportional_const	0.0
pi_integral_const	0.0
pi_proportional_scale	0.0
pi_proportional_exponent	-0.3
pi_proportional_norm_max	0.7
pi_integral_scale	0.0
pi_integral_exponent	0.4
pi_integral_norm_max	0.3
step_threshold		0.0
first_step_threshold	0.00002
max_frequency		900000000
# clock_servo		pi
clock_servo ntpshm
sanity_freq_limit	200000000
# ntpshm_segment		0
ntpshm_segment 2
#
# Transport options
#
transportSpecific	0x0
ptp_dst_mac		01:1B:19:00:00:00
p2p_dst_mac		01:80:C2:00:00:0E
udp_ttl			1
udp6_scope		0x0E
uds_address		/var/run/ptp4l
#
# Default interface options
#
network_transport	UDPv4
delay_mechanism		E2E
# time_stamping		hardware
time_stamping software

tsproc_mode		filter
delay_filter		moving_median
delay_filter_length	10
egressLatency		0
ingressLatency		0
boundary_clock_jbod	0
#
# Clock description
#
productDescription	;;
revisionData		;;
manufacturerIdentity	00:00:00
userDescription		;
timeSource		0xA0

        配置文件保存后,开启ptp4l。

/usr/sbin/ethtool --set-eee eth0 eee off
/usr/sbin/ifconfig eth0 192.168.2.158
nohup /usr/sbin/ptp4l -f /path/to/myptp4l.conf -i eth0 &

树莓派到工控机的时间同步

        由于树莓派已经配置为PTP授时服务器(master),那么工控机只要配置成PTP授时客户端(slave)即可。工控机使用的是ptp4l和chronyd两个软件,ptp4l与树莓派通讯获取当前时间,并通过共享内存的方式传递给chronyd,而chronyd更改系统时间,使系统时间与树莓派同步。

更改ptp4l配置文件

        首先ptp4l的配置文件需要做一点儿修改,主要是ntpshm_segment要改为0,全部配置文件如下:

[global]
#
# Default Data Set
#
twoStepFlag		1
# slaveOnly		0
slaveOnly		1
priority1		128
priority2		128
domainNumber		0
clockClass		248
clockAccuracy		0xFE
offsetScaledLogVariance	0xFFFF
free_running		0
freq_est_interval	1
dscp_event		0
dscp_general		0
#
# Port Data Set
#
logAnnounceInterval	1
logSyncInterval		0
logMinDelayReqInterval	0
logMinPdelayReqInterval	0
announceReceiptTimeout	3
syncReceiptTimeout	0
delayAsymmetry		0
fault_reset_interval	4
neighborPropDelayThresh	20000000
#
# Run time options
#
assume_two_step		0
logging_level		6
path_trace_enabled	0
follow_up_info		0
hybrid_e2e		0
tx_timestamp_timeout	1
use_syslog		1
verbose			0
summary_interval	0
kernel_leap		1
check_fup_sync		0
#
# Servo Options
#
pi_proportional_const	0.0
pi_integral_const	0.0
pi_proportional_scale	0.0
pi_proportional_exponent	-0.3
pi_proportional_norm_max	0.7
pi_integral_scale	0.0
pi_integral_exponent	0.4
pi_integral_norm_max	0.3
step_threshold		0.0
first_step_threshold	0.00002
max_frequency		900000000
# clock_servo		pi
clock_servo ntpshm
sanity_freq_limit	200000000
ntpshm_segment		0
#
# Transport options
#
transportSpecific	0x0
ptp_dst_mac		01:1B:19:00:00:00
p2p_dst_mac		01:80:C2:00:00:0E
udp_ttl			1
udp6_scope		0x0E
uds_address		/var/run/ptp4l
#
# Default interface options
#
network_transport	UDPv4
delay_mechanism		E2E
# time_stamping		hardware
time_stamping		software
tsproc_mode		filter
delay_filter		moving_median
delay_filter_length	10
egressLatency		0
ingressLatency		0
boundary_clock_jbod	0
#
# Clock description
#
productDescription	;;
revisionData		;;
manufacturerIdentity	00:00:00
userDescription		;
timeSource		0xA0

        配置文件保存后,开启ptp4l

sudo killall -9 ptp4l chronyd
nohup sudo ptp4l -f /path/to/myptp4l.conf -i enp3s0 &

更改chronyd配置文件

        chronyd的配置文件更改如下。主要还是refclock SHM的配置。至于为啥工控机和树莓派的chronyd配置文件长得很不一样,猜测应该是不同架构的软件包配置文件有所差异吧。

#refclock SHM 0  delay 0.5 refid NEMA
refclock SHM 0 refid PTP precision 1e-7
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

server clock.fmt.he.net iburst
server clock.sjc.he.net iburst
server time.cloudflare.com iburst
server time.google.com iburst
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

        配置文件保存后,开启chronyd。静待60秒后,看chronyc sources是不是PTP时钟源。

sudo chronyd -f /path/to/chrony/mychrony.conf
sleep 60
chronyc sources

树莓派到相机的时间同步

        博主使用的是Basler工业相机,支持PTP时间同步,因此只需要使用相机的sdk开启ptp时间同步即可。这里比较麻烦的一点是,Basler的官方ROS包暂时不支持开启PTP同步(虽然已经有人提交了代码了,但是一直没有merge到主分支里面),因此需要自己根据相机的sdk撸代码了。这里推荐两份参考资料,看官们可以根据此修改一点儿官方ros包的代码。接下来,博主默认Basler相机已经能够开启PTP时间同步,且发布出来的ros topic的时间戳是相机内部时间戳!

  • https://github.com/iron-ox/pylon-ros-camera/pull/1/files
  • https://github.com/magazino/pylon_camera/pull/38/files

至此,所有设备都直接或者间接同步到GPS时间了。

激光雷达和相机的同步采集

        正如前文所说,要实现激光雷达和相机的同步采集,需要根据激光雷达的帧周期同步触发相机的拍摄,实现雷达和相机的完全帧同步。 那问题来了,触发信号从哪里来呢?答案是GPS的PPS信号!PPS是一个1HZ的方波,它的每个上升沿的时刻都是整秒时刻,误差是纳秒级的,因此是一个绝佳的触发信号。那么,如果实现了激光雷达和相机均在PPS信号上升沿的时候采集数据,且打上各自时钟的时间戳,那么就实现了我们的目标了。
        但是,由于激光雷达和相机采集数据的方式不同,我们只能近似地保证激光雷达和相机的同步采集。如下图所示,激光雷达的激光束不断地360度旋转,假设帧率为10Hz的话,那么一帧点云中采集时刻最早的一个点和采集时刻最晚的一个点时间相差100ms。而相机就不同了,相机是瞬间曝光的,因此图像里面所有像素点的采集时刻都是一样的。这就导致了我们只能近似地保证激光雷达和相机的同步采集。具体方法就是当激光束旋转到相机视野中央的时候,触发相机,这样就保证了相机视野内的点云采集时间是跟图像采集时间是近似的。为什么说是近似的呢?对于相机视野中央的那些点云,它们的采集时间是跟图像采集时间一致的,但是对于相机视野边缘的那些点云,它们的采集时间就跟图像采集时间要有一定的时间偏差了,根据相机视野的大小和点云采集帧率的不同,时间偏差可能会有5ms~20ms左右。

激光雷达和相机的内部时钟同步示意图

        理论有了,那么怎么实施呢?插句题外话,KITTI数据集是怎么保证激光雷达和相机同步采集的?那个时候,机械式激光雷达还是外部旋转式的(类似于陀螺一样),作者在激光雷达外部贴了一个光电传感器,但激光雷达旋转到一定角度时,光电传感器触发,接通了相机的触发电路,从而相机曝光。但是现在的激光雷达为了小型化,高度集成,旋转元件都在内部了,因此也就无法采取跟KITTI数据集一样的方式了。不过,聪明的激光雷达制造商想出了一个相位锁定功能,也就是输入PPS,但PPS上升沿到来时,激光雷达的激光束恰好旋转到一定的角度。那么我们反过来想一下,激光雷达的激光束旋转到一定角度时,PPS上升沿刚好到来,那么我们把PPS信号当做相机硬件触发信号,不就可以触发相机了嘛。
        于是乎,我们设置激光雷达的相位锁定角度为相机视野中央,如果相机视野朝着车辆正前方,激光雷达的坐标系也是朝着车辆正前方的话,相位锁定角度应该是0度。那么问题就这样解决了,每当激光雷达的激光束旋转到0度位置,也就是相机视野正中央,PPS上升沿刚好到来,相机也因此触发,就这样就实现了激光雷达和相机的同步采集,采集到的点云和图像分别附上采集时激光雷达和相机的内部时间戳,传输到工控机。(速腾激光雷达ROS包有一个配置参数use_lidar_time记得设置为true)。博主实验获取的时间戳如下所示:

左相机 右相机 雷达
2021-02-05 16:11:31.000003019 1612512691.000003019 2021-02-05 16:11:31.000006118 1612512691.000006118 2021-02-05 16:11:29.250094891 1612512689.250094891
2021-02-05 16:11:32.000003323 1612512692.000003323 2021-02-05 16:11:31.999999862 1612512691.999999862 2021-02-05 16:11:29.350159883 1612512689.350159883
2021-02-05 16:11:33.000005627 1612512693.000005627 2021-02-05 16:11:32.999997910 1612512692.999997910 2021-02-05 16:11:29.450208902 1612512689.450208902
2021-02-05 16:11:29.550231934 1612512689.550231934
2021-02-05 16:11:29.650233030 1612512689.650233030
2021-02-05 16:11:29.750208855 1612512689.750208855
2021-02-05 16:11:29.850153923 1612512689.850153923
2021-02-05 16:11:29.950089931 1612512689.950089931
2021-02-05 16:11:30.050039053 1612512690.050039053
2021-02-05 16:11:30.150043011 1612512690.150043011
2021-02-05 16:11:30.250089884 1612512690.250089884
2021-02-05 16:11:30.350174904 1612512690.350174904
2021-02-05 16:11:30.450262070 1612512690.450262070
2021-02-05 16:11:30.550323009 1612512690.550323009
2021-02-05 16:11:30.750334978 1612512690.750334978
2021-02-05 16:11:30.850276947 1612512690.850276947
2021-02-05 16:11:30.950206041 1612512690.950206041
2021-02-05 16:11:31.050145864 1612512691.050145864
2021-02-05 16:11:31.150121927 1612512691.150121927
2021-02-05 16:11:31.250118971 1612512691.250118971
2021-02-05 16:11:31.350147009 1612512691.350147009
2021-02-05 16:11:31.450216055 1612512691.450216055
2021-02-05 16:11:31.550293922 1612512691.550293922
2021-02-05 16:11:31.650341988 1612512691.650341988
2021-02-05 16:11:31.750362873 1612512691.750362873
2021-02-05 16:11:31.850353956 1612512691.850353956
2021-02-05 16:11:31.950303078 1612512691.950303078
2021-02-05 16:11:32.050214052 1612512692.050214052
2021-02-05 16:11:32.150120020 1612512692.150120020
2021-02-05 16:11:32.250038862 1612512692.250038862
2021-02-05 16:11:32.350008965 1612512692.350008965
2021-02-05 16:11:32.450034857 1612512692.450034857
2021-02-05 16:11:32.550119877 1612512692.550119877
2021-02-05 16:11:32.650241852 1612512692.650241852
2021-02-05 16:11:32.750367880 1612512692.750367880
2021-02-05 16:11:32.850463867 1612512692.850463867
2021-02-05 16:11:32.950510025 1612512692.950510025
2021-02-05 16:11:33.050494909 1612512693.050494909
2021-02-05 16:11:33.150435925 1612512693.150435925
2021-02-05 16:11:33.250333071 1612512693.250333071
2021-02-05 16:11:33.350236893 1612512693.350236893
2021-02-05 16:11:33.450165987 1612512693.450165987
2021-02-05 16:11:33.550113916 1612512693.550113916
2021-02-05 16:11:33.650095940 1612512693.650095940
2021-02-05 16:11:33.750138998 1612512693.750138998
2021-02-05 16:11:33.850213051 1612512693.850213051
2021-02-05 16:11:33.950275898 1612512693.950275898

        我们来分析一下上表所示的时间戳。可以看到,相机一秒钟触发一次,触发的时间点为整秒时刻(虽然时间有几微秒的偏差,但应该的PTP时间同步的误差来着),而激光雷达的采集时间是10HZ,时间戳有点儿奇怪,在一秒内的时间戳是0.05s,0.15s,0.25s,0.35s…0.85s,0.95s。需要说明的是,这是速腾激光雷达ROS包打出来的时间戳,而速腾激光雷达ROS包是以一帧点云(360度为一帧)的最后一个数据包的时间作为时间戳的。
        这里涉及到一个点云分帧的概念,即激光雷达连续地做扫描,而一帧点云一般只有360度,因此有一个点云分帧角度作为起始角度和结束角度。需要注意的是,假设我们以90度作为点云分帧的角度,那么89度和91度的点云采集时间就相差100ms了哦!明白了点云分帧角度的概念之后,我们应该设置点云分帧角度在相机视野外,这样才能保证点云和图像的同时性。博主把点云分帧角度设置为180度,相位锁定角度为0度,点云采集帧率为10Hz。因此,点云的时间戳如0.15s,其实代表着激光束扫描到180度的时刻为0.15秒,因此180度为该帧点云的结束角度,所以这个时刻其实是晚于激光束扫描到0度(也就是相位锁定角度)的时刻的,结合点云采集帧率计算,激光束扫描到0度的时刻应该是0.10秒。因此,我们在做点云和图像时间对齐时,应该把点云的时间戳减0.05秒,然后再进行对齐。
        我们来找一下表格里面时间对齐的图像和点云。以左相机为例,UTC时间1612512691.000003019这帧图像,应该对应UTC时间1612512691.050145864这帧点云。算一下时间差1612512691.050145864-0.05-1612512691.000003019=0.000142845,也就是0.1ms,达到了亚毫秒级同步了。
        至此,我们实现了激光雷达和相机内部时钟的同步,以及点云采集和图像采集的时间同步,本博文的基本目标已经完成了。

更进一步

        细心的看官肯定发现了,上面给出的解决方案,相机的采集帧率只有1Hz,而点云的采集帧率有10Hz,这在很多场景下没法使用。这时候,单片机就派上用场了。我们可以使用单片机,把PPS信号转发为任意频率、但是跟PPS信号同相位的方波,这样就可以控制相机的采集频率了。下图就展示了单片机的输入输出,把PPS信号转发为同相位的5HZ相机触发信号。具体的实现原理需要使用到中断、定时器、GPIO输入输出等功能,应该是最基础的单片机应用。博主还没有来得及实现,在这里就不献丑了。

单片机转发PPS信号示意图

参考文献

为了使博文不至于冗长,博主省略了很多细节和原理介绍,下面这些参考文献可供学习使用。
https://www.researchgate.net/publication/339630757_Camera-LIDAR_Detection_Fusion

https://connecttech.com/resource-center/kdb349-gps-time-synchronization-linux/

https://blog.csdn.net/xiaohu50/article/details/78731534

https://gpsd.gitlab.io/gpsd/gpsd-time-service-howto.html#_ptp_with_software_timestamping

https://t.codebug.vip/questions-1646062.htm

http://www.rjsystems.nl/en/2100-ntpd-garmin-gps-18-lvc-gpsd.php#rslt

https://gpsd.gitlab.io/gpsd/gpsd-time-service-howto.html#_feeding_chrony_from_gpsd

https://livox-wiki-cn.readthedocs.io/zh_CN/latest/tutorials/timestamp_sychronization.html#id14

https://wiki.alpinelinux.org/wiki/Chrony_and_GPSD

https://blog.csdn.net/ReCclay/article/details/104679944

https://www.raspberrypi.org/documentation/configuration/uart.md

https://blog.csdn.net/qishi_blog/article/details/52843696?utm_source=blogxgwz7&utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-2&spm=1001.2101.3001.4242

本博文原创,转发请注明出处!

猜你喜欢

转载自blog.csdn.net/qq_24972557/article/details/115049024