Publisher Demo (C++)

This tutorial demonstrates how to publish ROS 2 messages from within a YASMIN state machine using the PublisherState in C++. This is useful for sending commands to actuators, broadcasting status updates, or generating test data. Publishing states integrate with other state machine logic, allowing you to coordinate complex behaviors that involve both sensing (subscribing) and acting (publishing).

Background

The PublisherState allows states to:

1. Define the Publisher State

Create a class inheriting from yasmin_ros::PublisherState, templated with the message type std_msgs::msg::Int32. The constructor requires two parameters: the topic name ("count") and a callback function that creates the message to publish. The create_int_msg method is invoked each time the state executes. It retrieves the current counter from the blackboard, increments it, stores the updated value back in the blackboard, and creates an Int32 message with the new counter value. This demonstrates stateful publishing - the counter persists across state executions thanks to the blackboard, allowing you to track progress or maintain sequences.

class PublishIntState
    : public yasmin_ros::PublisherState<std_msgs::msg::Int32> {

public:
  PublishIntState()
      : yasmin_ros::PublisherState<std_msgs::msg::Int32>(
            "count", // topic name
            std::bind(&PublishIntState::create_int_msg, this,
                      _1) // create msg handler callback
        ) {};

  std_msgs::msg::Int32
  create_int_msg(yasmin::Blackboard::SharedPtr blackboard) {

    int counter = blackboard->get<int>("counter");
    counter++;
    blackboard->set<int>("counter", counter);

    YASMIN_LOG_INFO("Creating message %d", counter);
    std_msgs::msg::Int32 msg;
    msg.data = counter;
    return msg;
  };
};

2. Create the State Machine

First, we define a check_count callback function that compares the current counter against a maximum value stored in the blackboard. This function introduces a small delay (1 second) to simulate processing time, then returns "outcome1" if the maximum is reached (to exit), or "outcome2" to continue publishing. In the main function, we create a state machine with two states: PUBLISHING_INT publishes a message and transitions to CHECKINNG_COUNTS, which evaluates the condition. If the maximum hasn't been reached, it loops back to PUBLISHING_INT, creating a publish-check cycle. We initialize the blackboard with counter = 0 and max_count = 10, so the machine will publish 10 messages before completing. This pattern is useful for sending sequences of commands, periodic status updates, or controlled test data generation.

std::string
check_count(yasmin::Blackboard::SharedPtr blackboard) {

  // Sleep for 1 second to simulate some processing time
  rclcpp::sleep_for(std::chrono::seconds(1));
  YASMIN_LOG_INFO("Checking count: %d", blackboard->get<int>("counter"));

  if (blackboard->get<int>("counter") >= blackboard->get<int>("max_count")) {
    return "outcome1";
  } else {
    return "outcome2";
  }
}

int main(int argc, char *argv[]) {
  // Initialize ROS 2
  rclcpp::init(argc, argv);

  // Set up ROS 2 loggers
  yasmin_ros::set_ros_loggers();
  YASMIN_LOG_INFO("yasmin_publisher_demo");

  // Create a state machine with a final outcome
  auto sm = yasmin::StateMachine::make_shared(
      std::initializer_list<std::string>{yasmin_ros::basic_outcomes::SUCCEED},
      true);

  // Add states to the state machine
  sm->add_state("PUBLISHING_INT", std::make_shared<PublishIntState>(),
                {
                    {yasmin_ros::basic_outcomes::SUCCEED,
                     "CHECKINNG_COUNTS"}, // Transition back to itself
                });
  sm->add_state("CHECKINNG_COUNTS",
                yasmin::CbState::make_shared(
                    std::initializer_list<std::string>{"outcome1", "outcome2"},
                    check_count),
                {{"outcome1", yasmin_ros::basic_outcomes::SUCCEED},
                 {"outcome2", "PUBLISHING_INT"}});

  // Publisher for visualizing the state machine's status
  yasmin_viewer::YasminViewerPub yasmin_pub(sm, "YASMIN_PUBLISHER_DEMO");

  // Execute the state machine
  yasmin::Blackboard::SharedPtr blackboard =
      yasmin::Blackboard::make_shared();
  blackboard->set<int>("counter", 0);
  blackboard->set<int>("max_count", 10);
  try {
    std::string outcome = (*sm.get())(blackboard);
    YASMIN_LOG_INFO(outcome.c_str());
  } catch (const std::exception &e) {
    YASMIN_LOG_WARN(e.what());
  }

  rclcpp::shutdown();

  return 0;
}

3. Run the Demo

Execute the publisher demo and use ros2 topic echo /count in another terminal to observe the incrementing integer messages being published by the state machine.

ros2 run yasmin_demos publisher_demo