天天看點

apollo代碼學習2.2——深度解析(control)

每次遇見複雜的事情總是在先尋找一種簡單明了的方式進行研究,用一種淺顯易懂的方式來表達。

 今天繼續apollo代碼中control子產品的總結。

 好了,還是先看下總架構圖吧:

apollo代碼學習2.2——深度解析(control)

回憶下上次的代碼總結。如下:

int ApolloApp::Spin() {
  ros::AsyncSpinner spinner(callback_thread_num_);         ///開消息線程
  auto status = Init();                                    ///子產品初始化(由子類具體重寫的)
  if (!status.ok()) {
    AERROR << Name() << " Init failed: " << status;
    return -1;
  }
  status = Start();                                        ///子產品開啟(由子類具體重寫的)
  if (!status.ok()) {
    AERROR << Name() << " Start failed: " << status;
    return -2;
  }
  ExportFlags();                                           ///輸出一些flag參數
  spinner.start();                                         ///消息線程開啟
  ros::waitForShutdown();                                  ///消息循環處理并檢測關閉
  Stop();                                                  ///退出(由子類具體重寫的)
  AINFO << Name() << " exited.";
  return 0;
}
           

 可以說百度的apollo代碼架構非正常範,從上面的代碼結構來看,control子產品就是包括:開消息線程---->初始化子產品---->開啟---->輸出flag參數---->消息處理開啟---->循環處理并檢測關閉---->關閉。但是有些步驟是比較複雜的,上次總結僅僅針對初始化,那麼今天,開始總結下開始子產品。

 開始子產品主要的流程在檔案control.cc中。下面來看。

///control子產品開啟
Status Control::Start() {
  // set initial vehicle state by cmd
  // need to sleep, because advertised channel is not ready immediately
  // simple test shows a short delay of 80 ms or so
  AINFO << "Control resetting vehicle state, sleeping for 1000 ms ...";                      
  usleep(1000 * 1000);                                                                ///先休眠一會

  // should init_vehicle first, let car enter work status, then use status msg
  // trigger control

  AINFO << "Control default driving action is "                                               
        << DrivingAction_Name(control_conf_.action());                                ///檢視驅動模式
  pad_msg_.set_action(control_conf_.action());                                        ///設定踏闆驅動模式

  timer_ = AdapterManager::CreateTimer(
      ros::Duration(control_conf_.control_period()), &Control::OnTimer, this);        ///啟用定時器做消息處理-間隔為0.01s

  AINFO << "Control init done!";                                                               

  common::monitor::MonitorBuffer buffer(&monitor_);                                   ///日志緩存
  buffer.INFO("control started");

  return Status::OK();
}
           

上面的start()函數中主要講一下開啟事件定時器,                                                                                                                                        AdapterManager::CreateTimer(ros::Duration(control_conf_.control_period()), &Control::OnTimer, this);

定時器時間間隔為control_period=0.01s,OnTimer将指向具體事件。

void Control::OnTimer(const ros::TimerEvent &) {
  double start_timestamp = Clock::NowInSeconds();                            ///擷取目前開始時刻

  ControlCommand control_command;                                            ///聲明一個指令類

  Status status = ProduceControlCommand(&control_command);                    ///産生指令
  AERROR_IF(!status.ok()) << "Failed to produce control command:"                              
                          << status.error_message();

  double end_timestamp = Clock::NowInSeconds();                               ///擷取結束時刻

  if (pad_received_) {
    control_command.mutable_pad_msg()->CopyFrom(pad_msg_);
    pad_received_ = false;
  }                                                                            ///将産生的新指令移送至緩存

  const double time_diff_ms = (end_timestamp - start_timestamp) * 1000;        ///計算産生指令所用的時間
  control_command.mutable_latency_stats()->set_total_time_ms(time_diff_ms);
  ADEBUG << "control cycle time is: " << time_diff_ms << " ms.";
  status.Save(control_command.mutable_header()->mutable_status());

  SendCmd(&control_command);                                                   ///發送指令
}
           

 相關注釋已經寫在了代碼中,就不一行一行解釋了。上面的代碼段中主要包含産生控制指令接口。

         ProduceControlCommand(&control_command);這個函數稍微長一點,但還是一些檢查處理資訊。如下:

///一下為control子產品計算控制指令
Status Control::ProduceControlCommand(ControlCommand *control_command) {
  Status status = CheckInput();                                                ///檢查所需輸入信号是否正常
  // check data
  if (!status.ok()) {
    AERROR_EVERY(100) << "Control input data failed: "                          
                      << status.error_message();
    estop_ = true;
  } else {
    Status status_ts = CheckTimestamp();                                        ///檢查時間撮
    if (!status_ts.ok()) {
      AERROR << "Input messages timeout";
      estop_ = true;
      status = status_ts;
    }
  }

  // check estop
  estop_ = estop_ || trajectory_.estop().is_estop();

  // if planning set estop, then no control process triggered
  if (!estop_) {
    if (chassis_.driving_mode() == Chassis::COMPLETE_MANUAL) {
      controller_agent_.Reset();                              ///控制器進行複位。
      AINFO_EVERY(100) << "Reset Controllers in Manual Mode";
    }

    auto debug = control_command->mutable_debug()->mutable_input_debug();
    debug->mutable_localization_header()->CopyFrom(localization_.header());
    debug->mutable_canbus_header()->CopyFrom(chassis_.header());
    debug->mutable_trajectory_header()->CopyFrom(trajectory_.header());
   
      ///控制子產品的具體算法在controller_agent_中,這裡相當于調用一個控制器接口。
    Status status_compute = controller_agent_.ComputeControlCommand(
        &localization_, &chassis_, &trajectory_, control_command);

    if (!status_compute.ok()) {
      AERROR << "Control main function failed"
             << " with localization: " << localization_.ShortDebugString()
             << " with chassis: " << chassis_.ShortDebugString()
             << " with trajectory: " << trajectory_.ShortDebugString()
             << " with cmd: " << control_command->ShortDebugString()
             << " status:" << status_compute.error_message();
      estop_ = true;
      status = status_compute;
    }
  }
        
    /**
     * 以下代碼可以解釋為程式運作異常,estop_為false,程式強制将控制輸出量置為0,即讓車輛原地停止。
     */
  if (estop_) {
    AWARN_EVERY(100) << "Estop triggered! No control core method executed!";                  
    // set Estop command
    control_command->set_speed(0);
    control_command->set_throttle(0);
    control_command->set_brake(control_conf_.soft_estop_brake());
    control_command->set_gear_location(Chassis::GEAR_DRIVE);
  }
  // check signal
  ///将車輛控制信号中的軌迹決策存儲起來。      
  if (trajectory_.decision().has_vehicle_signal()) {
    control_command->mutable_signal()->CopyFrom(
        trajectory_.decision().vehicle_signal());
  }
  return status;
}
           

ProduceControlCommand代碼段先要檢查下資料通道是否正常輸入資料,再檢查資料幀的時間搓。這裡需要注意的是,代碼段中會檢查兩個資訊資料接受的狀态,不僅僅是control自身資料接收情況,還包括上遊子產品中軌迹輸出的狀态。通過estop_參數來判斷車輛的駕駛模式(手動還是自動)。當然正常情況下肯定是自動喽,前面的程式隻要沒有異常都會很自然設定成為自動駕駛,後面就開始自動駕駛的代碼,但是假若車輛在運作當中出現異常,代碼中也進行了相關設定,将控制指令中的目标參數均置0。

if (estop_) {
    AWARN_EVERY(100) << "Estop triggered! No control core method executed!";                  
    // set Estop command
    control_command->set_speed(0);
    control_command->set_throttle(0);
    control_command->set_brake(control_conf_.soft_estop_brake());
    control_command->set_gear_location(Chassis::GEAR_DRIVE);
  }
           

其實上面這段代碼我們主要看的是下面的代碼

if (!estop_) {
.........
}
           

 這部分主要是對控制器進行操作,當設定好驅動模式後,就調用Status status_compute = controller_agent_.ComputeControlCommand( &localization_, &chassis_, &trajectory_, control_command );這個接口進行計算。這個函數調用的是算法層面的代碼,具展現在先不介紹了,後續補充。

 到此, Status status = ProduceControlCommand(&control_command);的流程就算結束了,我們繼續回到定時器函數往下看代碼。

 計算完控制指令,會将pad_received_資訊儲存起來。最後将計算資料釋出出去。

SendCmd(&control_command);
           

具體如下:

void Control::SendCmd(ControlCommand *control_command) {
  // set header
  AdapterManager::FillControlCommandHeader(Name(), control_command);  ///添加控制指令幀的頭資訊

  ADEBUG << control_command->ShortDebugString();                      ///列印資訊
  if (FLAGS_is_control_test_mode) {
    ADEBUG << "Skip publish control command in test mode";
    return;
  }                                                                    ///若是在測試模式下,将不釋出控制指令
  AdapterManager::PublishControlCommand(*control_command);             ///釋出控制指令
}
           

 這段代碼也很容易了解了。好了,今天就先總結到這裡,其實開始步驟還沒有總結完,下一次再接着總結。

本文僅僅針對子產品start()步驟進行了簡單梳理,下一篇會接着總結。歡迎關注。

剛剛學習apollo,難免寫的不準确,歡迎大家指正。