Advertisement
Guest User

ros writing c++

a guest
Jan 4th, 2017
120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.11 KB | None | 0 0
  1. Writing a Simple Publisher and Subscriber (C++)
  2. Description: This tutorial covers how to write a publisher and subscriber node in C++.
  3.  
  4. Tutorial Level: BEGINNER
  5.  
  6. Next Tutorial: Examining the simple publisher and subscriber
  7.  
  8.  
  9. catkin rosbuild
  10.  
  11. Contents
  12.  
  13. Writing the Publisher Node
  14. The Code
  15. The Code Explained
  16. Writing the Subscriber Node
  17. The Code
  18. The Code Explained
  19. Building your nodes
  20. Building your nodes
  21. Additional Resources
  22. Video Tutorial
  23.  
  24.  
  25. Writing the Publisher Node
  26. "Node" is the ROS term for an executable that is connected to the ROS network. Here we'll create a publisher ("talker") node which will continually broadcast a message.
  27.  
  28.  
  29.  
  30. Change directories to your beginner_tutorials package you created in your catkin workspace previous tutorials:
  31.  
  32.  
  33. roscd beginner_tutorials
  34. The Code
  35.  
  36. Create a src directory in the beginner_tutorials package directory:
  37.  
  38.  
  39. mkdir src
  40. This directory will contain any source files for our beginner_tutorials package.
  41.  
  42. Create the src/talker.cpp file within the beginner_tutorials package and paste the following inside it:
  43.  
  44. https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp
  45.  
  46. Toggle line numbers
  47. 27 #include "ros/ros.h"
  48. 28 #include "std_msgs/String.h"
  49. 29
  50. 30 #include <sstream>
  51. 31
  52. 32 /**
  53. 33 * This tutorial demonstrates simple sending of messages over the ROS system.
  54. 34 */
  55. 35 int main(int argc, char **argv)
  56. 36 {
  57. 37 /**
  58. 38 * The ros::init() function needs to see argc and argv so that it can perform
  59. 39 * any ROS arguments and name remapping that were provided at the command line.
  60. 40 * For programmatic remappings you can use a different version of init() which takes
  61. 41 * remappings directly, but for most command-line programs, passing argc and argv is
  62. 42 * the easiest way to do it. The third argument to init() is the name of the node.
  63. 43 *
  64. 44 * You must call one of the versions of ros::init() before using any other
  65. 45 * part of the ROS system.
  66. 46 */
  67. 47 ros::init(argc, argv, "talker");
  68. 48
  69. 49 /**
  70. 50 * NodeHandle is the main access point to communications with the ROS system.
  71. 51 * The first NodeHandle constructed will fully initialize this node, and the last
  72. 52 * NodeHandle destructed will close down the node.
  73. 53 */
  74. 54 ros::NodeHandle n;
  75. 55
  76. 56 /**
  77. 57 * The advertise() function is how you tell ROS that you want to
  78. 58 * publish on a given topic name. This invokes a call to the ROS
  79. 59 * master node, which keeps a registry of who is publishing and who
  80. 60 * is subscribing. After this advertise() call is made, the master
  81. 61 * node will notify anyone who is trying to subscribe to this topic name,
  82. 62 * and they will in turn negotiate a peer-to-peer connection with this
  83. 63 * node. advertise() returns a Publisher object which allows you to
  84. 64 * publish messages on that topic through a call to publish(). Once
  85. 65 * all copies of the returned Publisher object are destroyed, the topic
  86. 66 * will be automatically unadvertised.
  87. 67 *
  88. 68 * The second parameter to advertise() is the size of the message queue
  89. 69 * used for publishing messages. If messages are published more quickly
  90. 70 * than we can send them, the number here specifies how many messages to
  91. 71 * buffer up before throwing some away.
  92. 72 */
  93. 73 ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
  94. 74
  95. 75 ros::Rate loop_rate(10);
  96. 76
  97. 77 /**
  98. 78 * A count of how many messages we have sent. This is used to create
  99. 79 * a unique string for each message.
  100. 80 */
  101. 81 int count = 0;
  102. 82 while (ros::ok())
  103. 83 {
  104. 84 /**
  105. 85 * This is a message object. You stuff it with data, and then publish it.
  106. 86 */
  107. 87 std_msgs::String msg;
  108. 88
  109. 89 std::stringstream ss;
  110. 90 ss << "hello world " << count;
  111. 91 msg.data = ss.str();
  112. 92
  113. 93 ROS_INFO("%s", msg.data.c_str());
  114. 94
  115. 95 /**
  116. 96 * The publish() function is how you send messages. The parameter
  117. 97 * is the message object. The type of this object must agree with the type
  118. 98 * given as a template parameter to the advertise<>() call, as was done
  119. 99 * in the constructor above.
  120. 100 */
  121. 101 chatter_pub.publish(msg);
  122. 102
  123. 103 ros::spinOnce();
  124. 104
  125. 105 loop_rate.sleep();
  126. 106 ++count;
  127. 107 }
  128. 108
  129. 109
  130. 110 return 0;
  131. 111 }
  132.  
  133. The Code Explained
  134. Now, let's break the code down.
  135.  
  136.  
  137. Toggle line numbers
  138. 27 #include "ros/ros.h"
  139. 28
  140. ros/ros.h is a convenience include that includes all the headers necessary to use the most common public pieces of the ROS system.
  141.  
  142.  
  143. Toggle line numbers
  144. 28 #include "std_msgs/String.h"
  145. 29
  146. This includes the std_msgs/String message, which resides in the std_msgs package. This is a header generated automatically from the String.msg file in that package. For more information on message definitions, see the msg page.
  147.  
  148.  
  149. Toggle line numbers
  150. 47 ros::init(argc, argv, "talker");
  151. Initialize ROS. This allows ROS to do name remapping through the command line -- not important for now. This is also where we specify the name of our node. Node names must be unique in a running system.
  152.  
  153. The name used here must be a base name, ie. it cannot have a / in it.
  154.  
  155.  
  156. Toggle line numbers
  157. 54 ros::NodeHandle n;
  158. Create a handle to this process' node. The first NodeHandle created will actually do the initialization of the node, and the last one destructed will cleanup any resources the node was using.
  159.  
  160.  
  161. Toggle line numbers
  162. 73 ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
  163. Tell the master that we are going to be publishing a message of type std_msgs/String on the topic chatter. This lets the master tell any nodes listening on chatter that we are going to publish data on that topic. The second argument is the size of our publishing queue. In this case if we are publishing too quickly it will buffer up a maximum of 1000 messages before beginning to throw away old ones.
  164.  
  165. NodeHandle::advertise() returns a ros::Publisher object, which serves two purposes: 1) it contains a publish() method that lets you publish messages onto the topic it was created with, and 2) when it goes out of scope, it will automatically unadvertise.
  166.  
  167.  
  168. Toggle line numbers
  169. 75 ros::Rate loop_rate(10);
  170. A ros::Rate object allows you to specify a frequency that you would like to loop at. It will keep track of how long it has been since the last call to Rate::sleep(), and sleep for the correct amount of time.
  171.  
  172. In this case we tell it we want to run at 10Hz.
  173.  
  174.  
  175. Toggle line numbers
  176. 81 int count = 0;
  177. 82 while (ros::ok())
  178. 83 {
  179. By default roscpp will install a SIGINT handler which provides Ctrl-C handling which will cause ros::ok() to return false if that happens.
  180.  
  181. ros::ok() will return false if:
  182.  
  183. a SIGINT is received (Ctrl-C)
  184. we have been kicked off the network by another node with the same name
  185. ros::shutdown() has been called by another part of the application.
  186. all ros::NodeHandles have been destroyed
  187. Once ros::ok() returns false, all ROS calls will fail.
  188.  
  189.  
  190. Toggle line numbers
  191. 87 std_msgs::String msg;
  192. 88
  193. 89 std::stringstream ss;
  194. 90 ss << "hello world " << count;
  195. 91 msg.data = ss.str();
  196. We broadcast a message on ROS using a message-adapted class, generally generated from a msg file. More complicated datatypes are possible, but for now we're going to use the standard String message, which has one member: "data".
  197.  
  198.  
  199. Toggle line numbers
  200. 101 chatter_pub.publish(msg);
  201. Now we actually broadcast the message to anyone who is connected.
  202.  
  203.  
  204. Toggle line numbers
  205. 93 ROS_INFO("%s", msg.data.c_str());
  206. ROS_INFO and friends are our replacement for printf/cout. See the rosconsole documentation for more information.
  207.  
  208.  
  209. Toggle line numbers
  210. 103 ros::spinOnce();
  211. Calling ros::spinOnce() here is not necessary for this simple program, because we are not receiving any callbacks. However, if you were to add a subscription into this application, and did not have ros::spinOnce() here, your callbacks would never get called. So, add it for good measure.
  212.  
  213.  
  214. Toggle line numbers
  215. 105 loop_rate.sleep();
  216. Now we use the ros::Rate object to sleep for the time remaining to let us hit our 10Hz publish rate.
  217.  
  218. Here's the condensed version of what's going on:
  219.  
  220. Initialize the ROS system
  221. Advertise that we are going to be publishing std_msgs/String messages on the chatter topic to the master
  222. Loop while publishing messages to chatter 10 times a second
  223. Now we need to write a node to receive the messsages.
  224.  
  225. Writing the Subscriber Node
  226. The Code
  227. Create the src/listener.cpp file within the beginner_tutorials package and paste the following inside it:
  228.  
  229. https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/listener/listener.cpp
  230.  
  231. Toggle line numbers
  232. 28 #include "ros/ros.h"
  233. 29 #include "std_msgs/String.h"
  234. 30
  235. 31 /**
  236. 32 * This tutorial demonstrates simple receipt of messages over the ROS system.
  237. 33 */
  238. 34 void chatterCallback(const std_msgs::String::ConstPtr& msg)
  239. 35 {
  240. 36 ROS_INFO("I heard: [%s]", msg->data.c_str());
  241. 37 }
  242. 38
  243. 39 int main(int argc, char **argv)
  244. 40 {
  245. 41 /**
  246. 42 * The ros::init() function needs to see argc and argv so that it can perform
  247. 43 * any ROS arguments and name remapping that were provided at the command line.
  248. 44 * For programmatic remappings you can use a different version of init() which takes
  249. 45 * remappings directly, but for most command-line programs, passing argc and argv is
  250. 46 * the easiest way to do it. The third argument to init() is the name of the node.
  251. 47 *
  252. 48 * You must call one of the versions of ros::init() before using any other
  253. 49 * part of the ROS system.
  254. 50 */
  255. 51 ros::init(argc, argv, "listener");
  256. 52
  257. 53 /**
  258. 54 * NodeHandle is the main access point to communications with the ROS system.
  259. 55 * The first NodeHandle constructed will fully initialize this node, and the last
  260. 56 * NodeHandle destructed will close down the node.
  261. 57 */
  262. 58 ros::NodeHandle n;
  263. 59
  264. 60 /**
  265. 61 * The subscribe() call is how you tell ROS that you want to receive messages
  266. 62 * on a given topic. This invokes a call to the ROS
  267. 63 * master node, which keeps a registry of who is publishing and who
  268. 64 * is subscribing. Messages are passed to a callback function, here
  269. 65 * called chatterCallback. subscribe() returns a Subscriber object that you
  270. 66 * must hold on to until you want to unsubscribe. When all copies of the Subscriber
  271. 67 * object go out of scope, this callback will automatically be unsubscribed from
  272. 68 * this topic.
  273. 69 *
  274. 70 * The second parameter to the subscribe() function is the size of the message
  275. 71 * queue. If messages are arriving faster than they are being processed, this
  276. 72 * is the number of messages that will be buffered up before beginning to throw
  277. 73 * away the oldest ones.
  278. 74 */
  279. 75 ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
  280. 76
  281. 77 /**
  282. 78 * ros::spin() will enter a loop, pumping callbacks. With this version, all
  283. 79 * callbacks will be called from within this thread (the main one). ros::spin()
  284. 80 * will exit when Ctrl-C is pressed, or the node is shutdown by the master.
  285. 81 */
  286. 82 ros::spin();
  287. 83
  288. 84 return 0;
  289. 85 }
  290.  
  291. The Code Explained
  292. Now, let's break it down piece by piece, ignoring some pieces that have already been explained above.
  293.  
  294.  
  295. Toggle line numbers
  296. 34 void chatterCallback(const std_msgs::String::ConstPtr& msg)
  297. 35 {
  298. 36 ROS_INFO("I heard: [%s]", msg->data.c_str());
  299. 37 }
  300. This is the callback function that will get called when a new message has arrived on the chatter topic. The message is passed in a boost shared_ptr, which means you can store it off if you want, without worrying about it getting deleted underneath you, and without copying the underlying data.
  301.  
  302.  
  303. Toggle line numbers
  304. 75 ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
  305. Subscribe to the chatter topic with the master. ROS will call the chatterCallback() function whenever a new message arrives. The 2nd argument is the queue size, in case we are not able to process messages fast enough. In this case, if the queue reaches 1000 messages, we will start throwing away old messages as new ones arrive.
  306.  
  307. NodeHandle::subscribe() returns a ros::Subscriber object, that you must hold on to until you want to unsubscribe. When the Subscriber object is destructed, it will automatically unsubscribe from the chatter topic.
  308.  
  309. There are versions of the NodeHandle::subscribe() function which allow you to specify a class member function, or even anything callable by a Boost.Function object. The roscpp overview contains more information.
  310.  
  311.  
  312. Toggle line numbers
  313. 82 ros::spin();
  314. ros::spin() enters a loop, calling message callbacks as fast as possible. Don't worry though, if there's nothing for it to do it won't use much CPU. ros::spin() will exit once ros::ok() returns false, which means ros::shutdown() has been called, either by the default Ctrl-C handler, the master telling us to shutdown, or it being called manually.
  315.  
  316. There are other ways of pumping callbacks, but we won't worry about those here. The roscpp_tutorials package has some demo applications which demonstrate this. The roscpp overview also contains more information.
  317.  
  318. Again, here's a condensed version of what's going on:
  319.  
  320. Initialize the ROS system
  321. Subscribe to the chatter topic
  322. Spin, waiting for messages to arrive
  323. When a message arrives, the chatterCallback() function is called
  324.  
  325.  
  326. Building your nodes
  327. You used catkin_create_pkg in a previous tutorial which created a package.xml and a CMakeLists.txt file for you.
  328.  
  329. The generated CMakeLists.txt should look like this (with modifications from the Creating Msgs and Srvs tutorial and unused comments and examples removed):
  330.  
  331. https://raw.github.com/ros/catkin_tutorials/master/create_package_modified/catkin_ws/src/beginner_tutorials/CMakeLists.txt
  332.  
  333. Toggle line numbers
  334. 1 cmake_minimum_required(VERSION 2.8.3)
  335. 2 project(beginner_tutorials)
  336. 3
  337. 4 ## Find catkin and any catkin packages
  338. 5 find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
  339. 6
  340. 7 ## Declare ROS messages and services
  341. 8 add_message_files(DIRECTORY msg FILES Num.msg)
  342. 9 add_service_files(DIRECTORY srv FILES AddTwoInts.srv)
  343. 10
  344. 11 ## Generate added messages and services
  345. 12 generate_messages(DEPENDENCIES std_msgs)
  346. 13
  347. 14 ## Declare a catkin package
  348. 15 catkin_package()
  349.  
  350. Don't worry about modifying the commented (#) examples, simply add these few lines to the bottom of your CMakeLists.txt:
  351.  
  352.  
  353. include_directories(include ${catkin_INCLUDE_DIRS})
  354.  
  355. add_executable(talker src/talker.cpp)
  356. target_link_libraries(talker ${catkin_LIBRARIES})
  357. add_dependencies(talker beginner_tutorials_generate_messages_cpp)
  358.  
  359. add_executable(listener src/listener.cpp)
  360. target_link_libraries(listener ${catkin_LIBRARIES})
  361. add_dependencies(listener beginner_tutorials_generate_messages_cpp)
  362. Your resulting CMakeLists.txt file should look like this:
  363.  
  364. https://raw.github.com/ros/catkin_tutorials/master/create_package_pubsub/catkin_ws/src/beginner_tutorials/CMakeLists.txt
  365.  
  366. Toggle line numbers
  367. 1 cmake_minimum_required(VERSION 2.8.3)
  368. 2 project(beginner_tutorials)
  369. 3
  370. 4 ## Find catkin and any catkin packages
  371. 5 find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
  372. 6
  373. 7 ## Declare ROS messages and services
  374. 8 add_message_files(FILES Num.msg)
  375. 9 add_service_files(FILES AddTwoInts.srv)
  376. 10
  377. 11 ## Generate added messages and services
  378. 12 generate_messages(DEPENDENCIES std_msgs)
  379. 13
  380. 14 ## Declare a catkin package
  381. 15 catkin_package()
  382. 16
  383. 17 ## Build talker and listener
  384. 18 include_directories(include ${catkin_INCLUDE_DIRS})
  385. 19
  386. 20 add_executable(talker src/talker.cpp)
  387. 21 target_link_libraries(talker ${catkin_LIBRARIES})
  388. 22 add_dependencies(talker beginner_tutorials_generate_messages_cpp)
  389. 23
  390. 24 add_executable(listener src/listener.cpp)
  391. 25 target_link_libraries(listener ${catkin_LIBRARIES})
  392. 26 add_dependencies(listener beginner_tutorials_generate_messages_cpp)
  393.  
  394. This will create two executables, talker and listener, which by default will go into package directory of your devel space, located by default at ~/catkin_ws/devel/lib/<package name>.
  395.  
  396. Note that you have to add dependencies for the executable targets to message generation targets:
  397.  
  398. add_dependencies(talker beginner_tutorials_generate_messages_cpp)
  399. This makes sure message headers of this package are generated before being used. If you use messages from other packages inside your catkin workspace, you need to add dependencies to their respective generation targets as well, because catkin builds all projects in parallel. As of *Groovy* you can use the following variable to depend on all necessary targets:
  400.  
  401. target_link_libraries(talker ${catkin_LIBRARIES})
  402. You can invoke executables directly or you can use rosrun to invoke them. They are not placed in '<prefix>/bin' because that would pollute the PATH when installing your package to the system. If you wish for your executable to be on the PATH at installation time, you can setup an install target, see: catkin/CMakeLists.txt
  403.  
  404. For more detailed discription of the CMakeLists.txt file see: catkin/CMakeLists.txt
  405.  
  406. Now run catkin_make:
  407.  
  408. # In your catkin workspace
  409. $ catkin_make
  410. Note: Or if you're adding as new pkg, you may need to tell catkin to force making by --force-cmake option. See catkin/Tutorials/using_a_workspace#With_catkin_make.
  411.  
  412. Now that you have written a simple publisher and subscriber, let's examine the simple publisher and subscriber.
  413.  
  414. Additional Resources
  415. Here are some additional resources contributed by the community:
  416.  
  417. Video Tutorial
  418. The following video presents a small tutorial explaining how to write and test a publisher and subscriber in ROS with C++ and Python based on the talker/listener example above
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement