使用行為樹控制機器人(一) —— 節點

文章目錄

  • 一、背景需求
  • 二、創建ActionNodes
    • 1. 功能實現
      • 1.1 頭文件定義
      • 1.2 源文件實現
      • 1.3 main文件實現
      • 1.4 my_tree.xml 實現
    • 2. 執行結果
  • 三、 執行失敗處理
    • 1. 添加嘗試次數
      • 1.1 功能實現
      • 1.2 實驗結果
    • 2. 完善異常處理
      • 2.1 多節點組合兜底
      • 2.2 實驗結果

使用行為樹控制機器人(一) —— 節點
使用行為樹控制機器人(二) —— 黑板
使用行為樹控制機器人(三) —— 通用端口

近期在從ros1導航跨到ros2導航,ros2導航的任務調度邏輯采用的是行為樹,所以在魔改ros2導航前先知己知彼學習一下行為樹到底是個啥?本文也是在大佬的基礎上加入自己的學習過程記錄。(如整理有誤,還請指點)
學習時參考鏈接:ROS機器人行為樹教程

一、背景需求

關于行為樹各節點的定義及其作用的博客整理,可以先參考鏈接中的教程,后續看自己是否有時間整理,本文直接根據需求進行代碼實操。通過行為樹控制機器人的一簡單場景需求如下:

在這里插入圖片描述

二、創建ActionNodes

1. 功能實現

對于每個動作作為簡單案例多以打印輸出后,直接返回成功,后續根據對行為樹的掌握程度結合實際需求進行自定義復雜需求。

1.1 頭文件定義

根據場景需求創建如下 SimpleActionNode:

  • CheckBattery()
  • GripperInterface::open()
  • GripperInterface::close()
  • CameraInterface::open()
  • CameraInterface::close()
#ifndef BEHAVIOR_TREE_NODES_H
#define BEHAVIOR_TREE_NODES_H#include "behaviortree_cpp/bt_factory.h"
#include <iostream>// 同步動作節點 (無端口)
class ApproachObject : public BT::SyncActionNode
{
public:ApproachObject(const std::string& name, const BT::NodeConfig& config) : BT::SyncActionNode(name, config){}// 端口是節點與黑板(Blackboard)之間進行數據交換的接口// 必須實現靜態端口聲明方法static BT::PortsList providedPorts() {return {}; // 無端口}BT::NodeStatus tick() override;
};// 夾爪控制接口
class GripperInterface
{
public:GripperInterface() : _open(true) {}BT::NodeStatus open();BT::NodeStatus close();private:bool _open; // 共享狀態
};// 相機控制接口
class CameraInterface
{
public:CameraInterface() : _open(true) {}BT::NodeStatus open();BT::NodeStatus close();private:bool _open; // 共享狀態
};// 電池檢查函數聲明
BT::NodeStatus CheckBattery();#endif // BEHAVIOR_TREE_NODES_H

1.2 源文件實現

#ifndef BEHAVIOR_TREE_NODES_H
#define BEHAVIOR_TREE_NODES_H#include "behaviortree_cpp/bt_factory.h"
#include <iostream>// 同步動作節點 (無端口)
class ApproachObject : public BT::SyncActionNode
{
public:ApproachObject(const std::string& name, const BT::NodeConfig& config) : BT::SyncActionNode(name, config){}// 端口是節點與黑板(Blackboard)之間進行數據交換的接口// 必須實現靜態端口聲明方法static BT::PortsList providedPorts() {return {}; // 無端口}BT::NodeStatus tick() override;
};// 夾爪控制接口
class GripperInterface
{
public:GripperInterface() : _open(true) {}BT::NodeStatus open();BT::NodeStatus close();private:bool _open; // 共享狀態
};// 相機打開
BT::NodeStatus CameraInterface::open() 
{_open = true;std::cout << "CameraInterface::open" << std::endl;return BT::NodeStatus::SUCCESS;
}// 相機關閉
BT::NodeStatus CameraInterface::close() 
{std::cout << "CameraInterface::close" << std::endl;_open = false;return BT::NodeStatus::SUCCESS;
}// 電池檢查函數聲明
BT::NodeStatus CheckBattery();#endif // BEHAVIOR_TREE_NODES_H

1.3 main文件實現

#include "behavior_tree_nodes.h"
#include "behaviortree_cpp/bt_factory.h"int main()
{// 創建行為樹工廠BT::BehaviorTreeFactory factory;// 注冊自定義節點factory.registerNodeType<ApproachObject>("ApproachObject");// 注冊簡單條件節點 (使用正確的lambda簽名)factory.registerSimpleCondition("CheckBattery", [](BT::TreeNode&) { return CheckBattery(); });// 創建相機、夾爪實例并注冊動作CameraInterface camera;GripperInterface gripper;factory.registerSimpleAction("OpenCamera", [&camera](BT::TreeNode&) { return camera.open(); });factory.registerSimpleAction("OpenGripper", [&gripper](BT::TreeNode&) { return gripper.open(); });factory.registerSimpleAction("CloseGripper", [&gripper](BT::TreeNode&) { return gripper.close(); });factory.registerSimpleAction("CloseCamera", [&camera](BT::TreeNode&) { return camera.close(); });try {// 從XML文件加載行為樹auto tree = factory.createTreeFromFile("../trees/my_tree.xml");// 打印樹結構 (調試用)std::cout << "------ Behavior Tree Structure ------" << std::endl;BT::printTreeRecursively(tree.rootNode());std::cout << "------------------------------------" << std::endl;// 執行行為樹tree.tickWhileRunning();} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;return 1;}return 0;
}

上述采用的是三種注冊節點方式:

  • registerNodeType
  • registerSimpleCondition
  • registerSimpleAction
    ?
特性維度registerNodeTyperegisterSimpleActionregisterSimpleCondition
節點基類需繼承 BT::ActionNode 等自動封裝為 SimpleActionNode自動封裝為 SimpleConditionNode
返回值自行實現 tick() 返回狀態
SUCCESS / FAILURE / RUNNING
Lambda 返回 NodeStatus
SUCCESS / FAILURE / RUNNING
Lambda 返回 bool
SUCCESS / FAILURE
數據交互通過黑板端口(強類型)可選端口(需手動檢查)無端口(純條件檢查)
狀態維護支持(成員變量)不支持(無狀態)不支持
典型用途復雜動作/控制節點簡單一次性動作條件判斷(瞬時完成)
生命周期控制可重寫 halt() 等方法
線程安全更易實現需外部保證需外部保證

1.4 my_tree.xml 實現

在這里插入圖片描述

行為樹實現邏輯如上,xml文件定義如下:

<root BTCPP_format="4" main_tree_to_execute="MainTree"><BehaviorTree ID="MainTree"><Sequence name="root_sequence"><CheckBattery name="battery_check"/><OpenCamera name="open_camera"/><OpenGripper name="open_gripper"/><ApproachObject name="approach_object"/><CloseGripper name="close_gripper"/><CloseCamera name="close_camera"/></Sequence></BehaviorTree>
</root>

注意
XML中使用的標識符必須與用于注冊TreeNodes的標識符相一致,比如:xml文件定義的CheckBattery 要和 main文件中 factory.registerSimpleCondition("CheckBattery", [](BT::TreeNode&) { return CheckBattery(); });CheckBattery 作對應。

2. 執行結果

------ Behavior Tree Structure ------
----------------
root_sequencebattery_checkopen_cameragripper_controlRetryUntilSuccessfulopen_gripperrecovery_sequenceclose_camera_on_failureForceFailurerecovery_successapproach_objectclose_gripperclose_camera
----------------
------------------------------------
[ Battery: OK ]
CameraInterface::open
GripperInterface::open successfully!
ApproachObject: approach_object
GripperInterface::close
CameraInterface::close

三、 執行失敗處理

1. 添加嘗試次數

1.1 功能實現

觀察整個功能實現邏輯,如出現執行異常(如,夾爪初始化中打開夾爪失敗),無再次嘗試的可能。故調整代碼,在對打開夾爪進行多次打開嘗試,如嘗試3次,3次嘗試都失敗再返回錯誤。行為樹邏輯及xml文件如下:
在這里插入圖片描述

<root BTCPP_format="4" main_tree_to_execute="MainTree"><BehaviorTree ID="MainTree"><Sequence name="root_sequence"><CheckBattery name="battery_check"/><OpenCamera name="open_camera"/><RetryUntilSuccessful num_attempts="3"><OpenGripper name="open_gripper"/></RetryUntilSuccessful><ApproachObject name="approach_object"/><CloseGripper name="close_gripper"/><CloseCamera name="close_camera"/></Sequence></BehaviorTree>
</root>

為了實現打開次數的嘗試,為打開夾爪添加變量計數:

// 夾爪打開
BT::NodeStatus GripperInterface::open() 
{if(_open_cnt < 2){_open_cnt ++;std::cout << "\033[1;31m"<< "GripperInterface::open failed!"<< "\033[0m" << std::endl;return BT::NodeStatus::FAILURE;}_open = true;_open_cnt = 0;std::cout << "\033[1;32m"<< "GripperInterface::open successfully!"<<"\033[0m" << std::endl;return BT::NodeStatus::SUCCESS;
}

1.2 實驗結果

下圖第一次測試,修改xml嘗試 2次 打開夾爪失敗后,直接返回失敗;第二次測試,嘗試 3次 打開夾爪并成功運行整個流程。
在這里插入圖片描述

2. 完善異常處理

2.1 多節點組合兜底

觀察嘗試多次打開夾爪實現,發現若嘗試打開夾爪多次依舊打開失敗,直接躺平,沒有進行關閉相機操作,實際場景這是不允許的!!此時就用到了其他節點的組合(此處,想一想該如何組合節點):

在這里插入圖片描述
上述行為樹表示:
當3次嘗試打開夾爪均返回失敗,此時通過 Fallback節點,進行關閉相機操作。>

注意
ForceFailure裝飾器 無論相機是否關閉成功,即:
CloseCamera (SUCCESS) → ForceFailure(AlwaysSuccess) → 最終返回FAILURE CloseCamera (FAILURE) → ForceFailure(AlwaysSuccess) → 最終返回FAILURE
?
為什么要添加 ForceFailure節點,想象一下,若沒有該節點,通過Fallback節點關閉相機成功,此時返回成功,會繼續執行ApproachObject、CloseGripper等操作,這是不允許的。

<root BTCPP_format="4" main_tree_to_execute="MainTree"><BehaviorTree ID="MainTree"><Sequence name="root_sequence"><CheckBattery name="battery_check"/><OpenCamera name="open_camera"/><!-- 主控制流程 --><Fallback name="gripper_control"><!-- 嘗試3次開夾爪 --><RetryUntilSuccessful num_attempts="3"><OpenGripper name="open_gripper"/></RetryUntilSuccessful><!-- 失敗恢復流程 --><Sequence name="recovery_sequence"><CloseCamera name="close_camera_on_failure"/><!-- ForceFailure  裝飾器, 強制覆蓋子節點結果,始終返回FAILURE--><ForceFailure><AlwaysSuccess name="recovery_success"/></ForceFailure></Sequence></Fallback><ApproachObject name="approach_object"/><CloseGripper name="close_gripper"/><CloseCamera name="close_camera"/></Sequence></BehaviorTree>
</root>

2.2 實驗結果

通過下圖可以看出:嘗試 2次 打開夾爪失敗后,先關閉相機后 再直接返回失敗;第二次測試,嘗試 3次 打開夾爪并成功運行整個流程。
在這里插入圖片描述

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

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

相關文章

JavaScript Window Location

JavaScript Window Location JavaScript中的window.location對象是操作瀏覽器地址欄URL的一個非常有用的對象。它允許開發者獲取當前頁面的URL、查詢字符串、路徑等&#xff0c;并且可以修改它們來導航到不同的頁面。以下是關于window.location的詳細解析。 1. window.location…

Kubernetes生產環境健康檢查自動化指南

核心腳本功能&#xff1a; 一鍵檢查集群核心組件狀態自動化掃描節點/Pod異常存儲與網絡關鍵指標檢測風險分級輸出&#xff08;紅/黃/綠標識&#xff09;一、自動化巡檢腳本 (k8s-health-check.sh) #!/bin/bash # Desc: Kubernetes全維度健康檢查腳本 # 執行要求&#xff1a;kub…

消息隊列系統測試報告

目錄 一、項目背景 二、RabbitMQ介紹 1.什么是RabbitMQ&#xff1f; 2.RabbitMQ的工作流程是怎么樣的&#xff1f; 3.項目設計 三、測試概述 MQ 測試目標&#xff1a; 測試用例統計&#xff1a; 核心模塊測試詳情及代碼示例&#xff1a; 1. 數據庫管理&#xff08;Da…

基于 Axios 的 HTTP 請求封裝文件解析

import axios from "axios"; import { ElMessage } from "element-plus"; import store from "/store"; import router from "/router";// 創建axios實例 const service axios.create({baseURL: "http://localhost:8080/api&quo…

PowerDesigner生成帶注釋的sql方法

前提是name里面是有文字的&#xff1a; 方法開始&#xff1a; 第一步&#xff1a; Database → Edit Current DBMS → Script → Objects → Column → Add 把輸出模板改成&#xff1a; %20:COLUMN% %30:DATATYPE%[.Z:[%Compressed%? compressed][ %NULLNOTNULL%][%IDENTITY…

獵板PCB:專業鍵盤PCB板解決方案供應商

獵板PCB深耕印刷電路板&#xff08;PCB&#xff09;制造領域&#xff0c;憑借前沿技術與深厚積淀&#xff0c;在鍵盤PCB板細分市場積極布局&#xff0c;致力于為不同客戶提供多樣化、高性能的鍵盤PCB板產品&#xff0c;滿足多元需求。一、定義&#xff1a;鍵盤PCB板鍵盤PCB板&a…

基于 Spring Boot 的登錄功能實現詳解

在 Web 應用開發中&#xff0c;登錄功能是保障系統安全的第一道防線。本文將結合實際代碼&#xff0c;詳細解析一個基于 Spring Boot 框架的登錄功能實現&#xff0c;包括驗證碼生成、用戶驗證、Token 機制等關鍵環節。技術棧概覽本登錄功能實現涉及以下核心技術和組件&#xf…

vue+django 大模型心理學智能診斷評測系統干預治療輔助系統、智慧心理醫療、帶知識圖譜

vuedjango 大模型心理學智能診斷評測系統干預治療輔助系統、智慧心理醫療、帶知識圖譜文章結尾部分有CSDN官方提供的學長 聯系方式名片 文章結尾部分有CSDN官方提供的學長 聯系方式名片 關注B站&#xff0c;有好處&#xff01;編號:D003 pro基于大模型心理學問卷、智能診斷&…

【linux】企業級WEB應用服務器tomcat

一 WEB技術1.1 HTTP協議和B/S 結構操作系統有進程子系統&#xff0c;使用多進程就可以充分利用硬件資源。進程中可以多個線程&#xff0c;每一個線程可以被CPU調度執行&#xff0c;這樣就可以讓程序并行的執行。這樣一臺主機就可以作為一個服務器為多個客戶端提供計算服務。客戶…

【Unity優化】Unity多場景加載優化與資源釋放完整指南:解決Additive加載卡頓、預熱、卸載與內存釋放問題

【Unity優化】Unity多場景加載優化與資源釋放完整指南&#xff1a;解決Additive加載卡頓、預熱、卸載與內存釋放問題 本文將完整梳理 Unity 中通過 SceneManager.LoadSceneAsync 使用 Additive 模式加載子場景時出現的卡頓問題&#xff0c;分析其本質&#xff0c;提出不同階段的…

B 樹與 B + 樹解析與實現

一、磁盤存儲優化的核心邏輯 在大規模數據處理場景中&#xff0c;磁盤 I/O 效率是性能瓶頸的核心。磁盤訪問具有以下特性&#xff1a; 隨機訪問成本高&#xff1a;磁頭尋道時間&#xff08;Seek Time&#xff09;可達毫秒級&#xff0c;相比內存訪問&#xff08;納秒級&#…

MySQL 查詢相同記錄并保留時間最晚的一條

要在 MySQL 中查詢相同記錄并僅保留時間最晚的那一條&#xff0c;你可以使用以下幾種方法&#xff1a;方法一&#xff1a;使用子查詢和 GROUP BY假設你的表名為 your_table&#xff0c;時間字段為 create_time&#xff0c;其他用于判斷記錄相同的字段為 field1, field2 等&…

在 .NET Core 5.0 中啟用 Gzip 壓縮 Response

在 .NET Core 5.0 中啟用 Gzip 壓縮 Response 在 .NET Core 5.0 (ASP.NET Core 5.0) 中啟用 Gzip 壓縮主要通過響應壓縮中間件實現。以下是詳細配置步驟&#xff1a; 1. 安裝必要的 NuGet 包 首先確保已安裝響應壓縮包&#xff1a; dotnet add package Microsoft.AspNetCore.Re…

[Oracle] TRUNC()函數

TRUNC() 是 Oracle 中一個多功能函數&#xff0c;主要用于對數值、日期進行截斷操作1.TRUNC()函數用于數值處理語法格式TRUNC(number, decimal_places)參數說明number&#xff1a;要截斷的數值 decimal_places&#xff1a;保留的小數位數(可選)&#xff0c;默認為0(截斷所有小數…

GPT-oss:OpenAI再次開源新模型,技術報告解讀

1.簡介OpenAI 發布了兩款開源權重推理模型 gpt-oss-120b 與 gpt-oss-20b&#xff0c;均采用 Apache 2.0 許可&#xff0c;主打在代理工作流中執行復雜推理、調用工具&#xff08;如搜索、Python 代碼執行&#xff09;并嚴格遵循指令。120b 為 36 層 MoE 結構&#xff0c;活躍參…

python tcp 框架

目錄 python tcp 框架 asyncio websockets python tcp 框架 asyncio import asyncio import json import timeclass TCPClient:def __init__(self, host, port, heartbeat_interval10):self.host hostself.port portself.heartbeat_interval heartbeat_intervalself.read…

HTML 與 CSS:從 “認識標簽” 到 “美化頁面” 的入門指南

個人主頁&#xff1a;?喜歡做夢 目錄 &#x1f3a0;HTML &#x1f3a1;一、什么是HTML&#xff1f; ??1.定義 ??2.核心特點 ??3.HTML的基本結構 ??4.標簽的層次結構關系 &#x1f3a1;二、HTML的常用標簽 &#x1f305;1.文本列表標簽 標題標簽&#xff1a;h…

【MATLAB 2025a】安裝離線幫助文檔

文章目錄一、在 MATLAB 設置中安裝二、從math works 網站下載ISO&#xff1a;適用于給無法聯網的電腦安裝或自定義路徑三、startup文件說明四、重要說明&#x1f9e9;&#x1f9e9;【Matlab】最新版2025a發布&#xff0c;深色模式、Copilot編程助手上線&#xff01; 版本&#…

Linux系統編程Day8 -- Git 教程(初階)

往期內容回顧 基于Linux系統知識的第一個程序 自動化構建工具-make/Makefile gcc/g編譯及鏈接 Vim工具的使用 Linux常用工具&#xff08;yum與vim&#xff09; ?????? Linux系統編程Day4-- Shell與權限 回顧進度條程序的編寫&#xff1a; //.h文件內容 #include<stdio…