Behavior tree 編程實戰
寫在前面的話
本文主要在前文的基礎上,通過《Behavior trees in ROS and AI》以及小明工坊中所提供的部分代碼進行實戰化分析。 此處給出兩處代碼地址以及原文: 古月居小明工坊 :https://www.guyuehome.com/5311 Behavior trees in ROS and AI : https://behaviortree.github.io/BehaviorTree.CPP/tutorial_01_first_tree/
如何建立一棵行為樹
行為樹枝干的編輯
建立行為樹的方法主要有兩種,一種是靜態編譯,一種是XML動態生成,這里主要剖析的是靜態編譯 的方法。 建立一棵行為樹的最基本的架構在 ROS-BehaviorTree/behavior_tree_core/src/gtest/external_ros_nodes_test.cpp 中得到了詳細的體現:
#include <behavior_tree.h>int main(int argc, char **argv)
{ros::init(argc, argv, "BehaviorTree");try{int TickPeriod_milliseconds = 1000;BT::ROSAction* action = new BT::ROSAction("action");BT::NegationNode* decorator = new BT::NegationNode("decorator");BT::ROSCondition* condition = new BT::ROSCondition("condition");BT:: SequenceNode* sequence1 = new BT::SequenceNode("seq1");sequence1->AddChild(decorator);decorator->AddChild(condition);sequence1->AddChild(action);Execute(sequence1, TickPeriod_milliseconds); // from BehaviorTree.cpp}catch (BT::BehaviorTreeException& Exception){std::cout << Exception.what() << std::endl;}return 0;
}
代碼心得: behavior_tree.h 文件為mit所開發的一款關于行為樹創建的庫,可以通過以下的鏈接獲得: https://github.com/BehaviorTree/BehaviorTree.CPP 整個枝干的創建過程基本上與C++中基礎的二叉樹的創建類似,通過new來生成新的結點以及定義指針來完成結點間對應關系的構建。但不同的是行為樹結合了ROS的特點,可以通過結點的調用將葉子進行單獨的編寫,從而起到了模塊化的作用。 如果代碼正確而所觀察到的rqt_graph產生了錯誤,即發生了節點間相互作用失敗的案例,可以嘗試通過檢查Cmake文件得以解決。 并在此處推薦ROS編譯的常用軟件Roboware ,可以自動生成Cmake.list以及pakeage.xml文件,還是比較方便的。 在創建完行為樹的基本枝干后就進入到了行為樹葉子結點的編輯中。
行為樹葉子結點的編輯
ROS-BehaviorTree功能包中并沒有對葉子結點進行具體功能的實現,只是在ROS-BehaviorTree/behavior_tree_core/src/路徑下給出了一些葉子結點編譯的模板程序。 而在古月居小明工坊的《ROS實驗 | 行為樹實現機器人智能》一文中給出了關于行為樹真正實現其功能的具體的案例。 我們以ROS-Behavior-Tree/behavior_tree_leaves/nodes/action_patrol.cpp為例,現在把代碼貼在下面:
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include <behavior_tree_core/BTAction.h>
#include <string>
#include <tf/transform_listener.h>
#include <geometry_msgs/Twist.h>
#include <turtlesim/Spawn.h>enum Status
{RUNNING, SUCCESS, FAILURE
}; class BTAction
{
protected:ros::NodeHandle nh_;actionlib::SimpleActionServer<behavior_tree_core::BTAction> as_;std::string action_name_;//定義發布的反饋和結果behavior_tree_core::BTFeedback feedback_; //action 反饋,SUCCESS,FAILUREbehavior_tree_core::BTResult result_; ros::Publisher turtle_vel = nh_.advertise<geometry_msgs::Twist>("turtle2/cmd_vel", 10);public:
//構造函數,boost參數綁定explicit BTAction(std::string name) :as_(nh_, name, boost::bind(&BTAction::execute_callback, this, _1), false),action_name_(name){as_.start();}~BTAction(){}void execute_callback(const behavior_tree_core::BTGoalConstPtr &goal){tf::TransformListener listener;tf::StampedTransform transform_a, transform_b, transform;listener.waitForTransform("/turtle2", "/point_a", ros::Time(0), ros::Duration(3.0));listener.lookupTransform("/turtle2", "/point_a", ros::Time(0), transform_a);listener.waitForTransform("/turtle2", "/point_b", ros::Time(0), ros::Duration(3.0));listener.lookupTransform("/turtle2", "/point_b", ros::Time(0), transform_b);double distance_a, distance_b;distance_a = sqrt(pow(transform_a.getOrigin().x(), 2) + pow(transform_a.getOrigin().y(), 2));distance_b = sqrt(pow(transform_b.getOrigin().x(), 2) + pow(transform_b.getOrigin().y(), 2));if (nh_.hasParam("/goal_point")){if(distance_a < 0.5) {nh_.setParam("/goal_point", "b");ROS_INFO("Change direction to b");}else if(distance_b < 0.5){nh_.setParam("/goal_point", "a");ROS_INFO("Change direction to b");}}else{nh_.setParam("/goal_point", "a");}std::string direction;nh_.getParam("/goal_point", direction);if(direction == "a"){ROS_INFO("Nav to a");transform = transform_a;}else if(direction == "b"){ROS_INFO("Nav to b");transform = transform_b;}geometry_msgs::Twist vel_msg;vel_msg.angular.z = 1.0 * atan2(transform.getOrigin().y(),transform.getOrigin().x());vel_msg.linear.x = 0.8;turtle_vel.publish(vel_msg);set_status(SUCCESS);ROS_INFO("Action nav_b is successful!");}void set_status(int status){feedback_.status = status;result_.status = feedback_.status;as_.publishFeedback(feedback_);as_.setSucceeded(result_);switch (status) {case SUCCESS:ROS_INFO("Action %s Succeeded", ros::this_node::getName().c_str() );break;case FAILURE:ROS_INFO("Action %s Failed", ros::this_node::getName().c_str() );break;default:break;}}
};int main(int argc, char** argv)
{ros::init(argc, argv, "action");ROS_INFO(" Enum: %d", RUNNING);ROS_INFO(" Action Ready for Ticks");BTAction bt_action(ros::this_node::getName());ros::spin();return 0;
}
這段代碼主要是在小海龜范圍內沒有敵情時,在固定的兩個點間以擬好的速度進行巡邏。 比較重要的是首先用了enum枚舉出了行為樹的三種反饋模式: SUCCESS,FAILURE,RUNNING. 接下來使用了C++中較為方便的類以及析構函數,將參數較為方便的傳遞給內部函數以及變量,有助于實現程序的模塊化進行。 接下來比較重要的就是這兩行:
behavior_tree_core::BTFeedback feedback_; //action 反饋,SUCCESS,FAILUREbehavior_tree_core::BTResult result_;
這兩行代碼主要是定義了行為樹所特有的反饋,以及結果(對于其實現的功能可以理解為結果,但實際上并不是結果那么簡單)。 以及對于反饋與結果具體內容的實現,可以在函數 set_status中具體體現:
void set_status(int status){feedback_.status = status;result_.status = feedback_.status;as_.publishFeedback(feedback_);as_.setSucceeded(result_);switch (status) {case SUCCESS:ROS_INFO("Action %s Succeeded", ros::this_node::getName().c_str() );break;case FAILURE:ROS_INFO("Action %s Failed", ros::this_node::getName().c_str() );break;default:break;}}
};
行為樹就是想這子葉反饋SUCCESS,FAILURE,或者RUNNING,將子葉的工作狀況反饋給上一級,從而實現不同情況的判斷,動作執行的時機等,使其具有了一定的智能。 在回調這一塊,我們在 ROS-Behavior-Tree/behavior_tree_leaves/nodes/condition_have_enemy.cpp 文件中可以得到更具體的展示:
distance = sqrt(pow(transform.getOrigin().x(),2)+pow(transform.getOrigin().y(),2));nh_.setParam("/produc_status",distance);if(distance<2){ROS_INFO("Find enemy!");set_status(SUCCESS);}else{ROS_INFO("Everything is ok");set_status(FAILURE);}}
在這里我們可以更加清晰的看到SUCCESS與FAILURE在行為樹狀態結點實現其功能時的重要性,從而可以一窺行為樹實現其功能中tick(),即回調,的重要性。
總結
以上是生活随笔 為你收集整理的Behavior tree 编程实战 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。