From 16949db9f19376e5d92df0cc97426083b9ebe86f Mon Sep 17 00:00:00 2001 From: Bjar Ne Date: Wed, 4 Aug 2021 22:01:29 +0000 Subject: [PATCH 1/5] Initial tutorial for warehouse_ros --- .../move_group.launch.py | 230 ++++++++++++++++++ .../persistent_scenes_and_states.rst | 59 +++++ .../rviz_connect.png | Bin 0 -> 29943 bytes .../quickstart_in_rviz_tutorial.rst | 2 + index.rst | 1 + 5 files changed, 292 insertions(+) create mode 100644 doc/persistent_scenes_and_states/move_group.launch.py create mode 100644 doc/persistent_scenes_and_states/persistent_scenes_and_states.rst create mode 100644 doc/persistent_scenes_and_states/rviz_connect.png diff --git a/doc/persistent_scenes_and_states/move_group.launch.py b/doc/persistent_scenes_and_states/move_group.launch.py new file mode 100644 index 0000000000..61959ef0b1 --- /dev/null +++ b/doc/persistent_scenes_and_states/move_group.launch.py @@ -0,0 +1,230 @@ +import os +import yaml +from launch import LaunchDescription +from launch_ros.actions import Node +from launch.actions import ExecuteProcess +from ament_index_python.packages import get_package_share_directory +import xacro + + +def load_file(package_name, file_path): + package_path = get_package_share_directory(package_name) + absolute_file_path = os.path.join(package_path, file_path) + + try: + with open(absolute_file_path, "r") as file: + return file.read() + except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available + return None + + +def load_yaml(package_name, file_path): + package_path = get_package_share_directory(package_name) + absolute_file_path = os.path.join(package_path, file_path) + + try: + with open(absolute_file_path, "r") as file: + return yaml.safe_load(file) + except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available + return None + + +def generate_launch_description(): + + # planning_context + robot_description_config = xacro.process_file( + os.path.join( + get_package_share_directory("moveit_resources_panda_moveit_config"), + "config", + "panda.urdf.xacro", + ) + ) + robot_description = {"robot_description": robot_description_config.toxml()} + + robot_description_semantic_config = load_file( + "moveit_resources_panda_moveit_config", "config/panda.srdf" + ) + robot_description_semantic = { + "robot_description_semantic": robot_description_semantic_config + } + + kinematics_yaml = load_yaml( + "moveit_resources_panda_moveit_config", "config/kinematics.yaml" + ) + robot_description_kinematics = {"robot_description_kinematics": kinematics_yaml} + + # Planning Functionality + ompl_planning_pipeline_config = { + "move_group": { + "planning_plugin": "ompl_interface/OMPLPlanner", + "request_adapters": """default_planner_request_adapters/AddTimeOptimalParameterization default_planner_request_adapters/FixWorkspaceBounds default_planner_request_adapters/FixStartStateBounds default_planner_request_adapters/FixStartStateCollision default_planner_request_adapters/FixStartStatePathConstraints""", + "start_state_max_bounds_error": 0.1, + } + } + ompl_planning_yaml = load_yaml( + "moveit_resources_panda_moveit_config", "config/ompl_planning.yaml" + ) + ompl_planning_pipeline_config["move_group"].update(ompl_planning_yaml) + + # Trajectory Execution Functionality + moveit_simple_controllers_yaml = load_yaml( + "moveit_resources_panda_moveit_config", "config/panda_controllers.yaml" + ) + moveit_controllers = { + "moveit_simple_controller_manager": moveit_simple_controllers_yaml, + "moveit_controller_manager": "moveit_simple_controller_manager/MoveItSimpleControllerManager", + } + + trajectory_execution = { + "moveit_manage_controllers": True, + "trajectory_execution.allowed_execution_duration_scaling": 1.2, + "trajectory_execution.allowed_goal_duration_margin": 0.5, + "trajectory_execution.allowed_start_tolerance": 0.01, + } + + planning_scene_monitor_parameters = { + "publish_planning_scene": True, + "publish_geometry_updates": True, + "publish_state_updates": True, + "publish_transforms_updates": True, + } + + ## BEGIN_SUB_TUTORIAL add_config + ## * Add a dictionary with the warehouse_ros options + warehouse_ros_config = { + # For warehouse_ros_sqlite + "warehouse_plugin": "warehouse_ros_sqlite::DatabaseConnection", + "warehouse_host": "/path/to/my/warehouse_db.sqlite", + # For warehouse_ros_mongodb + "warehouse_port": 33829, + "warehouse_host": "localhost", + "warehouse_plugin": "warehouse_ros_mongo::MongoDatabaseConnection", + } + ## END_SUB_TUTORIAL + + # Start the actual move_group node/action server + ## BEGIN_SUB_TUTORIAL set_config_move_group + ## * Add it to the Move Group config + run_move_group_node = Node( + package="moveit_ros_move_group", + executable="move_group", + output="screen", + parameters=[ + robot_description, + robot_description_semantic, + kinematics_yaml, + ompl_planning_pipeline_config, + trajectory_execution, + moveit_controllers, + planning_scene_monitor_parameters, + # here + warehouse_ros_config, + ], + ) + ## END_SUB_TUTORIAL + + # RViz + rviz_config_file = ( + get_package_share_directory("moveit2_tutorials") + "/launch/move_group.rviz" + ) + + ## BEGIN_SUB_TUTORIAL set_config_rviz + ## * and to the RViz config + rviz_node = Node( + package="rviz2", + executable="rviz2", + name="rviz2", + output="log", + arguments=["-d", rviz_config_file], + parameters=[ + robot_description, + robot_description_semantic, + ompl_planning_pipeline_config, + kinematics_yaml, + # here + warehouse_ros_config, + ], + ) + ## END_SUB_TUTORIAL + + # Static TF + static_tf = Node( + package="tf2_ros", + executable="static_transform_publisher", + name="static_transform_publisher", + output="log", + arguments=["0.0", "0.0", "0.0", "0.0", "0.0", "0.0", "world", "panda_link0"], + ) + + # Publish TF + robot_state_publisher = Node( + package="robot_state_publisher", + executable="robot_state_publisher", + name="robot_state_publisher", + output="both", + parameters=[robot_description], + ) + + # ros2_control using FakeSystem as hardware + ros2_controllers_path = os.path.join( + get_package_share_directory("moveit_resources_panda_moveit_config"), + "config", + "panda_ros_controllers.yaml", + ) + ros2_control_node = Node( + package="controller_manager", + executable="ros2_control_node", + parameters=[robot_description, ros2_controllers_path], + output={ + "stdout": "screen", + "stderr": "screen", + }, + ) + + # Load controllers + load_controllers = [] + for controller in [ + "panda_arm_controller", + "panda_hand_controller", + "joint_state_controller", + ]: + load_controllers += [ + ExecuteProcess( + cmd=["ros2 run controller_manager spawner.py {}".format(controller)], + shell=True, + output="screen", + ) + ] + + # Warehouse mongodb server + ## BEGIN_SUB_TUTORIAL start_mongodb_server + ## * start the MongoDB server (if necessary) + mongodb_server_node = Node( + package="warehouse_ros_mongo", + executable="mongo_wrapper_ros.py", + parameters=[ + warehouse_ros_config, + ], + output="screen", + ) + ## END_SUB_TUTORIAL + + return LaunchDescription( + [ + rviz_node, + static_tf, + robot_state_publisher, + run_move_group_node, + ros2_control_node, + mongodb_server_node, + ] + + load_controllers + ) + + +## BEGIN_TUTORIAL +## CALL_SUB_TUTORIAL add_config +## CALL_SUB_TUTORIAL set_config_move_group +## CALL_SUB_TUTORIAL set_config_rviz +## CALL_SUB_TUTORIAL start_mongodb_server +## END_TUTORIAL diff --git a/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst b/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst new file mode 100644 index 0000000000..657d288f49 --- /dev/null +++ b/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst @@ -0,0 +1,59 @@ +Persistent Planning Scenes and Robot States +=========================================== + +The "MotionPlanning" plugin of RViz offers the possibility to save +complete planning scenes and robot states persistently. +Currently, two storage plugins (based on +`warehouse_ros `_) are available: + +* `warehouse_ros_mongo `_, which uses MongoDB as backend (default) +* `warehouse_ros_sqlite `_, which uses SQLite as backend + +You can install both of them with your favourite package manager +(e.g. ``apt-get install ros-foxy-warehouse-ros-mongo``) or +`build them from source <../getting_started/getting_started.html>`_ +(of course, you'll have to check out the corresponding repository into your ``src`` folder for that). + +Storage plugin selection +------------------------ + +The storage plugin is determined by the parameter ``warehouse_plugin``. +Valid options are ``warehouse_ros_mongo::MongoDatabaseConnection`` for MongoDB and +``warehouse_ros_sqlite::DatabaseConnection`` for SQLite. +Furthermore, the parameters ``warehouse_host`` and ``warehouse_port`` configure the connection details. +In case of the SQLite plugin, ``warehouse_host`` contains the path of the database file. +They have to be set in the launch file (e. g. ``move_group.launch.py``) as followed: + +.. tutorial-formatter:: ./move_group.launch.py + +You can find the launch file :codedir:`here in the tutorials GitHub repository ` +or (unmodified) in your workspace at ``install/moveit2_tutorials/share/moveit2_tutorials/launch/demo.launch.py``. + +Connecting to the storage backend +--------------------------------- + +After choosing the storage plugin and configuring the launch.py file, +run RViz using :: + + ros2 launch moveit2_tutorials demo.launch.py + +In RViz, navigate to the "Context" tab of the "MotionPlanning" window. +Verify the connetion details (host/port for MongoDB, file path for SQLite) +and click on "Connect". + +.. image:: rviz_connect.png + :width: 600px + +After that, a dialogue box will appear and ask you whether you'd like to erase all current +states and scenes in RViz (not in the database, the persistent data is not affected by that). + +Saving/Loading scenes and states +-------------------------------- + +Now that you connected successfully, +you can save and restore robot states and planned scenes. +This can be done in the "Stored Scenes" resp. "Stored States" tab in RViz. + +To save a start state, drag the green manipulator to the correct position and click "Save Start". +The goal state (orange manipulator) can be saved with the "Save Goal" button. +To restore a state, select it in the list and click on "Set as Start" resp. "Set as Goal". diff --git a/doc/persistent_scenes_and_states/rviz_connect.png b/doc/persistent_scenes_and_states/rviz_connect.png new file mode 100644 index 0000000000000000000000000000000000000000..ba1be11fc5deafca4b5b2674733528618a53c924 GIT binary patch literal 29943 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 RPToW`#o^n@C#7B+{|f-nPOks} literal 0 HcmV?d00001 diff --git a/doc/quickstart_in_rviz/quickstart_in_rviz_tutorial.rst b/doc/quickstart_in_rviz/quickstart_in_rviz_tutorial.rst index a0910b3fbc..cb8c28789b 100644 --- a/doc/quickstart_in_rviz/quickstart_in_rviz_tutorial.rst +++ b/doc/quickstart_in_rviz/quickstart_in_rviz_tutorial.rst @@ -226,3 +226,5 @@ Next Tutorials * To easily control your robot using Python, check out the `Move Group Python Interface <../move_group_python_interface/move_group_python_interface_tutorial.html>`_ * To create your own ``*_moveit_config`` package, check out the `Setup Assistant <../setup_assistant/setup_assistant_tutorial.html>`_ + +* Save and restore robot states from a database or from disk using `warehouse_ros <../persistent_scenes_and_states/persistent_scenes_and_states.html>`_ diff --git a/index.rst b/index.rst index 238387d2a5..adc43b67fb 100644 --- a/index.rst +++ b/index.rst @@ -83,6 +83,7 @@ Configuration doc/trajopt_planner/trajopt_planner_tutorial doc/pilz_industrial_motion_planner/pilz_industrial_motion_planner doc/planning_adapters/planning_adapters_tutorial.rst + doc/persistent_scenes_and_states/persistent_scenes_and_states Miscellaneous ---------------------------- From d7b2c2a35781ab6dbc5f0d25f2e193c1fbe4f3f7 Mon Sep 17 00:00:00 2001 From: Bjar Ne <43565432+gleichdick@users.noreply.github.com> Date: Thu, 5 Aug 2021 16:16:55 +0200 Subject: [PATCH 2/5] Fix warehouse_ros_sqlite URL Undo this change after warehouse_ros_sqlite is moved to ros-planning --- .../persistent_scenes_and_states.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst b/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst index 657d288f49..73703a733b 100644 --- a/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst +++ b/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst @@ -7,7 +7,7 @@ Currently, two storage plugins (based on `warehouse_ros `_) are available: * `warehouse_ros_mongo `_, which uses MongoDB as backend (default) -* `warehouse_ros_sqlite `_, which uses SQLite as backend +* `warehouse_ros_sqlite `_, which uses SQLite as backend You can install both of them with your favourite package manager (e.g. ``apt-get install ros-foxy-warehouse-ros-mongo``) or From 25e8df7dbf733feb33546d00c7c6fe43a37b4279 Mon Sep 17 00:00:00 2001 From: Bjar Ne <43565432+gleichdick@users.noreply.github.com> Date: Tue, 10 Aug 2021 13:51:42 +0200 Subject: [PATCH 3/5] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Görner --- doc/persistent_scenes_and_states/move_group.launch.py | 8 ++++---- .../persistent_scenes_and_states.rst | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/persistent_scenes_and_states/move_group.launch.py b/doc/persistent_scenes_and_states/move_group.launch.py index 61959ef0b1..5f755dfbb1 100644 --- a/doc/persistent_scenes_and_states/move_group.launch.py +++ b/doc/persistent_scenes_and_states/move_group.launch.py @@ -95,10 +95,10 @@ def generate_launch_description(): # For warehouse_ros_sqlite "warehouse_plugin": "warehouse_ros_sqlite::DatabaseConnection", "warehouse_host": "/path/to/my/warehouse_db.sqlite", - # For warehouse_ros_mongodb - "warehouse_port": 33829, - "warehouse_host": "localhost", - "warehouse_plugin": "warehouse_ros_mongo::MongoDatabaseConnection", + # For warehouse_ros_mongodb use the following instead + # "warehouse_port": 33829, + # "warehouse_host": "localhost", + # "warehouse_plugin": "warehouse_ros_mongo::MongoDatabaseConnection", } ## END_SUB_TUTORIAL diff --git a/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst b/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst index 73703a733b..a140f13b23 100644 --- a/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst +++ b/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst @@ -38,7 +38,7 @@ run RViz using :: ros2 launch moveit2_tutorials demo.launch.py In RViz, navigate to the "Context" tab of the "MotionPlanning" window. -Verify the connetion details (host/port for MongoDB, file path for SQLite) +Verify the connection details (host/port for MongoDB, file path for SQLite) and click on "Connect". .. image:: rviz_connect.png @@ -46,6 +46,7 @@ and click on "Connect". After that, a dialogue box will appear and ask you whether you'd like to erase all current states and scenes in RViz (not in the database, the persistent data is not affected by that). +As you just started RViz, you can safely select "yes". Saving/Loading scenes and states -------------------------------- From 8d82990eae3674ae695aeb81a519b7a04e80400a Mon Sep 17 00:00:00 2001 From: Bjar Ne <43565432+gleichdick@users.noreply.github.com> Date: Tue, 10 Aug 2021 14:06:27 +0200 Subject: [PATCH 4/5] Update persistent_scenes_and_states.rst --- .../persistent_scenes_and_states.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst b/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst index a140f13b23..fba6d7c4eb 100644 --- a/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst +++ b/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst @@ -21,7 +21,8 @@ The storage plugin is determined by the parameter ``warehouse_plugin``. Valid options are ``warehouse_ros_mongo::MongoDatabaseConnection`` for MongoDB and ``warehouse_ros_sqlite::DatabaseConnection`` for SQLite. Furthermore, the parameters ``warehouse_host`` and ``warehouse_port`` configure the connection details. -In case of the SQLite plugin, ``warehouse_host`` contains the path of the database file. +In case of the SQLite plugin, ``warehouse_host`` contains the path of the database file, +and ``warehouse_port`` is unused. They have to be set in the launch file (e. g. ``move_group.launch.py``) as followed: .. tutorial-formatter:: ./move_group.launch.py From cfce8a30f11ef573a72a18ec469c0630498e3fd9 Mon Sep 17 00:00:00 2001 From: Bjar Ne Date: Thu, 7 Oct 2021 23:20:47 +0000 Subject: [PATCH 5/5] Add suggestions from @v4hn and @henningkayser --- .../move_group.launch.py | 20 +++++++++---------- .../persistent_scenes_and_states.rst | 15 +++++++------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/doc/persistent_scenes_and_states/move_group.launch.py b/doc/persistent_scenes_and_states/move_group.launch.py index 5f755dfbb1..15b3519a28 100644 --- a/doc/persistent_scenes_and_states/move_group.launch.py +++ b/doc/persistent_scenes_and_states/move_group.launch.py @@ -186,7 +186,7 @@ def generate_launch_description(): for controller in [ "panda_arm_controller", "panda_hand_controller", - "joint_state_controller", + "joint_state_broadcaster", ]: load_controllers += [ ExecuteProcess( @@ -198,15 +198,15 @@ def generate_launch_description(): # Warehouse mongodb server ## BEGIN_SUB_TUTORIAL start_mongodb_server - ## * start the MongoDB server (if necessary) - mongodb_server_node = Node( - package="warehouse_ros_mongo", - executable="mongo_wrapper_ros.py", - parameters=[ - warehouse_ros_config, - ], - output="screen", - ) + ## * Optionally, start the MongoDB server (uncomment if necessary) + # mongodb_server_node = Node( + # package="warehouse_ros_mongo", + # executable="mongo_wrapper_ros.py", + # parameters=[ + # warehouse_ros_config, + # ], + # output="screen", + # ) ## END_SUB_TUTORIAL return LaunchDescription( diff --git a/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst b/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst index fba6d7c4eb..b1ec84e07f 100644 --- a/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst +++ b/doc/persistent_scenes_and_states/persistent_scenes_and_states.rst @@ -1,4 +1,4 @@ -Persistent Planning Scenes and Robot States +Warehouse - Persistent Scenes and States =========================================== The "MotionPlanning" plugin of RViz offers the possibility to save @@ -6,8 +6,8 @@ complete planning scenes and robot states persistently. Currently, two storage plugins (based on `warehouse_ros `_) are available: -* `warehouse_ros_mongo `_, which uses MongoDB as backend (default) -* `warehouse_ros_sqlite `_, which uses SQLite as backend +* `warehouse_ros_mongo `_, which uses MongoDB as backend +* `warehouse_ros_sqlite `_, which uses SQLite as backend You can install both of them with your favourite package manager (e.g. ``apt-get install ros-foxy-warehouse-ros-mongo``) or @@ -17,19 +17,18 @@ You can install both of them with your favourite package manager Storage plugin selection ------------------------ +The warehouse plugin and settings have to be specified in the launch files of your MoveIt configuration. +You should adapt ``move_group.launch.py`` if you do not wish to use the MongoDB plugin. The storage plugin is determined by the parameter ``warehouse_plugin``. Valid options are ``warehouse_ros_mongo::MongoDatabaseConnection`` for MongoDB and ``warehouse_ros_sqlite::DatabaseConnection`` for SQLite. Furthermore, the parameters ``warehouse_host`` and ``warehouse_port`` configure the connection details. -In case of the SQLite plugin, ``warehouse_host`` contains the path of the database file, +In case of the SQLite plugin, ``warehouse_host`` contains the path to the database file, and ``warehouse_port`` is unused. -They have to be set in the launch file (e. g. ``move_group.launch.py``) as followed: +If the database file does not exist yet, an empty database will be created. .. tutorial-formatter:: ./move_group.launch.py -You can find the launch file :codedir:`here in the tutorials GitHub repository ` -or (unmodified) in your workspace at ``install/moveit2_tutorials/share/moveit2_tutorials/launch/demo.launch.py``. - Connecting to the storage backend ---------------------------------