自动驾驶时间同步分析概述--PPS/GPRMC/PTP/全域架构时间同步方案

时间同步的重要性在生活中已经充分体现。试想你因一个姑娘在酒吧和别人大打出手,并约定下周六早上九点在后海小树林里进行群体活动。为此你微信召集了在南非、印度、泰国干建筑的好兄弟。可在你如期赴约的时候,发现队友只有河畔的孤影。当你在病床上睁开双眼的时候,看到了你好兄弟们愧疚的黑黢黢的脸庞,原来他们忘记考虑时差了。

自动驾驶系统更是如此,身边的一众小弟

(域控制器、激光雷达、相机、毫米波雷达等)

哪个不是桀骜不驯、性格迥异,想要管理好他们,时间同步是基础。家族里的其他域控制器(座舱域、动力域、车身域、底盘域等)也需要实现时间同步,不然来你家看个世界杯,都不能及时分享国足进球、问鼎世界杯冠军的喜悦。

因此自动驾驶圈黑话第四期就详细介绍自动驾驶系统时间同步的前世今生。

1 时间同步背景

儿时,我们都哭过闹过让父母给我们买过儿童电子手表,但这个手表有一个缺点,每过一段时间总会和CCTV的标准时间存在偏差,因此需要手工校准。自动驾驶里的域控制器和传感器若都采用内置的硬件时钟,同样会存在不同硬件时间存在偏差的现象。试想在感知融合阶段,左侧车道有一辆时速80km/h的车辆正准备超车变道,由于没有时间同步,激光300ms前的点云数据与相机当前的图像数据融合。融合时激光点云数据置信度又高于图像数据,则决策单元误判断为后车没有超车行为,继续当前的行车状态或加速行驶,那么下一秒则可能酿成“亲人两行泪”这一悲剧。

除了硬件时钟偏差的原因,各种传感器的采样频率也不一致,当前激光典型采样频率为10HZ,相机为30fps,高精度组合导航为100HZ

没有准确的时钟同步,各传感器在哪一帧进行融合,在哪里进行插值都没法进行判断。

两个传感器即使采样频率一致,其每帧数据的采样点也一般不一致。

真正应了那句,不主动求变,那就只能生死由命富贵在天了。

除了自动驾驶域需要精确的时钟信息外,其他域相关功能同样也需要。

大屏上实时高精地图显示;驾驶员实时疲劳检测;实时的流媒体后视镜显示;车辆与路侧RSU之间的实时信息交互,无一不需要全域架构内的精确时间同步。

2 时间同步技术

要实现时间同步,首先需要一位德高望重,威名远播的的族长(时钟源)。

族长负责宗族内部的管理和各项事务的主持(进行时间同步)。

有的家族过于庞大,族长年事又高,一般会再选一名柱首(主时钟节点)及几名房长(边界时钟节点)替族长处理族里日常杂事,族员的恩怨情仇(主时钟设备对其它设备进行授时)。

时钟源

自动驾驶系统目前绝大多数标配高精度GNSS接收机,而GNSS中导航卫星内置高精度原子钟,GNSS接收机通过解算导航卫星信号,可以获得超高精度的时钟信号。所以GNSS除了广为人知的定位功能,还有一个鲜为人知但重要非凡的授时功能。

原子钟是人类目前最精确的时间测量仪器,原子在不同能级之间的移动称为“跃迁”,且由高能级跃迁到低能级时,会释放电磁波。

而对同一种原子来说,这种频率是固定的,且不受温度和压力影响,只与自身能量有关,物理学上称之为“共振频率”。物理学家通过一些物理手段,获得共振频率的准确物理值。

并以此值作为产生时间信号的基本节拍,即丈量时间的基本单位。据相关报道,北斗三号卫星上的原子钟300年才会有1s累积误差。

GNSS车载接收机在接收到大于等于4颗卫星的地方,通过解算即可获得接收机系统时间与卫星原子钟之间钟差,并通过钟差来校准自己的系统时间,这一步就是GNSS的授时功能。

至此,GNSS车载接收机已经得到上天诰命,成为这个家族最德高望重的族长了。

“两条族规”-PPS+GPRMC

GNSS接收机加冕完成后,会颁布两条族规。一条是时间周期为1s的同步脉冲信号PPS,脉冲宽度5ms~100ms;一条是通过标准串口输出GPRMC标准的时间同步报文。

同步脉冲前沿时刻与GPRMC报文的发送在同一时刻,误差为ns级别,误差可以忽略

GPRMC是一条包含UTC时间(精确到秒),经纬度定位数据的标准格式报文。

 

PPS秒脉冲为物理电平输出,接收及处理PPS信号的时间在ns级别,依旧可以忽略。

但GPRMC数据一般通过波特率9600的串口发送,发送、接收、处理时间tx在ms级别,是时间同步的关键。以下是使用PPS+GPRMC进行时间同步的原理。

(1)设备收到PPS秒脉冲信号后,将内部以晶振为时钟源的系统时间里的毫秒及以下时间清零,并由此开始计算毫秒时间。

(2)当收到GPRMC数据后,提取报文里的时、分、秒、年、月、日UTC时间。

(3)将收到秒脉冲到解析出GPRMC中UTC时间所用的时间tx,与UTC整秒时间相加,同步给系统时间,至此已完成一次时间同步。下一秒再进行相同的过程,每秒准确校准一次。

聪明的人可能已经恍然大悟,后面哪个小弟需要进行时间同步,谁做两根线接上这两个物理接口就妥了。可见你是一位自以为是的主管,一位听不进去良言的中层!

(1)PPS是一个低功率的脉冲电平信号,驱动电流少的只有0.5mA,多的也就20mA,带几个小弟还行,十几个就很困难了。

(2)PPS是无屏蔽的单线脉冲信号,十几根PPS线穿梭在车内,极易受到车内恶劣电磁环境的干扰,届时根本无法区分出是干扰脉冲还是同步脉冲。

(3)GPRMC通过RS232串口发送同步报文,RS232是一种1对1的全双工通信形式,也可以通过主从形式实现1对几数据传输。但对十几,实属罕见,只能通过试验验证到底可不可行。但至少线束工程师是打死不愿答应的。

(4)当时钟源丢失的时候,所有需要时间同步的设备都一下子没有了主心骨,每个小弟都可以自立门户,没有二当家的及时站出来,主持大局。这对功能安全要求极高的自动驾驶系统来说,根本无法接受。

高精度时间同步协议PTP

因此基于单纯的PPS和GPRMC实现整个自动驾驶系统的时间同步,具有理论可行性,但并不具有实际可操作性。

而基于网络的高精度时间同步协议PTP(Precision Time Protocol,1588 V2),同步精度可以达到亚微秒级。这对于主干网络为以太网的全域架构来说,简直是万事具备,只欠各域控制器的硬件PHY芯片支持了。

PTP是一种主从式的时间同步系统,采用硬件时间戳,因此可以大幅减少软件处理时间。

同时PTP可运行在L2层(MAC层)和L4层(UDP层),运行在L2层网络时,直接在MAC层进行报文解析,不用经过四层UDP协议栈,从而大幅减少协议栈驻留时间,进一步提高时间同步精度,对于自动驾驶系统来说非常友善。

设备中运行PTP协议的网络端口称为PTP端口,PTP主端口用来发布时间,PTP从端口用来接收时间。同时定义了三种时钟节点,边界时钟节点(BC,Boundary Clock)、普通时钟节点(OC,Ordinary Clock)和透明时钟节点(TC,Transparent clock)。

(1)边界时钟节点拥有多个PTP端口,其中一个用来同步上游设备时间,其余端口用来向下游设备发送时间。当边界时钟节点的上游时间同步设备是GNSS接收机时,此时的边界时钟节点就是一个主时钟节点(最优时钟)

(2)普通时钟节点只有一个PTP端口,用来同步上游时钟节点的时间。

(3)透明时钟,人如其名,具有多个PTP端口,收到什么时间,转发什么时间,不进行协议解析,内部不参与时间同步。

PTP通过在主从设备之间交互同步报文,并记录下报文发送时间,从而计算网络传输延迟和主从设备间时钟的偏差。

PTP定义了四条同步报文:Sync、Follow_Up、Delay_Req、Delay_Resp,精确同步过程如下。

 

(1)PTP主端口向从端口发送Sync报文,同步记录下Sync发送的时间t1。从端口收到Sync报文后,记录下收到的时间t2。

(2)紧接着主端口将t1时间放到Follow_Up报文发送给从端口,从端口收到此报文后就可以解析出t1,并由此得到第一个方程式:t1+网络延时+时钟偏差=t2。

(3)从端口向主端口发送Delay_Req报文,同步记录下Delay_Req发送的时间t3。主端口收到报文后,记录下收到的时间t4。

(4)紧接着主端口将t4时间放到Delay_Resp报文发送给从端口,从端口收到此报文后就可以解析出t4,并由此得到第一个方程式:t3+网络延时-时钟偏差=t4。

两个未知数,两个方程组,应用初中数学知识可以解出:网络延时=[(t2-t1)+(t4-t1)]/2,时钟偏差=[(t2-t1)-(t4-t3)]/2。 

3 全域架构时间同步方案

全域架构下,智驾域控制器因为直接连接GNSS接收机(或内置),而GNSS又是绝佳的时钟源,因此智驾域控制器自然而然成为主时钟节点,中央网关域控制器通过车载以太网主干网串联起其它域控制器,自然而然成为边界时钟的最佳选择,这样在时钟源丢失的时候,边界时钟节点同步主时钟节点的系统时间,仍然可以保持整个全域架构内相对时间一致。

其它域内传感器、执行器的时间同步需求,若没有,此域控制器设计成普通时钟节点即可。如有,可以设计成边界时钟,以保证无时钟源时的相对时间统一。

基于以太网设备的时间同步方案已经完善,而对于非车载以太网设备但有非常强烈同步需求的相机,我们还得特殊处理一下。

将相机设置为外触发模式,通过主控给相机外触发脉冲信号。

相机拍照时,曝光时刻也会产生脉冲信号发送给主控,主控记录此时系统时间,并将时间戳数据放到相机的图像数据里。

  • 34
    点赞
  • 170
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是基于STM32和GPS模块实现时间同步PPS检测的示例代码: 首先,需要配置STM32的UART和外部中断来接收GPS模块的NMEA协议数据和PPS信号。 ```c #include "stm32f4xx.h" #include "stdio.h" /* GPS串口定义 */ #define GPS_USART USART1 #define GPS_USART_CLK RCC_APB2Periph_USART1 #define GPS_USART_CLK_INIT RCC_APB2PeriphClockCmd #define GPS_USART_IRQn USART1_IRQn #define GPS_USART_IRQHandler USART1_IRQHandler #define GPS_USART_BAUDRATE 9600 /* GPS定位信息结构体定义 */ typedef struct { float latitude; // 纬度 float longitude; // 经度 float altitude; // 海拔高度 float speed; // 速度 float course; // 航向 int date; // 日期(YYYYMMDD) int time; // 时间(HHMMSS.SSS) } gps_info_t; /* GPS定位信息变量定义 */ gps_info_t gps_info = {0}; /* PPS信号检测变量定义 */ volatile uint32_t pps_count = 0; volatile uint32_t last_pps_time = 0; volatile uint8_t is_pps_detected = 0; /* GPS定位信息解析函数 */ void gps_parse(char *str) { float lat_degree, lat_minute, lon_degree, lon_minute; char lat_direction, lon_direction; int year, month, day, hour, minute, second; if (sscanf(str, "$GPGGA,%*f,%2d%2d.%*f,%c,%3d%2d.%*f,%c,%*d,%*d,%.1f,%*f,M,%.1f,M,,", &year, &month, &lat_degree, &lat_minute, &lat_direction, &gps_info.altitude, &gps_info.latitude) == 7) { // 解析纬度 gps_info.latitude = (float)lat_degree + lat_minute / 60.0; if (lat_direction == 'S') { gps_info.latitude = -gps_info.latitude; } if (sscanf(str, "$GPRMC,%2d%2d%2d.%*f,A,%3d%2d.%*f,%c,%4d%2d%2d,,,%*s*%*s", &hour, &minute, &second, &lon_degree, &lon_minute, &lon_direction, &year, &month, &day) == 9) { // 解析经度 gps_info.longitude = (float)lon_degree + lon_minute / 60.0; if (lon_direction == 'W') { gps_info.longitude = -gps_info.longitude; } // 解析时间 gps_info.time = hour * 10000 + minute * 100 + second; // 解析日期 gps_info.date = year * 10000 + month * 100 + day; } } } /* GPS串口接收中断处理函数 */ void GPS_USART_IRQHandler(void) { if (USART_GetITStatus(GPS_USART, USART_IT_RXNE) != RESET) { static char gps_buffer[128] = {0}; static uint8_t gps_index = 0; char ch = USART_ReceiveData(GPS_USART) & 0xff; if (ch == '$') { gps_index = 0; memset(gps_buffer, 0, sizeof(gps_buffer)); } gps_buffer[gps_index++] = ch; if (ch == '\n') { gps_parse(gps_buffer); } USART_ClearITPendingBit(GPS_USART, USART_IT_RXNE); } } /* PPS信号检测外部中断处理函数 */ void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) != RESET) { uint32_t current_time = TIM_GetCounter(TIM2); if (current_time - last_pps_time > 1000) { pps_count++; is_pps_detected = 1; } last_pps_time = current_time; EXTI_ClearITPendingBit(EXTI_Line0); } } /* 定时器2初始化函数 */ void TIM2_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF; TIM_TimeBaseStructure.TIM_Prescaler = 84 - 1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); } /* 外部中断初始化函数 */ void EXTI_Init(void) { EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0); EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } int main(void) { USART_InitTypeDef USART_InitStructure; /* GPS串口初始化 */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(GPS_USART_CLK, ENABLE); GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = GPS_USART_BAUDRATE; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(GPS_USART, &USART_InitStructure); USART_ITConfig(GPS_USART, USART_IT_RXNE, ENABLE); USART_Cmd(GPS_USART, ENABLE); /* PPS检测定时器和外部中断初始化 */ TIM2_Init(); EXTI_Init(); while (1) { if (is_pps_detected) { is_pps_detected = 0; // 在这里添加PPS信号检测后的处理代码 } // 在这里添加GPS定位信息处理代码 } } ``` 在主函数中,通过解析GPS模块发送的NMEA协议数据,可以获取到GPS定位信息。同时,使用定时器和外部中断来检测PPS信号,计算出两个PPS信号之间的时间差,从而实现时间同步。 需要注意的是,PPS信号的检测精度和稳定性对于时间同步的准确性有很大的影响,因此需要根据实际需求进行优化和调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值