BehaviorTree / BehaviorTree.ROS2

BehaviorTree.CPP utilities to work with ROS2
Apache License 2.0
144 stars 59 forks source link

How to combine `StatefulActionNode` and `RosServiceNode`? #43

Closed li9i closed 7 months ago

li9i commented 7 months ago

This issue relates to issue #41.

Assume a tree consisting of a single Sequence of StatefulActionNode nodes, all belonging to class class TickRemoteTree : public BT::StatefulActionNode:

<root BTCPP_format="4">
  <BehaviorTree ID="tree">

    <Sequence>
      <TickRemoteTree robot_name="A" var="1"/>
      <TickRemoteTree robot_name="B" var="2"/>

      <TickRemoteTree robot_name="A" var="10"/>
      <TickRemoteTree robot_name="B" var="20"/> 

      <TickRemoteTree robot_name="A" var="100"/>
      <TickRemoteTree robot_name="B" var="200"/>
    </Sequence>

  </BehaviorTree>
</root>

What I am trying to achieve is execute each TickRemoteTree action sequentially, that is, make robot A sleep for 1 sec, then wake up, then make robot B sleep for 2 sec, then wake up, then make robot A sleep again for 10 sec, and so on. (Essentially the same thing as https://github.com/BehaviorTree/BehaviorTree.CPP/issues/242#issue-747304122)

The way this must be achieved is by sending variable var to robots A and B via a call to a service that they provide. Assume that var specifies the amount of time each robot should sleep. Since TickRemoteTree is a StatefulActionNode, each TickRemoteTree node returns RUNNING after being ticked, and execution should not continue further than each node, since all TickRemoteTree are wrapped inside a Sequence. After waking up, each robot calls a service called tockFromRemoteTree that TickRemoteTree provides, making the node able to escape from a RUNNING state and into a SUCCESS or FAILURE state.

However, it is clear I am misunderstanding some things because what happens in practice is that

It is as though all six nodes are pointers to a single changing object, but I would like to have six different objects. What is perhaps needed is some mix between RosServiceNode and StatefulActionNode, which seem to be complementary because the latter cannot deal with services on its own (at least to my understanding and programming level). Someone would say this is why the RosActionNode exists. But what if one needed to avoid actions altogether? (actions are unbridgeable between ROS 1 and ROS2).

li9i commented 7 months ago

Well the problem is found to be lying with the residing of the service server as a member of the TickRemoteTree class. I will be closing this issue as I believe that it stems only from this limitation, but feel free to answer the question below:

Why is it that when a node is reused in a tree, and the class of that node has a service server as a member (created through this_node->create_service)---why is it that if the service is called all instances of the node have their service callbacks called? I can understand that this is a delicate issue that might not be able to be implemented (or perhaps violates the logic of Behavior Trees altogether), my question is can it?