因为学习的原因,最近要研究一下百度apollo自动驾驶平台的红绿灯识别模块,写下点东西作为学习的记录,apollo版本为6.0
apollo感知模块的总体架构如图所示,我们看到在红绿灯模块中用到了6mm和12mm的前置摄像头,这个6mm指的是焦距为6毫米,6mm是广角镜头,拍摄范围比较大,而12mm是长焦距摄像头,拍摄的距离比较远,在后面我们可以知道,同一个时间点只以一个摄像头的图片为依据
下面这张是展现更多细节的模块图
apollo总体的感知模块如图所示,红绿灯模块相关的流程在最上面一行,通过图片预处理,神经网络识别,以及后处理过程,最终输出识别的结果
红绿灯相关的代码模块大部分都在apollo/modules/perception/camera下面,解释一下每个文件夹的大致内容:
代码位置apollo/modules/perception/base/traffic_light.h
一般情况下,红绿灯有红,黄,绿这三种状态,不过考虑到红绿灯会有闪烁的或者坏掉的情况,定义了第4种颜色黑色,并且有可能所有的摄像头都无法拍摄到红绿灯的位置,所以还要定义一个未知颜色,所以在这个枚举类中,我们看到一共定义了5种红绿灯的颜色
enum class TLColor {
TL_UNKNOWN_COLOR = 0,
TL_RED = 1,
TL_YELLOW = 2,
TL_GREEN = 3,
TL_BLACK = 4,
TL_TOTAL_COLOR_NUM = 5
};
预处理阶段的输入数据有四种,分别是:
输出数据为:
预处理相关的代码均在apollo/modules/perception/camera/lib/traffic_light/preprocessor文件夹下
每个红绿灯拥有唯一id,并且边界上的四个点表示一个红绿灯,每个点都是世界坐标系中的3维坐标点
一个信号灯的记录方式大致上如下面的代码所示,当有汽车的定位信息后,4个边界点可以通过查询高精度地图获取
signal info:
id {
id: "xxx"
}
boundary {
point { x: ... y: ... z: ... }
point { x: ... y: ... z: ... }
point { x: ... y: ... z: ... }
point { x: ... y: ... z: ... }
}
3维世界坐标系中的边界点随后被投射到每个摄像头图像的2维坐标系,最后会选择具有最长的焦距且能够看到所有信号灯的摄像头图片作为输出图像。投射到该图像上的信号边界盒将作为输出的边界盒。
之后会将摄像头id和缓存图片存储在队列中
因为没有必要时时刻刻去检测红绿灯,毕竟计算资源有限,而且红绿灯变化也是周期性的,所以系统设定了一个时间间隔去,每隔一段时间去缓存队列中获取图片信息,被选取的图像会被输送到处理阶段,不适合的图片会被丢弃。
处理阶段有四个部分
前面经过预处理阶段的投射,在图片上得到了一个投射框,但是得到的投射框不是完全可靠的,所以要通过投射的信号灯位置计算的一个更大的兴趣区域(Region of Interest ROI)被用来确定信号灯精确的边界盒。
信号灯检测(detect)是一个常规的卷积神经网络检测任务,它接收带有ROI信息的图像作为输入数据,顺序输出边界盒。输出结果中的信号灯数量可能多于输入数据。
红绿灯检测相关的代码调用在apollo/modules/perception/camera/lib/traffic_light/detector/detection/detection.cc中
Apollo会根据输入信号灯的位置、形状及检测的评分选择合适的信号灯(这部分选择的逻辑在https://github.com/ApolloAuto/apollo/blob/master/modules/perception/camera/lib/traffic_light/detector/detection/select.cc中,大致为计算2D平面中框出的红绿灯的中心点,以及投影框的中心点,利用公式计算出一个GaussianScore,然后GaussianScore占0.7的权重,置信度占0.3的权重,最终计算出一个总的分数来进行比较)。如果CNN在ROI内找不到任何的信号灯,则输入数据中的信号灯将被标记为未知,且跳过剩下的两个步骤。
红绿灯识别任务的目的是识别红绿灯的颜色,该任务使用常规的卷积神经网络完成,相关的调用代码在apollo/modules/perception/camera/lib/traffic_light/detector/recognition/recognition.cc中
识别模块接受的输入是带有ROI信息的图像和一组边界盒信息作为输入数据,输出的是一个四维向量,分别表示每个边界盒是黑色、红色、黄色和绿色的概率,当且仅当概率足够大时,有最大概率的类别会被识别为信号灯的状态。否则信号灯状态被设置为未知,表示状态未确定
在识别阶段,因为遮挡或者红绿灯闪烁问题,输出的状态可能不是真正的状态,所以要对对应的结果进行修正
修正相关的逻辑在apollo/modules/perception/camera/lib/traffic_light/tracker/semantic_decision.cc中
修正分为很多种情况
第一,在ReviseBySemantic函数中修正了识别多个红绿灯的情况,如果有多个红绿灯出现,那么将会识别每个红绿灯的颜色,某个颜色出现,那么这个颜色计数就会加1,如果存在这个最多的数量是唯一的,那么就输出这个颜色,如果有两种及两种以上的颜色计数并列第一,那么会输出未知
第二,如果识别的结果是红色或者绿色,那么就会直接输出。如果接收到黑色或者未知,修正器会检测状态保存列表。如果信号灯状态已经确定持续了一段时间,那么将保存的状态输出。否则将黑色或者未知输出。
第三,因为时间顺序关系的存在,黄色只会在绿色之后红色之前出现,所以为了安全的考虑,在绿色出现之前任何红色之后的黄色都会被设置为红色。
在一次红绿灯图片检测和识别的流程中,一共会用到两个神经网络模型,分别是红绿灯检测以及红绿灯识别,检测用于在ROI中画出红绿灯的边界盒,红绿灯识别用于检测边界盒中红绿灯的颜色信息,两者在apollo中使用的都是caffe卷积神经网络模型
红绿灯检测神经网络配置信息位置
apollo/modules/perception/production/data/perception/camera/models/traffic_light_detection/detection.pt
红绿灯识别神经网络配置信息位置
apollo/modules/perception/production/data/perception/camera/models/traffic_light_recognition/recognition_caffe.pt