Skip to content

Add Pause and SequenceWithBlackboardMemory BT nodes #5247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

redvinaa
Copy link
Contributor

@redvinaa redvinaa commented Jun 9, 2025

Basic Info

Info Please fill out this column
Ticket(s) this addresses (add tickets here #5213)
Primary OS tested on Ubuntu
Robotic platform tested on custom robot
Does this PR contain AI generated software? No

Description of contribution in a few bullet points

Description of documentation updates required from your changes

  • Headers are well-documented
  • Added 2 new control nodes, they need to be described in the docs with examples (create mwe from the code in the discussion)

Description of how this change was tested

  • Have been using for a long time on humble, now on jazzy
  • Haven't tested on rolling, because I don't have a rolling stack

Future work that may be required in bullet points

  • Tests still need to be written for both BT nodes!

For Maintainers:

  • Check that any new parameters added are updated in docs.nav2.org
  • Check that any significant change is added to the migration guide
  • Check that any new features OR changes to existing behaviors are reflected in the tuning guide
  • Check that any new functions have Doxygen added
  • Check that any new features have test coverage
  • Check that any new plugins is added to the plugins page
  • If BT Node, Additionally: add to BT's XML index of nodes for groot, BT package's readme table, and BT library lists

Copy link

codecov bot commented Jun 9, 2025

Codecov Report

Attention: Patch coverage is 93.51852% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...r_tree/plugins/control/pause_resume_controller.cpp 94.36% 4 Missing ⚠️
...lugins/control/sequence_with_blackboard_memory.cpp 89.28% 3 Missing ⚠️
Files with missing lines Coverage Δ
...r_tree/plugins/control/pause_resume_controller.hpp 100.00% <100.00%> (ø)
...lugins/control/sequence_with_blackboard_memory.hpp 100.00% <100.00%> (ø)
...lugins/control/sequence_with_blackboard_memory.cpp 89.28% <89.28%> (ø)
...r_tree/plugins/control/pause_resume_controller.cpp 94.36% <94.36%> (ø)

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member

@SteveMacenski SteveMacenski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is missing tests coverage - but you know that :-) Also make sure to fix DCO

Copy link
Contributor

mergify bot commented Jun 10, 2025

@redvinaa, your PR has failed to build. Please check CI outputs and resolve issues.
You may need to rebase or pull in main due to API changes (or your contribution genuinely fails).

@redvinaa
Copy link
Contributor Author

Woah, this suddenly became more serious than I expected :D
I still have to write the tests and make this work with my jazzy stack, so it might take some time.

Copy link
Member

@SteveMacenski SteveMacenski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woah, this suddenly became more serious than I expected :D

I think this is really great work! Definitely happy to have it contributed! 🥇

I still have to write the tests and make this work with my jazzy stack, so it might take some time.

OK! Only really nitpicks, tests, and doc updates left in my book

Copy link
Contributor

mergify bot commented Jun 20, 2025

@redvinaa, your PR has failed to build. Please check CI outputs and resolve issues.
You may need to rebase or pull in main due to API changes (or your contribution genuinely fails).

1 similar comment
Copy link
Contributor

mergify bot commented Jun 20, 2025

@redvinaa, your PR has failed to build. Please check CI outputs and resolve issues.
You may need to rebase or pull in main due to API changes (or your contribution genuinely fails).

@redvinaa
Copy link
Contributor Author

@SteveMacenski I started writing the tests and I have a few questions.

The dummy BT node used for tests now returns IDLE after reset. Shouldn't it always return the status that it was set to?
The SequenceWithBlackboardMemoryNode logic has a resetChildren call, and all the dummy nodes return IDLE after it, making the tests fail.

Also, could you give me some pointers on how to write tests for the SequenceWithBlackboardMemoryNode? It needs changing the value of input ports between tests. I only found tests where the input port values are set in the SetUp part, but that cannot be parameterized. I also tried creating a new BT node instance for every single test, but I haven't managed to make that work yet, and even if I did, it wouldn't be a nice solution.

@redvinaa
Copy link
Contributor Author

I found the bug which didn't let me set current_child_idx input_port: #5295

@SteveMacenski
Copy link
Member

SteveMacenski commented Jul 2, 2025

Sorry for the delay - I've been underwater the last couple of weeks and now committing today to get through my email review backlog. I apologize for the delay and thank you for your help!

For tests, you can do something more in the theme of the action nodes: https://github.com/ros-navigation/navigation2/blob/main/nav2_behavior_tree/test/plugins/action/test_append_goal_pose_to_goals_action.cpp. Here, we make an example BT XML that is loaded and used instead. This is actually a better way to write the tests IMO and the dummy node manual tree population done in the control test you linked to is an outdated way. We know those can have some problems since the tree isn't being constructed in the same way it would be in an application. You could still potentially use a dummy node within the tree, but the tree itself is constructed fully, including the ports and blackboard.

That way, the blackboard of the BT is maintained between ticks since it acts like a proper behavior tree (because it is one). I think that answers your question!

@redvinaa redvinaa marked this pull request as ready for review July 4, 2025 13:27
Copy link
Contributor

mergify bot commented Jul 4, 2025

@redvinaa, your PR has failed to build. Please check CI outputs and resolve issues.
You may need to rebase or pull in main due to API changes (or your contribution genuinely fails).

@redvinaa
Copy link
Contributor Author

redvinaa commented Jul 4, 2025

I've implemented the tests using xml strings as in the action nodes, and DummyNodes. I've had to be creative, because none of the existing tests needed this level of complexity. Tell me what you think of this solution @SteveMacenski. I've added a new header file to access nodes from the tree and cast them to the correct type. I also had to modify the DummyNode implementation a bit, so it returns IDLE after resetting, but on tick it again returns the state that it was set to. I don't think it breaks existing tests.

Copy link
Contributor

mergify bot commented Jul 4, 2025

@redvinaa, your PR has failed to build. Please check CI outputs and resolve issues.
You may need to rebase or pull in main due to API changes (or your contribution genuinely fails).

Copy link
Contributor

mergify bot commented Jul 8, 2025

@redvinaa, your PR has failed to build. Please check CI outputs and resolve issues.
You may need to rebase or pull in main due to API changes (or your contribution genuinely fails).

1 similar comment
Copy link
Contributor

mergify bot commented Jul 8, 2025

@redvinaa, your PR has failed to build. Please check CI outputs and resolve issues.
You may need to rebase or pull in main due to API changes (or your contribution genuinely fails).

Copy link
Contributor

mergify bot commented Jul 8, 2025

@redvinaa, your PR has failed to build. Please check CI outputs and resolve issues.
You may need to rebase or pull in main due to API changes (or your contribution genuinely fails).

@redvinaa redvinaa force-pushed the pause-bt-nodes branch 2 times, most recently from 55be1a2 to dd67e94 Compare July 8, 2025 14:36
Copy link
Contributor

mergify bot commented Jul 8, 2025

@redvinaa, your PR has failed to build. Please check CI outputs and resolve issues.
You may need to rebase or pull in main due to API changes (or your contribution genuinely fails).

Copy link
Contributor

mergify bot commented Jul 10, 2025

This pull request is in conflict. Could you fix it @redvinaa?

redvinaa added 4 commits July 11, 2025 09:57
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
redvinaa added 8 commits July 11, 2025 09:57
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
@redvinaa
Copy link
Contributor Author

@SteveMacenski can you take a look at this?

Copy link
Member

@SteveMacenski SteveMacenski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last bit are docs:

  • Add to the Nav2 Specific BT Nodes page for how to use
  • Migration guide
  • Configuration guide for param descriptions on the nodes + examples
  • Add to the Navigation Plugins page

* Restart the loop only if (reset_on_failure == true)
*
*/
class SequenceWithBlackboardMemoryNode : public BT::ControlNode
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is otherwise the same as the Sequence node, can we inherit from it, call the base class within the tick function and return the value? We can set the port if needed on the terminal states. That would make it easier to maintain unless there's a reason we cannot.

Also, do we want this to be a normal sequence or a reactive sequence? Typically I think of users with reactive sequences or pipeline sequences with Nav2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would make it a lot easier, but as I see the current_child_idx is private in the member class. I opened a PR to change that, but it's gonna take time to get accepted (or rejected).

class SequenceWithBlackboardMemoryNode : public BT::ControlNode
{
public:
SequenceWithBlackboardMemoryNode(const std::string & name, const BT::NodeConfiguration & conf);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is also very similar to a BT.CPP provided node: https://www.behaviortree.dev/docs/nodes-library/sequencenode/#sequencewithmemory which means a different thing. Maybe we can rename here like SequenceWithState?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it is confusing. I think the name should definitely include blackboard because it's what mostly distinguishes this node's functionality. State I think is also confusing because all the nodes have states already (like RUNNING and SUCCESS). What about SequenceWithBlackboardIndex?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think of blackboard being an implementation detail if you said something like 'output', but I don't object.

This is actually a good ChatGPT question:

  • SequenceWithProgressOutput
  • SequenceWithIndex
  • IndexedSequence
  • SequenceWithRunningIndex

How about something like IndexedSequence? I think just WithBlackboardIndex or WithIndex implies that the port is only an output, not a possible input as well. But I don't want to spend too much time on naming, so whatever you feel is best

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, blackboard is an implementation detail, but the name definitely has to include the info that the index is read and written to (and also that it's a sequence, of course). I think calling it something with "blackboard" is close enough to the truth, while being far more descriptive than the chatgpt generated ones. Do you have something better than "blackboard" that still captures this info?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StatefulSequence, PersistentSequence, ResumableSequence

I'm thinking Stateful or Resumable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think stateful is not a lot better than state, for the same reasons. Resumable also means something different, like stopping and then continuing again. This sequence doesn't stop, it just does the processing in chunks (node by node). So nothing to resume in my opinion.

If it's out of these three, I'd go with persistent. But it's still hard to decipher from the name what exactly persists.
Then I'd rather go back to IndexedSequence, the word index I think also takes us closer to the truth.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resumable also means something different, like stopping and then continuing again.

Isn't this how it works? If you give it a child idx of 3, wouldn't it start at child idx 3?

If it's out of these three, I'd go with persistent. But it's still hard to decipher from the name what exactly persists.

Works for me 👍 I think that's what the doc guides are to some degree. I don't think anyone could know what Truncate Path vs Truncate Path Local means without looking at some docs without knowing anything about them

case BT::NodeStatus::FAILURE:
RCLCPP_ERROR(
logger_, "%s child returned FAILURE", state_names.at(state_).c_str());
return BT::NodeStatus::FAILURE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: What's the logical recovery if an On Paused or On Resume branches return a failure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now in that case we halt and set the internal state to RESUMED.
I think the transition branches shouldn't fail, unless there was an unexpected error that should fail this node too. At least that was my intuition, but I haven't put much thought into this edge case.

The reasoning is also valid, that if a transition failed, we should stay in the state before the transition. Though I don't know how we would handle that, because we either add recovery branches to this node (which would make it practically unusable), or return failure (but that halts the node, and maybe risks accidentally staying in PAUSED state).

Let me know what you think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe have a retry tag for the number of times to retry a state transition? We have that in other BT nodes so that wouldn't be out of place here and give the opportunity for small networking errors to be recoverable. If it ultimately fails, maybe its best to set the entire BT node to failure and halt it. Let the upstream nodes of this one figure out how to handle that.

I don't think it would be good to just resume, if the ON_RESUME state didn't finish, for example. There could be some initializations or activations that didn't happen that wouldn't make navigating possible. I think if those transition branches fail, that's a terminal failure state (after a couple of retries).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe have a retry tag for the number of times to retry a state transition?

Why would this be the responsibility of the PauseResumeController? There is a Retry BT node, I think that should be used to utilize the modularity of the BT.

I don't think it would be good to just resume, if the ON_RESUME state didn't finish

Definitely agree with this one.

Copy link
Member

@SteveMacenski SteveMacenski Jul 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we return failure from the PauseResumeController, do we maintain the current errored state for a retry to come in and re-tick to attempt? I don't think so from


  if (status() == BT::NodeStatus::IDLE) {
    state_ = RESUMED;
  }

Which I think is actually the correct behavior. Its easier to retry within this node knowing its current state since a failure caused by the RESUME or PAUSED branch don't need retries, only the ON_RESUME and ON_PAUSED branches need retries generated by a failed transition. I don't think it makes sense to do that externally since that's tied to internal implementation information. It would be very difficult for another node to know if a failure from this one should trigger a retry or not without leaking alot of its state onto the blackboard. That starts to get a little complex and I think retry within a Controller BT node is pretty common across our Nav2 and BT.CPP nodes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant retry as part of the ON_PAUSE and ON_RESUME branches, like this

<PauseResumeController pause_service_name="/pause" resume_service_name="/resume">
    <!-- RESUMED branch -->

    <!-- PAUSED branch (optional) -->

    <RetryUntilSuccessful num_attempts="3">
        <!-- ON_PAUSE branch (optional) -->
    </RetryUntilSuccessful>

    <RetryUntilSuccessful num_attempts="3">
        <!-- ON_RESUME branch (optional) -->
    </RetryUntilSuccessful>
</Pause>

This would be transparent and wouldn't complicate the node logic unnecessarily.

What's the advantage of doing it as part of the PauseResumeController?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, uh, that makes sense too :-)

Can you actually add that as part of your docs examples to have the ON_ transition branches use retries? That would meet my request!

@SteveMacenski SteveMacenski requested a review from Copilot July 14, 2025 21:39
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces two new behavior tree control nodes—SequenceWithBlackboardMemory and PauseResumeController—along with test utilities and comprehensive tests to enable pause/resume functionality and persistent sequencing state.

  • Added DummyNode and get_node_from_tree test utilities to support node status injection and retrieval.
  • Implemented SequenceWithBlackboardMemoryNode and PauseResumeController plugins, registered them in CMake and XML index.
  • Added unit tests for both new BT nodes and updated CMakeLists to include the new tests.

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/utils/test_dummy_tree_node.hpp Added DummyNode test utility
test/utils/get_node_from_tree.hpp Introduced helper to retrieve nodes from a tree
test/plugins/control/test_sequence_with_blackboard_memory.cpp Added tests for sequence with blackboard memory
test/plugins/control/test_pause_resume_controller.cpp Added tests for pause/resume controller
test/plugins/control/CMakeLists.txt Registered new control node tests
plugins/control/sequence_with_blackboard_memory.cpp Implemented SequenceWithBlackboardMemoryNode
plugins/control/pause_resume_controller.cpp Implemented PauseResumeController
nav2_behavior_tree/nav2_tree_nodes.xml Declared new BT nodes in XML index
include/nav2_behavior_tree/plugins/control/sequence_with_blackboard_memory.hpp Declared ports and docs for sequence node
include/nav2_behavior_tree/plugins/control/pause_resume_controller.hpp Declared ports, states, and docs for pause node
CMakeLists.txt Linked new plugin libraries
Comments suppressed due to low confidence (3)

nav2_behavior_tree/plugins/control/pause_resume_controller.cpp:68

  • The exception message refers to "PauseNode" but the class is named PauseResumeController. Update the message to match the class name to avoid confusion.
            "PauseNode must have at least one and at most four children "

nav2_behavior_tree/include/nav2_behavior_tree/plugins/control/pause_resume_controller.hpp:71

  • The XML usage example ends with </Pause> but the node tag is PauseResumeController. Update the closing tag to </PauseResumeController> for consistency.
 * </Pause>

nav2_behavior_tree/include/nav2_behavior_tree/plugins/control/sequence_with_blackboard_memory.hpp:37

  • The documentation references a reset_on_failure option which doesn't exist in this node. Remove or update this line to reflect the actual behavior.
 *   Restart the loop only if (reset_on_failure == true)

@SteveMacenski
Copy link
Member

Do with the AI generated code review as you see fit. If you think its pedantic or unnecessary, ignore. If it brings up a good point, please fix

redvinaa added 2 commits July 15, 2025 15:41
Signed-off-by: redvinaa <redvinaa@gmail.com>
Signed-off-by: redvinaa <redvinaa@gmail.com>
@SteveMacenski
Copy link
Member

@redvinaa I also noticed the BT.CPP change was merged in - but no rush

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants