Write a study note for yourself to check later
Authoring publisher nodes
Node refers to the executable file in the ROS network. Next, we will create a publisher node ("talker"), which will continuously broadcast messages in the ROS network.
Switch to the beginer you created earlier_ Under the tutorials package path:
cd ~/catkin_ws/src/beginner_tutorials
source code
In beginer_ Create an src folder in the tutorials package path:
mkdir -p ~/catkin_ws/src/beginner_tutorials/src
This folder will be used to place the beginer_ All source code for tutorials package.
In beginer_ Create src/talker Cpp file, and paste the following code into the file:
https://raw.github.com/ros/ros_tutorials/groovy-devel/roscpp_tutorials/talker/talker.cpp
27 #include "ros/ros.h" 28 #include "std_msgs/String.h" 29 30 #include <sstream> 31 32 /** 33 * This tutorial demonstrates simple sending of messages over the ROS system. 34 */ 35 int main(int argc, char **argv) 36 { 37 /** 38 * The ros::init() function needs to see argc and argv so that it can perform 39 * any ROS arguments and name remapping that were provided at the command line. For programmatic 40 * remappings you can use a different version of init() which takes remappings 41 * directly, but for most command-line programs, passing argc and argv is the easiest 42 * way to do it. The third argument to init() is the name of the node. 43 * 44 * You must call one of the versions of ros::init() before using any other 45 * part of the ROS system. 46 */ 47 ros::init(argc, argv, "talker"); 48 49 /** 50 * NodeHandle is the main access point to communications with the ROS system. 51 * The first NodeHandle constructed will fully initialize this node, and the last 52 * NodeHandle destructed will close down the node. 53 */ 54 ros::NodeHandle n; 55 56 /** 57 * The advertise() function is how you tell ROS that you want to 58 * publish on a given topic name. This invokes a call to the ROS 59 * master node, which keeps a registry of who is publishing and who 60 * is subscribing. After this advertise() call is made, the master 61 * node will notify anyone who is trying to subscribe to this topic name, 62 * and they will in turn negotiate a peer-to-peer connection with this 63 * node. advertise() returns a Publisher object which allows you to 64 * publish messages on that topic through a call to publish(). Once 65 * all copies of the returned Publisher object are destroyed, the topic 66 * will be automatically unadvertised. 67 * 68 * The second parameter to advertise() is the size of the message queue 69 * used for publishing messages. If messages are published more quickly 70 * than we can send them, the number here specifies how many messages to 71 * buffer up before throwing some away. 72 */ 73 ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); 74 75 ros::Rate loop_rate(10); 76 77 /** 78 * A count of how many messages we have sent. This is used to create 79 * a unique string for each message. 80 */ 81 int count = 0; 82 while (ros::ok()) 83 { 84 /** 85 * This is a message object. You stuff it with data, and then publish it. 86 */ 87 std_msgs::String msg; 88 89 std::stringstream ss; 90 ss << "hello world " << count; 91 msg.data = ss.str(); 92 93 ROS_INFO("%s", msg.data.c_str()); 94 95 /** 96 * The publish() function is how you send messages. The parameter 97 * is the message object. The type of this object must agree with the type 98 * given as a template parameter to the advertise<>() call, as was done 99 * in the constructor above. 100 */ 101 chatter_pub.publish(msg); 102 103 ros::spinOnce(); 104 105 loop_rate.sleep(); 106 ++count; 107 } 108 109 110 return 0; 111 }
Code description
Now, let's explain the code in sections.
#include "ros/ros.h"
Ros/ros H is a practical header file, which refers to most commonly used header files in the ROS system.
#include "std_msgs/String.h"
This refers to std_msgs/String message, which is stored in STD_ In msgs package, string msg file automatically generated header file. For message definitions, please refer to the msg page.
ros::init(argc, argv, "talker");
Initialize ROS. It allows ROS to remap names from the command line -- however, this is not the focus of this discussion. Here, we can also specify the name of the node -- the name of the node must be unique during operation.
The name here must be a base name, that is, the name cannot contain symbols such as /.
ros::NodeHandle n;
Create a handle for the node of this process. The first created NodeHandle will initialize the node, and the last destroyed NodeHandle will release all the resources occupied by the node.
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
Tell the master that we will publish STD on chatter (topic name)_ Msgs/string message type. In this way, the master will tell all nodes that have subscribed to chatter topics that there will be data publishing. The second parameter is the size of the publishing sequence. If we publish messages too frequently, the previously published messages will be discarded when the number of messages in the buffer is greater than 1000.
NodeHandle::advertise() returns a ros::Publisher object, which has two functions: 1) it has a publish() member function that allows you to publish messages on topic; 2) If the message type is not correct, it will refuse to publish.
ros::Rate loop_rate(10);
The ros::Rate object allows you to specify the frequency of self circulation. It tracks the time elapsed since the last call to Rate::sleep() and sleeps until a frequency period.
In this example, we run it at 10Hz.
int count = 0; while (ros::ok()) {
roscpp will generate a SIGINT handle by default, which is responsible for handling Ctrl-C keyboard operations -- making ros::ok() return false.
ros::ok() returns false if one of the following conditions occurs:
SIGINT triggered (Ctrl-C)
Kicked out of the ROS network by another node with the same name
ros::shutdown() called by another part of the program
All ros::NodeHandles in the node have been destroyed
Once ros::ok() returns false, all ROS calls will be invalidated.
std_msgs::String msg; std::stringstream ss; ss << "hello world " << count; msg.data = ss.str();
We use a message adaptation class generated from the msg file to broadcast messages in the ROS network. Now we use the standard String message, which has only one data member "data". Of course, you can also publish more complex message types.
chatter_pub.publish(msg);
Here, we send messages to all nodes that subscribe to chatter topics.
ROS_INFO("%s", msg.data.c_str());
ROS_INFO and other similar functions can be used to replace functions such as printf/cout. For details, please refer to rosconsole documentation for more information.
ros::spinOnce();
In this example, it is not necessary to call ros::spinOnce() because we do not accept callbacks. However, if your program contains other callback functions, you'd better add the statement ros::spinOnce() here, otherwise your callback function will never be called.
loop_rate.sleep();
This statement calls the ros::Rate object to sleep for a period of time so that the publishing frequency is 10Hz.
Summarize the above contents:
Initialize ROS system
Broadcast in the ROS network. We will publish STD on chatter topic_ Message of type msgs/string
Publish messages on chatter 10 times per second
Next, we will write a node to receive this message.
Compose subscriber node
source code
In beginer_ Create src/listener Cpp file and paste the following code:
#include "ros/ros.h" #include "std_msgs/String.h" /** * This tutorial demonstrates simple receipt of messages over the ROS system. */ void chatterCallback(const std_msgs::String::ConstPtr& msg) { ROS_INFO("I heard: [%s]", msg->data.c_str()); } int main(int argc, char **argv) { /** * The ros::init() function needs to see argc and argv so that it can perform * any ROS arguments and name remapping that were provided at the command line. For programmatic * remappings you can use a different version of init() which takes remappings * directly, but for most command-line programs, passing argc and argv is the easiest * way to do it. The third argument to init() is the name of the node. * * You must call one of the versions of ros::init() before using any other * part of the ROS system. */ ros::init(argc, argv, "listener"); /** * NodeHandle is the main access point to communications with the ROS system. * The first NodeHandle constructed will fully initialize this node, and the last * NodeHandle destructed will close down the node. */ ros::NodeHandle n; /** * The subscribe() call is how you tell ROS that you want to receive messages * on a given topic. This invokes a call to the ROS * master node, which keeps a registry of who is publishing and who * is subscribing. Messages are passed to a callback function, here * called chatterCallback. subscribe() returns a Subscriber object that you * must hold on to until you want to unsubscribe. When all copies of the Subscriber * object go out of scope, this callback will automatically be unsubscribed from * this topic. * * The second parameter to the subscribe() function is the size of the message * queue. If messages are arriving faster than they are being processed, this * is the number of messages that will be buffered up before beginning to throw * away the oldest ones. */ ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); /** * ros::spin() will enter a loop, pumping callbacks. With this version, all * callbacks will be called from within this thread (the main one). ros::spin() * will exit when Ctrl-C is pressed, or the node is shutdown by the master. */ ros::spin(); return 0; }
Code description
Now we will explain the code one by one. Of course, the code explained before will not be repeated.
void chatterCallback(const std_msgs::String::ConstPtr& msg) { ROS_INFO("I heard: [%s]", msg->data.c_str()); }
This is a callback function, which will be called when the chatter topic is received. The message is in the form of boost shared_ptr pointer, which means you can store it without copying data.
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
Tell the master that we want to subscribe to the chatter topic. When a message is posted to this topic, ROS will call the chatterCallback() function. The second parameter is the queue size to prevent us from processing messages fast enough. When the cache reaches 1000 messages, new messages will arrive and the previously received messages will be discarded.
NodeHandle::subscribe() returns the ros::Subscriber object. You must keep it active until you no longer want to subscribe to the message. When this object is destroyed, it will automatically unsubscribe from the chatter topic.
There are various NodeHandle::subscribe() functions that allow you to specify member functions of classes, even boost Any data type that a function object can call. roscpp overview provides more detailed information.
ros::spin();
ros::spin() enters the self loop and can call the message callback function as soon as possible. If no message arrives, it won't use a lot of CPU, so don't worry. Once ros::ok() returns false, ros::spin() will immediately jump out of the self loop. This may be because ros::shutdown() is called, or the user presses Ctrl-C, causing the master to tell the node to terminate the operation. It is also possible that the node is shut down artificially.
There are other methods for callback, but we do not cover them here. For information, please refer to roscpp_tutorials Some demo applications in package. For more detailed information, please refer to roscpp overview.
Next, let's summarize:
Initialize ROS system
Subscribe to chatter topics
Enter the self circulation and wait for the message to arrive
When the message arrives, call the chatterCallback() function
Compile node
Catkin was used in previous tutorials_ Create_ PKG created the package XML and cmakelists Txt file.
Generated cmakelists Txt should look like this (the modified and unused comments and examples in the Creating Msgs and Srvs tutorial have been removed):
cmake_minimum_required(VERSION 2.8.3) project(beginner_tutorials) ## Find catkin and any catkin packages find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg) ## Declare ROS messages and services add_message_files(DIRECTORY msg FILES Num.msg) add_service_files(DIRECTORY srv FILES AddTwoInts.srv) ## Generate added messages and services generate_messages(DEPENDENCIES std_msgs) ## Declare a catkin package catkin_package()
At cmakelists Add several statements at the end of TXT file:
include_directories(include ${catkin_INCLUDE_DIRS}) add_executable(talker src/talker.cpp) target_link_libraries(talker ${catkin_LIBRARIES}) add_executable(listener src/listener.cpp) target_link_libraries(listener ${catkin_LIBRARIES})
Results, cmakelists Txt file looks like this:
cmake_minimum_required(VERSION 2.8.3) project(beginner_tutorials) ## Find catkin and any catkin packages find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg) ## Declare ROS messages and services add_message_files(FILES Num.msg) add_service_files(FILES AddTwoInts.srv) ## Generate added messages and services generate_messages(DEPENDENCIES std_msgs) ## Declare a catkin package catkin_package() ## Build talker and listener include_directories(include ${catkin_INCLUDE_DIRS}) add_executable(talker src/talker.cpp) target_link_libraries(talker ${catkin_LIBRARIES}) add_dependencies(talker beginner_tutorials_generate_messages_cpp) add_executable(listener src/listener.cpp) target_link_libraries(listener ${catkin_LIBRARIES}) add_dependencies(listener beginner_tutorials_generate_messages_cpp)
This will generate two executable files, talker and listener, which are stored in the devel space directory by default, specifically in ~/catkin_ Ws/devel/lib/<package name>
Now to add a dependency on the generated message file for the executable:
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
This ensures that the header file of the custom message has been generated before it is used. Because catkin compiles all packages in parallel, if you want to use messages from other packages in other catkin workspaces, you also need to add dependencies on the message files generated by them. Of course, in the Groovy version, you can use the following variable to add dependencies on all necessary files:
add_dependencies(talker ${catkin_EXPORTED_TARGETS})
You can call executables directly, or you can use rosrun to call them. They will not be installed in the /bin PATH because that will change the PATH environment variable of the system. If you are sure to install the executable to this PATH, you need to set the installation location. Please refer to catkin/cmakelists Txt
If you need information about cmakelists Txt for more details, please refer to catkin/cmakelists Txt
Now run catkin_make:
catkin_make
Note: if you are adding a new package, you need to tell catkin to force compile through the --force-cmake option.
References
reference catkin/Tutorials/using_a_workspace#With_catkin_make.