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:
- Write the publisher's implementation;
- Write subscriber implementation;
- Edit the configuration file;
- 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:
- Write the publisher's implementation;
- Write subscriber implementation;
- Add executable permissions for python files;
- Edit the configuration file;
- 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:
- Create msg file in fixed format
- Edit profile
- 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:
- Publisher
- Receiver
- Data (custom message here)
technological process:
- Write the publisher's implementation;
- Write subscriber implementation;
- Edit the configuration file;
- 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:
- Write the publisher's implementation;
- Write subscriber implementation;
- Add executable permissions for python files;
- Edit the configuration file;
- 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} )