The next example shows the difference between a SequenceNode
and a ReactiveSequence
.
(接下来的例子将会展示SequenceNode和ReactiveSequence的区别
)
An Asynchronous Action has it's own thread. This allows the user to use blocking functions but to return the flow of execution to the tree.
(异步的动作有自己的线程。这允许用户使用阻塞函数将执行流程返回到行为树)
Learn more about Asynchronous Actions
(了解有关异步操作的详细信息)
Users should fully understand how Concurrency is achieved in BT.CPP and learn best practices about how to develop their own Asynchronous Actions. You will find an extensive article here.
(用户应充分了解如何在 BT.CPP 中实现并发并了解如何开发自己的异步操作,您将在这里找到一篇广泛的文章。)
// Custom type
struct Pose2D
{
double x, y, theta;
};
class MoveBaseAction : public AsyncActionNode
{
public:
MoveBaseAction(const std::string& name, const NodeConfiguration& config)
: AsyncActionNode(name, config)
{ }
static PortsList providedPorts()
{
return{ InputPort<Pose2D>("goal") };
}
NodeStatus tick() override;
// This overloaded method is used to stop the execution of this node.
void halt() override
{
_halt_requested.store(true);
}
private:
std::atomic_bool _halt_requested;
};
//-------------------------
NodeStatus MoveBaseAction::tick()
{
Pose2D goal;
if ( !getInput<Pose2D>("goal", goal))
{
throw RuntimeError("missing required input [goal]");
}
printf("[ MoveBase: STARTED ]. goal: x=%.f y=%.1f theta=%.2f\n",
goal.x, goal.y, goal.theta);
_halt_requested.store(false);
int count = 0;
// Pretend that "computing" takes 250 milliseconds.
// It is up to you to check periodically _halt_requested and interrupt
// this tick() if it is true.
while (!_halt_requested && count++ < 25)
{
std::this_thread::sleep_for( std::chrono::milliseconds(10) );
}
std::cout << "[ MoveBase: FINISHED ]" << std::endl;
return _halt_requested ? NodeStatus::FAILURE : NodeStatus::SUCCESS;
}
The method MoveBaseAction::tick()
is executed in a thread different from the main thread that invoked MoveBaseAction::executeTick()
.
(MoveBaseAction::tick()在一个不同于main主线程里面,它调用了
MoveBaseAction::executeTick()
.)
You are responsible for the implementation of a valid halt() functionality.
(你有负责实现终止 函数的功能)
The user must also implement convertFromString<Pose2D>(StringView)
, as shown in the previous tutorial.
(用户还必须实现convertFromString<Pose2D>(StringView),如前一教程所示。)
Sequence VS ReactiveSequence
这是一个Sequence例子
<root>
<BehaviorTree>
<Sequence>
<BatteryOK/>
<SaySomething message="mission started..." />
<MoveBase goal="1;2;3"/>
<SaySomething message="mission completed!" />
</Sequence>
</BehaviorTree>
</root>
int main()
{
using namespace DummyNodes;
using std::chrono::milliseconds;
BehaviorTreeFactory factory;
factory.registerSimpleCondition("BatteryOK", std::bind(CheckBattery));
factory.registerNodeType<MoveBaseAction>("MoveBase");
factory.registerNodeType<SaySomething>("SaySomething");
auto tree = factory.createTreeFromText(xml_text);
NodeStatus status;
std::cout << "\n--- 1st executeTick() ---" << std::endl;
status = tree.tickRoot();
tree.sleep( milliseconds(150) );
std::cout << "\n--- 2nd executeTick() ---" << std::endl;
status = tree.tickRoot();
tree.sleep( milliseconds(150) );
std::cout << "\n--- 3rd executeTick() ---" << std::endl;
status = tree.tickRoot();
std::cout << std::endl;
return 0;
}
期望的输出:
--- 1st executeTick() ---
[ Battery: OK ]
Robot says: "mission started..."
[ MoveBase: STARTED ]. goal: x=1 y=2.0 theta=3.00
--- 2nd executeTick() ---
[ MoveBase: FINISHED ]
--- 3rd executeTick() ---
Robot says: "mission completed!"
You may have noticed that when executeTick()
was called, MoveBase
returned RUNNING the 1st and 2nd time, and eventually SUCCESS the 3rd time.
(你会发现当executeTick()被调用时,MoveBase返回运行中在第一和第二次,在第三次返回成功
)
BatteryOK
is executed only once.
(BatteryOK只执行一次
)
If we use a ReactiveSequence
instead, when the child MoveBase
returns RUNNING, the sequence is restarted and the condition BatteryOK
is executed again.
(如果我们使用的是ReactiveSequence,当子节点MoveBase返回运行中,序列节点会重启并且BatteryOK会再次被执行
)
If, at any point, BatteryOK
returned FAILURE, the MoveBase
action would be interrupted (halted, to be specific).
(如果在一些点,BatteryOK 返回失败,MoveBase将会被中断,具体来说会停止
)
<root>
<BehaviorTree>
<ReactiveSequence>
<BatteryOK/>
<Sequence>
<SaySomething message="mission started..." />
<MoveBase goal="1;2;3"/>
<SaySomething message="mission completed!" />
</Sequence>
</ReactiveSequence>
</BehaviorTree>
</root>
期望输出:
--- 1st executeTick() ---
[ Battery: OK ]
Robot says: "mission started..."
[ MoveBase: STARTED ]. goal: x=1 y=2.0 theta=3.00
--- 2nd executeTick() ---
[ Battery: OK ]
[ MoveBase: FINISHED ]
--- 3rd executeTick() ---
[ Battery: OK ]
Robot says: "mission completed!"
Event Driven trees?
(事件驱动树?)
important
We used the command tree.sleep()
instead of std::this_thread::sleep_for()
for a reason.
【请注意,我们使用tree.sleep()而不是std::this_thread::sleep_for()是有原因的
】
The method Tree::sleep()
should be preferred, because it can be interrupted by a Node in the tree when "something changed". Tree::sleep() will be interrupted when an AsyncActionNode::tick()
is completed or, more generally, when the method TreeNode::emitStateChanged()
is invoked.
【应该首选方法Tree::sleep(),因为当“有什么变化”时,树中的节点可以中断它。当AsyncActionNode::tick()完成时,或者更一般地说,当调用TreeNode::emitStateChanged()方法时,Tree::sleep()将被中断。】
【说明】
BehaviorTree.CPP行为树学习系列是翻译自源英文网站,由于个人知识能力水平有限,如有错误的地方,请在评论区留言,会不断修改和完善的
本想全部都翻译一遍的,发现已经有大佬翻译过了,本着不重复造轮子的原则,大佬的翻译部分链接为
https://blog.csdn.net/qq_37766828/category_11885955.html
这里是目前为止后面未翻译的部分