{"id":1090,"date":"2020-01-06T15:14:59","date_gmt":"2020-01-06T15:14:59","guid":{"rendered":"https:\/\/roboticsbackend.com\/?p=1090"},"modified":"2021-09-28T20:26:35","modified_gmt":"2021-09-28T20:26:35","slug":"ros-asyncspinner-example","status":"publish","type":"post","link":"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/","title":{"rendered":"ROS AsyncSpinner Example"},"content":{"rendered":"<p>In this tutorial I&#8217;ll show you how to use a ROS AsyncSpinner with an example. You&#8217;ll see when using a roscpp AsyncSpinner is required, instead of the standard roscpp spinner.<\/p>\n<p>This tutorial could also be called: how to solve roscpp callback issues, when it seems that some callbacks are stuck or late.<\/p>\n<p>We&#8217;ll start by seeing a code example which has some callback problems. Some callbacks will be stuck, waiting for other callbacks (from different topics) to finish. And then we&#8217;ll see how using AsyncSpinner instead of the standard roscpp spinner can solve that.<\/p>\n<p>Also, the problem you&#8217;ll see here only applies to roscpp callbacks, not rospy. You&#8217;ll get to know why during this tutorial.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_81 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-69fcb42aaafcf\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-69fcb42aaafcf\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#If_you_like_having_problems_start_with_a_few_roscpp_callbacks\" >If you like having problems, start with a few roscpp callbacks!<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#Setup_with_no_callback_issue_for_now\" >Setup with no callback issue (for now)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#A_callback_is_stuck_or_late_because_of_another_callback\" >A callback is stuck or late because of another callback<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#roscpp_spin_is_single-threaded\" >roscpp spin is single-threaded<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#Other_scenarios_when_you_can_have_this_issue\" >Other scenarios when you can have this issue<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#Using_roscpp_AsyncSpinner\" >Using roscpp AsyncSpinner<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#Solve_the_callback_problem_with_AsyncSpinner\" >Solve the callback problem with AsyncSpinner<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#Conclusion_on_AsyncSpinner\" >Conclusion on AsyncSpinner<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"If_you_like_having_problems_start_with_a_few_roscpp_callbacks\"><\/span>If you like having problems, start with a few roscpp callbacks!<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>It&#8217;s really important that you understand the problem first. I won&#8217;t tell you to use AsyncSpinner because it&#8217;s just &#8220;better&#8221;, without letting you know why.<\/p>\n<p>That&#8217;s why this first part is quite long and detailed, so you can understand the real reason behind all that. Once you understand the issue here, you&#8217;ll easily see when it makes sense to use AsyncSpinner in your roscpp nodes.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Setup_with_no_callback_issue_for_now\"><\/span>Setup with no callback issue (for now)<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Let&#8217;s create one roscpp node with 2 simple callbacks for 2 simple topics.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">#include &lt;ros\/ros.h&gt;\r\n#include &lt;std_msgs\/String.h&gt;\r\n\r\nvoid callbackTalker1(const std_msgs::String::ConstPtr &amp;msg)\r\n{\r\n    ROS_INFO(\"In callback talker1.\");\r\n    ROS_INFO(\"%s\", msg-&gt;data.c_str());\r\n}\r\n\r\nvoid callbackTalker2(const std_msgs::String::ConstPtr &amp;msg)\r\n{\r\n    ROS_INFO(\"In callback talker2.\");\r\n    ROS_INFO(\"%s\", msg-&gt;data.c_str());\r\n}\r\n\r\n\r\nint main(int argc, char **argv)\r\n{\r\n    ros::init(argc, argv, \"talker_subscribers\");\r\n    ros::NodeHandle nh;\r\n\r\n    ros::Subscriber counter1_sub = nh.subscribe(\"talker1\", 10, callbackTalker1);\r\n    ros::Subscriber counter2_sub = nh.subscribe(\"talker2\", 10, callbackTalker2);\r\n\r\n    ros::spin();\r\n}<\/pre>\n<p>That&#8217;s pretty simple here: the node subscribes to 2 topics, named &#8220;talker1&#8221; and &#8220;talker2&#8221;. Both topics use the std_msg\/String data type. The 2 callbacks do the same thing, which is displaying the received message.<\/p>\n<p>So, it seems like there is no problem with that!<\/p>\n<p>Now let&#8217;s create a node to publish on those topics.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">#!\/usr\/bin\/env python\r\n\r\nimport rospy\r\nfrom std_msgs.msg import String\r\n\r\nif __name__ == \"__main__\":\r\n    rospy.init_node(\"talker_publishers\")\r\n\r\n    talker1_pub = rospy.Publisher(\"talker1\", String, queue_size=10)\r\n    talker2_pub = rospy.Publisher(\"talker2\", String, queue_size=10)\r\n\r\n    rate = rospy.Rate(1.0)\r\n\r\n    while not rospy.is_shutdown():\r\n        talker1_pub.publish(\"Hey\")\r\n        talker2_pub.publish(\"Oy\")\r\n        rate.sleep()<\/pre>\n<p><strong>Important note<\/strong>: you can create those publishers in Cpp or Python, it really doesn&#8217;t matter. Nothing is wrong with the publisher node, the issue will come (later) from the subscriber node. You could also publish directly from the terminal, but for simplicity, it&#8217;s better to just write a small and minimal node.<\/p>\n<p>This node will simply publish on both &#8220;talker1&#8221; and &#8220;talker2&#8221; topics at a 1 Hz frequency. The first publisher will send &#8220;Hey&#8221; and the second publisher will send &#8220;Oy&#8221;. That&#8217;s it. For the rest of this tutorial we won&#8217;t modify this node.<\/p>\n<p>Now, you can start <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">roscore<\/code>, and then the publisher node with for example <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rosrun asyncspinner_tutorials talker_publishers.py<\/code>. You can keep it running for the rest of this tutorial or restart it for each test. For simplicity, in the following, when I write &#8220;start the subscriber&#8221; I&#8217;ll assume that the publisher is already running.<\/p>\n<p>And, if you start the roscpp subscriber node, you&#8217;ll get this:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">$ rosrun asyncspinner_tutorials talker_subscribers \r\n[ INFO] [1572016597.728219447]: Hey\r\n[ INFO] [1572016597.730289973]: Oy\r\n[ INFO] [1572016598.728299107]: Oy\r\n[ INFO] [1572016598.728426453]: Hey\r\n[ INFO] [1572016599.728055430]: Hey\r\n[ INFO] [1572016599.728406145]: Oy\r\n[ INFO] [1572016600.728138572]: Hey\r\n[ INFO] [1572016600.728322249]: Oy\r\n[ INFO] [1572016601.728317492]: Oy\r\n[ INFO] [1572016601.728488483]: Hey<\/pre>\n<p>It&#8217;s working pretty well, no problem here! From the log timestamps you can see that each &#8220;Hey&#8221; and &#8220;Oy&#8221; are received by the subscriber and printed every second, which is the rate set on the publisher side.<\/p>\n<p>You can notice that sometimes, data from the &#8220;talker1&#8221; topic comes after data from the &#8220;talker2&#8221; topic. This is not an issue here. In fact, in the publisher we publish on those topics without any delay, so it&#8217;s like they&#8217;re sent at the exact same time. The subscriber node will receive them at almost the same time, and when waking up the callback functions, the second one may be called before the first one, that&#8217;s it.<\/p>\n<p>Now, let&#8217;s get to the problem.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"A_callback_is_stuck_or_late_because_of_another_callback\"><\/span>A callback is stuck or late because of another callback<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Let&#8217;s add a delay in the first callback.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">void callbackTalker1(const std_msgs::String::ConstPtr &amp;msg)\r\n{\r\n    ROS_INFO(\"In callback talker1.\");\r\n    ros::Duration(2.0).sleep();\r\n    ROS_INFO(\"%s\", msg-&gt;data.c_str());\r\n}<\/pre>\n<p>The callback for &#8220;talker1&#8221; topic will now take 2 seconds, in addition to the time to process log messages.<\/p>\n<p>I&#8217;ve also added a log before the sleep() function so we can see in the terminal when the callback is called, and when it&#8217;s about to exit.<\/p>\n<p>Compile with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">catkin_make<\/code>, and start the roscpp subscriber node again.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">$ rosrun asyncspinner_tutorials talker_subscribers \r\n[ INFO] [1572017356.727231274]: In callback talker1. # --&gt; 1. + 2.\r\n[ INFO] [1572017358.729365483]: Hey                  # --&gt; 3.\r\n[ INFO] [1572017358.729531863]: Oy                   # --&gt; 4.\r\n[ INFO] [1572017358.729620036]: Oy\r\n[ INFO] [1572017358.729695363]: In callback talker1. # --&gt; 5.\r\n[ INFO] [1572017360.729897573]: Hey\r\n[ INFO] [1572017360.730062467]: In callback talker1.\r\n[ INFO] [1572017362.730324234]: Hey\r\n[ INFO] [1572017362.730476893]: Oy\r\n[ INFO] [1572017362.730599778]: Oy\r\n[ INFO] [1572017362.730718380]: In callback talker1.\r\n[ INFO] [1572017364.730940686]: Hey\r\n[ INFO] [1572017364.731074145]: In callback talker1.\r\n[ INFO] [1572017366.731296653]: Hey\r\n[ INFO] [1572017366.731472865]: Oy\r\n[ INFO] [1572017366.731568124]: In callback talker1.<\/pre>\n<p>Things start to get weird!<\/p>\n<p>So, what&#8217;s happening here (I&#8217;ve added numbers after the logs so this will be easier for you to follow):<\/p>\n<ol>\n<li>The subscriber node first receives a message from &#8220;talker1&#8221; and calls its associated callback.<\/li>\n<li>The callback is stuck for 2 seconds because of the sleep() we added. Nothing wrong with that, but as you can see it also prevents the &#8220;talker2&#8221; callback from being called! We didn&#8217;t change the &#8220;talker2&#8221; callback and yet it&#8217;s stuck because of another callback taking too much time.<\/li>\n<li>When the 2 seconds pause ends, &#8220;Hey&#8221; is printed, the &#8220;talker1&#8221; callback exits.<\/li>\n<li>Right after that &#8211; as you can see with the log timestamps &#8211; the &#8220;talker2&#8221; callback is called 2 times! This is because messages continued to arrive while &#8220;talker1&#8221; callback was stuck (the node kept them in a temporary queue), and now all the callbacks are triggered &#8211; one by one.<\/li>\n<li>And right after that, the &#8220;talker1&#8221; callback is called again, because while it was waiting for 2 seconds, another &#8220;talker1&#8221; message arrived, and the callback is already late.<\/li>\n<\/ol>\n<p>And now, you already have a few messages in both the &#8220;talker1&#8221; and &#8220;talker2&#8221; waiting queues. So it&#8217;s becoming a mess and you can&#8217;t tell when a message will be printed by the subscriber node.<\/p>\n<p>Also, when the temporary queue for a topic is full (limit set by the queue size parameter when creating the publisher or subscriber), some messages will be lost. In this code example I&#8217;ve set the queue size to 10. It means that 10 messages maximum will be saved, waiting for the callback to be triggered. After that, when a new message arrives, another one is deleted &#8211; and definitively lost.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"roscpp_spin_is_single-threaded\"><\/span>roscpp spin is single-threaded<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>First, let&#8217;s talk about the &#8220;talker1&#8221; subscriber. It&#8217;s quite obvious that if you publish on this topic every second, and then in the callback you wait for 2 seconds, messages on this topic will be delayed. It&#8217;s not really an internal problem here, it&#8217;s more of a developer choice to do that (in this case, not a very good choice, but that made a good example for this tutorial).<\/p>\n<p>The real question is: <strong>why is the &#8220;talker2&#8221; callback affected by the &#8220;talker1&#8221; callback?<\/strong> That&#8217;s the problem we will solve in this tutorial. We really want to receive messages from &#8220;talker2&#8221; at 1Hz.<\/p>\n<p>The reason behind all that is that the roscpp standard spinner (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">ros::spin()<\/code>) is single-threaded. It will execute all callbacks one by one, trying to respect the arrival date of messages or requests.<\/p>\n<p>A callback will be executed only if the previous callback has finished. And the next callback will have to wait for the current callback to end.<\/p>\n<p>That&#8217;s a pretty simple mechanism, and now you can understand why we had so many issues with the &#8220;talker2&#8221; callback. The &#8220;talker1&#8221; callback took a long time to complete, thus preventing the &#8220;talker2&#8221; callback from being executed.<\/p>\n<p>Note: this issue only happens with roscpp. The rospy spinner is already multi-threaded, so you won&#8217;t be able to reproduce this issue if you translate the Cpp subscriber node to Python.<\/p>\n<p>And as you could guess, the solution here will be to use the roscpp AsyncSpinner!<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Other_scenarios_when_you_can_have_this_issue\"><\/span>Other scenarios when you can have this issue<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Of course, this issue doesn&#8217;t only happen when you have 2 subscribers and one is blocking the other. I chose this example because I think it allows to explain the problem without having to write &#8211; and explain &#8211; too much code.<\/p>\n<p>Here are some common cases when you might encounter this problem:<\/p>\n<ul>\n<li>You have one subscriber and one service server. The service server callback takes some time to respond, and the subscriber callback is delayed.<\/li>\n<li>You have multiple subscriber callbacks and need a more precise timing. Even if no callback is completely blocking the callback stack, when you add up all callback execution duration, it can lead to some unwanted small &#8220;delays&#8221; in the last called callback.<\/li>\n<li>You are using the roscpp ActionServer and want to be able to work with action callbacks and other subscriber callbacks in the same node.<\/li>\n<li>Etc.<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"Using_roscpp_AsyncSpinner\"><\/span>Using roscpp AsyncSpinner<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>AsyncSpinner is a threaded spinner. It means that each callback will get its own thread (if available). This should solve our problem with the &#8220;talker2&#8221; callback.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Solve_the_callback_problem_with_AsyncSpinner\"><\/span>Solve the callback problem with AsyncSpinner<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Keep the &#8220;talker1&#8221; callback with the delay, just change the spinner in the main.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">...\r\n\r\nint main(int argc, char **argv)\r\n{\r\n    ros::init(argc, argv, \"talker_subscribers\");\r\n    ros::NodeHandle nh;\r\n\r\n    ros::AsyncSpinner spinner(0);\r\n    spinner.start();\r\n\r\n    ros::Subscriber counter1_sub = nh.subscribe(\"talker1\", 10, callbackTalker1);\r\n    ros::Subscriber counter2_sub = nh.subscribe(\"talker2\", 10, callbackTalker2);\r\n\r\n    ros::waitForShutdown();\r\n    \/\/ ros::spin(); --&gt; we don't use that anymore\r\n}<\/pre>\n<p>You need 3 lines instead of one:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">ros::AsyncSpinner spinner(0);<\/code> will create the spinner. The parameter corresponds to the number of threads you want to use. 0 means that the spinner will use as many threads as there are processors on your machine. If you use 3 for example, only 3 threads will be used.<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">spinner.start();<\/code> will start the AsyncSpinner asynchronously, as its name suggests. Note that the multi-threaded spinner is now running in the background, and the program continues.<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">ros::waitForShutdown();<\/code> is required here, because the AsyncSpinner won&#8217;t block. This command simply waits for the node to be killed.<\/li>\n<\/ul>\n<p>Now, let&#8217;s run the subscriber node again:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">$ rosrun asyncspinner_tutorials talker_subscribers \r\n[ INFO] [1572020143.073816636]: In callback talker1.\r\n[ INFO] [1572020143.075847158]: Oy\r\n[ INFO] [1572020144.073720083]: Oy\r\n[ INFO] [1572020145.072990625]: Oy\r\n[ INFO] [1572020145.075870583]: Hey\r\n[ INFO] [1572020145.075935900]: In callback talker1.\r\n[ INFO] [1572020146.072956221]: Oy\r\n[ INFO] [1572020147.072851190]: Oy\r\n[ INFO] [1572020147.076047053]: Hey\r\n[ INFO] [1572020147.076103314]: In callback talker1.\r\n[ INFO] [1572020148.072887200]: Oy\r\n[ INFO] [1572020149.072907233]: Oy\r\n[ INFO] [1572020149.076195823]: Hey<\/pre>\n<p>Well, as you can see, the &#8220;talker2&#8221; callback is now properly working! You can see a &#8220;Oy&#8221; message every second, no matter what is happening with the &#8220;talker1&#8221; callback.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Conclusion_on_AsyncSpinner\"><\/span>Conclusion on AsyncSpinner<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>This callback issue can sometimes be quite tricky to find. You may spend a huge amount of time trying to debug your application, before you find out that a callback is not on time because of other callbacks. I hope this ROS AsyncSpinner example will help you shorten this debugging time.<\/p>\n<p>A good practice is to always keep your callback functions fast, so you can avoid this kind of situation. But when you have several topic subscribers, service servers, and action servers running on your node, well that can quickly become quite tricky!<\/p>\n<p>So, if your node is quite simple and you know exactly what&#8217;s going on, using a single threaded spinner (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">ros::spin()<\/code>) might be more appropriate. If you have too many callbacks and are afraid that things will go out of control, use a roscpp AsyncSpinner.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial I&#8217;ll show you how to use a ROS AsyncSpinner with an example. You&#8217;ll see when using a roscpp AsyncSpinner is required, instead of the standard roscpp spinner. This tutorial could also be called: how to solve roscpp callback issues, when it seems that some callbacks are stuck or late. We&#8217;ll start by &#8230; <a title=\"ROS AsyncSpinner Example\" class=\"read-more\" href=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/\" aria-label=\"Read more about ROS AsyncSpinner Example\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-1090","post","type-post","status-publish","format-standard","hentry","category-ros"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>ROS AsyncSpinner Example - The Robotics Back-End<\/title>\n<meta name=\"description\" content=\"Solve your callback problems with this roscpp AsyncSpinner tutorial (+ code example). Learn when to use this multi-threaded spinner.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"ROS AsyncSpinner Example - The Robotics Back-End\" \/>\n<meta property=\"og:description\" content=\"Solve your callback problems with this roscpp AsyncSpinner tutorial (+ code example). Learn when to use this multi-threaded spinner.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/\" \/>\n<meta property=\"og:site_name\" content=\"The Robotics Back-End\" \/>\n<meta property=\"article:published_time\" content=\"2020-01-06T15:14:59+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-09-28T20:26:35+00:00\" \/>\n<meta name=\"author\" content=\"ed\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@RoboticsBackend\" \/>\n<meta name=\"twitter:site\" content=\"@RoboticsBackend\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"ed\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/\"},\"author\":{\"name\":\"ed\",\"@id\":\"https:\/\/roboticsbackend.com\/#\/schema\/person\/a20832f15e39847d8eea5be981767353\"},\"headline\":\"ROS AsyncSpinner Example\",\"datePublished\":\"2020-01-06T15:14:59+00:00\",\"dateModified\":\"2021-09-28T20:26:35+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/\"},\"wordCount\":1642,\"publisher\":{\"@id\":\"https:\/\/roboticsbackend.com\/#organization\"},\"articleSection\":[\"ROS Tutorials\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/\",\"url\":\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/\",\"name\":\"ROS AsyncSpinner Example - The Robotics Back-End\",\"isPartOf\":{\"@id\":\"https:\/\/roboticsbackend.com\/#website\"},\"datePublished\":\"2020-01-06T15:14:59+00:00\",\"dateModified\":\"2021-09-28T20:26:35+00:00\",\"description\":\"Solve your callback problems with this roscpp AsyncSpinner tutorial (+ code example). Learn when to use this multi-threaded spinner.\",\"breadcrumb\":{\"@id\":\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/roboticsbackend.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"ROS AsyncSpinner Example\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/roboticsbackend.com\/#website\",\"url\":\"https:\/\/roboticsbackend.com\/\",\"name\":\"The Robotics Back-End\",\"description\":\"Program Robots, Step by Step\",\"publisher\":{\"@id\":\"https:\/\/roboticsbackend.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/roboticsbackend.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/roboticsbackend.com\/#organization\",\"name\":\"Robotics Back-End\",\"url\":\"https:\/\/roboticsbackend.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/roboticsbackend.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/roboticsbackend.com\/wp-content\/uploads\/2020\/02\/logo_hd.png\",\"contentUrl\":\"https:\/\/roboticsbackend.com\/wp-content\/uploads\/2020\/02\/logo_hd.png\",\"width\":2500,\"height\":1875,\"caption\":\"Robotics Back-End\"},\"image\":{\"@id\":\"https:\/\/roboticsbackend.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/RoboticsBackend\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/roboticsbackend.com\/#\/schema\/person\/a20832f15e39847d8eea5be981767353\",\"name\":\"ed\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/roboticsbackend.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/7b666620f11fb12df5674e1e1ee525afe3d4ceecdaa57f8c60f6a937a33e3427?s=96&d=identicon&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/7b666620f11fb12df5674e1e1ee525afe3d4ceecdaa57f8c60f6a937a33e3427?s=96&d=identicon&r=g\",\"caption\":\"ed\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"ROS AsyncSpinner Example - The Robotics Back-End","description":"Solve your callback problems with this roscpp AsyncSpinner tutorial (+ code example). Learn when to use this multi-threaded spinner.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/","og_locale":"en_US","og_type":"article","og_title":"ROS AsyncSpinner Example - The Robotics Back-End","og_description":"Solve your callback problems with this roscpp AsyncSpinner tutorial (+ code example). Learn when to use this multi-threaded spinner.","og_url":"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/","og_site_name":"The Robotics Back-End","article_published_time":"2020-01-06T15:14:59+00:00","article_modified_time":"2021-09-28T20:26:35+00:00","author":"ed","twitter_card":"summary_large_image","twitter_creator":"@RoboticsBackend","twitter_site":"@RoboticsBackend","twitter_misc":{"Written by":"ed","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#article","isPartOf":{"@id":"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/"},"author":{"name":"ed","@id":"https:\/\/roboticsbackend.com\/#\/schema\/person\/a20832f15e39847d8eea5be981767353"},"headline":"ROS AsyncSpinner Example","datePublished":"2020-01-06T15:14:59+00:00","dateModified":"2021-09-28T20:26:35+00:00","mainEntityOfPage":{"@id":"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/"},"wordCount":1642,"publisher":{"@id":"https:\/\/roboticsbackend.com\/#organization"},"articleSection":["ROS Tutorials"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/","url":"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/","name":"ROS AsyncSpinner Example - The Robotics Back-End","isPartOf":{"@id":"https:\/\/roboticsbackend.com\/#website"},"datePublished":"2020-01-06T15:14:59+00:00","dateModified":"2021-09-28T20:26:35+00:00","description":"Solve your callback problems with this roscpp AsyncSpinner tutorial (+ code example). Learn when to use this multi-threaded spinner.","breadcrumb":{"@id":"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/roboticsbackend.com\/ros-asyncspinner-example\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/roboticsbackend.com\/"},{"@type":"ListItem","position":2,"name":"ROS AsyncSpinner Example"}]},{"@type":"WebSite","@id":"https:\/\/roboticsbackend.com\/#website","url":"https:\/\/roboticsbackend.com\/","name":"The Robotics Back-End","description":"Program Robots, Step by Step","publisher":{"@id":"https:\/\/roboticsbackend.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/roboticsbackend.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/roboticsbackend.com\/#organization","name":"Robotics Back-End","url":"https:\/\/roboticsbackend.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/roboticsbackend.com\/#\/schema\/logo\/image\/","url":"https:\/\/roboticsbackend.com\/wp-content\/uploads\/2020\/02\/logo_hd.png","contentUrl":"https:\/\/roboticsbackend.com\/wp-content\/uploads\/2020\/02\/logo_hd.png","width":2500,"height":1875,"caption":"Robotics Back-End"},"image":{"@id":"https:\/\/roboticsbackend.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/RoboticsBackend"]},{"@type":"Person","@id":"https:\/\/roboticsbackend.com\/#\/schema\/person\/a20832f15e39847d8eea5be981767353","name":"ed","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/roboticsbackend.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/7b666620f11fb12df5674e1e1ee525afe3d4ceecdaa57f8c60f6a937a33e3427?s=96&d=identicon&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/7b666620f11fb12df5674e1e1ee525afe3d4ceecdaa57f8c60f6a937a33e3427?s=96&d=identicon&r=g","caption":"ed"}}]}},"_links":{"self":[{"href":"https:\/\/roboticsbackend.com\/wp-json\/wp\/v2\/posts\/1090","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/roboticsbackend.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/roboticsbackend.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/roboticsbackend.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/roboticsbackend.com\/wp-json\/wp\/v2\/comments?post=1090"}],"version-history":[{"count":0,"href":"https:\/\/roboticsbackend.com\/wp-json\/wp\/v2\/posts\/1090\/revisions"}],"wp:attachment":[{"href":"https:\/\/roboticsbackend.com\/wp-json\/wp\/v2\/media?parent=1090"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/roboticsbackend.com\/wp-json\/wp\/v2\/categories?post=1090"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/roboticsbackend.com\/wp-json\/wp\/v2\/tags?post=1090"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}