Monitor Demo (C++)

This tutorial demonstrates how to monitor ROS 2 topics using YASMIN's MonitorState in C++. This state subscribes to a topic and processes incoming messages, allowing your state machine to react to sensor data or other published information. The MonitorState is particularly valuable in robotics applications where you need to wait for specific sensor readings, check for environmental conditions, or synchronize with external events before proceeding with other tasks.

Background

The MonitorState is useful when you need to:

1. Define the Monitor State

Create a class inheriting from yasmin_ros::MonitorState, templated with the message type nav_msgs::msg::Odometry. The constructor takes six parameters: the topic name ("odom"), a list of possible outcomes, the monitor handler callback, QoS settings (quality of service for subscription reliability), message queue size, and timeout in seconds. The monitor_handler method is called for each received message. It logs the robot's position coordinates (x, y, z) from the odometry message, decrements the counter, and returns "outcome2" when the desired number of messages have been processed, or "outcome1" to continue monitoring. If no message arrives within the timeout period, the state automatically returns the TIMEOUT outcome.

class PrintOdometryState
    : public yasmin_ros::MonitorState<nav_msgs::msg::Odometry> {

public:
  int times;

  PrintOdometryState(int times)
      : yasmin_ros::MonitorState<nav_msgs::msg::Odometry>(
            "odom",                   // topic name
            {"outcome1", "outcome2"}, // possible outcomes
            std::bind(&PrintOdometryState::monitor_handler, this, _1,
                      _2), // monitor handler callback
            10,            // QoS for the topic subscription
            10,            // queue size for the callback
            10             // timeout for receiving messages
        ) {
    this->times = times;
  };

  std::string
  monitor_handler(std::shared_ptr<yasmin::blackboard::Blackboard> blackboard,
                  std::shared_ptr<nav_msgs::msg::Odometry> msg) {

    (void)blackboard;

    YASMIN_LOG_INFO("x: %f", msg->pose.pose.position.x);
    YASMIN_LOG_INFO("y: %f", msg->pose.pose.position.y);
    YASMIN_LOG_INFO("z: %f", msg->pose.pose.position.z);

    this->times--;

    if (this->times <= 0) {
      return "outcome2";
    }

    return "outcome1";
  };
};

2. Create the State Machine

In the main function, we create a simple state machine with one state that monitors odometry messages. The PrintOdometryState is initialized to process 5 messages before transitioning. The state machine defines four possible transitions: "outcome1" loops back to continue monitoring, "outcome2" exits successfully after processing all messages, TIMEOUT exits if no messages are received within 10 seconds, and CANCEL exits if the state is cancelled externally. This setup ensures the program handles all possible scenarios gracefully. The loop-back transition ("outcome1" → "PRINTING_ODOM") allows the state to process multiple messages without recreating the state object, which is efficient for continuous monitoring tasks.

int main(int argc, char *argv[]) {

  YASMIN_LOG_INFO("yasmin_monitor_demo");
  rclcpp::init(argc, argv);
  yasmin_ros::set_ros_loggers();

  auto sm = std::make_shared<yasmin::StateMachine>(
      std::initializer_list<std::string>{"outcome4"});

  rclcpp::on_shutdown([sm]() {
    if (sm->is_running()) {
      sm->cancel_state();
    }
  });

  sm->add_state(
      "PRINTING_ODOM", std::make_shared<PrintOdometryState>(5),
      {
          {"outcome1", "PRINTING_ODOM"},
          {"outcome2", "outcome4"},
          {yasmin_ros::basic_outcomes::TIMEOUT, "outcome4"},
          {yasmin_ros::basic_outcomes::CANCEL, "outcome4"},
      });

  yasmin_viewer::YasminViewerPub yasmin_pub(sm, "YASMIN_MONITOR_DEMO");

  try {
    std::string outcome = (*sm.get())();
    YASMIN_LOG_INFO(outcome.c_str());
  } catch (const std::exception &e) {
    YASMIN_LOG_WARN(e.what());
  }

  rclcpp::shutdown();
  return 0;
}

3. Run the Demo

Run the monitor demo to observe how the state processes incoming odometry messages. Make sure a source is publishing to the /odom topic, or run a navigation simulation that provides odometry data.

ros2 run yasmin_demos monitor_demo