Creating Custom Behaviors

The MoveIt Studio SDK is built around 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.

Overview

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.

Writing a New Behavior in C++

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 do 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).

Important Behavior Design Guidelines

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

Behaviors must not block while ticking

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.

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>

// Include headers for your custom Behaviors
#include <my_package/my_basic_behavior.hpp>
#include <my_package/my_shared_resources_behavior.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 MoveIt Studio's core functionality
    my_custom_behaviors:
      - "my_package::MyBehaviorLoader"  # Your new custom Behavior loader plugin

Note

Finished reference Behaviors can be found in the MoveIt Studio Example Behaviors repository.