ROS communication mechanism -- topic and msg file

Keywords: ROS

sketch

Topic message communication means that the publisher who sends information and the subscriber who receives information send and receive information in the form of topic message. The subscriber node that wants to receive the topic receives the information of the publisher node corresponding to the topic name registered in the master node. Based on this information, the subscriber node connects directly to the publisher node to send and receive messages.

characteristic

The topic is one-way, which is suitable for sensor data that need to send messages continuously, because they send and receive messages continuously through a single connection. In addition, a single publisher can communicate with multiple subscribers. On the contrary, a subscriber can communicate with multiple publishers on a single topic.

Related common commands

commanddetailed description
rostopic listDisplays the active topic directory
rostopic echo [Topic name]Display the message content of the specified topic in real time
rostopic find [type name]Displays the topic of the message using the specified type
rostopic type [Topic name]Displays the message type of the specified topic
rostopic bw [Topic name]Displays the message bandwidth of the specified topic
rostopic hz [Topic name]Displays the message data publishing cycle of the specified topic
rostopic info [Topic name]Displays information about the specified topic
rostopic pub [Topic name] [message type] [parameter]Publish a message with the specified topic name

communication model


PS: Reference to the original text

Core element

  1. ROS Master (Manager)
    It must first be run and use the XMLRPC server to manage the connection information in the message communication between nodes.
  2. Talker (publisher)
    Generate the specified topic and publish the TCPROS message to all subscribers.
  3. Listener (subscriber)
    Subscribe to the specified topic and receive the specified TCPROS message from the publisher.

PS: XMLRPC (XML remote procedure call) is an RPC Protocol. Its encoding form adopts XML encoding format, and the transmission mode adopts HTTP protocol, which neither maintains the connection state nor checks the connection state. XMLRPC is a very simple convention that is only used to define small data types or commands, so it is relatively simple. With this feature, XMLRPC is very lightweight and supports multiple programming languages, so it is very suitable for ROS supporting various hardware and languages.

Communication process

  1. Run master node
    The master node is responsible for node to node connection and message communication, similar to the Name Server. roscore is its running command. When running the master node, you can register the name of each node and obtain information as needed. Without a master node, access and message exchange (such as topics and services) cannot be established between nodes. The master node uses XML remote procedure call (XML RPC) to communicate with the node.
  2. Run publisher node
    The publisher node registers the publisher node name, topic name, message type, URI address and port with the master node.
  3. Run subscriber node
    The subscriber node registers its subscriber node name, topic name, message type, URI address and port with the master node at run time.
  4. The master node sends publisher node information to the subscriber node
    The master node sends the name, topic name, message type, URI address and port of the publisher that the subscriber wants to access to the subscriber node.
  5. The subscriber node sends a connection request to the publisher node
    The subscriber node requests a direct connection to the publisher node according to the publisher information received from the master node. In this case, the information to be sent includes the subscriber node name, the topic name, and the message type.
  6. The publisher node makes a connection response
    The publisher node sends the URI address and port of the TCP server to the subscriber node as a connection response.
  7. The publisher node establishes a connection with the subscriber node
    The subscriber node uses TCPROS to create a client corresponding to the publisher node and connect directly to the publisher node. After the publisher and subscriber establish a connection, they can send messages directly without going through the ROS Master. Therefore, even if the ROS Master is closed, the communication between them will not be affected.
  8. send message
    The publisher node sends a message to the subscriber node. The communication between nodes uses a TCP/IP mode called TCPROS.

Code examples (publishers and subscribers)

Requirement Description: write publisher and subscriber files. The publisher publishes text messages at a frequency of 10HZ(10 times per second). The subscriber subscribes to the messages and prints them.

First, a topic is created here_ Test package, and then create talker.cpp (publisher) and listener (subscriber) files respectively.

Publisher (talker.cpp)

#include "ros/ros.h"
#include "std_msgs/UInt32.h"

int main(int argc, char **argv)
{
    // Set encoding
    setlocale(LC_ALL, "");

    // 1. Initialize ROS node
    // Parameter 3 is the node name, which is globally unique
    ros::init(argc, argv, "talker");

    // 2. Instantiate ROS handle
    ros::NodeHandle nh;

    // 3. Instantiate publisher object
    // Parameter 1 is the name of the topic to which the data is to be published, and parameter 2 is the buffer size
    ros::Publisher pub = nh.advertise<std_msgs::UInt32>("chatter", 1000);

    // 4. Define the data to be published
    int count = 0;
    std_msgs::UInt32 msg;

    // 5. Define the frequency of data publishing as 10 times per second
    ros::Rate loop_rate(10);

    // ros::ok() means that the loop condition is satisfied as long as the node is still running
    while (ros::ok())
    {
        msg.data = count;

        // 6. Release news
        pub.publish(msg);

        // Print sent information
        ROS_INFO("The message sent is:%d", msg.data);

        // Process ROS message callback, and only cycle once
        // There is no callback received here, so this sentence can not be written
        ros::spinOnce();

        count++;
        loop_rate.sleep();
    }
    
    return 0;
}

Subscriber (listener.cpp)

#include "ros/ros.h"
#include "std_msgs/UInt32.h"

void chatterCallback(const std_msgs::UInt32::ConstPtr& msg)
{
    // 4. Process subscribed messages
    ROS_INFO("Subscribed to message: [%d]", msg->data);
}

int main(int argc, char **argv)
{
    // Set encoding
    setlocale(LC_ALL, "");

    // 1. Initialize ROS node
    // Parameter 3 is the node name, which is globally unique
    ros::init(argc, argv, "listener");

    // 2. Instantiate ROS handle
    ros::NodeHandle nh;

    // 3. Instantiate subscriber object
    // Parameter 1 is the name of the topic to be subscribed, parameter 2 is the buffer size, and parameter 3 is the callback function for processing subscription messages
    ros::Subscriber sub = nh.subscribe<std_msgs::UInt32>("chatter", 1000, chatterCallback);

    // Process the ROS message callback and loop until the ROS node exits (Ctrl+C is executed or ros::shutdown() is called)
    ros::spin();
    
    return 0;
}


Configure CMakeLists.txt

Note: CMakeLists.txt here refers to topic_ Add the following contents to the CMakeLists.txt file under the test package instead of the whole project.

# Node build options, configuring executables
add_executable(talker src/talker.cpp)
add_executable(listener src/listener.cpp)

# Node build options to configure the target link library
target_link_libraries(talker
  ${catkin_LIBRARIES}
)
target_link_libraries(listener
  ${catkin_LIBRARIES}
)

Compile and run

Use Ctrl+Shift+B to compile, then use the roscore command to start the master node, and then run the environment variable under source to run the publisher and subscriber nodes respectively to see the printout of communication data.

Problem extension

First run the publisher node, print the information from 0, and then run the subscriber node. The information is printed directly from 196. Before the subscriber runs, the information published by the publisher cannot be received.

Trying to run the subscriber node first and then the publisher node, there is still a loss of information. The reason is that when the publisher starts publishing messages, it has not been registered in the master node.

You can add a delay in talker.cpp to delay the sending time of the first data.

ros::Duration(1.0).sleep(); 

Send the message after the registration is completed, and you can receive the complete message.

Custom message

ROS nodes communicate with each other through messages. ROS provides some standard data types and a mechanism for developing custom message types based on standard message types. In the above example, we use ROS standard messages. In actual use, we often need to customize messages to meet the requirements.

The message type of ROS can be here see.

Requirement Description: create an msg file about student information, including name, age and grade. The publisher sends student information, and the subscriber prints student information.

Custom msg file

First, in topic_ Create the msg folder in the test package, and then create the message file (Student.msg) in the folder.

string name
uint8 age
float32 score

Edit profile

  • Add compilation dependency and execution dependency in package.xml
<!-- Compile time dependency -->
<build_depend>message_generation</build_depend>
<!-- Runtime dependency -->
<exec_depend>message_runtime</exec_depend>
  • Edit msg related configuration in CMakeLists.txt
# The component package that catkin depends on when it is built. The first three ROS packages have been automatically generated when they are created. Message is added here_ generation
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

# Configure msg source FILES. FILES will reference *. msg FILES in the msg directory of the current feature pack directory and automatically generate a header file (*. h)
add_message_files(
  FILES
  Student.msg
)

# Rely on STD when generating messages_ msgs
generate_messages(
  DEPENDENCIES
  std_msgs
)

# Runtime dependency describes the function packages of library, catkin build dependency and system dependency
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES topic_test
 CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

Execute compilation

Compile using Ctrl+Shift+B, and then in / devel / topic_ See the automatically generated header file Student.h in the test directory, and you can reference it in the following code.

PS: in C before modifying the code_ cpp_ Add the header file path in the properties.json file, otherwise the header file will not be found when it is referenced in the code.

Modify subscriber and publisher code

Next, we use custom messages in our code to communicate.

  • Publisher (talker.cpp)
#include "ros/ros.h"
#include "topic_test/Student.h"

int main(int argc, char **argv)
{
    // Set encoding
    setlocale(LC_ALL, "");

    // 1. Initialize ROS node
    // Parameter 3 is the node name, which is globally unique
    ros::init(argc, argv, "talker");

    // 2. Instantiate ROS handle
    ros::NodeHandle nh;

    // 3. Instantiate publisher object
    // Parameter 1 is the name of the topic to which the data is to be published, and parameter 2 is the buffer size
    ros::Publisher pub = nh.advertise<topic_test::Student>("chatter", 1000);

    // 4. Define the data to be published
    topic_test::Student msg;
    msg.name = "Zhang San";
    msg.age = 18;
    msg.score = 85.6;

    // 5. Define the frequency of data publishing as 1 time per second
    ros::Rate loop_rate(1);

    // ros::ok() means that the loop condition is satisfied as long as the node is still running
    while (ros::ok())
    {
        // 6. Release news
        pub.publish(msg);

        // Print sent information
        ROS_INFO("The message sent is: name-%s, Age-%d, achievement-%.2f", msg.name.c_str(), msg.age, msg.score);

        // Process ROS message callback, and only cycle once
        // There is no callback received here, so this sentence can not be written
        ros::spinOnce();

        loop_rate.sleep();
    }
    
    return 0;
}

  • Subscriber (listener.cpp)
#include "ros/ros.h"
#include "topic_test/Student.h"

void chatterCallback(const topic_test::Student::ConstPtr& msg)
{
    // 4. Process subscribed messages
    ROS_INFO("Subscribed to message: Name[%s], Age[%d], Achievements[%.2f]", msg->name.c_str(), msg->age, msg->score);
}

int main(int argc, char **argv)
{
    // Set encoding
    setlocale(LC_ALL, "");

    // 1. Initialize ROS node
    // Parameter 3 is the node name, which is globally unique
    ros::init(argc, argv, "listener");

    // 2. Instantiate ROS handle
    ros::NodeHandle nh;

    // 3. Instantiate subscriber object
    // Parameter 1 is the name of the topic to be subscribed, parameter 2 is the buffer size, and parameter 3 is the callback function for processing subscription messages
    ros::Subscriber sub = nh.subscribe<topic_test::Student>("chatter", 1000, chatterCallback);

    // Process the ROS message callback and loop until the ROS node exits (Ctrl+C is executed or ros::shutdown() is called)
    ros::spin();
    
    return 0;
}

Compile and run

Use Ctrl+Shift+B to compile, then use the roscore command to start the master node, and then run the environment variable under source to run the publisher and subscriber nodes respectively to see the printout of communication data.

☝ ★★★ - return to the general directory of ROS robot development notes Summary - ★★★ ☝

Posted by daxxy on Mon, 11 Oct 2021 10:26:10 -0700