资讯详情

【CeleX5事件相机使用系列】传感器时间戳同步问题

因为我需要同时得到它event和frame,所以需要在Loop在模式下获取两个数据的时间戳。这两天折腾了很久,发现CeleX传感器缺乏相关性API,还有一些问题。在这里做记录总结和分享。

Loop模式的时序

根据手册,Loop模式下,目前处于Mode1-3的任意一个模式,且这个模式完成后切换到下一个模式。所谓“完成”,对于fullframe就是收集足够设定的图片数量,event模式,则是持续了足够的时间。

在这里插入图片描述 可根据手册使用setPictureNumber()设置图片的数量,以及 setEventDuration()设定event模型的持续时间。我不知道是芯仑硬件的问题,还是我的设置方法不正确。

Event In-Pixel Timestamp Mode的时间戳

CeleXMP有多种event但只有模式In-Pixel该模式可以输出相机收集到的事件的时间戳,而不仅仅是传输的时间戳。但在使用该模式时,芯仑的库代码强制将系统时钟上限设置为70MHz,这也让我坑了很久,导致我一直以为的是100MHz的时钟。

但问题是,

下图最左侧sys是PC时间戳,来自ROS的Time,中间的In是我把每次Mode2下一组events数据输出后,第一查看event的in_pixel_timestamp。通过比较两列数据,可以发现传感器内部时间戳比实际时间慢一个量级,而不是严格的量级(一开始:sys 4.71, In 462,结束时:sys 5.43, In 549)。我TM,谁知道这到底是什么?另外off_pixel_timestamp不是增加,每组events输出从0开始,所以不需要。 此外,需要注意的是,芯仑在输出每组事件数据时,每行从左到右依次输出,但起始线不是从0开始,而是这段时间内第一次事件发生的线,然后依次访问。在这种策略下,(例如:0us第一件事发生在100行,所以从100行输出;50us有一个事件发生在200行,但此时刚刚输出到第120行的数据,在100行us当第150行发生事件时。然后,当输出到第150行时,首先输出150行的数据,但时间戳比即将输出的200行晚)。(比如上一个loop时间生成的数据无法输出,因为它已经扫描了这条线,但它存在于缓存中。loop读取输出时)。

Full frame的时间戳

因为我需要判断,events在哪个full frame前后,所以需要得到 full frame 时间戳。但是查阅手册后,芯仑没有提供获取信息 full frame 内部时间戳API,只有一个函数getFullPicBuffer收到图像时可以获得PC的时间戳。MD,系统的时间戳我自己得不到吗?API完全没有卵用。

由于传感器内部时间戳无法获得,内部时间戳只能通过外部时间戳进行推测。但是,没有任何方法可以随时获得内部时间戳,只能通过阅读events的buf获得events生成时的时间戳。但根本不明白这个时间戳的物理含义。所以我采用了以下方法:

最终的解决方案

所有时间戳PC上ROS以时间戳为准。

full frame很容易处理,只需收到full frame时读取一下ros然后修改时间msg的header,再次发布。

而对于event比较棘手的问题有两个. event生成时间戳含义不明;2. 一组event到达时间戳不会增加。

为了解决第一个问题,我不需要知道准确的时间,只需要大致准确,所以我会event时间戳,和ros是时间戳形成比例,重新形成比例event生成时间戳插值映射到两个mode两者之间的这段时间。

只需要解决第二个问题sort就这样。计算比例也很方便。

)核心代码如下:

std::vector<EventData> eventData; pCeleX5->getEventDataVector(eventData);  // 读取数据  // 根据生成时间对每组事件进行排序,并获得内部时间戳的差PC时间戳的差值 std::sort(eventData.begin(), eventData.end(), cmp);
uint32_t sensor_ts_begin = eventData[0].tInPixelIncreasing;
uint32_t sensor_ts_end = eventData[eventData.size() - 1].tInPixelIncreasing;
double pc_ts_diff = (ros::Time::now() - g_last_events_ts).toSec();		// pc ts between two events group

// 转化为msg消息
...
for (int i = 0; i < eventData.size(); ++i){ 
        
	...
	// 计算增量(内部时间戳的增量)
	double delta = ((double)data.tInPixelIncreasing - sensor_ts_begin) / ((double)sensor_ts_end - sensor_ts_begin) * pc_ts_diff;	
	// 根据增量,重新映射事件的生成时间(内部时间戳的增量,与外部时间戳的增量的比值,确定实际的内部时间)
	e.in_pixel_timestamp = ((double)g_last_events_ts.toSec() + delta) * 1e6;		
	msg.events[i] = e;
}
g_last_events_ts = ros::Time::now();		// 重置当前loop模式的时间戳
pubEvents.publish(msg);						// 发布消息

下图左侧是发送节点,右侧是接受节点。左侧发送时,Image和Events都是对齐到了PC的系统时钟,右侧为接受节点截取的结果。同时可以注意到,由于我才用了映射的方式,使得事件的时间戳严格递增,而不会出现这一帧最早的事件比上一帧最晚的要早。 (解释一下,由于对时间戳进行了映射,所以图中的dur基本是两个Mode2之间的时间差,其中包括Mode1的一张图,还有Mode2和3的时间)

小结

  1. 想MR。想用的功能都没有,二次开发很不友好;
  2. 注意:ROS的时间戳是很大的一个数值,所以在计算时,一定要用double类型,而不能用float。我被*0.1f这种表示坑了半天,时间戳总是对不上,才发现是float精度问题。
  3. 同时要注意,EventData,celex5_msgs/Event 当中的时间戳一个是uint32,一个是uint64,而系统的时间戳是double。所以量及转换时需要注意。我也搞不懂为啥这俩还不一样。
  4. 在显示数据时,ROS_INFO/cout等方式对于很大的数显示不全,可以采用printf,但printf好久不用了:%d整数,%f浮点数,%lu是unsigned long,为uint32的定义,%llu是uint64。

微信公众号:【事件相机】,交流事件相机的相关科研与应用。欢迎大家关注

标签: 04ts传感器02ts传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台