赞
踩
目录
2. 接口:根据架构设计抽象接口类(类中方法设为虚函数)--camera/lib/interface
4. APP类: 将lib中的类组合成基本的功能类 -- camera/app
Apollo开源代码链接:https://github.com/ApolloAuto/apollo
本文主要讲解Apollo/modules/perception/camera中的视觉感知部分的架构和算法。
概述:Apollo的架构进行了良好的接口设计,层层抽象,保证程序模块化;同时,有效地利用了多态机制,保证程序的灵活性和可扩展性。
可执行程序层 集成优化部分独立开发,主程序简洁
————————
app层 单链路实现,排pipeline、优化参数,不断修改迭代
————————
lib层 单元模块独立开发,通过多态实现不同版本的lib
主要设计和实现流程:1)架构设计 2)接口设计 3)lib库的单元模块实现 4)APP类的单链路实现 5)Tools可执行程序的单功能实现。
其中,多态机制主要通过第2~4步过程实现:
a. 定义接口基类,interface中将其成员函数声明为虚函数;
b. 继承接口基类,实现不同的lib版本;
c. 将lib组合成App类,App中定义接口类类型的指针作为数据成员,以便根据配置文件灵活调用不同版本的lib;
下面对5步设计和实现流程依次讲解。
引自:https://github.com/ApolloAuto/apollo/blob/master/docs/specs/perception_apollo_5.0.md
架构图中红色框图内部为本文讲解的视觉感知架构相关内容。
- 每一个小黑框就是一个lib模块;
- 箭头链接的一条链路就是一个app;
- 最后在app前面接入camera或离线图像,后面接上可视化或结果输出就成为可执行文件tools。
备注:
以上各个lib中的算法是该架构下的一种实作,Apollo中实现了各个lib的基本功能;开发者根据实际情况可能需要不同的实现方式以达到更好的效果,因此,开发者可以自己继承接口类实现自己的lib。
备注:以下lib代码中存在,但是架构图中未体现
•tl_preprocessor根据高精地图tfl位置投影到图像上
•motion_service & plane_motion 提供车辆位置服务
讨论:既然可以直接在可执行程序中组合lib、排单链路的pipeline,为什么还要在可执行程序和lib库之间再抽象一层APP类?
实际开发过程中,主程序(集成及优化)和单链路功能(算法开发)往往是不同的人开发和维护的。主程序希望给到自己手里的接口不要改动,即集成优化的程序员不想关注太多功能算法开发的工作;但是,对于功能算法开发的程序员做单链路开发调试过程中,需要不断重排pipeline、优化参数等。通过增加APP类层,一方面,使得APP类与可执行程序之间的接口尽量可以保持不变(一般只有Init和Update),保证主程序简洁,集成人员可以快速集成、排调度等;另一方面,功能算法开发人员在APP类中排pipeline、优化参数,并通过单元测试进行功能验证。
APP层串联起了大粒度(主程序层)和小粒度(单元模块能),通过app层一定程度地解耦了大粒度(主程序层)和小粒度(单元模块能)的开发,使得主程序和单模块都可以各自独立开发。
一方面,app层对下面的小粒度(lib层),通过多态机制调用不同版本的lib实现,保证lib单元模块的实现可以独立开发。在定义APP时,在数据成员中定义需要的lib对应的接口(基类)类型的指针,以便利用多态的机制根据实际需要使用lib的不同版本(Apollo默认定义的lib或者用户自己实现的lib)。
例如obstacle_camera_perception.h中定义了需要组合的lib指针类型的数据成员:
- std::shared_ptr<BaseObstacleTransformer> transformer_;
- std::shared_ptr<BaseObstaclePostprocessor> obstacle_postprocessor_;
- std::shared_ptr<BaseObstacleTracker> tracker_;
- std::shared_ptr<BaseFeatureExtractor> extractor_;
- std::shared_ptr<BaseLaneDetector> lane_detector_;
- std::shared_ptr<BaseLanePostprocessor> lane_postprocessor_;
- std::shared_ptr<BaseCalibrationService> calibration_service_;
从而,在使用时根据输入的配置将不同的lib组合成功能APP类。
另一方面,app层对上面的可执行程序层的接口简洁且保持不变,保证可执行程序的主函数可以独立开发和维护。APP层主要是排pipeline和调参数,app层的存在就是为修改提供方便的,所以可以尽情地优化修改app类的内容,没必要写不同版本的app类实现,虽然在Apollo5.5的APP中traffic_light_camera_perception \ lane_camera_perception \ obstacle_camera_perception均派生自接口类base_camera_perception,更多应该是保证不同APP接口的美观一致,而不是为了把APP层自身做成多态的。
有了以上APP和lib就可以准备构筑完整的工程了。tools中包含的具有一定完整功能的工程了,是用来编译具有main函数入口的可执行文件的。前面加上载入图像,中间实例化APP类实现视觉感知的核心内容,后面再加上结果可视化或结果保存\发送等,就是实现了完整的单功能。
备注:somehow, 在Apollo5.5中,tools里面的可执行文件中的视觉感知核心模块并没有使用app类,而是重新组合了lib类实现视觉感知功能,可以理解tools里面的可执行文件更像是单链路debug开发用的。
另外:
• camera/common 中为 lib 和 app 公用的类 & 函数• camera/test 单元测试(基于 google -- gtest )
在各个APP和lib的类中传递的是结构体,视觉感知算法中主要包括两个结构体:其中一个结构体定义所有的感知结果,在主函数中实例化该结构体,然后在从神经网络输出结果到各种后处理算法的整个流水线上对感知结果进行各种操作,包括赋值和修改其参数,因此,函数中传递的一般为该结构体的指针,以便在对其内容进行修改(CameraFrame *frame);另一个结构体为配置参数,类根据其参数不同进行不同操作,所以函数中的参数一般定义为const的引用。
定义在Apollo/modules/perception/camera/common/camera_frame.h中,其数据成员又包括时间戳、载入数据、目标检测结果结构体OD、车道线检测结果结构体LaneLine、交通灯检测结果结构体TrafficLight。部分代码如下:
- // timestamp
- double timestamp = 0.0;
-
- DataProvider *data_provider = nullptr;
- //.............
-
- std::vector<base::ObjectPtr> proposed_objects;
- // segmented objects
- std::vector<base::ObjectPtr> detected_objects;
- // tracked objects
- std::vector<base::ObjectPtr> tracked_objects;
- // detect lane mark info
- std::vector<base::LaneLine> lane_objects;
- // detected traffic lights
- std::vector<base::TrafficLightPtr> traffic_lights;
-
- //............
以上基本的数据成员的结构体定义在Apollo/modules/perception/base目录下,如TrafficLight的结构体(traffic_light.h)、车道线的结构体LaneLine (lane_struct.h)、OD的结构体Object(object.h)。
使用示例:Apollo/modules/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。