开始本文之前,先介绍几个基本概念。 1. 相机特性-曝光和读出 相机获取一帧图像分为曝光和读出两个阶段。相机使用的传感器不同,相机的曝光时间和读出时间的重叠关系也有所不同,分为交叠曝光非交叠曝光两种。交叠曝光和非交叠曝光相比,交叠曝光可以减少曝光时间对出图时间的影响。
  • 非交叠曝光是指当前帧的曝光和读出都完成后,再进行下一帧的曝光和读出。非交叠曝光帧周期大于曝光时间与帧读出时间的和。v2-e05e9d2c605b8148a7e0b2afb925c29b_r                                                                              内触发模式非交叠曝光
  • 交叠曝光是指当前帧的曝光和前一帧的读出过程有重叠,即前一帧读出的同时,下一帧已经开始曝光。交叠曝光帧周期小于等于曝光时间与帧读出时间的和。v2-e05e9d2c605b8148a7e0b2afb925c29b_r    内触发模式非交叠曝光
  • 对!上一段就是为了告诉你:后文叙述中无论当前帧曝光时间和上一帧的读出时间是否重叠都不要惊讶。
2. 图像采集:触发模式(外部输入) 相机的触发模式分为内触发模式和外触发模式 2 种。
  • 内触发模式:相机通过设备内部给出的信号采集图像。
  • 外触发模式:相机通过外部给出的信号采集图像。外部信号可以是软件信号,也可以是硬件信号,主要包含软触发和硬件触发。外触发模式如图:
v2-e05e9d2c605b8148a7e0b2afb925c29b_r

外触发模式

软触发:触发信号由软件发出(也可以利用相机SDK提供的API接口进行软触发)。

硬件触发:外部设备通过相机的 I/O 接口与相机进行连接,触发脉冲信号由外部设备给到相机进行采图。其实是直接对相机内部寄存器进行读写。下图是海康相机的电源IO的6-pin线缆: v2-3569c70ce6b19a5b178010651d1abef3_720w   v2-50916bbf35176954c9cd85426388d963_720w   v2-c90f02d1a1d36cc9d5889c7468306a85_r

海康相机电源及IO接口(6-pin Hirose)

      其中,海康相机有1个光耦隔离输入Line0+,1个可配置输入输出Line2+,可选择一个配置为输入信号。 3. 触发输出 相机触发输出信号为开关信号,可用于控制报警灯、光源、PLC等外部设备。触发输出信号可通过Strobe 信号实现。 相机的曝光发生时,会立即输出一个Strobe信号,该信号为高电平有效。后文我们主要使用该信号对Lidar等其他传感器进行硬触发。这里有一个Strobe的预输出的概念。Strobe信号早于曝光生效。其工作原理为延迟曝光先进行Strobe输出。该功能可应用于响应比较慢的外部设备。Strobe预输出时序如图所示。(后面会讲为啥要延迟曝光)v2-e05e9d2c605b8148a7e0b2afb925c29b_r

Strobe信号预输出时序

现在我们回到正题吧,现在讲起来就很快了。 要同步相机和激光雷达的时间戳主要有三个方式,硬触发、软触发、软触发+硬触发。下面我以手绘示意图的形式一一介绍。 先说硬触发吧。一个MCU产生脉冲信号对三个传感器设备进行硬触发。 v2-27de24f6cad2e8bb6d5c3b0cffa1602a_r 对于软触发+硬触发来说,可以先用相机SDK的API对一个相机进行软触发,然后利用相机的外触发信号Strobe对雷达和相机等其他传感器进行硬触发。 v2-510f801fd087c7466bc9ced9386ca3a5_r 这里需要注意一个问题,如果进行软触发的第一个相机在曝光的同时发出Strobe信号,其他被硬触发的传感器总归是晚了一步,不能完全同步。因此引出了之前Strobe的预输出的概念,即先进行Strobe输出再延迟曝光。 配置该模式时注意四点:
  1. 上升沿or下降沿的触发方式;
  2. 有效电平宽度,电平幅值,你要知道能触发Lidar的最低幅值;
  3. Strobe预输出
  4. 两边电平信号是否一样,3v or 5v是否需要升压...最后讲讲不被推荐的软触发。首先调用API操作相比于硬触发(对传感器内部寄存器直接进行读写操作)明显是慢了,执行第二句命令API(2)前API(1)就已经花了些时间。
    //读取lidar和image数据的线程1
    while(1)
    {
    API(1); //软触发第一个sensor
    API(2); //软触发第二个sensor
    //假设脉冲周期为0.5s
    }
    //处理数据线程2
    for(i=0;i<nimage;i++)
    {  
        t1 = getTickCount();
    //部分程序有互斥锁
        t2 = getTickCount();
        Time = (t2 - t1) / getTickFrequency(); //当前帧处理时间
        sleep(1/fps-Time);
    }
    如上图示意,如果处理单帧数据的时间超过0.5s,线程1就会读到下一帧数据,就会导致线程2数据混乱。因此线程2单帧处理时间需要小于0.5s,且每帧需要等待(1/fps-当前帧处理时间)。