C++中机器人应用程序的行为树(ROS2)
马库斯·布赫霍尔茨
一、说明
由于 BT 是提供创建 BT 框架(C++ 年)的C++库,因此本文重点介绍 BT 的概述。 您对此领域的兴趣可以通过在线课程和实践课程来扩展,我强烈推荐。如果您对学习ROS,机器人,AI,ML,软件等更感兴趣,请查看The ConstructSim网站,并从您可以学到的内容中获得灵感。他们还有一个完美的机器人开发人员大师班,让您进入梦想中的公司。
此外,对于在线课程,请下载免费书籍并从此YouTube频道中获得灵感。
在这里,您还可以找到BT的精彩介绍链接到在线环境,您可以在其中运行移动机器人。
二、行为树、软件架构
BT 框架是一个C++库,使您能够在机器人或游戏应用程序中构建 BT。BT 提供了抽象方法(内置于 C++ 年)来保护应用程序中的逻辑关系。例如,如果您开发游戏,则行为树可以与游戏的性能相关,或者定义游戏角色在某些游戏情况下的行为方式等。在这方面,我们声明BT框架提供了在C++类之间启用逻辑/抽象关系的工具,这些类的方法是根据程序的逻辑树(BT)结构调用的。
BT 可以与状态机 (SM) 相关联,但是 BT 优于 SM 存在某些差异。SM 的主要问题是状态之间的转换随着状态数量的增加而增加。
SM 中的状态是紧密连接的,当需要重用时,通常会出现问题。诚然,设计师(软件工程师)可以轻松处理SM,但一个人无疑会发现很难理解和预测系统的整体行为。这些问题中的大多数都由行为树解决,它增强了模块化设计并且可以重用。行为树本质上是分层的。
下图为您提供了BT的一般概述。 如您所见,BT框架可以独立运行,无需ROS(正如我在设计游戏时提到的)。稍后我将给你一个例子,说明如何构建一个简单的BT并运行你的第一个程序。请注意,官方网站上的教程非常出色,应相应地进行研究以解决所有剩余问题。

我们可以想象您的机器人执行任务。为了保证机器人(例如机器人清洁器)的任务性能,“主要”任务分为几个子任务,如路径规划、感知、空间定向和电源管理等(在 ROS 中,所有这些子任务都可以分布在节点上)。所有这些子任务都可以被视为低级任务,这些任务不是由BT构建的。
一个任务或动作可以依赖于一个或多个先前的动作,同时,不同的条件必须为真/假才能影响机器人/应用程序动作。
关于机器人技术,BT是一种从低级任务控制(路径规划器,电源管理等)转移到更高层次行为的抽象。
当我们设计机器人系统时,我们仍然必须考虑基本组件(低级动作/任务)如何工作并寻找优化(例如,通过添加启发式来提高路径算法的性能)。
然而,为了构建整个机器人应用程序的架构,我们需要转向更高层次的编程抽象,这意味着将路径规划器或电源管理等任务作为一个简单的组件进行评估。
我们使用高度抽象的概念,因此详细说明这种抽象的位置似乎是合理的。下图概述了抽象堆栈。

所描绘的堆栈可以考虑如下。工程师/设计师负责机器人应用程序的需求,并构建应用程序的逻辑流程(创建行为树)。
BT(逻辑流)的结构模仿机器人的行为(游戏中的人物)。我们可以说,设计人员在逻辑上连接了机器人基元以反映机器人应用的规范。
BT 被建模为 XML 格式化文件。C++框架(BehaviorTree.CPP)允许在您的应用程序中构建BT。机器人任务的定义(C++中的程序功能)完成以下堆栈。
在讨论BT的概念时,我使用了逻辑流公式。在软件设计的上下文中,我们可以将所讨论的概念评估为某些软件模块(节点)之间的逻辑连接。逻辑连接可以描述为使用某个逻辑运算符,如 AND、OR 或 NOT 包装到 BT 框架中。
BT框架的美妙之处在于,设计人员可以完全实现所有机制和其他组件,以构建复杂的逻辑结构。此外,逻辑流可以设计为多线程应用程序,而无需不必要的工作。支持线程处理的所有C++机制都合并到框架中。
三、逻辑基元
由于本文的想法是对BT的唯一介绍,我将重点介绍基本的逻辑组件,我将在后面的实际C++示例中使用这些组件。ROS2(银河)的例子,你可以在这里或课程中找到。
在简单的原理中(仅出于本文的目的),我们可以区分两个逻辑节点(BT 机制)。回退实现逻辑OR和序列节点,实现AND逻辑。
两者都可以描述如下,
回退节点(OR 运算符)。(作者)

BT 的体系结构在应用程序读取的 XML 文件中指定。BehaviorTree.CPP框架根据XML规范(我们说调用回调)管理逻辑流(勾选BT节点)。基于状态的节点(此处为任务)将返回信号发送到“根”(应用程序的主节点)。信号可以是成功、失败或正在运行。
下面的示例结合了上述机制,并显示了上述信号如何在BT上分布。

我们可以这样描述上面的BT,
推导出这种BT哲学,我们可以通过考虑以下示例来描述以下BT的工作原理。
1. 呈现的BT由4个任务组成,落回和序列节点。
2. 将刻度发送到该回退的根。我们沿着左侧走。回退向任务 1 发送一个时钟周期,这是一个失败(当然这只是模拟)。通常,成功或失败的决定是由正在运行的程序做出的。在我们的例子中,回调返回失败。
请注意,现在我们运行回退节点(OR),它至少需要一个成功才能返回成功,因此可以检查所有节点。对于序列节点 (AND),第一个 FAILURE 终止“节点调查”。
3. 与任务 1 类似,任务 2 回调返回失败。由于我们仍然运行回退机制(OR),因此我们继续。
4. 现在,即时报价被发送到序列节点 (AND)。序列将时钟周期发送到任务 3 并接收成功,但序列是逻辑 AND 操作,因此我们继续检查是否全部成功。
任务 4 是成功,因此根也收到成功。
描述上述 BT 描述的逻辑的 XML 可以表述为:
<root main_tree_to_execute = "MainTree" ><BehaviorTree ID="MainTree"><Fallback name="root"><Task1 name="task_1"/><Task2 name="task_2"/><Sequence><Task3 name="task_3"/><Task4 name="task_4"/></Sequence></Fallback></BehaviorTree></root>)";
下面您将找到可以在 ROSject 中运行的代码。(ROS2银河)
#include "behaviortree_cpp_v3/bt_factory.h"using namespace BT;class Task1 : public BT::SyncActionNode
{public:Task1(const std::string& name) : BT::SyncActionNode(name, {}){}// You must override the virtual function tick()NodeStatus tick() override{std::cout << "Task1: " << this->name() << std::endl;return BT::NodeStatus::FAILURE;}
};class Task2 : public BT::SyncActionNode
{public:Task2(const std::string& name) : BT::SyncActionNode(name, {}){}// You must override the virtual function tick()NodeStatus tick() override{std::cout << "Task2: " << this->name() << std::endl;return BT::NodeStatus::FAILURE;}
};class Task3 : public BT::SyncActionNode
{public:Task3(const std::string& name) : BT::SyncActionNode(name, {}){}// You must override the virtual function tick()NodeStatus tick() override{std::cout << "Task3: " << this->name() << std::endl;return BT::NodeStatus::SUCCESS;}
};class Task4 : public BT::SyncActionNode
{public:Task4(const std::string& name) : BT::SyncActionNode(name, {}){}// You must override the virtual function tick()NodeStatus tick() override{std::cout << "Task4: " << this->name() << std::endl;return BT::NodeStatus::SUCCESS;}
};static const char* xml_text_medium = R"(<root main_tree_to_execute = "MainTree" ><BehaviorTree ID="MainTree"><Fallback name="root"><Task1 name="task_1"/><Task2 name="task_2"/><Sequence><Task3 name="task_3"/><Task4 name="task_4"/></Sequence></Fallback></BehaviorTree></root>)";int main()
{BehaviorTreeFactory factory;factory.registerNodeType<Task1>("Task1");factory.registerNodeType<Task2>("Task2");factory.registerNodeType<Task3>("Task3");factory.registerNodeType<Task4>("Task4");std::cout << "\n------------ BUILDING A NEW TREE ------------" << std::endl;auto tree = factory.createTreeFromText(xml_text_medium);tree.tickRoot();std::cout << std::endl;// }return 0;
}
In the ROSject (on a Linux terminal) perform the following actions,
cd /ros2_ws/src/bt_course_files/BehaviorTree.CPP/course_bttouch bt_medium.cpp// go to VSC to the same folder where you created bt_medium.cpp// paste above code// in the same folder modify the CMakeList.txt by adding:
// CompileExample("bt_medium")cd ros2_ws/src/bt_course_files/BehaviorTree.CPP/build
cmake ..
make// after building is finishcd course_bt
./bt_medium
预期产出
感谢您的阅读。
四、结论
关于BT的概念,需要更多的文档介绍;BT 可以与状态机 (SM) 相关联,但是 BT 优于 SM 存在某些差异。SM 的主要问题是状态之间的转换随着状态数量的增加而增加,而BT没有类似难点。
相关文章:

C++中机器人应用程序的行为树(ROS2)
马库斯布赫霍尔茨 一、说明 以下文章为您提供了对机器人应用程序或框架中经常使用的行为树的一般直觉:ROS,Moveit和NAV2。了解行为 Tress (BT) 框架的原理为您提供了在游戏领域应用知识的绝佳机会。BT可以与Unity或Unreal集成。 由…...

像Vuex一样使用redux
redux基础知识 本篇文章主要介绍redux的基本使用方法,并简单封装,像vuex一样写redux 学习文档 英文文档: https://redux.js.org/ 中文文档: http://www.redux.org.cn/ Github: https://github.com/reactjs/redux redux是什么 redux和vuex几乎是一…...

关于模板的大致认识【C++】
文章目录 函数模板函数模板的原理函数模板的实例化模板参数的匹配原则 类模板类模板的定义格式类模板的实例化 非类型模板参数typename 与class模板的特化函数模板特化类模板特化全特化偏特化 模板的分离编译 函数模板 函数模板的原理 template <typename T> //模板参数…...

C#如何遍历类的属性,并获取描述/注释
要获取属性的描述/注释,需要使用System.ComponentModel命名空间中的DescriptionAttribute。可以通过反射获取属性上的DescriptionAttribute,并获取其Description属性值。 首先,需要引入System.ComponentModel命名空间: using Sy…...

ffmpeg 子进程从内存读取文件、提取图片到内存
除了网络、文件io,由python或java或go或c等语言开启的ffmpeg子进程还支持pipe,可以从stdin读入数据,输出转化后的图像到stdout。无需编译 ffmpeg,直接调用 ffmpeg.exe不香么! “从内存读”可用于边下载边转码…...

Springboot+Netty+WebSocket搭建简单的消息通知
SpringbootNettyWebSocket搭建简单的消息通知 一、快速开始 1、添加依赖 <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.36.Final</version> </dependency> <dependency>…...

@RefreshScope静态变量注入
RefreshScope注解通常用于注入实例变量,而不是静态变量。由于静态变量与类直接关联,刷新操作无法直接影响它们。 如果你需要动态刷新静态变量的值,一种可行的方案是使用一个通过Value注解注入的实例变量,并在该实例变量的getter方…...

多维时序 | MATLAB实现SABO-CNN-GRU-Attention多变量时间序列预测
多维时序 | MATLAB实现SABO-CNN-GRU-Attention多变量时间序列预测 目录 多维时序 | MATLAB实现SABO-CNN-GRU-Attention多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 多维时序 | MATLAB实现SABO-CNN-GRU-Attention多变量时间序列预测。 模型描…...

SAP 凭证项目文本 增强 demo2
SAP 凭证项目文本 增强 demo2 增强点 AC_DOCUMENT METHOD if_ex_ac_document~change_initial. DATA: ls_item TYPE accit, ls_exitem TYPE accit_sub, lv_sgtxt TYPE bseg-sgtxt, lv_bktxt TYPE bkpf-bktxt, lv_zuonr TYPE bseg-zuonr, lv_blart TYPE bkpf-blart, lv_zprono TY…...

一套基于C#语言开发的LIMS实验室信息管理系统源码
实验室信息管理系统(LIMS)是指帮助实验室组织和管理实验数据的计算机软件系统,它将实验室操作有机地组织在一起,以满足实验室工作流程的所有要求。它能以不同的方式支持实验室的工作,从简单的过程(如样品采集和入库)到复杂的流程(…...

kubesphere部署rocketmq5.x,并对外暴露端口
kubesphere是青云开源的k8s管理工具,用户可以方便的通过页面进行k8s部署的部署,rocketmq则是阿里开源的一款mq平台,现在版本为5.1.3版本,较比4.x版本的rocketmq有比较大的调整:比如客户端的轻量化(统一通过…...

5.8 汇编语言:汇编高效除法运算
通常情况下计算除法会使用div/idiv这两条指令,该指令分别用于计算无符号和有符号除法运算,但除法运算所需要耗费的时间非常多,大概需要比乘法运算多消耗10倍的CPU时钟,在Debug模式下,除法运算不会被优化,但…...

如何通过python来给手机发送一条短信?
要通过Python发送短信到手机,您可以使用不同的短信服务提供商的API。以下是一个使用Twilio和Sinch服务提供商的示例,您可以根据自己的选择来决定使用哪个。 使用Twilio发送短信: 首先,注册一个Twilio账户并获取您的账户SID、认证令牌和Twilio号码。 安装 twilio 包,如果您…...

无涯教程-PHP - IntlChar类
在PHP7中,添加了一个新的 IntlChar 类,该类试图公开其他ICU函数。此类定义了许多静态方法和常量,可用于操作unicode字符。使用此类之前,您需要先安装 Intl 扩展名。 <?phpprintf(%x, IntlChar::CODEPOINT_MAX);print (IntlCh…...

【Linux操作系统】Linux系统编程中信号捕捉的实现
在Linux系统编程中,信号是一种重要的机制,用于实现进程间通信和控制。当某个事件发生时,如用户按下CtrlC键,操作系统会向进程发送一个信号,进程可以捕获并相应地处理该信号。本篇博客将介绍信号的分类、捕获与处理方式…...

【PHP】基础语法变量常量
文章目录 PHP简介前置知识了解静态网站的特点动态网站特点 PHP基础语法代码标记注释语句分隔(结束)符变量变量的基本概念变量的使用变量命名规则预定义变量可变变量变量传值内存分区 常量基本概念常量定义形式命名规则使用形式系统常量魔术常量 PHP简介 PHP定义:一…...

Failed to resolve: com.github.mcxtzhang:SwipeDelMenuLayout:V1.3.0
在allprojects下的repositories闭包里面添加jcenter()和maven {url https://jitpack.io},具体可以看你的第三方框架需要添加什么仓库,大多数都只需要上面两个。 我的build.gradle(Project)完整内容如下: buildscript …...

常用 Python IDE 汇总(非常详细)从零基础入门到精通,看完这一篇就够了
写 Python 代码最好的方式莫过于使用集成开发环境(IDE)了。它们不仅能使你的工作更加简单、更具逻辑性,还能够提升编程体验和效率。 每个人都知道这一点。而问题在于,如何从众多选项中选择最好的 Python 开发环境。初级开发者往往…...

【Hive】HQL Map 『CRUD | 相关函数』
文章目录 1. Map 增删改查1.1 声明 Map 数据类型1.2 增1.3 删1.4 改1.5 查 2. Map 相关函数2.1 单个Map 3. Map 与 String3.1 Map 转 string3.2 string 转 Map 1. Map 增删改查 1.1 声明 Map 数据类型 语法:map<基本数据类型, 基本数据类型> 注意是<>…...

ELF修复基本工作原理
ELF修复基本工作原理 ELF(Executable and Linkable Format)是一种常见的可执行文件和可链接文件的格式,广泛用于Linux和UNIX系统中。ELF修复是指对ELF文件进行修改或修复,以确保其正确加载和执行。 ELF修复的基本工作原理如下: 识别ELF文件:首先,需要识别和验证目标文…...

matlab实现输出的几种方式(disp函数、fprintf函数、print函数)
matlab实现输出的几种方式(disp函数、fprintf函数、print函数) 输出为文本、文件、打印 1、disp函数 显示变量的值,如果变量包含空数组,则会返回 disp,但不显示任何内容。 矩阵 A [1 0]; disp(A)结果 字符串 S …...

C/C++数据库编程
文章目录 0. Mysql安装与开发环境配置1. win10 Navicat 连接虚拟机的MySQL需要关闭防火墙2. 由于找不到libmysql.dIl, 无法继续执行代码。重新安装程序可能会解决此问题。3. 测试连接数据库,并插入数据4. C封装MySQL增删改查操作 0. Mysql安装与开发环境配置 MySQL…...

通过python在unity里调用C#接口
log: 背景 最近在做虚拟人底层驱动sdk测试,因为后端使用的是C#,我个人更倾向于python编程辅助测试工作,测试sdk需要通过开发提供的接口方法文档,通过传测试场景参数调用方法进行单元测试 技术&工具 项目语言 C# 项目工具 unity 测试…...

C++笔记之左值与右值、右值引用
C笔记之左值与右值、右值引用 code review! 文章目录 C笔记之左值与右值、右值引用1.左值与右值2.右值引用——关于int&& r 10;3.右值引用——对比int&& r 10;和int& r 10;4.右值引用(rvalue reference)的概念 1.左值与右值 2.…...

JS逆向-某招聘平台token
前言 本文是该专栏的第56篇,后面会持续分享python爬虫干货知识,记得关注。 通常情况下,JS调试相对方便,只需要chrome或者一些抓包工具,扩展插件,就可以顺利完成逆向分析。目前加密参数的常用逆向方式大致可分为以下几种,一种是根据源码的生成逻辑还原加密代码,一种是补…...

LLMs高效的多 GPU 计算策略Efficient multi-GPU compute strategies
很有可能在某个时候,您需要将模型训练工作扩展到超过一个GPU。在上一个视频中,我强调了当您的模型变得太大而无法适应单个GPU时,您需要使用多GPU计算策略。但即使您的模型确实适合单个GPU,使用多个GPU加速训练也有好处。即使您正在…...

jvm-类加载子系统
1.内存结构概述 类加载子系统负责从文件系统或网络中加载class文件,class文件在文件开头有特定的文件标识 ClassLoader只负责class文件的加载,至于它是否运行,则由Execution Engine决定 加载的类信息存放于一块称为方法区的内存空间ÿ…...
【实例分割】(一)Mask R-CNN详细介绍带python代码
目录 1.🍀🍀实例分割定义 2.🍀🍀Mask R-CNN 3.🍀🍀经典的实例分割算法 4.🍀🍀Mask R-CNN python代码 整理不易,欢迎一键三连!!!…...

面试官问我Redis怎么测,我一脸懵逼!
有些测试朋友来问我,redis要怎么测试?首先我们需要知道,redis是什么?它能做什么? redis是一个key-value类型的高速存储数据库。 redis常被用做:缓存、队列、发布订阅等。 所以,“redis要怎么测试…...

【Spring Boot】四种核心类的依赖关系:实体类、数据处理类、业务处理类、控制器类
//1.配置项目环境,创建Spring Boot项目。 //2.数据库设置,配置数据库。 //3.创建实体类,映射到数据库。 //4.创建数据处理层类,Repository //5.创建业务处理类,Service类 //6.创建控制器类,Controller类 Ar…...