资源准备
阅读声明
该文章不教 AimRT 开发环境搭建 、 从零搭建简单工程 、AimRT 的使用 。
以上内容请移步AimRT 官方文档,介绍的很详细。
本文章介绍的是 app 和 pkg 模式使用 、 app 模式基础上添加 pkg 模式 、 搭建复杂工程框架 、 AimRT 源码使用的部分 CMake 技巧 。
这些内容是官方文档所没有的,比如:官方文档的基础工程搭建Demo中并未使用pkg模式;其次在介绍pkg的部分虽然介绍pkg模式的使用、启动,但是关于aimrt_main可执行程序个人认为介绍的不要详细(也可能是个人水平问题)。
克隆源码
仓库地址:https://github.com/wxnlP/aimrt_template
克隆仓库:git clone https://github.com/wxnlP/aimrt_template.git
目录说明
├── HelloWorld --------- // 单个简单项目管理实践(无命名空间)
│ ├── app_start.sh --- // app模式启动脚本
│ ├── build.sh ------- // 编译构建脚本
│ ├── pkg_start.sh --- // pkg模式启动脚本
│ ├── setup.sh ------- // 临时环境变量配置脚本(减少启动指令的路径含量)
├── WorkSpaceExample --- // 参考官方源码的多个项目融合实践(有命名空间)
│ ├── app_start.sh --- // app模式启动脚本
│ ├── build.sh ------- // 编译构建脚本
│ ├── pkg_start.sh --- // pkg模式启动脚本
└── README.md ---------- // 说明文档
WorkSpaceExample 案例还不是最终版,比如只编译部分子项目/只不编译部分子项目还没有做,后续会持续更新。
请根据官方文档完成环境依赖安装,并进行Aimrt源码编译构建后再使用本仓库。
AimRT 工作模式
app 模式
app模式就和ROS的节点创建方式一样,需要一个main函数初始化、启动、关闭模块。
pkg 模式
pkg模式单独看官方文档比较晦涩难懂,若非有强大的编译原理相关知识可能只有上手用过以后才能明白。
简单来说,pkg模式通过官方提供的接口AIMRT_PKG_MAIN统一启动模块,无需手动启动。一般我们会建一个与module、install同级的目录xxx_pkg,该目录下有唯一一个启动文件pkg_main.cc,如下所示是一个简单的pkg_main.cc示例:
#include "aimrt_pkg_c_interface/pkg_macro.h"
#include "helloworld_module/helloworld_module.h"
using namespace aimrt::helloworld::helloworld_module;
static std::tuple<std::string_view, std::function<aimrt::ModuleBase*()>> aimrt_module_register_array[]{
{"HelloWorldModule", []() -> aimrt::ModuleBase* { return new HelloWorldModule(); }}
};
AIMRT_PKG_MAIN(aimrt_module_register_array);
在编译时,pkg模式与app模式的不同表现为会编译成动态库libxxx.so文件,需要借助官方的aimrt_main可执行文件(不同于app模式的自定义可执行文件)并对yaml做pkg配置实现启动,使用./aimrt_main --help可以查看其使用方法。
pkg需要的额外配置是pkgs标签,需要指定动态库名称,按我的示例或使用官方aimrt_cli生成的工程的动态库名称都是libxxx.so,其中xxx是启动文件pkg_main.cc所在的文件名称。
aimrt:
configurator:
temp_cfg_path: ./cfg/tmp
module:
pkgs:
- path: ./libhelloworld_pkg.so
modules:
- name: HelloWorldModule
log_lvl: INFO
那么,像我这样笨的学生就要问了,aimrt_main在哪里?
我们在编译源码的时候会生成aimrt_main在编译文件夹的根目录,但是在自己的工程中启用pkg模式后却并没有发现aimrt_main。
原因在于它实际生成在/build/_deps/aimrt-build/src/runtime/main/aimrt_main,而官方示例中使用CMake语法对可执行文件做了统一的生成位置指定,这个在后面会介绍如何使用。
至此,你应该知道如何使用pkg模式和使用aimrt_main启动了,需要注意生成的动态库要与aimrt_main位于同一路径,这样事情会变得简单,这个在后面也会介绍。
HelloWorld
本案例融合了app模式和pkg模式,启动时可以根据yaml配置文件和可执行程序自由选择(这就是逻辑和部署分离)。
app模式的CMake构建环境搭建参考了AimRT官方文档的快速开始;pkg模式的CMake构建环境搭建参考了AimRT源码中的helloworld示例,并取消了CMake构建脚本中的命名空间使用,方便新手阅读学习。
如何使用
AimRT的app模式需要手动注册模块,核心程序在src/app/helloworld_app/main.cc。
进入工程根目录 ,运行编译构建脚本,编译构建后,build根目录下会生成helloworld_app可执行文件:
./build.sh
运行临时环境变量配置脚本,激活环境变量CONFIG_FILE_PATH:
可以选择手动输入
yaml文件完整路径,则忽略该脚本。
./setup.sh
使用app模式执行:
可以选择自己到
build目录下执行helloworld_app可执行文件,后面加yaml文件路径。
# ./app_start.sh <可执行文件> <yaml文件路径>
./app_start.sh helloworld_app $CONFIG_FILE_PATH/helloworld_app.yaml
使用pkg模式执行:
可以选择自己到
build目录下执行aimrt_main可执行文件,后面加yaml文件路径。
# ./pkg_start.sh <yaml文件路径>
./pkg_start.sh $CONFIG_FILE_PATH/helloworld_app.yaml
脚本解读
build.sh
#!/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
#!/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
#!/bin/bash
PROJRCT_PATH="$(pwd)"
cd ${PROJRCT_PATH}/build/
./$1 $2
pkg_start.sh
#!/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源码](AimRT/AimRT: A high-performance runtime framework for modern robotics.)。
如何使用
与HelloWorld使用方法一样,不同点在于不再需要setup.sh。
CMake命名空间
WorkSpaceExample的目录如下:
├── CMakeLists.txt
├── app_start.sh
├── build.sh
├── cmake
│ └── GetAimRT.cmake
├── pkg_start.sh
└── src
├── CMakeLists.txt
├── executor
└── helloworld
├── CMakeLists.txt
├── app
├── install
├── module
└── pkg
src为父级命名空间,固定为aimrt:
set_root_namespace("aimrt")
src下每一个子项目如helloworld,均设置一个以文件夹命名的子级命名空间:
set_namespace()
子项目helloworld下的CMakeLists.txt及其下级CMakeLists.txt开始使用命名空间,具体如下:
- 获取当前文件夹名称
string(REGEX REPLACE ".*/\(.*\)" "\\1" CUR_DIR ${CMAKE_CURRENT_SOURCE_DIR})
- 获取当前目录的父级命名空间(不含当前)
get_namespace(CUR_SUPERIOR_NAMESPACE)
- 将命名空间的 “::” 换成 “_”
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,分别需要编译成可执行文件、动态库、静态库。因此基于命名空间会进一步做以下操作(以可执行文件为例):
# 设置静态库/动态库/可执行文件名称,"_"和"::"两种形式
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都会放在构建目录的根位置。
# 修改默认的库文件、存档文件的存放位置
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文件也会放在构建目录的根位置。
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文件路径
1099

被折叠的 条评论
为什么被折叠?



