Apollo Planning模組原始碼分析(上)
【本文篇幅較長共分為上下兩篇,此為第一部分】
阿波羅專案(https://github.com/ApolloAuto/apollo)規劃(Planning)模組位於名稱空間:apollo::planning,其作用在於構建無人車從起始點到目的地之間的路徑規劃,具體而言,就是給定導航地圖、導航路徑、當前定位點、車輛狀態(包括:位置、速度、加速度、底盤)、 周邊目標的感知及預測資訊(如交通標誌和障礙物等),規劃模組計算出可供控制模組(Controller)執行的一條安全且舒適的行駛路徑。注意,規劃模組輸出的路徑是區域性路徑而非全域性路徑。舉個簡單示例加以說明,假如無人車需從長沙智慧駕駛研究院行駛至長沙高鐵南站,首先需藉助Routing(路由尋徑)模組輸出全域性導航路徑,接下來才是規劃模組基於全域性導航路徑進行一小段、一小段具體行駛路徑的規劃。規劃模組內部結構及其與其他模組的互動示意如下圖所示。
規劃模組內部的主要類成員如下圖所示:
該模組的主要執行流程如下圖所示:
1
模組主入口
該模組的主入口為:
modules/planning/main.cc
:
APOLLO_MAIN(apollo::planning::Planning)
該巨集展開後為:
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]);
google::ParseCommandLineFlags(&argc, &argv, true);
signal(SIGINT, apollo::common::apollo_app_sigint_handler);
apollo::planning::Planning apollo_app_;
ros::init(argc, argv, apollo_app_.Name());
apollo_app_.Spin();
return 0;
}
Main函式完成以下工作:始化Google日誌工具,使用Google命令列解析工具解析相關引數,註冊接收中止訊號“SIGINT”的處理函式:apollo::common::apollo_app_sigint_handler(該函式的功能十分簡單,就是收到中止訊號“SIGINT”後,呼叫ros::shutdown()關閉ROS),建立apollo::planning::Planning物件:apollo_app_,初始化ROS環境,呼叫apollo_app_.Spin()數開始訊息處理迴圈。
int ApolloApp::Spin() {
ros::AsyncSpinner spinner(callback_thread_num_);
auto status = Init(); if (!status.ok()) {
AERROR << Name() << " Init failed: " << status;
ReportModuleStatus(apollo::hmi::ModuleStatus::UNINITIALIZED);
return -1;
}
ReportModuleStatus(apollo::hmi::ModuleStatus::INITIALIZED);
status = Start();
if (!status.ok()) {
AERROR << Name() << " Start failed: " << status;
ReportModuleStatus(apollo::hmi::ModuleStatus::STOPPED);
return -2;
}
ExportFlags();
ReportModuleStatus(apollo::hmi::ModuleStatus::STARTED);
spinner.start();
ros::waitForShutdown();
Stop();
ReportModuleStatus(apollo::hmi::ModuleStatus::STOPPED);
AINFO << Name() << " exited.";
return 0;
}
apollo::planning::Planning類的繼承關係見下圖:
基本時序如下圖所示:
現在來看ApolloApp::Spin()函式內部,首先建立ros::AsyncSpinner物件spinner,監控使用者操作。之後呼叫虛擬函式:Init()(實際呼叫apollo::planning::Planning::Init())執行初始化。
Status Planning::Init() {
hdmap_ = apollo::hdmap::HDMapUtil::BaseMapPtr();
CHECK(hdmap_) << "Failed to load map file:" << apollo::hdmap::BaseMapFile();
CHECK(apollo::common::util::GetProtoFromFile(FLAGS_planning_config_file,
&config_))
<< "failed to load planning config file " << FLAGS_planning_config_file;
if (!AdapterManager::Initialized()) {
AdapterManager::Init(FLAGS_planning_adapter_config_filename);
}
if (AdapterManager::GetLocalization() == nullptr) {
std::string error_msg("Localization is not registered");
AERROR << error_msg;
return Status(ErrorCode::PLANNING_ERROR, error_msg);
}
if (AdapterManager::GetChassis() == nullptr) {
std::string error_msg("Chassis is not registered");
AERROR << error_msg;
return Status(ErrorCode::PLANNING_ERROR, error_msg);
}
if (AdapterManager::GetRoutingResponse() == nullptr) {
std::string error_msg("RoutingResponse is not registered");
AERROR << error_msg;
return Status(ErrorCode::PLANNING_ERROR, error_msg);
}
if (AdapterManager::GetRoutingRequest() == nullptr) {
std::string error_msg("RoutingRequest is not registered");
AERROR << error_msg;
return Status(ErrorCode::PLANNING_ERROR, error_msg);
}
if (FLAGS_enable_prediction && AdapterManager::GetPrediction() == nullptr) {
std::string error_msg("Enabled prediction, but no prediction not enabled");
AERROR << error_msg;
return Status(ErrorCode::PLANNING_ERROR, error_msg);
}
if (FLAGS_enable_traffic_light &&
AdapterManager::GetTrafficLightDetection() == nullptr) { std::string error_msg("Traffic Light Detection is not registered");
AERROR << error_msg;
return Status(ErrorCode::PLANNING_ERROR, error_msg);
}
ReferenceLineProvider::instance()->Init(
hdmap_, config_.qp_spline_reference_line_smoother_config());
RegisterPlanners();
planner_ = planner_factory_.CreateObject(config_.planner_type());
if (!planner_) {
return Status(
ErrorCode::PLANNING_ERROR,
"planning is not initialized with config : " + config_.DebugString());
}
return planner_->Init(config_);
}
在apollo::planning::Planning::Init()函式內部,首先獲取高精地圖指標,之後執行介面卡管理者(AdapterManager)物件的初始化狀態,接著檢查AdapterManager裡定位介面卡(AdapterManager::GetLocalization())、底盤介面卡(AdapterManager::GetChassis()、路由尋徑響應介面卡(AdapterManager::GetRoutingResponse()、路由尋徑請求介面卡(AdapterManager::GetRoutingRequest())的初始化狀態,若啟用預測(FLAGS_enable_prediction)則繼續檢查預測介面卡(AdapterManager::GetPrediction()的初始化狀態,若啟用交通燈(FLAGS_enable_traffic_light)則繼續檢查交通燈介面卡(AdapterManager::GetTrafficLightDetection())的初始化狀態。接下來,執行參考線提供者(所謂參考線就是一條候選路徑)的初始化。此後呼叫Planning::RegisterPlanners()將RTKReplayPlanner、EMPlanner物件的建立函式註冊到計劃者工廠類物件planner_factory_中。另外通過讀取配置檔案中給定的計劃者型別,使用planner_factory_.CreateObject(config_.planner_type())動態生成所需的計劃者物件(查詢配置檔案得知,實際生成EMPlanner物件)。最後執行計劃者物件的初始化:planner_->Init(config_)
。apollo::planning::Planning::Init()
函式內,介面卡管理者的初始化過程值得深入探討:
AdapterManager::Init(FLAGS_planning_adapter_config_filename);
首先研究初始化傳入的引數:FLAGS_planning_adapter_config_filename實際上是利用Google開源庫gflags巨集:
DEFINE_string(planning_adapter_config_filename,
"modules/planning/conf/adapter.conf",
"The adapter configuration file");
定義出的一個字串變數,該變數的預設值為:”modules/planning/conf/adapter.conf”,關於該變數的描述資訊為: “The adapter configuration file”。配置檔案:modules/planning/conf/adapter.conf的內容如下:
config {
type: LOCALIZATION
mode: RECEIVE_ONLY
message_history_limit: 1
}
config {
type: CHASSIS
mode: RECEIVE_ONLY
message_history_limit: 1
}
config {
type: ROUTING_RESPONSE
mode: RECEIVE_ONLY
message_history_limit: 1
}
config {
type: ROUTING_REQUEST
mode: PUBLISH_ONLY
message_history_limit: 1
}
config {
type: PREDICTION
mode: RECEIVE_ONLY
message_history_limit: 10
}
config {
type: PLANNING_TRAJECTORY
mode: PUBLISH_ONLY
message_history_limit: 1
}
config {
type: TRAFFIC_LIGHT_DETECTION
mode: RECEIVE_ONLY
message_history_limit: 1
}
is_ros: true
該檔案表明,Planning模組配置以下幾種型別的介面卡:LOCALIZATION(僅接收)、CHASSIS(僅接收)、ROUTING_RESPONSE(僅接收)、ROUTING_REQUEST(僅釋出)、PREDICTION(僅接收)、PLANNING_TRAJECTORY(僅釋出)、TRAFFIC_LIGHT_DETECTION(僅接收),且使用ROS環境。
現在進入AdapterManager::Init(const std::string &adapter_config_filename)內部一探究竟:
void AdapterManager::Init(const std::string &adapter_config_filename) {
// Parse config file
AdapterManagerConfig configs;
CHECK(util::GetProtoFromFile(adapter_config_filename, &configs))
<< "Unable to parse adapter config file " << adapter_config_filename;
AINFO << "Init AdapterManger config:" << configs.DebugString();
Init(configs);
}
順藤摸瓜,繼續深入AdapterManager::Init(const AdapterManagerConfig &configs)內部,前面我們通過檢視配置檔案adapter.conf得知,此次配置類別為:LOCALIZATION、CHASSIS、ROUTING_RESPONSE、ROUTING_REQUEST、PREDICTION、PLANNING_TRAJECTORY、TRAFFIC_LIGHT_DETECTION。
void AdapterManager::Init(const AdapterManagerConfig &configs) {
if (Initialized()) {
return;
}
instance()->initialized_ = true;
if (configs.is_ros()) {
instance()->node_handle_.reset(new ros::NodeHandle());
}
for (const auto &config : configs.config()) {
switch (config.type()) {
// 省略不相關的配置類別
...
case AdapterConfig::CHASSIS:
EnableChassis(FLAGS_chassis_topic, config);
break;
case AdapterConfig::LOCALIZATION:
EnableLocalization(FLAGS_localization_topic, config); break;
// 省略不相關的配置類別
...
case AdapterConfig::TRAFFIC_LIGHT_DETECTION:
EnableTrafficLightDetection(FLAGS_traffic_light_detection_topic,
config);
// 省略不相關的配置類別
...
case AdapterConfig::ROUTING_REQUEST:
EnableRoutingRequest(FLAGS_routing_request_topic, config);
break;
case AdapterConfig::ROUTING_RESPONSE:
EnableRoutingResponse(FLAGS_routing_response_topic, config);
break;
case AdapterConfig::PLANNING_TRAJECTORY:
EnablePlanning(FLAGS_planning_trajectory_topic, config);
break;
case AdapterConfig::PREDICTION:
EnablePrediction(FLAGS_prediction_topic, config); break;
// 省略不相關的配置類別
...
default:
AERROR << "Unknown adapter config type!";
break;
}
上述程式碼中的FLAGS_chassis_topic、FLAGS_localization_topic、FLAGS_traffic_light_detection_topic、FLAGS_routing_request_topic、FLAGS_routing_response_topic、FLAGS_planning_trajectory_topic、FLAGS_prediction_topic是利用DEFINE_string巨集定義出來的幾個字串變數,其預設值分別為:
“/apollo/canbus/chassis”
“/apollo/localization/pose”
“/apollo/perception/traffic_light”
“/apollo/routing_request”
“/apollo/routing_response”
“/apollo/prediction”
程式碼中出現了幾個函式:
EnableChassis、
EnableLocalization、
EnableTrafficLightDetection、
EnableRoutingRequest、
EnableRoutingResponse、
EnablePlanning、
EnablePrediction,
但我們找遍AdapterManager類內部,也無法發現它們的定義,這是從哪裡冒出來的呢?其實這是巨集
REGISTER_ADAPTER(Chassis)、
REGISTER_ADAPTER(Localization)、
REGISTER_ADAPTER (TrafficLightDetection)、
REGISTER_ADAPTER(RoutingRequest)、
REGISTER_ADAPTER (RoutingResponse)、
REGISTER_ADAPTER(Planning)、
REGISTER_ADAPTER(Prediction)
的傑作。
下面以REGISTER_ADAPTER(Planning))為例進行分析:名稱空間Apollo::common::adapter內的介面卡管理者類AdapterManager使用巨集REGISTER_ADAPTER (Planning)註冊了一個PlanningAdapter類物件及與其相關的變數、函式。這個PlanningAdapter類是何方神聖?請看檔案:modules/common/adapters/message_adapters.h內的這條語句:
using PlanningAdapter = Adapter<planning::ADCTrajectory>;
原來PlanningAdapter類是模板類Adapter<planning::ADCTrajectory>的一個別名。
再來看巨集REGISTER_ADAPTER(Planning)展開後的實際程式碼,是不是找到了我們關注的函式:EnablePlanning(const std::string &topic_name, const AdapterConfig &config)?分析該函式內部可知其功能有兩個:一是訂閱接收訊息FLAGS_planning_trajectory_topic,並繫結該訊息的處理函式為PlanningAdapter::OnReceive;二是將匿名函式(或稱Lambda表示式,不熟悉的同學可查閱C++11標準)[this]() { Planning_->Observe(); }為規劃介面卡PlanningAdapter的觀察函式儲存到函式物件陣列:std::vector<std::function<void()>> observers_
內部,提供給相關程式碼呼叫。
public:
static void EnablePlanning(const std::string &topic_name, const AdapterConfig &config) { CHECK(config.message_history_limit() > 0) << "Message history limit must be greater than 0"; instance()->InternalEnablePlanning(topic_name, config); } static PlanningAdapter *GetPlanning() { return instance()->InternalGetPlanning(); } static AdapterConfig &GetPlanningConfig() { return instance()->Planningconfig_; } static bool FeedPlanningFile(const std::string &proto_file) { if (!instance()->Planning_) { AERROR << "Initialize adapter before feeding protobuf"; return false; } return GetPlanning()->FeedFile(proto_file); } static void PublishPlanning(const PlanningAdapter::DataType &data) { instance()->InternalPublishPlanning(data); } template <typename T> static void FillPlanningHeader(const std::string &module_name, T *data) { static_assert(std::is_same<PlanningAdapter::DataType, T>::value, "Data type must be the same with adapter's type!"); instance()->Planning_->FillHeader(module_name, data); } static void AddPlanningCallback(PlanningAdapter::Callback callback) { CHECK(instance()->Planning_) << "Initialize adapter before setting callback"; instance()->Planning_->AddCallback(callback); } template <class T> static void AddPlanningCallback( void (T::*fp)(const PlanningAdapter::Dssage_history_limit())); if (config.mode() != AdapterConfig::PUBLISH_ONLY && IsRos()) { Planningsubscriber_ = node_handle_->subscribe(topic_name, config.message_history_limit(), &PlanningAdapter::OnReceive, Planning_.get()); } if (config.mode() != AdapterConfig::RECEIVE_ONLY && IsRos()) { Planningpublisher_ = node_handle_->advertise<PlanningAdapter::DataType>( topic_name, config.message_history_limit(), config.latch()); } observers_.push_back([this]() { Planning_->Observe(); }); Planningconfig_ = config; } PlanningAdapter *InternalGetPlanning() { return Planning_.get(); } void InternalPublishPlanning(const PlanningAdapter::DataType &data) { /* Only publish ROS msg if node handle is initialized. */ if (IsRos()) { if (!Planningpublisher_.getTopic().empty()) { Planningpublisher_.publish(data); } else { AERROR << "Planning" << " is not valid."; } } else { /* For non-ROS mode, just triggers the callback. */ if (Planning_) { Planning_->OnReceive(data); } else { AERROR << "Planning" << " is null."; } } Planning_->SetLatestPublished(data); }ataType &data), T *obj) { AddPlanningCallback(std::bind(fp, obj, std::placeholders::_1)); } template <class T> static void AddPlanningCallback( void (T::*fp)(const PlanningAdapter::DataType &data)) { AddPlanningCallback(fp); } /* Returns false if there's no callback to pop out, true otherwise. */ static bool PopPlanningCallback() { return instance()->Planning_->PopCallback(); } private: std::unique_ptr<PlanningAdapter> Planning_; ros::Publisher Planningpublisher_; ros::Subscriber Planningsubscriber_; AdapterConfig Planningconfig_; void InternalEnablePlanning(const std::string &topic_name, const AdapterConfig &config) { Planning_.reset( new PlanningAdapter("Planning", topic_name, config.mePublishssage_history_limit())); if (config.mode() != AdapterConfig::PUBLISH_ONLY && IsRos()) { Planningsubscriber_ = node_handle_->subscribe(topic_name, config.message_history_limit(), &PlanningAdapter::OnReceive, Planning_.get()); } if (config.mode() != AdapterConfig::RECEIVE_ONLY && IsRos()) { Planningpublisher_ = node_handle_->advertise<PlanningAdapter::DataType>( topic_name, config.message_history_limit(), config.latch()); } observers_.push_back([this]() { Planning_->Observe(); }); Planningconfig_ = config; } PlanningAdapter *InternalGetPlanning() { return Planning_.get(); } void InternalPublishPlanning(const PlanningAdapter::DataType &data) { /* Only publish ROS msg if node handle is initialized. */ if (IsRos()) { if (!Planningpublisher_.getTopic().empty()) { Planningpublisher_.publish(data); } else { AERROR << "Planning" << " is not valid."; } } else { /* For non-ROS mode, just triggers the callback. */ if (Planning_) { Planning_->OnReceive(data); } else { AERROR << "Planning" << " is null."; } } Planning_->SetLatestPublished(data); }
現在重新回到ApolloApp::Spin()函式的分析。初始化完成後繼續呼叫虛擬函式:Start()((實際呼叫apollo::planning::Planning::Start()))開始執行;之後使用spinner來監聽各種訊號,並呼叫相關處理函式;最後,在收到ros::waitForShutdown()訊號後,呼叫Stop()(實際呼叫apollo::planning::Planning::Stop())完成資源清理退出。除錯資訊的輸出貫穿於整個函式過程。
2
介面卡管理者類(AdapterManager)分析
AdapterManager類不屬於規劃模組,但它是所有訊息介面卡的管理者,這裡對其作一初步分析。顧名思義,AdapterManager是所有介面卡的管理容器。什麼是介面卡,Apollo專案為什麼要使用介面卡?第一個問題很好理解,借用網上經常提到的一個例子,中國電壓標準是220V,美國電壓標準是110V,中國電器要在美國使用,必須藉助一個變壓器,這個變壓器就是介面卡。另外一個例子是,中國插座標準是扁孔,歐洲插座標準是圓孔,中國的電源插頭到歐洲使用,必須藉助一個插孔轉接器,這個轉接器也是介面卡。以上只是對介面卡的形象描述,C++中用到的介面卡,自然是來自於鼎鼎有名的GOF名著《Design Patterns: Elements of Reusable Object-Oriented Software》中提到的Adapter設計模式。關於該模式的描述,網上有一堆文章,此處不再贅述。
關於第二個問題,Apollo專案各模組內部的訊息傳遞使用Google Protocol Buffer格式,各模組之間的底層訊息傳遞目前使用ROS機制,今後可能會替換為百度自研訊息機制。不管Apollo專案各模組之間的底層訊息傳遞機制如何改變,我們必須確保各模組內部的訊息格式不變,即使用Google Protocol Buffer格式,否則會因為底層訊息傳遞機制的修改,導致各模組內部涉及訊息處理的程式碼被迫修改,這既嚴重破壞各模組程式碼的獨立性和正確性,也嚴重影響專案開發效率。這就是Apollo建立眾多Adapter及其管理者AdapterManager的根本原因。
Apollo 2.0版本目前定義了以下介面卡:
using ChassisAdapter = Adapter<::apollo::canbus::Chassis>
;using ChassisDetailAdapter = Adapter<::apollo::canbus::ChassisDetail>;
using ControlCommandAdapter = Adapter<control::ControlCommand>;
using GpsAdapter = Adapter<apollo::localization::Gps>;
using ImuAdapter = Adapter<localization::Imu>;
using RawImuAdapter = Adapter<apollo::drivers::gnss::Imu>;
using LocalizationAdapter = Adapter<apollo::localization::LocalizationEstimate>;
using MonitorAdapter = Adapter<apollo::common::monitor::MonitorMessage>;
using PadAdapter = Adapter<control::PadMessage>;
using PerceptionObstaclesAdapter = Adapter<perception::PerceptionObstacles>;
using PlanningAdapter = Adapter<planning::ADCTrajectory>;
using PointCloudAdapter = Adapter<::sensor_msgs::PointCloud2>;
using ImageShortAdapter = Adapter<::sensor_msgs::Image>;
using ImageLongAdapter = Adapter<::sensor_msgs::Image>;
using PredictionAdapter = Adapter<prediction::PredictionObstacles>;
using DriveEventAdapter = Adapter<DriveEvent>;
using TrafficLightDetectionAdapter = Adapter<perception::TrafficLightDetection>;
using RoutingRequestAdapter = Adapter<routing::RoutingRequest>;
using RoutingResponseAdapter = Adapter<routing::RoutingResponse>;
using RelativeOdometryAdapter =
Adapter<calibration::republish_msg::RelativeOdometry>;
using InsStatAdapter = Adapter<drivers::gnss::InsStat>;
using InsStatusAdapter = Adapter<gnss_status::InsStatus>;
using GnssStatusAdapter = Adapter<gnss_status::GnssStatus>;
using SystemStatusAdapter = Adapter<apollo::monitor::SystemStatus>;
using MobileyeAdapter = Adapter<drivers::Mobileye>;
using DelphiESRAdapter = Adapter<drivers::DelphiESR>;
using ContiRadarAdapter = Adapter<drivers::ContiRadar>;
using CompressedImageAdapter = Adapter<sensor_msgs::CompressedImage>;
using GnssRtkObsAdapter = Adapter<apollo::drivers::gnss::EpochObservation>;
using GnssRtkEphAdapter = Adapter<apollo::drivers::gnss::GnssEphemeris>;
using GnssBestPoseAdapter = Adapter<apollo::drivers::gnss::GnssBestPose>;
using LocalizationMsfGnssAdapter =
Adapter<apollo::localization::LocalizationEstimate>;
using LocalizationMsfLidarAdapter =
Adapter<apollo::localization::LocalizationEstimate>;
using LocalizationMsfSinsPvaAdapter =
Adapter<apollo::localization::IntegSinsPva>;
const sensor_msgs::PointCloud2& message);
注意,AdapterManager類是使用巨集DECLARE_SINGLETON(AdapterManager))定義的單例項類,獲取該類物件請使用AdapterManager::instance()。
1資料成員AdapterManager類的資料成員除了包含上述各個介面卡物件及其關聯的訊息釋出、訂閱物件外,還有如下重要成員:
1. ROS節點控制代碼(std::unique_ptr<ros::NodeHandle> node_handle_)
內建的ROS節點控制代碼。
2. 觀測回撥函式陣列(std::vector<std::function<void()>> observers_)
各個介面卡提供的回撥函式陣列。
3. 初始化標誌(bool initialized_)
記錄該類是否完成初始化。
1. Init函式
前文已描述,就是完成各類介面卡的註冊。
2. Observe函式
呼叫各介面卡提供的回撥函式獲取當前各模組的觀測資料。
3. CreateTimer函式
建立一個定時器,在定時器內定時呼叫傳入的回撥函式。例如在Planning模組Planning::Start函式中,建立一個定時器來週期性地執行規劃任務。
Status Planning::Start() {
timer_ = AdapterManager::CreateTimer(
ros::Duration(1.0 / FLAGS_planning_loop_rate), &Planning::OnTimer, this);
ReferenceLineProvider::instance()->Start();
AINFO << "Planning started";
return Status::OK();
}
3
規劃類(Planning)分析
Planning類是規劃模組的主要類,它將GPS和IMU提供的資訊作為輸入,處理後生成規劃資訊(包括路徑和速度資訊),提供給控制模組使用。
1資料成員該類資料成員包括:規劃者工廠類物件(planner_factory_)、規劃配置類物件(config_)、高精地圖類物件指標(hdmap_)、幀類物件指標(frame_)、規劃者類物件指標(planner_)、可釋出軌跡類物件指標(last_publishable_trajectory_)、計時器物件(timer_)。注意frame_、planner_和last_publishable_trajectory_使用C++標準庫的唯一性智慧指標unique_ptr包裝,其優點在於:一是不用人工管理記憶體;二是上述指標由Planning類物件專享,確保資料的唯一性。
1. 規劃者工廠類物件(apollo::common::util::Factory<PlanningConfig::PlannerType, Planner> planner_factory_)
該物件的職能是註冊所有的規劃者類(目前註冊了兩個:RTK重放規劃者RTKReplayPlanner,用於軌跡回放,無實際用途;最大期望規劃者EMPlanner,可實用),並根據配置資訊生成合適的規劃者類物件(當前配置檔案使用EMPlanner)。
2. 規劃配置類物件(PlanningConfig config_)
該物件的職能是讀取配置檔案資訊,給規劃類物件提供合適的引數。
3. 高精地圖類物件指標(hdmap::HDMap* hdmap_)
該指標用於儲存高精地圖,給規劃類物件提供導航地圖及車輛定位點等資訊。
4. 幀類物件指標(std::unique_ptr<Frame> frame_)
該指標用於儲存幀(Frame)物件,規劃類物件所有的決策都在該物件上完成。
5. 規劃者類物件指標(std::unique_ptr<Planner> planner_)
該指標用於儲存具體規劃者物件。
6. 可釋出軌跡類物件指標(std::unique_ptr<PublishableTrajectory> last_publishable_trajectory_)
該指標用於記錄規劃路徑的位置和速度資訊。
7. 計時器物件(ros::Timer timer_)
該物件用於定期監控各類操作使用者,呼叫相關處理函式。
該類主要包含三個過載函式:Init、Start、Stop(均為基類ApolloApp的虛擬函式)和幾個功能函式:InitFrame、RunOnce、Plan,其他函式不太重要,如感興趣可直接檢視原始碼。
1. Init函式
該函式完成以下工作:獲取高精地圖;讀取配置檔案獲取相關引數;使用介面卡管理者(apollo::common::adapterAdapterManager,Apollo專案內的所有模組如定位、底盤、預測、控制、規劃等全部歸其管理,類似於一個家庭管家)物件獲取規劃模組所需的定位、底盤、路徑