AimRT工程模板最佳实践

资源准备

阅读声明

该文章不教 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统一启动模块,无需手动启动。一般我们会建一个与moduleinstall同级的目录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模式的自定义可执行文件)并对yamlpkg配置实现启动,使用./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构建脚本中的命名空间使用,方便新手阅读学习。

如何使用

AimRTapp模式需要手动注册模块,核心程序在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_DIRCUR_SUPERIOR_NAMESPACECUR_SUPERIOR_NAMESPACE_UNDERLINE,若是在helloworld根目录下的CMakeLists.txt,则:

  • ${CUR_DIR}=helloworld
  • ${CUR_SUPERIOR_NAMESPACE}=aimrt::helloworld
  • ${CUR_SUPERIOR_NAMESPACE_UNDERLINE}=aimrt_helloworld

在子项目下的文件夹apppkgmodule,分别需要编译成可执行文件、动态库、静态库。因此基于命名空间会进一步做以下操作(以可执行文件为例):

# 设置静态库/动态库/可执行文件名称,"_"和"::"两种形式
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文件路径

参考资料

Welcome to AimRT — AimRT v1.0.0 documentation

https://github.com/AimRT/AimRT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值