{"id":314,"date":"2020-03-20T09:19:03","date_gmt":"2020-03-20T09:19:03","guid":{"rendered":"https:\/\/erdalpekel.de\/?p=314"},"modified":"2021-02-13T09:22:41","modified_gmt":"2021-02-13T09:22:41","slug":"automatic-robot-state-initialization-in-gazebo","status":"publish","type":"post","link":"https:\/\/erdalpekel.de\/?p=314","title":{"rendered":"Automatic robot state initialization in Gazebo"},"content":{"rendered":"\n<p>In the last blog post we introduced position based control of the FRANKA EMIKA Panda robot in the Gazebo simulation environment for a more stable movement profile. We also introduced a custom joint position controller that can be used to manually command the joints within the simulation to a desired position in order to prevent motion planning initialization errors that are caused by invalid joint angle configurations after spawning the robot in the Gazebo world.<\/p>\n\n\n\n<p>In this post we are going to implement a ROS node that will automatically reset the robot state at startup in contrast to the manual terminal commands in the last post and therefore simplify the usage of the simulation package overall.<\/p>\n\n\n\n<p>A quick side note: The current package configuration causes no p gain specified for pid errors. The gain parameters are not specified intentionally. If we were to specify gains, the gazebo robot hardware initializer would employ a PID controller, which in turn requires parameter tuning for smooth operation. If we however do not specify gain parameters the initialization will fall back to custom position control mechanism which is more stable (see implementation details <a href=\"https:\/\/github.com\/ros-simulation\/gazebo_ros_pkgs\/blob\/e42cc26a473733b05abc648181a6de1286105ed6\/gazebo_ros_control\/src\/default_robot_hw_sim.cpp#L222\">1<\/a> <a href=\"https:\/\/github.com\/ros-simulation\/gazebo_ros_pkgs\/blob\/e42cc26a473733b05abc648181a6de1286105ed6\/gazebo_ros_control\/src\/default_robot_hw_sim.cpp#L322\">2<\/a>).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Robot state initalization<\/h2>\n\n\n\n<p>We will start off by adding the dependencies to the package.xml and CMakeLists.txt file and add the new node <em>robot_state_initializer_node<\/em> to the CMake configuration:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"vim\" class=\"language-vim\">find_package(catkin REQUIRED\n             COMPONENTS controller_manager ...)\n\nadd_executable(robot_state_initializer_node src\/robot_state_initializer.cpp)\n\ntarget_link_libraries(robot_state_initializer_node ${catkin_LIBRARIES})<\/code><\/pre>\n\n\n\n<p>The <em>controller_manager<\/em> dependency is needed for the custom message types that will be used for programmatic controller switching within the new ROS node. It should also be added to the dependencies in the package.xml file. In our case it is already there.<\/p>\n\n\n\n<p>We will now create the file robot_state_initializer.cpp:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"cpp\" class=\"language-cpp\">#include &lt;controller_manager_msgs\/SwitchController.h&gt;\n#include &lt;ros\/ros.h&gt;\n#include &lt;std_msgs\/Float64MultiArray.h&gt;\n\nint main(int argc, char **argv) {\n  ros::init(argc, argv, \"robot_state_initializer_node\");\n  ros::NodeHandle node_handle;\n\n  std::vector&lt;double&gt; panda_ready_state{0, -0.785, 0, -2.356, 0, 1.571, 0.785};\n\n  \/\/ define variables\n  std::string joint_position_command_topic{\"\/joint_position_controller\/command\"},\n      controller_manager_switch_topic{\"\/controller_manager\/switch_controller\"};\n  ros::ServiceClient switch_controller_client =\n      node_handle.serviceClient&lt;controller_manager_msgs::SwitchController&gt;(controller_manager_switch_topic);\n  ros::Publisher joint_position_publisher =\n      node_handle.advertise&lt;std_msgs::Float64MultiArray&gt;(joint_position_command_topic, 1);\n\n  \/\/ sleep for 2 seconds in order to make sure that the system is up and running\n  ros::Duration(2.0).sleep();\n\n  \/\/ 1. switch from default joint trajectory controller to custom position controller\n  std::string panda_arm_controller{\"panda_arm_controller\"}, panda_hand_controller{\"panda_hand_controller\"},\n      joint_position_controller{\"joint_position_controller\"};\n  std::vector&lt;std::string&gt; stop_controllers{panda_arm_controller, panda_hand_controller};\n  std::vector&lt;std::string&gt; start_controllers{joint_position_controller};\n\n  controller_manager_msgs::SwitchController srv_switch_controller;\n  srv_switch_controller.request.stop_controllers = stop_controllers;\n  srv_switch_controller.request.start_controllers = start_controllers;\n  srv_switch_controller.request.strictness = controller_manager_msgs::SwitchControllerRequest::BEST_EFFORT;\n\n  \/\/ housekeeping for logging\n  std::ostringstream stream_start_controllers, stream_stop_controllers;\n  for (const auto &amp;elem : start_controllers) {\n    stream_start_controllers &lt;&lt; elem &lt;&lt; \", \";\n  }\n  for (const auto &amp;elem : stop_controllers) {\n    stream_stop_controllers &lt;&lt; elem &lt;&lt; \", \";\n  }\n\n  if (switch_controller_client.call(srv_switch_controller)) {\n    ROS_INFO_STREAM(\"Success switching controllers from \" &lt;&lt; stream_stop_controllers.str() &lt;&lt; \" to \"\n                                                          &lt;&lt; stream_start_controllers.str());\n  } else {\n    ROS_WARN_STREAM(\"Error switching controllers from \" &lt;&lt; stream_stop_controllers.str() &lt;&lt; \" to \"\n                                                        &lt;&lt; stream_start_controllers.str());\n    return -1;\n  }\n\n  \/\/ 2. publish the desired joint position to the custom controller\n  std_msgs::Float64MultiArray command_msg;\n  command_msg.data = panda_ready_state;\n  joint_position_publisher.publish(command_msg);\n\n  \/\/ sleep for 1 seconds in order to make sure that the controller finishes moving the robot\n  ros::Duration(1.0).sleep();\n\n  \/\/ 3. Restore default controllers\n  std::swap(stop_controllers, start_controllers);\n  srv_switch_controller.request.stop_controllers = stop_controllers;\n  srv_switch_controller.request.start_controllers = start_controllers;\n\n  if (switch_controller_client.call(srv_switch_controller)) {\n    ROS_INFO_STREAM(\"Success switching controllers from \" &lt;&lt; stream_stop_controllers.str() &lt;&lt; \" to \"\n                                                          &lt;&lt; stream_start_controllers.str());\n  } else {\n    ROS_WARN_STREAM(\"Error switching controllers from \" &lt;&lt; stream_stop_controllers.str() &lt;&lt; \" to \"\n                                                        &lt;&lt; stream_start_controllers.str());\n    return -1;\n  }\n\n  ros::spin();\n  return 0;\n}<\/code><\/pre>\n\n\n\n<p>The following steps are carried out successively:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Define the robot&#8217;s ready state in a <em>std::vector<\/em> container.<\/li><li>Wait for two seconds in order to make sure that the simulation is ready.<\/li><li>Create a <em>controller_manager_msgs:SwitchController<\/em> type message and populate it with the vectors for the controllers that need to be stopped and the ones that should be started. <strong>It is important to distinguish between starting a controller and loading it.<\/strong> The controller needs to be loaded by the controller manager in order to be available for starting with this procedure. We will explain how to load it beforehand in the next paragraph within the launch file.<\/li><li>If controller switching was successful we send a message of type <em>std_msgs::Float64MultiArray<\/em> populated with the desired robot configuration to our custom joint position controller via the topic <em>\/joint_position_controller\/command<\/em> <\/li><li>We let the node sleep for a second and thereby make sure that the controller has enough time to execute our command<\/li><li>In a final step we repeat the controller switching procedure but swap the contents of the controllers to be started and stopped with <em>std::swap<\/em><\/li><\/ul>\n\n\n\n<p>We will also extend the launch file simulation.launch for automatic startup of the new node with the simulation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml\">&lt;!-- load (not start!) custom joint position controller --&gt;\n&lt;node pkg=\"controller_manager\" type=\"spawner\" name=\"joint_position_launcher\" args=\"--stopped joint_position_controller\" \/&gt;\n\n&lt;!-- run custom node for automatic intialization --&gt;\n&lt;node pkg=\"panda_simulation\" type=\"robot_state_initializer_node\" name=\"robot_state_initializer_node\" \/&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Results<\/h2>\n\n\n\n<p>We can now launch our simulation with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">roslaunch panda_simulation simulation.launch<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img fetchpriority=\"high\" decoding=\"async\" width=\"935\" height=\"617\" src=\"https:\/\/erdalpekel.de\/wp-content\/uploads\/2020\/03\/robot_state_initializer.gif\" alt=\"\" class=\"wp-image-319\"\/><figcaption>Automatic robot state reset demonstration in Gazebo for Panda robot<\/figcaption><\/figure>\n\n\n\n<p>Sometimes the MoveIt! plugin in the RViz window needs to be reset for regular use:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"684\" src=\"https:\/\/erdalpekel.de\/wp-content\/uploads\/2020\/03\/moveit-rvi-reset-1024x684.png\" alt=\"\" class=\"wp-image-322\" srcset=\"https:\/\/erdalpekel.de\/wp-content\/uploads\/2020\/03\/moveit-rvi-reset-1024x684.png 1024w, https:\/\/erdalpekel.de\/wp-content\/uploads\/2020\/03\/moveit-rvi-reset-300x200.png 300w, https:\/\/erdalpekel.de\/wp-content\/uploads\/2020\/03\/moveit-rvi-reset-768x513.png 768w, https:\/\/erdalpekel.de\/wp-content\/uploads\/2020\/03\/moveit-rvi-reset.png 1503w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption>Resetting the MoveIt! plugin for for motion planning after controller switching<\/figcaption><\/figure>\n\n\n\n<p> Regular motion planning tasks can now be carried out.<\/p>\n\n\n\n<p>The custom <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/erdalpekel\/panda_simulation\" target=\"_blank\">ROS package<\/a> as well as the modified <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/erdalpekel\/franka_ros\" target=\"_blank\">franka_ros<\/a> repository that is needed for the simulation are available in my <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/erdalpekel\" target=\"_blank\">GitHub profile<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post we implement a ROS node that automatically initializes the robot state in Gazebo to a valid configuration . This is achieved by means of a custom joint position controller and the controller switching mechanism provided by the controller manager. <\/p>\n","protected":false},"author":1,"featured_media":326,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,5,3,8,7],"tags":[],"class_list":["post-314","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gazebo","category-robot-operating-system-ros","category-robotics","category-ros","category-simulation"],"_links":{"self":[{"href":"https:\/\/erdalpekel.de\/index.php?rest_route=\/wp\/v2\/posts\/314","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/erdalpekel.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/erdalpekel.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/erdalpekel.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/erdalpekel.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=314"}],"version-history":[{"count":12,"href":"https:\/\/erdalpekel.de\/index.php?rest_route=\/wp\/v2\/posts\/314\/revisions"}],"predecessor-version":[{"id":390,"href":"https:\/\/erdalpekel.de\/index.php?rest_route=\/wp\/v2\/posts\/314\/revisions\/390"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/erdalpekel.de\/index.php?rest_route=\/wp\/v2\/media\/326"}],"wp:attachment":[{"href":"https:\/\/erdalpekel.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=314"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/erdalpekel.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=314"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/erdalpekel.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=314"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}