Apollo 開放車輛的接口標準主要涉及到兩大部分,即線控系統和車輛系統。Apollo 對這兩者的功能指標、性能指標、安全指標進行一系列的約定并提出了相關標準。以常見的剎車和油門為例, Apollo 對這兩者的控制精度、控制力度、系統的周期時間、響應時間都有著嚴格的規定。
線控系統對指令越界保護和控制的處理等安全指標都有著明確約定以及標準化的要求。而車輛系統要求有相對穩定的CAN信號通道,同時對于車輛電源,包括電壓、功率、最大波動、輸出誤差都有一系列的規定,以夠保證在整個自動駕駛過程中電源輸出穩定。
本文由Apollo開發者社區認證布道師-阿淵撰寫,對Apollo 3.5 車輛配置方案進行了詳細講解,希望這篇文章給感興趣的同學帶來更多幫助。
以下,ENJOY
最近在研究百度無人車 Apollo 的工廠模式及車輛配置方式,有一些小心得希望和大家一起分享。
Apollo 無人駕駛平臺支持 Lincoln MKZ、WEY VV6 等來自多個 OEM 的不同車型。
Apollo 兼容的開放車型,來源: http://apollo.auto/vehicle/certificate_cn.html
眾所周知,各車廠車型的配置方式、接口、信號都各不相同。那么 Apollo 是如何兼容各個車型的呢?本文將從以下三個層次來回答這個問題。
從平臺構架上看,Apollo 借助“開放車輛認證平臺 (Open Vehicle Certificate Platform)"完成與汽車的交互,其他上層平臺無需關注底層實現。
Apollo 3.5 架構圖, 來源:https://github.com/ApolloAuto/apollo
Apollo 的平臺架構如上圖所示,Apollo 開放平臺包括了以下幾個部分:
云端服務平臺
開源軟件平臺
硬件開發平臺
開放車輛認證平臺
這里我們著重了解一下“開放車輛認證平臺”。
來源:http://apollo.auto/developer_cn.html
目前各個 OEM 廠商的大多使用 CAN 總線協議來進行車輛內部各個 ECU 節點之間的通訊。CAN 總線通訊協議中各節點的信息使用 DBC(Database Can)文件來進行來進行描述。
The DBC file describes the communication of a single CAN network. This information is sufficient to monitor and analyze the network and to simulate nodes not physically available.
DBC文件描述了單個CAN網絡的通信。 此信息足以監視和分析網絡并模擬物理上不可用的節點。[1]
各車廠的 DBC 文件定義通常并不相同,并且是嚴格保密的。為了解決開發者在開發無人駕駛系統中與車輛交互的問題,Apollo 搭建了《開放汽車認證平臺》,并提出了開放車輛認證計劃。
開放車輛認證計劃第一次在業內提出標準化的無人駕駛系統與車輛接口,透過這個計劃,車企/車輛提供商可以更方便的將車輛平臺接入到Apollo開放平臺,從而覆蓋更廣泛的無人駕駛開發者人群,加速無人駕駛能力的上車部署。[2]
該平臺作為軟硬件中間層,提出了開放車輛接口標準,定義了系統與汽車的線控接口,負責完成系統與汽車的具體交互。該平臺抽象出了與車型無關的信號作為上層算法模塊的輸入,使得上層平臺可以與底層車輛信號解耦。
Apollo 的開放車輛接口標準定義了 Apollo 需要的諸多用于控制車輛和接收反饋的信號。大體而言, Apollo 需要車企提供線控轉向、驅動、制動、檔位、駐車、燈光、雨刮控制、喇叭控制等控制及故障反饋等接口。Apollo 乘用車的線控需求具體的詳細信息可參見下列規范。
https://link.zhihu.com/?target=http%3A//apollo-homepage.bj.bcebos.com/Apollo_by_wire_requirement.xlsx
此外,根據《開放車輛認證車企認證流程》,想要接入到 Apollo 開放平臺,車企需要遵循 Apollo 的接口規范,向 Apollo 開放平臺提供對應的 DBC 文件。
Apollo 在與開放車輛的信號交互上和開放車輛配置上均使用了Protobuf。
Protocol Buffers 是一種輕便高效的結構化數據存儲格式,可以用于結構化數據串行化,或者說序列化。它很適合做數據存儲或 RPC 數據交換格式。可用于通訊協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。目前提供了 C++、Java、Python 三種語言的 API。[3]
通常車企會使用 DBC 文件來完成 CAN 信號的定義和解析, Apollo 則大量使用了 Protobuf 來進行模塊間的通信和配置,因此車企需要使用 Apollo 提供的工具基于 DBC 來生成 Apollo 可用的 Proto 文件 (如下所示)。
1//modules/canbus/proto/wey.proto 2messageWey{ 3optionalAds_shifter_115ads_shifter_115=1;//controlmessage 4optionalAds_eps_113ads_eps_113=2;//controlmessage 5optionalStatus_310status_310=3;//reportmessage 6optionalVin_resp3_393vin_resp3_393=4;//reportmessage 7optionalVin_resp2_392vin_resp2_392=5;//reportmessage 8optionalVin_resp1_391vin_resp1_391=6;//reportmessage 9optionalAds_req_vin_390ads_req_vin_390=7;//controlmessage10optionalAds1_111ads1_111=8;//controlmessage11optionalFbs2_240fbs2_240=9;//reportmessage12optionalFbs1_243fbs1_243=10;//reportmessage13optionalFbs4_235fbs4_235=11;//reportmessage14optionalFail_241fail_241=12;//reportmessage15optionalFbs3_237fbs3_237=13;//reportmessage16optionalAds3_38eads3_38e=14;//controlmessage17}
Protobuf 提供的 Codegen 工具會根據 Proto 文件中定義的變量生成可直接使用的 C++ 代碼,十分便捷。
Protobuf 提供了一種名為TextFormat的序列化格式,該格式類似于Json,清晰易懂。配合事先定義的 Proto 文件, 開發者可以輕易實現從可讀的配置文件到具體對象的實例的反射,配置文件經過反序列化后可以作為業務代碼類的輸入。這種方式使得配置變得便捷,不易出錯,且具有很好的向后兼容性。
Apollo 的代碼中大量使用了這種方式來管理配置。Apollo 激活車輛的配置文件為 modules/canbus/conf/canbusconf.pb.txt,開發者可以在這里定義車型及對應的 CAN card 的參數,開發者只需修改 "vehicle_parameter" 相應的字段,即可使 Apollo 支持對應的車型。
1#modules/canbus/conf/canbus_conf.pb.txt 2vehicle_parameter{ 3brand:LINCOLN_MKZ 4max_enable_fail_attempt:5 5driving_mode:COMPLETE_AUTO_DRIVE 6} 7 8can_card_parameter{ 9brand:ESD_CAN10type:PCI_CARD11channel_id:CHANNEL_ID_ZERO12}1314enable_debug_mode:false15enable_receiver_log:false16enable_sender_log:false
上述配置文件的參數的含義是由下面的 Proto 文件決定的。
1//modules/canbus/proto/canbus_conf.proto 2messageCanbusConf{ 3optionalapollo.canbus.VehicleParametervehicle_parameter=1; 4optionalapollo.drivers.canbus.CANCardParametercan_card_parameter=2; 5optionalboolenable_debug_mode=3[default=false]; 6optionalboolenable_receiver_log=4[default=false]; 7optionalboolenable_sender_log=5[default=false]; 8} 910//modules/canbus/proto/vehicle_parameter.proto11//Apollo支持了LINCON_MKZ,GEM,LEXUS等多種車型12messageVehicleParameter{13enumVehicleBrand{14LINCOLN_MKZ=0;15GEM=1;16LEXUS=2;17TRANSIT=3;18GE3=4;19WEY=5;20}21optionalVehicleBrandbrand=1;22optionaldoublemax_engine_pedal=2;23optionalint32max_enable_fail_attempt=3;24optionalChassis.DrivingModedriving_mode=4;25}2627//modules/drivers/canbus/proto/can_card_parameter.proto28messageCANCardParameter{29enumCANCardBrand{30FAKE_CAN=0;31ESD_CAN=1;32SOCKET_CAN_RAW=2;33HERMES_CAN=3;34}35...36}
另外要提到一點的是,Protobuf 提供了兩個版本的庫,即精簡版 ("libprotobuf-lite.so") 和 完整版 ("libprotobuf.so" )。
The "lite" library is much smaller than the full library, and is more appropriate for resource-constrained systems such as mobile phones.
精簡版體積遠小于完整版,因此更適合使用在諸如移動電話等資源受限的系統上。[4]
精簡版的 Protobuf 常用于嵌入式設備,但精簡版的庫并不支持 TextFormat 的反射功能。開發者如果想兼具代碼體積和功能的話,可以考慮自己寫一套格式化語言的反射機制,有興趣的同學可以參考《簡單的 C++ 結構體字段反射》。
Apollo Software Overview, 來源:https://github.com/ApolloAuto/apollo
從軟件實現上看,Apollo 通過CANBus模塊來實現對車輛的管理和通訊 。
CANBus 模塊接收并執行來自 Control 模塊的指令,同時收集汽車底盤的狀態,這些狀態是Apollo 抽象出的一組與車型無關的信號。Canbus 模塊處理這些狀態與各個汽車底盤信號的映射關系,隨后將這些狀態反饋回 Control 模塊。基于這樣的設計,Apollo 得以兼容多個不同的車型。
chassis.proto文件對 Apollo 抽象出的信號進行了定義,大體包括下列信息:
Chassis 信號
CANBus 模塊主要由以下兩個部件組成
Vehicle:the vehicle itself, including itscontrollerandmessage manager
CAN Client- CAN client has been moved to/modules/drivers/canbussince it is shared by different sensors utilizing the canbus protocol[5]
在這里著重介紹一下Vehicle部分。
Vehicle的Controller(modules/canbus/vehicle/vehicle_controller.h)的類圖如下(有簡化):
Vehicle Controller 類負責完成與汽車底盤的具體交互,下面對部分公有接口做一些解釋。
1/***@briefstartthevehiclecontroller.*注:該函數會在內部起一個名為"SecurityDogThreadFunc"的線程,該線程會周期性的檢*查與底盤的通訊狀況,關鍵信號是否有響應,是否有錯誤等等。*@returntrueifsuccessfullystarted.*/ 2virtualboolStart()=0; 3 4/***@briefstopthevehiclecontroller.*/ 5virtualvoidStop()=0; 6 7/***@briefcalculateandreturnthechassis.*注:該函數完成了汽車底盤信號和Apollo內部定義的底盤狀態信號的映射。*@returnsacopyofchassis.Usecopyheretoavoidmulti-threadissues.*/ 8virtualChassischassis()=0; 910/***@briefupdatethevehiclecontroller.*注:該函數負責執行來自Control模塊的具體的指令。根據指令的要求和汽車目前所處*的模式(完全自動、完全手動、自動轉向等)來為執行器的信號(檔位、油門、轉向等)進行*賦值。*@paramcommandthecontrolcommand*@returnerror_code*/11virtualcommon::ErrorCodeUpdate(constcontrol::ControlCommand&command);
Vehicle 的 MessageManager 類負責完成對具體信號的接收、發送、解析等,其類圖如下:
1//modules/drivers/canbus/can_comm/message_manager.h 2//用于指定系統向汽車底盤發送的控制型號 3template
接下來我們以 Wey VV6 車型為例,來分析 Apollo 是如何在代碼層面上完成配置任務的。
Wey 文件夾包含有如下文件:
根據 Apollo 的官方文件how_to_add_a_new_vehicle, 想要為 Apollo 添加 Wey 車型需要完成以下內容:
實現新的車輛控制器--wey_controller.cc,繼承VehicleController類
實現新的消息管理器--wey_message_manager.cc繼承MessageManager類
實現新的車輛工廠類--wey_vehicle_factory.cc, 繼承AbstractVehicleFactory類
更新配置文件
在modules/canbus/vehicle/vehicle_factory.cc中進行注冊
更新配置文件modules/canbus/conf/canbus_conf.pb.txt
通過上述方式可以增加新車型的原因在于 Apollo 的配置是基于工廠模式實現的。
工廠方法模式(Factory method pattern)是一種實現了“工廠”概念的面向對象設計模式。就像其他創建型模式一樣,它也是處理在不指定對象具體類型的情況下創建對象的問題。工廠方法模式的實質是“定義一個創建對象的接口,但讓實現這個接口的類來決定實例化哪個類。工廠方法讓類的實例化推遲到子類中進行。”[6]
Canbus 模塊中 Vehicle 相關的內容使用工廠模式抽象出了 VehicleController,MessageManager,AbstractVehicleFactory 三個接口。 Canbus 的業務代碼(canbus_component.cc)通過以上接口來操縱具體的對象,用戶無需關心具體的對象是什么,從而實現了業務邏輯和目標對象的解耦。
工廠方法模式的定義和實現的相關講解有很多,本文就不再贅述,可參考下列鏈接和書籍:
https://en.wikipedia.org/wiki/Factory_method_pattern
《設計模式:可復用面向對象軟件的基礎》
《Head First 設計模式》
Apollo 社區布道師賀志國老師曾對 Apollo 的工廠模式進行過介紹, 接下來本文會在此基礎上繼續延伸。
https://blog.csdn.net/davidhopper/article/details/79197075
Apollo 提供了一個工廠模版(modules/common/util/factory.h),該模版可支持任何類型的輸入,類圖如下:
工廠模版
Factory類包含了Register()、Unregister()、Empty()、CreateObjectOrNull()、CreateObject()等公有函數,其中Register()、Unregister()函數用于注冊和反注冊產品類,其作用與經典模式中抽象工廠接口類的功能類似,Empty()函數用于判斷當前工廠類中是否包含產品創建函數,CreateObjectOrNull()、CreateObject()函數用于創建可能包含空指針和不包含空指針的產品類對象。[7]
Factory 工廠模版維護了一個 Map 用來管理 IdentifierType 和 ProductCreator 的鍵值對,根據輸入的 IdentifierType, 模版可返回 ProductCreator 生產的產品,從而實現了從 IdentifierType 到 Product 的“映射“。
在Canbus 模塊中,工廠類為 "VehicleFactory", 該類繼承于工廠模版 "Factory" 。VehicleFactory 工廠維護了鍵值對為 VehicleParameter::VehicleBrand和AbstractVehicleFactory 的 Map。
如下所示,每新注冊一種車型,該 Map 中就會插入一條汽車品牌(VehicleBrand)和該品牌汽車生產工廠(AbstractVehicleFactory )的鍵值對。
1voidVehicleFactory::RegisterVehicleFactory(){ 2Register(apollo::common::LINCOLN_MKZ,[]()->AbstractVehicleFactory*{ 3returnnewLincolnVehicleFactory(); 4}); 5Register(apollo::common::GEM,[]()->AbstractVehicleFactory*{ 6returnnewGemVehicleFactory(); 7}); 8Register(apollo::common::LEXUS,[]()->AbstractVehicleFactory*{ 9returnnewLexusVehicleFactory();10});11Register(apollo::common::TRANSIT,[]()->AbstractVehicleFactory*{12returnnewTransitVehicleFactory();13});14Register(apollo::common::GE3,[]()->AbstractVehicleFactory*{15returnnewGe3VehicleFactory();16});17Register(apollo::common::WEY,[]()->AbstractVehicleFactory*{18returnnewWeyVehicleFactory();19});20}
當VehicleFactory類的"CreateVehicle" 方法被調用時, VehicleFactory會根據輸入的汽車品牌,在 Map 中查找并返回可以生產這種汽車的工廠 。
例如輸入汽車品牌"WEY" , VehicleFactory 會返回 WeyVehicleFactory ,WeyVehicleFactory 繼承于 AbstractVehicleFactory 。
1/**2*@briefCreatesanAbstractVehicleFactoryobjectbasedonvehicle_parameter3*@paramvehicle_parameterisdefinedinvehicle_parameter.proto4*/5std::unique_ptrCreateVehicle(6constVehicleParameter&vehicle_parameter);
AbstracVehicleFactory 工廠會產出一組適用于該品牌車型的產品即 MessageManager 和 Vehicle controller。
以 “Wey” 為例, WeyVehicleFactory 會產出 WeyMessageManager 和 WeyController 用于實現 “Wey”車型的通訊和控制。
完整的類圖如下所示:
最后對 CANBus 模塊的CanbusComponent進行介紹 。該類繼承于 " TimerComponent", 主要作用為處理來自控制模塊的控制指令,并將信號消息發送至 Can card。
CanbusComponent 的初始化函數 (init )主要完成了以下工作:
1. 讀取 CANBus 配置文件
1if(!GetProtoConfig(&canbus_conf_)){2AERROR<"Unable?to?load?canbus?conf?file:?"?<
2. 根據配置文件初始化 Can—client.
1can_client_=can_factory->CreateCANClient(canbus_conf_.can_card_parameter());
3. 根據配置文件獲取汽車工廠
1VehicleFactoryvehicle_factory;2vehicle_factory.RegisterVehicleFactory();3autovehicle_object=4vehicle_factory.CreateVehicle(canbus_conf_.vehicle_parameter());
4. 獲取該汽車工廠生產的 message_manager 和 Vehicle_contorller
1message_manager_=vehicle_object->CreateMessageManager();2...3//初始化can_receiver_和can_sender_4if(can_receiver_.Init(can_client_.get(),message_manager_.get(),5canbus_conf_.enable_receiver_log())!=ErrorCode::OK){...}6if(can_sender_.Init(can_client_.get(),canbus_conf_.enable_sender_log())!=7ErrorCode::OK){...}89vehicle_controller_=vehicle_object->CreateVehicleController();
5. 使能 Can 收發和 Vehicle_contorller
初始化完成之后,CanbusComponent 會周期性的報告車身狀態,并執行來自 Control 模塊和 Guardian 模塊的命令。
1boolCanbusComponent::Proc(){ 2//publish底盤信息 3PublishChassis(); 4if(FLAGS_enable_chassis_detail_pub){ 5//Publish底盤的細節信息 6PublishChassisDetail(); 7} 8returntrue; 9}1011//事件觸發,執行來自Control模塊的指令12voidCanbusComponent::OnControlCommand(constControlCommand&control_command){...}1314//事件觸發,執行來自Gurdian模塊的指令15voidCanbusComponent::OnGuardianCommand(16constGuardianCommand&guardian_command){17apollo::control::ControlCommandcontrol_command;18control_command.CopyFrom(guardian_command.control_command());19OnControlCommand(control_command);20}
Apollo 開放車輛認證平臺定義了系統與線控車輛的接口標準,并且從各個車型中抽象出了用于算法的與具體車型無關的信號。
在軟件模塊中, Canbus 模塊負責處理這些信號與車輛底盤信號的映射。
Apollo 以 Protobuf 為基礎使得車輛配置管理變得十分簡潔易用。
Apollo 使用抽象工廠模式,使業務邏輯得以與具體的車輛解耦。
上述方式的綜合應用,使得 Apollo 得以支持多種不同的車輛。
-
自動駕駛
+關注
關注
784文章
13812瀏覽量
166457 -
無人車
+關注
關注
1文章
302瀏覽量
36475 -
Apollo
+關注
關注
5文章
342瀏覽量
18452
原文標題:開發者說 | Apollo 3.5 車輛配置方案
文章出處:【微信號:Apollo_Developers,微信公眾號:Apollo開發者社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論