apollo2.5源码解析之运行流程
"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事件通信机制
推荐阅读
-
Spring5源码解析4-refresh方法之invokeBeanFactoryPostProcessors
-
Laravel框架源码解析之模型Model原理与用法解析
-
Spring源码解析之ConfigurableApplicationContext
-
angularjs 源码解析之injector
-
angularjs 源码解析之scope
-
Java并发之ReentrantLock类源码解析
-
Android图片加载利器之Picasso源码解析
-
Vue源码解析之Template转化为AST的实现方法
-
死磕 java同步系列之Phaser源码解析
-
asp.net abp模块化开发之通用树2:设计思路及源码解析