MoveIt Studio Behavior Interface  2.0.2
Library for developing custom behaviors for use in MoveIt Studio
MoveIt Studio Behavior Interface

This package contains the base classes used to create new Behavior plugins that can be executed as part of Objectives.


Documentation

What is a Behavior?

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.

How do I create my own Behaviors?

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:

  1. Create a new C++ class defining your custom Behavior.
  2. Create a Behavior loader plugin to register your custom Behavior with the Objective Server's behavior tree factory.
  3. Add the name of the plugin library to your robot config package's YAML file to allow that type of robot to load those Behaviors when running Objectives.

Refer to the MoveIt Studio Documentation for a summary of the concepts described below.

Writing a new Behavior in C++

Before starting to create your new Behavior, decide if you need to use ROS functionality within that Behavior.

If you do not need ROS...

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.

If you need ROS...

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.

Implementing the Behavior

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.

Important Behavior Design Patterns

Here are a few key things to keep in mind when creating your own Behaviors:

Do not block ticking the tree

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.

Indicate error states by returning BT::NodeStatus::FAILURE

Returning a failure state allows us to handle errors through the structure of the Objective behavior tree while keeping the system up and running.

Creating a Behavior loader plugin

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:

#include <behaviortree_cpp/bt_factory.h>
#include <moveit_studio_behavior_interface/behavior_context.hpp>
#include <moveit_studio_behavior_interface/shared_resources_node_loader.hpp>
#include <pluginlib/class_list_macros.hpp>
// Headers for your custom Behaviors
#include <my_package/my_basic_behavior.hpp>
#include <my_package/my_shared_resources_behaviopr.hpp>
namespace my_package
{
class MyBehaviorLoader : public SharedResourcesNodeLoaderBase
{
public:
void registerBehaviors(BT::BehaviorTreeFactory& factory,
const std::shared_ptr<BehaviorContext>& shared_resources) override
{
// Register a Behavior derived from basic BehaviorTree.Cpp node types
registerBehavior<MyBasicBehavior>(factory, "MyBasicBehavior");
// Register a Behavior derived from SharedResourcesNode
registerBehavior<MySharedResourcesBehavior>(factory, "MySharedResourcesBehavior", shared_resources);
}
};
}
PLUGINLIB_EXPORT_CLASS(my_package::MyBehaviorLoader,
moveit_studio::behaviors::SharedResourcesNodeLoaderBase);

Adding the loader plugin to the robot base config package

Edit the config/base_config.yaml file to add your new Behavior loader plugin to the objectives.behavior_loader_plugins entry:

objectives:
behavior_loader_plugins:
core:
- "moveit_studio::behaviors::CoreBehaviorsLoader" # Behaviors to support Studio's core functionality
my_custom_behaviors:
- "my_package::MyBehaviorLoader" # Your new custom Behavior loader plugin

Architecture

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.

Behavior UML Diagram