# aimrt_template **Repository Path**: wxnlP/aimrt_template ## Basic Information - **Project Name**: aimrt_template - **Description**: AimRT开发的工程实践模板。 - **Primary Language**: C++ - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-12 - **Last Updated**: 2026-03-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 近期更新说明 > 近期更新均是针对`WorkSpaceExample`,后续会一些学习使用的程序也会放在该仓库下,有兴趣可以关注更新。 ``` 2025-8-4 ------ 1.为helloworld子项目新增一键启动脚本(就是绑定yaml的shell脚本),进入build目录,运行指定.sh文件即可。 ----------------- 个人还是更喜欢我的pkg_start.sh脚本,灵活一些,不需要手动切换目录。不过一键启动脚本也有其好处,建议都加上。 2025-8-5 ------ 1.优化子项目的CMakeLists.txt,提高复用性。 ----------------- 新建项目可以直接复制helloworld子项目,CMakeLists.txt仅需要修改模块和pkg使用的依赖。 --------------- 2.新增executor子项目,包括执行器基础、执行器协程接口、执行器定时器。 --------------- 3.新增parameter子项目,包括参数的基本使用。 2025-8-6 ------ 1.统一所有子项目的日志规范。 --------------- 2.新增aimrt_cli新建子项目channel,内容待补充。 --------------- 3.新增工作空间转移脚本,只需手动修改一个CMakeList即可完成编译。 2025-8-22 ----- 1.将未完善的子项目channel移出默认编译,逐步添加子项目使用说明。 2025-8-24 ----- 1.完善子项目channel,移出AIMRT_BUILD_EXAMPLES依赖。 --------------- 2.添加子项目channel说明文档。 2025-8-25 ----- 1.添加子项目chn_protocols,使用自定义protobuf、ros2协议。 --------------- 2.解决ros2后端与非ros2协议的配合问题。 2025-9-1 ------ 1.对工作空间转移脚本进行升级,增加了module和pkg目录的工作空间替换,并优化了使用提示。 --------------- 2.重新整理channel.yaml文件,添加协议配置部分,可以参考该文件使用aimrt_cli生成子项目。 --------------- 并将文件改名为config_template.yaml。 2025-9-2 ------ 1.添加子项目rpc以及rpc protocols的自定义。 2025-9-3 ------ 2.将子项目工作空间转移脚本、配置文件放在tools文件夹,以后就在tools文件夹做子项目添加。 2025-9-5 ------ 1.进一步完善工作空间转移脚本change_workspace.sh,一键转移工作空间无需额外操作。 ``` ## 目录说明 关键脚本说明: ``` ├── HelloWorld ------------ // 单个简单项目管理实践(无命名空间) │   ├── app_start.sh ------ // app模式启动脚本 │   ├── build.sh ---------- // 编译构建脚本 │   ├── pkg_start.sh ------ // pkg模式启动脚本 │   ├── setup.sh ---------- // 临时环境变量配置脚本(减少启动指令的路径含量) ├── WorkSpaceExample ------ // 参考官方源码的多个项目融合实践(有命名空间) │   ├── tools │ └── change_workspace.sh --- // 工作空间转移脚本 │   ├── app_start.sh ------ // app模式启动脚本 │   ├── build.sh ---------- // 编译构建脚本 │   ├── pkg_start.sh ------ // pkg模式启动脚本 └── README.md ------------- // 说明文档 ``` **WorkSpaceExample 案例还不是最终版,比如只编译部分子项目/只不编译部分子项目还没有做,后续会持续更新。** 请根据[官方文档](https://docs.aimrt.org/tutorials/quick_start/build_from_source_ubuntu.html)完成环境依赖安装,并进行`Aimrt`源码编译构建后再使用该仓库。 ## HelloWorld 本案例融合了`app`模式和`pkg`模式,启动时可以根据`yaml`配置文件和可执行程序自由选择(这就是逻辑和部署分离)。 - `app`模式的`CMake`构建环境搭建参考了`AimRT`官方文档的[快速开始](https://docs.aimrt.org/tutorials/quick_start/helloworld_cpp.html); - `pkg`模式的`CMake`构建环境搭建参考了`AimRT`源码中的[helloworld示例](https://github.com/AimRT/AimRT/tree/main/src/examples/cpp/helloworld),并取消了`CMake`构建脚本中的命名空间使用,方便新手阅读学习。 ### 如何使用 `AimRT`的`app`模式需要手动注册模块,核心程序在`src/app/helloworld_app/main.cc`。 **进入工程根目录** ,运行编译构建脚本,编译构建后,`build`根目录下会生成`helloworld_app`可执行文件: ```shell ./build.sh ``` 运行临时环境变量配置脚本,激活环境变量`CONFIG_FILE_PATH`: > 可以选择手动输入`yaml`文件完整路径,则忽略该脚本。 ```shell ./setup.sh ``` 使用`app`模式执行: > 可以选择自己到`build`目录下执行`helloworld_app`可执行文件,后面加`yaml`文件路径。 ```shell # ./app_start.sh <可执行文件> ./app_start.sh helloworld_app $CONFIG_FILE_PATH/helloworld_app.yaml ``` 使用`pkg`模式执行: > 可以选择自己到`build`目录下执行`aimrt_main`可执行文件,后面加`yaml`文件路径。 ```shell # ./pkg_start.sh ./pkg_start.sh $CONFIG_FILE_PATH/helloworld_app.yaml ``` ### 脚本解读 `build.sh` ```bash #!/bin/bash # exit on error and print each command set -e if [ -d ./build/install ]; then rm -rf ./build/install fi # 使用Ninja加速编译 cmake -B build -G Ninja $@ cmake --build build --config Release --parallel $(nproc) ``` 编译构建脚本就是将`CMake`的构建和编译指令合到一起了,这与直接使用`CMake`指令快速验证效果是一样的。 --- `setup.sh` ```shell #!/bin/bash PROJRCT_PATH="$(pwd)" AIMRT_PATH="$(dirname $(pwd))" # 项目的构建目录 PROJECT_BUILD_PATH="${PROJRCT_PATH}/build" # 项目的cfg文件目录 CONFIG_FILE_PATH="${PROJRCT_PATH}/src/install/linux/cfg" # aimrt_main目录 # AIMRT_MAIN_FILE="${PROJRCT_PATH}/build/_deps/aimrt-build/src/runtime/main/aimrt_main" # 将aimrt_main拷贝到当前项目的构建目录 # cp ${AIMRT_MAIN_FILE} ${PROJECT_BUILD_PATH} # 定义aimrt_main临时环境变量(可选) # export AIMRT_MAIN="${PROJECT_BUILD_PATH}/aimrt_main" ``` 为了优雅的启动`AimRT`,这个脚本做了很多改动,不过后来基本都没用上,在搞清楚一些`CMake`配置后便只保留了`CONFIG_FILE_PATH`环境变量,在终端`$CONFIG_FILE_PATH`便可以替换大串路径。 > 注意,在 **多个项目融合实践** 中这个脚本已经彻底被取代,转而使用`CMake`程序将`yaml`拷贝到`build`目录,使得路径简单且固定(这一点和官方源码一致)。 --- `app_start.sh` ```shell #!/bin/bash PROJRCT_PATH="$(pwd)" cd ${PROJRCT_PATH}/build/ ./$1 $2 ``` `pkg_start.sh` ```shell #!/bin/bash PROJRCT_PATH="$(pwd)" cd ${PROJRCT_PATH}/build/ ./aimrt_main --cfg_file_path="$1" ``` 两个启动脚本使用的思想一致:切换到`build`目录,然后启动对于的可执行文件,`yaml`文件路径通过终端参数传递。 > `$1`为第一个参数,`$0`是`.sh`文件本身,依次类推。 ## WorkSpaceExample 本案例可以理解为是多项目融合实践,因为它可以管理多个单独项目,一起编译或单独编译(单独编译还没做,不过官方源码给了很好的示例)。 > 官方并没有此称呼,这是个人随便起的😂。官方文档示例中,无论手动搭建或使用`CLI`自动搭建,结果均是一个单独的项目,类似上面的`HelloWorld`示例(差别仅`CMake`构建脚本更复杂规范)。不过,`AimRT`的源码中的`Example`是使用这种方法搭建的,这也是我这样搭建开发环境的原因之一。 本案例唯一参考[AimRT源码](https://github.com/AimRT/AimRT)。 ### 如何使用 与`HelloWorld`使用方法一样,不同点在于不再需要`setup.sh`。 ### CMake命名空间 `WorkSpaceExample`的部分目录如下: ``` ├── CMakeLists.txt ├── app_start.sh ├── build.sh ├── cmake │   └── GetAimRT.cmake ├── pkg_start.sh └── src ├── CMakeLists.txt ├── executor ------------------- // executor子项目 └── helloworld ----------------- // helloworld子项目 ├── CMakeLists.txt ├── app -------------------- // app模式启动文件 ├── install ├── module └── pkg -------------------- // pkg模式启动文件 ``` `src`为父级命名空间,固定为`aimrt`: ```cmake set_root_namespace("aimrt") ``` `src`下每一个子项目如`helloworld`,均设置一个以文件夹命名的子级命名空间: ```cmake set_namespace() ``` 子项目`helloworld`下的`CMakeLists.txt`及其下级`CMakeLists.txt`开始使用命名空间,具体如下: - 获取当前文件夹名称 ```cmake string(REGEX REPLACE ".*/\(.*\)" "\\1" CUR_DIR ${CMAKE_CURRENT_SOURCE_DIR}) ``` - 获取当前目录的父级命名空间(不含当前) ```cmake get_namespace(CUR_SUPERIOR_NAMESPACE) ``` - 将命名空间的 "::" 换成 "_" ```cmake string(REPLACE "::" "_" CUR_SUPERIOR_NAMESPACE_UNDERLINE ${CUR_SUPERIOR_NAMESPACE}) ``` > 重点关注三个变量`CUR_DIR`、`CUR_SUPERIOR_NAMESPACE`、`CUR_SUPERIOR_NAMESPACE_UNDERLINE`,若是在`helloworld`根目录下的`CMakeLists.txt`,则: > > - `${CUR_DIR}=helloworld` > > - `${CUR_SUPERIOR_NAMESPACE}=aimrt::helloworld` > > - `${CUR_SUPERIOR_NAMESPACE_UNDERLINE}=aimrt_helloworld` 在子项目下的文件夹`app`、`pkg`、`module`,分别需要编译成可执行文件、动态库、静态库。因此基于命名空间会进一步做以下操作(以可执行文件为例): ```cmake # 设置静态库/动态库/可执行文件名称,"_"和"::"两种形式 set(CUR_TARGET_NAME ${CUR_SUPERIOR_NAMESPACE_UNDERLINE}_${CUR_DIR}) set(CUR_TARGET_ALIAS_NAME ${CUR_SUPERIOR_NAMESPACE}::${CUR_DIR}) # 定义一个静态库/动态库/可执行文件目标 add_library(${CUR_TARGET_NAME} SHARED) # 建立一个静态库/动态库/可执行文件的别名 add_library(${CUR_TARGET_ALIAS_NAME} ALIAS ${CUR_TARGET_NAME}) # 重命名可执行文件/动态库的输出名称 set_target_properties(${CUR_TARGET_NAME} PROPERTIES OUTPUT_NAME ${CUR_DIR}) ``` ### 使用的CMake技巧 在工作空间根目录的`CMakeLists.txt`中,修改默认的库文件、存档文件的存放位置,这样`aimrt_main`等可执行文件、pkg模式生成的`.so`都会放在构建目录的根位置。 ```cmake # 修改默认的库文件、存档文件的存放位置 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) ``` 在每个子项目的`CMakeLists.txt`中,做资源文件(主要是`yaml`文件)的拷贝,这样`yaml`文件也会放在构建目录的根位置。 ```cmake add_custom_target( ${CUR_SUPERIOR_NAMESPACE_UNDERLINE}_${CUR_DIR}_build_all ALL COMMAND ${CMAKE_COMMAND} -E copy_directory ${CUR_INSTALL_SOURCE_DIR} ${CMAKE_BINARY_DIR} DEPENDS aimrt::runtime::main ${CUR_SUPERIOR_NAMESPACE}::${CUR_DIR}::helloworld_app ${CUR_SUPERIOR_NAMESPACE}::${CUR_DIR}::helloworld_pkg) ``` ### 脚本解读 与`HelloWorld`的脚本区别不大: - 取消`setup.sh` - 修改启动脚本的`yaml`文件路径 ## aimrt_cli新建子项目 > `aimrt_cli`新建的工程可以直接编译,这里介绍的是将工程加入到`WorkSpaceExample`工作空间作为一个子项目的方法。 `channel`子项目是通过`aimrt_cli`自动生成的,通过`change_workspace.sh`脚本一键转移到`WorkSpaceExample`工作空间~~,然后修改子项目的根目录下的`CMakeLists.txt`即可使用~~。 示例:[aimrt_wkspace_template](https://github.com/wxnlP/aimrt_wkspace_template?tab=readme-ov-file#) `change_workspace.sh`使用: ```shell # 注意aimrt_cli的project_name参数要与子项目名称一致 ./change_workspace.sh <子项目名称> <工作空间路径> <命名空间> ``` --- **下面的部分在`1.1`版本已经完全由脚本替代** --- 问题主要在下面俩个语法处: - 删除`install`(建议删除) - 修改`add_custom_target`依赖的工作空间和输出路径,在此之前需要定义几个固定变量 ![](https://tonmoon.obs.cn-east-3.myhuaweicloud.com/img/tonmoon/20250806155617761.png) 修改内容: - 新增语法,放在`add_custom_target`前。 ```cmake string(REGEX REPLACE ".*/\(.*\)" "\\1" CUR_DIR ${CMAKE_CURRENT_SOURCE_DIR}) # 获取当前目录的父级命名空间(不含当前) get_namespace(CUR_SUPERIOR_NAMESPACE) # 将命名空间的 "::" 换成 "_" string(REPLACE "::" "_" CUR_SUPERIOR_NAMESPACE_UNDERLINE ${CUR_SUPERIOR_NAMESPACE}) ``` - 修改语法,使用变量替换。 ```cmake # build all add_custom_target( ${CUR_SUPERIOR_NAMESPACE_UNDERLINE}_${CUR_DIR}_build_all ALL COMMAND ${CMAKE_COMMAND} -E copy_directory ${CUR_INSTALL_SOURCE_DIR}/bin ${CMAKE_BINARY_DIR} DEPENDS aimrt::runtime::main ${CUR_SUPERIOR_NAMESPACE}::${CUR_DIR}::pb_pkg ${CUR_SUPERIOR_NAMESPACE}::${CUR_DIR}::ros2_pkg ${CUR_SUPERIOR_NAMESPACE}::${CUR_DIR}::publisher_pkg ${CUR_SUPERIOR_NAMESPACE}::${CUR_DIR}::subscriber_pkg) ``` ~~但需要注意,使用脚本转移后模块和`pkg`的工作空间与其他子项目规范并不一致。~~ 原因在于我自己搭建的工作空间的父级命名空间实际比`aimrt_cli`自动生成的工程高一级,~~但这并不影响编译~~。 ~~为保持工作空间的规范,修改`module`和`pkg`下文件的命名空间,加上父级命名空间即可。~~ ![](https://tonmoon.obs.cn-east-3.myhuaweicloud.com/img/tonmoon/20250806160102810.png) **该部分已经由 2025.9.1 更新的脚本替代完成** ,以上图为例,默认生成的工程命名空间为`channel::pb_publisher_module`,脚本会根据提供的`<命名空间>`参数补全父级命名空间。