Adds onLoopBeforeTick callback to BT::TreeExecutionServer. #101
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Changes
Adds
virtual bool onLoopBeforeTick()
callback to theBT::TreeExecutionServer
, that runs before the behavior tree tick() and, if it returns false, skips the tick. It is the natural counterpart toonLoopAfterTick(BT::NodeStatus)
.Problem Statement
TL:DR; In my opinion, TreeExecutionServer's interface needs this callback to be a flexible base class.
Design Thoughts
BT::TreeExecutionServer
, like many classes in the BehaviorTree ecosystem is locked down withprivate
member variables and the PImpl pattern. I believe that it is clearly designed to only be extended via overriding the callbacks. Thus, I think it is vital that it has full expressiveness in its callbacks alone.User Story
I am using BehaviorTree.CPP and .ROS for a real-world robotics project, including the
BT::TreeExecutionServer
. Most of my changes are in a child class of theBT::TreeExecutionServer
, calledChildServer
here. We are visualizing trees, live, using Groot2.For the foreseeable future, we have non-reactive behavior trees that rely on many layers of sequence behavior nodes. To make it possible to go slowly with the robot, one behavior at a time, I implemented pause and resume ROS services of type
std_srv/Trigger
. These setChildServer::isPaused
accordingly.To implement pause, you have to prevent the behavior tree from being ticked. BehaviorTree.CPP lets you inject a pretick callback on any behavior node. From the selection of available callbacks in BT.CPP and
BT::TreeExecutionServer
, it was the only option I had. (I would have much preferred that theexecute()
loop call a protected functionvirtual serverTick()
, that I could then overload to conditionally invoke the parent's function.) My callback was a member function ofChildServer
. It blocked using a spinning loop on a ROS timer that regularly polledrclcpp::ok
andChildServer::isPaused
. (The alternative was to return failure - which I rejected because the goal is to not tick, not to fail.)The action server was blocked when I only wanted to avoid ticking the tree. I found it impossible to replicate the same checks performed by the loop of
BT::TreeExecutionServer::execute
. I couldn't poll the action goal state because the action server is buried in the PImpl, and thegoal_handle
is passed between ROS callbacks and not stored in the class. I would have also liked to invokestop_action()
from my code, but it was a lambda hidden insideexecute
.This approach crashed sometimes. My best guesses for why:
TreeNode::executeTick
for a node that doesn't exist any more, it can crash.Discussion
This PR is to add a
virtual bool onLoopBeforeTick()
callback that runs before the behavior tree tick and, if it returns false, skips the tick.Benefits:
In conclusion, I think this change makes TreeExecutionServer a more robust and flexible base class.