开发者说|Apollo 源码分析感知篇(五):车道线检测基本流程
下面是由社区开发者—Frank909提供的文章,之前讲到了Apollo 6.0代码中如何进行红绿灯检测,这篇文章介绍另外一个感知任务:车道线检测。
相关的文件路径整理如下:
因为有了之前红绿灯检测代码分析的经验,我们自然能够知道感知任务是先从它的 Component开始。
车道线检测的Component是lane_detection_component.cc路径:
modules/perception/onboard/component
梳理代码可以得到基础的流程框架:
1、initConfig
InitConfig从本地目录中读取Proto文件,然后配置变量。那么,如何知道从哪里读取呢?在Dag目录下有配置。
我们只需找到lane_detection_component.config文件。
主要定义了输入输出Channel,Frame缓存数量,默认的相机离地物理高度。
2、initSensorInfo
if (!EXEC_ALL_FUNS(LaneDetectionComponent, this,
LaneDetectionComponent::init_func_arry_)) {
return false;
}
猜测应该是批量初始化数组列表中的函数,同时又查看了init_func_arry_的定义。
将函数指针全存储在数组当中,然后通过一个命令统一初始化。回到 InitSensor的逻辑,主要是检查相机数量是不是2个。Apollo只支持2个相机,多一个少一个都不行。检测相机数量后,又初始化一个Sensormanger,然后再获取图像的宽高,Frame Id信息就完成了。
3、initAlgorithmPlugin
4、initCameraFrames
这个方法中,会初始化CameraFrames容量为20,猜测是为了进行车道线的缓存及进行算法的跟踪。然后,针对每个相机初始化Data Provider,并设置相机内参、外参、相机高度,最终得到针孔模型,针孔模型是为了进行坐标变换。
5、initProjectMatrix 和 initMotionService
这2个方法用来初始化投影矩阵和监听Motion Service,Motion Service用来接收车辆运动信息。
6、initCameraListeners
给每一个相机绑定一个OnReceiveImage回调函数,然后创建一个CameraReader。
这部分代码和红绿灯检测没有什么不同,当通道中有图片,OnReceiveImage将被触发,触发之后进行相应的算法逻辑。
7、onReceiveImage
Apollo中数据传输并不一定能保证有序传输,有时会收到一些过期信息,在车道线检测中,这种数据将不会进行处理,Apollo代码注释需要保证Monotoic。OnReceiveImage 核心代码是调用InternalProc,所以我们需要看看这部分代码在做什么。其实也很简单,获取世界到相机坐标转换矩阵和相机到世界转换矩阵。在线标定,得到Pitch和Eight结果。然后调用camera_lane_pipeline_的Perception方法,最后发送结果。接下来我们注意力将转到camera_lane_pipeline_中,前面讲到,它是最核心的算法实现。
看名字方可获知,LaneCameraPerception中最重要的3个成员变量是
lane_detector
lane_postprocessor_
calibration_service_
猜测,车道线检测前需要在线标定,然后进行检测,最后对检测的结果进行后续处理生成最终的车道线结果。
我们阅读它的Perception代码,可以得到这样的流程图:
其实整个过程非常的清晰,主要是对Detector和Postprocessor进行车道线检测和后期处理,如果重点不是在算法实现而是在算法集成上,关注到这一层面就可以。
当然,如果我们想了解更多的细节,比如车道线如何表示,如何存储,就需要我们更深入整理代码。
1、CameraFrame 及 LaneLine 表示
过往的文章分析红绿灯检测时提到过Apollo将检测到的信息存放在CameraFrame这个数据结构当中,车道线检测也是一样的。
我们需要关注Base::LaneLine这个数据结构,在CameraFrame中它是用Vector存在的。
如何定义一条车道线可以很简单,也可以很复杂。
Apollo 中车道线相关的数据还是很多的。
LaneLineType,车道线类型。
Apollo 中只定义了 4 种车道线类型:
白虚线
白实线
黄虚线
黄实线
并且,当车辆正在变道时,可能出现车道线在车辆中间位置,又或是车道中间长长的箭头标志被定义为EGO_CENTER。
LaneLineCubicCurve,车道线表达式。
车道可能是直的,也可能是弯曲的,用三次曲线表达式就可以拟合车道线。
EndPoints,端点
这个按照描述应该是物理位置,车道线的两端。
2、LaneDetector
在LaneCameraPerception::Init中有这样一段代码:
Apollo支持不同的车道线检测模型,具体采用哪个模型需通过配置文件提前指定,这个文件是Lane.pt。
我们可以看到采用的是dark_scnn模型。
SCNN是2018年提出的模型,S是Spatial的意思,最大的Ideal在于提出了有效利用空间关系,毕竟车道线不同于其它目标,它在特定方向是细长且连续性较高的。
Detector的工作非常简单,就是进行图像推理,然后给出所有像素,每个像素是车道线标志的概率值。
所以,可视化这一过程,可以用Opencv将Net Output的结果生成概率图。
3、postprocessor
既然Detector的工作量非常简单,那么主要工作便是集成在Postprocessor身上。我们先来看Process2D的过程。
主要的工作就是筛选合格的车道线标志,拟合,通过位置关系设置车道线类型,但代码非常复杂,本篇文章不再讲述,后面将用一篇文章来做详细分析。
Process2D之后还有Process3D函数,主要借助投影关系做车道线的物理世界坐标拟合。
从红绿灯检测到车道线检测,可以看到算法并不是很难,但需要注意细节,如何高效应对现实场景并不是仅仅复现论文上的算法就可以。本文简单讲解了车道线的基本流程,理顺了大致思路,但由于篇幅问题,很多细节还没有讲明白,后面将专门写文章进行分析。
*Frank909 微信公众号
https://mp.weixin.qq.com/s/t9tpwI8fHLuQE084AjRIXA
以上是"Apollo 源码分析感知篇(五):车道线检测基本流程"的全部内容,更多话题讨论、技术交流可以扫描下方二维码添加『Apollo小哥哥』为好友,进开发者交流群。