AimRT 从零到一:官方示例精讲 —— 一、工具链与基本概念
工具链与基本概念
aimrt_cli
工具
用法示例
aimrt_cli gen -p [config.yaml] -o [output_folder]
# 示例:
aimrt_cli gen -p helloworld.yaml -o ./helloworld/
- 配置驱动:通过 YAML 定义项目结构/编译选项/部署模式
- 工程生成:自动创建 CMake 工程、模块模板、配置文件
- 安全机制:输出目录需为空,避免文件覆盖冲突
配置文档示例:
直接看示例可能无法理解,快进到后续示例中我们自行编写的配置文件,对照学习
# 基础信息配置(必填项)
base_info:# 项目名称,将作为代码的命名空间project_name: test_prj# 构建模式标签,可自定义编译选项# 格式为 {PROJECT_NAME}_{OPTION_NAME},若无需要可置为空列表 []build_mode_tags: ["EXAMPLE", "SIMULATION", "TEST_CAMERA"]# AIMRT引入选项配置(必须使用单引号包裹值)aimrt_import_options:# 是否构建运行时AIMRT_BUILD_RUNTIME: 'ON'# 是否使用fmt库AIMRT_USE_FMT_LIB: 'ON'# 是否使用protobuf构建AIMRT_BUILD_WITH_PROTOBUF: 'ON'# 是否使用本地protoc编译器AIMRT_USE_LOCAL_PROTOC_COMPILER: 'OFF'# 是否使用protoc python插件AIMRT_USE_PROTOC_PYTHON_PLUGIN: 'OFF'# 是否与ROS2一起构建AIMRT_BUILD_WITH_ROS2: 'ON'# 依赖的标准模块配置(可选)
depends_std_modules:- name: xxx # 依赖库名称(应与实际库名一致)git_repository: https://github.com/xxx/xxx.git # 库地址git_tag: v0.1.5 # 版本标签import_options: # 导入选项(暂不支持)XXX: 'ON'- name: yyygit_repository: https://github.com/yyy/yyy.gitgit_tag: v0.1.11# 协议配置(可选)
protocols:- name: my_proto # 协议名称type: protobuf # 协议类型(protobuf/ros2)options: # 选项(暂不支持)xxx: xxx- name: my_ros2_prototype: ros2options:zzz: zzz# 构建模式标签,仅在指定模式下构建build_mode_tag: ["EXAMPLE"]- name: example_prototype: protobuf# 未设置build_mode_tag表示在所有模式下都构建build_mode_tag: ["EXAMPLE"]# 模块配置(可选)
modules:- name: my_foo_module # 模块名称- name: my_bar_module- name: exmaple_module# 仅在EXAMPLE模式下构建build_mode_tag: ["EXAMPLE"]options: # 选项(暂不支持)aaa: aaa# 模块包配置(可选)
pkgs:- name: pkg1 # 包名称modules: # 包含的模块- name: my_foo_module # 模块名namespace: local # 命名空间(自定义模块用local)- name: my_bar_modulenamespace: localoptions:sss: sss- name: pkg2modules:- name: exmaple_modulenamespace: local- name: ep_example_bar_modulenamespace: ep_example_aimrt_module # 外部模块使用其实际命名空间build_mode_tag: ["EXAMPLE"] # 仅在EXAMPLE模式下构建options:sss: sss# 部署配置(可选)
deploy_modes:- name: exmaple_mode # 部署模式名称build_mode_tag: ["EXAMPLE"] # 构建模式检查deploy_ins: # 部署实例配置- name: local_ins_1 # 实例名称pkgs: # 依赖的包- name: pkg1options:disable_modules: [] # 禁用的模块列表- name: local_ins_2 # 简单实例(无包配置)- name: remote_ins_123# 简单部署模式(默认在所有模式下构建)- name: deploy_mode_1- name: deploy_mode_2
CMake 依赖修正(cmake/GetAimRT.cmake
)
由aimrt_cli
工具生成的项目框架,还需要指定仓库和版本
# cmake/GetAimRT.cmake
FetchContent_Declare(aimrtGIT_REPOSITORY https://github.com/AimRT/aimrt.gitGIT_TAG v0.8.3) # 必须指定版本号
aimrt_main主进程
AimRT 提供的aimrt_main
可执行程序,在运行时根据配置文件加载动态库形式的Pkg
,导入其中的Module
项目工作流程
集成业务逻辑的两种方式(App与Pkg)
AimRT 框架可以通过两种方式来集成业务逻辑,分别是 App模式 和 Pkg模式,实际采用哪种方式需要根据具体场景进行判断。两者的区别如下:
-
App模式:在开发者自己的 Main 函数中直接链接 AimRT 运行时库,编译时直接将业务逻辑代码编译进主程序:
- 优势:没有 dlopen 这个步骤,没有 so,只会有最终一个 exe。
- 劣势:可能会有第三方库的冲突;无法独立的发布
Module
,想要二进制发布只能直接发布 exe。 - 使用场景:一般用于小工具、小型 Demo 场景,没有太大的模块解耦需求;
-
Pkg模式:使用 AimRT 提供的 aimrt_main 可执行程序,在运行时根据配置文件加载动态库形式的
Pkg
,导入其中的Module
类:- 优势:编译业务
Module
时只需要链接非常轻量的 AimRT 接口层,不需要链接 AimRT 运行时库,减少潜在的依赖冲突问题;可以二进制发布 so;独立性较好。 - 劣势:框架基于 dlopen 加载
Pkg
,极少数场景下会有一些兼容性问题。 - 使用场景:一般用于中大型项目,对模块解耦、二进制发布等有较强烈需求时;
- 优势:编译业务
无论采用哪种方式都不影响业务逻辑,且两种方式可以共存,实际采用哪种方式需要根据具体场景进行判断。
注意,上述说的两种方式只是针对 Cpp 开发接口。如果是使用 Python 开发,则只支持App模式。
执行器
Executor
,或者叫执行器,是指一个可以运行任务的抽象概念,一个执行器可以是一个 Fiber、Thread 或者 Thread Pool,我们平常写的代码也是默认的直接指定了一个执行器:Main 线程。一般来说,能提供以下接口的就可以算是一个执行器:
void Execute(std::function<void()>&& task);
还有一种Executor
提供定时执行的功能,可以指定在某个时间点或某段时间之后再执行任务。其接口类似如下:
void ExecuteAt(std::chrono::system_clock::time_point tp, std::function<void()>&& task);
void ExecuteAfter(std::chrono::nanoseconds dt, std::function<void()>&& task);
在 AimRT 中,执行器功能由接口层和实际执行器的实现两部分组成,两者相互解耦。接口层定义了执行器的抽象 Api,提供投递任务的接口。而实现层则负责实际的任务执行,根据实现类型的不同有不一样的表现。AimRT 官方提供了几种执行器,例如基于 Asio 的线程池、基于 Tbb 的无锁线程池、基于时间轮的定时执行器等。
开发者使用 AimRT 的执行器功能时,在业务层将任务打包成一个闭包,然后调用接口层的 API,将任务投递到具体的执行器内,而执行器会根据自己的调度策略,在一定时机执行投递过来的任务。具体逻辑流程如下图所示: