1. 程式人生 > >Apollo Planning模組原始碼分析(上)

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_) 
記錄該類是否完成初始化。

2重要成員函式

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_) 
該物件用於定期監控各類操作使用者,呼叫相關處理函式。

2重要成員函式

該類主要包含三個過載函式:Init、Start、Stop(均為基類ApolloApp的虛擬函式)和幾個功能函式:InitFrame、RunOnce、Plan,其他函式不太重要,如感興趣可直接檢視原始碼。 


1. Init函式 
該函式完成以下工作:獲取高精地圖;讀取配置檔案獲取相關引數;使用介面卡管理者(apollo::common::adapterAdapterManager,Apollo專案內的所有模組如定位、底盤、預測、控制、規劃等全部歸其管理,類似於一個家庭管家)物件獲取規劃模組所需的定位、底盤、路徑