AimRT 從零到一:官方示例精講 —— 二、HelloWorld示例.md

HelloWorld示例

官方倉庫:helloworld

配置文件(configuration_helloworld.yaml? )

依據官方示例項目結構自行編寫YAML配置文件

# 基礎信息
base_info:project_name: helloworld  # 項目名稱build_mode_tags: ["EXAMPLE", "SIMULATION", "TEST_CAMERA"]  # 構建模式標簽aimrt_import_options:  # AimRT框架的構建選項AIMRT_BUILD_TESTS: "OFF"  # 是否構建測試代碼AIMRT_BUILD_EXAMPLES: "ON"  # 是否構建示例代碼AIMRT_BUILD_DOCUMENT: "OFF"  # 是否構建文檔AIMRT_BUILD_RUNTIME: "ON"  # 是否構建運行時核心AIMRT_BUILD_CLI_TOOLS: "OFF"  # 是否構建命令行工具AIMRT_BUILD_WITH_PROTOBUF: "ON"  # 是否啟用Protobuf支持AIMRT_USE_LOCAL_PROTOC_COMPILER: "OFF"  # 是否使用本地protoc編譯器AIMRT_BUILD_WITH_ROS2: "OFF"  # 是否集成ROS2支持AIMRT_BUILD_NET_PLUGIN: "OFF"  # 是否構建網絡插件AIMRT_BUILD_ROS2_PLUGIN: "OFF"  # 是否構建ROS2插件# ...  # 其他插件選項(MQTT、Zenoh等)# 模塊  會生成 ./helloworld/src/module/helloworld_module/helloworld_module.h
modules:- name: HelloWorld_module  # 自定義模塊名稱,同時會據此生成module的類名#   build_mode_tag: ["EXAMPLE"]  # 可選:指定模塊的構建標簽# pkg   會生成 ./helloworld/src/pkg/helloworld_pkg/pkg_main.cc
pkgs:- name: HelloWorld_pkg  # 包名modules:- name: HelloWorld_module  # 包含的模塊# 部署  會生成 ./helloworld/src/install 目錄及相應的啟動腳本、配置文件
deploy_modes:- name: local_deploy  # 部署模式名稱deploy_ins: # 部署實例- name: local_ins_helloworld  # 實例名稱pkgs:- name: HelloWorld_pkg  # 實例加載的包

運行aimrt_cli?工具,生成腳手架代碼

aimrt_cli gen -p configuration_helloworld.yaml -o helloworld

module目錄

HelloWorld_module

完整代碼查看官方倉庫,這里只討論核心邏輯,建議對照源碼閱讀。

模塊定義(helloworld_module.h?)
class HelloworldModule : public aimrt::ModuleBase {
public:// 必須實現的三大生命周期方法bool Initialize(aimrt::CoreRef core) override;  // 初始化配置bool Start() override;                         // 業務邏輯入口void Shutdown() override;                      // 資源釋放private:aimrt::CoreRef core_;  // 框架核心句柄
};
模塊實現(helloworld_module.cc?)
初始化階段

AimRT會在這一過程初始化自己的資源、例如日志、通信等模塊。

官方文檔提到:需要注意的是,AimRT 的Initialize階段僅僅是 AimRT 框架自身的初始化階段,可能只是整個服務初始化階段的一部分。使用者可能還需要在 AimRT 的Start階段先初始化自己的一些業務邏輯,比如檢查上下游資源、進行一些配置等,然后再真正的進入整個服務的運行階段。

思考能否將自己的一些資源獲取相關的業務邏輯也放在這一階段(似乎沒必要)

bool HelloworldModule::Initialize(aimrt::CoreRef core) {core_ = core;  // 綁定框架核心// 讀取 YAML 配置示例auto cfg_path = core_.GetConfigurator().GetConfigFilePath();YAML::Node cfg = YAML::LoadFile(cfg_path);AIMRT_INFO("Loaded config: {}", cfg["param"].as<std::string>());return true;  // 初始化成功標志
}
運行階段

初始化結束后,AimRT就會調用Start?進入運行階段,執行開發人員自己的業務邏輯。

bool Start() {AIMRT_INFO("Module Started");  // 日志輸出演示return true;
}
停止階段

AimRT 收到停止信號后會調用Shutdown?方法,進行資源釋放等操作。

void Shutdown() {AIMRT_INFO("Cleanup resources");
}

所有的業務邏輯我們都會在Module類中進行實現,具體加載運行方式會由AimRT根據配置文件進行集成

Pkg目錄

Pkg模式集成(pkg_main.cc)

AimRT 提供的 aimrt_main? 可執行程序,在運行時會根據配置文件加載 動態庫 形式的 Pkg,并導入其中的 Module 類。加載后,框架會自動調用 Initialize? 進行初始化,隨后執行 Start? 啟動模塊。

具體流程可參考 aimrt_main? 源碼:aimrt_main主函數代碼

注冊表定義

static std::tuple<std::string_view, FactoryFunc>aimrt_module_register_array[] = {{"HelloWorldModule",  // 模塊標識名[]() { return new HelloWorldModule(); }}  // 工廠函數
};
  • 模塊名稱:用于標識 HelloWorldModule?,框架會通過它查找模塊。
  • 工廠函數:返回 HelloWorldModule? 的實例,供 AimRT 運行時調用。

注冊宏

AIMRT_PKG_MAIN(aimrt_module_register_array)  // 框架入口宏

??AIMRT_PKG_MAIN?? 宏展開后,會生成 4 個 C 接口函數,供 ?aimrt_main?? 調用:

  • ?AimRTDynlibGetModuleNum()?:獲取當前動態庫中的模塊數量。
  • ?AimRTDynlibGetModuleNameList()?:獲取所有可用的模塊名稱。
  • ?AimRTDynlibCreateModule()?:根據模塊名稱創建模塊實例。
  • ?AimRTDynlibDestroyModule()?:銷毀模塊實例,釋放資源。

加載運行

運行 build? 目錄下的 start_local_deploy_local_ins_helloworld.sh? 啟動進程。

腳本名稱 由創建工程時的((20250401152311-auw052s “配置文件”))自動生成。

# 這是啟動腳本的具體內容
./aimrt_main --cfg_file_path=./cfg/local_deploy_local_ins_helloworld_cfg.yaml

運行流程:

  1. 解析配置文件,查找需要加載的模塊名稱。
  2. 在注冊表中匹配模塊名,調用對應的工廠函數創建實例。
  3. 依次執行 ?Initialize()? ? ?Start()? ?,完成模塊啟動。

App目錄

**App模式官網概念:**?開發者在自己的 Main 函數中注冊/創建各個模塊,編譯時直接將業務邏輯編譯進主程序;

可以簡單理解為我們手動實現了aimrt_main?啟動邏輯,分別有兩種實現方式:創建模塊 和 注冊模塊

核心接口

需要在CMake中引用相關庫:

# Set link libraries of target
target_link_libraries(${CUR_TARGET_NAME}PRIVATE gflags::gflagsaimrt::runtime::core)

此時即可使用 core/aimrt_core.h? 文件中的aimrt::runtime::core::AimRTCore?類,核心接口如下:

namespace aimrt::runtime::core {class AimRTCore {public:struct Options {std::string cfg_file_path;};public:void Initialize(const Options& options);void Start();std::future<void> AsyncStart();void Shutdown();module::ModuleManager& GetModuleManager();};}  // namespace aimrt::runtime::core

接口使用說明如下:

  • ?void Initialize(const Options& options)?:用于初始化框架。

    • 接收一個AimRTCore::Options?作為初始化參數。其中最重要的項是cfg_file_path?,用于設置配置文件路徑。
    • 如果初始化失敗,會拋出一個異常。
  • ?void Start()?:啟動框架。

    • 如果啟動失敗,會拋出一個異常。
    • 必須在 Initialize 方法之后調用,否則行為未定義。
    • 如果啟動成功,會阻塞當前線程,并將當前線程作為本AimRTCore?實例的主線程。
  • ?std::future<void> AsyncStart()?:異步啟動框架。

    • 如果啟動失敗,會拋出一個異常。
    • 必須在 Initialize 方法之后調用,否則行為未定義。
    • 如果啟動成功,會返回一個std::future<void>?句柄,需要在調用 Shutdown? 方法后調用該句柄的 wait? 方法阻塞等待結束。
    • 該方法會在內部新啟動一個線程作為本AimRTCore?實例的主線程
    • 閱讀函數源碼可以發現,創建的后臺線程唯一作用就是等待 shutdown 信號,并執行清理工作
  • ?void Shutdown()?:停止框架。

    • 可以在任意線程、任意階段調用此方法,也可以調用任意次數。
    • 調用此方法后,Start?方法將在執行完主線程中的所有任務后,退出阻塞。
    • 需要注意,有時候業務會阻塞住主線程中的任務,導致Start?方法無法退出阻塞、優雅結束整個框架,此時需要在外部強制 kill。

注冊模塊(helloworld_app_registration_mode?)(推薦方案)

通過RegisterModule?可以直接注冊一個標準模塊。開發者需要先實現一個繼承于ModuleBase?基類的Module?,然后在AimRTCor?e實例調用Initialize?方法之前注冊該Module?實例,在此方式下仍然有一個比較清晰的Module?邊界。

參考代碼:

#include <csignal>
#include <iostream>#include "HelloWorld_module/HelloWorld_module.h"	// 這里引用的是我們之前創建的Module類
#include "core/aimrt_core.h"using namespace aimrt::runtime::core;
using namespace helloworld::HelloWorld_module;AimRTCore* global_core_ptr = nullptr;void SignalHandler(int sig) {if (global_core_ptr && (sig == SIGINT || sig == SIGTERM)) {global_core_ptr->Shutdown();return;}raise(sig);
};int32_t main(int32_t argc, char** argv) {signal(SIGINT, SignalHandler);signal(SIGTERM, SignalHandler);std::cout << "AimRT start." << std::endl;try {AimRTCore core;global_core_ptr = &core;// register moduleHelloworldModule helloworld_module;core.GetModuleManager().RegisterModule(helloworld_module.NativeHandle());AimRTCore::Options options;if (argc > 1) options.cfg_file_path = argv[1];core.Initialize(options);core.Start();core.Shutdown();global_core_ptr = nullptr;} catch (const std::exception& e) {std::cout << "AimRT run with exception and exit. " << e.what() << std::endl;return -1;}std::cout << "AimRT exit." << std::endl;return 0;
}

通過下面的命令啟動編譯后的可執行文件

./helloworld_app_registration_mode ./cfg/examples_cpp_helloworld_app_mode_cfg.yaml

**創建模塊(**?helloworld_app?)

AimRTCore?實例調用Initialize方法之后,通過CreateModule?可以創建一個模塊,并返回一個aimrt::CoreRef?句柄,開發者可以直接基于此句柄調用一些框架的方法,比如 RPC 或者 Log 等。在此方式下沒有一個比較清晰的Module?邊界,不利于大型項目的組織,一般僅用于快速做一些小工具。

參考代碼:

// Copyright (c) 2023, AgiBot Inc.
// All rights reserved.#include <csignal>
#include <iostream>#include "aimrt_module_cpp_interface/core.h"
#include "core/aimrt_core.h"using namespace aimrt::runtime::core;// 全局指針,指向 AimRTCore 實例,用于在信號處理函數中進行關閉操作
AimRTCore* global_core_ptr = nullptr;/*** @brief 信號處理函數** 處理 SIGINT (Ctrl+C) 和 SIGTERM 信號,如果 AimRTCore 實例存在,則調用其* Shutdown 方法進行關閉。*/
void SignalHandler(int sig) {if (global_core_ptr && (sig == SIGINT || sig == SIGTERM)) {global_core_ptr->Shutdown();  // 調用 Shutdown 方法,優雅退出return;}raise(sig);  // 傳遞其他信號,按照默認行為處理
}int32_t main(int32_t argc, char** argv) {// 注冊信號處理函數,確保進程收到終止信號時能正確清理資源signal(SIGINT, SignalHandler);signal(SIGTERM, SignalHandler);std::cout << "AimRT 啟動." << std::endl;  // 日志:AimRT 啟動try {AimRTCore core;global_core_ptr = &core;  // 賦值全局指針,以便信號處理時可以訪問// 配置 AimRTCore 選項AimRTCore::Options options;if (argc > 1)options.cfg_file_path =argv[1];  // 如果傳入了參數,則使用該參數作為配置文件路徑core.Initialize(options);  // 初始化 AimRTCore 實例// 創建 AimRT 模塊,并獲取模塊句柄aimrt::CoreRef module_handle(core.GetModuleManager().CreateModule("HelloWorldModule"));// 記錄日志,表示模塊創建成功AIMRT_HL_INFO(module_handle.GetLogger(), "這是一個示例日志。");// 讀取并解析 YAML 配置文件auto file_path = module_handle.GetConfigurator().GetConfigFilePath();if (!file_path.empty()) {YAML::Node cfg_node = YAML::LoadFile(std::string(file_path));for (const auto& itr : cfg_node) {std::string k = itr.first.as<std::string>();std::string v = itr.second.as<std::string>();AIMRT_HL_INFO(module_handle.GetLogger(), "cfg [{} : {}]", k, v);}}// 異步啟動 AimRT 實例,返回一個 future 對象auto fu = core.AsyncStart();AIMRT_HL_INFO(module_handle.GetLogger(), "啟動成功。");// 日志:啟動成功。// 等待 AimRT 運行結束(即 shutdown 觸發)fu.wait();// 關閉 AimRT 實例core.Shutdown();// 清空全局指針,避免懸空指針問題global_core_ptr = nullptr;} catch (const std::exception& e) {std::cout << "AimRT 運行時發生異常,退出: " << e.what() << std::endl;return -1;}std::cout << "AimRT 退出." << std::endl;  // 日志:AimRT 退出return 0;
}

通過下面的命令啟動編譯后的可執行文件:

./helloworld_app_registration_mode ./cfg/examples_cpp_helloworld_app_mode_cfg.yaml

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/78752.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/78752.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/78752.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Tauri 跨平臺開發指南及實戰:用前端技術征服桌面應用(合集-萬字長文)

厭倦了笨重的Electron應用&#xff1f;想要構建體積小、性能高、安全可靠的跨平臺桌面應用&#xff1f;Tauri將是你的不二之選&#xff01;本教程帶你從入門到精通&#xff0c;掌握這個下一代桌面應用開發框架&#xff0c;并通過實戰APK分析工具項目&#xff0c;將理論知識轉化…

【LeetCode 熱題 100】矩陣置零 / 螺旋矩陣 / 旋轉圖像 / 搜索二維矩陣 II

??個人主頁&#xff1a;小羊 ??所屬專欄&#xff1a;LeetCode 熱題 100 很榮幸您能閱讀我的文章&#xff0c;誠請評論指點&#xff0c;歡迎歡迎 ~ 目錄 矩陣矩陣置零螺旋矩陣旋轉圖像搜索二維矩陣 II 矩陣 矩陣置零 矩陣置零 用兩個數組分別標記行和列&#xff0c;判斷…

JavaScript進階(三十一): === 與 == 比較運算符

文章目錄 一、前言二、嚴格相等運算符 ()三、寬松相等運算符 ()四、推薦做法五、特殊情況 一、前言 在 JavaScript 中&#xff0c; 和 都是比較運算符&#xff0c;但它們在比較時有重要區別&#xff1a; 二、嚴格相等運算符 () 不進行類型轉換只有當兩個操作數的值和類型都…

HTML與安全性:XSS、防御與最佳實踐

HTML 與安全性&#xff1a;XSS、防御與最佳實踐 前言 現代 Web 應用程序無處不在&#xff0c;而 HTML 作為其基礎結構&#xff0c;承載著巨大的安全責任。跨站腳本攻擊&#xff08;XSS&#xff09;仍然是 OWASP Top 10 安全威脅之一&#xff0c;對用戶數據和網站完整性構成嚴…

安達發|破解醫療器械多BOM困局:APS生產計劃排產軟件解決方案

在醫療器械設備制造行業&#xff0c;生產計劃與排程&#xff08;Advanced Planning and Scheduling, APS&#xff09;系統的應用至關重要。由于醫療器械行業具有嚴格的法規要求&#xff08;如FDA、ISO 13485&#xff09;、復雜的多級BOM&#xff08;Bill of Materials&#xff…

組件輪播與樣式結構重用實驗

任務一&#xff1a;使用“Swiper 輪播組件”對自行選擇的圖片和文本素材分別進行輪播&#xff0c;且調整對應的“loop”、“autoPlay”“interval”、“vertical”屬性&#xff0c;實現不同的輪播效果&#xff0c;使用Swiper 樣式自定義&#xff0c;修改默認小圓點和被選中小圓…

【Stable Diffusion】文生圖進階指南:采樣器、噪聲調度與迭代步數的解析

在Stable Diffusion文生圖(Text-to-Image)的創作過程中,采樣器(Sampler)、噪聲調度器(Schedule type)和采樣迭代步數(Steps)是影響生成效果的核心參數。本文將從技術原理、參數優化到實踐應用,深入剖析DPM++ 2M采樣器、Automatic噪聲調度器以及采樣步數的設計邏輯與協…

第一天 車聯網定義、發展歷程與生態體系

前言 車聯網&#xff08;Internet of Vehicles, IoV&#xff09;作為物聯網&#xff08;IoT&#xff09;在汽車領域的延伸&#xff0c;正在徹底改變人們的出行方式。無論是自動駕駛、遠程診斷&#xff0c;還是實時交通優化&#xff0c;車聯網技術都扮演著核心角色。本文將從零…

foc控制 - clarke變換和park變換

1. foc控制框圖 下圖是foc控制框圖&#xff0c;本文主要是講解foc控制中的larke變換和park變換clarke變換將 靜止的 a b c abc abc坐標系 變換到 靜止的 α β αβ αβ坐標系&#xff0c;本質上還是以 定子 為基準的坐標系park變換 則將 α β αβ αβ坐標系 變換到 隨 轉…

軟件系統容量管理:反模式剖析與模式應用

在數字化時代&#xff0c;軟件系統的重要性日益凸顯。隨著業務的不斷拓展和用戶數量的持續增長&#xff0c;軟件系統的容量管理成為保障其高效運行的關鍵因素。《發布&#xff01;軟件的設計與部署》第二部分圍繞容量展開深入探討&#xff0c;系統地闡述了容量的定義、范圍&…

23種設計模式-行為型模式之解釋器模式(Java版本)

Java 解釋器模式&#xff08;Interpreter Pattern&#xff09;詳解 &#x1f9e0; 什么是解釋器模式&#xff1f; 解釋器模式是一種行為型設計模式&#xff0c;主要用于解釋和執行語言的語法規則。它定義了一個解釋器來處理特定的語言句法&#xff0c;并通過一個抽象語法樹來…

基于Springboot + vue + 爬蟲實現的高考志愿智能推薦系統

項目描述 本系統包含管理員和學生兩個角色。 管理員角色&#xff1a; 個人中心管理&#xff1a;管理員可以管理自己的個人信息。 高校信息管理&#xff1a;管理員可以查詢、添加或刪除高校信息&#xff0c;并查看高校詳細信息。 學生管理&#xff1a;管理員可以查詢、添加或…

五種機器學習方法深度比較與案例實現(以手寫數字識別為例)

正如人們有各種各樣的學習方法一樣&#xff0c;機器學習也有多種學習方法。若按學習時所用的方法進行分類&#xff0c;則機器學習可分為機械式學習、指導式學習、示例學習、類比學習、解釋學習等。這是溫斯頓在1977年提出的一種分類方法。 有關機器學習的基本概念&#xff0c;…

Blender插件 三維人物角色動作自動綁定 Auto-Rig Pro V3.68.44 + Quick Rig V1.26.16

Auto-Rig Pro是一個集角色綁定、動畫重定向和Unity、Unreal Engine的Fbx導出于一體的全能解決方案。最初作為我個人的內部角色綁定工具開發&#xff0c;我幾年前將其發布&#xff0c;并自那時起增加了許多新功能。 Blender插件介紹 Auto-Rig Pro插件簡介 Auto-Rig Pro是一個強…

網絡基礎概念:從菜鳥到入門

前言&#xff1a;快遞小哥的故事 想象一下你要給朋友寄個禮物&#xff0c;這個過程其實和網絡通信非常相似&#xff1a; 1. 你需要知道朋友的”地址“&#xff08;IP地址&#xff09; 2. 要注明是送到他家大門還是物業代收&#xff08;端口號&#xff09; 3. 要選擇快遞公司并…

23種設計模式-行為型模式之中介者模式(Java版本)

Java 中介者模式&#xff08;Mediator Pattern&#xff09;詳解 &#x1f9e0; 什么是中介者模式&#xff1f; 中介者模式是一種行為型設計模式&#xff0c;它通過定義一個中介者對象來封裝一組對象之間的交互。中介者使得各個對象不需要顯式地知道彼此之間的關系&#xff0c…

【Redis】基礎4:作為分布式鎖

文章目錄 1. 一些概念2. MySQL方案2.1 方案一&#xff1a;事務特性2.1.1 存在的問題2.1.2 解決方案 2.2 方案二&#xff1a;樂觀鎖2.3 方案三&#xff1a;悲觀鎖 3. Redis3.1 實現原理3.2 實現細節3.2.1 問題1&#xff1a;持有期間鎖過期問題3.2.2 問題2&#xff1a;判斷和釋放…

深度學習---框架流程

核心六步 一、數據準備 二、模型構建 三、模型訓練 四、模型驗證 五、模型優化 六、模型推理 一、數據準備&#xff1a;深度學習的基石 數據是模型的“燃料”&#xff0c;其質量直接決定模型上限。核心步驟包括&#xff1a; 1. 數據收集與標注 來源&#xff1a;公開數據集…

阿里云 OpenManus 實戰:高效AI協作體系

阿里云 OpenManus 實戰&#xff1a;高效AI協作體系 寫在最前面初體驗&#xff1a;快速部署&#xff0c;開箱即用 真實案例分享&#xff1a;從單體開發到智能良好提示詞過程展示第一步&#xff1a;為亞馬遜美國站生成商品描述第二步&#xff1a;為eBay全球站生成商品描述結果分析…

Kubernetes》》k8s》》explain查 yaml 參數

在創建json 和yaml 時&#xff0c;我們可能不知道具體的參數該怎么寫。同樣 我們可以通過explain這個 命令來查看 每個參數具體的作用與寫法 # 查看 pod類性有哪些參數 kubectl explain pod# 查看pod中 spec下面有哪些參數 kubectl explain pod.spec