MoveIt Studio Behavior Interface
2.3.0
Library for developing custom behaviors for use in MoveIt Studio
|
This package contains the base classes used to create new Behavior plugins that can be executed as part of Objectives.
In MoveIt Studio, Behaviors are the building blocks used to create Objectives. They define a discrete sensing, planning, motion execution, or decision-making step.
MoveIt Studio relies on the BehaviorTree.Cpp library's behavior tree implementation. If you are not already familiar with BehaviorTree.Cpp, we strongly recommend that you review its introductory documentation before continuing.
MoveIt Studio Behaviors are C++ classes that derive from BehaviorTreeCpp BT::TreeNode
classes.
As a developer, you can create custom Behaviors within your own packages and have the Objective Server load them at runtime from plugins.
To do this, you must complete three main steps:
Refer to the MoveIt Studio Documentation for a summary of the concepts described below.
Before starting to create your new Behavior, decide if you need to use ROS functionality within that Behavior.
Behaviors that do not need to interact with ROS can derive from one of the BehaviorTree.Cpp action node types, such as BT::SyncActionNode
or BT::StatefulActionNode
.
Behaviors that do need to interact with ROS need to derive from the SharedResourcesNode
class provided in this package. This class adds an additional constructor parameter that allows pointing it to an instance of a ROS node (along with some additional shared resources) when it is created. The SharedResourcesNode
class is templated, and takes the BT::SyncActionNode
and BT::StatefulActionNode
types as the template parameter. The type provided as the template parameter will be used as the base class for SharedResourcesNode
.
Your Behavior class needs to implement the virtual functions in the BehaviorTree.Cpp action node class it is derived from. Refer to the BehaviorTree.Cpp documentation to see what you specifically need to do (for example, read here for BT::StatefulActionNode
).
Your source code needs to include separate .hpp
and .cpp
files to allow writing a Behavior loader plugin in the next step.
Here are a few key things to keep in mind when creating your own Behaviors:
When the behavior tree is ticked, it is important that the state of each Behavior can be evaluated quickly. This happens in the Behavior's tick()
function. It is important that tick()
can finish very quickly, so for Behaviors derived from BT::SyncActionNode
do not perform long-running processes within the Behavior's implementation of tick()
.
As a rule of thumb, a process that runs longer than 1 millisecond should be implemented as an asynchronous action node.
One approach for implementing asynchronous Behaviors is to handle long-running processes in a separate thread, so that ticking the Behavior just checks if the long-running process has completed yet. Inheriting from BT::StatefulActionNode
provides a simple way to allow your Behavior to check the progress of this long-running process.
Returning a failure state allows us to handle errors through the structure of the Objective behavior tree while keeping the system up and running.
Custom Behaviors are registered with the Objective Server behavior tree factory through a Behavior loader plugin. This is a class that is loaded at runtime using pluginlib, instantiated within the Objective Server, and called to register the Behaviors.
Behavior loader plugins are derived from the SharedResourcesNodeLoaderBase class.
An example implementation of a Behavior loader plugin would look like this:
Edit the config/base_config.yaml
file to add your new Behavior loader plugin to the objectives.behavior_loader_plugins
entry:
This UML diagram shows the relationships between the example user-implemented MyBasicBehavior
and MySharedResourcesBehavior
classes, the user-implemented MyBehaviorLoader
Behavior loader plugin, and the base classes provided in this package and Behaviortree.Cpp.