主頁 > 軟體設計 > 激光雷達與相機時間同步問題的低成本完整解決方案

激光雷達與相機時間同步問題的低成本完整解決方案

2021-04-02 11:30:13 軟體設計

激光雷達和相機的時間同步是做自動駕駛感知方面實驗的基礎,目前網路上給出的各種解決方案都不是很完整,博主也走了不少彎路,最后探索出一個較為完整的低成本解決方案(重點是低成本,可靠性一般),這是第一次在CSDN上寫作,寫得不好還請各位看官批評指正哈哈!

文章目錄

  • 總體技術路線
  • 設備選型
  • 激光雷達和相機的內部時鐘同步
    • GPS到激光雷達的時間同步
    • GPS到樹莓派的時間同步/樹莓派做PTP授時服務器
      • 樹莓派串口配置
      • 開啟樹莓派pps-gpio
      • 關閉樹莓派ntpd功能
      • 使用gpsd和chronyd對樹莓派進行授時,使用ptp4l把樹莓派配置為授時服務器
        • gpsd配置
        • chronyd配置
        • PTP4l配置
    • 樹莓派到工控機的時間同步
      • 更改ptp4l組態檔
      • 更改chronyd組態檔
    • 樹莓派到相機的時間同步
  • 激光雷達和相機的同步采集
  • 更進一步
  • 參考文獻
  • 本博文原創,轉發請注明出處!

總體技術路線

激光雷達和相機的時間同步是多傳感器融合是先決條件,但是精確的時間同步需要涉及到軟體和硬體的相關知識,因此也是不大容易實作的,下面是目前網路上已有的資料(截止到2021.03.21):

  • 激光雷達與相機標定時的時間同步問題怎么進行解決?
  • 在自動駕駛領域,如何實作激光雷達和相機的時間同步呢?
  • 激光雷達與相機標定的時間戳同步問題
  • Camera-LIDAR_Detection_Fusion
  • 無人駕駛中和傳感器有關的事兒

其中蘇笑云大佬的回答是比較清晰,大佬提到:更徹底的解決方法是選擇可以觸發拍攝的相機(必須是硬體線控),根據激光雷達的幀周期同步觸發相機的拍攝,實作雷達和相機的完全幀同步,實作完全的毫秒級同步, 本文也是根據這個解決方案來做的,所謂激光雷達與相機時間同步其實涉及到兩個問題:一個是激光雷達和相機的內部時鐘要同步;一個是要對采集出來的點云和影像進行時間對齊,因此本文也是從這兩個方面來進行技術路線的描述的,本文的技術路線也參考了上述的論文《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.0000030192021-02-05 16:11:31.000006118 1612512691.0000061182021-02-05 16:11:29.250094891 1612512689.250094891
2021-02-05 16:11:32.000003323 1612512692.0000033232021-02-05 16:11:31.999999862 1612512691.9999998622021-02-05 16:11:29.350159883 1612512689.350159883
2021-02-05 16:11:33.000005627 1612512693.0000056272021-02-05 16:11:32.999997910 1612512692.9999979102021-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

本博文原創,轉發請注明出處!

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/271617.html

標籤:其他

上一篇:關于機器學習崗位幾個不恰實際的期望

下一篇:C#/.net API 什么是RESt?RESTFUL API 設計簡述

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more