BehaviorTree.CPP
BehaviorTree.CPP copied to clipboard
Pause and resume tree execution
Hi @facontidavide ,
Is there a way to pause and resume the execution of a tree, let's say from a keyboard input or something running in parallel ?
I tried something with the following tree:
<root >
<BehaviorTree>
<ReactiveSequence name="sequence">
<Supervisor name="supervisor"/>
<SequenceStar name="sequenceStar">
<MyAsyncAction name="action_A"/>
<MyAsyncAction name="action_B"/>
</SequenceStar>
</ReactiveSequence>
</BehaviorTree>
</root>
where:
Supervisoris aCoroActionNodewhich always returnsSUCCESSexcept if the user hitpon the keyboard. Hittingptoggles between returningSUCCESSand returningRUNNINGMyAsyncActionis aCoroActionNodewhich returnsSUCCESSafter 5 seconds
With this setup, I can halt and resume the sequenceStar, but when it resumes, it always restarts from action_A even if it returned SUCCESS.
I was expecting that it would restart from action_B when I paused and resumed once action_A succeded.
Am I doing something wrong ?
Here is my code btw: src.zip
Whenever action_B returns SUCCESS, the SequenceStar is restarted. Therefore there is no reason why what Supervisor does outside the SequenceStar should affect it.
About the PAUSE/RESUME, I am actually planning to create an interface similar to "breakpoints" that are inserted and removed from the "outside". But For the time being, a solution for you would be to put your Supervisor inside the SequenceStar
But action_A and action_B would be still running sequentially.
What you need is to use ReactiveFallback instead of ReactiveSequence. Like this:
<root >
<BehaviorTree>
<ReactiveFallback>
<IsPaused name="check_pause"/>
<SequenceStar name="sequenceStar">
<MyAsyncAction name="action_A"/>
<MyAsyncAction name="action_B"/>
</SequenceStar>
</ReactiveSequence>
</BehaviorTree>
</root>
Instead of Supervisor, IsPaused returns SUCCESS if in pause state, or FAILURE otherwise. The problem is that the sequenceStar will be halted....
Whenever
action_Breturns SUCCESS, theSequenceStaris restarted. Therefore there is no reason why whatSupervisordoes outside the SequenceStar should affect it.
Actually, action_B is halted when Supervisor returns SUCCESS (first time I hit p). Here is my output:

What does action_B return when it is halted ? Maybe if I found a way to make it returns FAILURE, it will work. I'm gonna try the ReactiveFallback too.
About the PAUSE/RESUME, I am actually planning to create an interface similar to "breakpoints" that are inserted and removed from the "outside".
I think it would be a nice feature, but it is more reassuring to be able to interrupt the execution at any time. That's why I was thinking about something like a Supervisor node which would allow several modes:
- pause
- resume
- stop
- step by step
- inject an action or subtree
Instead of Supervisor, IsPaused returns SUCCESS if in pause state, or FAILURE otherwise. The problem is that the sequenceStar will be halted....
Indeed :)

I managed to make the first tree work by commenting the following line in the lib: https://github.com/BehaviorTree/BehaviorTree.CPP/blob/f54f6d83e5c06f44eae2b4d9700f5178dbdb4159/src/controls/sequence_star_node.cpp#L77 Why is the index reset when halt is triggered ?
If B is halted, index is reset If B returns FAILURE, index is not reset (it is however reset then because of the ReactiveSequence)
If I comment this line, the SequenceStar correctly restart to the halted action.
I have almost exactly the same needs and have been wanting to add pause / resume functionality. This is partly for debug purposes, for which @facontidavide 's breakpoint idea sound useful, but it is also a feature we need to implement our behaviors.
Imagine two behaviors, Docking and Error Recovery. We want to execute the Docking behavior unless we detect an Error in which case we want to perform Error Recovery and then resume Docking. Docking could be restarted, but sequences would have their current_child_idx_ reset and start ticking from the first child. In a well designed behavior tree the already completed nodes would detect that they completed successfully the first time and immediately return success.
Unfortunately that is not always possible, imagine picking up a cup, flipping it upside down to dump out the water, then flipping it back to upright and putting it down. The cup is in the same position, the only way to detect that the cup has been dumped out is to detect the water level or detect the water all over the floor. Detecting either of those is likely to be significantly more difficult than just resuming from where the tree was. In this hypothetical repeating the dumping of a cup might only waste time, but there can be actions that are dangerous to repeat.
Like @Acwok I toyed with creating a child class of sequence_start_node that removed the current_child_idx_ reset in halt, but we still want the ability of the node to be fully halted if necessary so I believe it needs a new function.
I propose to add a pause function to TreeNode; much like halt, this pause function only applies to async nodes in the RUNNING state. For most nodes pause will just call halt, but for sequence nodes (and any other where it makes sense) pause will not reset current_child_idx_.
I had originally hoped to not need to implement a new NodeStatus, but it seems like it must be added as (for example):
RUNNINGseems like it would work, but for StatefulActionNode,onStartwould not be recalled upon restart.IDLEseems like a good choice too, but thenonHaltedwould not be called if an ACTUAL halt occurs.
So it seems like a NodeStatus::PAUSED must be implemented that behaves almost identically to IDLE but allows the states to be differentiated.
EDIT: And I am happy to implement this change, I just want to discuss and make sure it's inline with what you'd want.
We had the same need (to pause and resume) in out team, too. While @BenArtes 's idea was what I thought at first, we found that it might not be a good idea to put the controller inside the main BT. This decision is made because we are using ROS, and when making an ActionServer, adding pause wasn't an option.
What we tried was to implement a way to start the tree from a certain state. We made the following changes to the library:
- Add a pair of functions to export/import the current state in string, to every control node. (e.g. current_child_idx_ for sequence nodes)
- Add a logger to send the uid, NodeStatus change, and the state export string, if any.
- Add a function inside Tree, to halt and recursively set the NodeStatus and state import string.
Then, a controller (another program) that does the following:
- Records the log taken from the above mentioned logger.
- Halt the main BT program if needed.
- Restores by sending the list of uid, NodeStatus, and state export string to the main BT program.
This way, we can separate the controller element and the logic element without trying to implement a pause state into ROS's Actionlib.
While our work is wip and I don't think I can publish it, this idea might be another take for the pause problem, so I'm sharing my opinions.
is there any updates on this issue? these features would be extremely helpful for debugging indeed
Many of the problems and proposed solutions in this thread are based on different use cases.
-
Groot 2 and BT.CPP 4.1.x finally implement the "breakpoint interface", which allow the user to interactively set breakpoints and wait for the user's input.
-
If you want to skip a certain branch, based on a certain internal state, you should probably use the preconditions introduced in version 4.X. This is probably what I would have suggested to @BenArtes now.
-
pausing the entire tree is as easy as stopping the tick of the root.
-
The solution suggested by @SubaruArai seems very interesting, but it is more related to persistency (an interesting topic).
5 @wisesama , what do YOU have in mind?
I am closing this issue because the scope is now unclear. Feel free to open another one with a more specific scope