【行为树】04-响应式序列和异步节点

Reactive Sequences and Asynchronous Nodes

响应式序列和异步节点

下一个例子展示了SequenceNodeReactiveSequence之间的区别。

一个异步动作有它自己的线程。这允许用户使用阻塞函数,但将执行流返回到树。

!!! 警告 “了解更多关于异步操作”

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](asynchronous_nodes.md).
// 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;
}

方法MoveBaseAction::tick()在不同于调用MoveBaseAction::executeTick()的主线程中执行。

您需要负责实现一个有效的__halt()__功能。

用户还必须实现convertFromString<Pose2D>(StringView),就像在之前的教程中所示。

Sequence VS ReactiveSequence

以下示例应该使用简单的“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;
}

预期输出:

    --- 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!"

您可能已经注意到,当调用executeTick()时,MoveBase在第一次和第二次返回__RUNNING__,最终第三次返回__SUCCESS__。

BatteryOK 只会被执行一次。

如果我们使用ReactiveSequence,当子MoveBase返回RUNNING时,序列将被重新启动,并且条件BatteryOK将再次执行。

如果在任何时候,BatteryOK 返回 FAILUREMoveBase 动作将会被 中断(具体来说是_停止_)。

 <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!"

事件驱动树?

!!! 重要
我们使用命令tree.sleep()而不是std::this_thread::sleep_for()是有原因的。

方法Tree::sleep()应该是首选,因为当"某些东西发生变化"时,它可以被树中的一个节点中断。
AsyncActionNode::tick()完成或更一般地,调用方法TreeNode::emitStateChanged()时,Tree::sleep()会被中断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值