Tutorial 4: Reactive Trees

翻译自:https://www.behaviortree.dev

Reactive Sequences and Asynchronous Nodes

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. [异步操作有它自己的线程。这允许用户使用阻塞函数,但将执行流程返回给树。]

warning “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() 在与调用 MoveBaseAction::executeTick() 的主线程不同的线程中执行。]

You are responsible for the implementation of a valid halt() functionality. [您负责实现有效的 halt() 功能。]

The user must also implement convertFromString<Pose2D>(StringView), as shown in the previous tutorial. [用户还必须实现 convertFromString(StringView),如上一教程所示。]

Sequence VS ReactiveSequence

The following example should use a simple SequenceNode. [以下示例应使用简单的 SequenceNode。]

 <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;
}

Expected output:

    --- 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 第一次和第二次返回 RUNNING,最后第三次返回 SUCCESS。]

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 返回 RUNNING 时,将重新启动序列并再次执行条件 BatteryOK。]

If, at any point, BatteryOK returned FAILURE, the MoveBase action would be interrupted (halted, to be specific). [如果在任何时候,BatteryOK 返回 FAILURE,MoveBase 操作将被中断(具体而言,暂停)。]

 <root>
     <BehaviorTree>
        <ReactiveSequence>
            <BatteryOK/>
            <Sequence>
                <SaySomething   message="mission started..." />
                <MoveBase       goal="1;2;3"/>
                <SaySomething   message="mission completed!" />
            </Sequence>
        </ReactiveSequence>
     </BehaviorTree>
 </root>

Expected output:

    --- 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() 将被中断。]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值