Apollo5.5源码解析:交通灯感知

一、概述

Apollo交通灯感知源码位置:Apollo/modules/perception/camera/lib/traffic_light

采用的交通灯感知方案总共分为三步:

  1. 首先根据高精地图+定位所提供的交通灯空间位置投影到图像上选取ROI; (preprocessor)
  2. 随后在ROI区域内进行深度学习的交通灯检测和分类;(detector:detection+recognition)
  3. 最后,进行后处理决策。(tracker,备注:该类中继承了BaseTrafficLightTracker,但实现的功能是后处理决策,并没有针对每个交通灯做ID序号的追踪,实际针对交通灯的应用需求也无需给出每个交通灯的ID序号)

二、选取ROI

第一步选取ROI不是必需的,也可以在全图上直接进行检测分类。但是,全图检测分类方案中为了保证推理的实时性,往往将原图resize成较小图后放入CNN网络,这会造成大目标变为小目标,降低检测查准率和召回率,以及分类准确率。选取ROI的优点是:节省算力、保证实时性。

三、检测分类

Apollo示例中采用两个网络分别进行检测和分类,也可以使用一个网络同时给出检测和分类结果。

四、决策后处理

这里重点讲解决策后处理。该类名为SemanticReviser,交通灯的Semantic是指交通灯的“直行、左转、右转、掉头等”;Reviser是指针对每个Semantic的交通灯,对其颜色进行修正。

该模块的作用:

  • 修正深度学习给出的个别灯颜色的误分类(a. 通过单帧内相同语义交通灯的颜色进行投票 b. 根据历史帧和当前帧中交通灯颜色信息)
  • 据帧间信息给出绿灯是否在闪烁状态

具体操作方法解析:

1. 首先,根据语义对当前帧内所有交通灯进行归类。

semantic_decision.cpp中入口函数为:

bool SemanticReviser::Track(const TrafficLightTrackerOptions &options,CameraFrame *frame)

该函数根据语义对一帧内所有交通灯进行归类。场景示例:一帧内可能存在2~4个直行灯,这些灯的颜色及闪烁状态是完全一致的。

将相同语义的所有灯归入一个SemanticTable. 随后,对每一个SemanticTable调用函数

void SemanticReviser::ReviseByTimeSeries(double time_stamp, SemanticTable semantic_table, std::vector *lights) 

2. 其次,对当前帧内相同语义SemanticTable的所有灯的颜色进行投票获得该SemanticTable对应的灯的颜色,并将少数票对应的颜色更改为投票获得的颜色结果。

 

场景示例:一帧内4个直行交通灯中3个被检测为红色,一个被误检为绿灯,则投票得到该SemanticTable颜色为红色。

在ReviseByTimeSeries函数中,调用函数

base::TLColor SemanticReviser::ReviseBySemantic(SemanticTable semantic_table, std::vector *lights)

3. 最后,根据当前帧SemanticTable和历史history_semantic_(对每个语义的SemanticTable维护一个SemanticTable类型的history_semantic_)决定是否修正当前SemanticTable的输出结果,并更新history_semantic_,同时,给出绿灯的闪烁状态。

3.1 修正当前SemanticTable输出

在利用帧间信息修改交通灯颜色时用到了一些先验知识:

  • 如果history颜色是Red,当前颜色是yellow,则将当前颜色修正为Red, 因为一般红灯之后应该是绿灯,而不是黄灯,大概率是当前帧将红灯误检为黄灯了;
  • 如果当前给出的颜色是unknown(单帧内投票时如果第一种颜色和第二种一致则设为unknow)时,直接将当前颜色设为history的颜色;
  • 如果历史帧为黑色,则需要连续给出多帧一致的颜色才将当前帧颜色更新为新的颜色,否则将当前帧颜色修改为黑色。如果当前帧为黑色,历史帧为其他颜色,则将当前帧颜色更新为历史帧颜色。

其中,修改交通灯颜色调用函数

void SemanticReviser::ReviseLights(std::vector *lights,
                                   const std::vector &light_ids,
                                   base::TLColor dst_color)

3.2 更新history_semantic_

将当前帧的时间戳更新进history_semantic_的时间戳。时间戳是一定更新的;如果根据history_semantic_颜色修正了当前SemanticTable的颜色,则history_semantic_的颜色不变;否则,将history_semantic_颜色Update为当前SemanticTable对应的颜色。

更新history_semantic_中的颜色调用函数

void SemanticReviser::UpdateHistoryAndLights(
    const SemanticTable &cur, std::vector *lights,
    std::vector::iterator *history)

3.3 判断交通灯是否是闪烁状态

在闪烁状态时,将当前帧的黑色设为历史帧灯的颜色(如黑色出现之前为绿色,则将当前帧颜色设为绿色),在SemanticTable结构体中单独设置blink状态位,并通过存储的last_dark_time_stamp和last_bright_time_stamp两个属性判断是否是闪烁状态。如果出现 亮-》黑-》亮的模式,则在第二亮出现时将blink状态为置为true。需要当前时间戳(刚出现第二次亮的状态)与last_bright_time_stamp时间间隔大于阈值(把个别帧漏检导致的亮-》黑-》亮 模式过滤掉),则为闪烁。对应逻辑为:

        case base::TLColor::TL_GREEN: //确定绿色是否在闪烁
          UpdateHistoryAndLights(semantic_table, lights, &iter);
          if (time_stamp - iter->last_bright_time_stamp > blink_threshold_s_ && //两次亮的时间间隔大于阈值
              iter->last_dark_time_stamp > iter->last_bright_time_stamp) //之前对应 亮-黑 的模式
          {  
            iter->blink = true;  
          }
          iter->last_bright_time_stamp = time_stamp;
          ADEBUG << "High confidence color " << s_color_strs[cur_color];
          break;

退出闪烁状态的逻辑:

    if (pre_color != iter->color ||   //brigth颜色发生变化
        fabs(iter->last_dark_time_stamp - iter->last_bright_time_stamp) >
            non_blink_threshold_s_)  //长时间保持bright或dark状态超过阈值 
    { 
      iter->blink = false;
    }

 

你可能感兴趣的:(自动驾驶)