欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

apollo2.5源码解析之运行流程

程序员文章站 2022-07-12 11:46:52
...

"The "apollo_app" build target defines the abstract class ApolloApp, which is implemented by all modules, as well as the macro APOLLO_MAIN, used to launch each module."

上面是apollo2.5文档中关于common模块的apollo_app部分的介绍,因此我们主要来分析ApolloApp和APOLLO_MAIN,两者都定义在modules/common/apollo_app.h文件中。

  • ApolloApp

ApolloApp是用于定义Apollo应用程序接口的基本模块类, Apollo应用程序持续运行,直到被SIGINT信号或ROS停止, Apollo中的许多基本组件如定位和控制等,都是Apollo应用程序的例子。ApolloApp是一个抽象基类,定义了各个模块App类的公共接口。

class ApolloApp {
 public:
  /**
   * @brief module name. It is used to uniquely identify the app.
   */
  virtual std::string Name() const = 0;

  /**
   * @brief this is the entry point of an Apollo App. It initializes the app,
   * starts the app, and stop the app when the ros has shutdown.
   */
  virtual int Spin();

  /**
   * The default destructor.
   */
  virtual ~ApolloApp() = default;

  /**
   * @brief set the number of threads to handle ros message callbacks.
   * The default thread number is 1
   */
  void SetCallbackThreadNumber(uint32_t callback_thread_num);

 protected:
  /**
   * @brief The module initialization function. This is the first function being
   * called when the App starts. Usually this function loads the configurations,
   * subscribe the data from sensors or other modules.
   * @return Status initialization status
   */
  virtual apollo::common::Status Init() = 0;

  /**
   * @brief The module start function. Apollo app usually triggered to execute
   * in two ways: 1. Triggered by upstream messages, or 2. Triggered by timer.
   * If an app is triggered by upstream messages, the Start() function usually
   * register a call back function that will be called when an upstream message
   * is received. If an app is triggered by timer, the Start() function usually
   * register a timer callback function.
   * @return Status start status
   */
  virtual apollo::common::Status Start() = 0;

  /**
   * @brief The module stop function. This function will be called when
   * after ros::shutdown() has finished. In the default APOLLO_MAIN macro,
   * ros::shutdown() is called when SIGINT is received.
   */
  virtual void Stop() = 0;

  /** The callback thread number
   */
  uint32_t callback_thread_num_ = 1;

 private:
  /**
   * @brief Export flag values to <FLAGS_log_dir>/<name>.flags.
   */
  void ExportFlags() const;
};

首先,来说明一下三个比较重要的纯虚函数接口:

Init():模块初始化函数,这是应用程序启动时调用的第一个函数,通常此函数会加载配置和订阅来自传感器或其他模块的数据。
Start():模块启动函数,Apollo应用程序通常可以以两种方式被触发执行。一是由上游消息触发,二是由定时器触发。
如果应用程序由上游消息触发,则Start()函数通常会注册一个回调函数,该函数将在收到上游消息时被调用。
如果应用程序由计时器触发,则Start()函数通常会注册计时器回调函数。
Stop():模块停止函数,当ros::shutdown()完成后,将调用此函数。在默认的APOLLO_MAIN宏中,当捕获到SIGINT信号时调用ros::shutdown()。

接着,来着重分析一下Spin()函数:

int ApolloApp::Spin() {
  auto status = Init();
  if (!status.ok()) {
    AERROR << Name() << " Init failed: " << status;
    return -1;
  }

  std::unique_ptr<ros::AsyncSpinner> spinner;
  if (callback_thread_num_ > 1) {
    spinner = std::unique_ptr<ros::AsyncSpinner>(
        new ros::AsyncSpinner(callback_thread_num_));
  }

  status = Start();
  if (!status.ok()) {
    AERROR << Name() << " Start failed: " << status;
    return -2;
  }
  ExportFlags();
  if (spinner) {
    spinner->start();
  } else {
    ros::spin();
  }
  ros::waitForShutdown();
  Stop();
  AINFO << Name() << " exited.";
  return 0;
}

Spin()函数是模块节点的入口,用于初始化、启动、当ros关闭时停止模块节点,一个模块节点也就是一个Apollo应用程序。Spin()函数内部调用Init()、Start()和Stop()函数完成模块节点的实现,此函数一般不会被各个模块App类重写,也就是使用基类ApolloApp的实现。

  • APOLLO_MAIN

APOLLO_MAIN宏的定义如下:

#define APOLLO_MAIN(APP)                                       \
  int main(int argc, char **argv) {                            \
    google::InitGoogleLogging(argv[0]);                        \
    google::ParseCommandLineFlags(&argc, &argv, true);         \
    signal(SIGINT, apollo::common::apollo_app_sigint_handler); \
    APP apollo_app_;                                           \
    ros::init(argc, argv, apollo_app_.Name());                 \
    apollo_app_.Spin();                                        \
    return 0;                                                  \
  }

首先,使用signal函数注册SIGINT信号与信号处理函数apollo_app_sigint_handler,apollo_app_sigint_handler函数在程序捕获到SIGINT信号后会调用ros::shutdown()函数关闭当前模块节点。接着,实例化一个当前模块APP类对象apollo_app_,然后apollo_app_的模块名称会传入ros::init()函数中初始化当前模块节点。最后,调用apollo_app_的基类部分的Spin()函数。apollo_app_sigint_handler函数代码如下:

void apollo_app_sigint_handler(int signal_num) {
  AINFO << "Received signal: " << signal_num;
  // only response for ctrl + c
  if (signal_num != SIGINT) {
    return;
  }
  bool static is_stopping = false;
  if (is_stopping) {
    return;
  }
  is_stopping = true;
  ros::shutdown();
}
  • 示例

这里以localization模块为例,分析一下整个运行流程。首先,在localization模块的main.cc文件(modules/localization/main.cc)中,使用宏APOLLO_MAIN,实现了一个localization节点,这里的节点与ros中的node概念一致,相当于一个进程:

APOLLO_MAIN(apollo::localization::Localization)

宏展开之后的代码如下:

int main(int argc, char **argv) {                            
    google::InitGoogleLogging(argv[0]);                        
    google::ParseCommandLineFlags(&argc, &argv, true);         
    signal(SIGINT, apollo::common::apollo_app_sigint_handler); 
    apollo::localization::Localization apollo_app_;                                           
    ros::init(argc, argv, apollo_app_.Name());                 
    apollo_app_.Spin();                                        
    return 0;                                                  
  }

 

相关标签: apollo