感知模块是自动驾驶技术中最重要、最基础的一环,能够为路径规划、驾驶决策提供重要的道路交通信息,它主要包括识别周围环境信息、检测静止或运动目标状态等方面。本节将围绕自动驾驶的实际应用问题,解析如何在Autoware Core Perception模块中实现交通信号灯识别(traffic light recognition)。首先进入src/autoware/core_perception/trafficlight_recognizer/launch,在该文件夹下共包含feat_proj.launch、roi_extractor.launch、traffic_llight_recognition_mxnet.launch、traffic_light_recognition_ssd.launch、traffic_light_recognition.launch等五个文件。
feat_proj
feat_proj部分的主要功能是通过一些投影变换获取信号灯的位置信息。在src/autoware/core_perception/trafficlight_recognizer/launch中,feat_proj.launch文件主要包含一些基础参数信息,如camera_id、use_path_info等,以及节点feat_proj信息,如下图1所示:
图1 feat_proj.launch文件信息
节点feat_proj
在src/autoware/core_perception/trafficlight_recognizer/nodes/feat_proj/feat_proj.cpp文件中找到主函数main(),其中包含参数初始化、加载vector_map()方法(load_point()、load_lines()、load_vectors()方法等)、并调用cameraInfoCallback()、adjust_xyCallback()、getTransform()、echSignals2()等方法来获取信号灯的位置,如下图2所示:
图2 feat_proj.cpp文件中main()函数参数信息
vector_map()方法
在src/autoware/common/libvectormap/src/vector_map.cpp中定义了相关方法的具体操作,以load_points()方法为例。如下图3所示,该方法主要是用于加载point并进行一些必要的操作,以获取点坐标信息及位置关系。
图3 vector_map()中load_points()方法参数信息
cameraInfoCallback()方法
在src/autoware/core_perception/trafficlight_recognizer/nodes/feat_proj/feat_proj.cpp文件中定义了cameraInfoCallback()方法的具体操作,其主要用于获取相机参数信息及图片相关参数,如下图4所示:
图4 cameraInfoCallback()方法参数信息
adjust_xyCallback()方法
同样在src/autoware/core_perception/trafficlight_recognizer/nodes/feat_proj/feat_proj.cpp文件中定义了adjust_xyCallback()方法的具体操作,如下图5所示,该方法主要用于获取坐标投影变换后的结果。
图5 adjust_xyCallback()方法参数信息
getTransform()方法
getTransform()方法主要是用于进行图片变换,以便获取位置和方向参数,如下图6所示:
图6 getTransform()方法参数信息
echoSignals2()方法
在src/autoware/core_perception/trafficlight_recognizer/nodes/feat_proj/feat_proj.cpp文件中还定义了echoSignals2()方法,通过一系列的投影变换获取信号灯不同角度的位置信息,如下图7所示。首先,建立服务器(server)与信号灯之间的相应关系,以获取信号灯数据;随后,调用project2()方法对位置坐标进行变换,得到相应的投影坐标;最后,利用GetSignalAngleInCameraSystem()方法计算相机坐标系下信号灯角度信息。
图7 echoSignals2()方法参数信息
project2()方法
project2()方法的返回结果是一个布尔值,该方法主要是用于获取世界坐标点在图像平面的投影变换结果,随后进一步判断点坐标与图像平面的位置关系,如下图8所示。
图8 project2()方法参数信息
GetSignalAngleInCameraSystem()方法
该方法的返回结果是一个双精度值,用于获取相机坐标系统下信号灯的角度信息。如下图9所示,首选通过ConvertDegreeToRadian()方法将vector_map进行弧度变换,获取ROS格式下的几何信息(roll、pitch、yaw等);然后,通过相机坐标系统分别对信号灯的几何信息进行转换;最后再调用ConvertRadianToDegree()方法获取信号灯角度信息。
图9 GetSignalAngleInCameraSystem()方法参数信息
ConvertDegreeToRadian()方法和ConvertRadianToDegree()方法
ConvertDegreeToRadian()和ConvertRadianToDegree()两种方法的返回结果均为双精度值,主要功能是实现角度-弧度、弧度-角度的变换过程,如下图10所示。
图10 ConvertDegreeToRadian()和ConvertRadianToDegree()方法参数信息
roi_extractor
roi_extractor部分主要是用于获取信号灯的roi区域,在src/autoware/core_perception/trafficlight_recognizer/launch中,roi_extractor.launch文件包含一些基础参数信息,如target_directory、minimum_height、similarity_threshold等,以及节点roi_extractor信息,如下图11所示:
图11 roi_extractor.launch文件信息
节点roi_extractor
如下图12所示,在src/autoware/core_perception/trafficlight_recognizer/nodes/roi_extractor/roi_extractor.cpp文件中找到主函数main(),首先对节点参数进行初始化并获取必要的参数信息(image_topic_name、target_directory_name等);随后分别调用CreateTargetDirectory()、ImageRawCallback()、ROISignalCallback()方法接收并存储信号灯roi区域图片。
图12 roi_extractor.cpp文件中main()函数参数信息
CreateTargetDirectory()方法
在src/autoware/core_perception/trafficlight_recognizer/nodes/roi_extractor/roi_extractor.cpp中定义了CreateTargetDirectory()方法的具体操作,如下图13所示,该方法通过调用MakeDirectoryTree()方法创建目录并调用CountFileNum()方法统计目录下的文件数量,最后根据所确定的目录名和文件名存储信号灯roi图片。
图13 CreateTargeDirectory()方法参数信息
MakeDirectoryTree()方法
MakeDirectoryTree()方法的功能是用于创建目录结构,如下图14所示:
图14 MakeDirectoryTree()方法参数信息
CountFileNum()方法
CountFileNum()方法的返回结果是一个整型值,用于统计目标目录下所包含的文件数,如下图15所示:
图15 CountFileNum()方法参数信息
ImageRawCallback()方法
ImageRawCallback()方法用于对图片进行处理,并保留系统时间戳数据以确保相同图片不会被重复处理,如下图16所示:
图16 ImageRawCallback()方法参数信息
ROISignalCallback()方法
ROISignalCallback()方法用于获取信号灯roi区域的位置信息,如下图17所示。该方法首先确定图片中顶部信号灯的初步位置,并提取其roi区域;随后调用CalculateSimilarity()方法计算图片的相似度,以进一步获取确定的roi信息。最后保存相应的信号的roi图片并记录时间戳信息。
图17 ROISignalCallback()方法参数信息
CalculateSimilarity()方法
CalculateSimilarity()方法返回一个双精度结果,主要是通过对比两张图片的直方图差异性来间接计算图片的相似度,如下图18所示。首先,创建对图片进行颜色空间变换并量化hue值;然后,分别计算两张图片的直方图差异性得到相似度水平。
图18 CalculateSimilarity()方法参数信息
traffic_light_recognition_mxnet
traffic_light_recognition_mxnet部分主要是通过深度学习框架mxnet[1],调用目标识别模型来解决交通信号灯识别问题。值得注意的是,在开始该部分学习之前需要安装mxnet框架并配置相关的运行环境。进入src/autoware/core_perception/trafficlight_recognizer/launch,在traffic_light_recognition_mxnet.launch文件中包含了参数列表以及节点region_tlr_mxnet信息,如下图19所示。
图19 traffic_light_recognition_mxnet.launch文件信息
节点region_tlr_mxnet
如下图20所示,在src/autoware/core_perception/trafficlight_recognizer/nodes/region_tlr_mxnet/region_tlr_mxnet.cpp文件中找到main()函数,其中包括初始化ROS节点参数,并调用类方法RegionTLRMxNetROSNode()的RunRecognition()方法进行信号灯识别等操作。
图20 region_tlr_mxnet.cpp文件中main()函数参数信息
RunRecognition()方法
RunRecognition()方法主要定义了信号灯的识别过程,如下图21所示。该方法首先调用GetROSParam()方法从ROS服务端获取参数信息;然后,调用Init()方法初始化目标识别模型参数;最后通过StartSubscribersAndPublishers()方法开启ROS节点,用于接收和发布信息。
图21 节点region_tlr_mxnet中RunRecognition()方法参数信息
GetROSParam()方法
GetROSParam()方法主要用于获取ROS节点参数信息,包括图片名(image_topic_name)、网络配置文件(network_definition_file)、预训练模型文件(pretrained_model_file)等,如下图22所示。
图22 GetROSParam()方法参数信息
Init()方法
跳转到src/autoware/core_perception/trafficlight_recognizer/nodes/region_tlr_mxnet/mxnet_traffic_light_recognizer.cpp,文件中定义了Init()方法的具体操作,该方法的主要功能是初始化网络参数并进行信号灯识别。如下图23所示,首先定义一些必要参数,包括是否使用gpu、gpu id、前向传播过程(num_input_nodes),并对输入尺寸的格式进行限制等;然后利用mxnet框架创建预测层(MXPredCreate),导入预训练模型文件和网络配置文件进行信号灯识别。
图23 Init()方法参数信息
StartSubscribersAndPublishers()方法
在src/autoware/core_perception/trafficlight_recognizer/nodes/region_tlr_mxnet/mxnet_traffic_light_recognizer.cpp文件中,StartSubscribersAndPublishers()方法用于启动ROS信息接收节点,如下图24所示。首先,启动信息接收节点,并调用ImageRawCallback()、ROISignalCallback()和SuperimposeCb()方法接收输入图片、信号灯ROI区域并对其进行处理;然后,通过信息发布节点,将信号灯状态(颜色)、信号灯位置识别结果等信息发布。
图24 StartSubscriberAndPublishers()方法参数信息
ImageRawCallback()方法
ImageRawCallback()方法主要用于对图片进行创建、复制等初步处理,如下图25所示。
图25 ImageRawCallback()方法参数信息
ROISignalCallback()方法
ROISignalCallback()方法主要用于对信号灯ROI区域进行处理,从而获取信号灯状态信息。如下图26所示,首先在图中对应信号灯ROI区域,以确定信号灯位置;然后,调用RecognizeLightState()、DetermineState()方法识别并更新信号灯状态;最后通过PublishTrafficLight()、PublishString()、PublishMarkerArray()、PublishImage()方法将识别结果发布,并保存时间戳信息。
图26 ROISignalCallback()方法参数信息
RecognizeLightState()方法
在src/autoware/core_perception/trafficlight_recognizer/nodes/region_tlr_mxnet/mxnet_traffic_light_recognizer.cpp文件中,RecognizeLightState()方法用于识别信号灯的状态。如下图27所示,该方法接收信号灯图片并调用PreProccessImage()方法对图片进行预处理;然后调用mxnet框架中预测层方法进行信号灯识别,并根据识别结果将其转为对应的信号灯状态。
图27 RecognizeLightState()方法参数信息
PreProcessImage()方法
PreProcessImage()方法主要用于对输入图片的尺寸、通道进行预处理,以适应网络输入的尺寸,如下图28所示。
图28 PreProcessImage()方法参数信息
DetermineState()方法
DetermineState()方法通过对比信号灯状态候选结果的差异性来确定最终的信号灯状态信息,如下图29所示。
图29 DetermineState()方法参数信息
PublishTrafficLight()方法
PublishTrafficLight()方法用于发布信号灯颜色识别结果,如下图30所示。
图30 PublishTrafficLight()方法参数信息
PublishString()方法
与PublishTrafficLight()方法类似,PublishString()方法以string形式发布信号灯的识别结果,所下图31所示。
图31 PublishString()方法参数信息
PublishMarkerArray()方法
PublishMarkerArray()方法流程主要如下:首先对不同颜色的RGBA颜色空间进行定义,如下图32所示;
图32 PublishMarkerArray()方法中RGBA颜色空间信息
然后,分别设置Marker命名空间、类型、位置、范围、颜色等信息,以确定MarkerArray格式的识别结果,如下图33所示;
图33 PublishMarkerArray()方法中MarkerArray格式信息
最后,将每个信号灯的Marker信息进行合成并将识别结果发布,如下图34所示。
图34 PublishMarkerArray()方法中MarkerArray合成信息
PublishImage()方法
PublishIImage()方法主要是将信号灯识别结果转为相应的标签并在图片上显示。如下图35所示,首先定义一些必要的参数,如参数label用于存放信号灯状态识别结果、参数FontFace用于定义图片添加内容的字体大小等。然后,将信号灯识别结果以对应的颜色展示在图片上,并添加标签、车道信息等;最后,发布结果图片。
图35 PublishImage()方法参数信息
SuperimposeCb()方法
SuperimposeCb()方法用于将识别结果叠加在图片上并展示,如下图36所示。
图36 SuperimposeCb()方法参数信息
traffic_light_recognition_ssd
traffic_light_recognition_ssd部分主要是通过利用SSD模型来识别交通信号灯,有关SSD模型的原理介绍可在先前小节中查看,这里侧重介绍如何Autoware平台中调用SSD模型并识别交通信号灯。进入src/autoware/core_perception/trafficlight_recognizer/launch,在traffic_light_recognition_ssd.launch文件中包含了参数列表以及节点region_tlr_ssd信息,如下图37所示。
图37 traffic_light_recognition_ssd.launch文件信息
节点region_tlr_ssd
在src/autoware/core_perception/trafficlight_recognizer/nodes/region_tlr_ssd/region_tlr_ssd.cpp文件中找到main()函数入口,其中包括初始化ROS节点参数,并调用类方法RegionTLRSSDROSNode()的RunRecognition()方法进行信号灯识别等操作,如下图38所示。
图38 region_tlr_ssd.cpp文件中main()函数参数信息
RunRecognition()方法
与上述节点region_tlr_mxnet中方法类似,这里的RunRecognition()方法同样是用于识别信号灯,如下图39所示。
图39 节点region_tlr_ssd中RunRecognition()方法参数信息
总体而言,该部分所采用的的方法与节点region_tlr_mxnet中对应部分的方法基本一致,主要流程如下:
1. 该方法首先调用GetROSParam()方法从ROS服务端获取参数信息;
2. 调用Init()方法初始化SSD模型参数;
3. 调用StartSubscribersAndPublishers()方法开启ROS信息收发节点:
(1) 信息接收节点:ImageRawCallback()和ROISignalCallback()方法接收信号灯图片并获取位置信息:
(a) ROISignalCallback()方法调用RecognizeLightState()、DetermineState()方法确定信号灯状态:
(aa) RecognizeLightState()方法调用WrapInputLayer()、Preprocess()方法对图片进行处理;
(b) ROISignalCallback()方法调用PublishTrafficLight()、PublishString()、PublishMarkerArray()等方法以特定格式发布 结果;
(2) 信息发布节点:发布信号灯状态(颜色)、识别结果等信息。
相关方法可以参考节点region_tlr_mxnet部分解析,这里不再一一赘述。
traffic_light_recognition
traffic_light_recognition部分主要用于识别交通信号灯并对识别结果进行微调,在src/autoware/core_perception/trafficlight_recognizer/launch中,traffic_light_recognition.launch文件主要包含节点region_lr、节点tl_switch及其相关参数信息,如下图40所示:
图40 traffic_light_recognition.launch文件信息
节点region_tlr
在src/autoware/core_perception/trafficlight_recognizer/nodes/region_tlr/region_tlr.cpp文件中定义了该部分所采用的具体方法。如下图41所示,在main()函数中首先定义了红、黄、绿三种颜色的HSV取值阈值,并初始化ROS节点参数;然后,调用image_raw_cb()、extractedPos_cb()、tunedResult_cb()和superimpose_cb()方法提取信号灯位置信息并对结果进行微调显示;最后,通过ROS节点将识别结果发布。
图41 region_tlr.cpp文件中main()函数参数信息
image_raw_cb()方法
image_raw_cb()方法主要用于对输入图片进行预处理,并调用putResult_inText()方法将识别结果添加到图片上显示,如下图42所示。
图42 image_raw_cb()方法参数信息
putResult_inText()方法
put_Result_inText()方法主要是将信号灯识别结果与状态标签对应起来,并在图片上添加状态标签、车道等信息,如下图43所示。
图43 putResult_inText()方法参数信息
extractedPos_cb()方法
extractedPos_cb()方法的功能与节点region_tlr_mxnet中PublishMarkerArray()方法类似,用于获取信号灯的位置。如下图44所示,首先根据识别结果确定信号灯状态信息。
图44 extractedPos_cb()方法信号灯状态信息
然后,通过设置MarkerArray参数对每个信号灯状态进行合成并发布,如下图45所示。
图45 extractedPos_cb()方法信号灯Marker信息
tunedResult_cb()方法
tunedResult_cb()方法主要通过设置HSV颜色空间阈值对信号灯状态识别结果进行微调,如下图46所示。
图46 tunedResult_cb()方法参数信息
superimpose_cb()方法
superimpose_cb()方法用于判断是否展示信号灯图片的识别结果,如下图47所示。
图47 superimpose()方法参数信息
节点tl_switch
在src/autoware/core_perception/trafficlight_recognizer/nodes/tl_switch/tl_switch.cpp文件中定义了该部分所采用的具体方法。如下图48所示,在main()函数中包含ROS节点初始化,调用并初始化类方法TLSwitch中light_color_switch()方法等部分。
图48 tl.switch.cpp文件中main()函数参数信息
TLSwitch()方法
TLSwitch()方法根据信号灯识别结果和外部系统参数,并调用camera_light_color_callbakck()、ams_light_color_callback()、reset_light_msg()和watchdog_timer()方法来转换信号灯状态,如下图49所示。
图49 TLSwitch()方法参数信息
camera_light_color_callback()方法
camera_light_color_callback()方法主要用于获取相机中颜色信息,并通过switch_color()方法更改相应颜色信息,如下图50所示。
图50 camera_light_color_callback()方法参数信息
switch_color()方法
switch_color()方法根据外部系统参数对颜色状态进行更改,并将最终状态信息发布,如下图51所示。
图51 switch_color()方法参数信息
ams_light_color_callback()方法
ams_light_color_callback()方法主要是根据外部系统信息调整颜色状态,如下图52所示。
图52 ams_light_color_callback()方法参数信息
reset_light_msg()方法
reset_light_msg()方法用于重置信号灯状态,如下图53所示。
图53 reset_light_msg()方法参数信息
watchdog_timer()方法
watchdog_timer()方法主要用于监测系统时间线程信息,并根据系统情况判断是否更改颜色状态,如下图54所示。
图54 watchdog_timer()方法参数信息
结束语
总的来说,本节内容主要介绍如何在Autoware平台开展交通信号灯识别项目,主要包括feat_proj、roi_extractor、region_tlr_mxnet、region_tlr_ssd、region_tlr和tl.switch五个节点内容,理清整个项目的流程并解析各个方法的具体操作。
参考文献
[1] MXNet: http://mxnet.incubator.apache.org/