一万字详解 “智元机器人“ 开源的高性能 AimRT 框架 (一)

序言

AimRT是智元机器人开源的一个面向现代机器人领域的运行时开发框架。它基于 Modern C++ 开发, 相比 ros 具有轻量易部署的特点;本人对“稚晖君”一直是抱有崇拜的,所以一直关注着智元机器人的动态,看到AimRT 开源就下载下来学习了一下,所以写下此文,以作记录。

本文是第一篇,主要介绍 AimRT 的核心设计理念,以及初始化流程,存在不足之处,还请指正。

核心设计理念

1. 双阶段运行模型

AimRT 将运行时间分为两个关键阶段(2),这篇文章也会分析这两个阶段干了什么。

  • Initialize 阶段

    • 进行资源申请、注册和初始化
    • 单线程执行,简化开发者的线程安全考虑
    • 生成初始化报告,便于问题诊断
  • Start 阶段

    • 执行高效的循环任务处理
    • 避免额外的资源申请和注册操作
    • 专注于业务逻辑的执行效率

资源申请和注册操作在 Initialize 阶段完成,Start 阶段将不会在有框架层面的资源申请和注册操作,专注于业务逻辑的执行效率。

2. 逻辑实现与部署运行分离

AimRT 采用了清晰的分层设计:

  • 业务逻辑层

    • 专注于功能实现
    • 不需关心部署细节
    • 提供 C/C++/Python 多语言接口
  • 部署运行层

    • 灵活的部署配置
    • 可选的通信方式(共享内存/网络)
    • 插件化的扩展机制

初始化流程

AimRT 的主要组件包括:

  • configurator 配置管理器
  • plugin 插件管理器
  • executor 执行器
  • logger 日志管理器
  • signal 信号管理器

AimRT 各个组件流程是基于状态机实现,其中初始化流程分为

  • init configurator
  • init plugin
  • init executor
    • init main thread executor
    • init guard thread executor
    • init executor manager
  • init logger
  • init allocator
  • init rpc
  • init channel
  • init parameter
  • init module

AimRT 对于每个阶段,都会维护一个 hook 函数列表,用户开发插件时,可以注册自己的 hook 函数,在每个阶段执行时,会调用这些 hook 函数, 具体实现如下,所以当需要扩展 AimRT 功能时,可以实现自己的 hook 函数,并注册到 AimRTCore 中,本文也会基于此分析 AimRT 在每个阶段干了什么。

void AimRTCore::EnterState(State state) {
  state_ = state;
  for (const auto& func : hook_task_vec_array_[static_cast<uint32_t>(state)])
    func();
}

并且对于每个阶段都会有对应的 manager 类, 这也提供了极大的扩展性,本文也会基于此分析 AimRT 在每个阶段干了什么。

1. configurator

configurator是 AimRT 的配置管理器,主要负责解析和管理配置文件。在初始化阶段完成了检查配置文件是是否存在以及为整个框架提供了配置基础,后续的组件初始化都会依赖这些配置信息。

2. plugin

plugin是 AimRT 的插件管理器,主要负责加载和管理插件。在初始化阶段完成了插件的加载和初始化,并提供了插件的注册和调用机制‘

3. executor

executor 存在三个,分别是 main thread executor, guard thread executor 和 executor manager。

  • main thread 初始化过程中,会把当前线程绑定到指定的 CPU 核心上运行,并设置调度策略,这样做的目的是为了减少线程切换带来的开销,提高性能。
  try {
    util::SetNameForCurrentThread(name_);
    util::BindCpuForCurrentThread(options_.thread_bind_cpu);
    util::SetCpuSchedForCurrentThread(options_.thread_sched_policy);
  } catch (const std::exception& e) {
    AIMRT_WARN("Set thread policy for main thread get exception, {}", e.what());
  }
  • guard thread 实现初始化过程中也会执行上述的绑核和调度策略设置,并且实现了一个多生产者-单消费者的任务队列模型,在后续的执行过程中,插件或者module 会选择指定的执行器,并且会把任务提交到这个队列中,然后由 guard thread 负责调度执行。

  • executor manager 主要负责管理主线程执行器和守护线程执行器以及自主选择的执行器,并且提供了插件或者 module 选择执行器和提交任务的接口,可以看到AimRT支持多个种类的执行器选择,具体可以看官网有详细的执行器配置说明

  RegisterAsioExecutorGenFunc();
  RegisterTBBExecutorGenFunc();
  RegisterSimpleThreadExecutorGenFunc();
  RegisterTImeWheelExecutorGenFunc();

4. log

在 AimRT 框架中,日志管理器(LoggerManager)负责处理所有的日志记录和管理功能。其主要功能包括:

  1. 初始化日志后端:

    • Initialize 方法中,首先注册了控制台日志和文件日志的后端生成函数。这使得框架能够支持多种日志输出方式。
    RegisterConsoleLoggerBackendGenFunc();
    RegisterRotateFileLoggerBackendGenFunc();
    
  2. 配置加载:

    • 从传入的 YAML 配置节点中加载日志选项,确保日志管理器根据用户的配置进行初始化。
    if (options_node && !options_node.IsNull())
        options_ = options_node.as<Options>();
    
  3. 创建日志后端:

    • 遍历配置中的后端选项,查找对应的日志后端生成函数,并创建日志后端实例。确保不允许重复的日志后端类型。
    for (auto& backend_options : options_.backends_options) {
        auto finditr = logger_backend_gen_func_map_.find(backend_options.type);
        AIMRT_CHECK_ERROR_THROW(finditr != logger_backend_gen_func_map_.end(),
                                "Invalid logger backend type '{}'.",
                                backend_options.type);
    
        auto logger_backend_ptr = finditr->second();
        AIMRT_CHECK_ERROR_THROW(
            logger_backend_ptr,
            "Gen logger backend failed, logger backend type '{}'.",
            backend_options.type);
    
        if (!logger_backend_ptr->AllowDuplicates()) {
            AIMRT_CHECK_ERROR_THROW(
                std::find_if(logger_backend_vec_.begin(), logger_backend_vec_.end(),
                             [type = backend_options.type](const auto& backend_ptr) -> bool {
                                 return backend_ptr->Type() == type;
                             }) == logger_backend_vec_.end(),
                "Logger backend type'{}' do not allow duplicate.", backend_options.type);
        }
    
        logger_backend_ptr->Initialize(backend_options.options);
        logger_backend_vec_.emplace_back(std::move(logger_backend_ptr));
    }
    

AimRT 的日志管理器还支持多种日志输出方式,具体配置方式可以查看日志配置,在v0.9.0-rc2的版本还支持自定义配置日至输出方式。

5. rpc

在讲RPCChannel这些之前需要先讲一下过滤器,过滤器也是AimRT的一个核心概念,Filter 在 RPC 或 Channel 每次被调用时触发,以一种类似洋葱的运行结构,如下图就是AimRT官方提供的示意图,很形象直观。

在 AimRT 框架中,RPC 管理器(RpcManager)负责处理远程过程调用的初始化和管理。

  1. 注册 RPC 后端:

    • Initialize 方法中,首先注册本地 RPC 后端和调试日志过滤器。
    RegisterLocalRpcBackend();
    RegisterDebugLogFilter();
    
  2. 配置加载:

    • 从传入的 YAML 配置节点中加载 RPC 选项,确保 RPC 管理器根据用户的配置进行初始化。
    if (options_node && !options_node.IsNull())
        options_ = options_node.as<Options>();
    
  3. 初始化 RPC 注册表和后端管理器:

    • 创建 RPC 注册表并设置日志记录器。
    • 设置 RPC 后端管理器的日志记录器、RPC 注册表和客户端/服务器过滤器管理器。
    rpc_registry_ptr_ = std::make_unique<RpcRegistry>();
    rpc_registry_ptr_->SetLogger(logger_ptr_);
    rpc_backend_manager_.SetLogger(logger_ptr_);
    rpc_backend_manager_.SetRpcRegistry(rpc_registry_ptr_.get());
    
  4. 初始化 RPC 后端:

    • 根据配置初始化指定的 RPC 后端,并注册到 RPC 后端管理器中。
    • 确保每个后端类型在配置中有效。
    for (auto& backend_options : options_.backends_options) {
        auto finditr = std::find_if(
            rpc_backend_vec_.begin(), rpc_backend_vec_.end(),
            [&backend_options](const auto& ptr) {
                return ptr->Name() == backend_options.type;
            });
    
        AIMRT_CHECK_ERROR_THROW(finditr != rpc_backend_vec_.end(),
                                "Invalid rpc backend type '{}'",
                                backend_options.type);
    
        (*finditr)->SetRpcRegistry(rpc_registry_ptr_.get());
        (*finditr)->Initialize(backend_options.options);
        rpc_backend_manager_.RegisterRpcBackend(finditr->get());
    }
    
  5. 设置客户端和服务器规则:

    • 根据配置设置客户端和服务器的后端和过滤器规则。
    rpc_backend_manager_.SetClientsBackendsRules(client_backends_rules);
    rpc_backend_manager_.SetClientsFiltersRules(client_filters_rules);
    rpc_backend_manager_.SetServersBackendsRules(server_backends_rules);
    rpc_backend_manager_.SetServersFiltersRules(server_filters_rules);
    
  6. 初始化后端管理器:

    • 完成 RPC 后端管理器的初始化,确保所有配置的后端和规则生效。
    rpc_backend_manager_.Initialize();
    

通过这些步骤,AimRT 的 RPC 管理器为远程过程调用提供了灵活且可扩展的支持,允许用户根据需求配置不同的 RPC 后端和过滤器规则。这种设计使得 AimRT 能够高效地处理分布式系统中的远程调用。

6. channel

在 AimRT 框架中,通道管理器(ChannelManager)负责处理消息通道的初始化和管理,其主要功能包括:

  1. 注册通道后端和过滤器:

    • Initialize 方法中,首先注册本地通道后端和调试日志过滤器。这些注册操作为后续的通道管理提供了基础。
    RegisterLocalChannelBackend();
    RegisterDebugLogFilter();
    
  2. 配置加载:

    • 从传入的 YAML 配置节点中加载通道选项,确保通道管理器根据用户的配置进行初始化。
    if (options_node && !options_node.IsNull())
        options_ = options_node.as<Options>();
    
  3. 初始化通道注册表和后端管理器:

    • 创建通道注册表并设置日志记录器。
    • 设置通道后端管理器的日志记录器、通道注册表和发布/订阅过滤器管理器。
    channel_registry_ptr_ = std::make_unique<ChannelRegistry>();
    channel_registry_ptr_->SetLogger(logger_ptr_);
    channel_backend_manager_.SetLogger(logger_ptr_);
    channel_backend_manager_.SetChannelRegistry(channel_registry_ptr_.get());
    
  4. 初始化通道后端:

    • 根据配置初始化指定的通道后端,并注册到通道后端管理器中。
    • 确保每个后端类型在配置中有效。
    for (auto& backend_options : options_.backends_options) {
        auto finditr = std::find_if(
            channel_backend_vec_.begin(), channel_backend_vec_.end(),
            [&backend_options](const auto& ptr) {
                return ptr->Name() == backend_options.type;
            });
    
        AIMRT_CHECK_ERROR_THROW(finditr != channel_backend_vec_.end(),
                                "Invalid channel backend type '{}'",
                                backend_options.type);
    
        (*finditr)->SetChannelRegistry(channel_registry_ptr_.get());
        (*finditr)->Initialize(backend_options.options);
        channel_backend_manager_.RegisterChannelBackend(finditr->get());
    }
    
  5. 设置发布和订阅规则:

    • 根据配置设置发布和订阅主题的后端和过滤器规则。
    channel_backend_manager_.SetPubTopicsBackendsRules(pub_backends_rules);
    channel_backend_manager_.SetPublishFiltersRules(pub_filters_rules);
    channel_backend_manager_.SetSubTopicsBackendsRules(sub_backends_rules);
    channel_backend_manager_.SetSubscribeFiltersRules(sub_filters_rules);
    
  6. 初始化后端管理器:

    • 完成通道后端管理器的初始化,确保所有配置的后端和规则生效。
    channel_backend_manager_.Initialize();
    

通过这些步骤,AimRT 的通道管理器为消息传递提供了灵活且可扩展的支持,允许用户根据需求配置不同的通道后端和过滤器规则。

  1. module

在 AimRT 框架中,模块管理器(ModuleManager)负责处理模块的加载和初始化。其主要功能包括:

  1. 初始化检查:

    • 确保模块代理配置器在初始化之前已经设置。
    • 通过原子操作确保模块管理器只能被初始化一次,避免多次初始化带来的潜在问题。
    AIMRT_CHECK_ERROR_THROW(
        module_proxy_configurator_,
        "Module proxy configurator is not set before initialize.");
    
    AIMRT_CHECK_ERROR_THROW(
        std::atomic_exchange(&state_, State::kInit) == State::kPreInit,
        "Module manager can only be initialized once.");
    
  2. 配置加载:

    • 从传入的 YAML 配置节点中加载模块选项,确保模块管理器根据用户的配置进行初始化。
    if (options_node && !options_node.IsNull())
        options_ = options_node.as<Options>();
    
  3. 加载动态库:

    • 遍历配置中的包选项,加载所有动态库。
    • 使用 ModuleLoader 加载指定路径的包,并记录加载的模块信息。
    for (auto& pkg_options : options_.pkgs_options) {
        auto module_loader_ptr = std::make_unique<ModuleLoader>();
        module_loader_ptr->SetLogger(logger_ptr_);
        module_loader_ptr->LoadPkg(pkg_options.path, pkg_options.disable_modules, pkg_options.enable_modules);
        module_loader_map_.emplace(pkg_options.path, std::move(module_loader_ptr));
    }
    
  4. 初始化直接注册的模块:

    • 遍历已注册的模块,检查模块指针的有效性。
    • 初始化模块包装器,并将其添加到模块管理器中。
    for (const auto& item : registered_module_vec_) {
        const auto* module_ptr = item.second;
        AIMRT_CHECK_ERROR_THROW(module_ptr != nullptr, "Module point is null!");
        InitModule(module_wrapper_ptr.get());
        module_wrapper_map_.emplace(module_name, std::move(module_wrapper_ptr));
    }
    
  5. 初始化动态库加载的模块:

    • 遍历动态库加载的模块,检查模块指针的有效性。
    • 初始化模块包装器,并将其添加到模块管理器中。
    for (const auto& module_loader_itr : module_loader_map_) {
        const auto& module_name_list = module_loader.GetLoadedModuleNameList();
        for (const auto& module_name : module_name_list) {
            const auto* module_ptr = module_loader.GetModule(module_name);
            AIMRT_CHECK_ERROR_THROW(module_ptr != nullptr, "Module point is null!");
            InitModule(module_wrapper_ptr.get());
            module_wrapper_map_.emplace(module_name, std::move(module_wrapper_ptr));
        }
    }
    

总结

可以发现,AimRT 框架的初始化流程包括配置管理器、插件管理器、执行器、日志管理器、RPC 管理器、通道管理器和模块管理器的初始化。每个组件在初始化阶段根据配置文件进行资源申请、注册和设置,从代码中也可以看出,AimRT提供的可扩展性和可定制化程度非常高,要定制化实现某个功能只要使用类似于 RegisterHOOK类似的函数即可集成进入框架。

### 关于机器人的介绍 #### 资本竞争背景 在人形机器人赛道上,存在显著的资本竞争态势。“南”有机器人,“北”则为银河通用。这两大公司之间的竞争不仅限于资金投入,在产品创新和技术研发方面也展开了激烈角逐[^2]。 #### 技术实力与发展历程 机器人专注于先进的人工智能技术和机器人解决方案的研发与应用。凭借强大的科研团队以及持续的技术积累,公司在自然语言处理、计算机视觉等多个领域取得了突破性的成果。这些技术进步推动了机器人产品的不断迭代升级,使其在市场上保持竞争优势。 #### 商业化进程 机器人致力于将前沿科技转化为实用的产品和服务,积极拓展商业应用场景。除了消费级市场外,还深入探索工业自动化、医疗健康等行业领域,通过提供定制化的能解决方案帮助企业提高效率并降低成本。目前,机器人已经在多个行业实现了成功的项目落地案例,证明了其技术创新能力和商业模式的有效性。 #### 主要产品线 虽然具体型号未提及,但从整体描述来看,机器人涵盖了多种类型的能设备,包括但不限于家用服务型机器人、教育辅助工具等。这类产品通常集成了语音交互功能,能够作为孩子学习过程中的伙伴角色,帮助解释道理或者指导完成家庭作业等内容,从而增强用户体验感和粘性[^3]。 ```python # 示例代码展示如何创建个简单的对话接口模拟器 def chatbot_response(user_input): responses = { '为什么要去上学': '因为可以学到很多有趣的知识哦。', '为什么要好好写作业': '认真做作业可以帮助巩固课堂上学到的内容呢!' } return responses.get(user_input, "我不太明白你的意思") print(chatbot_response('为什么要去上学')) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值