2, Communication mechanism of ROS - topic

Posted by naggi on Mon, 07 Feb 2022 07:06:07 +0100

The basic communication mechanism in ROS mainly has the following three implementation strategies:

  • Topic communication (publish subscribe mode)

  • Service communication (request response mode)

  • Parameter server (parameter sharing mode)

1. Topic communication: publish subscribe mode

One node publishes messages to the topic, and the other node subscribes to messages from the topic

publisher: release

subscriber: subscription

Topic: constantly updated data transmission scenarios

For example: radar, camera, gps and other sensor data acquisition.

c + + implementation

Model implementation:

Publishers, subscribers and data

technological process:

  1. Write the publisher's implementation;
  2. Write subscriber implementation;
  3. Edit the configuration file;
  4. Compile and execute.

Publisher:

#include"ros/ros.h"
#include"std_msgs/String.h" / / send the header file of the text
#Include < ssstream > / / realize string splicing digital header file
/*
    Publisher implementation:
       1.Include header file
         ROS Text type in -- > STD_ msgs/String. h

       2.Initializing the ros node: creating the object ergouzi
       3.Create node handle
       4.Create publisher object
       5.Write publishing logic and publish data

*/



int main(int argc, char  *argv[])
{
    
    setlocale(LC_ALL,"");//The log is sent in Chinese. It needs to be used to avoid garbled code.
    /* code */
    ros::init(argc,argv,"erGouZi");   //Initialize the node and create the node name. This is the node name. Initialize the ros node and give it a name, which is equivalent to your blind date
    
    ros::NodeHandle nh;//Create node handle nh

    ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);//Create a publisher. This is the topic name. In the publisher object, it calls the advertise function of the handle. There must be a generic type in it. It is the type of published message and a topic,

    //Circular release
    //It is required to send data at a frequency of 10 Hz and add a number after the text
    //Create the published message first
    std_msgs::String msg;
    //Release frequency: 10hz
    ros::Rate rate(1);
    //Set number
    int count = 0;//Add a counter
    //Write a loop and publish it in the loop
     ros::Duration(3.0).sleep(); 
    while(ros::ok())//As long as the node is alive, the cycle is established
    {
        count++;

       // msg.data = "hello";// Assign a value to MSG
       //Realize string splicing numbers
        std::stringstream ss;//Create a stringstream object
        ss <<"hello --->" << count;//String splicing, take out the spliced value and assign it to our msg
        msg.data = ss.str();//The data in the string stream can be extracted as a string
        pub.publish(msg);//Publish msg

        //Add log:
        ROS_INFO("Messages sent:%s",msg.data.c_str());
        rate.sleep();//Call rate frequency function
        ros::spinOnce();//The official suggestion is to handle callback functions
    }

    return 0;
}

 ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);

Advertise < type of message published >

Subscriber:

#include"ros/ros.h"
#include"std_msgs/String.h" / / send the header file of the text
#Include < ssstream > / / realize string splicing digital header file
/*
    Subscriber implementation:
       1.Include header file
         ROS Text type in -- > STD_ msgs/String. h

       2.Initializing the ros node: creating the object ergouzi
       3.Create node handle
       4.Create subscriber object
       5.Process subscribed data
       6.You need to declare a spin () function

*/

void doMsg(const std_msgs::String::ConstPtr &msg)//This parameter is subscribed to, and the subscribed parameter is STD_ The string type under msgs / string. The parameter of the subscription is modified by const. It is a drinking & MSG. Now I get the STD of the incoming message_ The string type under msgs / string is a reference to one of its constant pointers.
{//Get and subscribe to the data through msg
ROS_INFO("Data subscribed by Cuihua:%s",msg->data.c_str());//Under this msg, we know that it has a data. MSG here is a pointer, specifically a reference to the pointer, and then we need to use the arrow to call this data. In addition, this data is a string style type, and we want to print a C-style string, which will be displayed later c_str()

}
int main(int argc, char  *argv[])
{
       setlocale(LC_ALL,"");//The log is sent in Chinese. It needs to be used to avoid garbled code.
        // 2. Initialize the ros node: create the object Cuihua
        ros::init(argc,argv,"cuiHua");
       //3. Create node handle
       ros::NodeHandle nh;
       //4. Create subscriber object
       ros::Subscriber sub = nh.subscribe("fang",10,doMsg);//This callback function is step 5. It processes the subscribed data, but domsg is not available yet. We need to use the protocol
       //5. Process subscribed data

       ros::spin();//Later, our main function will be executed from top to bottom, but there is a callback function at the end of the statement to create the subscriber object. This callback function will be executed every time you subscribe to it. Then, if you execute it from top to bottom, the main function will be executed once. This is not feasible, When we come to spin after executing the line of creating subscriber object, we need to go back. What we mean by going back is to call the callback function of creating subscriber object. Because callback functions are executed frequently.
    //If you don't handle this spin in our subscription, it's likely that your message can't be subscribed. Then the result cannot be printed
    return 0;
}

cmakelists. txt configuration

1,add_ Executable (parameter 1 # src / parameter 2.cpp)

Add an executable file. Parameter 2 is the name of the executable file

Parameter 1 is a node name for the mapping of this source file. The general recommendation is the same as the name of the source file

add_executable(demo01_pub src/demo01_pub.cpp)

2,target_ link_ Libraries (parameter 1)
   ${catkin_LIBRARIES}
)

Parameter 1 is changed to the name we mapped

target_link_libraries(demo01_pub
   ${catkin_LIBRARIES}
)

Run: ros # registration # node name

For example, this node is mapped

Running is the package name of ros} demo01_pub

Implementation of python

technological process:

  1. Write the publisher's implementation;
  2. Write subscriber implementation;
  3. Add executable permissions for python files;
  4. Edit the configuration file;
  5. Compile and execute.

Publisher:

#! /usr/bin/env python
"""
    demand: Realize basic topic communication. One party publishes data and the other receives data,
         Key points of realization:
         1.Sender
         2.Receiver
         3.data(Normal text here)

         PS: Both need to set the same topic


    Message publisher:
        Circular release information:HelloWorld Suffix number

    Implementation process:
        1.Guide Package 
        2.initialization ROS node:name(only)
        3.Instantiate publisher object
        4.Organize the published data and write logic to publish the data


"""
#1. Guide Package 
import rospy
from std_msgs.msg import String

if __name__ == "__main__":
    #2. Initialize ROS node: name (unique)
    rospy.init_node("talker_p")
    #3. Instantiate publisher object
    pub = rospy.Publisher("chatter",String,queue_size=10)
    #4. Organize the published data and write logic to publish the data
    msg = String()  #Create msg object
    msg_front = "hello Hello"
    count = 0  #Counter 
    # Set cycle frequency
    rate = rospy.Rate(1)
    while not rospy.is_shutdown():

        #Splice string
        msg.data = msg_front + str(count)

        pub.publish(msg)
        rate.sleep()
        rospy.loginfo("Written data:%s",msg.data)
        count += 1

Subscriber:

#! /usr/bin/env python
"""
    demand: Realize basic topic communication. One party publishes data and the other receives data,
         Key points of realization:
         1.Sender
         2.Receiver
         3.data(Normal text here)


    Message subscriber:
        Subscribe to topics and print received messages

    Implementation process:
        1.Guide Package 
        2.initialization ROS node:name(only)
        3.Instantiate subscriber object
        4.Processing subscribed messages(Callback function)
        5.Set the callback function for circular call



"""
#1. Guide Package 
import rospy
from std_msgs.msg import String

def doMsg(msg):
    rospy.loginfo("I heard:%s",msg.data)

if __name__ == "__main__":
    #2. Initialize ROS node: name (unique)
    rospy.init_node("listener_p")
    #3. Instantiate subscriber object
    sub = rospy.Subscriber("chatter",String,doMsg,queue_size=10)
    #4. Process subscribed messages (callback function)
    #5. Set loop call callback function
    rospy.spin()

3. Add executable permissions

Enter scripts execution under the terminal: Chmod + X * py

4. Configure cmakelists txt

catkin_install_python(PROGRAMS
  scripts/talker_p.py
  scripts/listener_p.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

Implementation of ros:

The node name is the direct operation name.

2. Topic communication custom msg:

msgs is just a simple text file. Each line has field type and field name. The field types you can use are:

  • int8, int16, int32, int64 (or unsigned type: uint *)

  • float32, float64

  • string

  • time, duration

  • other msg files

  • variable-length array[] and fixed-length array[C]

There is also a special type in ROS: Header, which contains timestamp and coordinate frame information commonly used in ROS. You will often see that the first line of the msg file has a Header header.

Requirement: create a user-defined message, which contains person information: name, height, age, etc.

technological process:

  1. Create msg file in fixed format
  2. Edit profile
  3. Compile and generate intermediate files that can be called by Python or C + +

 

1. Define msg file

Create a new MSG directory under the function pack and add the file person msg

string name
uint16 age
float64 height

 

2. Edit profile

package. Adding compilation dependency and execution dependency to XML

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>
  <!-- 
  exce_depend Previously, the corresponding is run_depend Now illegal
  -->

Build: build

exec: Execute

CMakeLists.txt edit msg related configuration

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# Need to add message_generation, must have std_msgs


## Configure msg source file
add_message_files(
  FILES
  Person.msg
)


#Execution time dependency
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo02_talker_listener
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

Generate messages: generate messages

3. Compilation

View the compiled intermediate file:

Intermediate files that C + + needs to call (... / workspace / devel/include / package name / xxx.h)

Intermediate files that Python needs to call (... / workspace / devel / lib / Python 3 / dist packages / package name / msg)

Subsequent calls to related msg are made from these intermediate files

2. 1 topic communication custom msg call A(C + +)

analysis:

In the model implementation, ROS master does not need to be implemented, and the establishment of connection has been encapsulated. There are three key points to pay attention to:

  1. Publisher
  2. Receiver
  3. Data (custom message here)

technological process:

  1. Write the publisher's implementation;
  2. Write subscriber implementation;
  3. Edit the configuration file;
  4. Compile and execute.

0.vscode configuration

In order to facilitate code prompt and avoid throwing exceptions by mistake, you need to configure vscode first and configure the path of the previously generated head file into C_ cpp_ properties. The includepath attribute of JSON:

{
    "configurations": [
        {
            "browse": {
                "databaseFilename": "",
                "limitSymbolsToIncludedHeaders": true
            },
            "includePath": [
                "/opt/ros/noetic/include/**",
                "/usr/include/**",
                "/xxx/yyy working space/devel/include/**" //Path to the configuration head file 
            ],
            "name": "ROS",
            "intelliSenseMode": "gcc-x64",
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c11",
            "cppStandard": "c++17"
        }
    ],
    "version": 4
}

The following vscode configuration is required:

In order to facilitate code prompt and avoid throwing exceptions by mistake, you need to configure vscode first and configure the path of the previously generated head file into C_ cpp_ properties. The includepath attribute of JSON:

{
    "configurations": [
        {
            "browse": {
                "databaseFilename": "",
                "limitSymbolsToIncludedHeaders": true
            },
            "includePath": [
                "/opt/ros/noetic/include/**",
                "/usr/include/**",
                "/xxx/yyy working space/devel/include/**" //Path to the configuration head file 
            ],
            "name": "ROS",
            "intelliSenseMode": "gcc-x64",
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c11",
            "cppStandard": "c++17"
        }
    ],
    "version": 4
}

Publisher:

 

#include"ros/ros.h"
#include "plumbing_pubsub/Person.h"

/*
    Publisher: Publisher message
        1.Include header file
        #include "plumbing_pub_sub/Person.h"
        2.Initialize ros node
        3.Create node handle
        4.Create publisher object
        5.Write publishing logic and publish data


*/


int main(int argc, char *argv[])
{
        setlocale(LC_ALL,"");
        ROS_INFO("This is the publisher of the message");
        //     2. Initialize ros node
        ros::init(argc,argv,"banZhuRen");
        // 3. Create node handle
        ros::NodeHandle nh;

        // 4. Create publisher object
         ros::Publisher pub = nh.advertise<plumbing_pubsub::Person>("liaoTian",10);//Generics are set according to the type of your message. Our message content is the person under the function report
        // 5. Write release logic and release data
            //5-1. Create published data
            plumbing_pubsub::Person person;
            person.name ="Zhang San";
            person.age=1;
            person.height=1.73;
            //5-2. Set publishing frequency
            ros::Rate rate(1);
            //5-3 circular release data

            while(ros::ok)
            {
                person.age+=1;
                //Core: data release
                pub.publish(person);
                ROS_INFO("Published message:%s,%d,%.2f",person.name.c_str(),person.age,person.height);

                rate.sleep();

                ros::spinOnce();

            }


    return 0;
}

subscriber

#include"ros/ros.h"
#include "plumbing_pubsub/Person.h"
/*
    Subscriber: subscribe to messages
        1.Include header file
        #include "plumbing_pub_sub/Person.h"
        2.Initialize ros node
        3.Create node handle
        4.Create a subscriber object
        5.Processing subscription data
        6. Call spin() function


*/void doPerson(const plumbing_pubsub::Person::ConstPtr &person)//This parameter is subscribed to, and the subscribed parameter is STD_ The string type under msgs / string. The parameter of the subscription is modified by const. It is a drinking & MSG. Now I get the STD of the incoming message_ The string type under msgs / string is a reference to one of its constant pointers.
{//Get and subscribe to the data through msg
ROS_INFO("Subscriber information:%s,%d,%.2f",person->name.c_str(),person->age,person->height);//Under this msg, we know that it has a data. MSG here is a pointer, specifically a reference to the pointer, and then we need to use the arrow to call this data. In addition, this data is a string style type, and we want to print a C-style string, which will be displayed later c_str()

}

int main(int argc, char *argv[])
{
    
        setlocale(LC_ALL,"");
        ROS_INFO("This is the subscriber of the message");
        //     2. Initialize ros node
        ros::init(argc,argv,"jiaZhang");
        // 3. Create node handle
        ros::NodeHandle nh;
        // 4. Create subscriber object
        ros::Subscriber sub = nh.subscribe("liaoTian",10,doPerson);//Topic name
        // 5. Processing subscription data
        // 6.  Call spin() function

  ros::spin();
    return 0;
}

 

3. Configure cmakelists txt

Add is required_ Dependencies is used to set the intermediate file related to the dependent message.

add_executable(person_talker src/person_talker.cpp)
add_executable(person_listener src/person_listener.cpp)



add_dependencies(person_talker ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(person_listener ${PROJECT_NAME}_generate_messages_cpp)


target_link_libraries(person_talker
  ${catkin_LIBRARIES}
)
target_link_libraries(person_listener
  ${catkin_LIBRARIES}
)

add_dependencies(person_talker ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(person_listener ${PROJECT_NAME}_generate_messages_cpp)

Two more

Dependencies: dependencies

You also need to configure one thing for custom messages:

add_ Dependencies (parameter 1, parameter 2)

Set two parameters:

Parameter 1: source file mapping name

Parameter 2: ${PROJECT_NAME}_generate_messages_cpp

What is parameter 2 for

It can guarantee the dependency of a call

With this configuration, when compiling, I must first ensure that your customized msg is compiled, and then compile the cpp file calling msg

Implementation of python

technological process:

  1. Write the publisher's implementation;
  2. Write subscriber implementation;
  3. Add executable permissions for python files;
  4. Edit the configuration file;
  5. Compile and execute.

 

0.vscode configuration

In order to facilitate the code prompt and error throwing of exceptions, you need to configure vscode first and configure the python file path generated earlier into settings json

{
    "python.autoComplete.extraPaths": [
        "/opt/ros/noetic/lib/python3/dist-packages",
"/ xxx/yyy workspace / devel / lib / Python 3 / dist packages"
    ]
}

If it is not configured, there is no automatic replenishment function during later writing, which will not be affected during runtime

 1. Publisher

#! /usr/bin/env python
"""
    Publisher:
        Loop send message

"""
import rospy
from demo02_talker_listener.msg import Person


if __name__ == "__main__":
    #1. Initialize ROS node
    rospy.init_node("talker_person_p")
    #2. Create publisher object
    pub = rospy.Publisher("chatter_person",Person,queue_size=10)
    #3. Organization message
    p = Person()
    p.name = "Gourd tile"
    p.age = 18
    p.height = 0.75

    #4. Write message publishing logic
    rate = rospy.Rate(1)
    while not rospy.is_shutdown():
        pub.publish(p)  #Release news
        rate.sleep()  #dormancy
        rospy.loginfo("full name:%s, Age:%d, height:%.2f",p.name, p.age, p.height)

Subscriber:

#! /usr/bin/env python
"""
    Subscriber:
        Subscribe to messages

"""
import rospy
from demo02_talker_listener.msg import Person

def doPerson(p):
    rospy.loginfo("Information of the person received:%s, %d, %.2f",p.name, p.age, p.height)


if __name__ == "__main__":
    #1. Initialize the node
    rospy.init_node("listener_person_p")
    #2. Create subscriber object
    sub = rospy.Subscriber("chatter_person",Person,doPerson,queue_size=10)
    rospy.spin() #4. Circulation

 

3. Permission setting

Enter scripts execution under the terminal: Chmod + X * py

4. Configure cmakelists txt

catkin_install_python(PROGRAMS
  scripts/talker_p.py
  scripts/listener_p.py
  scripts/person_talker.py
  scripts/person_listener.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

Topics: Machine Learning AI Autonomous vehicles