Creating a Behavior to Publish a Message to a Topic

This example demonstrates how to write a Behavior that publishes ROS messages to a topic. This example will publish a std_msgs::msg::ColorRGBA to a topic. If you need a Behavior that publishes a std_msgs::msg::String to a topic, check out the PublishString Behavior that’s included in MoveIt Pro!

Technical Background

Behaviors that inherit from the SharedResourcesNode class can access an instance of rclcpp::Node which is managed by the Objective Server through the shared_resources_ class member.

This node can be used to interact with the ROS 2 middleware, for example by creating a Publisher that publishes message to a topic.

Behaviors which use ROS 2 Nodes should use this shared node instead of creating and independently spinning their own nodes. This is to avoid runtime deadlocks which can be caused by having multiple nodes and executors owned by the same process.

Implementing the PublishColorRGBA Behavior

Custom Behaviors in MoveIt Pro are built within ROS packages, so to add a new behavior we will need to create a new package.

This public repo contains some demo Behaviors to show an example of this.

This package needs to be within the workspace which is loaded by the MoveIt Pro Docker container on startup, which by default is located at ~/moveit_pro/moveit_studio_ur_ws.

To create a new PublishColorRGBA Behavior, first create a header file in the example_behaviors package at example_behaviors/include/example_behaviors/publish_color_rgba.hpp and add this code to that file:

#pragma once

#include <behaviortree_cpp/action_node.h>
#include <behaviortree_cpp/bt_factory.h>
#include <moveit_studio_behavior_interface/behavior_context.hpp>
#include <moveit_studio_behavior_interface/shared_resources_node.hpp>
#include <rclcpp/publisher.hpp>
#include <std_msgs/msg/color_rgba.hpp>

#include <memory>
#include <string>

namespace example_behaviors
{
class PublishColorRGBA final : public moveit_studio::behaviors::SharedResourcesNode<BT::SyncActionNode>
{
public:
  PublishColorRGBA(const std::string& name, const BT::NodeConfiguration& config,
              const std::shared_ptr<moveit_studio::behaviors::BehaviorContext>& shared_resources);

  static BT::PortsList providedPorts();

  static BT::KeyValueVector metadata();

  BT::NodeStatus tick() override;

private:
  std::shared_ptr<rclcpp::Publisher<std_msgs::msg::ColorRGBA>> publisher_;
};
}  // namespace example_behaviors

Next, create a source file at example_behaviors/src/publish_color_rgba.cpp and add this code to that file:

#include <example_behaviors/publish_color_rgba.hpp>

namespace example_behaviors
{
PublishColorRGBA::PublishColorRGBA(const std::string& name, const BT::NodeConfiguration& config,
                                 const std::shared_ptr<moveit_studio::behaviors::BehaviorContext>& shared_resources)
  : moveit_studio::behaviors::SharedResourcesNode<BT::SyncActionNode>(name, config, shared_resources)
  , publisher_{ shared_resources_->node->create_publisher<std_msgs::msg::ColorRGBA>("/my_topic", rclcpp::QoS(1)) }
{
}

BT::PortsList PublishColorRGBA::providedPorts()
{
  return {};
}

BT::KeyValueVector PublishColorRGBA::metadata()
{
  return { { "subcategory", "Example" },
           { "description", "Publishes a fixed std_msgs::msg::ColorRGBA message to a topic named \"/my_topic\"" } };
}

BT::NodeStatus PublishColorRGBA::tick()
{
  std_msgs::msg::ColorRGBA color_msg;
  color_msg.r = 0.5;
  color_msg.g = 0.5;
  color_msg.b = 0.5;
  color_msg.a = 0.5;
  publisher_->publish(color_msg);

  return BT::NodeStatus::SUCCESS;
}
}  // namespace example_behaviors

The publisher is created in the constructor of the PublishColorRGBA Behavior using the Node provided through shared_resources_.

When the PublishColorRGBA Behavior is run within an Objective, each time it is ticked it will publish the message to the "/my_topic" topic.

After that, add the new source file to the example_behaviors CMake library target in CMakeLists.txt following the same pattern used by the other Behaviors. It should look something like this:

add_library(
  example_behaviors SHARED
  src/hello_world.cpp
  src/delayed_message.cpp
  src/setup_mtc_wave_hand.cpp
  src/register_behaviors.cpp
  src/publish_color_rgba.cpp
)

Add a line to include your new Behavior for registration using the same pattern as the lines for the other example Behaviors.

#include <example_behaviors/hello_world.hpp>
#include <example_behaviors/delayed_message.hpp>
#include <example_behaviors/setup_mtc_wave_hand.hpp>
#include <example_behaviors/publish_color_rgba.hpp>

Then, add a line to register the new Behavior with the package’s Behavior loader plugin, following the same pattern as the lines for the other example Behaviors.

void registerBehaviors(BT::BehaviorTreeFactory& factory,
                   const std::shared_ptr<moveit_studio::behaviors::BehaviorContext>& shared_resources) override
{
  // Other Behaviors registered above
  moveit_studio::behaviors::registerBehavior<PublishColorRGBA>(factory, "PublishColorRGBA", shared_resources);

}