From 347e5fe9881ab3fd26f311756ed81fc46b4a578c Mon Sep 17 00:00:00 2001 From: Jan Hanca Date: Thu, 11 Sep 2025 17:02:41 +0200 Subject: [PATCH 1/3] Fix README.md Signed-off-by: Jan Hanca --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index eb42f7e..1c1e6f6 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ cd $PROJECT_PATH git submodule update --init --recursive ``` -3. Register gems: +4. Register gems: ```shell cd $PROJECT_PATH /opt/O3DE/25.05.1/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/ROS2/ @@ -33,7 +33,8 @@ cd $PROJECT_PATH /opt/O3DE/25.05.1/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/LevelGeoreferencing/ /opt/O3DE/25.05.1/scripts/o3de.sh register -gp ./Gems/o3de-ur-robots-gem/ ``` -4. Build and source the `ros2_ws` workspace: + +5. Build and source the `ros2_ws` workspace: ```bash cd $PROJECT_PATH cd ros2_ws @@ -41,7 +42,7 @@ colcon build source install/setup.bash ``` -5. Build the project: +6. Build the project: ```shell cd $PROJECT_PATH cmake -B build/linux -G "Ninja Multi-Config" -DLY_UNITY_BUILD=ON -DLY_STRIP_DEBUG_SYMBOLS=TRUE -DLY_DISABLE_TEST_MODULES=ON @@ -77,6 +78,7 @@ cd $PROJECT_PATH $O3DE_PATH/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/ROS2/ $O3DE_PATH/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/ROS2SampleRobots/ $O3DE_PATH/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/LevelGeoreferencing/ +$O3DE_PATH/scripts/o3de.sh register -gp ./Gems/o3de-ur-robots-gem/ ``` 5. Build and source the `ros2_ws` workspace: @@ -87,9 +89,10 @@ colcon build source install/setup.bash ``` -6. Build the project: +6. Build the project (you need to adjust the engine parameter in `project.json` file to switch from O3DE SDK to your O3DE source code): ```shell cd $PROJECT_PATH +sed -i 's/"engine": "o3de-sdk"/"engine": "o3de"/' project.json cmake -B build/linux -G "Ninja Multi-Config" -DLY_UNITY_BUILD=ON -DLY_STRIP_DEBUG_SYMBOLS=TRUE -DLY_DISABLE_TEST_MODULES=ON cmake --build build/linux --config profile ``` @@ -115,7 +118,7 @@ ros2 launch ur_moveit_config ur_moveit.launch.py ur_type:=ur10 launch_rviz:=true ``` ## Changes in files -Two repositories are copied to `ros2_ws` folder and modified to add a gripper to the UR robot: +Two Universal Robots repositories are copied to `ros2_ws` folder and modified to add a gripper to the UR robot: - Universal_Robots_ROS2_Description - from https://github.com/RobotecAI/ROSCon2023Demo/tree/8569a6753d05bd785ebdd6d93d053142d7646b99/ros2_ws/src/Universal_Robots_ROS2_Description - To it the meshes for the gripper were added from (location - ros2_ws/src/Universal_Robots_ROS2_Description/urdf/finger_gripper) - The `ros2_ws/src/Universal_Robots_ROS2_Description/urdf/ur_macro.xacro` file was modified to include the gripper @@ -124,6 +127,7 @@ Two repositories are copied to `ros2_ws` folder and modified to add a gripper to - To file `ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur_control.launch.py` the `panda_hand_controller` was added. - To file `ros2_ws/src/Universal_Robots_ROS2_Driver/ur_moveit_config/srdf/ur_macro.srdf.xacro` the `panda_hand` group was added with appropriate collision disables. +One more repository was added to `ros2_ws` folder and modified to work with ROS2 Humble: - Dingo - from https://github.com/dingo-cpr/dingo - All packages except `dingo_description` were removed. - The `dingo_description` package was modified to remove all references to gazebo and accessories. The files modified are: From 5bde9735272838f8b3681663761f487e4c9aba31 Mon Sep 17 00:00:00 2001 From: Jan Hanca Date: Thu, 11 Sep 2025 17:06:23 +0200 Subject: [PATCH 2/3] Remove registration of Gems; use project.json Signed-off-by: Jan Hanca --- Project/project.json | 4 ++++ README.md | 26 ++++---------------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/Project/project.json b/Project/project.json index 78bf162..c118dc1 100644 --- a/Project/project.json +++ b/Project/project.json @@ -15,6 +15,10 @@ "icon_path": "preview.png", "engine": "o3de-sdk", "external_subdirectories": [ + "../Gems/o3de-ur-robots-gem", + "../Gems/o3de-extras/Gems/LevelGeoreferencing", + "../Gems/o3de-extras/Gems/ROS2", + "../Gems/o3de-extras/Gems/ROS2SampleRobots", "Gem" ], "restricted": "SimulationInterfacesDemo", diff --git a/README.md b/README.md index 1c1e6f6..f63d749 100644 --- a/README.md +++ b/README.md @@ -25,16 +25,7 @@ cd $PROJECT_PATH git submodule update --init --recursive ``` -4. Register gems: -```shell -cd $PROJECT_PATH -/opt/O3DE/25.05.1/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/ROS2/ -/opt/O3DE/25.05.1/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/ROS2SampleRobots/ -/opt/O3DE/25.05.1/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/LevelGeoreferencing/ -/opt/O3DE/25.05.1/scripts/o3de.sh register -gp ./Gems/o3de-ur-robots-gem/ -``` - -5. Build and source the `ros2_ws` workspace: +4. Build and source the `ros2_ws` workspace: ```bash cd $PROJECT_PATH cd ros2_ws @@ -42,7 +33,7 @@ colcon build source install/setup.bash ``` -6. Build the project: +5. Build the project: ```shell cd $PROJECT_PATH cmake -B build/linux -G "Ninja Multi-Config" -DLY_UNITY_BUILD=ON -DLY_STRIP_DEBUG_SYMBOLS=TRUE -DLY_DISABLE_TEST_MODULES=ON @@ -72,16 +63,7 @@ cd $PROJECT_PATH git submodule update --init --recursive ``` -4. Register gems: -```shell -cd $PROJECT_PATH -$O3DE_PATH/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/ROS2/ -$O3DE_PATH/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/ROS2SampleRobots/ -$O3DE_PATH/scripts/o3de.sh register -gp ./Gems/o3de-extras/Gems/LevelGeoreferencing/ -$O3DE_PATH/scripts/o3de.sh register -gp ./Gems/o3de-ur-robots-gem/ -``` - -5. Build and source the `ros2_ws` workspace: +4. Build and source the `ros2_ws` workspace: ```bash cd $PROJECT_PATH cd ros2_ws @@ -89,7 +71,7 @@ colcon build source install/setup.bash ``` -6. Build the project (you need to adjust the engine parameter in `project.json` file to switch from O3DE SDK to your O3DE source code): +5. Build the project (you need to adjust the engine parameter in `project.json` file to switch from O3DE SDK to your O3DE source code): ```shell cd $PROJECT_PATH sed -i 's/"engine": "o3de-sdk"/"engine": "o3de"/' project.json From 64c0784c3c15d1e68f325566677d2eb3765b4886 Mon Sep 17 00:00:00 2001 From: Jan Hanca Date: Fri, 12 Sep 2025 12:21:45 +0200 Subject: [PATCH 3/3] Remove unused UR ROS2 Driver; update readme Signed-off-by: Jan Hanca --- README.md | 56 +- .../ur/CHANGELOG.rst | 80 - .../ur/CMakeLists.txt | 14 - .../ur/package.xml | 28 - .../ur_bringup/CHANGELOG.rst | 142 -- .../ur_bringup/CMakeLists.txt | 14 - .../config/test_goal_publishers_config.yaml | 57 - .../test_velocity_goal_publishers_config.yaml | 10 - .../ur_bringup/config/ur10_update_rate.yaml | 3 - .../ur_bringup/config/ur10e_update_rate.yaml | 3 - .../ur_bringup/config/ur16e_update_rate.yaml | 3 - .../ur_bringup/config/ur3_update_rate.yaml | 3 - .../ur_bringup/config/ur3e_update_rate.yaml | 3 - .../ur_bringup/config/ur5_update_rate.yaml | 3 - .../ur_bringup/config/ur5e_update_rate.yaml | 3 - .../ur_bringup/config/ur_controllers.yaml | 160 -- ...test_forward_velocity_controller.launch.py | 63 - ...test_joint_trajectory_controller.launch.py | 63 - ...aled_joint_trajectory_controller.launch.py | 63 - .../ur_bringup/launch/ur10.launch.py | 102 -- .../ur_bringup/launch/ur10e.launch.py | 102 -- .../ur_bringup/launch/ur16e.launch.py | 102 -- .../ur_bringup/launch/ur3.launch.py | 102 -- .../ur_bringup/launch/ur3e.launch.py | 102 -- .../ur_bringup/launch/ur5.launch.py | 102 -- .../ur_bringup/launch/ur5e.launch.py | 102 -- .../ur_bringup/launch/ur_control.launch.py | 92 -- .../launch/ur_dashboard_client.launch.py | 66 - .../ur_bringup/package.xml | 46 - .../ur_bringup/scripts/start_ursim.sh | 43 - .../ur_calibration/CHANGELOG.rst | 78 - .../ur_calibration/CMakeLists.txt | 100 -- .../ur_calibration/README.md | 78 - .../ur_calibration/doc/algorithm.rst | 97 -- .../doc/calibration_example.png | 3 - .../doc/calibration_example_corrected.png | 3 - .../doc/calibration_uncorrected.png | 3 - .../ur_calibration/doc/conf.py | 85 - .../ur_calibration/doc/index.rst | 17 - .../ur_calibration/doc/usage.rst | 28 - .../include/ur_calibration/calibration.hpp | 218 --- .../ur_calibration/calibration_consumer.hpp | 76 - .../launch/calibration_correction.launch.py | 70 - .../ur_calibration/package.xml | 32 - .../ur_calibration/src/calibration.cpp | 239 --- .../src/calibration_consumer.cpp | 81 - .../src/calibration_correction.cpp | 177 --- .../ur_calibration/test/calibration_test.cpp | 248 --- .../ur_controllers/CHANGELOG.rst | 168 -- .../ur_controllers/CMakeLists.txt | 175 --- .../ur_controllers/README.md | 76 - .../ur_controllers/controller_plugins.xml | 37 - .../ur_controllers/doc/index.rst | 380 ----- .../doc/traj_with_speed_scaling.png | 3 - .../doc/traj_without_speed_scaling.png | 3 - .../ur_controllers/force_mode_controller.hpp | 147 -- .../freedrive_mode_controller.hpp | 130 -- .../ur_controllers/gpio_controller.hpp | 202 --- .../passthrough_trajectory_controller.hpp | 184 --- .../scaled_joint_trajectory_controller.hpp | 89 -- .../speed_scaling_state_broadcaster.hpp | 86 -- .../ur_configuration_controller.hpp | 105 -- .../ur_controllers/package.xml | 52 - .../src/force_mode_controller.cpp | 380 ----- .../src/force_mode_controller_parameters.yaml | 12 - .../src/freedrive_mode_controller.cpp | 337 ---- .../freedrive_mode_controller_parameters.yaml | 17 - .../ur_controllers/src/gpio_controller.cpp | 594 ------- .../src/gpio_controller_parameters.yaml | 11 - .../src/passthrough_trajectory_controller.cpp | 624 -------- ...ough_trajectory_controller_parameters.yaml | 31 - .../scaled_joint_trajectory_controller.cpp | 364 ----- ...oint_trajectory_controller_parameters.yaml | 6 - .../src/speed_scaling_state_broadcaster.cpp | 152 -- ..._scaling_state_broadcaster_parameters.yaml | 11 - .../src/ur_configuration_controller.cpp | 128 -- ...r_configuration_controller_parameters.yaml | 6 - .../test/force_mode_controller_params.yaml | 7 - .../freedrive_mode_controller_params.yaml | 5 - .../test/test_load_force_mode_controller.cpp | 61 - .../test_load_freedrive_mode_controller.cpp | 61 - .../ur_dashboard_msgs/CHANGELOG.rst | 74 - .../ur_dashboard_msgs/CMakeLists.txt | 43 - .../ur_dashboard_msgs/action/SetMode.action | 25 - .../ur_dashboard_msgs/msg/ProgramState.msg | 5 - .../ur_dashboard_msgs/msg/RobotMode.msg | 12 - .../ur_dashboard_msgs/msg/SafetyMode.msg | 15 - .../ur_dashboard_msgs/package.xml | 34 - .../ur_dashboard_msgs/srv/AddToLog.srv | 4 - .../srv/GetLoadedProgram.srv | 4 - .../ur_dashboard_msgs/srv/GetProgramState.srv | 5 - .../ur_dashboard_msgs/srv/GetRobotMode.srv | 4 - .../ur_dashboard_msgs/srv/GetSafetyMode.srv | 4 - .../srv/IsProgramRunning.srv | 4 - .../ur_dashboard_msgs/srv/IsProgramSaved.srv | 5 - .../ur_dashboard_msgs/srv/Load.srv | 5 - .../ur_dashboard_msgs/srv/Popup.srv | 4 - .../ur_dashboard_msgs/srv/RawRequest.srv | 4 - .../ur_moveit_config/config/controllers.yaml | 3 - .../ur_robot_driver/CHANGELOG.rst | 289 ---- .../ur_robot_driver/CMakeLists.txt | 214 --- .../ur_robot_driver/README.md | 96 -- .../config/test_goal_publishers_config.yaml | 65 - .../test_velocity_goal_publishers_config.yaml | 10 - .../config/ur10_update_rate.yaml | 3 - .../config/ur10e_update_rate.yaml | 3 - .../config/ur16e_update_rate.yaml | 3 - .../config/ur20_update_rate.yaml | 3 - .../config/ur30_update_rate.yaml | 3 - .../config/ur3_update_rate.yaml | 3 - .../config/ur3e_update_rate.yaml | 3 - .../config/ur5_update_rate.yaml | 3 - .../config/ur5e_update_rate.yaml | 3 - .../config/ur_controllers.yaml | 177 --- .../ur_robot_driver/doc/ROS_INTERFACE.md | 213 --- .../doc/architecture_coarse.svg | 302 ---- .../ur_robot_driver/doc/conf.py | 175 --- .../ur_robot_driver/doc/features.rst | 57 - .../ur_robot_driver/doc/index.rst | 25 - .../ur_robot_driver/doc/make.bat | 35 - .../ur_robot_driver/doc/overview.rst | 22 - ...World_2021_Making_a_robot_ROS2_powered.pdf | Bin 132 -> 0 bytes .../ur_robot_driver/doc/resources/README.md | 46 - .../resources/ros2_control_ur_driver.drawio | 1 - .../doc/setup_tool_communication.rst | 76 - .../ur_robot_driver/doc/usage.rst | 320 ---- .../ur_robot_driver/doc/usage/controllers.rst | 127 -- .../ur_robot_driver/examples/examples.py | 180 --- .../ur_robot_driver/examples/force_mode.py | 142 -- .../hardware_interface_plugin.xml | 9 - .../ur_robot_driver/controller_stopper.hpp | 89 -- .../ur_robot_driver/dashboard_client_ros.hpp | 154 -- .../ur_robot_driver/hardware_interface.hpp | 310 ---- .../ur_robot_driver/urcl_log_handler.hpp | 112 -- ...test_forward_velocity_controller.launch.py | 56 - ...test_joint_trajectory_controller.launch.py | 56 - ...aled_joint_trajectory_controller.launch.py | 56 - .../ur_robot_driver/launch/ur10.launch.py | 104 -- .../ur_robot_driver/launch/ur10e.launch.py | 104 -- .../ur_robot_driver/launch/ur16e.launch.py | 104 -- .../ur_robot_driver/launch/ur20.launch.py | 104 -- .../ur_robot_driver/launch/ur3.launch.py | 104 -- .../ur_robot_driver/launch/ur30.launch.py | 104 -- .../ur_robot_driver/launch/ur3e.launch.py | 104 -- .../ur_robot_driver/launch/ur5.launch.py | 104 -- .../ur_robot_driver/launch/ur5e.launch.py | 104 -- .../launch/ur_control.launch.py | 636 -------- .../launch/ur_dashboard_client.launch.py | 58 - .../ur_robot_driver/package.xml | 71 - .../resources/externalcontrol-1.0.5.urcap | Bin 35287 -> 0 bytes .../resources/ros_control.urscript | 143 -- .../ur_robot_driver/resources/rs485-1.0.urcap | Bin 141984 -> 0 bytes .../resources/rtde_input_recipe.txt | 12 - .../resources/rtde_output_recipe.txt | 27 - .../ur_robot_driver/scripts/example_move.py | 219 --- .../ur_robot_driver/scripts/start_ursim.sh | 37 - .../scripts/tool_communication.py | 79 - .../scripts/wait_dashboard_server.sh | 11 - .../src/controller_stopper.cpp | 180 --- .../src/controller_stopper_node.cpp | 66 - .../src/dashboard_client_node.cpp | 61 - .../src/dashboard_client_ros.cpp | 402 ----- .../src/hardware_interface.cpp | 1372 ----------------- .../src/ur_ros2_control_node.cpp | 100 -- .../ur_robot_driver/src/urcl_log_handler.cpp | 101 -- .../src/urscript_interface.cpp | 92 -- .../ur_robot_driver/test/dashboard_client.py | 102 -- .../integration_test_controller_switch.py | 407 ----- .../test/integration_test_force_mode.py | 514 ------ .../ur_robot_driver/test/launch_args.py | 148 -- .../ur_robot_driver/test/robot_driver.py | 439 ------ .../ur_robot_driver/test/test_common.py | 385 ----- .../test/urscript_interface.py | 141 -- .../ur_robot_driver/__init__.py | 0 174 files changed, 22 insertions(+), 18421 deletions(-) delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur/CHANGELOG.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur/CMakeLists.txt delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur/package.xml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/CHANGELOG.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/CMakeLists.txt delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/test_goal_publishers_config.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/test_velocity_goal_publishers_config.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur10_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur10e_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur16e_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur3_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur3e_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur5_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur5e_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur_controllers.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_forward_velocity_controller.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_joint_trajectory_controller.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_scaled_joint_trajectory_controller.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur10.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur10e.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur16e.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur3.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur3e.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur5.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur5e.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur_control.launch.py delete mode 100755 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur_dashboard_client.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/package.xml delete mode 100755 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/scripts/start_ursim.sh delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/CHANGELOG.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/CMakeLists.txt delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/README.md delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/algorithm.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_example.png delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_example_corrected.png delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_uncorrected.png delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/conf.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/index.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/usage.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/include/ur_calibration/calibration.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/include/ur_calibration/calibration_consumer.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/launch/calibration_correction.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/package.xml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration_consumer.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration_correction.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/test/calibration_test.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/CHANGELOG.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/CMakeLists.txt delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/README.md delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/controller_plugins.xml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/index.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/traj_with_speed_scaling.png delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/traj_without_speed_scaling.png delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/force_mode_controller.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/freedrive_mode_controller.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/gpio_controller.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/passthrough_trajectory_controller.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/scaled_joint_trajectory_controller.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/speed_scaling_state_broadcaster.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/ur_configuration_controller.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/package.xml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/force_mode_controller.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/force_mode_controller_parameters.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/freedrive_mode_controller.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/freedrive_mode_controller_parameters.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/gpio_controller.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/gpio_controller_parameters.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/passthrough_trajectory_controller.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/passthrough_trajectory_controller_parameters.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/scaled_joint_trajectory_controller.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/scaled_joint_trajectory_controller_parameters.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/speed_scaling_state_broadcaster.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/speed_scaling_state_broadcaster_parameters.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/ur_configuration_controller.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/ur_configuration_controller_parameters.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/force_mode_controller_params.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/freedrive_mode_controller_params.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/test_load_force_mode_controller.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/test_load_freedrive_mode_controller.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/CHANGELOG.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/CMakeLists.txt delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/action/SetMode.action delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/ProgramState.msg delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/RobotMode.msg delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/SafetyMode.msg delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/package.xml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/AddToLog.srv delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetLoadedProgram.srv delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetProgramState.srv delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetRobotMode.srv delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetSafetyMode.srv delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/IsProgramRunning.srv delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/IsProgramSaved.srv delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/Load.srv delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/Popup.srv delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/RawRequest.srv delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/CHANGELOG.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/CMakeLists.txt delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/README.md delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/test_goal_publishers_config.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/test_velocity_goal_publishers_config.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur10_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur10e_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur16e_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur20_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur30_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur3_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur3e_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur5_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur5e_update_rate.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur_controllers.yaml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/ROS_INTERFACE.md delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/architecture_coarse.svg delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/conf.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/features.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/index.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/make.bat delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/overview.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/resources/2021-10_ROS_World_2021_Making_a_robot_ROS2_powered.pdf delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/resources/README.md delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/resources/ros2_control_ur_driver.drawio delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/setup_tool_communication.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/usage.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/usage/controllers.rst delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/examples/examples.py delete mode 100755 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/examples/force_mode.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/hardware_interface_plugin.xml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/controller_stopper.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/dashboard_client_ros.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/urcl_log_handler.hpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_forward_velocity_controller.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_joint_trajectory_controller.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_scaled_joint_trajectory_controller.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur10.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur10e.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur16e.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur20.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur3.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur30.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur3e.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur5.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur5e.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur_control.launch.py delete mode 100755 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur_dashboard_client.launch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/package.xml delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/externalcontrol-1.0.5.urcap delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/ros_control.urscript delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rs485-1.0.urcap delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rtde_input_recipe.txt delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rtde_output_recipe.txt delete mode 100755 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/example_move.py delete mode 100755 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/start_ursim.sh delete mode 100755 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/tool_communication.py delete mode 100755 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/wait_dashboard_server.sh delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/controller_stopper.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/controller_stopper_node.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/dashboard_client_node.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/dashboard_client_ros.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/hardware_interface.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/ur_ros2_control_node.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/urcl_log_handler.cpp delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/urscript_interface.cpp delete mode 100755 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/dashboard_client.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/integration_test_controller_switch.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/integration_test_force_mode.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/launch_args.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/robot_driver.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/test_common.py delete mode 100755 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/urscript_interface.py delete mode 100644 ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/ur_robot_driver/__init__.py diff --git a/README.md b/README.md index f63d749..3a8f912 100644 --- a/README.md +++ b/README.md @@ -25,15 +25,7 @@ cd $PROJECT_PATH git submodule update --init --recursive ``` -4. Build and source the `ros2_ws` workspace: -```bash -cd $PROJECT_PATH -cd ros2_ws -colcon build -source install/setup.bash -``` - -5. Build the project: +4. Build the project: ```shell cd $PROJECT_PATH cmake -B build/linux -G "Ninja Multi-Config" -DLY_UNITY_BUILD=ON -DLY_STRIP_DEBUG_SYMBOLS=TRUE -DLY_DISABLE_TEST_MODULES=ON @@ -63,15 +55,7 @@ cd $PROJECT_PATH git submodule update --init --recursive ``` -4. Build and source the `ros2_ws` workspace: -```bash -cd $PROJECT_PATH -cd ros2_ws -colcon build -source install/setup.bash -``` - -5. Build the project (you need to adjust the engine parameter in `project.json` file to switch from O3DE SDK to your O3DE source code): +4. Build the project (you need to adjust the engine parameter in `project.json` file to switch from O3DE SDK to your O3DE source code): ```shell cd $PROJECT_PATH sed -i 's/"engine": "o3de-sdk"/"engine": "o3de"/' project.json @@ -94,25 +78,29 @@ or from the O3DE Editor: /opt/O3DE/25.05.1/bin/Linux/profile/Default/Editor --project-path ~/devroot/projects/SimulationInterfacesDemo/Project ``` -The following command launches MoveIt for the robot: +## Run the MoveIt demo: + +Build and source the `ros2_ws` workspace. Run the MoveIt launcher: ```bash +cd $PROJECT_PATH/ros2_ws +colcon build +source install/setup.bash ros2 launch ur_moveit_config ur_moveit.launch.py ur_type:=ur10 launch_rviz:=true use_fake_hardware:=true use_sim_time:=true ``` -## Changes in files -Two Universal Robots repositories are copied to `ros2_ws` folder and modified to add a gripper to the UR robot: -- Universal_Robots_ROS2_Description - from https://github.com/RobotecAI/ROSCon2023Demo/tree/8569a6753d05bd785ebdd6d93d053142d7646b99/ros2_ws/src/Universal_Robots_ROS2_Description - - To it the meshes for the gripper were added from (location - ros2_ws/src/Universal_Robots_ROS2_Description/urdf/finger_gripper) - - The `ros2_ws/src/Universal_Robots_ROS2_Description/urdf/ur_macro.xacro` file was modified to include the gripper -- Universal_Robots_ROS2_Driver - from https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver/tree/f06092e4f32ae1d042459cfaaae96b5c0ea1b21d - - To file `ros2_ws/src/Universal_Robots_ROS2_Driver/ur_moveit_config/config/controllers.yaml` the `panda_hand_controller` was added. - - To file `ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur_control.launch.py` the `panda_hand_controller` was added. - - To file `ros2_ws/src/Universal_Robots_ROS2_Driver/ur_moveit_config/srdf/ur_macro.srdf.xacro` the `panda_hand` group was added with appropriate collision disables. - -One more repository was added to `ros2_ws` folder and modified to work with ROS2 Humble: -- Dingo - from https://github.com/dingo-cpr/dingo +## ROS 2 Workspace + +The workspace is build based on existing resources with some modifications to add a gripper to the UR robot and to make it work with ROS2 Humble. In particular: +1. [Universal_Robots_ROS2_Description](https://github.com/UniversalRobots/Universal_Robots_ROS2_Description) fork used in [ROSCon2023Demo](https://github.com/RobotecAI/ROSCon2023Demo) repository was used as a base (commit `8569a675`). The following modifications were made to add the gripper: + - The gripper [assets](./ros2_ws/src/Universal_Robots_ROS2_Description/urdf/finger_gripper) were added. + - The [urdf/ur_macro.xacro](./ros2_ws/src/Universal_Robots_ROS2_Description/urdf/ur_macro.xacro) file was modified adding the gripper. +2. [Universal_Robots_ROS2_Driver](https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver/tree/f06092e4f32ae1d042459cfaaae96b5c0ea1b21d) repository was used as a base (commit `f06092e4`). The following modifications were made to add the gripper: + - All packages except `ur_moveit_config` were removed. + - The [config/controllers.yaml](./ros2_ws/src/Universal_Robots_ROS2_Driver/ur_moveit_config/config/controllers.yaml) file was modified adding the gripper. + - The [srdf/ur_macro.srdf.xacro](./ros2_ws/src/Universal_Robots_ROS2_Driver/ur_moveit_config/srdf/ur_macro.srdf.xacro) file was modified including appropriate collision disables. +3. [Dingo](https://github.com/dingo-cpr/dingo) repository was used as a base (commit `68806fd`) for the Dingo robot. The following modifications were made to make the robot usable without gazebo in ROS 2 Humble: - All packages except `dingo_description` were removed. - - The `dingo_description` package was modified to remove all references to gazebo and accessories. The files modified are: - - `ros2_ws/src/dingo/dingo_description/urdf/dingo-d.urdf.xacro` - - `ros2_ws/src/dingo/dingo_description/urdf/dingo-o.urdf.xacro` + - The `dingo_description` package was modified to remove all references to gazebo and accessories. The files modified are [urdf/dingo-d.urdf.xacro](./ros2_ws/src/dingo/dingo_description/urdf/dingo-d.urdf.xacro) and [urdf/dingo-o.urdf.xacro](./ros2_ws/src/dingo/dingo_description/urdf/dingo-o.urdf.xacro). - The `dingo_description` package was modified to work with ROS2 Humble + +> **Note:** the `dingo_description` package was used to import the robot into O3DE, and it was stored in the native format after the import was completed. The package is not needed anymore. It is a part of the workspace to allow tracking of changes made to the original files. diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur/CHANGELOG.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur/CHANGELOG.rst deleted file mode 100644 index 9cdb637..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur/CHANGELOG.rst +++ /dev/null @@ -1,80 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ur -^^^^^^^^^^^^^^^^^^^^^^^^ - -2.5.2 (2025-01-21) ------------------- - -2.5.1 (2024-12-21) ------------------- - -2.5.0 (2024-12-18) ------------------- -* Update package maintainers (backport of `#1203 `_) -* Contributors: mergify[bot] - -2.2.16 (2024-10-28) -------------------- - -2.2.15 (2024-07-26) -------------------- - -2.2.14 (2024-07-01) -------------------- - -2.2.13 (2024-06-17) -------------------- - -2.2.12 (2024-05-16) -------------------- - -2.2.11 (2024-04-08) -------------------- - -2.2.10 (2024-01-03) -------------------- - -2.2.9 (2023-09-22) ------------------- - -2.2.8 (2023-06-26) ------------------- - -2.2.7 (2023-06-02) ------------------- - -2.2.6 (2022-11-28) ------------------- - -2.2.5 (2022-11-19) ------------------- - -2.2.4 (2022-10-07) ------------------- - -2.2.3 (2022-07-27) ------------------- - -2.2.2 (2022-07-19) ------------------- - -2.2.1 (2022-06-27) ------------------- - -2.2.0 (2022-06-20) ------------------- -* wip -* Rework bringup (`#403 `_) - * Copy all bringup files to ur_robot_driver - * Update documentation to use launchfiles from ur_robot_driver - * Add deprecation warnings for ur_bringup - * Add ``ur`` metapackage - * Added deprecation warning to toplevel README - * Added meta package to CI checks -* Contributors: Felix Exner - -* Rework bringup (`#403 `_) -* Contributors: Felix Exner - -0.0.3 (2020-10-29) ------------------- diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur/CMakeLists.txt b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur/CMakeLists.txt deleted file mode 100644 index 9ce9127..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.8) -project(ur) - - -find_package(ament_cmake REQUIRED) - -if(BUILD_TESTING) - find_package(ament_lint_auto REQUIRED) - set(ament_cmake_copyright_FOUND TRUE) - set(ament_cmake_cpplint_FOUND TRUE) - ament_lint_auto_find_test_dependencies() -endif() - -ament_package() diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur/package.xml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur/package.xml deleted file mode 100644 index b88fb05..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur/package.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - ur - 2.5.2 - Metapackage for universal robots - - Felix Exner - Rune Søe-Knudsen - Universal Robots A/S - - BSD-3-Clause - - ament_cmake - - ur_calibration - ur_controllers - ur_dashboard_msgs - ur_moveit_config - ur_robot_driver - - ament_lint_auto - ament_lint_common - - - ament_cmake - - diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/CHANGELOG.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/CHANGELOG.rst deleted file mode 100644 index 28b5d35..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/CHANGELOG.rst +++ /dev/null @@ -1,142 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ur_bringup -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -2.5.2 (2025-01-21) ------------------- -* Update pre-commit the same as on the main branch (`#1232 `_) -* Contributors: Felix Exner - -2.5.1 (2024-12-21) ------------------- - -2.5.0 (2024-12-18) ------------------- -* Update package maintainers (backport of `#1203 `_) -* Forward trajectory controller (backport of `#944 `_) -* Contributors: mergify[bot] - -2.2.16 (2024-10-28) -------------------- - -2.2.15 (2024-07-26) -------------------- - -2.2.14 (2024-07-01) -------------------- - -2.2.13 (2024-06-17) -------------------- -* Simplify launch file for ur_bringup pkg (`#1004 `_) -* Add calibration file to launch arguments (`#1001 `_) -* Contributors: Vincenzo Di Pentima - -2.2.12 (2024-05-16) -------------------- - -2.2.11 (2024-04-08) -------------------- - -2.2.10 (2024-01-03) -------------------- - -2.2.9 (2023-09-22) ------------------- - -2.2.8 (2023-06-26) ------------------- - -2.2.7 (2023-06-02) ------------------- -* Default path to ur_client_library urscript (`#316 `_) (`#553 `_) - * Change default path for urscript for headless mode. - * Replace urscript path also in newer ur_robot_driver launchfile -* This commits adds additional configuration parameters needed for multiarm support. -* Contributors: Lennart Nachtigall, mergify[bot], livanov93 - -2.2.6 (2022-11-28) ------------------- - -2.2.5 (2022-11-19) ------------------- - -2.2.4 (2022-10-07) ------------------- -* Remove duplicated update_rate parameter (`#479 `_) -* Contributors: Felix Exner - -2.2.3 (2022-07-27) ------------------- - -2.2.2 (2022-07-19) ------------------- -* Made sure all past maintainers are listed as authors (`#429 `_) -* Contributors: Felix Exner - -2.2.1 (2022-06-27) ------------------- - -2.2.0 (2022-06-20) ------------------- -* Updated package maintainers -* Rework bringup (`#403 `_) -* Fix force_torque_sensor_broadcaster config (`#405 `_) -* Prepare for humble (`#394 `_) -* Update dependencies on all packages (`#391 `_) -* Add sphinx documentation (`#340 `_) -* Use upstream fts_broadcaster (`#304 `_) -* Update license to BSD-3-Clause (`#277 `_) -* Only start controller_stopper when not using fake_hardware (`#332 `_) -* Added controller stopper node (`#309 `_) -* Fix package dependencies (`#306 `_) -* Make documentation on how to use driver clearer. (`#300 `_) -* Update MoveIt file for working with simulation. (`#278 `_) -* Start the tool communication script if the flag is set (`#267 `_) -* Used ``spawner`` instead of ``spanwer.py`` in launch files (`#293 `_) -* Do not start dashboard client if FakeHardware simulation is used. (`#286 `_) -* Use scaled trajectory controller per default. (`#287 `_) -* Separate control node (`#281 `_) -* Fix launch file arguments. (`#243 `_) -* Move Servo launching into the main MoveIt launch file. Make it optional. (`#239 `_) -* Tool communication (`#218 `_) -* fix missing executable arg of joint_state_broadcaster (`#248 `_) -* Remove obsolete comment 🐒 (`#242 `_) -* Revert "Ignition Gazebo simulation for UR robots (`#232 `_)" (`#241 `_) -* Use 'spawner' instead of 'spawner.py' (`#225 `_) -* Ignition Gazebo simulation for UR robots (`#232 `_) -* Empirically set update rates. (`#227 `_) -* Fix update rate configuration (`#222 `_) -* Test execution tests (`#216 `_) -* Integration tests improvement (`#206 `_) -* Add resend program service and enable headless mode (`#198 `_) -* Add default per joint constraints. (`#203 `_) -* Do not customize the planning scene topics (`#205 `_) -* Implement "choices" for robot_type param (`#204 `_) -* Joint limits parameters for Moveit planning (`#187 `_) -* Rename the joint controller that is launched by default (`#185 `_) -* Enabling velocity mode (`#146 `_) -* Add parameters for checking start state (`#143 `_) -* Update for changes to ros2_control and ros2_controllers - See: https://github.com/ros-controls/ros2_control/commit/156a3f6aaed319585a8a1fd445693e2e08c30ccd - and: https://github.com/ros-controls/ros2_controllers/commit/612f610c24d026a41abd2dd026902c672cf778c9#diff-5d3e18800b3a217b37b91036031bdb170f5183970f54d1f951bb12f2e4847706 -* Removed dashboard client from hardware interface -* README cleanup, make MoveIt installation optional (`#86 `_) -* Using modern python -* Restore ur_control.launch.py -* Added view_ur for checking description -* Make an optional launch arg for RViz, document it in README (`#82 `_) -* Review CI by correcting the configurations (`#71 `_) -* Add support for gpios, update MoveIt and ros2_control launching (`#66 `_) -* Delete controller_stopper and ur_bringup pkgs -* Add XML schema to all ``package.xml`` files - Better enable ``ament_xmllint`` to check validity. -* Update packaging for ROS2 -* Update package.xml files so ``ros2 pkg list`` shows all pkgs -* Delete all launch/config files with no UR5 relation -* Update CMakeLists and package.xml for: - - ur5_moveit_config - - ur_bringup - - ur_description -* Change pkg versions to 0.0.0 -* Add ur5_moveit_config, ur_bringup, ur_description pkgs -* Contributors: AndyZe, Denis Stogl, Denis Štogl, Felix Exner, John Morris, Kenneth Bogert, Mads Holm Peters, Marvin Große Besselmann, Thomas Barbier, Vatan Aksoy Tezer, livanov93, relffok, Robert Wilbrandt diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/CMakeLists.txt b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/CMakeLists.txt deleted file mode 100644 index 457158a..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(ur_bringup) - -find_package(ament_cmake REQUIRED) - -install(DIRECTORY config launch - DESTINATION share/${PROJECT_NAME} -) - -install(PROGRAMS scripts/start_ursim.sh - DESTINATION lib/${PROJECT_NAME} -) - -ament_package() diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/test_goal_publishers_config.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/test_goal_publishers_config.yaml deleted file mode 100644 index a0a85f6..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/test_goal_publishers_config.yaml +++ /dev/null @@ -1,57 +0,0 @@ -publisher_scaled_joint_trajectory_controller: - ros__parameters: - - controller_name: "scaled_joint_trajectory_controller" - wait_sec_between_publish: 6 - - goal_names: ["pos1", "pos2", "pos3", "pos4"] - pos1: [0.785, -1.57, 0.785, 0.785, 0.785, 0.785] - pos2: [0.0, -1.57, 0.0, 0.0, 0.0, 0.0] - pos3: [0.0, -1.57, 0.0, 0.0, -0.785, 0.0] - pos4: [0.0, -1.57, 0.0, 0.0, 0.0, 0.0] - - joints: - - shoulder_pan_joint - - shoulder_lift_joint - - elbow_joint - - wrist_1_joint - - wrist_2_joint - - wrist_3_joint - - check_starting_point: true - starting_point_limits: - shoulder_pan_joint: [-0.1,0.1] - shoulder_lift_joint: [-1.6,-1.5] - elbow_joint: [-0.1,0.1] - wrist_1_joint: [-1.6,-1.5] - wrist_2_joint: [-0.1,0.1] - wrist_3_joint: [-0.1,0.1] - -publisher_joint_trajectory_controller: - ros__parameters: - - controller_name: "joint_trajectory_controller" - wait_sec_between_publish: 6 - - goal_names: ["pos1", "pos2", "pos3", "pos4"] - pos1: [0.785, -1.57, 0.785, 0.785, 0.785, 0.785] - pos2: [0.0, -1.57, 0.0, 0.0, 0.0, 0.0] - pos3: [0.0, -1.57, 0.0, 0.0, -0.785, 0.0] - pos4: [0.0, -1.57, 0.0, 0.0, 0.0, 0.0] - - joints: - - shoulder_pan_joint - - shoulder_lift_joint - - elbow_joint - - wrist_1_joint - - wrist_2_joint - - wrist_3_joint - - check_starting_point: true - starting_point_limits: - shoulder_pan_joint: [-0.1,0.1] - shoulder_lift_joint: [-1.6,-1.5] - elbow_joint: [-0.1,0.1] - wrist_1_joint: [-1.6,-1.5] - wrist_2_joint: [-0.1,0.1] - wrist_3_joint: [-0.1,0.1] diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/test_velocity_goal_publishers_config.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/test_velocity_goal_publishers_config.yaml deleted file mode 100644 index 4d27858..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/test_velocity_goal_publishers_config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -publisher_forward_velocity_controller: - ros__parameters: - - controller_name: "forward_velocity_controller" - wait_sec_between_publish: 5 - - goal_names: ["pos1", "pos2", "pos3"] - pos1: [0.0, 0.0, 0.0, 0.0, 0.0, 0.05] - pos2: [0.0, 0.0, 0.0, 0.0, 0.0, -0.05] - pos3: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur10_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur10_update_rate.yaml deleted file mode 100644 index 7525e1e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur10_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 125 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur10e_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur10e_update_rate.yaml deleted file mode 100644 index 66ef3d7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur10e_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 500 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur16e_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur16e_update_rate.yaml deleted file mode 100644 index 66ef3d7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur16e_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 500 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur3_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur3_update_rate.yaml deleted file mode 100644 index 7525e1e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur3_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 125 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur3e_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur3e_update_rate.yaml deleted file mode 100644 index 66ef3d7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur3e_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 500 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur5_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur5_update_rate.yaml deleted file mode 100644 index 7525e1e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur5_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 125 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur5e_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur5e_update_rate.yaml deleted file mode 100644 index 66ef3d7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur5e_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 500 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur_controllers.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur_controllers.yaml deleted file mode 100644 index 8c7e30b..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/config/ur_controllers.yaml +++ /dev/null @@ -1,160 +0,0 @@ -controller_manager: - ros__parameters: - joint_state_broadcaster: - type: joint_state_broadcaster/JointStateBroadcaster - - io_and_status_controller: - type: ur_controllers/GPIOController - - speed_scaling_state_broadcaster: - type: ur_controllers/SpeedScalingStateBroadcaster - - force_torque_sensor_broadcaster: - type: force_torque_sensor_broadcaster/ForceTorqueSensorBroadcaster - - joint_trajectory_controller: - type: joint_trajectory_controller/JointTrajectoryController - - scaled_joint_trajectory_controller: - type: ur_controllers/ScaledJointTrajectoryController - - forward_velocity_controller: - type: velocity_controllers/JointGroupVelocityController - - forward_position_controller: - type: position_controllers/JointGroupPositionController - - tcp_pose_broadcaster: - type: pose_broadcaster/PoseBroadcaster - - passthrough_trajectory_controller: - type: ur_controllers/PassthroughTrajectoryController - - ur_configuration_controller: - type: ur_controllers/URConfigurationController - -speed_scaling_state_broadcaster: - ros__parameters: - state_publish_rate: 100.0 - tf_prefix: "$(var tf_prefix)" - -io_and_status_controller: - ros__parameters: - tf_prefix: "$(var tf_prefix)" - -ur_configuration_controller: - ros__parameters: - tf_prefix: "$(var tf_prefix)" - -force_torque_sensor_broadcaster: - ros__parameters: - sensor_name: $(var tf_prefix)tcp_fts_sensor - state_interface_names: - - force.x - - force.y - - force.z - - torque.x - - torque.y - - torque.z - frame_id: $(var tf_prefix)tool0 - topic_name: ft_data - - -joint_trajectory_controller: - ros__parameters: - joints: - - $(var tf_prefix)shoulder_pan_joint - - $(var tf_prefix)shoulder_lift_joint - - $(var tf_prefix)elbow_joint - - $(var tf_prefix)wrist_1_joint - - $(var tf_prefix)wrist_2_joint - - $(var tf_prefix)wrist_3_joint - command_interfaces: - - position - state_interfaces: - - position - - velocity - state_publish_rate: 100.0 - action_monitor_rate: 20.0 - allow_partial_joints_goal: false - constraints: - stopped_velocity_tolerance: 0.2 - goal_time: 0.0 - $(var tf_prefix)shoulder_pan_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)shoulder_lift_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)elbow_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_1_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_2_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_3_joint: { trajectory: 0.2, goal: 0.1 } - - -scaled_joint_trajectory_controller: - ros__parameters: - joints: - - $(var tf_prefix)shoulder_pan_joint - - $(var tf_prefix)shoulder_lift_joint - - $(var tf_prefix)elbow_joint - - $(var tf_prefix)wrist_1_joint - - $(var tf_prefix)wrist_2_joint - - $(var tf_prefix)wrist_3_joint - command_interfaces: - - position - state_interfaces: - - position - - velocity - state_publish_rate: 100.0 - action_monitor_rate: 20.0 - allow_partial_joints_goal: false - constraints: - stopped_velocity_tolerance: 0.2 - goal_time: 0.0 - $(var tf_prefix)shoulder_pan_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)shoulder_lift_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)elbow_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_1_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_2_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_3_joint: { trajectory: 0.2, goal: 0.1 } - speed_scaling_interface_name: $(var tf_prefix)speed_scaling/speed_scaling_factor - -passthrough_trajectory_controller: - ros__parameters: - tf_prefix: "$(var tf_prefix)" - joints: - - $(var tf_prefix)shoulder_pan_joint - - $(var tf_prefix)shoulder_lift_joint - - $(var tf_prefix)elbow_joint - - $(var tf_prefix)wrist_1_joint - - $(var tf_prefix)wrist_2_joint - - $(var tf_prefix)wrist_3_joint - state_interfaces: - - position - - velocity - speed_scaling_interface_name: $(var tf_prefix)speed_scaling/speed_scaling_factor - -forward_velocity_controller: - ros__parameters: - joints: - - $(var tf_prefix)shoulder_pan_joint - - $(var tf_prefix)shoulder_lift_joint - - $(var tf_prefix)elbow_joint - - $(var tf_prefix)wrist_1_joint - - $(var tf_prefix)wrist_2_joint - - $(var tf_prefix)wrist_3_joint - interface_name: velocity - -forward_position_controller: - ros__parameters: - joints: - - $(var tf_prefix)shoulder_pan_joint - - $(var tf_prefix)shoulder_lift_joint - - $(var tf_prefix)elbow_joint - - $(var tf_prefix)wrist_1_joint - - $(var tf_prefix)wrist_2_joint - - $(var tf_prefix)wrist_3_joint - -tcp_pose_broadcaster: - ros__parameters: - frame_id: $(var tf_prefix)base - pose_name: $(var tf_prefix)tcp_pose - tf: - child_frame_id: $(var tf_prefix)tool0_controller diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_forward_velocity_controller.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_forward_velocity_controller.launch.py deleted file mode 100644 index c00d612..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_forward_velocity_controller.launch.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Lovro Ivanov -# -# Description: After a robot has been loaded, this will execute a series of trajectories. - -from launch import LaunchDescription -from launch.substitutions import PathJoinSubstitution -from launch_ros.actions import Node -from launch_ros.substitutions import FindPackageShare - - -def generate_launch_description(): - print( - "\033[91m" - "DEPRECATION WARNING: " - "Launch files from the ur_bringup package are deprecated and will be removed from Iron " - "Irwini on. Please use the same launch files from the ur_robot_driver package." - "\033[0m" - ) - - velocity_goals = PathJoinSubstitution( - [FindPackageShare("ur_bringup"), "config", "test_velocity_goal_publishers_config.yaml"] - ) - - return LaunchDescription( - [ - Node( - package="ros2_controllers_test_nodes", - executable="publisher_forward_position_controller", - name="publisher_forward_velocity_controller", - parameters=[velocity_goals], - output="screen", - ) - ] - ) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_joint_trajectory_controller.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_joint_trajectory_controller.launch.py deleted file mode 100644 index 556b314..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_joint_trajectory_controller.launch.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl -# -# Description: After a robot has been loaded, this will execute a series of trajectories. - -from launch import LaunchDescription -from launch.substitutions import PathJoinSubstitution -from launch_ros.actions import Node -from launch_ros.substitutions import FindPackageShare - - -def generate_launch_description(): - print( - "\033[91m" - "DEPRECATION WARNING: " - "Launch files from the ur_bringup package are deprecated and will be removed from Iron " - "Irwini on. Please use the same launch files from the ur_robot_driver package." - "\033[0m" - ) - - position_goals = PathJoinSubstitution( - [FindPackageShare("ur_bringup"), "config", "test_goal_publishers_config.yaml"] - ) - - return LaunchDescription( - [ - Node( - package="ros2_controllers_test_nodes", - executable="publisher_joint_trajectory_controller", - name="publisher_joint_trajectory_controller", - parameters=[position_goals], - output="screen", - ) - ] - ) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_scaled_joint_trajectory_controller.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_scaled_joint_trajectory_controller.launch.py deleted file mode 100644 index ecaf404..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/test_scaled_joint_trajectory_controller.launch.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl -# -# Description: After a robot has been loaded, this will execute a series of trajectories. - -from launch import LaunchDescription -from launch.substitutions import PathJoinSubstitution -from launch_ros.actions import Node -from launch_ros.substitutions import FindPackageShare - - -def generate_launch_description(): - print( - "\033[91m" - "DEPRECATION WARNING: " - "Launch files from the ur_bringup package are deprecated and will be removed from Iron " - "Irwini on. Please use the same launch files from the ur_robot_driver package." - "\033[0m" - ) - - position_goals = PathJoinSubstitution( - [FindPackageShare("ur_bringup"), "config", "test_goal_publishers_config.yaml"] - ) - - return LaunchDescription( - [ - Node( - package="ros2_controllers_test_nodes", - executable="publisher_joint_trajectory_controller", - name="publisher_scaled_joint_trajectory_controller", - parameters=[position_goals], - output="screen", - ) - ] - ) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur10.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur10.launch.py deleted file mode 100644 index e838f7c..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur10.launch.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. \ - Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur10", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur10e.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur10e.launch.py deleted file mode 100644 index f014274..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur10e.launch.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. \ - Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur10e", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur16e.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur16e.launch.py deleted file mode 100644 index d27c8bf..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur16e.launch.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. \ - Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur16e", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur3.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur3.launch.py deleted file mode 100644 index 7add624..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur3.launch.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. \ - Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur3", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur3e.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur3e.launch.py deleted file mode 100644 index e9745d5..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur3e.launch.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. \ - Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur3e", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur5.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur5.launch.py deleted file mode 100644 index 14bb98c..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur5.launch.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. \ - Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur5", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur5e.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur5e.launch.py deleted file mode 100644 index 0b5c6f8..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur5e.launch.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. \ - Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur5e", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur_control.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur_control.launch.py deleted file mode 100644 index 8b1b395..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur_control.launch.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import IncludeLaunchDescription, DeclareLaunchArgument -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration -from launch_ros.substitutions import FindPackageShare - - -def generate_launch_description(): - print( - "\033[91m" - "Deprecation warning: " - "Launch files from the ur_bringup package are deprecated and will be removed from Iron " - "Irwini on. Please use the same launch files from the ur_robot_driver package." - "\033[0m" - ) - declared_arguments = [] - # UR specific arguments - declared_arguments.append( - DeclareLaunchArgument( - "ur_type", - description="Type/series of used UR robot.", - choices=[ - "ur3", - "ur3e", - "ur5", - "ur5e", - "ur10", - "ur10e", - "ur16e", - "ur20", - "ur30", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", description="IP address by which the robot can be reached." - ) - ) - - ur_type = LaunchConfiguration("ur_type") - robot_ip = LaunchConfiguration("robot_ip") - - return LaunchDescription( - declared_arguments - + [ - IncludeLaunchDescription( - PythonLaunchDescriptionSource( - [ - FindPackageShare("ur_robot_driver"), - "/launch", - "/ur_control.launch.py", - ] - ), - launch_arguments={ - "ur_type": ur_type, - "robot_ip": robot_ip, - }.items(), - ) - ] - ) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur_dashboard_client.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur_dashboard_client.launch.py deleted file mode 100755 index 70bbca6..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/launch/ur_dashboard_client.launch.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2021, PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument -from launch.substitutions import LaunchConfiguration -from launch_ros.actions import Node - - -def generate_launch_description(): - print( - "\033[91m" - "DEPRECATION WARNING: " - "Launch files from the ur_bringup package are deprecated and will be removed from Iron " - "Irwini on. Please use the same launch files from the ur_robot_driver package." - "\033[0m" - ) - - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - - dashboard_client_node = Node( - package="ur_robot_driver", - executable="dashboard_client", - name="dashboard_client", - output="screen", - emulate_tty=True, - parameters=[{"robot_ip": robot_ip}], - ) - - return LaunchDescription(declared_arguments + [dashboard_client_node]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/package.xml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/package.xml deleted file mode 100644 index 9561da9..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/package.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - ur_bringup - 2.5.2 - Launch file and run-time configurations, e.g. controllers. - - Felix Exner - Rune Søe-Knudsen - Universal Robots A/S - - BSD-3-Clause - - https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver/issues - https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver - - Lovro Ivanov - Denis Stogl - Denis Stogl - - ament_cmake - ament_cmake_python - - rclpy - - controller_manager - force_torque_sensor_broadcaster - joint_state_broadcaster - joint_state_publisher - joint_trajectory_controller - launch - launch_ros - position_controllers - robot_state_publisher - ros2_controllers_test_nodes - rviz2 - ur_controllers - ur_description - urdf - velocity_controllers - xacro - - - ament_cmake - - diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/scripts/start_ursim.sh b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/scripts/start_ursim.sh deleted file mode 100755 index 3c60f1e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_bringup/scripts/start_ursim.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -# copyright 2022 Universal Robots A/S -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - - -echo -e "\033[0;31mDEPRECATION WARNING: " \ - "Launch files from the ur_bringup package are deprecated and will be removed from Iron " \ - "Irwini on. Please use the same launch files from the ur_robot_driver package." \ - "\033[0m" - -ORANGE='\033[0;33m' -NC='\033[0m' # No Color - -URSIM_CMD="ros2 run ur_client_library start_ursim.sh" - -echo -e "${ORANGE} DEPRECATION WARNING: The script starting URSim was moved to the ur_client_library package. This script here does still work, but will be removed with ROS Jazzy. Please use `${URSIM_CMD}` to start URSim in future." -$URSIM_CMD diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/CHANGELOG.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/CHANGELOG.rst deleted file mode 100644 index 8e5b751..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/CHANGELOG.rst +++ /dev/null @@ -1,78 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ur_calibration -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -2.5.2 (2025-01-21) ------------------- - -2.5.1 (2024-12-21) ------------------- - -2.5.0 (2024-12-18) ------------------- -* Update package maintainers (backport of `#1203 `_) -* Initialize segments in constructor of DHRobot in calibration.hpp (backport of `#1197 `_) -* Contributors: mergify[bot] - -2.2.16 (2024-10-28) -------------------- - -2.2.15 (2024-07-26) -------------------- - -2.2.14 (2024-07-01) -------------------- - -2.2.13 (2024-06-17) -------------------- -* Fix calibration (`#1022 `_) -* Contributors: Felix Exner, Vincenzo Di Pendima - -2.2.12 (2024-05-16) -------------------- - -2.2.11 (2024-04-08) -------------------- - -2.2.10 (2024-01-03) -------------------- - -2.2.9 (2023-09-22) ------------------- - -2.2.8 (2023-06-26) ------------------- - -2.2.7 (2023-06-02) ------------------- - -2.2.6 (2022-11-28) ------------------- - -2.2.5 (2022-11-19) ------------------- -* Fixes launchfile references (`#490 `_) - The filename with the dual type ending was wrong. -* Contributors: Felix Exner - -2.2.4 (2022-10-07) ------------------- - -2.2.3 (2022-07-27) ------------------- - -2.2.2 (2022-07-19) ------------------- -* Made sure all past maintainers are listed as authors (`#429 `_) -* Contributors: Felix Exner - -2.2.1 (2022-06-27) ------------------- - -2.2.0 (2022-06-20) ------------------- -* Updated package maintainers -* Update license to BSD-3-Clause (`#277 `_) -* Add missing dependency on angles and update formatting for linters. (`#283 `_) -* Calibration extraction package (`#186 `_) -* Contributors: Denis Štogl, Felix Exner, livanov93 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/CMakeLists.txt b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/CMakeLists.txt deleted file mode 100644 index 0b17e4d..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/CMakeLists.txt +++ /dev/null @@ -1,100 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(ur_calibration) - -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options(-Wall -Wextra -Wpedantic) -endif() - -add_compile_options(-Wno-unused-parameter) - -if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) - message("${PROJECT_NAME}: You did not request a specific build type: selecting 'RelWithDebInfo'.") - set(CMAKE_BUILD_TYPE RelWithDebInfo) -endif() - -find_package(ament_cmake REQUIRED) -find_package(rclcpp REQUIRED) -find_package(ur_robot_driver REQUIRED) - -find_package(Eigen3 REQUIRED) -find_package(yaml-cpp REQUIRED) -find_package(ur_client_library REQUIRED) - -set(YAML_CPP_INCLUDE_DIRS ${YAML_CPP_INCLUDE_DIR}) - -########### -## Build ## -########### - -add_library(calibration - src/calibration.cpp - src/calibration_consumer.cpp -) -target_include_directories(calibration - PUBLIC - include - ${EIGEN3_INCLUDE_DIRS} - ${YAML_CPP_INCLUDE_DIRS} -) -target_link_libraries(calibration - ${YAML_CPP_LIBRARIES} -) -ament_target_dependencies(calibration - rclcpp - ur_robot_driver -) - -add_executable(calibration_correction - src/calibration_correction.cpp -) -target_include_directories(calibration_correction - PRIVATE - include -) -target_link_libraries(calibration_correction - ur_client_library::urcl -) -target_link_libraries(calibration_correction - calibration -) - -install(TARGETS calibration_correction - RUNTIME DESTINATION lib/${PROJECT_NAME} -) - -install(DIRECTORY include/ - DESTINATION include -) - -install(DIRECTORY launch - DESTINATION share/${PROJECT_NAME} -) - -## Add gtest based cpp test target and link libraries -if(BUILD_TESTING) - find_package(ament_cmake_gmock REQUIRED) - - # Get the first item (it will be the build space version of the build path). - list(GET ament_index_build_path 0 ament_index_build_path) - if(WIN32) - # On Windows prevent CMake errors and prevent it being evaluated as a list. - string(REPLACE "\\" "/" ament_index_build_path "${ament_index_build_path}") - endif() - - ament_add_gmock( - calibration_test - test/calibration_test.cpp - ) - target_link_libraries(calibration_test - calibration - ) -endif() - -ament_export_include_directories( - include -) -ament_export_dependencies( - ur_robot_driver - rclcpp -) -ament_package() diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/README.md b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/README.md deleted file mode 100644 index d55e476..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# ur_calibration - -Package for extracting the factory calibration from a UR robot and changing it to be used by `ur_description` to gain a correct URDF model. - -Each UR robot is calibrated inside the factory giving exact forward and inverse kinematics. To also -make use of this in ROS, you first have to extract the calibration information from the robot. - -Though this step is not necessary, to control the robot using this driver, it is highly recommended -to do so, as end effector positions might be off in the magnitude of centimeters. - -## Nodes -### calibration_correction -This node extracts calibration information directly from a robot, calculates the URDF correction and -saves it into a .yaml file. - -In the launch folder of the ur_calibration package is a helper script: - -```bash -$ ros2 launch ur_calibration calibration_correction.launch.py \ -robot_ip:= target_filename:="${HOME}/my_robot_calibration.yaml" -``` - -For the parameter `robot_ip` insert the IP address on which the ROS pc can reach the robot. As -`target_filename` provide an absolute path where the result will be saved to. - -## Creating a calibration / launch package for all local robots -When dealing with multiple robots in one organization it might make sense to store calibration data -into a package dedicated to that purpose only. To do so, create a new package (if it doesn't already -exist) - -```bash -# Replace your actual colcon_ws folder -$ cd /src -$ ros2 pkg create _ur_launch --build-type ament_cmake --dependencies ur_client_library \ ---description "Package containing calibrations and launch files for our UR robots." -# Create a skeleton package -$ mkdir -p _ur_launch/etc -$ mkdir -p _ur_launch/launch -$ echo 'install(DIRECTORY etc launch DESTINATION share/${PROJECT_NAME})' >> _ur_launch/CMakeLists.txt -$ colcon build --packages-select _ur_launch -``` - -We can use the new package to store the calibration data in that package. We recommend naming each -robot individually, e.g. *ex-ur10-1*. - -```bash -$ ros2 launch ur_calibration calibration_correction.launch.py \ -robot_ip:= \ -target_filename:="$(ros2 pkg prefix _ur_launch)/share/_ur_launch/etc/ex-ur10-1_calibration.yaml" -``` - -To make life easier, we create a launchfile for this particular robot. We base it upon the -respective launchfile in the driver: - -```bash -# Replace your actual colcon_ws folder -$ cd /src/_ur_launch/launch -$ cp $(ros2 pkg prefix ur_robot_driver)/share/ur_robot_driver/launch/ur_control.launch.py ex-ur10-1.launch.py -``` - -Next, modify the parameter section of the new launchfile to match your actual calibration: - -```py -kinematics_params = PathJoinSubstitution( - [FindPackageShare("_ur_launch"), "etc", "", "ex-ur10-1_calibration.yaml"] - ) - -``` - -Then, anybody cloning this repository can startup the robot simply by launching - -```bash -# Replace your actual colcon_ws folder -$ cd -$ colcon build --packages-select _ur_launch -$ ros2 launch _ur_launch ex-ur10-1.launch.py -robot_ip:=xxx.yyy.zzz.www ur_type:=ur5e use_fake_hardware:=false launch_rviz:=true -``` diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/algorithm.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/algorithm.rst deleted file mode 100644 index 0364390..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/algorithm.rst +++ /dev/null @@ -1,97 +0,0 @@ -Calibration correction algorithm -================================ - -When extracting the robot's calibration there will be a set of Denavit–Hartenberg (DH) parameters -describing the robot. Due to the calibration process they can seem a bit unintuitive since the -``d``-parameter of the second and third joint can be quite large on those. - -For example, let's consider the following dh parameters taken from a real robot: - -.. code-block:: yaml - - # joint1 joint2 joint3 joint4 joint5 joint6 - dh_theta: [-2.4147806894359e-07 1.60233952386695 -1.68607190752171 0.0837331147700119 -1.01260355871158e-07 3.91986209186124e-08 ] - dh_a: [ 2.12234865571206e-05 0.0193171326277006 -0.569251663611088 -4.61409023720934e-05 -6.39280053471802e-05 0 ] - dh_d: [ 0.180539811714259 439.140974079901 -446.027059806332 7.0603368964236 0.119811341150314 0.115670917257426 ] - dh_alpha: [ 1.57014608044242 0.0013941666682559 0.00693818880325995 1.56998468543761 -1.57038520649543 0 ] - -One can see that the upper arm is placed 439 m out of the base link with the lower arm being 7 m to -the other side. - -We can build a robot description that splits each DH segment into two links: One for ``d`` and -``theta`` representing the rotational part of the joint and one for ``a`` and ``alpha`` -representing the "passive" part of the joint displacing the next link. -:numref:`calibration_example` shows (a part of) the description matching the parameters above. The -arm links are left out of the image as they are really far away. - -.. _calibration_example: -.. figure:: calibration_example.png - :alt: Example of a calibrated model - - This shows an example calibrated model when using the DH parameters directly as explained above. - The two arm links are virtually displaced very far from the physical robot while the TCP ends up - at the correct location again - - -For explaining the correction algorithm, we will use an artificial set of DH parameters for a -UR10e: - -.. code-block:: yaml - - # join1 joint2 joint3 joint4 joint5 joint6 - dh_theta: [0 0 0 0 0 0 ] - dh_a: [0 -0.6127 -0.57155 0 0 0 ] - dh_d: [0.1807 1 0.5 -1.32585 0.11985 0.11655] - dh_alpha: [1.570796327 0.2 0 1.570796327 -1.570796327 0 ] - -The resulting uncorrected model can be seen in :numref:`calibration_uncorrected`. The upper arm is -placed 1 m to the left of the shoulder, the upper arm is placed 0.5 m further out and there's an -added ``alpha`` rotation in joint2. - -Note: This is not a valid calibration, so when placing this "calibration" on a robot and using the -correction, we won't get correct tcp pose results. This only serves as a exaggerated example. - -.. _calibration_uncorrected: -.. figure:: calibration_uncorrected.png - :alt: Exaggerated calibrated model - - This shows an artificial calibration only to show the algorithm. This is no valid calibration! - -In :numref:`calibration_uncorrected` the separation between the two DH components can be seen quite -clearly. joint2's ``d`` and ``theta`` parameters are represented by ``upper_arm_d`` and its ``a`` -and ``alpha`` parameters result in ``upper_arm_a``. - -The "correction" step tries to bring the two arm segments back to their physical representation. -In principle, the d parameter to zero, first. With this change, -the kinematic structure gets destroyed, which has to be corrected: - -- With setting ``d`` to 0, both the start (``upper_arm_d``) and end (``upper_arm_a``) points of the - passive segment move along the joint's rotational axis. Instead, the end point of the passive - segment has to move along the rotational axis of the next segment. This requires adapting - ``a`` and ``theta``, if the two rotational axes are not parallel. - -- The length of moving along the next segment's rotational axis is calculated by intersecting - the next rotational axis with the XY-plane of the moved ``_d`` frame. This gets subtracted from - the next joint's ``d`` parameter. - -Note that the parameters from this model are not strict DH parameters anymore, as the two frames at -the tip of the upper and lower arm have to get an additional rotation to compensate the change of -the arm segment orientation, when the tip is moving along its rotation axis. - -The resulting "DH parameters" are then reassembled into six individual transforms that can become -the six frames of the robot's kinematic chain. This is exported in a yaml representation and gets -read by the description package. - -Also, no correction of the visual meshes is performed. Strictly speaking, the visual -model is not 100 % correct, but with a calibrated model and ideal meshes this cannot be achieved and -the inaccuracies introduced by this are considered negligible. - -The example as visualized in :numref:`calibration_example` looks as follows if a description with -the correct parameters is loaded: - -.. figure:: calibration_example_corrected.png - :alt: Example with corrected kinematics structure - - This shows the model from :numref:`calibration_example` with the calibration correction applied. - The robot is correctly assembled and the ``base->tool0`` transformation is exactly the same as - on the robot controller. diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_example.png b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_example.png deleted file mode 100644 index 98e35d4..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_example.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2fe0ec7ef32a38c9f700cb4cd1f6ae882bae8c4f99361024ea45dc863b5d61be -size 40759 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_example_corrected.png b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_example_corrected.png deleted file mode 100644 index 7410955..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_example_corrected.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:52c7e2fbf22c3956d218f254bbd7fc0e8a956366bc0d5890e8279b85d9d0da94 -size 100338 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_uncorrected.png b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_uncorrected.png deleted file mode 100644 index 63002b7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/calibration_uncorrected.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:99d8e8de8930306e60f6b7976695546730c20d2c835c18e2655640fe7a10b862 -size 213664 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/conf.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/conf.py deleted file mode 100644 index c0b683e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/conf.py +++ /dev/null @@ -1,85 +0,0 @@ -project = "ur_calibration" -copyright = "2022, Universal Robots A/S" -author = "Felix Exner" - -# The short X.Y version -version = "" -# The full version, including alpha/beta/rc tags -release = "" - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -source_suffix = [".rst"] - -# The master toctree document. -master_doc = "index" - -numfig = True - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = None - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "alabaster" - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = "ur_calibration_doc" - - -# -- Options for LaTeX output ------------------------------------------------ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/index.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/index.rst deleted file mode 100644 index c91768e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/index.rst +++ /dev/null @@ -1,17 +0,0 @@ - -ur_calibration -============== - -Package for extracting the factory calibration from a UR robot and changing it to be used by ``ur_description`` to gain a correct URDF model. - -Each UR robot is calibrated inside the factory giving exact forward and inverse kinematics. To also -make use of this in ROS, you first have to extract the calibration information from the robot. - -Though this step is not necessary, to control the robot using this driver, it is highly recommended -to do so, as end effector positions might be off in the magnitude of centimeters. - -.. toctree:: - :maxdepth: 2 - - usage - algorithm diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/usage.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/usage.rst deleted file mode 100644 index ecf4b23..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/doc/usage.rst +++ /dev/null @@ -1,28 +0,0 @@ -Usage -===== - -To use the calibration correction this package provides a launchfile that extracts calibration -information directly from a robot, calculates the URDF correction and saves it into a .yaml file: - -.. code-block:: bash - - $ ros2 launch ur_calibration calibration_correction.launch.py \ - robot_ip:= target_filename:="${HOME}/my_robot_calibration.yaml" - -For the parameter ``robot_ip`` insert the IP address on which the ROS pc can reach the robot. As -``target_filename`` provide an absolute path where the result will be saved to. - -With that, you can launch your specific robot with the correct calibration using - -.. code-block:: bash - - $ ros2 launch ur_robot_driver ur_control.launch.py \ - ur_type:=ur5e \ - robot_ip:=192.168.56.101 \ - kinematics_params_file:="${HOME}/my_robot_calibration.yaml" - -Adapt the robot model matching to your robot. - -Ideally, you would create a package for your custom workcell, as explained in `the custom workcell -tutorial -`_. diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/include/ur_calibration/calibration.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/include/ur_calibration/calibration.hpp deleted file mode 100644 index 6cfcc3f..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/include/ur_calibration/calibration.hpp +++ /dev/null @@ -1,218 +0,0 @@ -// -- BEGIN LICENSE BLOCK ---------------------------------------------- -// Created on behalf of Universal Robots A/S -// Copyright 2019 FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -// -- END LICENSE BLOCK ------------------------------------------------ - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-01-10 - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \date 2021-09-22 - * - */ -//---------------------------------------------------------------------- - -#ifndef UR_CALIBRATION__CALIBRATION_HPP_ -#define UR_CALIBRATION__CALIBRATION_HPP_ - -#include -#include -#include -#include -#include - -#include "rclcpp/rclcpp.hpp" -#include "yaml-cpp/yaml.h" - -namespace ur_calibration -{ -/*! - * \brief An internal representation of a DH-parametrized link. - * - * Each segment consists of parameters a, d, alpha and theta. - */ -struct DHSegment -{ - double d_; - double a_; - double theta_; - double alpha_; - - /*! - * \brief Creates an element with defined elements. - */ - DHSegment(const double d, const double a, const double theta, const double alpha) - : d_(d), a_(a), theta_(theta), alpha_(alpha) - { - } - - /*! - * \brief Creates a Segment with all elements equal to zero. - */ - DHSegment() : d_(0), a_(0), theta_(0), alpha_(0) - { - } - - /*! - * \brief Adds another segment element-wise (a+a, d+d, alpha+alpha, theta+theta) - * - * \param other Other segment to add - * - * \returns Segment consisting of element-wise addition of \p this and \p other - */ - DHSegment operator+(const DHSegment& other) - { - return DHSegment(this->d_ + other.d_, this->a_ + other.a_, this->theta_ + other.theta_, - this->alpha_ + other.alpha_); - } -}; - -/*! - * \brief Internal representation of a robot based on DH parameters. - * - * Note that this representation doesn't contain a real DH parameter representation, but is used for - * a corrected model of calibrated UR robots. Shoulder and elbow axes are artificially shortened in - * the final representation, requiring a correction parameter in \p theta_2 and \p theta_3. - */ -struct DHRobot -{ - std::vector segments_; - double delta_theta_correction2_; - double delta_theta_correction3_; - - /*! - * \brief Create a new robot representation giving a set of \ref DHSegment objects - */ - explicit DHRobot(const std::vector& segments) : segments_(segments) - { - delta_theta_correction2_ = 0; - delta_theta_correction3_ = 0; - } - - DHRobot() = default; - - /*! - * \brief Adds another robot representation, by adding their segments element-wise. See \ref - * DHSegment::operator+ for details. - */ - DHRobot operator+(const DHRobot& other) - { - assert(this->segments_.size() == other.segments_.size()); - DHRobot ret; - for (size_t i = 0; i < this->segments_.size(); ++i) { - ret.segments_.push_back(this->segments_[i] + other.segments_[i]); - } - return ret; - } -}; - -/*! - * \brief Class that handles the calibration correction for Universal Robots - * - * Universal robots provide a factory calibration of their DH parameters to exactly estimate their - * TCP pose using forward kinematics. However, those DH parameters construct a kinematic chain that - * can be very long, as upper arm and lower arm segments can be drawn out of their physical position - * by multiple meters (even above 100m can occur). - * - * This class helps creating a kinematic chain, that is as close as possible to the physical model, - * by dragging the upper and lower arm segments back to their zero position. - */ -class Calibration -{ -public: - explicit Calibration(const DHRobot& robot); - virtual ~Calibration(); - - /*! - * \brief Corrects a UR kinematic chain in such a way that shoulder and elbow offsets are 0. - */ - void correctChain(); - - /*! - * \brief Get the transformation matrix representation of the chain as constructed by the - * DH parameters. - * - * This will contain twice as many transformation matrices as joints, as for each set of DH - * parameters two matrices are generated. If you'd like to receive one matrix per joint instead, - * use the getSimplified() function instead. - * - * \returns A vector of 4x4 transformation matrices, two for each joint going from the base to the - * tcp. - */ - std::vector getChain() - { - return chain_; - } - - /*! - * \brief Get the transformation matrix representation of the chain, where each joint is - * represented by one matrix. - * - * \returns Vector of 4x4 transformation matrices, one for each joint going from the base to the - * tcp. - */ - std::vector getSimplified() const; - - /*! - * \brief Generates a yaml representation of all transformation matrices as returned by - * getSimplified() - * - * \returns A YAML tree representing all transformation matrices. - */ - YAML::Node toYaml() const; - - /*! - * \brief Calculates the forwart kinematics given a joint configuration with respect to the base - * link. - * - * \param joint_values Joint values for which the forward kinematics should be calculated. - * \param link_nr If given, the cartesian position for this joint (starting at 1) is returned. By - * default the 6th joint is used. - * - * \returns Transformation matrix giving the full pose of the requested link in base coordinates. - */ - Eigen::Matrix4d calcForwardKinematics(const Eigen::Matrix& joint_values, const size_t link_nr = 6); - -private: - // Corrects a single axis - void correctAxis(const size_t correction_index); - - // Builds the chain from robot_parameters_ - void buildChain(); - - DHRobot robot_parameters_; - std::vector link_names_ = { "shoulder", "upper_arm", "forearm", "wrist_1", "wrist_2", "wrist_3" }; - - std::vector chain_; -}; -} // namespace ur_calibration -#endif // UR_CALIBRATION__CALIBRATION_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/include/ur_calibration/calibration_consumer.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/include/ur_calibration/calibration_consumer.hpp deleted file mode 100644 index 9acadc9..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/include/ur_calibration/calibration_consumer.hpp +++ /dev/null @@ -1,76 +0,0 @@ -// -- BEGIN LICENSE BLOCK ---------------------------------------------- -// Created on behalf of Universal Robots A/S -// Copyright 2019 FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -// -- END LICENSE BLOCK ------------------------------------------------ - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-05-28 - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \date 2021-09-22 - * - */ -//---------------------------------------------------------------------- - -#ifndef UR_CALIBRATION__CALIBRATION_CONSUMER_HPP_ -#define UR_CALIBRATION__CALIBRATION_CONSUMER_HPP_ - -#include - -#include "ur_client_library/comm/pipeline.h" -#include "ur_client_library/primary/robot_state/kinematics_info.h" - -#include "ur_calibration/calibration.hpp" - -namespace ur_calibration -{ -class CalibrationConsumer : public urcl::comm::IConsumer -{ -public: - CalibrationConsumer(); - virtual ~CalibrationConsumer() = default; - - virtual bool consume(std::shared_ptr product); - - bool isCalibrated() const - { - return calibrated_; - } - - YAML::Node getCalibrationParameters() const; - -private: - bool calibrated_; - YAML::Node calibration_parameters_; -}; -} // namespace ur_calibration -#endif // UR_CALIBRATION__CALIBRATION_CONSUMER_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/launch/calibration_correction.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/launch/calibration_correction.launch.py deleted file mode 100644 index e660630..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/launch/calibration_correction.launch.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Lovro Ivanov - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument -from launch.substitutions import LaunchConfiguration -from launch_ros.actions import Node - - -def generate_launch_description(): - - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", description="The IP address at which the robot is reachable." - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "target_filename", - default_value="robot_calibration.yaml", - description="The extracted calibration information " - "will be written to this target file.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - output_filename = LaunchConfiguration("target_filename") - - calibration_correction = Node( - package="ur_calibration", - executable="calibration_correction", - parameters=[{"robot_ip": robot_ip}, {"output_filename": output_filename}], - output="screen", - ) - - nodes_to_start = [ - calibration_correction, - ] - - return LaunchDescription(declared_arguments + nodes_to_start) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/package.xml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/package.xml deleted file mode 100644 index 67de5a3..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/package.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - ur_calibration - 2.5.2 - Package for extracting the factory calibration from a UR robot and change it such that it can be used by ur_description to gain a correct URDF - - Felix Exner - Rune Søe-Knudsen - Universal Robots A/S - - BSD-3-Clause - - Lovro Ivanov - - ament_cmake - - rclcpp - ur_client_library - ur_robot_driver - - eigen - yaml-cpp - - ament_cmake_gmock - ament_cmake_gtest - ament_lint_auto - ament_lint_common - - - ament_cmake - - diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration.cpp deleted file mode 100644 index 9a680d8..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration.cpp +++ /dev/null @@ -1,239 +0,0 @@ -// -- BEGIN LICENSE BLOCK ---------------------------------------------- -// Created on behalf of Universal Robots A/S -// Copyright 2019 FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -// -- END LICENSE BLOCK ------------------------------------------------ - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-01-10 - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \date 2021-09-22 - * - */ -//---------------------------------------------------------------------- - -#include "ur_calibration/calibration.hpp" - -#include - -namespace ur_calibration -{ -Calibration::Calibration(const DHRobot& robot_parameters) : robot_parameters_(robot_parameters) -{ - buildChain(); -} - -Calibration::~Calibration() -{ -} - -void Calibration::correctChain() -{ - correctAxis(1); - correctAxis(2); -} - -void Calibration::correctAxis(const size_t link_index) -{ - // Each DH-Segment is split into two chain segments. One representing the d and theta parameters and - // one with the a and alpha parameters. If we start from the first segment (which represents d and - // theta), there follows a passive segment (with a and alpha) and the next d/theta-segment after - // that. - // - // In principle, the d parameter of the first segment gets set to zero, first. With this change, - // the kinematic structure gets destroyed, which has to be corrected: - // - With setting d to 0, both the start and end points of the passive segment move along the - // rotational axis of the start segment. Instead, the end point of the passive segment has to - // move along the rotational axis of the next segment. This creates a change in a and theta, if - // the two rotational axes are not parallel. - // - // - The length of moving along the next segment's rotational axis is calculated by intersecting - // the rotational axis with the XY-plane of the first segment. - // - auto& d_theta_segment = chain_[2 * link_index]; - auto& a_alpha_segment = chain_[2 * link_index + 1]; - - auto& d = d_theta_segment(2, 3); - auto& a = a_alpha_segment(0, 3); - - if (d == 0.0) { - // Nothing to do here. - return; - } - - // Start of the next joint's d_theta segment relative to the joint before the current one - Eigen::Matrix4d next_joint_root = Eigen::Matrix4d::Identity(); - next_joint_root *= d_theta_segment * a_alpha_segment; - - Eigen::Vector3d next_root_position = next_joint_root.topRightCorner(3, 1); - - const auto& next_d_theta_segment = chain_[(link_index + 1) * 2]; - Eigen::Vector3d next_d_theta_end = (next_joint_root * next_d_theta_segment).topRightCorner(3, 1); - - // Construct a representation of the next segment's rotational axis - Eigen::ParametrizedLine next_rotation_axis; - next_rotation_axis = Eigen::ParametrizedLine::Through(next_root_position, next_d_theta_end); - - RCLCPP_DEBUG_STREAM(rclcpp::get_logger("ur_calibration"), "next rotation axis:" << std::endl - << "Base:" << std::endl - << next_rotation_axis.origin() - << std::endl - << "Direction:" << std::endl - << next_rotation_axis.direction()); - - // XY-Plane of first segment's start - Eigen::Hyperplane plane(Eigen::Vector3d(0, 0, 1), Eigen::Vector3d::Zero()); - - // Intersect the rotation axis of the next joint with the XY-Plane. - // * The intersection_param is the length moving along the rotation axis until intersecting the plane. - // * The intersection point will be used for calculating the new angle theta. - double intersection_param = next_rotation_axis.intersectionParameter(plane); - Eigen::Vector3d intersection_point = next_rotation_axis.intersectionPoint(plane); - - // A non-zero a parameter will result in an intersection point at (a, 0) even without any - // additional rotations. This effect has to be subtracted from the resulting theta value. - double subtraction_angle = 0.0; - if (std::abs(a) > 0) { - // This is pi - subtraction_angle = atan(1) * 4; - } - double new_theta = std::atan2(intersection_point.y(), intersection_point.x()) - subtraction_angle; - // Upper and lower arm segments on URs all have negative length due to dh params - double new_link_length = -1 * intersection_point.norm(); - RCLCPP_DEBUG_STREAM(rclcpp::get_logger("ur_calibration"), "Next joint's rotation axis intersecting at " - << std::endl - << intersection_point); - RCLCPP_DEBUG_STREAM(rclcpp::get_logger("ur_calibration"), "Angle is " << new_theta); - RCLCPP_DEBUG_STREAM(rclcpp::get_logger("ur_calibration"), "Length is " << new_link_length); - RCLCPP_DEBUG_STREAM(rclcpp::get_logger("ur_calibration"), "Intersection param is " << intersection_param); - - // as we move the passive segment towards the first segment, we have to move away the next segment - // again, to keep the same kinematic structure. - double sign_dir = next_rotation_axis.direction().z() > 0 ? 1.0 : -1.0; - double distance_correction = intersection_param * sign_dir; - - // Set d parameter of the first segment to 0 and theta to the calculated new angle - // Correct arm segment length and angle - d = 0.0; - d_theta_segment.topLeftCorner(3, 3) = Eigen::AngleAxisd(new_theta, Eigen::Vector3d::UnitZ()).toRotationMatrix(); - - // Correct arm segment length and angle - a = new_link_length; - a_alpha_segment.topLeftCorner(3, 3) = - Eigen::AngleAxisd(robot_parameters_.segments_[link_index].theta_ - new_theta, Eigen::Vector3d::UnitZ()) - .toRotationMatrix() * - Eigen::AngleAxisd(robot_parameters_.segments_[link_index].alpha_, Eigen::Vector3d::UnitX()).toRotationMatrix(); - - // Correct next joint's d parameter - chain_[2 * link_index + 2](2, 3) -= distance_correction; -} - -Eigen::Matrix4d Calibration::calcForwardKinematics(const Eigen::Matrix& joint_values, - const size_t link_nr) -{ - // Currently ignore input and calculate for zero vector input - Eigen::Matrix4d output = Eigen::Matrix4d::Identity(); - - std::vector simplified_chain = getSimplified(); - for (size_t i = 0; i < link_nr; ++i) { - Eigen::Matrix4d rotation = Eigen::Matrix4d::Identity(); - rotation.topLeftCorner(3, 3) = Eigen::AngleAxisd(joint_values(i), Eigen::Vector3d::UnitZ()).toRotationMatrix(); - output *= simplified_chain[i] * rotation; - } - - return output; -} - -void Calibration::buildChain() -{ - chain_.clear(); - for (size_t i = 0; i < robot_parameters_.segments_.size(); ++i) { - Eigen::Matrix4d seg1_mat = Eigen::Matrix4d::Identity(); - seg1_mat.topLeftCorner(3, 3) = - Eigen::AngleAxisd(robot_parameters_.segments_[i].theta_, Eigen::Vector3d::UnitZ()).toRotationMatrix(); - seg1_mat(2, 3) = robot_parameters_.segments_[i].d_; - - chain_.push_back(seg1_mat); - - Eigen::Matrix4d seg2_mat = Eigen::Matrix4d::Identity(); - seg2_mat.topLeftCorner(3, 3) = - Eigen::AngleAxisd(robot_parameters_.segments_[i].alpha_, Eigen::Vector3d::UnitX()).toRotationMatrix(); - seg2_mat(0, 3) = robot_parameters_.segments_[i].a_; - - chain_.push_back(seg2_mat); - } -} - -std::vector Calibration::getSimplified() const -{ - std::vector simplified_chain; - simplified_chain.push_back(chain_[0]); - for (size_t i = 1; i < chain_.size() - 1; i += 2) { - simplified_chain.emplace_back(chain_[i] * chain_[i + 1]); - /* - Eigen::Matrix3d rot_a = chain_[i].topLeftCorner(3, 3); - Eigen::Vector3d rpy_a = rot_a.eulerAngles(0, 1, 2); - - Eigen::Matrix3d rot_b = chain_[i + 1].topLeftCorner(3, 3); - Eigen::Vector3d rpy_b = rot_b.eulerAngles(0, 1, 2); - - Eigen::Matrix3d rot = simplified_chain.back().topLeftCorner(3, 3); - Eigen::Vector3d rpy = rot.eulerAngles(0, 1, 2); - Eigen::Quaterniond quat(rot); - */ - } - simplified_chain.push_back(chain_.back()); - return simplified_chain; -} - -YAML::Node Calibration::toYaml() const -{ - YAML::Node node; - - std::vector chain = getSimplified(); - - for (std::size_t i = 0; i < link_names_.size(); ++i) { - YAML::Node link; - link["x"] = chain[i](0, 3); - link["y"] = chain[i](1, 3); - link["z"] = chain[i](2, 3); - Eigen::Matrix3d rot = chain[i].topLeftCorner(3, 3); - Eigen::Vector3d rpy = rot.eulerAngles(0, 1, 2); - link["roll"] = rpy[0]; - link["pitch"] = rpy[1]; - link["yaw"] = rpy[2]; - node["kinematics"][link_names_[i]] = link; - } - - return node; -} -} // namespace ur_calibration diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration_consumer.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration_consumer.cpp deleted file mode 100644 index 13ed188..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration_consumer.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// -- BEGIN LICENSE BLOCK ---------------------------------------------- -// Created on behalf of Universal Robots A/S -// Copyright 2019 FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -// -- END LICENSE BLOCK ------------------------------------------------ - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-05-28 - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \date 2021-09-22 - * - */ -//---------------------------------------------------------------------- - -#include "ur_calibration/calibration_consumer.hpp" - -#include - -namespace ur_calibration -{ -CalibrationConsumer::CalibrationConsumer() : calibrated_(false) -{ -} - -bool CalibrationConsumer::consume(std::shared_ptr product) -{ - auto kin_info = std::dynamic_pointer_cast(product); - if (kin_info != nullptr) { - RCLCPP_INFO(rclcpp::get_logger("ur_calibration"), "%s", product->toString().c_str()); - DHRobot my_robot; - for (size_t i = 0; i < kin_info->dh_a_.size(); ++i) { - my_robot.segments_.push_back( - DHSegment(kin_info->dh_d_[i], kin_info->dh_a_[i], kin_info->dh_theta_[i], kin_info->dh_alpha_[i])); - } - Calibration calibration(my_robot); - calibration.correctChain(); - - calibration_parameters_ = calibration.toYaml(); - calibration_parameters_["kinematics"]["hash"] = kin_info->toHash(); - calibrated_ = true; - } - return true; -} - -YAML::Node CalibrationConsumer::getCalibrationParameters() const -{ - if (!calibrated_) { - throw(std::runtime_error("Cannot get calibration, as no calibration data received yet")); - } - return calibration_parameters_; -} -} // namespace ur_calibration diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration_correction.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration_correction.cpp deleted file mode 100644 index 8b2a5fa..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/src/calibration_correction.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// -- BEGIN LICENSE BLOCK ---------------------------------------------- -// Created on behalf of Universal Robots A/S -// Copyright 2019 FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -// -- END LICENSE BLOCK ------------------------------------------------ - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-01-10 - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \date 2021-09-22 - * - */ -//---------------------------------------------------------------------- - -#include "ur_calibration/calibration_consumer.hpp" - -#include -#include -#include - -#include "ur_client_library/comm/parser.h" -#include "ur_client_library/comm/pipeline.h" -#include "ur_client_library/comm/producer.h" -#include "ur_client_library/comm/stream.h" -#include "ur_client_library/primary/package_header.h" -#include "ur_client_library/primary/primary_parser.h" - -#include "ur_robot_driver/urcl_log_handler.hpp" - -#include "ament_index_cpp/get_package_share_directory.hpp" -#include "rclcpp/exceptions/exceptions.hpp" -#include "sensor_msgs/msg/joint_state.hpp" -#include "tf2_ros/transform_listener.h" - -namespace fs = std::filesystem; - -using urcl::UrException; -using urcl::comm::INotifier; -using urcl::comm::Pipeline; -using urcl::comm::URProducer; -using urcl::comm::URStream; -using urcl::primary_interface::PrimaryPackage; -using urcl::primary_interface::PrimaryParser; -using urcl::primary_interface::UR_PRIMARY_PORT; - -using ur_calibration::CalibrationConsumer; - -class CalibrationCorrection : public rclcpp::Node -{ -public: - CalibrationCorrection() - : Node( - "ur_calibration", - rclcpp::NodeOptions().allow_undeclared_parameters(true).automatically_declare_parameters_from_overrides(true)) - { - std::string output_package_name; - - try { - // The robot's IP address - robot_ip_ = this->get_parameter("robot_ip").as_string(); - // The target file where the calibration data is written to - output_filename_ = this->get_parameter("output_filename").as_string(); - } catch (rclcpp::exceptions::ParameterNotDeclaredException& e) { - RCLCPP_FATAL_STREAM(this->get_logger(), e.what()); - exit(1); - } - } - - virtual ~CalibrationCorrection() = default; - - void run() - { - URStream stream(robot_ip_, UR_PRIMARY_PORT); - PrimaryParser parser; - URProducer prod(stream, parser); - CalibrationConsumer consumer; - - INotifier notifier; - - Pipeline pipeline(prod, &consumer, "Pipeline", notifier); - pipeline.run(); - while (!consumer.isCalibrated()) { - rclcpp::sleep_for(rclcpp::Duration::from_seconds(0.1).to_chrono()); - } - calibration_data_.reset(new YAML::Node); - *calibration_data_ = consumer.getCalibrationParameters(); - } - - bool writeCalibrationData() - { - if (calibration_data_ == nullptr) { - RCLCPP_ERROR_STREAM(this->get_logger(), "Calibration data not yet set."); - return false; - } - - fs::path out_path = fs::absolute(output_filename_); - - fs::path dst_path = out_path.parent_path(); - if (!fs::exists(dst_path)) { - RCLCPP_ERROR_STREAM(this->get_logger(), "Parent folder " << dst_path << " does not exist."); - return false; - } - RCLCPP_INFO_STREAM(this->get_logger(), "Writing calibration data to " << out_path); - if (fs::exists(output_filename_)) { - RCLCPP_WARN_STREAM(this->get_logger(), "Output file " << output_filename_ << " already exists. Overwriting."); - } - std::ofstream file(output_filename_); - if (file.is_open()) { - file << *calibration_data_; - } else { - RCLCPP_ERROR_STREAM(this->get_logger(), "Failed writing the file. Do you have the correct rights?"); - return false; - } - RCLCPP_INFO_STREAM(this->get_logger(), "Wrote output."); - - return true; - } - -private: - std::string robot_ip_; - std::string output_filename_; - std::shared_ptr calibration_data_; -}; - -int main(int argc, char* argv[]) -{ - rclcpp::init(argc, argv); - - ur_robot_driver::registerUrclLogHandler(); - - try { - auto calib_node = std::make_shared(); - calib_node->run(); - if (!calib_node->writeCalibrationData()) { - RCLCPP_ERROR_STREAM(calib_node->get_logger(), "Failed writing calibration data. See errors above for details."); - return -1; - } - RCLCPP_INFO_STREAM(calib_node->get_logger(), "Calibration correction done"); - } catch (const UrException& e) { - RCLCPP_ERROR_STREAM(rclcpp::get_logger("ur_calibration"), e.what()); - } catch (const std::exception& e) { - RCLCPP_ERROR_STREAM(rclcpp::get_logger("ur_calibration"), e.what()); - } - - rclcpp::shutdown(); - - return 0; -} diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/test/calibration_test.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/test/calibration_test.cpp deleted file mode 100644 index e5f0685..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_calibration/test/calibration_test.cpp +++ /dev/null @@ -1,248 +0,0 @@ -// -- BEGIN LICENSE BLOCK ---------------------------------------------- -// Created on behalf of Universal Robots A/S -// Copyright 2019 FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -// -- END LICENSE BLOCK ------------------------------------------------ - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-03-11 - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \date 2021-09-27 - * - */ -//---------------------------------------------------------------------- - -#include -#include -#include -#include - -using ur_calibration::Calibration; -using ur_calibration::DHRobot; -using ur_calibration::DHSegment; - -using CalibrationMat = Eigen::Matrix; - -namespace -{ -/* -bool isApproximately(const double val1, const double val2, const double precision) -{ - return std::abs(val1 - val2) < precision; -} -*/ -template -void doubleEqVec(const Eigen::Matrix vec1, const Eigen::Matrix vec2, - const double precision) -{ - for (size_t i = 0; i < dim_; ++i) { - EXPECT_NEAR(vec1[i], vec2[i], precision); - } -} - -} // namespace - -DHRobot model_from_dh(std::array d, std::array a, std::array theta, - std::array alpha) -{ - DHRobot robot; - for (size_t i = 0; i < 6; ++i) { - robot.segments_.emplace_back(DHSegment(d[i], a[i], theta[i], alpha[i])); - } - return robot; -} - -class CalibrationTest : public ::testing::TestWithParam> -{ -public: - void SetUp() - { - robot_models_.insert({ "ur10_ideal", model_from_dh({ 0.1273, 0, 0, 0.163941, 0.1157, 0.0922 }, // d - { 0, -0.612, -0.5723, 0, 0, 0 }, // a - { 0, 0, 0, 0, 0, 0 }, // theta - { pi / 2, 0, 0, pi / 2, -pi / 2, 0 } // alpha - ) }); - robot_models_.insert({ "ur10", model_from_dh({ 0.1273, 0, 0, 0.163941, 0.1157, 0.0922 }, // d - { 0, -0.612, -0.5723, 0, 0, 0 }, // a - { 0, 0, 0, 0, 0, 0 }, // theta - { pi_2, 0, 0, pi_2, -pi_2, 0 } // alpha - ) }); - robot_models_.insert({ "ur10e", model_from_dh({ 0.1807, 0, 0, 0.17415, 0.11985, 0.11655 }, // d - { 0, -0.6127, -0.57155, 0, 0, 0 }, // a - { 0, 0, 0, 0, 0, 0 }, // theta - { pi_2, 0, 0, pi_2, -pi_2, 0 } // alpha - ) }); - } - void TearDown() - { - Test::TearDown(); - } - -protected: - const double pi = std::atan(1) * 4; - const double pi_2 = 1.570796327; // This is what the simulated robot reports as pi/2 - std::map robot_models_; -}; - -TEST_F(CalibrationTest, ur10_fw_kinematics_ideal) -{ - const auto& my_robot = robot_models_["ur10_ideal"]; - Calibration calibration(my_robot); - - Eigen::Matrix jointvalues; - Eigen::Vector3d expected_position; - Eigen::Quaterniond expected_orientation; - { - jointvalues << 0, 0, 0, 0, 0, 0; - Eigen::Matrix4d fk = calibration.calcForwardKinematics(jointvalues); - EXPECT_DOUBLE_EQ(fk(0, 3), my_robot.segments_[1].a_ + my_robot.segments_[2].a_); - EXPECT_DOUBLE_EQ(fk(1, 3), -1 * (my_robot.segments_[3].d_ + my_robot.segments_[5].d_)); - EXPECT_DOUBLE_EQ(fk(2, 3), my_robot.segments_[0].d_ - my_robot.segments_[4].d_); - } - - { - jointvalues << M_PI_2, -M_PI_4, M_PI_2, -M_PI_4, 0, 0; - Eigen::Matrix4d fk = calibration.calcForwardKinematics(jointvalues); - - expected_position << my_robot.segments_[3].d_ + my_robot.segments_[5].d_, - my_robot.segments_[1].a_ / std::sqrt(2) + my_robot.segments_[2].a_ / std::sqrt(2), - my_robot.segments_[0].d_ - my_robot.segments_[1].a_ / std::sqrt(2) + my_robot.segments_[2].a_ / std::sqrt(2) - - my_robot.segments_[4].d_; - doubleEqVec(expected_position, fk.topRightCorner(3, 1), 1e-8); - } -} - -TEST_F(CalibrationTest, ur10_fw_kinematics_real) -{ - // This test compares a corrected ideal model against positions taken from a simulated robot. - const auto& my_robot = robot_models_["ur10"]; - Calibration calibration(my_robot); - - Eigen::Matrix jointvalues; - Eigen::Vector3d expected_position; - Eigen::Quaterniond expected_orientation; - { - jointvalues << -1.6007002035724084976209269370884, -1.7271001974688928726209269370884, - -2.2029998938189905288709269370884, -0.80799991289247685699592693708837, 1.59510004520416259765625, - -0.03099996248354131012092693708837; - expected_position << -0.179925914147547, -0.606869755162764, 0.230789102067257; - - Eigen::Matrix4d fk = calibration.calcForwardKinematics(jointvalues); - - doubleEqVec(expected_position, fk.topRightCorner(3, 1), 1e-15); - } - { - jointvalues << 1.32645022869110107421875, 2.426007747650146484375, 5.951572895050048828125, - 1.27409040927886962890625, -0.54105216661562138824592693708837, 0.122173048555850982666015625; - expected_position << 0.39922988003280424074148413637886, 0.59688365069340565405298093537567, - -0.6677819040276375961440180617501; - - Eigen::Matrix4d fk = calibration.calcForwardKinematics(jointvalues); - - doubleEqVec(expected_position, fk.topRightCorner(3, 1), 1e-15); - } - - TearDown(); -} - -TEST_P(CalibrationTest, calibration) -{ - // This test compares the forward kinematics of the model constructed from uncorrected - // parameters with the one from the corrected parameters. They are tested against random - // joint values and should be equal (in a numeric sense). - - auto [robot_model, robot_calibration] = GetParam(); - auto my_robot = robot_models_.at(robot_model); - Calibration calibration(my_robot); - - Eigen::Matrix jointvalues; - jointvalues << 0, 0, 0, 0, 0, 0; - - for (size_t i = 0; i < 1000; ++i) { - Calibration calibration(my_robot + robot_calibration); - jointvalues = 2 * pi * Eigen::Matrix::Random(); - Eigen::Matrix4d fk_orig = calibration.calcForwardKinematics(jointvalues); - Eigen::Matrix3d rot_mat_orig = fk_orig.topLeftCorner(3, 3); - Eigen::Quaterniond rot_orig(rot_mat_orig); - calibration.correctChain(); - Eigen::Matrix4d fk_corrected = calibration.calcForwardKinematics(jointvalues); - Eigen::Matrix3d rot_mat_corrected = fk_corrected.topLeftCorner(3, 3); - Eigen::Quaterniond rot_corrected(rot_mat_corrected); - double angle_error = std::abs(rot_orig.angularDistance(rot_corrected)); - EXPECT_NEAR(fk_orig(0, 3), fk_corrected(0, 3), 1e-12); - EXPECT_NEAR(fk_orig(1, 3), fk_corrected(1, 3), 1e-12); - EXPECT_NEAR(fk_orig(2, 3), fk_corrected(2, 3), 1e-12); - EXPECT_NEAR(angle_error, 0.0, 1e-12); - } -} - -// Tests are parametrized by both, the robot model (e.g. "ur10e") and the DH diff as they are stored on the robot -// controller's calibration file. -// The test will then assemble the DH parameters using the ones from the base model and the calibration. -INSTANTIATE_TEST_SUITE_P( - CalibrationTests, CalibrationTest, - ::testing::Values( - std::make_tuple("ur10", - model_from_dh({ 0.00065609212979853, 1.4442162376284788, 0.854147723854608, -2.2989425877563705, - -1.573498686836816e-05, 1.9072435590711256e-05 }, // d - { 4.6311376834935676e-05, -0.00012568315331862312, 0.00186216581161458, - 9.918593870679266e-05, 4.215462720453189e-06, 0 }, // a - { -7.290070070824746e-05, -0.01713897289704999, -0.03707159413492756, - 0.054279462160583214, 1.488984257025741e-07, 1.551499479707493e-05 }, // theta - { 0.000211987863869334, -0.0072553625957652995, -0.013483226769541364, - 0.0013495820227329425, -0.001263136163679901, 0 } // alpha - )), - - std::make_tuple("ur10e", - model_from_dh({ -0.000144894975118076141, 303.469135666158195, -309.88394307789747, - 6.41459904397394975, -4.48232900190081995e-05, -0.00087071402790364627 }, // d - { 0.000108651068627930392, 0.240324175250532346, 0.00180628180213493472, - 2.63076149165684402e-05, 3.96638632500012715e-06, 0 }, // a - { 1.59613525316931737e-07, -0.917209621528830232, 7.12936131346499469, - 0.0710299029424392298, -1.64258976526054923e-06, - 9.17286101034808787e-08 }, // theta - { -0.000444781952841921679, -0.00160215112531214153, 0.00631917793331091861, - -0.00165055247340828437, 0.000763682515545038854, 0 } // alpha - )), - std::make_tuple("ur10e", - model_from_dh({ -0.000160188285741325043, 439.140974079900957, -446.027059806332147, - 6.88618689642360149, -3.86588496858186748e-05, -0.00087908274257374186 }, // d - { 2.12234865571205891e-05, 0.632017132627700651, 0.00229833638891230319, - -4.61409023720933908e-05, -6.39280053471801522e-05, 0 }, // a - { -2.41478068943590252e-07, 1.60233952386694556, -1.68607190752171299, - 0.0837331147700118711, -1.01260355871157781e-07, - 3.91986209186123702e-08 }, // theta - { -0.000650246557584166496, 0.00139416666825590402, 0.00693818880325995126, - -0.000811641562390219562, 0.000411120504570705592, 0 } // alpha - )) - - )); diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/CHANGELOG.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/CHANGELOG.rst deleted file mode 100644 index dbd46a0..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/CHANGELOG.rst +++ /dev/null @@ -1,168 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ur_controllers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -2.5.2 (2025-01-21) ------------------- -* Update pre-commit the same as on the main branch (`#1232 `_) -* Contributors: Felix Exner - -2.5.1 (2024-12-21) ------------------- -* Add missing test dependencies for ur_controllers (backport of `#1215 `_) -* Contributors: mergify[bot] - -2.5.0 (2024-12-18) ------------------- -* Freedrive Controller (`#1114 `_) (`#1211 `_) -* Add force mode controller (`#1049 `_) (`#1193 `_) -* Update package maintainers (backport of `#1203 `_) -* Forward trajectory controller (backport of `#944 `_) -* [SJTC] Make scaling interface optional (`#1145 `_) (`#1172 `_) -* Contributors: mergify[bot] - -2.2.16 (2024-10-28) -------------------- -* Allow setting the analog output domain when setting an analog output (backport of `#1123 `_) -* Service to get software version of robot (backport of `#964 `_) -* Contributors: mergify[bot], Felix Enxer, Jacob Larsen - -2.2.15 (2024-07-26) -------------------- -* Updated scaled JTC to latest upstream updates (`#1067 `_) -* Contributors: Felix Exner (fexner) - -2.2.14 (2024-07-01) -------------------- - -2.2.13 (2024-06-17) -------------------- -* this simple fix should fix the goal time violated issue (backport of `#882 `_) -* Contributors: Lennart Nachtigall - -2.2.12 (2024-05-16) -------------------- -* Use latched publishing for robot_mode and safety_mode (`#991 `_) -* Contributors: Felix Exner - -2.2.11 (2024-04-08) -------------------- - -2.2.10 (2024-01-03) -------------------- -* Update JTC API (`#896 `_) -* Remove noisy controller log message (`#858 `_) -* Contributors: Felix Exner (fexner), mergify[bot], Robert Wilbrandt - -2.2.9 (2023-09-22) ------------------- -* Update sjtc to newest upstream API -* Contributors: Felix Exner - -2.2.8 (2023-06-26) ------------------- - -2.2.7 (2023-06-02) ------------------- -* added missing command interfaces into gpio controller (`#693 `_) (`#702 `_) -* Adding maximum retry counter in gpio controller (Multiarm part 3) - v2 (`#672 `_) (`#696 `_) -* Ported controllers to generate_parameters library and added prefix for controllers (Multiarm part 2) (`#594 `_) (`#695 `_) -* Introduce hand back control service (`#528 `_) (`#670 `_) -* Added services to set tool voltage and zero force torque sensor (`#466 `_) (`#582 `_) -* Contributors: mergify[bot], Mads Holm Peters, Lennart Nachtigall, livanov93 - -2.2.6 (2022-11-28) ------------------- -* Ros2 controllers 2.14 (`#547 `_) -* Contributors: Felix Exner - -2.2.5 (2022-11-19) ------------------- -* Revert "Adapt jtc controller params to new param api" - This reverts commit 65ac3679004fb0a622b00d334fa57056607dd23f. -* Contributors: Felix Exner - -2.2.4 (2022-10-07) ------------------- -* Adapt jtc controller params to new param api -* Contributors: Felix Exner - -2.2.3 (2022-07-27) ------------------- -* Adapt ros control api (`#448 `_) - * scaled jtc: Use get_interface_name instead of get_name - * Migrate from stopped controllers to inactive controllers - stopped controllers has been deprecated upstream -* Contributors: Felix Exner - -2.2.2 (2022-07-19) ------------------- -* Adapted to JTC interpolation method feature (`#439 `_) -* Made sure all past maintainers are listed as authors (`#429 `_) -* Contributors: Felix Exner - -2.2.1 (2022-06-27) ------------------- - -2.2.0 (2022-06-20) ------------------- -* Updated package maintainers -* Prepare for humble (`#394 `_) -* Update dependencies on all packages (`#391 `_) -* Update controllers' API (`#351 `_) -* Update binary dependencies (`#344 `_) -* Use upstream fts_broadcaster (`#304 `_) -* Update license to BSD-3-Clause (`#277 `_) -* Added controller stopper node (`#309 `_) -* Add missing dependency on angles and update formatting for linters. (`#283 `_) -* Payload service (`#238 `_) -* Integration tests improvement (`#206 `_) -* Add resend program service and enable headless mode (`#198 `_) -* Update controllers adding dt in to update as in ros2_control (`#171 `_) -* Update main branch with ros-controls changes (`#160 `_) -* Update CI configuration to support galactic and rolling (`#142 `_) -* Modify parameter declaration - approach equalization with ros-controls dependencies (`#152 `_) -* Moved registering publisher and service to on_active (`#151 `_) -* Correct formatting, include std::vector and update ros2_controller to master branch in repo file. -* Correct check for fixed has_trajectory_msg() - See: https://github.com/ros-controls/ros2_controllers/commit/32f089b3f3b53a817412c6bbce9046028786431e -* Update for changes to ros2_control and ros2_controllers - See: https://github.com/ros-controls/ros2_control/commit/156a3f6aaed319585a8a1fd445693e2e08c30ccd - and: https://github.com/ros-controls/ros2_controllers/commit/612f610c24d026a41abd2dd026902c672cf778c9#diff-5d3e18800b3a217b37b91036031bdb170f5183970f54d1f951bb12f2e4847706 -* Fix gpio controller (`#103 `_) -* Fixed speed slider service call (`#100 `_) -* Reintegrating missing ur_client_library dependency since the break the building process (`#97 `_) -* Setting speed slider with range of 0.0-1.0 and added warnings if range is exceeded (`#88 `_) -* Fix move to home bug (`#92 `_) -* Review CI by correcting the configurations (`#71 `_) -* Add support for gpios, update MoveIt and ros2_control launching (`#66 `_) -* Fix warning about deprecated controller_interface::return_type::SUCCESS (`#68 `_) -* Use GitHub Actions, use pre-commit formatting (`#56 `_) -* Scaled Joint Trajectory Controller (`#43 `_) -* Only load speed scaling interface -* Removed controller from config file to realign with current branch status -* Removed last remnants of joint_state_controller -* Added publisher rate -* Code formatting and cleanup -* Added publisher for speed scaling factor -* Initial version of the speed_scaling_state_controller -* Update licence. -* Fix clang tidy in multiple pkgs. -* Update force torque state controller. -* Prepare for testing. -* Update ft state controller with ros2_control changes. -* Remove lifecycle node (update with ros2_control changes). -* Claim individual resources. -* Add force torque controller. -* Claim individual resources. -* Add force torque controller. -* Add XML schema to all ``package.xml`` files - Better enable ``ament_xmllint`` to check validity. -* Update package.xml files so ``ros2 pkg list`` shows all pkgs -* Clean out ur_controllers, it needs a complete rewrite -* Update CMakeLists and package.xml for: - - ur5_moveit_config - - ur_bringup - - ur_description -* Change pkg versions to 0.0.0 -* Contributors: AndyZe, Denis Stogl, Denis Štogl, Felix Exner, John Morris, Kenneth Bogert, Lovro, Mads Holm Peters, Marvin Große Besselmann, livanov93 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/CMakeLists.txt b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/CMakeLists.txt deleted file mode 100644 index 79a89ac..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/CMakeLists.txt +++ /dev/null @@ -1,175 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(ur_controllers) - -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options(-Wall -Wextra) -endif() - -find_package(ament_cmake REQUIRED) -find_package(angles REQUIRED) -find_package(controller_interface REQUIRED) -find_package(geometry_msgs REQUIRED) -find_package(joint_trajectory_controller REQUIRED) -find_package(lifecycle_msgs REQUIRED) -find_package(pluginlib REQUIRED) -find_package(rclcpp_lifecycle REQUIRED) -find_package(rcutils REQUIRED) -find_package(realtime_tools REQUIRED) -find_package(std_msgs REQUIRED) -find_package(std_srvs REQUIRED) -find_package(tf2_geometry_msgs REQUIRED) -find_package(tf2_ros REQUIRED) -find_package(ur_dashboard_msgs REQUIRED) -find_package(ur_msgs REQUIRED) -find_package(generate_parameter_library REQUIRED) -find_package(trajectory_msgs REQUIRED) -find_package(control_msgs REQUIRED) -find_package(action_msgs REQUIRED) - - -set(THIS_PACKAGE_INCLUDE_DEPENDS - angles - controller_interface - geometry_msgs - joint_trajectory_controller - lifecycle_msgs - pluginlib - rclcpp_lifecycle - rcutils - realtime_tools - std_msgs - std_srvs - tf2_geometry_msgs - tf2_ros - ur_dashboard_msgs - ur_msgs - generate_parameter_library - control_msgs - trajectory_msgs - action_msgs -) - -include_directories(include) - -generate_parameter_library( - force_mode_controller_parameters - src/force_mode_controller_parameters.yaml -) - -generate_parameter_library( - gpio_controller_parameters - src/gpio_controller_parameters.yaml -) - -generate_parameter_library( - speed_scaling_state_broadcaster_parameters - src/speed_scaling_state_broadcaster_parameters.yaml -) - -generate_parameter_library( - scaled_joint_trajectory_controller_parameters - src/scaled_joint_trajectory_controller_parameters.yaml -) - -generate_parameter_library( - freedrive_mode_controller_parameters - src/freedrive_mode_controller_parameters.yaml -) - -generate_parameter_library( - passthrough_trajectory_controller_parameters - src/passthrough_trajectory_controller_parameters.yaml -) - -generate_parameter_library( - ur_configuration_controller_parameters - src/ur_configuration_controller_parameters.yaml -) - -add_library(${PROJECT_NAME} SHARED - src/force_mode_controller.cpp - src/scaled_joint_trajectory_controller.cpp - src/speed_scaling_state_broadcaster.cpp - src/freedrive_mode_controller.cpp - src/gpio_controller.cpp - src/passthrough_trajectory_controller.cpp - src/ur_configuration_controller.cpp) - -target_include_directories(${PROJECT_NAME} PRIVATE - include -) -target_link_libraries(${PROJECT_NAME} - force_mode_controller_parameters - gpio_controller_parameters - speed_scaling_state_broadcaster_parameters - scaled_joint_trajectory_controller_parameters - freedrive_mode_controller_parameters - passthrough_trajectory_controller_parameters - ur_configuration_controller_parameters -) -ament_target_dependencies(${PROJECT_NAME} - ${THIS_PACKAGE_INCLUDE_DEPENDS} -) - -target_compile_options(${PROJECT_NAME} PRIVATE -Wpedantic -Werror=return-type) - -# prevent pluginlib from using boost -target_compile_definitions(${PROJECT_NAME} PUBLIC "PLUGINLIB__DISABLE_BOOST_FUNCTIONS") -pluginlib_export_plugin_description_file(controller_interface controller_plugins.xml) - -install(TARGETS ${PROJECT_NAME} - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin -) - -install(DIRECTORY include/ - DESTINATION include -) - -install(FILES controller_plugins.xml - DESTINATION share/${PROJECT_NAME} -) - -ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS}) -ament_export_include_directories(include) -ament_export_libraries(${PROJECT_NAME}) - -ament_export_include_directories( - include -) - -ament_export_libraries( - ${PROJECT_NAME} -) - -if(BUILD_TESTING) - find_package(ament_cmake_gmock REQUIRED) - find_package(controller_manager REQUIRED) - find_package(hardware_interface REQUIRED) - find_package(ros2_control_test_assets REQUIRED) - - add_definitions(-DTEST_FILES_DIRECTORY="${CMAKE_CURRENT_SOURCE_DIR}/test") - ament_add_gmock(test_load_force_mode_controller - test/test_load_force_mode_controller.cpp - ) - target_link_libraries(test_load_force_mode_controller - ${PROJECT_NAME} - ) - ament_target_dependencies(test_load_force_mode_controller - controller_manager - ros2_control_test_assets - ) - ament_add_gmock(test_load_freedrive_mode_controller - test/test_load_freedrive_mode_controller.cpp - ) - target_link_libraries(test_load_freedrive_mode_controller - ${PROJECT_NAME} - ) - ament_target_dependencies(test_load_freedrive_mode_controller - controller_manager - ros2_control_test_assets - ) -endif() - -ament_package() diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/README.md b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/README.md deleted file mode 100644 index c6599dc..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# ur_controllers - -This package contains controllers and hardware interface for `ros_control` that are special to the UR -robot family. Currently this contains - - * A **speed_scaling_interface** to read the value of the current speed scaling into controllers. - * A **scaled_joint_command_interface** that provides access to joint values and commands in - combination with the speed scaling value. - * A **speed_scaling_state_controller** that publishes the current execution speed as reported by - the robot to a topic interface. Values are floating points between 0 and 1. - * A **scaled_joint_trajectory_controller** that is similar to the *joint_trajectory_controller*, - but it uses the speed scaling reported by the robot to reduce progress in the trajectory. - -## About this package -This package contains controllers not being available in the default `ros_control` set. They are -created to support more features offered by the UR robot family. Any of these controllers are -example implementations for certain features and are intended to be generalized and merged -into the default `ros_control` controller set at some future point. - -## Controller description -This packages offers a couple of specific controllers that will be explained in the following -sections. -### ur_controllers/SpeedScalingStateBroadcaster -This controller publishes the current actual execution speed as reported by the robot. Values are -floating points between 0 and 1. - -In the [`ur_robot_driver`](../ur_robot_driver) this is calculated by multiplying the two [RTDE](https://www.universal-robots.com/articles/ur/real-time-data-exchange-rtde-guide/) data -fields `speed_scaling` (which should be equal to the value shown by the speed slider position on the -teach pendant) and `target_speed_fraction` (Which is the fraction to which execution gets slowed -down by the controller). -### position_controllers/ScaledJointTrajectoryController and velocity_controllers/ScaledJointTrajectoryController -These controllers work similar to the well-known -[`joint_trajectory_controller`](http://wiki.ros.org/joint_trajectory_controller). - -However, they are extended to handle the robot's execution speed specifically. Because the default -`joint_trajectory_controller` would interpolate the trajectory with the configured time constraints (ie: always assume maximum velocity and acceleration supported by the robot), -this could lead to significant path deviation due to multiple reasons: - - The speed slider on the robot might not be at 100%, so motion commands sent from ROS would - effectively get scaled down resulting in a slower execution. - - The robot could scale down motions based on configured safety limits resulting in a slower motion - than expected and therefore not reaching the desired target in a control cycle. - - Motions might not be executed at all, e.g. because the robot is E-stopped or in a protective stop - - Motion commands sent to the robot might not be interpreted, e.g. because there is no - [`external_control`](https://github.com/UniversalRobots/Universal_Robots_ROS_Driver#prepare-the-robot) - program node running on the robot controller. - - The program interpreting motion commands could be paused. - -The following plot illustrates the problem: -![Trajectory execution with default trajectory controller](doc/traj_without_speed_scaling.png -"Trajectory execution with default trajectory controller") - -The graph shows a trajectory with one joint being moved to a target point and back to its starting -point. As the joint's speed is limited to a very low setting on the teach pendant, speed scaling -(black line) activates and limits the joint speed (green line). As a result, the target -trajectory (light blue) doesn't get executed by the robot, but instead the pink trajectory is executed. -The vertical distance between the light blue line and the pink line is the path error in each -control cycle. We can see that the path deviation gets above 300 degrees at some point and the -target point at -6 radians never gets reached. - -All of the cases mentioned above are addressed by the scaled trajectory versions. Trajectory execution -can be transparently scaled down using the speed slider on the teach pendant without leading to -additional path deviations. Pausing the program or hitting the E-stop effectively leads to -`speed_scaling` being 0 meaning the trajectory will not be continued until the program is continued. -This way, trajectory executions can be explicitly paused and continued. - -With the scaled version of the trajectory controller the example motion shown in the previous diagram becomes: -![Trajectory execution with scaled_joint_trajectory_controller](doc/traj_with_speed_scaling.png -"Trajectory execution with scaled_joint_trajectory_controller") - -The deviation between trajectory interpolation on the ROS side and actual robot execution stays minimal and the -robot reaches the intermediate setpoint instead of returning "too early" as in the example above. - -Under the hood this is implemented by proceeding the trajectory not by a full time step but only by -the fraction determined by the current speed scaling. If speed scaling is currently at 50% then -interpolation of the current control cycle will start half a time step after the beginning of the -previous control cycle. diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/controller_plugins.xml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/controller_plugins.xml deleted file mode 100644 index ec15809..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/controller_plugins.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - This controller publishes the readings of all available speed scaling factors. - - - - - Scaled position-based joint trajectory controller. - - - - - This controller publishes the Tool IO. - - - - - Controller to use UR's force_mode. - - - - - This controller handles the activation of the freedrive mode. - - - - - This controller forwards a joint-based trajectory to the robot controller for interpolation. - - - - - Controller used to get and change the configuration of the robot - - - diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/index.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/index.rst deleted file mode 100644 index f03632d..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/index.rst +++ /dev/null @@ -1,380 +0,0 @@ -ur_controllers -============== - -This package contains controllers and hardware interface for ``ros2_controllers`` that are special to the UR -robot family. Currently this contains: - - -* A **speed_scaling_state_broadcaster** that publishes the current execution speed as reported by - the robot to a topic interface. Values are floating points between 0 and 1. -* A **scaled_joint_trajectory_controller** that is similar to the *joint_trajectory_controller*\ , - but it uses the speed scaling reported to align progress of the trajectory between the robot and controller. -* A **io_and_status_controller** that allows setting I/O ports, controlling some UR-specific - functionality and publishes status information about the robot. - -About this package ------------------- - -This package contains controllers not being available in the default ``ros2_controllers`` set. They are -created to support more features offered by the UR robot family. Some of these controllers are -example implementations for certain features and are intended to be generalized and merged -into the default ``ros2_controllers`` controller set at some future point. - -Controller description ----------------------- - -This packages offers a couple of specific controllers that will be explained in the following -sections. - -.. _speed_scaling_state_broadcaster: - -ur_controllers/SpeedScalingStateBroadcaster -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This controller publishes the current actual execution speed as reported by the robot. Values are -floating points between 0 and 1. - -In the `ur_robot_driver -`_ -this is calculated by multiplying the two `RTDE -`_ data -fields ``speed_scaling`` (which should be equal to the value shown by the speed slider position on the -teach pendant) and ``target_speed_fraction`` (Which is the fraction to which execution gets slowed -down by the controller). - -.. _scaled_jtc: - -ur_controlers/ScaledJointTrajectoryController -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -These controllers work similar to the well-known -`joint_trajectory_controller `_. - -However, they are extended to handle the robot's execution speed specifically. Because the default -``joint_trajectory_controller`` would interpolate the trajectory with the configured time constraints (ie: always assume maximum velocity and acceleration supported by the robot), -this could lead to significant path deviation due to multiple reasons: - - -* The speed slider on the robot might not be at 100%, so motion commands sent from ROS would - effectively get scaled down resulting in a slower execution. -* The robot could scale down motions based on configured safety limits resulting in a slower motion - than expected and therefore not reaching the desired target in a control cycle. -* Motions might not be executed at all, e.g. because the robot is E-stopped or in a protective stop -* Motion commands sent to the robot might not be interpreted, e.g. because there is no - `external_control `_ - program node running on the robot controller. -* The program interpreting motion commands could be paused. - -The following plot illustrates the problem: - -.. image:: traj_without_speed_scaling.png - :target: traj_without_speed_scaling.png - :alt: Trajectory execution with default trajectory controller - - -The graph shows a trajectory with one joint being moved to a target point and back to its starting -point. As the joint's speed is limited to a very low setting on the teach pendant, speed scaling -(black line) activates and limits the joint speed (green line). As a result, the target -trajectory (light blue) doesn't get executed by the robot, but instead the pink trajectory is executed. -The vertical distance between the light blue line and the pink line is the path error in each -control cycle. We can see that the path deviation gets above 300 degrees at some point and the -target point at -6 radians never gets reached. - -All of the cases mentioned above are addressed by the scaled trajectory versions. Trajectory execution -can be transparently scaled down using the speed slider on the teach pendant without leading to -additional path deviations. Pausing the program or hitting the E-stop effectively leads to -``speed_scaling`` being 0 meaning the trajectory will not be continued until the program is continued. -This way, trajectory executions can be explicitly paused and continued. - -With the scaled version of the trajectory controller the example motion shown in the previous diagram becomes: - -.. image:: traj_with_speed_scaling.png - :target: traj_with_speed_scaling.png - :alt: Trajectory execution with scaled_joint_trajectory_controller - - -The deviation between trajectory interpolation on the ROS side and actual robot execution stays minimal and the -robot reaches the intermediate setpoint instead of returning "too early" as in the example above. - -Under the hood this is implemented by proceeding the trajectory not by a full time step but only by -the fraction determined by the current speed scaling. If speed scaling is currently at 50% then -interpolation of the current control cycle will start half a time step after the beginning of the -previous control cycle. - -.. _io_and_status_controller: - -ur_controllers/GPIOController -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This controller allows setting I/O ports, controlling some UR-specific functionality and publishes -status information about the robot. - -Published topics -"""""""""""""""" - -* ``~/io_states [ur_msgs/msg/IOStates]``: Status of all I/O ports -* ``~/robot_mode [ur_dashboard_msgs/msg/RobotMode]``: The current robot mode (e.g. ``POWER_OFF``, - ``IDLE``, ``RUNNING``) -* ``~/robot_program_running [std_msgs/msg/Bool]``: Publishes whether **the External Control - program** is running or not. If this is ``false`` no commands can be sent to the robot. -* ``~/safety_mode [ur_dashboard_msgs/msg/SafetyMode]``: The robot's current safety mode (e.g. - ``PROTECTIVE_STOP``, ``ROBOT_EMERGENCY_STOP``, ``NORMAL``) -* ``~/tool_data [ur_msgs/msg/ToolDataMsg]``: Information about the robot's tool configuration - -Advertised services -""""""""""""""""""" - -* ``~/hand_back_control [std_srvs/srv/Trigger]``: Calling this service will make the robot program - exit the *External Control* program node and continue with the rest of the program. -* ``~/resend_robot_program [std_srvs/srv/Trigger]``: When :ref:`headless_mode` is used, this - service can be used to restart the *External Control* program on the robot. -* ``~/set_io [ur_msgs/srv/SetIO]``: Set an output pin on the robot. -* ``~/set_analog_output [ur_msgs/srv/SetAnalogOutput]``: Set an analog output on the robot. This - also allows specifying the domain. -* ``~/set_payload [ur_msgs/srv/SetPayload]``: Change the robot's payload on-the-fly. -* ``~/set_speed_slider [ur_msgs/srv/SetSpeedSliderFraction]``: Set the value of the speed slider. -* ``~/zero_ftsensor [std_srvs/srv/Trigger]``: Zeroes the reported wrench of the force torque - sensor. - -.. _passthrough_trajectory_controller: - -ur_controllers/PassthroughTrajectoryController -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This controller uses a ``control_msgs/FollowJointTrajectory`` action but instead of interpolating -the trajectory on the ROS pc it forwards the complete trajectory to the robot controller for -interpolation and execution. This way, the realtime requirements for the control PC can be -massively decreased, since the robot always "knows" what to do next. That means that you should be -able to run a stable driver connection also without a real-time patched kernel. - -Interpolation depends on the robot controller's implementation, but in conjunction with the -ur_robot_driver it defaults to mimicking ros2_control's spline interpolation. So, any trajectory -planned e.g. with MoveIt! will be executed following the trajectory exactly. - -A trajectory sent to the controller's action server will be forwarded to the robot controller and -executed there. Once all setpoints are transferred to the robot, the controller goes into a waiting -state where it waits for the trajectory to be finished. While waiting, the controller tracks the -time spent on the trajectory to ensure the robot isn't stuck during execution. - -This controller also supports **speed scaling** such that and scaling down of the trajectory done -by the robot, for example due to safety settings on the robot or simply because a slower execution -is configured on the teach pendant. This will be considered, during execution monitoring, so the -controller basically tracks the scaled time instead of the real time. - -.. note:: - - When using this controller with the URSim simulator execution times can be slightly larger than - the expected time depending on the simulation host's resources. This effect will not be present - when using a real UR arm. - -.. note:: - - This controller can currently only be used with URSim or a real UR robot. Neither mock hardware - nor gazebo support this type of trajectory interfaces at the time being. - -Tolerances -"""""""""" - -Currently, the trajectory passthrough controller only supports goal tolerances and goal time -tolerances passed in the action directly. Please make sure that the tolerances are completely -filled with all joint names. - -A **goal time tolerance** of ``0.0`` means that no goal time tolerance is set and the action will -not fail when execution takes too long. - -Action interface / usage -"""""""""""""""""""""""" - -To use this controller, publish a goal to the ``~/follow_joint_trajectory`` action interface -similar to the `joint_trajectory_controller `_. - -Currently, the controller doesn't support replacing a running trajectory action. While a trajectory -is being executed, goals will be rejected until the action has finished. If you want to replace it, -first cancel the running action and then send a new one. - -Parameters -"""""""""" - -The trajectory passthrough controller uses the following parameters: - -+----------------------------------+--------------+----------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| Parameter name | Type | Default value | Description | -| | | | | -+----------------------------------+--------------+----------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| ``joints`` (required) | string_array | | Joint names to listen to | -+----------------------------------+--------------+----------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| ``state_interfaces`` (required) | string_array | | State interfaces provided by the hardware for all joints. Subset of ``["position", "velocity", "acceleration"]`` | -+----------------------------------+--------------+----------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| ``speed_scaling_interface_name`` | string | ``speed_scaling/speed_scaling_factor`` | Fully qualified name of the speed scaling interface name. | -+----------------------------------+--------------+----------------------------------------+------------------------------------------------------------------------------------------------------------------+ -| ``tf_prefix`` | string | | Urdf prefix of the corresponding arm | -+----------------------------------+--------------+----------------------------------------+------------------------------------------------------------------------------------------------------------------+ - -Interfaces -"""""""""" - -In order to use this, the hardware has to export a command interface for passthrough operations for each joint. It always has -to export position, velocity and acceleration interfaces in order to be able to project the full -JointTrajectory definition. This is why there are separate fields used, as for passthrough mode -accelerations might be relevant also for robots that don't support commanding accelerations -directly to their joints. - -.. code:: xml - - - - - - - - - - - - - - - - - - - - - - - - - -.. note:: - - The hardware component has to take care that the passthrough command interfaces cannot be - activated in parallel to the streaming command interfaces. - -Implementation details / dataflow -""""""""""""""""""""""""""""""""" - -* A trajectory passed to the controller will be sent to the hardware component one by one. -* The controller will send one setpoint and then wait for the hardware to acknowledge that it can - take a new setpoint. -* This happens until all setpoints have been transferred to the hardware. Then, the controller goes - into a waiting state where it monitors execution time and waits for the hardware to finish - execution. -* If execution takes longer than anticipated, a warning will be printed. -* If execution finished taking longer than expected (plus the goal time tolerance), the action will fail. -* When the hardware reports that execution has been aborted (The ``passthrough_trajectory_abort`` - command interface), the action will be aborted. -* When the action is preempted, execution on the hardware is preempted. - -.. _force_mode_controller: - -ur_controllers/ForceModeController -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This controller activates the robot's *Force Mode*. This allows direct force control running on the -robot control box. This controller basically interfaces the URScript function ``force_mode(...)``. - -Force mode can be combined with (and only with) the :ref:`passthrough trajectory controller -` in order to execute motions under a given force constraints. - -.. note:: - This is not an admittance controller, as given force constraints in a certain Cartesian - dimension will overwrite the motion commands in that dimension. E.g. when specifying a certain - force in the base frame's ``z`` direction, any motion resulting from the move command in the - base frame's ``z`` axis will not be executed. - -Parameters -"""""""""" - -+----------------------------------+--------+---------------+---------------------------------------------------------------------+ -| Parameter name | Type | Default value | Description | -| | | | | -+----------------------------------+--------+---------------+---------------------------------------------------------------------+ -| ``tf_prefix`` | string | | Urdf prefix of the corresponding arm | -+----------------------------------+--------+---------------+---------------------------------------------------------------------+ -| ``check_io_successful_retries`` | int | 10 | Amount of retries for checking if setting force_mode was successful | -+----------------------------------+--------+---------------+---------------------------------------------------------------------+ - -Service interface / usage -""""""""""""""""""""""""" - -The controller provides two services: One for activating force_mode and one for leaving it. To use -those services, the controller has to be in ``active`` state. - -* ``~/stop_force_mode [std_srvs/srv/Trigger]``: Stop force mode -* ``~/start_force_mode [ur_msgs/srv/SetForceMode]``: Start force mode - -In ``ur_msgs/srv/SetForceMode`` the fields have the following meanings: - -task_frame - All information (selection vector, wrench, limits, etc) will be considered to be relative - to that pose. The pose's frame_id can be anything that is transformable to the robot's - ``base`` frame. -selection_vector_ - 1 means that the robot will be compliant in the corresponding axis of the task frame. -wrench - The forces/torques the robot will apply to its environment. The robot adjusts its position - along/about compliant axis in order to achieve the specified force/torque. Values have no effect for non- - compliant axes. - Actual wrench applied may be lower than requested due to joint safety limits. -type - An integer [1;3] specifying how the robot interprets the force frame - - 1 - The force frame is transformed in a way such that its y-axis is aligned with a vector pointing - from the robot tcp towards the origin of the force frame. - 2 - The force frame is not transformed. - 3 - The force frame is transformed in a way such that its x-axis is the projection of the robot tcp - velocity vector onto the x-y plane of the force frame. -speed_limits - Maximum allowed tcp speed (relative to the task frame). This is **only relevant for axes marked as - compliant** in the selection_vector. -deviation_limits - For **non-compliant axes**, these values are the maximum allowed deviation along/about an axis - between the actual tcp position and the one set by the program. -damping_factor - Force mode damping factor. Sets the damping parameter in force mode. In range [0;1], default value is 0.025 - A value of 1 is full damping, so the robot will decelerate quickly if no force is present. A value of 0 - is no damping, here the robot will maintain the speed. -gain_scaling - Force mode gain scaling factor. Scales the gain in force mode. scaling parameter is in range [0;2], default is 0.5. - A value larger than 1 can make force mode unstable, e.g. in case of collisions or pushing against hard surfaces. - -.. _freedrive_mode_controller: - -ur_controllers/FreedriveModeController -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This controller activates the robot's *Freedrive Mode*, allowing to manually move the robot' joints. -This controller can't be combined with any other motion controller. - -Parameters -"""""""""" - -+----------------------+--------+---------------+---------------------------------------------------------------------------------------+ -| Parameter name | Type | Default value | Description | -| | | | | -+----------------------+--------+---------------+---------------------------------------------------------------------------------------+ -| ``tf_prefix`` | string | | Urdf prefix of the corresponding arm | -+----------------------+--------+---------------+---------------------------------------------------------------------------------------+ -| ``inactive_timeout`` | int | 1 | Time interval (in seconds) of message inactivity after which freedrive is deactivated | -+----------------------+--------+---------------+---------------------------------------------------------------------------------------+ - -Usage -""""" - -The controller provides the ``~/enable_freedrive_mode`` topic of type ``[std_msgs/msg/Bool]`` for handling activation and deactivation: - -* to start and keep freedrive active, you'll have to frequently publish a ``True`` msg on the indicated topic. - If no further messages are received by the controller within the ``inactive_timeout`` seconds, - freedrive mode will be deactivated. Hence, it is recommended to publish a ``True`` message at least every - ``inactive_timeout/2`` seconds. - - .. code-block:: - - ros2 topic pub --rate 2 /freedrive_mode_controller/enable_freedrive_mode std_msgs/msg/Bool "{data: true}" - -* to deactivate freedrive mode is enough to publish a ``False`` msg on the indicated topic or - to deactivate the controller or to stop publishing ``True`` on the enable topic and wait for the - controller timeout. diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/traj_with_speed_scaling.png b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/traj_with_speed_scaling.png deleted file mode 100644 index 0413c1a..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/traj_with_speed_scaling.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7c4ac7996ae86ec94c31b816d76437fb1194cc745b6847de63b01df46123a64c -size 104299 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/traj_without_speed_scaling.png b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/traj_without_speed_scaling.png deleted file mode 100644 index 3ec6d19..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/doc/traj_without_speed_scaling.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4795575b1c0f85d55ea2f81ac043ec7163c49b8a6aead11e52564970c07fd517 -size 107365 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/force_mode_controller.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/force_mode_controller.hpp deleted file mode 100644 index 9ae45a0..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/force_mode_controller.hpp +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2023, FZI Forschungszentrum Informatik, Created on behalf of Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2023-06-29 - */ -//---------------------------------------------------------------------- - -#pragma once -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "force_mode_controller_parameters.hpp" - -namespace ur_controllers -{ -enum CommandInterfaces -{ - FORCE_MODE_TASK_FRAME_X = 0u, - FORCE_MODE_TASK_FRAME_Y = 1, - FORCE_MODE_TASK_FRAME_Z = 2, - FORCE_MODE_TASK_FRAME_RX = 3, - FORCE_MODE_TASK_FRAME_RY = 4, - FORCE_MODE_TASK_FRAME_RZ = 5, - FORCE_MODE_SELECTION_VECTOR_X = 6, - FORCE_MODE_SELECTION_VECTOR_Y = 7, - FORCE_MODE_SELECTION_VECTOR_Z = 8, - FORCE_MODE_SELECTION_VECTOR_RX = 9, - FORCE_MODE_SELECTION_VECTOR_RY = 10, - FORCE_MODE_SELECTION_VECTOR_RZ = 11, - FORCE_MODE_WRENCH_X = 12, - FORCE_MODE_WRENCH_Y = 13, - FORCE_MODE_WRENCH_Z = 14, - FORCE_MODE_WRENCH_RX = 15, - FORCE_MODE_WRENCH_RY = 16, - FORCE_MODE_WRENCH_RZ = 17, - FORCE_MODE_TYPE = 18, - FORCE_MODE_LIMITS_X = 19, - FORCE_MODE_LIMITS_Y = 20, - FORCE_MODE_LIMITS_Z = 21, - FORCE_MODE_LIMITS_RX = 22, - FORCE_MODE_LIMITS_RY = 23, - FORCE_MODE_LIMITS_RZ = 24, - FORCE_MODE_ASYNC_SUCCESS = 25, - FORCE_MODE_DISABLE_CMD = 26, - FORCE_MODE_DAMPING = 27, - FORCE_MODE_GAIN_SCALING = 28, -}; -enum StateInterfaces -{ - INITIALIZED_FLAG = 0u, -}; - -struct ForceModeParameters -{ - std::array task_frame; - std::array selection_vec; - std::array limits; - geometry_msgs::msg::Wrench wrench; - double type; - double damping_factor; - double gain_scaling; -}; - -class ForceModeController : public controller_interface::ControllerInterface -{ -public: - controller_interface::InterfaceConfiguration command_interface_configuration() const override; - - controller_interface::InterfaceConfiguration state_interface_configuration() const override; - - controller_interface::return_type update(const rclcpp::Time& time, const rclcpp::Duration& period) override; - - CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_deactivate(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_init() override; - - CallbackReturn on_cleanup(const rclcpp_lifecycle::State& previous_state) override; - -private: - bool setForceMode(const ur_msgs::srv::SetForceMode::Request::SharedPtr req, - ur_msgs::srv::SetForceMode::Response::SharedPtr resp); - bool disableForceMode(const std_srvs::srv::Trigger::Request::SharedPtr req, - std_srvs::srv::Trigger::Response::SharedPtr resp); - rclcpp::Service::SharedPtr set_force_mode_srv_; - rclcpp::Service::SharedPtr disable_force_mode_srv_; - - std::unique_ptr tf_buffer_; - std::unique_ptr tf_listener_; - - std::shared_ptr param_listener_; - force_mode_controller::Params params_; - - realtime_tools::RealtimeBuffer force_mode_params_buffer_; - std::atomic force_mode_active_; - std::atomic change_requested_; - std::atomic async_state_; - - static constexpr double ASYNC_WAITING = 2.0; - /** - * @brief wait until a command interface isn't in state ASYNC_WAITING anymore or until the parameter maximum_retries - * have been reached - */ - bool waitForAsyncCommand(std::function get_value); -}; -} // namespace ur_controllers diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/freedrive_mode_controller.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/freedrive_mode_controller.hpp deleted file mode 100644 index 04e9080..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/freedrive_mode_controller.hpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2024, FZI Forschungszentrum Informatik, Created on behalf of Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Vincenzo Di Pentima dipentima@fzi.de - * \date 2024-09-26 - */ -//---------------------------------------------------------------------- -#ifndef UR_CONTROLLERS__FREEDRIVE_MODE_CONTROLLER_HPP_ -#define UR_CONTROLLERS__FREEDRIVE_MODE_CONTROLLER_HPP_ - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include "std_msgs/msg/bool.hpp" - -#include "freedrive_mode_controller_parameters.hpp" - -namespace ur_controllers -{ -enum CommandInterfaces -{ - FREEDRIVE_MODE_ASYNC_SUCCESS = 0u, - FREEDRIVE_MODE_ENABLE = 1, - FREEDRIVE_MODE_ABORT = 2, -}; - -using namespace std::chrono_literals; // NOLINT - -class FreedriveModeController : public controller_interface::ControllerInterface -{ -public: - controller_interface::InterfaceConfiguration command_interface_configuration() const override; - - controller_interface::InterfaceConfiguration state_interface_configuration() const override; - - // Change the input for the update function - controller_interface::return_type update(const rclcpp::Time& time, const rclcpp::Duration& period) override; - - CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_cleanup(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_deactivate(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_init() override; - -private: - // Command interfaces: optional is used only to avoid adding reference initialization - std::optional> async_success_command_interface_; - std::optional> enable_command_interface_; - std::optional> abort_command_interface_; - - std::shared_ptr> enable_freedrive_mode_sub_; - - rclcpp::TimerBase::SharedPtr freedrive_sub_timer_; ///< Timer to check for timeout on input - mutable std::chrono::seconds timeout_interval_; - void freedrive_cmd_callback(const std_msgs::msg::Bool::SharedPtr msg); - - std::shared_ptr freedrive_param_listener_; - freedrive_mode_controller::Params freedrive_params_; - - std::atomic freedrive_active_; - std::atomic change_requested_; - std::atomic async_state_; - std::atomic first_log_; - std::atomic timer_started_; - - void start_timer(); - void timeout_callback(); - - std::thread logging_thread_; - std::atomic logging_thread_running_; - std::atomic logging_requested_; - std::condition_variable logging_condition_; - std::mutex log_mutex_; - void log_task(); - void start_logging_thread(); - void stop_logging_thread(); - - static constexpr double ASYNC_WAITING = 2.0; - /** - * @brief wait until a command interface isn't in state ASYNC_WAITING anymore or until the parameter maximum_retries - * have been reached - */ - bool waitForAsyncCommand(std::function get_value); -}; -} // namespace ur_controllers -#endif // UR_CONTROLLERS__PASSTHROUGH_TRAJECTORY_CONTROLLER_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/gpio_controller.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/gpio_controller.hpp deleted file mode 100644 index b06b513..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/gpio_controller.hpp +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) 2021 PickNik LLC -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \date 2021-02-20 - * - */ -//---------------------------------------------------------------------- - -#ifndef UR_CONTROLLERS__GPIO_CONTROLLER_HPP_ -#define UR_CONTROLLERS__GPIO_CONTROLLER_HPP_ - -#include -#include -#include -#include - -#include "std_srvs/srv/trigger.hpp" - -#include "controller_interface/controller_interface.hpp" -#include "ur_msgs/msg/io_states.hpp" -#include "ur_msgs/msg/tool_data_msg.hpp" -#include "ur_dashboard_msgs/msg/robot_mode.hpp" -#include "ur_dashboard_msgs/msg/safety_mode.hpp" -#include "ur_msgs/srv/set_io.hpp" -#include "ur_msgs/srv/set_analog_output.hpp" -#include "ur_msgs/srv/set_speed_slider_fraction.hpp" -#include "ur_msgs/srv/set_payload.hpp" -#include "rclcpp/time.hpp" -#include "rclcpp/duration.hpp" -#include "std_msgs/msg/bool.hpp" -#include "gpio_controller_parameters.hpp" - -namespace ur_controllers -{ -enum CommandInterfaces -{ - DIGITAL_OUTPUTS_CMD = 0u, - ANALOG_OUTPUTS_CMD = 18, - TOOL_VOLTAGE_CMD = 20, - IO_ASYNC_SUCCESS = 21, - TARGET_SPEED_FRACTION_CMD = 22, - TARGET_SPEED_FRACTION_ASYNC_SUCCESS = 23, - RESEND_ROBOT_PROGRAM_CMD = 24, - RESEND_ROBOT_PROGRAM_ASYNC_SUCCESS = 25, - PAYLOAD_MASS = 26, - PAYLOAD_COG_X = 27, - PAYLOAD_COG_Y = 28, - PAYLOAD_COG_Z = 29, - PAYLOAD_ASYNC_SUCCESS = 30, - ZERO_FTSENSOR_CMD = 31, - ZERO_FTSENSOR_ASYNC_SUCCESS = 32, - HAND_BACK_CONTROL_CMD = 33, - HAND_BACK_CONTROL_ASYNC_SUCCESS = 34, - ANALOG_OUTPUTS_DOMAIN = 35, -}; - -enum StateInterfaces -{ - DIGITAL_OUTPUTS = 0u, - DIGITAL_INPUTS = 18, - ANALOG_OUTPUTS = 36, - ANALOG_INPUTS = 38, - ANALOG_IO_TYPES = 40, - TOOL_MODE = 44, - TOOL_OUTPUT_VOLTAGE = 45, - TOOL_OUTPUT_CURRENT = 46, - TOOL_TEMPERATURE = 47, - TOOL_ANALOG_INPUTS = 48, - TOOL_ANALOG_IO_TYPES = 50, - ROBOT_MODE = 52, - ROBOT_STATUS_BITS = 53, - SAFETY_MODE = 57, - SAFETY_STATUS_BITS = 58, - INITIALIZED_FLAG = 69, - PROGRAM_RUNNING = 70, -}; - -class GPIOController : public controller_interface::ControllerInterface -{ -public: - controller_interface::InterfaceConfiguration command_interface_configuration() const override; - - controller_interface::InterfaceConfiguration state_interface_configuration() const override; - - controller_interface::return_type update(const rclcpp::Time& time, const rclcpp::Duration& period) override; - - CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_deactivate(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_init() override; - -private: - bool setIO(ur_msgs::srv::SetIO::Request::SharedPtr req, ur_msgs::srv::SetIO::Response::SharedPtr resp); - - bool setAnalogOutput(ur_msgs::srv::SetAnalogOutput::Request::SharedPtr req, - ur_msgs::srv::SetAnalogOutput::Response::SharedPtr resp); - - bool setSpeedSlider(ur_msgs::srv::SetSpeedSliderFraction::Request::SharedPtr req, - ur_msgs::srv::SetSpeedSliderFraction::Response::SharedPtr resp); - - bool resendRobotProgram(std_srvs::srv::Trigger::Request::SharedPtr req, - std_srvs::srv::Trigger::Response::SharedPtr resp); - - bool handBackControl(std_srvs::srv::Trigger::Request::SharedPtr req, - std_srvs::srv::Trigger::Response::SharedPtr resp); - - bool setPayload(const ur_msgs::srv::SetPayload::Request::SharedPtr req, - ur_msgs::srv::SetPayload::Response::SharedPtr resp); - - bool zeroFTSensor(std_srvs::srv::Trigger::Request::SharedPtr req, std_srvs::srv::Trigger::Response::SharedPtr resp); - - void publishIO(); - - void publishToolData(); - - void publishRobotMode(); - - void publishSafetyMode(); - - void publishProgramRunning(); - -protected: - void initMsgs(); - - bool first_pass_; - - // internal commands - std::array standard_digital_output_cmd_; - std::array standard_analog_output_cmd_; - double target_speed_fraction_cmd_; - - // services - rclcpp::Service::SharedPtr resend_robot_program_srv_; - rclcpp::Service::SharedPtr hand_back_control_srv_; - rclcpp::Service::SharedPtr set_speed_slider_srv_; - rclcpp::Service::SharedPtr set_io_srv_; - rclcpp::Service::SharedPtr set_analog_output_srv_; - rclcpp::Service::SharedPtr set_payload_srv_; - rclcpp::Service::SharedPtr tare_sensor_srv_; - - std::shared_ptr> io_pub_; - std::shared_ptr> tool_data_pub_; - std::shared_ptr> robot_mode_pub_; - std::shared_ptr> safety_mode_pub_; - std::shared_ptr> program_state_pub_; - - ur_msgs::msg::IOStates io_msg_; - ur_msgs::msg::ToolDataMsg tool_data_msg_; - ur_dashboard_msgs::msg::RobotMode robot_mode_msg_; - ur_dashboard_msgs::msg::SafetyMode safety_mode_msg_; - std_msgs::msg::Bool program_running_msg_; - - // Parameters from ROS for gpio_controller - std::shared_ptr param_listener_; - gpio_controller::Params params_; - - static constexpr double ASYNC_WAITING = 2.0; - // TODO(anyone) publishers to add: tcp_pose_pub_ - // TODO(anyone) subscribers to add: script_command_sub_ - // TODO(anyone) service servers to add: resend_robot_program_srv_, deactivate_srv_, set_payload_srv_, tare_sensor_srv_ - - /** - * @brief wait until a command interface isn't in state ASYNC_WAITING anymore or until the parameter maximum_retries - * have been reached - */ - bool waitForAsyncCommand(std::function get_value); -}; -} // namespace ur_controllers - -#endif // UR_CONTROLLERS__GPIO_CONTROLLER_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/passthrough_trajectory_controller.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/passthrough_trajectory_controller.hpp deleted file mode 100644 index a3c91d1..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/passthrough_trajectory_controller.hpp +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2024, Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Jacob Larsen jala@universal-robots.com - * \date 2024-03-11 - * - * - * - * - */ -//---------------------------------------------------------------------- - -#ifndef UR_CONTROLLERS__PASSTHROUGH_TRAJECTORY_CONTROLLER_HPP_ -#define UR_CONTROLLERS__PASSTHROUGH_TRAJECTORY_CONTROLLER_HPP_ - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "passthrough_trajectory_controller_parameters.hpp" - -namespace ur_controllers -{ - -/* - * 0.0: No trajectory to forward, the controller is idling and ready to receive a new trajectory. - * 1.0: The controller has received and accepted a new trajectory. When the state is 1.0, the controller will write a - * point to the hardware interface. - * 2.0: The hardware interface will read the point written from the controller. The state will switch between 1.0 - * and 2.0 until all points have been read by the hardware interface. - * 3.0: The hardware interface has read all the points, and will now write all the points to the physical robot - * controller. - * 4.0: The robot is moving through the trajectory. - * 5.0: The robot finished executing the trajectory. - */ -const double TRANSFER_STATE_IDLE = 0.0; -const double TRANSFER_STATE_WAITING_FOR_POINT = 1.0; -const double TRANSFER_STATE_TRANSFERRING = 2.0; -const double TRANSFER_STATE_TRANSFER_DONE = 3.0; -const double TRANSFER_STATE_IN_MOTION = 4.0; -const double TRANSFER_STATE_DONE = 5.0; - -using namespace std::chrono_literals; // NOLINT - -class PassthroughTrajectoryController : public controller_interface::ControllerInterface -{ -public: - PassthroughTrajectoryController() = default; - ~PassthroughTrajectoryController() override = default; - - controller_interface::InterfaceConfiguration state_interface_configuration() const override; - - controller_interface::InterfaceConfiguration command_interface_configuration() const override; - - controller_interface::CallbackReturn on_init() override; - - controller_interface::CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override; - - controller_interface::CallbackReturn on_activate(const rclcpp_lifecycle::State& state) override; - - controller_interface::CallbackReturn on_deactivate(const rclcpp_lifecycle::State& state) override; - - controller_interface::return_type update(const rclcpp::Time& time, const rclcpp::Duration& period) override; - -private: - using FollowJTrajAction = control_msgs::action::FollowJointTrajectory; - using RealtimeGoalHandle = realtime_tools::RealtimeServerGoalHandle; - using RealtimeGoalHandlePtr = std::shared_ptr; - using RealtimeGoalHandleBuffer = realtime_tools::RealtimeBuffer; - - RealtimeGoalHandleBuffer rt_active_goal_; ///< Currently active action goal, if any. - rclcpp::TimerBase::SharedPtr goal_handle_timer_; ///< Timer to frequently check on the running goal - realtime_tools::RealtimeBuffer> joint_trajectory_mapping_; - - rclcpp::Duration action_monitor_period_ = rclcpp::Duration(50ms); - - /* Start an action server with an action called: /passthrough_trajectory_controller/forward_joint_trajectory. */ - void start_action_server(void); - - void end_goal(); - - bool check_goal_tolerance(); - - // Get a mapping between the trajectory's joint order and the internal one - std::unordered_map create_joint_mapping(const std::vector& joint_names) const; - - std::shared_ptr passthrough_param_listener_; - passthrough_trajectory_controller::Params passthrough_params_; - - rclcpp_action::Server::SharedPtr send_trajectory_action_server_; - - rclcpp_action::GoalResponse - goal_received_callback(const rclcpp_action::GoalUUID& uuid, - std::shared_ptr goal); - - rclcpp_action::CancelResponse goal_cancelled_callback( - const std::shared_ptr> goal_handle); - - void goal_accepted_callback( - std::shared_ptr> goal_handle); - - realtime_tools::RealtimeBuffer> joint_names_; - std::vector state_interface_types_; - - std::vector joint_state_interface_names_; - std::vector> joint_position_state_interface_; - std::vector> joint_velocity_state_interface_; - std::vector> joint_acceleration_state_interface_; - - bool check_goal_tolerances(std::shared_ptr goal); - bool check_goal_positions(std::shared_ptr goal); - bool check_goal_velocities(std::shared_ptr goal); - bool check_goal_accelerations(std::shared_ptr goal); - - trajectory_msgs::msg::JointTrajectory active_joint_traj_; - // std::vector path_tolerance_; - realtime_tools::RealtimeBuffer> goal_tolerance_; - realtime_tools::RealtimeBuffer goal_time_tolerance_{ rclcpp::Duration(0, 0) }; - - std::atomic current_index_; - std::atomic trajectory_active_; - rclcpp::Duration active_trajectory_elapsed_time_ = rclcpp::Duration::from_nanoseconds(0); - rclcpp::Duration max_trajectory_time_ = rclcpp::Duration::from_nanoseconds(0); - double scaling_factor_; - std::atomic number_of_joints_; - static constexpr double NO_VAL = std::numeric_limits::quiet_NaN(); - - std::optional> scaling_state_interface_; - std::optional> abort_command_interface_; - std::optional> transfer_command_interface_; - std::optional> time_from_start_command_interface_; - - rclcpp::Clock::SharedPtr clock_; -}; -} // namespace ur_controllers -#endif // UR_CONTROLLERS__PASSTHROUGH_TRAJECTORY_CONTROLLER_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/scaled_joint_trajectory_controller.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/scaled_joint_trajectory_controller.hpp deleted file mode 100644 index 58bd124..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/scaled_joint_trajectory_controller.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Marvin Große Besselmann grosse@fzi.de - * \date 2021-02-18 - * - */ -//---------------------------------------------------------------------- -#ifndef UR_CONTROLLERS__SCALED_JOINT_TRAJECTORY_CONTROLLER_HPP_ -#define UR_CONTROLLERS__SCALED_JOINT_TRAJECTORY_CONTROLLER_HPP_ - -#include -#include -#include "angles/angles.h" -#include "joint_trajectory_controller/joint_trajectory_controller.hpp" -#include "joint_trajectory_controller/trajectory.hpp" -#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp" -#include "rclcpp/time.hpp" -#include "rclcpp/duration.hpp" -#include "scaled_joint_trajectory_controller_parameters.hpp" - -namespace ur_controllers -{ -class ScaledJointTrajectoryController : public joint_trajectory_controller::JointTrajectoryController -{ -public: - ScaledJointTrajectoryController() = default; - ~ScaledJointTrajectoryController() override = default; - - controller_interface::InterfaceConfiguration state_interface_configuration() const override; - - controller_interface::CallbackReturn on_activate(const rclcpp_lifecycle::State& state) override; - - controller_interface::return_type update(const rclcpp::Time& time, const rclcpp::Duration& period) override; - - CallbackReturn on_init() override; - -protected: - struct TimeData - { - TimeData() : time(0.0), period(rclcpp::Duration::from_nanoseconds(0.0)), uptime(0.0) - { - } - rclcpp::Time time; - rclcpp::Duration period; - rclcpp::Time uptime; - }; - -private: - double scaling_factor_{ 1.0 }; - realtime_tools::RealtimeBuffer time_data_; - - std::optional> scaling_state_interface_ = - std::nullopt; - - std::shared_ptr scaled_param_listener_; - scaled_joint_trajectory_controller::Params scaled_params_; -}; -} // namespace ur_controllers - -#endif // UR_CONTROLLERS__SCALED_JOINT_TRAJECTORY_CONTROLLER_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/speed_scaling_state_broadcaster.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/speed_scaling_state_broadcaster.hpp deleted file mode 100644 index 4778f7c..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/speed_scaling_state_broadcaster.hpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Marvin Große Besselmann grosse@fzi.de - * \date 2021-02-10 - * - */ -//---------------------------------------------------------------------- - -#ifndef UR_CONTROLLERS__SPEED_SCALING_STATE_BROADCASTER_HPP_ -#define UR_CONTROLLERS__SPEED_SCALING_STATE_BROADCASTER_HPP_ - -#include -#include -#include - -#include "controller_interface/controller_interface.hpp" -#include "rclcpp_lifecycle/lifecycle_publisher.hpp" -#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp" -#include "rclcpp/time.hpp" -#include "rclcpp/duration.hpp" -#include "std_msgs/msg/float64.hpp" -#include "speed_scaling_state_broadcaster_parameters.hpp" - -namespace ur_controllers -{ -class SpeedScalingStateBroadcaster : public controller_interface::ControllerInterface -{ -public: - SpeedScalingStateBroadcaster(); - - controller_interface::InterfaceConfiguration command_interface_configuration() const override; - - controller_interface::InterfaceConfiguration state_interface_configuration() const override; - - controller_interface::return_type update(const rclcpp::Time& time, const rclcpp::Duration& period) override; - - controller_interface::CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override; - - controller_interface::CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override; - - controller_interface::CallbackReturn on_deactivate(const rclcpp_lifecycle::State& previous_state) override; - - controller_interface::CallbackReturn on_init() override; - -protected: - std::vector sensor_names_; - double publish_rate_; - - std::shared_ptr> speed_scaling_state_publisher_; - std_msgs::msg::Float64 speed_scaling_state_msg_; - - // Parameters from ROS for SpeedScalingStateBroadcaster - std::shared_ptr param_listener_; - speed_scaling_state_broadcaster::Params params_; -}; -} // namespace ur_controllers -#endif // UR_CONTROLLERS__SPEED_SCALING_STATE_BROADCASTER_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/ur_configuration_controller.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/ur_configuration_controller.hpp deleted file mode 100644 index 3775ee7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/include/ur_controllers/ur_configuration_controller.hpp +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2024, Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Jacob Larsen jala@universal-robots.com - * \date 2024-07-11 - * - * - * - * - */ -//---------------------------------------------------------------------- - -#ifndef UR_CONTROLLERS__UR_CONFIGURATION_CONTROLLER_HPP_ -#define UR_CONTROLLERS__UR_CONFIGURATION_CONTROLLER_HPP_ - -// TODO(fmauch): Currently, the realtime_box_best_effort doesn't include this -#include -#include // NOLINT - -#include - -#include - -#include "ur_msgs/srv/get_robot_software_version.hpp" -#include "ur_configuration_controller_parameters.hpp" - -namespace ur_controllers -{ - -// Struct to hold version information -struct VersionInformation -{ - uint32_t major = 0, minor = 0, build = 0, bugfix = 0; -}; - -// Enum for indexing into state interfaces. -enum StateInterfaces -{ - ROBOT_VERSION_MAJOR = 0, - ROBOT_VERSION_MINOR = 1, - ROBOT_VERSION_BUILD = 2, - ROBOT_VERSION_BUGFIX = 3, -}; - -class URConfigurationController : public controller_interface::ControllerInterface -{ -public: - controller_interface::InterfaceConfiguration command_interface_configuration() const override; - - controller_interface::InterfaceConfiguration state_interface_configuration() const override; - - controller_interface::return_type update(const rclcpp::Time& time, const rclcpp::Duration& period) override; - - CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_deactivate(const rclcpp_lifecycle::State& previous_state) override; - - CallbackReturn on_init() override; - -private: - realtime_tools::RealtimeBoxBestEffort> robot_software_version_{ - std::make_shared() - }; - - rclcpp::Service::SharedPtr get_robot_software_version_srv_; - - bool getRobotSoftwareVersion(ur_msgs::srv::GetRobotSoftwareVersion::Request::SharedPtr req, - ur_msgs::srv::GetRobotSoftwareVersion::Response::SharedPtr resp); - - std::shared_ptr param_listener_; - ur_configuration_controller::Params params_; -}; -} // namespace ur_controllers - -#endif // UR_CONTROLLERS__UR_CONFIGURATION_CONTROLLER_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/package.xml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/package.xml deleted file mode 100644 index 5ffeea8..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/package.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - ur_controllers - 2.5.2 - Provides controllers that use the speed scaling interface of Universal Robots. - - Felix Exner - Rune Søe-Knudsen - Universal Robots A/S - - BSD-3-Clause - - https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver/issues - https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver - - Denis Stogl - Marvin Große Besselmann - Lovro Ivanov - Andy Zelenak - Vincenzo Di Pentima - - ament_cmake - - angles - controller_interface - geometry_msgs - hardware_interface - joint_trajectory_controller - lifecycle_msgs - pluginlib - rclcpp_lifecycle - rcutils - realtime_tools - std_msgs - std_srvs - tf2_geometry_msgs - tf2_ros - ur_dashboard_msgs - ur_msgs - control_msgs - trajectory_msgs - action_msgs - - controller_manager - hardware_interface_testing - ros2_control_test_assets - - - ament_cmake - - diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/force_mode_controller.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/force_mode_controller.cpp deleted file mode 100644 index 3ecdd6c..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/force_mode_controller.cpp +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2023, FZI Forschungszentrum Informatik, Created on behalf of Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2023-06-29 - */ -//---------------------------------------------------------------------- - -#include -#include -#include -#include - -#include -namespace ur_controllers -{ -controller_interface::CallbackReturn ForceModeController::on_init() -{ - try { - // Create the parameter listener and get the parameters - param_listener_ = std::make_shared(get_node()); - params_ = param_listener_->get_params(); - } catch (const std::exception& e) { - fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what()); - return CallbackReturn::ERROR; - } - - return controller_interface::CallbackReturn::SUCCESS; -} -controller_interface::InterfaceConfiguration ForceModeController::command_interface_configuration() const -{ - controller_interface::InterfaceConfiguration config; - config.type = controller_interface::interface_configuration_type::INDIVIDUAL; - - const std::string tf_prefix = params_.tf_prefix; - RCLCPP_DEBUG(get_node()->get_logger(), "Configure UR force_mode controller with tf_prefix: %s", tf_prefix.c_str()); - - // Get all the command interfaces needed for force mode from the hardware interface - config.names.emplace_back(tf_prefix + "force_mode/task_frame_x"); - config.names.emplace_back(tf_prefix + "force_mode/task_frame_y"); - config.names.emplace_back(tf_prefix + "force_mode/task_frame_z"); - config.names.emplace_back(tf_prefix + "force_mode/task_frame_rx"); - config.names.emplace_back(tf_prefix + "force_mode/task_frame_ry"); - config.names.emplace_back(tf_prefix + "force_mode/task_frame_rz"); - config.names.emplace_back(tf_prefix + "force_mode/selection_vector_x"); - config.names.emplace_back(tf_prefix + "force_mode/selection_vector_y"); - config.names.emplace_back(tf_prefix + "force_mode/selection_vector_z"); - config.names.emplace_back(tf_prefix + "force_mode/selection_vector_rx"); - config.names.emplace_back(tf_prefix + "force_mode/selection_vector_ry"); - config.names.emplace_back(tf_prefix + "force_mode/selection_vector_rz"); - config.names.emplace_back(tf_prefix + "force_mode/wrench_x"); - config.names.emplace_back(tf_prefix + "force_mode/wrench_y"); - config.names.emplace_back(tf_prefix + "force_mode/wrench_z"); - config.names.emplace_back(tf_prefix + "force_mode/wrench_rx"); - config.names.emplace_back(tf_prefix + "force_mode/wrench_ry"); - config.names.emplace_back(tf_prefix + "force_mode/wrench_rz"); - config.names.emplace_back(tf_prefix + "force_mode/type"); - config.names.emplace_back(tf_prefix + "force_mode/limits_x"); - config.names.emplace_back(tf_prefix + "force_mode/limits_y"); - config.names.emplace_back(tf_prefix + "force_mode/limits_z"); - config.names.emplace_back(tf_prefix + "force_mode/limits_rx"); - config.names.emplace_back(tf_prefix + "force_mode/limits_ry"); - config.names.emplace_back(tf_prefix + "force_mode/limits_rz"); - config.names.emplace_back(tf_prefix + "force_mode/force_mode_async_success"); - config.names.emplace_back(tf_prefix + "force_mode/disable_cmd"); - config.names.emplace_back(tf_prefix + "force_mode/damping"); - config.names.emplace_back(tf_prefix + "force_mode/gain_scaling"); - - return config; -} - -controller_interface::InterfaceConfiguration ur_controllers::ForceModeController::state_interface_configuration() const -{ - controller_interface::InterfaceConfiguration config; - config.type = controller_interface::interface_configuration_type::INDIVIDUAL; - - const std::string tf_prefix = params_.tf_prefix; - // Get the state interface indicating whether the hardware interface has been initialized - config.names.emplace_back(tf_prefix + "system_interface/initialized"); - - return config; -} - -controller_interface::CallbackReturn -ur_controllers::ForceModeController::on_configure(const rclcpp_lifecycle::State& /*previous_state*/) -{ - const auto logger = get_node()->get_logger(); - - if (!param_listener_) { - RCLCPP_ERROR(get_node()->get_logger(), "Error encountered during configuration"); - return controller_interface::CallbackReturn::ERROR; - } - - // update the dynamic map parameters - param_listener_->refresh_dynamic_parameters(); - - // get parameters from the listener in case they were updated - params_ = param_listener_->get_params(); - - tf_buffer_ = std::make_unique(get_node()->get_clock()); - tf_listener_ = std::make_unique(*tf_buffer_); - - // Create the service server that will be used to start force mode - try { - set_force_mode_srv_ = get_node()->create_service( - "~/start_force_mode", - std::bind(&ForceModeController::setForceMode, this, std::placeholders::_1, std::placeholders::_2)); - disable_force_mode_srv_ = get_node()->create_service( - "~/stop_force_mode", - std::bind(&ForceModeController::disableForceMode, this, std::placeholders::_1, std::placeholders::_2)); - } catch (...) { - return LifecycleNodeInterface::CallbackReturn::ERROR; - } - - return LifecycleNodeInterface::CallbackReturn::SUCCESS; -} - -controller_interface::CallbackReturn -ur_controllers::ForceModeController::on_activate(const rclcpp_lifecycle::State& /*previous_state*/) -{ - change_requested_ = false; - force_mode_active_ = false; - async_state_ = std::numeric_limits::quiet_NaN(); - return LifecycleNodeInterface::CallbackReturn::SUCCESS; -} - -controller_interface::CallbackReturn -ur_controllers::ForceModeController::on_deactivate(const rclcpp_lifecycle::State& /*previous_state*/) -{ - // Stop force mode if this controller is deactivated. - command_interfaces_[CommandInterfaces::FORCE_MODE_DISABLE_CMD].set_value(1.0); - return LifecycleNodeInterface::CallbackReturn::SUCCESS; -} - -controller_interface::CallbackReturn -ur_controllers::ForceModeController::on_cleanup(const rclcpp_lifecycle::State& /*previous_state*/) -{ - set_force_mode_srv_.reset(); - disable_force_mode_srv_.reset(); - return CallbackReturn::SUCCESS; -} - -controller_interface::return_type ur_controllers::ForceModeController::update(const rclcpp::Time& /*time*/, - const rclcpp::Duration& /*period*/) -{ - async_state_ = command_interfaces_[CommandInterfaces::FORCE_MODE_ASYNC_SUCCESS].get_value(); - - // Publish state of force_mode? - if (change_requested_) { - if (force_mode_active_) { - const auto force_mode_parameters = force_mode_params_buffer_.readFromRT(); - command_interfaces_[CommandInterfaces::FORCE_MODE_TASK_FRAME_X].set_value(force_mode_parameters->task_frame[0]); - command_interfaces_[CommandInterfaces::FORCE_MODE_TASK_FRAME_Y].set_value(force_mode_parameters->task_frame[1]); - command_interfaces_[CommandInterfaces::FORCE_MODE_TASK_FRAME_Z].set_value(force_mode_parameters->task_frame[2]); - command_interfaces_[CommandInterfaces::FORCE_MODE_TASK_FRAME_RX].set_value(force_mode_parameters->task_frame[3]); - command_interfaces_[CommandInterfaces::FORCE_MODE_TASK_FRAME_RY].set_value(force_mode_parameters->task_frame[4]); - command_interfaces_[CommandInterfaces::FORCE_MODE_TASK_FRAME_RZ].set_value(force_mode_parameters->task_frame[5]); - - command_interfaces_[CommandInterfaces::FORCE_MODE_SELECTION_VECTOR_X].set_value( - force_mode_parameters->selection_vec[0]); - command_interfaces_[CommandInterfaces::FORCE_MODE_SELECTION_VECTOR_Y].set_value( - force_mode_parameters->selection_vec[1]); - command_interfaces_[CommandInterfaces::FORCE_MODE_SELECTION_VECTOR_Z].set_value( - force_mode_parameters->selection_vec[2]); - command_interfaces_[CommandInterfaces::FORCE_MODE_SELECTION_VECTOR_RX].set_value( - force_mode_parameters->selection_vec[3]); - command_interfaces_[CommandInterfaces::FORCE_MODE_SELECTION_VECTOR_RY].set_value( - force_mode_parameters->selection_vec[4]); - command_interfaces_[CommandInterfaces::FORCE_MODE_SELECTION_VECTOR_RZ].set_value( - force_mode_parameters->selection_vec[5]); - - command_interfaces_[CommandInterfaces::FORCE_MODE_WRENCH_X].set_value(force_mode_parameters->wrench.force.x); - command_interfaces_[CommandInterfaces::FORCE_MODE_WRENCH_Y].set_value(force_mode_parameters->wrench.force.y); - command_interfaces_[CommandInterfaces::FORCE_MODE_WRENCH_Z].set_value(force_mode_parameters->wrench.force.z); - command_interfaces_[CommandInterfaces::FORCE_MODE_WRENCH_RX].set_value(force_mode_parameters->wrench.torque.x); - command_interfaces_[CommandInterfaces::FORCE_MODE_WRENCH_RY].set_value(force_mode_parameters->wrench.torque.y); - command_interfaces_[CommandInterfaces::FORCE_MODE_WRENCH_RZ].set_value(force_mode_parameters->wrench.torque.z); - - command_interfaces_[CommandInterfaces::FORCE_MODE_LIMITS_X].set_value(force_mode_parameters->limits[0]); - command_interfaces_[CommandInterfaces::FORCE_MODE_LIMITS_Y].set_value(force_mode_parameters->limits[1]); - command_interfaces_[CommandInterfaces::FORCE_MODE_LIMITS_Z].set_value(force_mode_parameters->limits[2]); - command_interfaces_[CommandInterfaces::FORCE_MODE_LIMITS_RX].set_value(force_mode_parameters->limits[3]); - command_interfaces_[CommandInterfaces::FORCE_MODE_LIMITS_RY].set_value(force_mode_parameters->limits[4]); - command_interfaces_[CommandInterfaces::FORCE_MODE_LIMITS_RZ].set_value(force_mode_parameters->limits[5]); - - command_interfaces_[CommandInterfaces::FORCE_MODE_TYPE].set_value(force_mode_parameters->type); - command_interfaces_[CommandInterfaces::FORCE_MODE_DAMPING].set_value(force_mode_parameters->damping_factor); - command_interfaces_[CommandInterfaces::FORCE_MODE_GAIN_SCALING].set_value(force_mode_parameters->gain_scaling); - - // Signal that we are waiting for confirmation that force mode is activated - command_interfaces_[CommandInterfaces::FORCE_MODE_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - async_state_ = ASYNC_WAITING; - } else { - command_interfaces_[CommandInterfaces::FORCE_MODE_DISABLE_CMD].set_value(1.0); - command_interfaces_[CommandInterfaces::FORCE_MODE_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - async_state_ = ASYNC_WAITING; - } - change_requested_ = false; - } - - return controller_interface::return_type::OK; -} - -bool ForceModeController::setForceMode(const ur_msgs::srv::SetForceMode::Request::SharedPtr req, - ur_msgs::srv::SetForceMode::Response::SharedPtr resp) -{ - // Reject if controller is not active - if (get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) { - RCLCPP_ERROR(get_node()->get_logger(), "Can't accept new requests. Controller is not running."); - resp->success = false; - return false; - } - - ForceModeParameters force_mode_parameters; - - // transform task frame into base - const std::string tf_prefix = params_.tf_prefix; - if (std::abs(req->task_frame.pose.orientation.x) < 1e-6 && std::abs(req->task_frame.pose.orientation.y) < 1e-6 && - std::abs(req->task_frame.pose.orientation.z) < 1e-6 && std::abs(req->task_frame.pose.orientation.w) < 1e-6) { - RCLCPP_ERROR(get_node()->get_logger(), "Received task frame with all-zeros quaternion. It should have at least one " - "non-zero entry."); - resp->success = false; - return false; - } - - try { - auto task_frame_transformed = tf_buffer_->transform(req->task_frame, tf_prefix + "base"); - - force_mode_parameters.task_frame[0] = task_frame_transformed.pose.position.x; - force_mode_parameters.task_frame[1] = task_frame_transformed.pose.position.y; - force_mode_parameters.task_frame[2] = task_frame_transformed.pose.position.z; - - tf2::Quaternion quat_tf; - tf2::convert(task_frame_transformed.pose.orientation, quat_tf); - tf2::Matrix3x3 rot_mat(quat_tf); - rot_mat.getRPY(force_mode_parameters.task_frame[3], force_mode_parameters.task_frame[4], - force_mode_parameters.task_frame[5]); - } catch (const tf2::TransformException& ex) { - RCLCPP_ERROR(get_node()->get_logger(), "Could not transform %s to robot base: %s", - req->task_frame.header.frame_id.c_str(), ex.what()); - resp->success = false; - return false; - } - - // The selection vector dictates which axes the robot should be compliant along and around - force_mode_parameters.selection_vec[0] = req->selection_vector_x; - force_mode_parameters.selection_vec[1] = req->selection_vector_y; - force_mode_parameters.selection_vec[2] = req->selection_vector_z; - force_mode_parameters.selection_vec[3] = req->selection_vector_rx; - force_mode_parameters.selection_vec[4] = req->selection_vector_ry; - force_mode_parameters.selection_vec[5] = req->selection_vector_rz; - - // The wrench parameters dictate the amount of force/torque the robot will apply to its environment. The robot will - // move along/around compliant axes to match the specified force/torque. Has no effect for non-compliant axes. - force_mode_parameters.wrench = req->wrench; - - /* The limits specifies the maximum allowed speed along/around compliant axes. For non-compliant axes this value is - * the maximum allowed deviation between actual tcp position and the one that has been programmed. */ - force_mode_parameters.limits[0] = req->selection_vector_x ? req->speed_limits.linear.x : req->deviation_limits[0]; - force_mode_parameters.limits[1] = req->selection_vector_y ? req->speed_limits.linear.y : req->deviation_limits[1]; - force_mode_parameters.limits[2] = req->selection_vector_z ? req->speed_limits.linear.z : req->deviation_limits[2]; - force_mode_parameters.limits[3] = req->selection_vector_rx ? req->speed_limits.angular.x : req->deviation_limits[3]; - force_mode_parameters.limits[4] = req->selection_vector_ry ? req->speed_limits.angular.y : req->deviation_limits[4]; - force_mode_parameters.limits[5] = req->selection_vector_rz ? req->speed_limits.angular.z : req->deviation_limits[5]; - - if (req->type < 1 || req->type > 3) { - RCLCPP_ERROR(get_node()->get_logger(), "The force mode type has to be 1, 2, or 3. Received %u", req->type); - resp->success = false; - return false; - } - - /* The type decides how the robot interprets the force frame (the one defined in task_frame). See ur_script manual - * for explanation, under force_mode. */ - force_mode_parameters.type = static_cast(req->type); - - /* The damping factor decides how fast the robot decelarates if no force is present. 0 means no deceleration, 1 - * means quick deceleration*/ - if (req->damping_factor < 0.0 || req->damping_factor > 1.0) { - RCLCPP_ERROR(get_node()->get_logger(), "The damping factor has to be between 0 and 1. Received %f", - req->damping_factor); - resp->success = false; - return false; - } - force_mode_parameters.damping_factor = req->damping_factor; - - /*The gain scaling factor scales the force mode gain. A value larger than 1 may make force mode unstable. */ - if (req->gain_scaling < 0.0 || req->gain_scaling > 2.0) { - RCLCPP_ERROR(get_node()->get_logger(), "The gain scaling has to be between 0 and 2. Received %f", - req->gain_scaling); - resp->success = false; - return false; - } - if (req->gain_scaling > 1.0) { - RCLCPP_WARN(get_node()->get_logger(), - "A gain_scaling >1.0 can make force mode unstable, e.g. in case of collisions or pushing against " - "hard surfaces. Received %f", - req->gain_scaling); - } - force_mode_parameters.gain_scaling = req->gain_scaling; - - force_mode_params_buffer_.writeFromNonRT(force_mode_parameters); - force_mode_active_ = true; - change_requested_ = true; - - RCLCPP_DEBUG(get_node()->get_logger(), "Waiting for force mode to be set."); - const auto maximum_retries = params_.check_io_successful_retries; - int retries = 0; - while (async_state_ == ASYNC_WAITING || change_requested_) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - retries++; - - if (retries > maximum_retries) { - resp->success = false; - } - } - - resp->success = async_state_ == 1.0; - - if (resp->success) { - RCLCPP_INFO(get_node()->get_logger(), "Force mode has been set successfully."); - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Could not set the force mode."); - return false; - } - - return true; -} - -bool ForceModeController::disableForceMode(const std_srvs::srv::Trigger::Request::SharedPtr /*req*/, - std_srvs::srv::Trigger::Response::SharedPtr resp) -{ - force_mode_active_ = false; - change_requested_ = true; - RCLCPP_DEBUG(get_node()->get_logger(), "Waiting for force mode to be disabled."); - while (async_state_ == ASYNC_WAITING || change_requested_) { - // Asynchronous wait until the hardware interface has set the force mode - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - resp->success = async_state_ == 1.0; - if (resp->success) { - RCLCPP_INFO(get_node()->get_logger(), "Force mode has been disabled successfully."); - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Could not disable force mode."); - return false; - } - return true; -} -} // namespace ur_controllers - -#include "pluginlib/class_list_macros.hpp" - -PLUGINLIB_EXPORT_CLASS(ur_controllers::ForceModeController, controller_interface::ControllerInterface) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/force_mode_controller_parameters.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/force_mode_controller_parameters.yaml deleted file mode 100644 index 4eb69ef..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/force_mode_controller_parameters.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -force_mode_controller: - tf_prefix: { - type: string, - default_value: "", - description: "Urdf prefix of the corresponding arm" - } - check_io_successful_retries: { - type: int, - default_value: 10, - description: "Amount of retries for checking if setting force_mode was successful" - } diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/freedrive_mode_controller.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/freedrive_mode_controller.cpp deleted file mode 100644 index e1ae016..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/freedrive_mode_controller.cpp +++ /dev/null @@ -1,337 +0,0 @@ - -// Copyright 2024, FZI Forschungszentrum Informatik, Created on behalf of Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Vincenzo Di Pentima dipentima@fzi.de - * \date 2024-09-16 - */ -//---------------------------------------------------------------------- -#include -#include -#include -#include -#include - -namespace ur_controllers -{ -controller_interface::CallbackReturn FreedriveModeController::on_init() -{ - try { - // Create the parameter listener and get the parameters - freedrive_param_listener_ = std::make_shared(get_node()); - freedrive_params_ = freedrive_param_listener_->get_params(); - } catch (const std::exception& e) { - fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what()); - return CallbackReturn::ERROR; - } - - return controller_interface::CallbackReturn::SUCCESS; -} - -controller_interface::InterfaceConfiguration FreedriveModeController::command_interface_configuration() const -{ - controller_interface::InterfaceConfiguration config; - config.type = controller_interface::interface_configuration_type::INDIVIDUAL; - - const std::string tf_prefix = freedrive_params_.tf_prefix; - timeout_interval_ = std::chrono::seconds(freedrive_params_.inactive_timeout); - - // Get the command interfaces needed for freedrive mode from the hardware interface - config.names.emplace_back(tf_prefix + "freedrive_mode/async_success"); - config.names.emplace_back(tf_prefix + "freedrive_mode/enable"); - config.names.emplace_back(tf_prefix + "freedrive_mode/abort"); - - return config; -} - -controller_interface::InterfaceConfiguration -ur_controllers::FreedriveModeController::state_interface_configuration() const -{ - controller_interface::InterfaceConfiguration config; - config.type = controller_interface::interface_configuration_type::NONE; - - return config; -} - -controller_interface::CallbackReturn -ur_controllers::FreedriveModeController::on_configure(const rclcpp_lifecycle::State& previous_state) -{ - // Subscriber definition - enable_freedrive_mode_sub_ = get_node()->create_subscription( - "~/enable_freedrive_mode", 10, - std::bind(&FreedriveModeController::freedrive_cmd_callback, this, std::placeholders::_1)); - - timer_started_ = false; - - const auto logger = get_node()->get_logger(); - - if (!freedrive_param_listener_) { - RCLCPP_ERROR(get_node()->get_logger(), "Error encountered during configuration"); - return controller_interface::CallbackReturn::ERROR; - } - - // Update the dynamic map parameters - freedrive_param_listener_->refresh_dynamic_parameters(); - - // Get parameters from the listener in case they were updated - freedrive_params_ = freedrive_param_listener_->get_params(); - - start_logging_thread(); - - return ControllerInterface::on_configure(previous_state); -} - -controller_interface::CallbackReturn -ur_controllers::FreedriveModeController::on_activate(const rclcpp_lifecycle::State& state) -{ - change_requested_ = false; - freedrive_active_ = false; - async_state_ = std::numeric_limits::quiet_NaN(); - - first_log_ = false; - logging_thread_running_ = true; - logging_requested_ = false; - - { - const std::string interface_name = freedrive_params_.tf_prefix + "freedrive_mode/" - "async_success"; - auto it = std::find_if(command_interfaces_.begin(), command_interfaces_.end(), - [&](auto& interface) { return (interface.get_name() == interface_name); }); - if (it != command_interfaces_.end()) { - async_success_command_interface_ = *it; - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Did not find '%s' in command interfaces.", interface_name.c_str()); - return controller_interface::CallbackReturn::ERROR; - } - } - - { - const std::string interface_name = freedrive_params_.tf_prefix + "freedrive_mode/" - "enable"; - auto it = std::find_if(command_interfaces_.begin(), command_interfaces_.end(), - [&](auto& interface) { return (interface.get_name() == interface_name); }); - if (it != command_interfaces_.end()) { - enable_command_interface_ = *it; - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Did not find '%s' in command interfaces.", interface_name.c_str()); - return controller_interface::CallbackReturn::ERROR; - } - } - - { - const std::string interface_name = freedrive_params_.tf_prefix + "freedrive_mode/" - "abort"; - auto it = std::find_if(command_interfaces_.begin(), command_interfaces_.end(), - [&](auto& interface) { return (interface.get_name() == interface_name); }); - if (it != command_interfaces_.end()) { - abort_command_interface_ = *it; - abort_command_interface_->get().set_value(0.0); - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Did not find '%s' in command interfaces.", interface_name.c_str()); - return controller_interface::CallbackReturn::ERROR; - } - } - - return ControllerInterface::on_activate(state); -} - -controller_interface::CallbackReturn -ur_controllers::FreedriveModeController::on_cleanup(const rclcpp_lifecycle::State& /*previous_state*/) -{ - abort_command_interface_->get().set_value(1.0); - - stop_logging_thread(); - - return CallbackReturn::SUCCESS; -} - -controller_interface::CallbackReturn -ur_controllers::FreedriveModeController::on_deactivate(const rclcpp_lifecycle::State&) -{ - freedrive_active_ = false; - - freedrive_sub_timer_.reset(); - timer_started_ = false; - - return CallbackReturn::SUCCESS; -} - -controller_interface::return_type ur_controllers::FreedriveModeController::update(const rclcpp::Time& /*time*/, - const rclcpp::Duration& /*period*/) -{ - async_state_ = async_success_command_interface_->get().get_value(); - - if (change_requested_) { - if (freedrive_active_) { - // Check if the freedrive mode has been aborted from the hardware interface. E.g. the robot was stopped on the - // teach pendant. - if (!std::isnan(abort_command_interface_->get().get_value()) && - abort_command_interface_->get().get_value() == 1.0) { - RCLCPP_INFO(get_node()->get_logger(), "Freedrive mode aborted by hardware, aborting request."); - freedrive_active_ = false; - return controller_interface::return_type::OK; - } else { - RCLCPP_INFO(get_node()->get_logger(), "Received command to start Freedrive Mode."); - - // Set command interface to enable - enable_command_interface_->get().set_value(1.0); - - async_success_command_interface_->get().set_value(ASYNC_WAITING); - async_state_ = ASYNC_WAITING; - } - - } else { - RCLCPP_INFO(get_node()->get_logger(), "Received command to stop Freedrive Mode."); - - abort_command_interface_->get().set_value(1.0); - - async_success_command_interface_->get().set_value(ASYNC_WAITING); - async_state_ = ASYNC_WAITING; - } - first_log_ = true; - change_requested_ = false; - } - - if ((async_state_ == 1.0) && (first_log_)) { - first_log_ = false; - logging_requested_ = true; - - // Notify logging thread - logging_condition_.notify_one(); - } - return controller_interface::return_type::OK; -} - -void FreedriveModeController::freedrive_cmd_callback(const std_msgs::msg::Bool::SharedPtr msg) -{ - // Process the freedrive_mode command. - if (get_node()->get_current_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) { - if (msg->data) { - if ((!freedrive_active_) && (!change_requested_)) { - freedrive_active_ = true; - change_requested_ = true; - start_timer(); - } - } else { - if ((freedrive_active_) && (!change_requested_)) { - freedrive_active_ = false; - change_requested_ = true; - } - } - } - - if (freedrive_sub_timer_) { - freedrive_sub_timer_->reset(); - } -} - -void FreedriveModeController::start_timer() -{ - if (!timer_started_) { - // Start the timer only after the first message is received - freedrive_sub_timer_ = - get_node()->create_wall_timer(timeout_interval_, std::bind(&FreedriveModeController::timeout_callback, this)); - timer_started_ = true; - - RCLCPP_INFO(get_node()->get_logger(), "Timer started after receiving first command."); - } -} - -void FreedriveModeController::timeout_callback() -{ - if (timer_started_ && freedrive_active_) { - RCLCPP_INFO(get_node()->get_logger(), "Freedrive mode will be deactivated since no new message received."); - - freedrive_active_ = false; - change_requested_ = true; - } - - timer_started_ = false; -} - -void FreedriveModeController::start_logging_thread() -{ - if (!logging_thread_running_) { - logging_thread_running_ = true; - logging_thread_ = std::thread(&FreedriveModeController::log_task, this); - } -} - -void FreedriveModeController::stop_logging_thread() -{ - logging_thread_running_ = false; - if (logging_thread_.joinable()) { - logging_thread_.join(); - } -} - -void FreedriveModeController::log_task() -{ - while (logging_thread_running_) { - std::unique_lock lock(log_mutex_); - - auto condition = [this] { return !logging_thread_running_ || logging_requested_; }; - - // Wait for the condition - logging_condition_.wait(lock, condition); - - if (!logging_thread_running_) - break; - - if (freedrive_active_) { - RCLCPP_INFO(get_node()->get_logger(), "Freedrive mode has been enabled successfully."); - } else { - RCLCPP_INFO(get_node()->get_logger(), "Freedrive mode has been disabled successfully."); - } - - // Reset to log only once - logging_requested_ = false; - } -} - -bool FreedriveModeController::waitForAsyncCommand(std::function get_value) -{ - const auto maximum_retries = freedrive_params_.check_io_successful_retries; - int retries = 0; - while (get_value() == ASYNC_WAITING) { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - retries++; - - if (retries > maximum_retries) - return false; - } - return true; -} -} // namespace ur_controllers - -#include "pluginlib/class_list_macros.hpp" - -PLUGINLIB_EXPORT_CLASS(ur_controllers::FreedriveModeController, controller_interface::ControllerInterface) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/freedrive_mode_controller_parameters.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/freedrive_mode_controller_parameters.yaml deleted file mode 100644 index e50b422..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/freedrive_mode_controller_parameters.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -freedrive_mode_controller: - tf_prefix: { - type: string, - default_value: "", - description: "Urdf prefix of the corresponding arm" - } - inactive_timeout: { - type: int, - default_value: 1, - description: "Time interval (in seconds) of message inactivity after which freedrive is deactivated" - } - check_io_successful_retries: { - type: int, - default_value: 10, - description: "Amount of retries for checking if setting force_mode was successful" - } diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/gpio_controller.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/gpio_controller.cpp deleted file mode 100644 index 188542d..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/gpio_controller.cpp +++ /dev/null @@ -1,594 +0,0 @@ -// Copyright (c) 2021 PickNik LLC -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \date 2021-02-20 - * - */ -//---------------------------------------------------------------------- - -#include "ur_controllers/gpio_controller.hpp" - -#include - -namespace ur_controllers -{ -controller_interface::CallbackReturn GPIOController::on_init() -{ - try { - initMsgs(); - // Create the parameter listener and get the parameters - param_listener_ = std::make_shared(get_node()); - params_ = param_listener_->get_params(); - } catch (const std::exception& e) { - fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what()); - return CallbackReturn::ERROR; - } - - return controller_interface::CallbackReturn::SUCCESS; -} - -controller_interface::InterfaceConfiguration GPIOController::command_interface_configuration() const -{ - controller_interface::InterfaceConfiguration config; - config.type = controller_interface::interface_configuration_type::INDIVIDUAL; - - const std::string tf_prefix = params_.tf_prefix; - - for (size_t i = 0; i < 18; ++i) { - config.names.emplace_back(tf_prefix + "gpio/standard_digital_output_cmd_" + std::to_string(i)); - } - - for (size_t i = 0; i < 2; ++i) { - config.names.emplace_back(tf_prefix + "gpio/standard_analog_output_cmd_" + std::to_string(i)); - } - config.names.emplace_back(tf_prefix + "gpio/tool_voltage_cmd"); - - config.names.emplace_back(tf_prefix + "gpio/io_async_success"); - - config.names.emplace_back(tf_prefix + "speed_scaling/target_speed_fraction_cmd"); - - config.names.emplace_back(tf_prefix + "speed_scaling/target_speed_fraction_async_success"); - - config.names.emplace_back(tf_prefix + "resend_robot_program/resend_robot_program_cmd"); - - config.names.emplace_back(tf_prefix + "resend_robot_program/resend_robot_program_async_success"); - - // payload stuff - config.names.emplace_back(tf_prefix + "payload/mass"); - config.names.emplace_back(tf_prefix + "payload/cog.x"); - config.names.emplace_back(tf_prefix + "payload/cog.y"); - config.names.emplace_back(tf_prefix + "payload/cog.z"); - config.names.emplace_back(tf_prefix + "payload/payload_async_success"); - - // FTS sensor - config.names.emplace_back(tf_prefix + "zero_ftsensor/zero_ftsensor_cmd"); - config.names.emplace_back(tf_prefix + "zero_ftsensor/zero_ftsensor_async_success"); - - // hand back control --> make UR-program return - config.names.emplace_back(tf_prefix + "hand_back_control/hand_back_control_cmd"); - config.names.emplace_back(tf_prefix + "hand_back_control/hand_back_control_async_success"); - - config.names.emplace_back(tf_prefix + "gpio/analog_output_domain_cmd"); - - return config; -} - -controller_interface::InterfaceConfiguration ur_controllers::GPIOController::state_interface_configuration() const -{ - controller_interface::InterfaceConfiguration config; - config.type = controller_interface::interface_configuration_type::INDIVIDUAL; - - const std::string tf_prefix = params_.tf_prefix; - - // digital io - for (size_t i = 0; i < 18; ++i) { - config.names.emplace_back(tf_prefix + "gpio/digital_output_" + std::to_string(i)); - } - - for (size_t i = 0; i < 18; ++i) { - config.names.emplace_back(tf_prefix + "gpio/digital_input_" + std::to_string(i)); - } - - // analog io - for (size_t i = 0; i < 2; ++i) { - config.names.emplace_back(tf_prefix + "gpio/standard_analog_output_" + std::to_string(i)); - } - - for (size_t i = 0; i < 2; ++i) { - config.names.emplace_back(tf_prefix + "gpio/standard_analog_input_" + std::to_string(i)); - } - - for (size_t i = 0; i < 4; ++i) { - config.names.emplace_back(tf_prefix + "gpio/analog_io_type_" + std::to_string(i)); - } - - // tool - config.names.emplace_back(tf_prefix + "gpio/tool_mode"); - config.names.emplace_back(tf_prefix + "gpio/tool_output_voltage"); - config.names.emplace_back(tf_prefix + "gpio/tool_output_current"); - config.names.emplace_back(tf_prefix + "gpio/tool_temperature"); - - for (size_t i = 0; i < 2; ++i) { - config.names.emplace_back(tf_prefix + "gpio/tool_analog_input_" + std::to_string(i)); - } - for (size_t i = 0; i < 2; ++i) { - config.names.emplace_back(tf_prefix + "gpio/tool_analog_input_type_" + std::to_string(i)); - } - - // robot - config.names.emplace_back(tf_prefix + "gpio/robot_mode"); - for (size_t i = 0; i < 4; ++i) { - config.names.emplace_back(tf_prefix + "gpio/robot_status_bit_" + std::to_string(i)); - } - - // safety - config.names.emplace_back(tf_prefix + "gpio/safety_mode"); - for (size_t i = 0; i < 11; ++i) { - config.names.emplace_back(tf_prefix + "gpio/safety_status_bit_" + std::to_string(i)); - } - config.names.emplace_back(tf_prefix + "system_interface/initialized"); - - // program running - config.names.emplace_back(tf_prefix + "gpio/program_running"); - - return config; -} - -controller_interface::return_type ur_controllers::GPIOController::update(const rclcpp::Time& /*time*/, - const rclcpp::Duration& /*period*/) -{ - publishIO(); - publishToolData(); - publishRobotMode(); - publishSafetyMode(); - publishProgramRunning(); - return controller_interface::return_type::OK; -} - -controller_interface::CallbackReturn -ur_controllers::GPIOController::on_configure(const rclcpp_lifecycle::State& /*previous_state*/) -{ - const auto logger = get_node()->get_logger(); - - if (!param_listener_) { - RCLCPP_ERROR(get_node()->get_logger(), "Error encountered during init"); - return controller_interface::CallbackReturn::ERROR; - } - - // update the dynamic map parameters - param_listener_->refresh_dynamic_parameters(); - - // get parameters from the listener in case they were updated - params_ = param_listener_->get_params(); - - return LifecycleNodeInterface::CallbackReturn::SUCCESS; -} - -void GPIOController::publishIO() -{ - for (size_t i = 0; i < 18; ++i) { - io_msg_.digital_out_states[i].pin = i; - io_msg_.digital_out_states[i].state = static_cast(state_interfaces_[i].get_value()); - - io_msg_.digital_in_states[i].pin = i; - io_msg_.digital_in_states[i].state = - static_cast(state_interfaces_[i + StateInterfaces::DIGITAL_INPUTS].get_value()); - } - - for (size_t i = 0; i < 2; ++i) { - io_msg_.analog_in_states[i].pin = i; - io_msg_.analog_in_states[i].state = - static_cast(state_interfaces_[i + StateInterfaces::ANALOG_INPUTS].get_value()); - io_msg_.analog_in_states[i].domain = - static_cast(state_interfaces_[i + StateInterfaces::ANALOG_IO_TYPES].get_value()); - } - - for (size_t i = 0; i < 2; ++i) { - io_msg_.analog_out_states[i].pin = i; - io_msg_.analog_out_states[i].state = - static_cast(state_interfaces_[i + StateInterfaces::ANALOG_OUTPUTS].get_value()); - io_msg_.analog_out_states[i].domain = - static_cast(state_interfaces_[i + StateInterfaces::ANALOG_IO_TYPES + 2].get_value()); - } - - io_pub_->publish(io_msg_); -} - -void GPIOController::publishToolData() -{ - tool_data_msg_.tool_mode = static_cast(state_interfaces_[StateInterfaces::TOOL_MODE].get_value()); - tool_data_msg_.analog_input_range2 = - static_cast(state_interfaces_[StateInterfaces::TOOL_ANALOG_IO_TYPES].get_value()); - tool_data_msg_.analog_input_range3 = - static_cast(state_interfaces_[StateInterfaces::TOOL_ANALOG_IO_TYPES + 1].get_value()); - tool_data_msg_.analog_input2 = static_cast(state_interfaces_[StateInterfaces::TOOL_ANALOG_INPUTS].get_value()); - tool_data_msg_.analog_input3 = - static_cast(state_interfaces_[StateInterfaces::TOOL_ANALOG_INPUTS + 1].get_value()); - tool_data_msg_.tool_output_voltage = - static_cast(state_interfaces_[StateInterfaces::TOOL_OUTPUT_VOLTAGE].get_value()); - tool_data_msg_.tool_current = static_cast(state_interfaces_[StateInterfaces::TOOL_OUTPUT_CURRENT].get_value()); - tool_data_msg_.tool_temperature = - static_cast(state_interfaces_[StateInterfaces::TOOL_TEMPERATURE].get_value()); - tool_data_pub_->publish(tool_data_msg_); -} - -void GPIOController::publishRobotMode() -{ - auto robot_mode = static_cast(state_interfaces_[StateInterfaces::ROBOT_MODE].get_value()); - - if (robot_mode_msg_.mode != robot_mode) { - robot_mode_msg_.mode = robot_mode; - robot_mode_pub_->publish(robot_mode_msg_); - } -} - -void GPIOController::publishSafetyMode() -{ - auto safety_mode = static_cast(state_interfaces_[StateInterfaces::SAFETY_MODE].get_value()); - - if (safety_mode_msg_.mode != safety_mode) { - safety_mode_msg_.mode = safety_mode; - safety_mode_pub_->publish(safety_mode_msg_); - } -} - -void GPIOController::publishProgramRunning() -{ - auto program_running_value = static_cast(state_interfaces_[StateInterfaces::PROGRAM_RUNNING].get_value()); - bool program_running = program_running_value == 1.0 ? true : false; - if (program_running_msg_.data != program_running) { - program_running_msg_.data = program_running; - program_state_pub_->publish(program_running_msg_); - } -} - -controller_interface::CallbackReturn -ur_controllers::GPIOController::on_activate(const rclcpp_lifecycle::State& /*previous_state*/) -{ - while (state_interfaces_[StateInterfaces::INITIALIZED_FLAG].get_value() == 0.0) { - RCLCPP_INFO(get_node()->get_logger(), "Waiting for system interface to initialize..."); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - - try { - auto qos_latched = rclcpp::SystemDefaultsQoS(); - qos_latched.transient_local(); - // register publisher - io_pub_ = get_node()->create_publisher("~/io_states", rclcpp::SystemDefaultsQoS()); - - tool_data_pub_ = - get_node()->create_publisher("~/tool_data", rclcpp::SystemDefaultsQoS()); - - robot_mode_pub_ = get_node()->create_publisher("~/robot_mode", qos_latched); - - safety_mode_pub_ = get_node()->create_publisher("~/safety_mode", qos_latched); - - program_state_pub_ = get_node()->create_publisher("~/robot_program_running", qos_latched); - set_io_srv_ = get_node()->create_service( - "~/set_io", std::bind(&GPIOController::setIO, this, std::placeholders::_1, std::placeholders::_2)); - set_analog_output_srv_ = get_node()->create_service( - "~/set_analog_output", - std::bind(&GPIOController::setAnalogOutput, this, std::placeholders::_1, std::placeholders::_2)); - - set_speed_slider_srv_ = get_node()->create_service( - "~/set_speed_slider", - std::bind(&GPIOController::setSpeedSlider, this, std::placeholders::_1, std::placeholders::_2)); - - resend_robot_program_srv_ = get_node()->create_service( - "~/resend_robot_program", - std::bind(&GPIOController::resendRobotProgram, this, std::placeholders::_1, std::placeholders::_2)); - - hand_back_control_srv_ = get_node()->create_service( - "~/hand_back_control", - std::bind(&GPIOController::handBackControl, this, std::placeholders::_1, std::placeholders::_2)); - - set_payload_srv_ = get_node()->create_service( - "~/set_payload", std::bind(&GPIOController::setPayload, this, std::placeholders::_1, std::placeholders::_2)); - - tare_sensor_srv_ = get_node()->create_service( - "~/zero_ftsensor", - std::bind(&GPIOController::zeroFTSensor, this, std::placeholders::_1, std::placeholders::_2)); - } catch (...) { - return LifecycleNodeInterface::CallbackReturn::ERROR; - } - return LifecycleNodeInterface::CallbackReturn::SUCCESS; -} - -controller_interface::CallbackReturn -ur_controllers::GPIOController::on_deactivate(const rclcpp_lifecycle::State& /*previous_state*/) -{ - try { - // reset publisher - io_pub_.reset(); - tool_data_pub_.reset(); - robot_mode_pub_.reset(); - safety_mode_pub_.reset(); - program_state_pub_.reset(); - set_io_srv_.reset(); - set_speed_slider_srv_.reset(); - } catch (...) { - return LifecycleNodeInterface::CallbackReturn::ERROR; - } - return LifecycleNodeInterface::CallbackReturn::SUCCESS; -} - -bool GPIOController::setIO(ur_msgs::srv::SetIO::Request::SharedPtr req, ur_msgs::srv::SetIO::Response::SharedPtr resp) -{ - if (req->fun == req->FUN_SET_DIGITAL_OUT && req->pin >= 0 && req->pin <= 17) { - // io async success - command_interfaces_[CommandInterfaces::IO_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - command_interfaces_[req->pin].set_value(static_cast(req->state)); - - RCLCPP_INFO(get_node()->get_logger(), "Setting digital output '%d' to state: '%1.0f'.", req->pin, req->state); - - if (!waitForAsyncCommand([&]() { return command_interfaces_[CommandInterfaces::IO_ASYNC_SUCCESS].get_value(); })) { - RCLCPP_WARN(get_node()->get_logger(), "Could not verify that io was set. (This might happen when using the " - "mocked interface)"); - } - - resp->success = static_cast(command_interfaces_[IO_ASYNC_SUCCESS].get_value()); - return resp->success; - } else if (req->fun == req->FUN_SET_ANALOG_OUT && req->pin >= 0 && req->pin <= 2) { - // io async success - command_interfaces_[CommandInterfaces::IO_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - command_interfaces_[CommandInterfaces::ANALOG_OUTPUTS_CMD + req->pin].set_value(static_cast(req->state)); - - RCLCPP_INFO(get_node()->get_logger(), "Setting analog output '%d' to state: '%f'.", req->pin, req->state); - - if (!waitForAsyncCommand([&]() { return command_interfaces_[CommandInterfaces::IO_ASYNC_SUCCESS].get_value(); })) { - RCLCPP_WARN(get_node()->get_logger(), "Could not verify that io was set. (This might happen when using the " - "mocked interface)"); - } - - resp->success = static_cast(command_interfaces_[CommandInterfaces::IO_ASYNC_SUCCESS].get_value()); - return resp->success; - } else if (req->fun == req->FUN_SET_TOOL_VOLTAGE) { - command_interfaces_[CommandInterfaces::IO_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - command_interfaces_[CommandInterfaces::TOOL_VOLTAGE_CMD].set_value(static_cast(req->state)); - - RCLCPP_INFO(get_node()->get_logger(), "Setting tool voltage to: '%1.0f'.", req->state); - - if (!waitForAsyncCommand([&]() { return command_interfaces_[CommandInterfaces::IO_ASYNC_SUCCESS].get_value(); })) { - RCLCPP_WARN(get_node()->get_logger(), "Could not verify that io was set. (This might happen when using the " - "mocked interface)"); - } - - resp->success = static_cast(command_interfaces_[CommandInterfaces::IO_ASYNC_SUCCESS].get_value()); - return resp->success; - } else { - resp->success = false; - return false; - } -} - -bool GPIOController::setAnalogOutput(ur_msgs::srv::SetAnalogOutput::Request::SharedPtr req, - ur_msgs::srv::SetAnalogOutput::Response::SharedPtr resp) -{ - std::string domain_string = "UNKNOWN"; - switch (req->data.domain) { - case ur_msgs::msg::Analog::CURRENT: - domain_string = "CURRENT"; - break; - case ur_msgs::msg::Analog::VOLTAGE: - domain_string = "VOLTAGE"; - break; - default: - RCLCPP_ERROR(get_node()->get_logger(), "Domain must be either 0 (CURRENT) or 1 (VOLTAGE)"); - resp->success = false; - return false; - } - - if (req->data.pin < 0 || req->data.pin > 1) { - RCLCPP_ERROR(get_node()->get_logger(), "Invalid pin selected. Only pins 0 and 1 are allowed."); - resp->success = false; - return false; - } - - command_interfaces_[CommandInterfaces::IO_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - command_interfaces_[CommandInterfaces::ANALOG_OUTPUTS_CMD + req->data.pin].set_value( - static_cast(req->data.state)); - command_interfaces_[CommandInterfaces::ANALOG_OUTPUTS_DOMAIN].set_value(static_cast(req->data.domain)); - - RCLCPP_INFO(get_node()->get_logger(), "Setting analog output '%d' to state: '%f' in domain %s.", req->data.pin, - req->data.state, domain_string.c_str()); - - if (!waitForAsyncCommand([&]() { return command_interfaces_[CommandInterfaces::IO_ASYNC_SUCCESS].get_value(); })) { - RCLCPP_WARN(get_node()->get_logger(), "Could not verify that io was set. (This might happen when using the " - "mocked interface)"); - } - - resp->success = static_cast(command_interfaces_[IO_ASYNC_SUCCESS].get_value()); - return resp->success; -} - -bool GPIOController::setSpeedSlider(ur_msgs::srv::SetSpeedSliderFraction::Request::SharedPtr req, - ur_msgs::srv::SetSpeedSliderFraction::Response::SharedPtr resp) -{ - if (req->speed_slider_fraction >= 0.01 && req->speed_slider_fraction <= 1.0) { - RCLCPP_INFO(get_node()->get_logger(), "Setting speed slider to %.2f%%.", req->speed_slider_fraction * 100.0); - // reset success flag - command_interfaces_[CommandInterfaces::TARGET_SPEED_FRACTION_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - // set commanding value for speed slider - command_interfaces_[CommandInterfaces::TARGET_SPEED_FRACTION_CMD].set_value( - static_cast(req->speed_slider_fraction)); - - if (!waitForAsyncCommand([&]() { - return command_interfaces_[CommandInterfaces::TARGET_SPEED_FRACTION_ASYNC_SUCCESS].get_value(); - })) { - RCLCPP_WARN(get_node()->get_logger(), "Could not verify that target speed fraction was set. (This might happen " - "when using the mocked interface)"); - } - resp->success = - static_cast(command_interfaces_[CommandInterfaces::TARGET_SPEED_FRACTION_ASYNC_SUCCESS].get_value()); - } else { - RCLCPP_WARN(get_node()->get_logger(), "The desired speed slider fraction must be within range (0; 1.0]. Request " - "ignored."); - resp->success = false; - return false; - } - return true; -} - -bool GPIOController::resendRobotProgram(std_srvs::srv::Trigger::Request::SharedPtr /*req*/, - std_srvs::srv::Trigger::Response::SharedPtr resp) -{ - // reset success flag - command_interfaces_[CommandInterfaces::RESEND_ROBOT_PROGRAM_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - // call the service in the hardware - command_interfaces_[CommandInterfaces::RESEND_ROBOT_PROGRAM_CMD].set_value(1.0); - - if (!waitForAsyncCommand( - [&]() { return command_interfaces_[CommandInterfaces::RESEND_ROBOT_PROGRAM_ASYNC_SUCCESS].get_value(); })) { - RCLCPP_WARN(get_node()->get_logger(), "Could not verify that program was sent. (This might happen when using the " - "mocked interface)"); - } - resp->success = - static_cast(command_interfaces_[CommandInterfaces::RESEND_ROBOT_PROGRAM_ASYNC_SUCCESS].get_value()); - - if (resp->success) { - RCLCPP_INFO(get_node()->get_logger(), "Successfully resent robot program"); - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Could not resend robot program"); - return false; - } - - return true; -} - -bool GPIOController::handBackControl(std_srvs::srv::Trigger::Request::SharedPtr /*req*/, - std_srvs::srv::Trigger::Response::SharedPtr resp) -{ - // reset success flag - command_interfaces_[CommandInterfaces::HAND_BACK_CONTROL_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - // call the service in the hardware - command_interfaces_[CommandInterfaces::HAND_BACK_CONTROL_CMD].set_value(1.0); - - if (!waitForAsyncCommand( - [&]() { return command_interfaces_[CommandInterfaces::HAND_BACK_CONTROL_ASYNC_SUCCESS].get_value(); })) { - RCLCPP_WARN(get_node()->get_logger(), "Could not verify that hand_back_control was correctly triggered. (This " - "might happen when using the mocked interface)"); - } - resp->success = - static_cast(command_interfaces_[CommandInterfaces::HAND_BACK_CONTROL_ASYNC_SUCCESS].get_value()); - - if (resp->success) { - RCLCPP_INFO(get_node()->get_logger(), "Deactivated control"); - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Could not deactivate control"); - return false; - } - - return true; -} - -bool GPIOController::setPayload(const ur_msgs::srv::SetPayload::Request::SharedPtr req, - ur_msgs::srv::SetPayload::Response::SharedPtr resp) -{ - // reset success flag - command_interfaces_[CommandInterfaces::PAYLOAD_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - - command_interfaces_[CommandInterfaces::PAYLOAD_MASS].set_value(req->mass); - command_interfaces_[CommandInterfaces::PAYLOAD_COG_X].set_value(req->center_of_gravity.x); - command_interfaces_[CommandInterfaces::PAYLOAD_COG_Y].set_value(req->center_of_gravity.y); - command_interfaces_[CommandInterfaces::PAYLOAD_COG_Z].set_value(req->center_of_gravity.z); - - if (!waitForAsyncCommand( - [&]() { return command_interfaces_[CommandInterfaces::PAYLOAD_ASYNC_SUCCESS].get_value(); })) { - RCLCPP_WARN(get_node()->get_logger(), "Could not verify that payload was set. (This might happen when using the " - "mocked interface)"); - } - - resp->success = static_cast(command_interfaces_[CommandInterfaces::PAYLOAD_ASYNC_SUCCESS].get_value()); - - if (resp->success) { - RCLCPP_INFO(get_node()->get_logger(), "Payload has been set successfully"); - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Could not set the payload"); - return false; - } - - return true; -} - -bool GPIOController::zeroFTSensor(std_srvs::srv::Trigger::Request::SharedPtr /*req*/, - std_srvs::srv::Trigger::Response::SharedPtr resp) -{ - // reset success flag - command_interfaces_[CommandInterfaces::ZERO_FTSENSOR_ASYNC_SUCCESS].set_value(ASYNC_WAITING); - // call the service in the hardware - command_interfaces_[CommandInterfaces::ZERO_FTSENSOR_CMD].set_value(1.0); - - if (!waitForAsyncCommand( - [&]() { return command_interfaces_[CommandInterfaces::ZERO_FTSENSOR_ASYNC_SUCCESS].get_value(); })) { - RCLCPP_WARN(get_node()->get_logger(), "Could not verify that FTS was zeroed. (This might happen when using the " - "mocked interface)"); - } - - resp->success = static_cast(command_interfaces_[CommandInterfaces::ZERO_FTSENSOR_ASYNC_SUCCESS].get_value()); - - if (resp->success) { - RCLCPP_INFO(get_node()->get_logger(), "Successfully zeroed the force torque sensor"); - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Could not zero the force torque sensor"); - return false; - } - - return true; -} - -void GPIOController::initMsgs() -{ - io_msg_.digital_in_states.resize(standard_digital_output_cmd_.size()); - io_msg_.digital_out_states.resize(standard_digital_output_cmd_.size()); - io_msg_.analog_in_states.resize(2); - io_msg_.analog_out_states.resize(2); -} - -bool GPIOController::waitForAsyncCommand(std::function get_value) -{ - const auto maximum_retries = params_.check_io_successfull_retries; - int retries = 0; - while (get_value() == ASYNC_WAITING) { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - retries++; - - if (retries > maximum_retries) - return false; - } - return true; -} - -} // namespace ur_controllers - -#include "pluginlib/class_list_macros.hpp" - -PLUGINLIB_EXPORT_CLASS(ur_controllers::GPIOController, controller_interface::ControllerInterface) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/gpio_controller_parameters.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/gpio_controller_parameters.yaml deleted file mode 100644 index 718f6ec..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/gpio_controller_parameters.yaml +++ /dev/null @@ -1,11 +0,0 @@ -gpio_controller: - tf_prefix: { - type: string, - default_value: "", - description: "Urdf prefix of the corresponding arm" - } - check_io_successfull_retries: { - type: int, - default_value: 10, - description: "Amount of retries for checking if the selected gpio was set successfully" - } diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/passthrough_trajectory_controller.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/passthrough_trajectory_controller.cpp deleted file mode 100644 index 95f7266..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/passthrough_trajectory_controller.cpp +++ /dev/null @@ -1,624 +0,0 @@ -// Copyright 2024, Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Jacob Larsen jala@universal-robots.com - * \date 2024-03-11 - * - * - * - * - */ -//---------------------------------------------------------------------- - -#include -#include -#include - -#include -#include -#include -#include - -#include "ur_controllers/passthrough_trajectory_controller.hpp" - -namespace ur_controllers -{ - -double duration_to_double(const builtin_interfaces::msg::Duration& duration) -{ - return duration.sec + (duration.nanosec / 1000000000.0); -} - -controller_interface::CallbackReturn PassthroughTrajectoryController::on_init() -{ - passthrough_param_listener_ = std::make_shared(get_node()); - passthrough_params_ = passthrough_param_listener_->get_params(); - current_index_ = 0; - auto joint_names = passthrough_params_.joints; - joint_names_.writeFromNonRT(joint_names); - number_of_joints_ = joint_names.size(); - state_interface_types_ = passthrough_params_.state_interfaces; - scaling_factor_ = 1.0; - clock_ = get_node()->get_clock(); - return controller_interface::CallbackReturn::SUCCESS; -} - -controller_interface::CallbackReturn -PassthroughTrajectoryController::on_configure(const rclcpp_lifecycle::State& previous_state) -{ - start_action_server(); - trajectory_active_ = false; - - joint_state_interface_names_.clear(); - - joint_state_interface_names_.reserve(number_of_joints_ * state_interface_types_.size()); - - auto joint_names_internal = joint_names_.readFromRT(); - for (const auto& joint_name : *joint_names_internal) { - for (const auto& interface_type : state_interface_types_) { - joint_state_interface_names_.emplace_back(joint_name + "/" + interface_type); - } - } - - return ControllerInterface::on_configure(previous_state); -} - -void PassthroughTrajectoryController::start_action_server(void) -{ - send_trajectory_action_server_ = rclcpp_action::create_server( - get_node(), std::string(get_node()->get_name()) + "/follow_joint_trajectory", - std::bind(&PassthroughTrajectoryController::goal_received_callback, this, std::placeholders::_1, - std::placeholders::_2), - std::bind(&PassthroughTrajectoryController::goal_cancelled_callback, this, std::placeholders::_1), - std::bind(&PassthroughTrajectoryController::goal_accepted_callback, this, std::placeholders::_1)); - return; -} - -controller_interface::InterfaceConfiguration PassthroughTrajectoryController::state_interface_configuration() const -{ - controller_interface::InterfaceConfiguration conf; - conf.type = controller_interface::interface_configuration_type::INDIVIDUAL; - - std::copy(joint_state_interface_names_.cbegin(), joint_state_interface_names_.cend(), std::back_inserter(conf.names)); - - conf.names.push_back(passthrough_params_.speed_scaling_interface_name); - - return conf; -} - -controller_interface::InterfaceConfiguration PassthroughTrajectoryController::command_interface_configuration() const -{ - controller_interface::InterfaceConfiguration config; - config.type = controller_interface::interface_configuration_type::INDIVIDUAL; - - const std::string tf_prefix = passthrough_params_.tf_prefix; - - for (size_t i = 0; i < number_of_joints_; ++i) { - config.names.emplace_back(tf_prefix + "trajectory_passthrough/setpoint_positions_" + std::to_string(i)); - config.names.emplace_back(tf_prefix + "trajectory_passthrough/setpoint_velocities_" + std::to_string(i)); - config.names.emplace_back(tf_prefix + "trajectory_passthrough/setpoint_accelerations_" + std::to_string(i)); - } - - config.names.push_back(tf_prefix + "trajectory_passthrough/abort"); - config.names.emplace_back(tf_prefix + "trajectory_passthrough/transfer_state"); - config.names.emplace_back(tf_prefix + "trajectory_passthrough/time_from_start"); - - return config; -} - -controller_interface::CallbackReturn PassthroughTrajectoryController::on_activate(const rclcpp_lifecycle::State& state) -{ - // clear out vectors in case of restart - joint_position_state_interface_.clear(); - joint_velocity_state_interface_.clear(); - joint_acceleration_state_interface_.clear(); - - for (auto& interface_name : joint_state_interface_names_) { - auto interface_it = std::find_if(state_interfaces_.begin(), state_interfaces_.end(), - [&](auto& interface) { return (interface.get_name() == interface_name); }); - if (interface_it != state_interfaces_.end()) { - if (interface_it->get_interface_name() == "position") { - joint_position_state_interface_.emplace_back(*interface_it); - } else if (interface_it->get_interface_name() == "velocity") { - joint_velocity_state_interface_.emplace_back(*interface_it); - } else if (interface_it->get_interface_name() == "acceleration") { - joint_acceleration_state_interface_.emplace_back(*interface_it); - } - } - } - - auto it = std::find_if(state_interfaces_.begin(), state_interfaces_.end(), [&](auto& interface) { - return (interface.get_name() == passthrough_params_.speed_scaling_interface_name); - }); - if (it != state_interfaces_.end()) { - scaling_state_interface_ = *it; - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Did not find speed scaling interface in state interfaces."); - return controller_interface::CallbackReturn::ERROR; - } - - { - const std::string interface_name = passthrough_params_.tf_prefix + "trajectory_passthrough/" - "abort"; - auto it = std::find_if(command_interfaces_.begin(), command_interfaces_.end(), - [&](auto& interface) { return (interface.get_name() == interface_name); }); - if (it != command_interfaces_.end()) { - abort_command_interface_ = *it; - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Did not find '%s' in command interfaces.", interface_name.c_str()); - return controller_interface::CallbackReturn::ERROR; - } - } - - const std::string tf_prefix = passthrough_params_.tf_prefix; - { - const std::string interface_name = tf_prefix + "trajectory_passthrough/transfer_state"; - auto it = std::find_if(command_interfaces_.begin(), command_interfaces_.end(), - [&](auto& interface) { return (interface.get_name() == interface_name); }); - if (it != command_interfaces_.end()) { - transfer_command_interface_ = *it; - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Did not find '%s' in command interfaces.", interface_name.c_str()); - return controller_interface::CallbackReturn::ERROR; - } - } - { - const std::string interface_name = tf_prefix + "trajectory_passthrough/time_from_start"; - auto it = std::find_if(command_interfaces_.begin(), command_interfaces_.end(), - [&](auto& interface) { return (interface.get_name() == interface_name); }); - if (it != command_interfaces_.end()) { - time_from_start_command_interface_ = *it; - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Did not find '%s' in command interfaces.", interface_name.c_str()); - return controller_interface::CallbackReturn::ERROR; - } - } - - return ControllerInterface::on_activate(state); -} - -controller_interface::CallbackReturn PassthroughTrajectoryController::on_deactivate(const rclcpp_lifecycle::State&) -{ - abort_command_interface_->get().set_value(1.0); - if (trajectory_active_) { - const auto active_goal = *rt_active_goal_.readFromRT(); - std::shared_ptr result = - std::make_shared(); - result->set__error_string("Aborting current goal, since the controller is being deactivated."); - active_goal->setAborted(result); - rt_active_goal_.writeFromNonRT(RealtimeGoalHandlePtr()); - end_goal(); - } - return CallbackReturn::SUCCESS; -} - -controller_interface::return_type PassthroughTrajectoryController::update(const rclcpp::Time& /*time*/, - const rclcpp::Duration& period) -{ - const auto active_goal = *rt_active_goal_.readFromRT(); - - const auto current_transfer_state = transfer_command_interface_->get().get_value(); - - if (active_goal && trajectory_active_) { - if (current_transfer_state != TRANSFER_STATE_IDLE) { - // Check if the trajectory has been aborted from the hardware interface. E.g. the robot was stopped on the teach - // pendant. - if (abort_command_interface_->get().get_value() == 1.0 && current_index_ > 0) { - RCLCPP_INFO(get_node()->get_logger(), "Trajectory aborted by hardware, aborting action."); - std::shared_ptr result = - std::make_shared(); - active_goal->setAborted(result); - end_goal(); - return controller_interface::return_type::OK; - } - } - - active_joint_traj_ = active_goal->gh_->get_goal()->trajectory; - - if (current_index_ == 0 && current_transfer_state == TRANSFER_STATE_IDLE) { - active_trajectory_elapsed_time_ = rclcpp::Duration(0, 0); - max_trajectory_time_ = - rclcpp::Duration::from_seconds(duration_to_double(active_joint_traj_.points.back().time_from_start)); - transfer_command_interface_->get().set_value(TRANSFER_STATE_WAITING_FOR_POINT); - } - auto active_goal_time_tol = goal_time_tolerance_.readFromRT(); - auto joint_mapping = joint_trajectory_mapping_.readFromRT(); - - // Write a new point to the command interface, if the previous point has been read by the hardware interface. - if (current_transfer_state == TRANSFER_STATE_WAITING_FOR_POINT) { - if (current_index_ < active_joint_traj_.points.size()) { - // Write the time_from_start parameter. - time_from_start_command_interface_->get().set_value( - duration_to_double(active_joint_traj_.points[current_index_].time_from_start)); - - // Write the positions for each joint of the robot - auto joint_names_internal = joint_names_.readFromRT(); - // We've added the joint interfaces matching the order of the joint names so we can safely access - // them by the index. - for (size_t i = 0; i < number_of_joints_; i++) { - command_interfaces_[i * 3].set_value( - active_joint_traj_.points[current_index_].positions[joint_mapping->at(joint_names_internal->at(i))]); - // Optionally, also write velocities and accelerations for each joint. - if (active_joint_traj_.points[current_index_].velocities.size() > 0) { - command_interfaces_[i * 3 + 1].set_value( - active_joint_traj_.points[current_index_].velocities[joint_mapping->at(joint_names_internal->at(i))]); - if (active_joint_traj_.points[current_index_].accelerations.size() > 0) { - command_interfaces_[i * 3 + 2].set_value( - active_joint_traj_.points[current_index_] - .accelerations[joint_mapping->at(joint_names_internal->at(i))]); - } else { - command_interfaces_[i * 3 + 2].set_value(NO_VAL); - } - } else { - command_interfaces_[i * 3 + 1].set_value(NO_VAL); - command_interfaces_[i * 3 + 2].set_value(NO_VAL); - } - } - // Tell hardware interface that this point is ready to be read. - transfer_command_interface_->get().set_value(TRANSFER_STATE_TRANSFERRING); - current_index_++; - // Check if all points have been written to the hardware interface. - } else if (current_index_ == active_joint_traj_.points.size()) { - transfer_command_interface_->get().set_value(TRANSFER_STATE_TRANSFER_DONE); - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Hardware waiting for trajectory point while none is present!"); - } - - // When the trajectory is finished, report the goal as successful to the client. - } else if (current_transfer_state == TRANSFER_STATE_DONE) { - auto result = active_goal->preallocated_result_; - // Check if the actual position complies with the tolerances given. - if (!check_goal_tolerance()) { - result->error_code = control_msgs::action::FollowJointTrajectory::Result::GOAL_TOLERANCE_VIOLATED; - result->error_string = "Robot not within tolerances at end of trajectory."; - active_goal->setAborted(result); - end_goal(); - RCLCPP_ERROR(get_node()->get_logger(), "Trajectory failed, goal tolerances not met."); - } else if (active_goal_time_tol->nanoseconds() > 0 && - active_trajectory_elapsed_time_ > (max_trajectory_time_ + *active_goal_time_tol)) { - // Check if the goal time tolerance was complied with. - result->error_code = control_msgs::action::FollowJointTrajectory::Result::GOAL_TOLERANCE_VIOLATED; - result->error_string = - "Goal not reached within time tolerance. Missed goal time by " + - std::to_string((active_trajectory_elapsed_time_ - max_trajectory_time_ - *active_goal_time_tol).seconds()) + - " seconds."; - active_goal->setAborted(result); - end_goal(); - } else { - result->error_code = control_msgs::action::FollowJointTrajectory::Result::SUCCESSFUL; - result->error_string = "Trajectory executed successfully in " + - std::to_string(active_trajectory_elapsed_time_.seconds()) + - " (scaled) seconds! The real time needed for execution could be longer."; - active_goal->setSucceeded(result); - end_goal(); - RCLCPP_INFO(get_node()->get_logger(), "%s", result->error_string.c_str()); - } - } else if (current_transfer_state == TRANSFER_STATE_IN_MOTION) { - // Keep track of how long the trajectory has been executing, if it takes too long, send a warning. - if (scaling_state_interface_.has_value()) { - scaling_factor_ = scaling_state_interface_->get().get_value(); - } - - active_trajectory_elapsed_time_ = active_trajectory_elapsed_time_ + (period * scaling_factor_); - - // RCLCPP_INFO(get_node()->get_logger(), "Elapsed trajectory time: %f. Scaling factor: %f, period: %f", - // active_trajectory_elapsed_time_.seconds(), scaling_factor_, period.seconds()); - - if (active_trajectory_elapsed_time_ > (max_trajectory_time_ + *active_goal_time_tol) && trajectory_active_) { - RCLCPP_WARN_THROTTLE(get_node()->get_logger(), *clock_, 1000, - "Trajectory should be finished by now. You may want to cancel this goal, if it is not."); - } - } - } else if (current_transfer_state != TRANSFER_STATE_IDLE && current_transfer_state != TRANSFER_STATE_DONE) { - // No goal is active, but we are not in IDLE, either. We have been canceled. - abort_command_interface_->get().set_value(1.0); - - } else if (current_transfer_state == TRANSFER_STATE_DONE) { - // We have been informed about the finished trajectory. Let's reset things. - transfer_command_interface_->get().set_value(TRANSFER_STATE_IDLE); - abort_command_interface_->get().set_value(0.0); - } - - return controller_interface::return_type::OK; -} - -rclcpp_action::GoalResponse PassthroughTrajectoryController::goal_received_callback( - const rclcpp_action::GoalUUID& /*uuid*/, - std::shared_ptr goal) -{ - RCLCPP_INFO(get_node()->get_logger(), "Received new trajectory."); - // Precondition: Running controller - if (get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) { - RCLCPP_ERROR(get_node()->get_logger(), "Can't accept new trajectories. Controller is not running."); - return rclcpp_action::GoalResponse::REJECT; - } - - if (trajectory_active_) { - RCLCPP_ERROR(get_node()->get_logger(), "Can't accept new trajectory. A trajectory is already executing."); - return rclcpp_action::GoalResponse::REJECT; - } - - // Check that all parts of the trajectory are valid. - if (!check_goal_positions(goal)) { - RCLCPP_ERROR(get_node()->get_logger(), "Trajectory rejected"); - return rclcpp_action::GoalResponse::REJECT; - } - if (!check_goal_velocities(goal)) { - RCLCPP_ERROR(get_node()->get_logger(), "Trajectory rejected"); - return rclcpp_action::GoalResponse::REJECT; - } - if (!check_goal_accelerations(goal)) { - RCLCPP_ERROR(get_node()->get_logger(), "Trajectory rejected"); - return rclcpp_action::GoalResponse::REJECT; - } - if (!check_goal_tolerances(goal)) { - RCLCPP_ERROR(get_node()->get_logger(), "Trajectory rejected"); - return rclcpp_action::GoalResponse::REJECT; - } - - return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE; -} - -bool PassthroughTrajectoryController::check_goal_tolerances( - std::shared_ptr goal) -{ - auto& tolerances = goal->goal_tolerance; - auto joint_names_internal = joint_names_.readFromRT(); - if (!tolerances.empty()) { - for (auto& tol : tolerances) { - auto found_it = std::find(joint_names_internal->begin(), joint_names_internal->end(), tol.name); - if (found_it == joint_names_internal->end()) { - RCLCPP_ERROR(get_node()->get_logger(), - "Tolerance for joint '%s' given. This joint is not known to this controller.", tol.name.c_str()); - return false; - } - } - if (tolerances.size() != number_of_joints_) { - RCLCPP_ERROR(get_node()->get_logger(), "Tolerances for %lu joints given. This controller knows %lu joints.", - tolerances.size(), number_of_joints_.load()); - return false; - } - } - return true; -} - -bool PassthroughTrajectoryController::check_goal_positions( - std::shared_ptr goal) -{ - for (uint32_t i = 0; i < goal->trajectory.points.size(); i++) { - if (goal->trajectory.points[i].positions.size() != number_of_joints_) { - std::string msg; - msg = "Can't accept new trajectory. All trajectory points must have positions for all joints of the robot. (" + - std::to_string(number_of_joints_) + " joint positions per point)"; - RCLCPP_ERROR(get_node()->get_logger(), "%s", msg.c_str()); - msg = "Point nr " + std::to_string(i + 1) + - " has: " + std::to_string(goal->trajectory.points[i].positions.size()) + " positions."; - RCLCPP_ERROR(get_node()->get_logger(), "%s", msg.c_str()); - return false; - } - } - return true; -} - -bool PassthroughTrajectoryController::check_goal_velocities( - std::shared_ptr goal) -{ - for (uint32_t i = 0; i < goal->trajectory.points.size(); i++) { - if (goal->trajectory.points[i].velocities.size() != number_of_joints_ && - goal->trajectory.points[i].velocities.size() != 0) { - std::string msg; - msg = "Can't accept new trajectory. All trajectory points must either not have velocities or have them for all " - "joints of the robot. (" + - std::to_string(number_of_joints_) + " joint velocities per point)"; - RCLCPP_ERROR(get_node()->get_logger(), "%s", msg.c_str()); - msg = "Point nr " + std::to_string(i + 1) + - " has: " + std::to_string(goal->trajectory.points[i].velocities.size()) + " velocities."; - RCLCPP_ERROR(get_node()->get_logger(), "%s", msg.c_str()); - return false; - } - if (goal->trajectory.points[i].velocities.size() != goal->trajectory.points[0].velocities.size()) { - std::string msg; - msg = "Can't accept new trajectory. All trajectory points must have velocities for all joints of the robot. " - "(" + - std::to_string(number_of_joints_) + " joint velocities per point)"; - RCLCPP_ERROR(get_node()->get_logger(), "%s", msg.c_str()); - msg = "Point nr " + std::to_string(i) + " has: " + std::to_string(goal->trajectory.points[i].velocities.size()) + - " velocities."; - RCLCPP_ERROR(get_node()->get_logger(), "%s", msg.c_str()); - return false; - } - } - return true; -} - -bool PassthroughTrajectoryController::check_goal_accelerations( - std::shared_ptr goal) -{ - for (uint32_t i = 0; i < goal->trajectory.points.size(); i++) { - if (goal->trajectory.points[i].accelerations.size() != 0 && - goal->trajectory.points[i].accelerations.size() != number_of_joints_) { - std::string msg; - msg = "Can't accept new trajectory. All trajectory points must either not have accelerations or have them for " - "all joints of the robot. (" + - std::to_string(number_of_joints_) + " joint accelerations per point)"; - RCLCPP_ERROR(get_node()->get_logger(), "%s", msg.c_str()); - msg = "Point nr " + std::to_string(i) + - " has: " + std::to_string(goal->trajectory.points[i].accelerations.size()) + " accelerations."; - RCLCPP_ERROR(get_node()->get_logger(), "%s", msg.c_str()); - return false; - } - if (goal->trajectory.points[i].accelerations.size() != goal->trajectory.points[0].accelerations.size()) { - std::string msg; - msg = "Can't accept new trajectory. All trajectory points must have accelerations for all joints of the " - "robot. " - "(" + - std::to_string(number_of_joints_) + " joint accelerations per point)"; - RCLCPP_ERROR(get_node()->get_logger(), "%s", msg.c_str()); - msg = "Point nr " + std::to_string(i) + - " has: " + std::to_string(goal->trajectory.points[i].accelerations.size()) + " accelerations."; - RCLCPP_ERROR(get_node()->get_logger(), "%s", msg.c_str()); - return false; - } - } - return true; -} - -rclcpp_action::CancelResponse PassthroughTrajectoryController::goal_cancelled_callback( - const std::shared_ptr> goal_handle) -{ - // Check that cancel request refers to currently active goal (if any) - const auto active_goal = *rt_active_goal_.readFromNonRT(); - if (active_goal && active_goal->gh_ == goal_handle) { - RCLCPP_INFO(get_node()->get_logger(), "Cancelling active trajectory requested."); - - // Mark the current goal as canceled - auto result = std::make_shared(); - active_goal->setCanceled(result); - rt_active_goal_.writeFromNonRT(RealtimeGoalHandlePtr()); - trajectory_active_ = false; - } - return rclcpp_action::CancelResponse::ACCEPT; -} - -// Action goal was accepted, initialise values for a new trajectory. -void PassthroughTrajectoryController::goal_accepted_callback( - std::shared_ptr> goal_handle) -{ - RCLCPP_INFO_STREAM(get_node()->get_logger(), "Accepted new trajectory with " - << goal_handle->get_goal()->trajectory.points.size() << " points."); - current_index_ = 0; - - // TODO(fexner): Merge goal tolerances with default tolerances - - joint_trajectory_mapping_.writeFromNonRT(create_joint_mapping(goal_handle->get_goal()->trajectory.joint_names)); - - // sort goal tolerances to match internal joint order - std::vector goal_tolerances; - if (!goal_handle->get_goal()->goal_tolerance.empty()) { - auto joint_names_internal = joint_names_.readFromRT(); - std::stringstream ss; - ss << "Using goal tolerances\n"; - for (auto& joint_name : *joint_names_internal) { - auto found_it = - std::find_if(goal_handle->get_goal()->goal_tolerance.begin(), goal_handle->get_goal()->goal_tolerance.end(), - [&joint_name](auto& tol) { return tol.name == joint_name; }); - if (found_it != goal_handle->get_goal()->goal_tolerance.end()) { - goal_tolerances.push_back(*found_it); - ss << joint_name << " -- position: " << found_it->position << ", velocity: " << found_it->velocity - << ", acceleration: " << found_it->acceleration << std::endl; - } - } - RCLCPP_INFO_STREAM(get_node()->get_logger(), ss.str()); - } - goal_tolerance_.writeFromNonRT(goal_tolerances); - goal_time_tolerance_.writeFromNonRT(goal_handle->get_goal()->goal_time_tolerance); - RCLCPP_INFO_STREAM(get_node()->get_logger(), - "Goal time tolerance: " << duration_to_double(goal_handle->get_goal()->goal_time_tolerance) - << " sec"); - - // Action handling will be done from the timer callback to avoid those things in the realtime - // thread. First, we delete the existing (if any) timer by resetting the pointer and then create a new - // one. - // - RealtimeGoalHandlePtr rt_goal = std::make_shared(goal_handle); - rt_goal->execute(); - rt_active_goal_.writeFromNonRT(rt_goal); - goal_handle_timer_.reset(); - goal_handle_timer_ = get_node()->create_wall_timer(action_monitor_period_.to_chrono(), - std::bind(&RealtimeGoalHandle::runNonRealtime, rt_goal)); - trajectory_active_ = true; - return; -} - -bool PassthroughTrajectoryController::check_goal_tolerance() -{ - auto goal_tolerance = goal_tolerance_.readFromRT(); - auto joint_mapping = joint_trajectory_mapping_.readFromRT(); - auto joint_names_internal = joint_names_.readFromRT(); - if (goal_tolerance->empty()) { - return true; - } - - for (size_t i = 0; i < number_of_joints_; ++i) { - const std::string joint_name = joint_names_internal->at(i); - const auto& joint_tol = goal_tolerance->at(i); - const auto& setpoint = active_joint_traj_.points.back().positions[joint_mapping->at(joint_name)]; - const double joint_pos = joint_position_state_interface_[i].get().get_value(); - if (std::abs(joint_pos - setpoint) > joint_tol.position) { - // RCLCPP_ERROR( - // get_node()->get_logger(), "Joint %s should be at position %f, but is at position %f, where tolerance is %f", - // joint_position_state_interface_[i].get().get_name().c_str(), setpoint, joint_pos, joint_tol.position); - return false; - } - - if (!active_joint_traj_.points.back().velocities.empty()) { - const double joint_vel = joint_velocity_state_interface_[i].get().get_value(); - const auto& expected_vel = active_joint_traj_.points.back().velocities[joint_mapping->at(joint_name)]; - if (std::abs(joint_vel - expected_vel) > joint_tol.velocity) { - return false; - } - } - if (!active_joint_traj_.points.back().accelerations.empty()) { - const double joint_vel = joint_acceleration_state_interface_[i].get().get_value(); - const auto& expected_vel = active_joint_traj_.points.back().accelerations[joint_mapping->at(joint_name)]; - if (std::abs(joint_vel - expected_vel) > joint_tol.acceleration) { - return false; - } - } - } - - return true; -} - -void PassthroughTrajectoryController::end_goal() -{ - trajectory_active_ = false; - transfer_command_interface_->get().set_value(TRANSFER_STATE_IDLE); -} - -std::unordered_map -PassthroughTrajectoryController::create_joint_mapping(const std::vector& joint_names) const -{ - std::unordered_map joint_mapping; - auto joint_names_internal = joint_names_.readFromNonRT(); - for (auto& joint_name : *joint_names_internal) { - auto found_it = std::find(joint_names.begin(), joint_names.end(), joint_name); - if (found_it != joint_names.end()) { - joint_mapping.insert({ joint_name, found_it - joint_names.begin() }); - } - } - return joint_mapping; -} -} // namespace ur_controllers - -#include "pluginlib/class_list_macros.hpp" - -PLUGINLIB_EXPORT_CLASS(ur_controllers::PassthroughTrajectoryController, controller_interface::ControllerInterface) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/passthrough_trajectory_controller_parameters.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/passthrough_trajectory_controller_parameters.yaml deleted file mode 100644 index db88443..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/passthrough_trajectory_controller_parameters.yaml +++ /dev/null @@ -1,31 +0,0 @@ ---- -passthrough_trajectory_controller: - speed_scaling_interface_name: { - type: string, - default_value: "speed_scaling/speed_scaling_factor", - description: "Fully qualified name of the speed scaling interface name" - } - tf_prefix: { - type: string, - default_value: "", - description: "Urdf prefix of the corresponding arm" - } - joints: { - type: string_array, - default_value: [], - description: "Joint names to claim and listen to", - read_only: true, - validation: { - unique<>: null, - } - } - state_interfaces: { - type: string_array, - default_value: [], - description: "State interfaces provided by the hardware for all joints.", - read_only: true, - validation: { - unique<>: null, - subset_of<>: [["position", "velocity", "acceleration"]], - } - } diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/scaled_joint_trajectory_controller.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/scaled_joint_trajectory_controller.cpp deleted file mode 100644 index 9a97ab3..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/scaled_joint_trajectory_controller.cpp +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Marvin Große Besselmann grosse@fzi.de - * \date 2021-02-18 - * - */ -//---------------------------------------------------------------------- - -#include -#include - -#include "ur_controllers/scaled_joint_trajectory_controller.hpp" - -#include "lifecycle_msgs/msg/state.hpp" - -namespace ur_controllers -{ - -controller_interface::CallbackReturn ScaledJointTrajectoryController::on_init() -{ - // Create the parameter listener and get the parameters - scaled_param_listener_ = std::make_shared(get_node()); - scaled_params_ = scaled_param_listener_->get_params(); - if (!scaled_params_.speed_scaling_interface_name.empty()) { - RCLCPP_INFO(get_node()->get_logger(), "Using scaling state from the hardware from interface %s.", - scaled_params_.speed_scaling_interface_name.c_str()); - } else { - RCLCPP_INFO(get_node()->get_logger(), "No scaling interface set. This controller will not use speed scaling."); - } - - return JointTrajectoryController::on_init(); -} - -controller_interface::InterfaceConfiguration ScaledJointTrajectoryController::state_interface_configuration() const -{ - controller_interface::InterfaceConfiguration conf; - conf = JointTrajectoryController::state_interface_configuration(); - - if (!scaled_params_.speed_scaling_interface_name.empty()) { - conf.names.push_back(scaled_params_.speed_scaling_interface_name); - } - - return conf; -} - -controller_interface::CallbackReturn ScaledJointTrajectoryController::on_activate(const rclcpp_lifecycle::State& state) -{ - TimeData time_data; - time_data.time = get_node()->now(); - time_data.period = rclcpp::Duration::from_nanoseconds(0); - time_data.uptime = get_node()->now(); - time_data_.initRT(time_data); - - // Set scaling interfaces - if (!scaled_params_.speed_scaling_interface_name.empty()) { - auto it = std::find_if(state_interfaces_.begin(), state_interfaces_.end(), [&](auto& interface) { - return (interface.get_name() == scaled_params_.speed_scaling_interface_name); - }); - if (it != state_interfaces_.end()) { - scaling_state_interface_ = *it; - } else { - RCLCPP_ERROR(get_node()->get_logger(), "Did not find speed scaling interface in state interfaces."); - } - } - - return JointTrajectoryController::on_activate(state); -} - -controller_interface::return_type ScaledJointTrajectoryController::update(const rclcpp::Time& time, - const rclcpp::Duration& period) -{ - if (scaling_state_interface_.has_value()) { - scaling_factor_ = scaling_state_interface_->get().get_value(); - } - - if (get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) { - return controller_interface::return_type::OK; - } - auto logger = this->get_node()->get_logger(); - // update dynamic parameters - if (param_listener_->is_old(params_)) { - params_ = param_listener_->get_params(); - default_tolerances_ = get_segment_tolerances(logger, params_); - } - - auto compute_error_for_joint = [&](JointTrajectoryPoint& error, int index, const JointTrajectoryPoint& current, - const JointTrajectoryPoint& desired) { - // error defined as the difference between current and desired - if (joints_angle_wraparound_[index]) { - // if desired, the shortest_angular_distance is calculated, i.e., the error is - // normalized between -piget_trajectory_msg(); - auto new_external_msg = traj_msg_external_point_ptr_.readFromRT(); - // Discard, if a goal is pending but still not active (somewhere stuck in goal_handle_timer_) - if (current_external_msg != *new_external_msg && (*(rt_has_pending_goal_.readFromRT()) && !active_goal) == false) { - fill_partial_goal(*new_external_msg); - sort_to_local_joint_order(*new_external_msg); - // TODO(denis): Add here integration of position and velocity - traj_external_point_ptr_->update(*new_external_msg); - } - - // TODO(anyone): can I here also use const on joint_interface since the reference_wrapper is not - // changed, but its value only? - auto assign_interface_from_point = [&](auto& joint_interface, const std::vector& trajectory_point_interface) { - for (size_t index = 0; index < dof_; ++index) { - joint_interface[index].get().set_value(trajectory_point_interface[index]); - } - }; - - // current state update - state_current_.time_from_start.set__sec(0); - read_state_from_state_interfaces(state_current_); - - // currently carrying out a trajectory - if (has_active_trajectory()) { - // Main Speed scaling difference... - // Adjust time with scaling factor - TimeData time_data; - time_data.time = time; - rcl_duration_value_t t_period = (time_data.time - time_data_.readFromRT()->time).nanoseconds(); - time_data.period = rclcpp::Duration::from_nanoseconds(scaling_factor_ * t_period); - time_data.uptime = time_data_.readFromRT()->uptime + time_data.period; - rclcpp::Time traj_time = time_data_.readFromRT()->uptime + rclcpp::Duration::from_nanoseconds(t_period); - time_data_.reset(); - time_data_.initRT(time_data); - - bool first_sample = false; - // if sampling the first time, set the point before you sample - if (!traj_external_point_ptr_->is_sampled_already()) { - first_sample = true; - if (params_.open_loop_control) { - traj_external_point_ptr_->set_point_before_trajectory_msg(traj_time, last_commanded_state_, - joints_angle_wraparound_); - } else { - traj_external_point_ptr_->set_point_before_trajectory_msg(traj_time, state_current_, joints_angle_wraparound_); - } - } - - // find segment for current timestamp - joint_trajectory_controller::TrajectoryPointConstIter start_segment_itr, end_segment_itr; - const bool valid_point = traj_external_point_ptr_->sample(traj_time, interpolation_method_, state_desired_, - start_segment_itr, end_segment_itr); - - if (valid_point) { - const rclcpp::Time traj_start = traj_external_point_ptr_->time_from_start(); - // this is the time instance - // - started with the first segment: when the first point will be reached (in the future) - // - later: when the point of the current segment was reached - const rclcpp::Time segment_time_from_start = traj_start + start_segment_itr->time_from_start; - // time_difference is - // - negative until first point is reached - // - counting from zero to time_from_start of next point - const double time_difference = time_data.uptime.seconds() - segment_time_from_start.seconds(); - bool tolerance_violated_while_moving = false; - bool outside_goal_tolerance = false; - bool within_goal_time = true; - const bool before_last_point = end_segment_itr != traj_external_point_ptr_->end(); - auto active_tol = active_tolerances_.readFromRT(); - - // have we reached the end, are not holding position, and is a timeout configured? - // Check independently of other tolerances - if (!before_last_point && *(rt_is_holding_.readFromRT()) == false && cmd_timeout_ > 0.0 && - time_difference > cmd_timeout_) { - RCLCPP_WARN(logger, "Aborted due to command timeout"); - - traj_msg_external_point_ptr_.reset(); - traj_msg_external_point_ptr_.initRT(set_hold_position()); - } - - // Check state/goal tolerance - for (size_t index = 0; index < dof_; ++index) { - compute_error_for_joint(state_error_, index, state_current_, state_desired_); - - // Always check the state tolerance on the first sample in case the first sample - // is the last point - // print output per default, goal will be aborted afterwards - if ((before_last_point || first_sample) && *(rt_is_holding_.readFromRT()) == false && - !check_state_tolerance_per_joint(state_error_, index, active_tol->state_tolerance[index], - true /* show_errors */)) { - tolerance_violated_while_moving = true; - } - // past the final point, check that we end up inside goal tolerance - if (!before_last_point && *(rt_is_holding_.readFromRT()) == false && - !check_state_tolerance_per_joint(state_error_, index, active_tol->goal_state_tolerance[index], - false /* show_errors */)) { - outside_goal_tolerance = true; - - if (active_tol->goal_time_tolerance != 0.0) { - // if we exceed goal_time_tolerance set it to aborted - if (time_difference > active_tol->goal_time_tolerance) { - within_goal_time = false; - // print once, goal will be aborted afterwards - check_state_tolerance_per_joint(state_error_, index, default_tolerances_.goal_state_tolerance[index], - true /* show_errors */); - } - } - } - } - - // set values for next hardware write() if tolerance is met - if (!tolerance_violated_while_moving && within_goal_time) { - if (use_closed_loop_pid_adapter_) { - // Update PIDs - for (auto i = 0ul; i < dof_; ++i) { - tmp_command_[i] = (state_desired_.velocities[i] * ff_velocity_scale_[i]) + - pids_[i]->computeCommand(state_error_.positions[i], state_error_.velocities[i], - (uint64_t)period.nanoseconds()); - } - } - - // set values for next hardware write() - if (has_position_command_interface_) { - assign_interface_from_point(joint_command_interface_[0], state_desired_.positions); - } - if (has_velocity_command_interface_) { - if (use_closed_loop_pid_adapter_) { - assign_interface_from_point(joint_command_interface_[1], tmp_command_); - } else { - assign_interface_from_point(joint_command_interface_[1], state_desired_.velocities); - } - } - if (has_acceleration_command_interface_) { - assign_interface_from_point(joint_command_interface_[2], state_desired_.accelerations); - } - if (has_effort_command_interface_) { - assign_interface_from_point(joint_command_interface_[3], tmp_command_); - } - - // store the previous command. Used in open-loop control mode - last_commanded_state_ = state_desired_; - } - - if (active_goal) { - // send feedback - auto feedback = std::make_shared(); - feedback->header.stamp = time; - feedback->joint_names = params_.joints; - - feedback->actual = state_current_; - feedback->desired = state_desired_; - feedback->error = state_error_; - active_goal->setFeedback(feedback); - - // check abort - if (tolerance_violated_while_moving) { - auto result = std::make_shared(); - result->set__error_code(FollowJTrajAction::Result::PATH_TOLERANCE_VIOLATED); - result->set__error_string("Aborted due to path tolerance violation"); - active_goal->setAborted(result); - // TODO(matthew-reynolds): Need a lock-free write here - // See https://github.com/ros-controls/ros2_controllers/issues/168 - rt_active_goal_.writeFromNonRT(RealtimeGoalHandlePtr()); - rt_has_pending_goal_.writeFromNonRT(false); - - RCLCPP_WARN(logger, "Aborted due to state tolerance violation"); - - traj_msg_external_point_ptr_.reset(); - traj_msg_external_point_ptr_.initRT(set_hold_position()); - } else if (!before_last_point) { - // check goal tolerance - if (!outside_goal_tolerance) { - auto result = std::make_shared(); - result->set__error_code(FollowJTrajAction::Result::SUCCESSFUL); - result->set__error_string("Goal successfully reached!"); - active_goal->setSucceeded(result); - // TODO(matthew-reynolds): Need a lock-free write here - // See https://github.com/ros-controls/ros2_controllers/issues/168 - rt_active_goal_.writeFromNonRT(RealtimeGoalHandlePtr()); - rt_has_pending_goal_.writeFromNonRT(false); - - RCLCPP_INFO(logger, "Goal reached, success!"); - - traj_msg_external_point_ptr_.reset(); - traj_msg_external_point_ptr_.initRT(set_success_trajectory_point()); - } else if (!within_goal_time) { - const std::string error_string = - "Aborted due to goal_time_tolerance exceeding by " + std::to_string(time_difference) + " seconds"; - - auto result = std::make_shared(); - result->set__error_code(FollowJTrajAction::Result::GOAL_TOLERANCE_VIOLATED); - result->set__error_string(error_string); - active_goal->setAborted(result); - // TODO(matthew-reynolds): Need a lock-free write here - // See https://github.com/ros-controls/ros2_controllers/issues/168 - rt_active_goal_.writeFromNonRT(RealtimeGoalHandlePtr()); - rt_has_pending_goal_.writeFromNonRT(false); - - RCLCPP_WARN(logger, error_string.c_str()); - - traj_msg_external_point_ptr_.reset(); - traj_msg_external_point_ptr_.initRT(set_hold_position()); - } - } - } else if (tolerance_violated_while_moving && *(rt_has_pending_goal_.readFromRT()) == false) { - // we need to ensure that there is no pending goal -> we get a race condition otherwise - RCLCPP_ERROR(logger, "Holding position due to state tolerance violation"); - - traj_msg_external_point_ptr_.reset(); - traj_msg_external_point_ptr_.initRT(set_hold_position()); - } else if (!before_last_point && !within_goal_time && *(rt_has_pending_goal_.readFromRT()) == false) { - RCLCPP_ERROR(logger, "Exceeded goal_time_tolerance: holding position..."); - - traj_msg_external_point_ptr_.reset(); - traj_msg_external_point_ptr_.initRT(set_hold_position()); - } - // else, run another cycle while waiting for outside_goal_tolerance - // to be satisfied (will stay in this state until new message arrives) - // or outside_goal_tolerance violated within the goal_time_tolerance - } - } - - publish_state(state_desired_, state_current_, state_error_); - return controller_interface::return_type::OK; -} - -} // namespace ur_controllers - -#include "pluginlib/class_list_macros.hpp" -PLUGINLIB_EXPORT_CLASS(ur_controllers::ScaledJointTrajectoryController, controller_interface::ControllerInterface) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/scaled_joint_trajectory_controller_parameters.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/scaled_joint_trajectory_controller_parameters.yaml deleted file mode 100644 index e72b0e5..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/scaled_joint_trajectory_controller_parameters.yaml +++ /dev/null @@ -1,6 +0,0 @@ -scaled_joint_trajectory_controller: - speed_scaling_interface_name: { - type: string, - default_value: "speed_scaling/speed_scaling_factor", - description: "Fully qualified name of the speed scaling interface name" - } diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/speed_scaling_state_broadcaster.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/speed_scaling_state_broadcaster.cpp deleted file mode 100644 index 7534017..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/speed_scaling_state_broadcaster.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Marvin Große Besselmann grosse@fzi.de - * \date 2021-02-10 - * - */ -//---------------------------------------------------------------------- - -#include "ur_controllers/speed_scaling_state_broadcaster.hpp" - -#include "hardware_interface/types/hardware_interface_return_values.hpp" -#include "hardware_interface/types/hardware_interface_type_values.hpp" -#include "rclcpp/clock.hpp" -#include "rclcpp/qos.hpp" -#include "rclcpp/qos_event.hpp" -#include "rclcpp/time.hpp" -#include "rclcpp_lifecycle/lifecycle_node.hpp" -#include "rcpputils/split.hpp" -#include "rcutils/logging_macros.h" - -namespace rclcpp_lifecycle -{ -class State; -} // namespace rclcpp_lifecycle - -namespace ur_controllers -{ -SpeedScalingStateBroadcaster::SpeedScalingStateBroadcaster() -{ -} - -controller_interface::CallbackReturn SpeedScalingStateBroadcaster::on_init() -{ - try { - // Create the parameter listener and get the parameters - param_listener_ = std::make_shared(get_node()); - params_ = param_listener_->get_params(); - - RCLCPP_INFO(get_node()->get_logger(), "Loading UR SpeedScalingStateBroadcaster with tf_prefix: %s", - params_.tf_prefix.c_str()); - - } catch (std::exception& e) { - fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what()); - return controller_interface::CallbackReturn::ERROR; - } - - return controller_interface::CallbackReturn::SUCCESS; -} - -controller_interface::InterfaceConfiguration SpeedScalingStateBroadcaster::command_interface_configuration() const -{ - return controller_interface::InterfaceConfiguration{ controller_interface::interface_configuration_type::NONE }; -} - -controller_interface::InterfaceConfiguration SpeedScalingStateBroadcaster::state_interface_configuration() const -{ - controller_interface::InterfaceConfiguration config; - config.type = controller_interface::interface_configuration_type::INDIVIDUAL; - - const std::string tf_prefix = params_.tf_prefix; - config.names.push_back(tf_prefix + "speed_scaling/speed_scaling_factor"); - return config; -} - -controller_interface::CallbackReturn -SpeedScalingStateBroadcaster::on_configure(const rclcpp_lifecycle::State& /*previous_state*/) -{ - if (!param_listener_) { - RCLCPP_ERROR(get_node()->get_logger(), "Error encountered during init"); - return controller_interface::CallbackReturn::ERROR; - } - - // update the dynamic map parameters - param_listener_->refresh_dynamic_parameters(); - - // get parameters from the listener in case they were updated - params_ = param_listener_->get_params(); - - publish_rate_ = params_.state_publish_rate; - - RCLCPP_INFO(get_node()->get_logger(), "Publisher rate set to : %.1f Hz", publish_rate_); - - try { - speed_scaling_state_publisher_ = - get_node()->create_publisher("~/speed_scaling", rclcpp::SystemDefaultsQoS()); - } catch (const std::exception& e) { - // get_node() may throw, logging raw here - fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what()); - return controller_interface::CallbackReturn::ERROR; - } - return controller_interface::CallbackReturn::SUCCESS; -} - -controller_interface::CallbackReturn -SpeedScalingStateBroadcaster::on_activate(const rclcpp_lifecycle::State& /*previous_state*/) -{ - return controller_interface::CallbackReturn::SUCCESS; -} - -controller_interface::CallbackReturn -SpeedScalingStateBroadcaster::on_deactivate(const rclcpp_lifecycle::State& /*previous_state*/) -{ - return controller_interface::CallbackReturn::SUCCESS; -} - -controller_interface::return_type SpeedScalingStateBroadcaster::update(const rclcpp::Time& /*time*/, - const rclcpp::Duration& period) -{ - if (publish_rate_ > 0.0 && period > rclcpp::Duration(1.0 / publish_rate_, 0.0)) { - // Speed scaling is the only interface of the controller - speed_scaling_state_msg_.data = state_interfaces_[0].get_value() * 100.0; - - // publish - speed_scaling_state_publisher_->publish(speed_scaling_state_msg_); - } - return controller_interface::return_type::OK; -} - -} // namespace ur_controllers - -#include "pluginlib/class_list_macros.hpp" - -PLUGINLIB_EXPORT_CLASS(ur_controllers::SpeedScalingStateBroadcaster, controller_interface::ControllerInterface) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/speed_scaling_state_broadcaster_parameters.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/speed_scaling_state_broadcaster_parameters.yaml deleted file mode 100644 index 8ddff2a..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/speed_scaling_state_broadcaster_parameters.yaml +++ /dev/null @@ -1,11 +0,0 @@ -speed_scaling_state_broadcaster: - tf_prefix: { - type: string, - default_value: "", - description: "Urdf prefix of the corresponding arm" - } - state_publish_rate: { - type: double, - default_value: 100.0, - description: "Rate at which the speedscaling state will be published." - } diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/ur_configuration_controller.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/ur_configuration_controller.cpp deleted file mode 100644 index a6ec2d2..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/ur_configuration_controller.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2024, Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Jacob Larsen jala@universal-robots.com - * \date 2024-07-11 - * - * - * - * - */ -//---------------------------------------------------------------------- - -#include -#include -namespace ur_controllers -{ - -controller_interface::CallbackReturn URConfigurationController::on_init() -{ - return controller_interface::CallbackReturn::SUCCESS; -} - -controller_interface::CallbackReturn -URConfigurationController::on_configure(const rclcpp_lifecycle::State& /* previous_state */) -{ - param_listener_ = std::make_shared(get_node()); - params_ = param_listener_->get_params(); - - get_robot_software_version_srv_ = get_node()->create_service( - "~/get_robot_software_version", std::bind(&URConfigurationController::getRobotSoftwareVersion, this, - std::placeholders::_1, std::placeholders::_2)); - - return controller_interface::CallbackReturn::SUCCESS; -} - -controller_interface::InterfaceConfiguration URConfigurationController::command_interface_configuration() const -{ - // No command interfaces currently - controller_interface::InterfaceConfiguration config; - config.type = controller_interface::interface_configuration_type::INDIVIDUAL; - - return config; -} - -controller_interface::InterfaceConfiguration URConfigurationController::state_interface_configuration() const -{ - controller_interface::InterfaceConfiguration config; - config.type = controller_interface::interface_configuration_type::INDIVIDUAL; - - const std::string tf_prefix = params_.tf_prefix; - - config.names.emplace_back(tf_prefix + "get_robot_software_version/get_version_major"); - config.names.emplace_back(tf_prefix + "get_robot_software_version/get_version_minor"); - config.names.emplace_back(tf_prefix + "get_robot_software_version/get_version_build"); - config.names.emplace_back(tf_prefix + "get_robot_software_version/get_version_bugfix"); - - return config; -} - -controller_interface::return_type URConfigurationController::update(const rclcpp::Time& /* time */, - const rclcpp::Duration& /* period */) -{ - return controller_interface::return_type::OK; -} - -controller_interface::CallbackReturn -URConfigurationController::on_activate(const rclcpp_lifecycle::State& /* previous_state */) -{ - robot_software_version_.set([this](const std::shared_ptr ptr) { - ptr->major = state_interfaces_[StateInterfaces::ROBOT_VERSION_MAJOR].get_value(); - ptr->minor = state_interfaces_[StateInterfaces::ROBOT_VERSION_MINOR].get_value(); - ptr->build = state_interfaces_[StateInterfaces::ROBOT_VERSION_BUILD].get_value(); - ptr->bugfix = state_interfaces_[StateInterfaces::ROBOT_VERSION_BUGFIX].get_value(); - }); - return controller_interface::CallbackReturn::SUCCESS; -} - -controller_interface::CallbackReturn -URConfigurationController::on_deactivate(const rclcpp_lifecycle::State& /* previous_state */) -{ - return controller_interface::CallbackReturn::SUCCESS; -} - -bool URConfigurationController::getRobotSoftwareVersion( - ur_msgs::srv::GetRobotSoftwareVersion::Request::SharedPtr /*req*/, - ur_msgs::srv::GetRobotSoftwareVersion::Response::SharedPtr resp) -{ - std::shared_ptr temp; - return robot_software_version_.tryGet([resp](const std::shared_ptr ptr) { - resp->major = ptr->major; - resp->minor = ptr->minor; - resp->build = ptr->build; - resp->bugfix = ptr->bugfix; - }); -} -} // namespace ur_controllers - -#include "pluginlib/class_list_macros.hpp" - -PLUGINLIB_EXPORT_CLASS(ur_controllers::URConfigurationController, controller_interface::ControllerInterface) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/ur_configuration_controller_parameters.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/ur_configuration_controller_parameters.yaml deleted file mode 100644 index 4cbdf8a..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/src/ur_configuration_controller_parameters.yaml +++ /dev/null @@ -1,6 +0,0 @@ -ur_configuration_controller: - tf_prefix: { - type: string, - default_value: "", - description: "URDF prefix of the corresponding arm" - } diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/force_mode_controller_params.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/force_mode_controller_params.yaml deleted file mode 100644 index 648b919..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/force_mode_controller_params.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -force_mode_controller: - ros__parameters: - tf_prefix: "" - damping: 0.025 - gain_scaling: 0.5 - check_io_successful_retries: 10 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/freedrive_mode_controller_params.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/freedrive_mode_controller_params.yaml deleted file mode 100644 index 417d385..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/freedrive_mode_controller_params.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -freedrive_mode_controller: - ros__parameters: - tf_prefix: "" - inactive_timeout: 10 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/test_load_force_mode_controller.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/test_load_force_mode_controller.cpp deleted file mode 100644 index b7b016b..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/test_load_force_mode_controller.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2024, Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -#include -#include "controller_manager/controller_manager.hpp" -#include "rclcpp/executor.hpp" -#include "rclcpp/executors/single_threaded_executor.hpp" -#include "rclcpp/utilities.hpp" -#include "ros2_control_test_assets/descriptions.hpp" - -TEST(TestLoadForceModeController, load_controller) -{ - std::shared_ptr executor = std::make_shared(); - - controller_manager::ControllerManager cm( - std::make_unique(ros2_control_test_assets::minimal_robot_urdf), executor, - "test_controller_manager"); - - const std::string test_file_path = std::string{ TEST_FILES_DIRECTORY } + "/force_mode_controller_params.yaml"; - cm.set_parameter({ "test_force_mode_controller.params_file", test_file_path }); - - cm.set_parameter({ "test_force_mode_controller.type", "ur_controllers/ForceModeController" }); - - ASSERT_NE(cm.load_controller("test_force_mode_controller"), nullptr); -} - -int main(int argc, char* argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - rclcpp::init(argc, argv); - - int result = RUN_ALL_TESTS(); - rclcpp::shutdown(); - - return result; -} diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/test_load_freedrive_mode_controller.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/test_load_freedrive_mode_controller.cpp deleted file mode 100644 index 0fd65c4..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_controllers/test/test_load_freedrive_mode_controller.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2024, Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -#include -#include "controller_manager/controller_manager.hpp" -#include "rclcpp/executor.hpp" -#include "rclcpp/executors/single_threaded_executor.hpp" -#include "rclcpp/utilities.hpp" -#include "ros2_control_test_assets/descriptions.hpp" - -TEST(TestLoadFreedriveModeController, load_controller) -{ - std::shared_ptr executor = std::make_shared(); - - controller_manager::ControllerManager cm( - std::make_unique(ros2_control_test_assets::minimal_robot_urdf), executor, - "test_controller_manager"); - - const std::string test_file_path = std::string{ TEST_FILES_DIRECTORY } + "/freedrive_mode_controller_params.yaml"; - cm.set_parameter({ "test_freedrive_mode_controller.params_file", test_file_path }); - - cm.set_parameter({ "test_freedrive_mode_controller.type", "ur_controllers/FreedriveModeController" }); - - ASSERT_NE(cm.load_controller("test_freedrive_mode_controller"), nullptr); -} - -int main(int argc, char* argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - rclcpp::init(argc, argv); - - int result = RUN_ALL_TESTS(); - rclcpp::shutdown(); - - return result; -} diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/CHANGELOG.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/CHANGELOG.rst deleted file mode 100644 index 8b56330..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/CHANGELOG.rst +++ /dev/null @@ -1,74 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ur_dashboard_msgs -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -2.5.2 (2025-01-21) ------------------- - -2.5.1 (2024-12-21) ------------------- - -2.5.0 (2024-12-18) ------------------- -* Update package maintainers (backport of `#1203 `_) -* Contributors: mergify[bot] - -2.2.16 (2024-10-28) -------------------- - -2.2.15 (2024-07-26) -------------------- - -2.2.14 (2024-07-01) -------------------- - -2.2.13 (2024-06-17) -------------------- - -2.2.12 (2024-05-16) -------------------- - -2.2.11 (2024-04-08) -------------------- - -2.2.10 (2024-01-03) -------------------- - -2.2.9 (2023-09-22) ------------------- - -2.2.8 (2023-06-26) ------------------- - -2.2.7 (2023-06-02) ------------------- - -2.2.6 (2022-11-28) ------------------- - -2.2.5 (2022-11-19) ------------------- - -2.2.4 (2022-10-07) ------------------- - -2.2.3 (2022-07-27) ------------------- - -2.2.2 (2022-07-19) ------------------- -* Made sure all past maintainers are listed as authors (`#429 `_) -* Contributors: Felix Exner - -2.2.1 (2022-06-27) ------------------- - -2.2.0 (2022-06-20) ------------------- -* Updated package maintainers -* Update license to BSD-3-Clause (`#277 `_) -* Review CI by correcting the configurations (`#71 `_) -* Use GitHub Actions, use pre-commit formatting (`#56 `_) -* Add XML schema to all ``package.xml`` files -* Compile ur_dashboard_msgs for ROS2 -* Contributors: AndyZe, Denis Štogl, Felix Exner, John Morris, livanov93 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/CMakeLists.txt b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/CMakeLists.txt deleted file mode 100644 index b04e37f..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(ur_dashboard_msgs) - -find_package(ament_cmake REQUIRED) -find_package(rosidl_default_generators REQUIRED) -find_package(action_msgs REQUIRED) -find_package(builtin_interfaces REQUIRED) - -set(msg_files - msg/ProgramState.msg - msg/RobotMode.msg - msg/SafetyMode.msg -) - -set(srv_files - srv/AddToLog.srv - srv/GetLoadedProgram.srv - srv/GetProgramState.srv - srv/GetRobotMode.srv - srv/GetSafetyMode.srv - srv/IsProgramRunning.srv - srv/IsProgramSaved.srv - srv/Load.srv - srv/Popup.srv - srv/RawRequest.srv -) - -set(action_files - action/SetMode.action -) - -rosidl_generate_interfaces(${PROJECT_NAME} - ${msg_files} - ${srv_files} - ${action_files} - DEPENDENCIES - action_msgs - builtin_interfaces -) - -ament_export_dependencies(rosidl_default_runtime) - -ament_package() diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/action/SetMode.action b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/action/SetMode.action deleted file mode 100644 index d110997..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/action/SetMode.action +++ /dev/null @@ -1,25 +0,0 @@ -# This action is for setting the robot into a desired mode (e.g. RUNNING) and safety mode into a -# non-critical state (e.g. NORMAL or REDUCED), for example after a safety incident happened. - -# goal -int8 target_robot_mode - -# Stop program execution before restoring the target mode. Can be used together with 'play_program'. -bool stop_program - -# Play the currently loaded program after target mode is reached.# -# NOTE: Requesting mode RUNNING in combination with this will make the robot continue the motion it -# was doing before. This might probably lead into the same problem (protective stop, EM-Stop due to -# faulty motion, etc.) If you want to be safe, set the 'stop_program' flag below and manually play -# the program after robot state is returned to normal. -# This flag will only be used when requesting mode RUNNING -bool play_program - ---- -# result -bool success -string message ---- -# feedback -int8 current_robot_mode -int8 current_safety_mode diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/ProgramState.msg b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/ProgramState.msg deleted file mode 100644 index 8fc8274..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/ProgramState.msg +++ /dev/null @@ -1,5 +0,0 @@ -string STOPPED=STOPPED -string PLAYING=PLAYING -string PAUSED=PAUSED - -string state diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/RobotMode.msg b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/RobotMode.msg deleted file mode 100644 index 05f792b..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/RobotMode.msg +++ /dev/null @@ -1,12 +0,0 @@ -int8 NO_CONTROLLER=-1 -int8 DISCONNECTED=0 -int8 CONFIRM_SAFETY=1 -int8 BOOTING=2 -int8 POWER_OFF=3 -int8 POWER_ON=4 -int8 IDLE=5 -int8 BACKDRIVE=6 -int8 RUNNING=7 -int8 UPDATING_FIRMWARE=8 - -int8 mode diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/SafetyMode.msg b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/SafetyMode.msg deleted file mode 100644 index 7db0851..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/msg/SafetyMode.msg +++ /dev/null @@ -1,15 +0,0 @@ -uint8 NORMAL=1 -uint8 REDUCED=2 -uint8 PROTECTIVE_STOP=3 -uint8 RECOVERY=4 -uint8 SAFEGUARD_STOP=5 -uint8 SYSTEM_EMERGENCY_STOP=6 -uint8 ROBOT_EMERGENCY_STOP=7 -uint8 VIOLATION=8 -uint8 FAULT=9 -uint8 VALIDATE_JOINT_ID=10 -uint8 UNDEFINED_SAFETY_MODE=11 -uint8 AUTOMATIC_MODE_SAFEGUARD_STOP=12 -uint8 SYSTEM_THREE_POSITION_ENABLING_STOP=13 - -uint8 mode diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/package.xml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/package.xml deleted file mode 100644 index de4cb45..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/package.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - ur_dashboard_msgs - 2.5.2 - Messages around the UR Dashboard server. - - Felix Exner - Rune Søe-Knudsen - Universal Robots A/S - - BSD-3-Clause - - https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver/issues - https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver - - Denis Stogl - Lovro Ivanov - Andy Zelenak - - ament_cmake - - rosidl_default_generators - - action_msgs - - rosidl_default_runtime - - rosidl_interface_packages - - - ament_cmake - - diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/AddToLog.srv b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/AddToLog.srv deleted file mode 100644 index 230cee4..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/AddToLog.srv +++ /dev/null @@ -1,4 +0,0 @@ -string message ---- -string answer -bool success diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetLoadedProgram.srv b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetLoadedProgram.srv deleted file mode 100644 index c564420..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetLoadedProgram.srv +++ /dev/null @@ -1,4 +0,0 @@ ---- -string answer -string program_name -bool success diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetProgramState.srv b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetProgramState.srv deleted file mode 100644 index 68a4b08..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetProgramState.srv +++ /dev/null @@ -1,5 +0,0 @@ ---- -ProgramState state -string program_name -string answer -bool success diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetRobotMode.srv b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetRobotMode.srv deleted file mode 100644 index 7b41950..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetRobotMode.srv +++ /dev/null @@ -1,4 +0,0 @@ ---- -RobotMode robot_mode -string answer -bool success diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetSafetyMode.srv b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetSafetyMode.srv deleted file mode 100644 index 5b3874f..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/GetSafetyMode.srv +++ /dev/null @@ -1,4 +0,0 @@ ---- -SafetyMode safety_mode -string answer -bool success diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/IsProgramRunning.srv b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/IsProgramRunning.srv deleted file mode 100644 index 61d6067..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/IsProgramRunning.srv +++ /dev/null @@ -1,4 +0,0 @@ ---- -string answer -bool program_running # is a program running? -bool success # Did the dashboard server call succeed? diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/IsProgramSaved.srv b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/IsProgramSaved.srv deleted file mode 100644 index 065bbf6..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/IsProgramSaved.srv +++ /dev/null @@ -1,5 +0,0 @@ ---- -string answer -string program_name -bool program_saved # is the current program saved? -bool success # Did the dashboard server call succeed? diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/Load.srv b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/Load.srv deleted file mode 100644 index 6883ab6..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/Load.srv +++ /dev/null @@ -1,5 +0,0 @@ -# Service to load programs or installations -string filename ---- -string answer -bool success diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/Popup.srv b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/Popup.srv deleted file mode 100644 index 230cee4..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/Popup.srv +++ /dev/null @@ -1,4 +0,0 @@ -string message ---- -string answer -bool success diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/RawRequest.srv b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/RawRequest.srv deleted file mode 100644 index 92712ad..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_dashboard_msgs/srv/RawRequest.srv +++ /dev/null @@ -1,4 +0,0 @@ -# This service is there to support any dashboard query not explicitly supported -string query ---- -string answer diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_moveit_config/config/controllers.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_moveit_config/config/controllers.yaml index c447a7c..49c5d28 100644 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_moveit_config/config/controllers.yaml +++ b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_moveit_config/config/controllers.yaml @@ -3,7 +3,6 @@ controller_names: - joint_trajectory_controller - panda_hand_controller - scaled_joint_trajectory_controller: action_ns: follow_joint_trajectory type: FollowJointTrajectory @@ -16,7 +15,6 @@ scaled_joint_trajectory_controller: - wrist_2_joint - wrist_3_joint - joint_trajectory_controller: action_ns: follow_joint_trajectory type: FollowJointTrajectory @@ -29,7 +27,6 @@ joint_trajectory_controller: - wrist_2_joint - wrist_3_joint - panda_hand_controller: action_ns: gripper_cmd type: GripperCommand diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/CHANGELOG.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/CHANGELOG.rst deleted file mode 100644 index d312a32..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/CHANGELOG.rst +++ /dev/null @@ -1,289 +0,0 @@ -2.5.2 (2025-01-21) ------------------- -* Check quaternions for equal dot_product instead of comparing their components individually (backport `#1238 `_) (`#1243 `_) -* fix sphinx doc link in ur_robot_driver (`#1240 `_) (`#1242 `_) -* Update pre-commit the same as on the main branch (`#1232 `_) -* Disable pose broadcaster on mock hardware (backport of `#1229 `_) (`#1230 `_) -* Remove unused include (backport of `#1220 `_) - Co-authored-by: Bence Magyar -* Contributors: Felix Exner, mergify[bot] - -2.5.1 (2024-12-21) ------------------- - -2.5.0 (2024-12-18) ------------------- -* Freedrive Controller (`#1114 `_) (`#1211 `_) -* Add force mode controller (`#1049 `_) (`#1193 `_) -* Update package maintainers (backport of `#1203 `_) -* Forward trajectory controller (backport of `#944 `_) -* Use pose_broadcaster to publish the TCP pose (backport of `#1108 `_) -* Contributors: mergify[bot] - -2.2.16 (2024-10-28) -------------------- -* Allow setting the analog output domain when setting an analog output (backport of `#1123 `_) -* Fix component lifecycle (backport of `#1098 `_) -* [moveit] Disable execution_duration_monitoring by default (`#1133 `_) -* Service to get software version of robot (backport of `#964 `_) -* Assure the description is loaded as string (backport of `#1107 `_) -* Contributors: Felix Exner (fexner), mergify[bot], Jacob Larsen - -2.2.15 (2024-07-26) -------------------- -* Fix passing launch_dashobard_client launch argument (backport of `#1057 `_) -* Updated the UR family photo on the readme (backport of `#1064 `_) -* Contributors: Rune Søoe-Knudsen, Felix Exner - -2.2.14 (2024-07-01) -------------------- -* Add sleep between controller stopper's controller queries (backport of `#1038 `_) -* Contributors: Felix Exner - -2.2.13 (2024-06-17) -------------------- -* Use robot_receive_timeout instead of keepalive_count (`#1009 `_) -* Remove extra spaces from start_ursim statement in tests (backport of `#1010 `_) -* Add calibration file to launch arguments (`#1001 `_) -* Contributors: Vincenzo Di Pentima, Felix Exner - -2.2.12 (2024-05-16) -------------------- -* Remove dependency to docker.io (backport `#985 `_) -* Simplify tests (backport of `#849 `_) -* Update installation instructions for source build (backport `#967 `_) -* Move installation instructions to subpage (backport `#870 `_) -* Reduce number of controller_spawners to 3 (`#928 `_) -* Fix multi-line strings in DeclareLaunchArgument (backport `#948 `_) -* "use_fake_hardware" for UR20 (`#950 `_) -* Contributors: Vincenzo Di Pentima, Felix Exner, Robert Wilbrandt, Matthijs van der Burgh - -2.2.11 (2024-04-08) -------------------- -* Add UR30 support (`#930 `_) -* Move communication setup to on_configure instead of on_activate (`#936 `_) -* Contributors: Felix Exner, Vincenzo Di Pentima - -2.2.10 (2024-01-03) -------------------- -* Add backward_ros to driver (`#872 `_) (`#878 `_) -* Port configuration (`#835 `_) (`#847 `_) -* Update link to MoveIt! documentation (`#845 `_) -* Contributors: mergify[bot] - -2.2.9 (2023-09-22) ------------------- -* Added a test that sjtc correctly aborts on violation of constraints -* Added support for UR20 (`#805 `_) -* Introduced tf_prefix into log handler (`#713 `_) -* Start ursim from lib (`#733 `_) -* Run robot driver test also with tf_prefix (`#729 `_) -* Urscript interface (`#721 `_) (`#742 `_) -* Contributors: Felix Exner, Lennart Nachtigall, mergify[bot] - -2.2.8 (2023-06-26) ------------------- -* Use tf prefix properly (backport `#688 `_) (`#725 `_) -* Use SCHED_FIFO for controller_manager's main thread (`#719 `_) (`#722 `_) -* Contributors: mergify[bot] - -2.2.7 (2023-06-02) ------------------- -* Calling on_deactivate in dtr (`#679 `_) (`#704 `_) -* Adds full nonblocking readout support (Multiarm part 4) - v2 (`#673 `_) (`#703 `_) -* Correct calibration correction launch file in doc (`#590 `_) -* Introduce hand back control service (`#528 `_) (`#670 `_) -* Update definition of test goals to new version. (backport `#637 `_) (`#668 `_) -* Default path to ur_client_library urscript (`#316 `_) (`#553 `_) - * Change default path for urscript for headless mode. - * Replace urscript path also in newer ur_robot_driver launchfile -* Wait longer for controllers to load and activate -* Fix flaky tests (`#641 `_) -* Added services to set tool voltage and zero force torque sensor (`#466 `_) (`#582 `_) -* Controller spawner timeout (backport `#608 `_) (`#609 `_) -* Fix cmake dependency on controller_manager (backport `#598 `_) (`#599 `_) -* Increase timeout for first test service call to driver (Backport of `#605 `_) (`#607 `_) -* Update linters & checkers (backport `#426 `_) (`#556 `_) -* Clean up & improve execution tests (Backport of `#512 `_) (`#552 `_) -* Contributors: Felix Exner (fexner), Lennart Nachtigall, Robert Wilbrandt, mergify[bot], Denis Stogl, livanov93, Mads Holm Peters - -2.2.6 (2022-11-28) ------------------- -* Cleanup humble branch (`#545 `_) -* Contributors: Felix Exner (fexner) - -2.2.5 (2022-11-19) ------------------- -* ur_robot_driver: Controller_stopper fix deprecation warning -* Fix tool voltage setup (`#526 `_) - * Move BEGIN_REPLACE inside of header - * Change default value of tool_voltage - Keeping this at 0 requires users to explicitly set it to non-zero. This way - we won't accitentally destroy hardware that cannot handle 24V. -* Added dependency to socat (`#527 `_) - This is needed for the tool forwarding. -* Add a note in the tool_comm doc about a URCap conflict (`#524 `_) - * Add a note in the tool_comm doc about a URCap conflict - * Update ur_robot_driver/doc/setup_tool_communication.rst - Co-authored-by: Mads Holm Peters <79145214+urmahp@users.noreply.github.com> - * Fix formatting and one spelling mistake - Co-authored-by: Mads Holm Peters <79145214+urmahp@users.noreply.github.com> -* Contributors: Felix Exner, Felix Exner (fexner) - -2.2.4 (2022-10-07) ------------------- -* Remove the custom ursim docker files (`#478 `_) - This has been migrated inside the docs and is not needed anymore. -* Remove duplicated update_rate parameter (`#479 `_) -* Contributors: Felix Exner - -2.2.3 (2022-07-27) ------------------- -* Adapt ros control api (`#448 `_) - * scaled jtc: Use get_interface_name instead of get_name - * Migrate from stopped controllers to inactive controllers - stopped controllers has been deprecated upstream -* Contributors: Felix Exner - -2.2.2 (2022-07-19) ------------------- -* Made sure all past maintainers are listed as authors (`#429 `_) -* Silence a compilation warning (`#425 `_) - Since setting the receive timeout takes the time_buffer as an argument - this raises a "may be used uninitialized" warning. Setting this to 0 - explicitly should prevent that. -* Doc: Fix IP address in usage->ursim section (`#422 `_) -* Contributors: Felix Exner - -2.2.1 (2022-06-27) ------------------- -* Fixed controller name for force_torque_sensor_broadcaster (`#411 `_) -* Contributors: Felix Exner - -2.2.0 (2022-06-20) ------------------- -* Updated package maintainers -* Rework bringup (`#403 `_) -* Prepare for humble (`#394 `_) -* Update dependencies on all packages (`#391 `_) -* Update HW-interface API for humble. (`#377 `_) -* Use types in hardware interface from ros2_control in local namespace (`#339 `_) -* Update header extension to remove compile warning. (`#285 `_) -* Add resource files from ROS World. (`#226 `_) -* Add sphinx documentation (`#340 `_) -* Update license to BSD-3-Clause (`#277 `_) -* Update ROS_INTERFACE.md to current driver (`#335 `_) -* Fix hardware interface names in error output (`#329 `_) -* Added controller stopper node (`#309 `_) -* Correct link to calibration extraction (`#310 `_) -* Start the tool communication script if the flag is set (`#267 `_) -* Change driver constructor and change calibration check (`#282 `_) -* Use GPIO tag from URDF in driver. (`#224 `_) -* Separate control node (`#281 `_) -* Add missing dependency on angles and update formatting for linters. (`#283 `_) -* Do not print an error output if writing is not possible (`#266 `_) -* Update features.md (`#250 `_) -* Tool communication (`#218 `_) -* Payload service (`#238 `_) -* Import transformation of force-torque into tcp frame from ROS1 driver (https://github.com/UniversalRobots/Universal_Robots_ROS_Driver/blob/master/ur_robot_driver/src/hardware_interface.cpp). (`#237 `_) -* Make reading and writing work when hardware is disconnected (`#233 `_) -* Add missing command and state interfaces to get everything working with the fake hardware and add some comment into xacro file to be clearer. (`#221 `_) -* Decrease the rate of async tasks. (`#223 `_) -* Change robot type. (`#220 `_) -* Driver to headless. (`#217 `_) -* Test execution tests (`#216 `_) -* Integration tests improvement (`#206 `_) -* Set start modes to empty. Avoid position ctrl loop on start. (`#211 `_) -* Add resend program service and enable headless mode (`#198 `_) -* Implement "choices" for robot_type param (`#204 `_) -* Calibration extraction package (`#186 `_) -* Add breaking api changes from ros2_control to hardware_interface (`#189 `_) -* Fix prepare and perform switch operation (`#191 `_) -* Update CI configuration to support galactic and rolling (`#142 `_) -* Dockerize ursim with driver in docker compose (`#144 `_) -* Enabling velocity mode (`#146 `_) -* Moved registering publisher and service to on_active (`#151 `_) -* Converted io_test and switch_on_test to ROS2 (`#124 `_) -* Added loghandler to handle log messages from the Client Library with … (`#126 `_) -* Removed dashboard client from hardware interface -* [WIP] Updated feature list (`#102 `_) -* Moved Async check out of script running check (`#112 `_) -* Fix gpio controller (`#103 `_) -* Fixed speed slider service call (`#100 `_) -* Adding missing backslash and only setting workdir once (`#108 `_) -* Added dockerfile for the driver (`#105 `_) -* Using official Universal Robot Client Library (`#101 `_) -* Reintegrating missing ur_client_library dependency since the break the building process (`#97 `_) -* Fix readme hardware setup (`#91 `_) -* Fix move to home bug (`#92 `_) -* Using modern python -* Some intermediate commit -* Remove obsolete and unused files and packages. (`#80 `_) -* Review CI by correcting the configurations (`#71 `_) -* Add support for gpios, update MoveIt and ros2_control launching (`#66 `_) -* Quickfix against move home bug -* Added missing initialization -* Use GitHub Actions, use pre-commit formatting (`#56 `_) -* Put dashboard services into corresponding namespace -* Start dashboard client from within the hardware interface -* Added try catch blocks for service calls -* Removed repeated declaration of timeout parameter which lead to connection crash -* Removed static service name in which all auto generated services where mapped -* Removed unused variable -* Fixed clang-format issue -* Removed all robot status stuff -* Exchanged hardcoded value for RobotState msgs enum -* Removed currently unused controller state variables -* Added placeholder for industrial_robot_status_interface -* Fixed clang issues -* Added checks for internal robot state machine -* Only load speed scaling interface -* Changed state interface to combined speed scaling factor -* Added missing formatting in hardware interface -* Initial version of the speed_scaling_state_controller -* Fix clang tidy in multiple pkgs. -* Clang tidy fix. -* Update force torque state controller. -* Prepare for testing. -* Fix decision breaker for position control. Make decision effect instantaneous. -* Use only position interface. -* Update hardware interface for ROS2 (`#8 `_) -* Update the dashboard client for ROS2 (`#5 `_) -* Hardware interface framework (`#3 `_) -* Add XML schema to all ``package.xml`` files -* Silence ``ament_lint_cmake`` errors -* Update packaging for ROS2 -* Update package.xml files so ``ros2 pkg list`` shows all pkgs -* Clean out ur_robot_driver for initial ROS2 compilation -* Compile ur_dashboard_msgs for ROS2 -* Delete all launch/config files with no UR5 relation -* Initial work toward compiling ur_robot_driver -* Update CMakeLists and package.xml for: - - ur5_moveit_config - - ur_bringup - - ur_description -* Change pkg versions to 0.0.0 -* Contributors: AndyZe, Denis Stogl, Denis Štogl, Felix Exner, John Morris, Lovro, Mads Holm Peters, Marvin Große Besselmann, Rune Søe-Knudsen, livanov93, Robert Wilbrandt - -0.0.3 (2019-08-09) ------------------- -* Added a service to end ROS control from ROS side -* Publish IO state on ROS topics -* Added write channel through RTDE with speed slider and IO services -* Added subscriber to send arbitrary URScript commands to the robot - -0.0.2 (2019-07-03) ------------------- -* Fixed dependencies and installation -* Updated README -* Fixed passing parameters through launch files -* Added support for correctly switching controllers during runtime and using the standard - joint_trajectory_controller -* Updated externalcontrol URCap to version 1.0.2 - + Fixed Script timeout when running the URCap inside of a looping tree - + Fixed a couple of typos -* Increased minimal required UR software version to 3.7/5.1 - -0.0.1 (2019-06-28) ------------------- -Initial release diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/CMakeLists.txt b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/CMakeLists.txt deleted file mode 100644 index 68c082a..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/CMakeLists.txt +++ /dev/null @@ -1,214 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(ur_robot_driver) - -# Default to off as this only works in environments where a new docker container can be started with the appropriate networking, which -# might not be possible e.g. inside the buildfarm -option( - UR_ROBOT_DRIVER_BUILD_INTEGRATION_TESTS - "Build integration tests using the start_ursim script" - OFF -) - -add_compile_options(-Wall) -add_compile_options(-Wextra) -add_compile_options(-Wno-unused-parameter) - -if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) - message("${PROJECT_NAME}: You did not request a specific build type: selecting 'RelWithDebInfo'.") - set(CMAKE_BUILD_TYPE RelWithDebInfo) -endif() - -find_package(ament_cmake REQUIRED) -find_package(backward_ros REQUIRED) -find_package(controller_manager REQUIRED) -find_package(controller_manager_msgs REQUIRED) -find_package(geometry_msgs REQUIRED) -find_package(hardware_interface REQUIRED) -find_package(pluginlib REQUIRED) -find_package(rclcpp REQUIRED) -find_package(rclcpp_lifecycle REQUIRED) -find_package(rclpy REQUIRED) -find_package(std_msgs REQUIRED) -find_package(std_srvs REQUIRED) -find_package(tf2_geometry_msgs REQUIRED) -find_package(ur_client_library REQUIRED) -find_package(ur_dashboard_msgs REQUIRED) -find_package(ur_msgs REQUIRED) - -include_directories(include) - -set(THIS_PACKAGE_INCLUDE_DEPENDS - controller_manager - controller_manager_msgs - geometry_msgs - hardware_interface - pluginlib - rclcpp - rclcpp_lifecycle - std_msgs - std_srvs - tf2_geometry_msgs - ur_client_library - ur_dashboard_msgs - ur_msgs -) - -add_library(ur_robot_driver_plugin - SHARED - src/dashboard_client_ros.cpp - src/hardware_interface.cpp - src/urcl_log_handler.cpp -) -target_link_libraries( - ur_robot_driver_plugin - ur_client_library::urcl -) -target_include_directories( - ur_robot_driver_plugin - PRIVATE - include -) -ament_target_dependencies( - ur_robot_driver_plugin - ${${PROJECT_NAME}_EXPORTED_TARGETS} - ${THIS_PACKAGE_INCLUDE_DEPENDS} -) -pluginlib_export_plugin_description_file(hardware_interface hardware_interface_plugin.xml) - -# -# dashboard_client -# -add_executable(dashboard_client - src/dashboard_client_ros.cpp - src/dashboard_client_node.cpp - src/urcl_log_handler.cpp -) -target_link_libraries(dashboard_client ${catkin_LIBRARIES} ur_client_library::urcl) -ament_target_dependencies(dashboard_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${THIS_PACKAGE_INCLUDE_DEPENDS}) - -# -# ur_ros2_control_node -# -add_executable(ur_ros2_control_node - src/ur_ros2_control_node.cpp -) -ament_target_dependencies(ur_ros2_control_node - controller_manager - rclcpp -) - -# -# controller_stopper_node -# -add_executable(controller_stopper_node - src/controller_stopper.cpp - src/controller_stopper_node.cpp -) -ament_target_dependencies(controller_stopper_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${THIS_PACKAGE_INCLUDE_DEPENDS}) - -add_executable(urscript_interface - src/urscript_interface.cpp -) -ament_target_dependencies(urscript_interface ${${PROJECT_NAME}_EXPORTED_TARGETS} ${THIS_PACKAGE_INCLUDE_DEPENDS}) - -install( - TARGETS dashboard_client ur_ros2_control_node controller_stopper_node urscript_interface - DESTINATION lib/${PROJECT_NAME} -) - -# INSTALL -install( - TARGETS ur_robot_driver_plugin - DESTINATION lib -) -install( - DIRECTORY include/ - DESTINATION include -) - -## EXPORTS -ament_export_include_directories( - include -) -ament_export_libraries( - ur_robot_driver_plugin -) - -install(DIRECTORY resources - DESTINATION share/${PROJECT_NAME} - REGEX "/ursim/" EXCLUDE -) - - -install(DIRECTORY include/ - DESTINATION include -) - -ament_export_dependencies( - hardware_interface - pluginlib - rclcpp - rclcpp_lifecycle - ${THIS_PACKAGE_INCLUDE_DEPENDS} -) - -# Install Python execs -ament_python_install_package(${PROJECT_NAME}) - -# Install Python executables -install(PROGRAMS - scripts/tool_communication.py - scripts/example_move.py - scripts/start_ursim.sh - examples/examples.py - examples/force_mode.py - DESTINATION lib/${PROJECT_NAME} -) - -install(PROGRAMS scripts/wait_dashboard_server.sh - DESTINATION bin -) - -install(PROGRAMS scripts/start_ursim.sh - DESTINATION lib/${PROJECT_NAME} -) - -install(DIRECTORY config launch - DESTINATION share/${PROJECT_NAME} -) - -ament_package() - -if(BUILD_TESTING) - find_package(ur_controllers REQUIRED) - find_package(ur_description REQUIRED) - find_package(ur_msgs REQUIRED) - find_package(launch_testing_ament_cmake) - - if(${UR_ROBOT_DRIVER_BUILD_INTEGRATION_TESTS}) - add_launch_test(test/launch_args.py - TIMEOUT - 180 - ) - add_launch_test(test/dashboard_client.py - TIMEOUT - 180 - ) - add_launch_test(test/robot_driver.py - TIMEOUT - 800 - ) - add_launch_test(test/integration_test_controller_switch.py - TIMEOUT - 800 - ) - add_launch_test(test/integration_test_force_mode.py - TIMEOUT - 800 - ) - add_launch_test(test/urscript_interface.py - TIMEOUT - 500 - ) - endif() -endif() diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/README.md b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/README.md deleted file mode 100644 index 69234b2..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/README.md +++ /dev/null @@ -1,96 +0,0 @@ -# ur_robot_driver - -This package contains the actual driver for UR robots. It is part of the *universal_robots_driver* -repository and requires other packages from that repository. Also, see the [main repository's -README](../README.md) for information on how to install and startup this driver. - -## ROS-API -The ROS API is documented in a [standalone document](doc/ROS_INTERFACE.md). - -## Technical details -The following image shows a very coarse overview of the driver's architecture. - -![Architecture overview](doc/architecture_coarse.svg "Architecture overview") - -Upon connection to the primary interface the robot sends version and calibration information which -is consumed by the *calibration_check*. If the calibration reported by the robot doesn't match the -one configured (See [calibration guide](../ur_calibration/README.md)) an error will be printed to Roslog. - -Real-time data from the robot is read through the RTDE interface. This is done automatically as soon -as a connection to the robot could be established. Thus joint states and IO data will be immediately -available. - -To actually control the robot, a program node from the **External Control** URCap must be running on -the robot interpreting commands sent from an external source. When this program is not running, no -controllers moving the robot around will be available. Please see the [initial setup -guide](doc/installation/robot_setup.rst) on how to install and start this on the robot. - -The URScript that will be running on the robot is requested by the **External Control** program node -from the remote ROS PC. The robot *ur_control.launch* file has a parameter called `urscript_file` to -select a different program than the default one that will be sent as a response to a program -request. - -**Custom script snippets** can be sent to the robot on a topic basis. By default, they will -interrupt other programs (such as the one controlling the robot). For a certain subset of functions, -it is however possible to send them as secondary programs. See [UR -documentation](https://www.universal-robots.com/articles/ur/programming/secondary-program/) -on details. -
-**Note to e-Series users:** -The robot won't accept script code from a remote source unless the robot is put into -*remote_control-mode*. However, if put into *remote_control-mode*, the program containing the -**External Control** program node can't be started from the panel. -For this purpose, please use the **dashboard** services to load, start and stop the main program -running on the robot. See the [ROS-API documentation](doc/ROS_INTERFACE.md) for details on the -dashboard services. - -For using the **tool communication interface** on e-Series robots, a `socat` script is prepared to -forward the robot's tool communication interface to a local device on the ROS PC. See [the tool -communication setup guide](doc/setup_tool_communication.rst) for details. - -This driver is using [ROS-Control](https://wiki.ros.org/ros_control) for any control statements. -Therefore, it can be used with all position-based controllers available in ROS-Control. However, we -recommend using the controllers from the `ur_controllers` package. See it's -[documentation](../ur_controllers/README.md) for details. **Note: Speed scaling support will only be -available using the controllers from `ur_controllers`** - -## A note about modes -The term **mode** is used in different meanings inside this driver. - -### Remote control mode -On the e-series the robot itself can operate in different command modes: It can be either in **local control -mode** where the teach pendant is the single point of command or in **remote control mode**, where -motions from the TP, starting & loading programs from the TP activating the freedrive mode are -blocked. Note that the **remote control mode** has to be explicitly enabled in the robot's settings -under **Settings** -> **System** -> **Remote Control**. See the robot's manual for details. - -The **remote control mode** is needed for many aspects of this driver such as - * headless mode (see below) - * sending script code to the robot - * many dashboard functionalities such as - * restarting the robot after protective / EM-Stop - * powering on the robot and do brake release - * loading and starting programs - * the `set_mode` action, as it uses the dashboard calls mentioned above - -### Headless mode -Inside this driver, there's the **headless** mode, which can be either enabled or not. When the -[headless mode](./doc/ROS_INTERFACE.md#headless_mode-default-false) is activated, required script -code for external control will be sent to the robot directly when the driver starts. As soon as -other script code is sent to the robot either by sending it directly through this driver or by -pressing any motion-related button on the teach pendant, the script will be overwritten by this -action and has to be restarted by using the -[resend_robot_program](./doc/ROS_INTERFACE.md#resend_robot_program-std_srvstrigger) service. If this -is necessary, you will see the output `Connection to robot dropped, waiting for new connection.` -from the driver. Note that pressing "play" on the TP won't start the external control again, but -whatever program is currently loaded on the controller. This mode doesn't require the "External -Control" URCap being installed on the robot as the program is sent to the robot directly. However, -we recommend to use the non-headless mode and leverage the `set_mode` action to start program -execution without the teach pendant. The **headless** mode might be removed in future releases. - -**Note for the e-Series:** In order to leverage the **headless** mode on the e-Series the robot must -be in **remote_control_mode** as explained above. - -## controller_stopper -A small helper node that stops and restarts ROS controllers based on a boolean status topic. When the status goes to `false`, all running controllers except a set of predefined *consistent_controllers* gets stopped. If status returns to `true` the stopped controllers are restarted. -This is done by Subscribing to a robot's running state topic. Ideally this topic is latched and only publishes on changes. However, this node only reacts on state changes, so a state published each cycle would also be fine. diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/test_goal_publishers_config.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/test_goal_publishers_config.yaml deleted file mode 100644 index 79388fc..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/test_goal_publishers_config.yaml +++ /dev/null @@ -1,65 +0,0 @@ -publisher_scaled_joint_trajectory_controller: - ros__parameters: - - controller_name: "scaled_joint_trajectory_controller" - wait_sec_between_publish: 6 - - goal_names: ["pos1", "pos2", "pos3", "pos4"] - pos1: - positions: [0.785, -1.57, 0.785, 0.785, 0.785, 0.785] - pos2: - positions: [0.0, -1.57, 0.0, 0.0, 0.0, 0.0] - pos3: - positions: [0.0, -1.57, 0.0, 0.0, -0.785, 0.0] - pos4: - positions: [0.0, -1.57, 0.0, 0.0, 0.0, 0.0] - - joints: - - shoulder_pan_joint - - shoulder_lift_joint - - elbow_joint - - wrist_1_joint - - wrist_2_joint - - wrist_3_joint - - check_starting_point: true - starting_point_limits: - shoulder_pan_joint: [-0.1,0.1] - shoulder_lift_joint: [-1.6,-1.5] - elbow_joint: [-0.1,0.1] - wrist_1_joint: [-1.6,-1.5] - wrist_2_joint: [-0.1,0.1] - wrist_3_joint: [-0.1,0.1] - -publisher_joint_trajectory_controller: - ros__parameters: - - controller_name: "joint_trajectory_controller" - wait_sec_between_publish: 6 - - goal_names: ["pos1", "pos2", "pos3", "pos4"] - pos1: - positions: [0.785, -1.57, 0.785, 0.785, 0.785, 0.785] - pos2: - positions: [0.0, -1.57, 0.0, 0.0, 0.0, 0.0] - pos3: - positions: [0.0, -1.57, 0.0, 0.0, -0.785, 0.0] - pos4: - positions: [0.0, -1.57, 0.0, 0.0, 0.0, 0.0] - - joints: - - shoulder_pan_joint - - shoulder_lift_joint - - elbow_joint - - wrist_1_joint - - wrist_2_joint - - wrist_3_joint - - check_starting_point: true - starting_point_limits: - shoulder_pan_joint: [-0.1,0.1] - shoulder_lift_joint: [-1.6,-1.5] - elbow_joint: [-0.1,0.1] - wrist_1_joint: [-1.6,-1.5] - wrist_2_joint: [-0.1,0.1] - wrist_3_joint: [-0.1,0.1] diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/test_velocity_goal_publishers_config.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/test_velocity_goal_publishers_config.yaml deleted file mode 100644 index 4d27858..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/test_velocity_goal_publishers_config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -publisher_forward_velocity_controller: - ros__parameters: - - controller_name: "forward_velocity_controller" - wait_sec_between_publish: 5 - - goal_names: ["pos1", "pos2", "pos3"] - pos1: [0.0, 0.0, 0.0, 0.0, 0.0, 0.05] - pos2: [0.0, 0.0, 0.0, 0.0, 0.0, -0.05] - pos3: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur10_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur10_update_rate.yaml deleted file mode 100644 index 7525e1e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur10_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 125 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur10e_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur10e_update_rate.yaml deleted file mode 100644 index 66ef3d7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur10e_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 500 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur16e_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur16e_update_rate.yaml deleted file mode 100644 index 66ef3d7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur16e_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 500 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur20_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur20_update_rate.yaml deleted file mode 100644 index 66ef3d7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur20_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 500 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur30_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur30_update_rate.yaml deleted file mode 100644 index 66ef3d7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur30_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 500 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur3_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur3_update_rate.yaml deleted file mode 100644 index 7525e1e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur3_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 125 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur3e_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur3e_update_rate.yaml deleted file mode 100644 index 66ef3d7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur3e_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 500 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur5_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur5_update_rate.yaml deleted file mode 100644 index 7525e1e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur5_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 125 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur5e_update_rate.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur5e_update_rate.yaml deleted file mode 100644 index 66ef3d7..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur5e_update_rate.yaml +++ /dev/null @@ -1,3 +0,0 @@ -controller_manager: - ros__parameters: - update_rate: 500 # Hz diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur_controllers.yaml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur_controllers.yaml deleted file mode 100644 index 5e996bd..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/config/ur_controllers.yaml +++ /dev/null @@ -1,177 +0,0 @@ -controller_manager: - ros__parameters: - joint_state_broadcaster: - type: joint_state_broadcaster/JointStateBroadcaster - - io_and_status_controller: - type: ur_controllers/GPIOController - - speed_scaling_state_broadcaster: - type: ur_controllers/SpeedScalingStateBroadcaster - - force_torque_sensor_broadcaster: - type: force_torque_sensor_broadcaster/ForceTorqueSensorBroadcaster - - joint_trajectory_controller: - type: joint_trajectory_controller/JointTrajectoryController - - scaled_joint_trajectory_controller: - type: ur_controllers/ScaledJointTrajectoryController - - forward_velocity_controller: - type: velocity_controllers/JointGroupVelocityController - - forward_position_controller: - type: position_controllers/JointGroupPositionController - - force_mode_controller: - type: ur_controllers/ForceModeController - - freedrive_mode_controller: - type: ur_controllers/FreedriveModeController - - passthrough_trajectory_controller: - type: ur_controllers/PassthroughTrajectoryController - - tcp_pose_broadcaster: - type: pose_broadcaster/PoseBroadcaster - - passthrough_trajectory_controller: - type: ur_controllers/PassthroughTrajectoryController - - ur_configuration_controller: - type: ur_controllers/URConfigurationController - -speed_scaling_state_broadcaster: - ros__parameters: - state_publish_rate: 100.0 - tf_prefix: "$(var tf_prefix)" - -io_and_status_controller: - ros__parameters: - tf_prefix: "$(var tf_prefix)" - -ur_configuration_controller: - ros__parameters: - tf_prefix: "$(var tf_prefix)" - -force_torque_sensor_broadcaster: - ros__parameters: - sensor_name: $(var tf_prefix)tcp_fts_sensor - state_interface_names: - - force.x - - force.y - - force.z - - torque.x - - torque.y - - torque.z - frame_id: $(var tf_prefix)tool0 - topic_name: ft_data - - -joint_trajectory_controller: - ros__parameters: - joints: - - $(var tf_prefix)shoulder_pan_joint - - $(var tf_prefix)shoulder_lift_joint - - $(var tf_prefix)elbow_joint - - $(var tf_prefix)wrist_1_joint - - $(var tf_prefix)wrist_2_joint - - $(var tf_prefix)wrist_3_joint - command_interfaces: - - position - state_interfaces: - - position - - velocity - state_publish_rate: 100.0 - action_monitor_rate: 20.0 - allow_partial_joints_goal: false - constraints: - stopped_velocity_tolerance: 0.2 - goal_time: 0.0 - $(var tf_prefix)shoulder_pan_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)shoulder_lift_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)elbow_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_1_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_2_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_3_joint: { trajectory: 0.2, goal: 0.1 } - - -scaled_joint_trajectory_controller: - ros__parameters: - joints: - - $(var tf_prefix)shoulder_pan_joint - - $(var tf_prefix)shoulder_lift_joint - - $(var tf_prefix)elbow_joint - - $(var tf_prefix)wrist_1_joint - - $(var tf_prefix)wrist_2_joint - - $(var tf_prefix)wrist_3_joint - command_interfaces: - - position - state_interfaces: - - position - - velocity - state_publish_rate: 100.0 - action_monitor_rate: 20.0 - allow_partial_joints_goal: false - constraints: - stopped_velocity_tolerance: 0.2 - goal_time: 0.0 - $(var tf_prefix)shoulder_pan_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)shoulder_lift_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)elbow_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_1_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_2_joint: { trajectory: 0.2, goal: 0.1 } - $(var tf_prefix)wrist_3_joint: { trajectory: 0.2, goal: 0.1 } - speed_scaling_interface_name: $(var tf_prefix)speed_scaling/speed_scaling_factor - -passthrough_trajectory_controller: - ros__parameters: - tf_prefix: "$(var tf_prefix)" - joints: - - $(var tf_prefix)shoulder_pan_joint - - $(var tf_prefix)shoulder_lift_joint - - $(var tf_prefix)elbow_joint - - $(var tf_prefix)wrist_1_joint - - $(var tf_prefix)wrist_2_joint - - $(var tf_prefix)wrist_3_joint - state_interfaces: - - position - - velocity - speed_scaling_interface_name: $(var tf_prefix)speed_scaling/speed_scaling_factor - -forward_velocity_controller: - ros__parameters: - joints: - - $(var tf_prefix)shoulder_pan_joint - - $(var tf_prefix)shoulder_lift_joint - - $(var tf_prefix)elbow_joint - - $(var tf_prefix)wrist_1_joint - - $(var tf_prefix)wrist_2_joint - - $(var tf_prefix)wrist_3_joint - interface_name: velocity - -forward_position_controller: - ros__parameters: - joints: - - $(var tf_prefix)shoulder_pan_joint - - $(var tf_prefix)shoulder_lift_joint - - $(var tf_prefix)elbow_joint - - $(var tf_prefix)wrist_1_joint - - $(var tf_prefix)wrist_2_joint - - $(var tf_prefix)wrist_3_joint - -force_mode_controller: - ros__parameters: - tf_prefix: "$(var tf_prefix)" - -freedrive_mode_controller: - ros__parameters: - tf_prefix: "$(var tf_prefix)" - -tcp_pose_broadcaster: - ros__parameters: - frame_id: $(var tf_prefix)base - pose_name: $(var tf_prefix)tcp_pose - tf: - child_frame_id: $(var tf_prefix)tool0_controller diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/ROS_INTERFACE.md b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/ROS_INTERFACE.md deleted file mode 100644 index 829302e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/ROS_INTERFACE.md +++ /dev/null @@ -1,213 +0,0 @@ -**NOTE**: This documentation is obsolete and does not cover in full the current state of driver. - -# ROS interface - -The new driver for Universal Robots UR3, UR5 and UR10 robots with CB3 controllers and the e-series. - -## Nodes - -### ur_ros2_control_node - -This is the actual driver node containing the ROS2-Control stack. Interfaces documented here refer to the robot's hardware interface. Controller-specific API elements might be present for the individual controllers outside of this package. - -#### Parameters -Note that parameters are passed through the ros2_control xacro definition. - -##### headless_mode - -Start robot in headless mode. This does not require the 'External Control' URCap to be running on the robot, but this will send the URScript to the robot directly. On e-Series robots this requires the robot to run in 'remote-control' mode. - -##### input_recipe_filename (Required) - -Path to the file containing the recipe used for requesting RTDE inputs. - -##### kinematics/hash (Required) - -Hash of the calibration reported by the robot. This is used for validating the robot description is using the correct calibration. If the robot's calibration doesn't match this hash, an error will be printed. You can use the robot as usual, however Cartesian poses of the endeffector might be inaccurate. See the "ur_calibration" package on help how to generate your own hash matching your actual robot. - -##### non_blocking_read (default: "false") - -Enables non_blocking_read mode. Disables error generated when read returns without any data, sets -the read timeout to zero, and synchronizes read/write operations. This is a leftover from the ROS1 -driver and we currently see no real application where it would make sense to enable this. - -##### output_recipe_filename (Required) - -Path to the file containing the recipe used for requesting RTDE outputs. - -##### reverse_port (Required) - -Port that will be opened to communicate between the driver and the robot controller. - -##### robot_ip (Required) - -The robot's IP address. - -##### script_filename (Required) - -Path to the urscript code that will be sent to the robot. - -##### script_sender_port (Required) - -The driver will offer an interface to receive the program's URScript on this port. - -##### servoj_gain (Required) - -Specify the gain for the underlying servoj command. This will be used whenever position control is -active. A higher value will lead to sharper motions, but might also introduce -higher jerks and vibrations. - -Range: [100 - 3000] - -##### servoj_lookahead_time (Required) - -Specify lookahead_time parameter of underlying servoj command. This will be used whenever position -control is active. A higher value will result in smoother trajectories, but will also introduce a -higher delay between the commands sent from ROS and the motion being executed on the robot. - -Unit: seconds, range: [0.03 - 0.2] - - - -##### tool_baud_rate (Required) - -Baud rate used for tool communication. Will be set as soon as the UR-Program on the robot is started. See UR documentation for valid baud rates. Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### tool_parity (Required) - -Parity used for tool communication. Will be set as soon as the UR-Program on the robot is started. Can be 0 (None), 1 (odd) and 2 (even). Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### tool_rx_idle_chars (Required) - -Number of idle chars for the RX unit used for tool communication. Will be set as soon as the UR-Program on the robot is started. Valid values: min=1.0, max=40.0 Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### tool_stop_bits (Required) - -Number of stop bits used for tool communication. Will be set as soon as the UR-Program on the robot is started. Can be 1 or 2. Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### tool_tx_idle_chars (Required) - -Number of idle chars for the TX unit used for tool communication. Will be set as soon as the UR-Program on the robot is started. Valid values: min=0.0, max=40.0 Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### tool_voltage (Required) - -Tool voltage that will be set as soon as the UR-Program on the robot is started. Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### use_tool_communication (Required) - -Should the tool's RS485 interface be forwarded to the ROS machine? This is only available on e-Series models. Setting this parameter to TRUE requires multiple other parameters to be set,as well. - - -### dashboard_client - -#### Advertised Services - -##### add_to_log ([ur_dashboard_msgs/AddToLog](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/AddToLog.html)) - -Service to add a message to the robot's log - -##### brake_release ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Service to release the brakes. If the robot is currently powered off, it will get powered on on the fly. - -##### clear_operational_mode ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -If this service is called the operational mode can again be changed from PolyScope, and the user password is enabled. - -##### close_popup ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Close a (non-safety) popup on the teach pendant. - -##### close_safety_popup ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Close a safety popup on the teach pendant. - -##### connect ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Service to reconnect to the dashboard server - -##### get_loaded_program ([ur_dashboard_msgs/GetLoadedProgram](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/GetLoadedProgram.html)) - -Load a robot installation from a file - -##### get_robot_mode ([ur_dashboard_msgs/GetRobotMode](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/GetRobotMode.html)) - -Service to query the current robot mode - -##### get_safety_mode ([ur_dashboard_msgs/GetSafetyMode](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/GetSafetyMode.html)) - -Service to query the current safety mode - -##### load_installation ([ur_dashboard_msgs/Load](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/Load.html)) - -Load a robot installation from a file - -##### load_program ([ur_dashboard_msgs/Load](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/Load.html)) - -Load a robot program from a file - -##### pause ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Pause a running program. - -##### play ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Start execution of a previously loaded program - -##### popup ([ur_dashboard_msgs/Popup](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/Popup.html)) - -Service to show a popup on the UR Teach pendant. - -##### power_off ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Power off the robot motors - -##### power_on ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Power on the robot motors. To fully start the robot, call 'brake_release' afterwards. - -##### program_running ([ur_dashboard_msgs/IsProgramRunning](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/IsProgramRunning.html)) - -Query whether there is currently a program running - -##### program_saved ([ur_dashboard_msgs/IsProgramSaved](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/IsProgramSaved.html)) - -Query whether the current program is saved - -##### program_state ([ur_dashboard_msgs/GetProgramState](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/GetProgramState.html)) - -Service to query the current program state - -##### quit ([ur_dashboard_msgs/GetLoadedProgram](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/GetLoadedProgram.html)) - -Disconnect from the dashboard service. - -##### raw_request ([ur_dashboard_msgs/RawRequest](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/RawRequest.html)) - -General purpose service to send arbitrary messages to the dashboard server - -##### restart_safety ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Used when robot gets a safety fault or violation to restart the safety. After safety has been rebooted the robot will be in Power Off. NOTE: You should always ensure it is okay to restart the system. It is highly recommended to check the error log before using this command (either via PolyScope or e.g. ssh connection). - -##### shutdown ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Shutdown the robot controller - -##### stop ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Stop program execution on the robot - -##### unlock_protective_stop ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Dismiss a protective stop to continue robot movements. NOTE: It is the responsibility of the user to ensure the cause of the protective stop is resolved before calling this service. - -#### Parameters - -##### receive_timeout (Required) - -Timeout after which a call to the dashboard server will be considered failure if no answer has been received. - -##### robot_ip (Required) - -The IP address under which the robot is reachable. diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/architecture_coarse.svg b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/architecture_coarse.svg deleted file mode 100644 index 2dfd9e8..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/architecture_coarse.svg +++ /dev/null @@ -1,302 +0,0 @@ - - - - - - - - - - - - - - Robot - - - - - - - - - - - Primary Interface - - - - - - - - Secondary Interface - - - - - - - - RTDE Interface - - - - - - - - RS485 Daemon - - - - - - - - ExternalControl - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ROS-Driver - - - - - - - - - - - - - Initialization - - calibration_check - - - - - - - - - - Controllers - - speed_scaling - - tcp_force - - joint_states - - joint_control - - - - - - - - - - Custom Scripts - - URScript snippets - - - - - - - - - - IO Interface - Read / Write - - - - - - - - - - ScriptSender - URScript - - - - - - - - RS485 Device - - - - - - - - - - - - - - - - Request - - - Response - - - - - - diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/conf.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/conf.py deleted file mode 100644 index 1c42fdb..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/conf.py +++ /dev/null @@ -1,175 +0,0 @@ -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = "ur_robot_driver" -copyright = "2022, Universal Robots A/S" -author = "Felix Exner" - -# The short X.Y version -version = "" -# The full version, including alpha/beta/rc tags -release = "" - - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -source_suffix = [".rst", ".md"] - -# The master toctree document. -master_doc = "index" - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = None - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "alabaster" - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = "ur_robot_driverdoc" - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ( - master_doc, - "ur_robot_driver.tex", - "ur\\_robot\\_driver Documentation", - "Felix Exner", - "manual", - ), -] - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [(master_doc, "ur_robot_driver", "ur_robot_driver Documentation", [author], 1)] - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ( - master_doc, - "ur_robot_driver", - "ur_robot_driver Documentation", - author, - "ur_robot_driver", - "One line description of project.", - "Miscellaneous", - ), -] - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ["search.html"] diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/features.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/features.rst deleted file mode 100644 index 535f3be..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/features.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. role:: raw-html-m2r(raw) - :format: html - - -Feature list and roadmap ------------------------- - -.. list-table:: - :header-rows: 1 - - * - Feature - - ROS2 Driver - * - joint-position-based control - - yes - * - scaled joint-position-based control - - yes - * - joint-velocity-based control - - yes\ :raw-html-m2r:`1` - * - Cartesian position-based control - - no - * - Cartesian twist-based control - - no - * - Trajectory forwarding for execution on robot - - no - * - reporting of tcp wrench - - yes - * - pausing of programs - - yes - * - continue trajectories after EM-Stop resume - - yes - * - continue trajectories after protective stop - - yes - * - panel interaction in between possible - - yes - * - get and set IO states - - yes - * - use `tool communication forwarder `_ on e-series - - yes - * - use the driver without a teach pendant necessary - - yes - * - support of CB1 and CB2 robots - - no - * - trajectory extrapolation on robot on missing packages - - yes - * - use ROS as drop-in for TP-programs - - yes - * - headless mode - - yes - * - extract calibration from robot - - yes - * - send custom script commands to robot - - no - * - Reconnect on a disconnected robot - - yes - - -:raw-html-m2r:`1` Velocity-based joint control is implemented in the driver, but the current version of ros2_control do not yet support Velocity-based joint trajectory control diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/index.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/index.rst deleted file mode 100644 index bf536ad..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/index.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. ur_robot_driver documentation master file, created by - sphinx-quickstart on Fri Apr 8 13:58:02 2022. - You can adapt this file completely to your liking, but it should at least - contain the root ``toctree`` directive. - -Welcome to ur_robot_driver's documentation! -=========================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - overview - installation/toc - usage - setup_tool_communication - ROS_INTERFACE - generated/index - - - -Indices and tables -================== - -* :ref:`genindex` diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/make.bat b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/make.bat deleted file mode 100644 index 558ef60..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/overview.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/overview.rst deleted file mode 100644 index 07721cc..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/overview.rst +++ /dev/null @@ -1,22 +0,0 @@ -Overview -======== - -This driver collaborates closely with other ROS packages: - -``ur_bringup`` - Package to launch the driver, simulated robot and test scripts. -``ur_calibration`` - Package containing the calibration extraction program that will extract parameters for correctly - parametrizing the URDF with calibration data from the specific robot. -``ur_controllers`` - Controllers specifically made for UR manipulators. -``ur_dashboard_msgs`` - Message packages used for the `dashboard `_ communication. -``ur_moveit_config`` - MoveIt! configuration for a plain robot. This is good as a starting point, but for a real - application you would rather create your own MoveIt! configuration package containing your actual - robot environment. -``ur_description`` (`separate repository `_) - URDF description for UR manipulators - -.. include:: features.rst diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/resources/2021-10_ROS_World_2021_Making_a_robot_ROS2_powered.pdf b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/resources/2021-10_ROS_World_2021_Making_a_robot_ROS2_powered.pdf deleted file mode 100644 index 56d8144792170119aca9d8ee74375735336cd0d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132 zcmWN?OA^8$3;@tQr{DsX3FQ-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 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/setup_tool_communication.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/setup_tool_communication.rst deleted file mode 100644 index ff65168..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/setup_tool_communication.rst +++ /dev/null @@ -1,76 +0,0 @@ -.. _setup-tool-communication: - -Setting up the tool communication on an e-Series robot -====================================================== - -.. note:: - Currently, there seems to be issues with having the tool communication setup from this guide and - the "Robotiq Grippers" URCap running in parallel. If you are planning to use the "Robotiq - Grippers" URCap then currently please refrain from installing the rs485 URCap. - - See - `this issue `_ - for details and the current state. - -The Universal Robots e-Series provides an rs485 based interface at the tool flange that can be used -to attach an rs485-based device to the robot's tcp without the need to wire a separate cable along -the robot. - -This driver enables forwarding this tool communication interface to an external machine for example -to start a device's ROS driver on a remote PC. - -This document will guide you through installing the URCap needed for this and setting up your ROS -launch files to utilize the robot's tool communication. - -Robot setup ------------ - -For setting up the robot, please install the **rs485-1.0.urcap** found in the **resources** folder. -Installing a URCap is explained in the :ref:`setup guide ` for the **external-control** URCap. - -After installing the URCap the robot will expose its tool communication device to the network. - -Setup the ROS side ------------------- - -In order to use the tool communication in ROS, simply pass the correct parameters to the bringup -launch files: - -.. code-block:: bash - - $ ros2 launch ur_robot_driver ur_control.launch.py ur_type:=ur5e robot_ip:=yyy.yyy.yyy.yyy use_tool_communication:=true use_fake_hardware:=false launch_rviz:=false - # remember that your user needs to have the rights to write that file handle to /tmp/ttyUR - -Following parameters can be set `ur.ros2_control.xacro `_\ : - - -* ``tool_voltage`` -* ``tool_parity`` -* ``tool_baud_rate`` -* ``tool_stop_bits`` -* ``tool_rx_idle_chars`` -* ``tool_tx_idle_chars`` -* ``tool_device_name`` -* ``tool_tcp_port`` - -The ``tool_device_name`` is an arbitrary name for the device file at which the device will be -accessible in the local file system. Most ROS drivers for rs485 devices accept an argument to -specify the device file path. With the example above you could run the ``rs485_node`` from the package -``imaginary_drivers`` using the following command: - -.. code-block:: bash - - $ ros2 run imaginary_drivers rs485_node --ros-args -p device:=/tmp/ttyUR - -You can basically choose any device name, but your user has to have the correct rights to actually -create a new file handle inside this directory. Therefore, we didn't use the ``/dev`` folder in the -example, as users usually don't have the access rights to create new files there. - -For all the other tool parameters seen above, please refer to the Universal Robots user manual. - -More information can be found at `Universal_Robots_ToolComm_Forwarder_URCap `_. -The convenience script for ``socat`` call is `here `_ for ROS2 driver and can be run by: - -.. code-block:: bash - - $ ros2 run ur_robot_driver tool_communication.py --ros-args -p robot_ip:=192.168.56.101 -p device_name:=/tmp/ttyUR diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/usage.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/usage.rst deleted file mode 100644 index 208177d..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/usage.rst +++ /dev/null @@ -1,320 +0,0 @@ -.. role:: raw-html-m2r(raw) - :format: html - - -Usage -===== - -Launch files ------------- - -For starting the driver there are two main launch files in the ``ur_robot_driver`` package. - - -* ``ur_control.launch.py`` - starts ros2_control node including hardware interface, joint state broadcaster and a controller. This launch file also starts ``dashboard_client`` if real robot is used. -* ``ur_dashboard_client.launch.py`` - start the dashboard client for UR robots. - -Also, there are predefined launch files for all supported types of UR robots. - -The arguments for launch files can be listed using ``ros2 launch ur_robot_driver .launch.py --show-args``. -The most relevant arguments are the following: - - -* ``ur_type`` (\ *mandatory* ) - a type of used UR robot (\ *ur3*\ , *ur3e*\ , *ur5*\ , *ur5e*\ , *ur10*\ , *ur10e*\ , or *ur16e*\ , *ur20*\ , *ur30*\ ). -* ``robot_ip`` (\ *mandatory* ) - IP address by which the root can be reached. -* ``use_fake_hardware`` (default: *false* ) - use simple hardware emulator from ros2_control. - Useful for testing launch files, descriptions, etc. See explanation below. -* ``initial_positions`` (default: dictionary with all joint values set to 0) - Allows passing a dictionary to set the initial joint values for the fake hardware from `ros2_control `_. It can also be set from a yaml file with the ``load_yaml`` commands as follows: - - .. code-block:: - - - - In this example, the **initial_positions_file** is a xacro argument that contains the absolute path to a yaml file. An example of the initial positions yaml file is as follows: - - .. code-block:: - - elbow_joint: 1.158 - shoulder_lift_joint: -0.953 - shoulder_pan_joint: 1.906 - wrist_1_joint: -1.912 - wrist_2_joint: -1.765 - wrist_3_joint: 0.0 - -* ``fake_sensor_commands`` (default: *false* ) - enables setting sensor values for the hardware emulators. - Useful for offline testing of controllers. - -* ``robot_controller`` (default: *joint_trajectory_controller* ) - controller for robot joints to be started. - Available controllers: - - - * joint_trajectory_controller - * scaled_joint_trajectory_controller - - Note: *joint_state_broadcaster*\ , *speed_scaling_state_broadcaster*\ , *force_torque_sensor_broadcaster*\ , and *io_and_status_controller* will always start. - - *HINT* : list all loaded controllers using ``ros2 control list_controllers`` command. - -**NOTE**\ : The package can simulate hardware with the ros2_control ``FakeSystem``. This emulator enables an environment for testing of "piping" of hardware and controllers, as well as testing robot's descriptions. For more details see `ros2_control documentation `_ for more details. - -Modes of operation ------------------- - -As mentioned in the last section the driver has two basic modes of operation: Using fake hardware or -using real hardware(Or the URSim simulator, which is equivalent from the driver's perspective). -Additionally, the robot can be simulated using -`Gazebo `_ or -`ignition `_ but that's -outside of this driver's scope. - -.. list-table:: - :header-rows: 1 - - * - mode - - available controllers - * - fake_hardware - - :raw-html-m2r:`
  • joint_trajectory_controller
  • forward_velocity_controller
  • forward_position_controller
` - * - real hardware / URSim - - :raw-html-m2r:`
  • joint_trajectory_controller
  • scaled_joint_trajectory_controller
  • forward_velocity_controller
  • forward_position_controller
` - - -Usage with official UR simulator -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The easiest way to use URSim is the `Docker -image `_ provided by Universal Robots (See -`this link `_ for a CB3-series image). - -To start it, we've prepared a script: - -.. code-block:: bash - - ros2 run ur_client_library start_ursim.sh -m - -With this, we can spin up a driver using - -.. code-block:: bash - - ros2 launch ur_robot_driver ur_control.launch.py ur_type:= robot_ip:=192.168.56.101 launch_rviz:=true - -You can view the polyscope GUI by opening ``_. - -When we now move the robot in Polyscope, the robot's RViz visualization should move accordingly. - -For details on the Docker image, please see the more detailed guide :ref:`here `. - -Example Commands for Testing the Driver ---------------------------------------- - -Allowed UR - Type strings: ``ur3``\ , ``ur3e``\ , ``ur5``\ , ``ur5e``\ , ``ur10``\ , ``ur10e``\ , ``ur16e``\ , ``ur20``, ``ur30``. - -1. Start hardware, simulator or mockup -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - -* To do test with hardware, use: - - .. code-block:: - - ros2 launch ur_robot_driver ur_control.launch.py ur_type:= robot_ip:= launch_rviz:=true - - For more details check the argument documentation with ``ros2 launch ur_robot_driver ur_control.launch.py --show-arguments`` - - After starting the launch file start the external_control URCap program from the pendant, as described above. - -* To do an offline test with URSim check details about it in `this section <#usage-with-official-ur-simulator>`_ - -* To use mocked hardware(capability of ros2_control), use ``use_fake_hardware`` argument, like: - - .. code-block:: - - ros2 launch ur_robot_driver ur_control.launch.py ur_type:=ur5e robot_ip:=yyy.yyy.yyy.yyy use_fake_hardware:=true launch_rviz:=true - - **NOTE**\ : Instead of using the global launch file for control stack, there are also prepeared launch files for each type of UR robots named. They accept the same arguments are the global one and are used by: - - .. code-block:: - - ros2 launch ur_robot_driver .launch.py - -2. Sending commands to controllers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Before running any commands, first check the controllers' state using ``ros2 control list_controllers``. - - -* Send some goal to the Joint Trajectory Controller by using a demo node from `ros2_control_demos `_ package by starting the following command in another terminal: - - .. code-block:: - - ros2 launch ur_robot_driver test_scaled_joint_trajectory_controller.launch.py - - After a few seconds the robot should move. - -* To test another controller, simply define it using ``initial_joint_controller`` argument, for example when using fake hardware: - - .. code-block:: - - ros2 launch ur_robot_driver ur_control.launch.py ur_type:=ur5e robot_ip:=yyy.yyy.yyy.yyy initial_joint_controller:=joint_trajectory_controller use_fake_hardware:=true launch_rviz:=true - - And send the command using demo node: - - .. code-block:: - - ros2 launch ur_robot_driver test_joint_trajectory_controller.launch.py - - After a few seconds the robot should move(or jump when using emulation). - -3. Using only robot description -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you just want to test description of the UR robots, e.g., after changes you can use the following command: - -.. code-block:: - - ros2 launch ur_description view_ur.launch.py ur_type:=ur5e - -Using MoveIt ------------- - -`MoveIt! `_ support is built-in into this driver already. - -Real robot / URSim -^^^^^^^^^^^^^^^^^^ - -To test the driver with the example MoveIt-setup, first start the driver as described -`above <#start-hardware-simulator-or-mockup>`_. - -.. code-block:: - - ros2 launch ur_moveit_config ur_moveit.launch.py ur_type:=ur5e launch_rviz:=true - -Now you should be able to use the MoveIt Plugin in rviz2 to plan and execute trajectories with the -robot as explained `here `_. - -.. note:: - The MoveIt configuration provided here has Trajectory Execution Monitoring (TEM) *disabled*, as the - Scaled Joint Trajectory Controller may cause trajectories to be executed at a lower velocity - than they were originally planned by MoveIt. MoveIt's TEM however is not aware of this - deliberate slow-down due to scaling and will in most cases unnecessarily (and unexpectedly) - abort goals. - - Until this incompatibility is resolved, the default value for ``execution_duration_monitoring`` - is set to ``false``. Users who wish to temporarily (re)enable TEM at runtime (for use with - other, non-scaling controllers) can do so using the ROS 2 parameter services supported by - MoveIt. - - .. literalinclude:: ../../ur_moveit_config/launch/ur_moveit.launch.py - :language: python - :start-at: trajectory_execution = - :end-at: execution_duration_monitoring": False - :append: } - :dedent: 4 - :caption: ur_moveit_config/launch/ur_moveit.launch.py - -Fake hardware on ROS2 Galactic -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Currently, the ``scaled_joint_trajectory_controller`` does not work on ROS2 Galactic. There is an -`upstream Merge-Request `_ pending to fix that. Until this is merged and released, please change the -default controller in the `controllers.yaml `_ file. Make -sure that the ``default`` field is assigned ``true`` for the ``joint_trajectory_controller`` and ``false`` -for the -``scaled_joint_trajectory_controller``. - -.. code-block:: - - controller_names: - - scaled_joint_trajectory_controller - - joint_trajectory_controller - - - scaled_joint_trajectory_controller: - action_ns: follow_joint_trajectory - type: FollowJointTrajectory - default: false - joints: - - shoulder_pan_joint - - shoulder_lift_joint - - elbow_joint - - wrist_1_joint - - wrist_2_joint - - wrist_3_joint - - - joint_trajectory_controller: - action_ns: follow_joint_trajectory - type: FollowJointTrajectory - default: true - joints: - - shoulder_pan_joint - - shoulder_lift_joint - - elbow_joint - - wrist_1_joint - - wrist_2_joint - - wrist_3_joint - -Then start - -.. code-block:: - - ros2 launch ur_robot_driver ur_control.launch.py ur_type:=ur5e robot_ip:=yyy.yyy.yyy.yyy use_fake_hardware:=true launch_rviz:=false initial_joint_controller:=joint_trajectory_controller - # and in another shell - ros2 launch ur_moveit_config ur_moveit.launch.py ur_type:=ur5e launch_rviz:=true - -Custom URScript commands ------------------------- - -The driver's package contains a ``urscript_interface`` node that allows sending URScript snippets -directly to the robot. It gets started in the driver's launchfiles by default. To use it, simply -publish a message to its interface: - -.. code-block:: bash - - # simple popup - ros2 topic pub /urscript_interface/script_command std_msgs/msg/String '{data: popup("hello")}' --once - -Be aware, that running a program on this interface (meaning publishing script code to that interface) stops any running program on the robot. -Thus, the motion-interpreting program that is started by the driver gets stopped and has to be -restarted again. Depending whether you use headless mode or not, you'll have to call the -``resend_program`` service or press the ``play`` button on the teach panel to start the -external_control program again. - -.. note:: - Currently, there is no feedback on the code's correctness. If the code sent to the - robot is incorrect, it will silently not get executed. Make sure that you send valid URScript code! - -Multi-line programs -^^^^^^^^^^^^^^^^^^^ - -When you want to define multi-line programs, make sure to check that newlines are correctly -interpreted from your message. For this purpose the driver prints the program as it is being sent to -the robot. When sending a multi-line program from the command line, you can use an empty line -between each statement: - -.. code-block:: bash - - ros2 topic pub --once /urscript_interface/script_command std_msgs/msg/String '{data: - "def my_prog(): - - set_digital_out(1, True) - - movej(p[0.2, 0.3, 0.8, 0, 0, 3.14], a=1.2, v=0.25, r=0) - - textmsg(\"motion finished\") - - end"}' - -Non-interrupting programs -^^^^^^^^^^^^^^^^^^^^^^^^^ - -To prevent interrupting the main program, you can send certain commands as `secondary programs -`_. - -.. code-block:: bash - - ros2 topic pub --once /urscript_interface/script_command std_msgs/msg/String '{data: - "sec my_prog(): - - textmsg(\"This is a log message\") - - end"}' diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/usage/controllers.rst b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/usage/controllers.rst deleted file mode 100644 index 67c3a49..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/doc/usage/controllers.rst +++ /dev/null @@ -1,127 +0,0 @@ -Controllers -=========== - -This help page describes the different controllers available for the ``ur_robot_driver``. This -should help users finding the right controller for their specific use case. - -Where are controllers defined? ------------------------------- - -Controllers are defined in the ``config/ur_controllers.yaml`` file. - -How do controllers get loaded and started? ------------------------------------------- - -As this driver uses `ros2_control `_ all controllers are managed by the -controller_manager. During startup, a default set of running controllers is loaded and started, -another set is loaded in stopped mode. Stopped controllers won't be usable right away, but they -need to be started individually. - -Controllers that are actually writing to some command interfaces (e.g. joint positions) will claim -those interfaces. Only one controller claiming a certain interface can be active at one point. - -Controllers can be switched either through the controller_manager's service calls, through the -`rqt_controller_manager `_ gui or through the ``ros2 control`` verb from the package ``ros-${ROS_DISTRO}-ros2controlcli`` package. - -For example, to switch from the default ``scaled_joint_trajectory_controller`` to the -``forward_position_controller`` you can call - -.. code-block:: console - - $ ros2 control switch_controllers --deactivate scaled_joint_trajectory_controller \ - --activate forward_position_controller - [INFO 2024-09-23 20:32:04.373] [_ros2cli_1207798]: waiting for service /controller_manager/switch_controller to become available... - Successfully switched controllers - -Read-only broadcasters ----------------------- - -These broadcasters are read-only. They read states from the robot and publish them on a ROS topic. -As they are read-only, they don't claim any resources and can be combined freely. By default, they -are all started and running. Those controllers do not require the robot to have the -external_control script running. - -joint_state_broadcaster -^^^^^^^^^^^^^^^^^^^^^^^ - -Type: `joint_state_broadcaster/JointStateBroadcaster `_ - -Publishes all joints' positions, velocities, and motor currents as ``sensor_msgs/JointState`` on the ``joint_states`` topic. - -.. note:: - - The effort field contains the currents reported by the joints and not the actual efforts in a - physical sense. - -speed_scaling_state_broadcaster -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Type: :ref:`ur_controllers/SpeedScalingStateBroadcaster ` - -This broadcaster publishes the current actual execution speed as reported by the robot. Values are -floating points between 0 and 1. - -force_torque_sensor_broadcaster -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Type: `force_torque_sensor_broadcaster/ForceTorqueSensorBroadcaster `_ - -Publishes the robot's wrench as reported from the controller. - -Commanding controllers ----------------------- - -The commanding controllers control the robot's motions. Those controllers can't be combined -arbitrarily, as they will claim hardware resources. Only one controller can claim one hardware -resource at a time. - -scaled_joint_trajectory_controller (Default motion controller) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Type: :ref:`ur_controllers/ScaledJointTrajectoryController ` - -Scaled version of the -`joint_trajectory_controller -`_. -It uses the robot's speed scaling information and thereby the safety compliance features, like pause on safeguard stop. In addition, it also makes it possible to adjust execution speed using the speed slider on the teach pendant or set the program in pause and restart it again. -See it's linked documentation for details. - -io_and_status_controller -^^^^^^^^^^^^^^^^^^^^^^^^ - -Type: :ref:`ur_controllers/GPIOController ` - -Allows setting I/O ports, controlling some UR-specific functionality and publishes status information about the robot. - -forward_velocity_controller -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Type: `velocity_controllers/JointGroupVelocityController `_ - -Allows setting target joint positions directly. The robot tries to reach the target position as -fast as possible. The user is therefore responsible for sending commands that are achievable. This -controller is particularly useful when doing servoing such as ``moveit_servo``. - -forward_position_controller -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Type: `position_controllers/JointGroupPositionController `_ - -Allows setting target joint velocities directly. The user is responsible for sending commands that -are achievable. This controller is particularly useful when doing servoing such as -``moveit_servo``. - -force_mode_controller -^^^^^^^^^^^^^^^^^^^^^ - -Type: :ref:`ur_controllers/ForceModeController ` - -Allows utilizing the robot's builtin *Force Mode*. - -freedrive_mode_controller -^^^^^^^^^^^^^^^^^^^^^^^^^ - -Type: :ref:`ur_controllers/FreedriveModeController ` - -Allows utilizing the robot's *Freedrive mode*, making it possible to manually move the robot's -joints. diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/examples/examples.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/examples/examples.py deleted file mode 100644 index 17b03a9..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/examples/examples.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2024, Universal Robots A/S -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import rclpy -from builtin_interfaces.msg import Duration -from control_msgs.action import FollowJointTrajectory - -from rclpy.action import ActionClient -from rclpy.node import Node -from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint -from ur_msgs.srv import SetIO -from controller_manager_msgs.srv import SwitchController -from std_srvs.srv import Trigger - -TIMEOUT_WAIT_SERVICE = 10 -TIMEOUT_WAIT_SERVICE_INITIAL = 60 -TIMEOUT_WAIT_ACTION = 10 - -ROBOT_JOINTS = [ - "shoulder_pan_joint", - "shoulder_lift_joint", - "elbow_joint", - "wrist_1_joint", - "wrist_2_joint", - "wrist_3_joint", -] - - -# Helper functions -def waitForService(node, srv_name, srv_type, timeout=TIMEOUT_WAIT_SERVICE): - client = node.create_client(srv_type, srv_name) - if client.wait_for_service(timeout) is False: - raise Exception(f"Could not reach service '{srv_name}' within timeout of {timeout}") - - node.get_logger().info(f"Successfully connected to service '{srv_name}'") - return client - - -def waitForAction(node, action_name, action_type, timeout=TIMEOUT_WAIT_ACTION): - client = ActionClient(node, action_type, action_name) - if client.wait_for_server(timeout) is False: - raise Exception( - f"Could not reach action server '{action_name}' within timeout of {timeout}" - ) - - node.get_logger().info(f"Successfully connected to action '{action_name}'") - return client - - -class Robot: - def __init__(self, node): - self.node = node - self.service_interfaces = { - "/io_and_status_controller/set_io": SetIO, - "/dashboard_client/play": Trigger, - "/controller_manager/switch_controller": SwitchController, - } - self.init_robot() - - def init_robot(self): - self.service_clients = { - srv_name: waitForService(self.node, srv_name, srv_type) - for (srv_name, srv_type) in self.service_interfaces.items() - } - - self.jtc_action_client = waitForAction( - self.node, - "/scaled_joint_trajectory_controller/follow_joint_trajectory", - FollowJointTrajectory, - ) - self.passthrough_trajectory_action_client = waitForAction( - self.node, - "/passthrough_trajectory_controller/follow_joint_trajectory", - FollowJointTrajectory, - ) - - def set_io(self, pin, value): - """Test to set an IO.""" - set_io_req = SetIO.Request() - set_io_req.fun = 1 - set_io_req.pin = pin - set_io_req.state = value - - self.call_service("/io_and_status_controller/set_io", set_io_req) - - def send_trajectory(self, waypts, time_vec, action_client): - """Send robot trajectory.""" - if len(waypts) != len(time_vec): - raise Exception("waypoints vector and time vec should be same length") - - # Construct test trajectory - joint_trajectory = JointTrajectory() - joint_trajectory.joint_names = ROBOT_JOINTS - for i in range(len(waypts)): - point = JointTrajectoryPoint() - point.positions = waypts[i] - point.time_from_start = time_vec[i] - joint_trajectory.points.append(point) - - # Sending trajectory goal - goal_response = self.call_action( - action_client, FollowJointTrajectory.Goal(trajectory=joint_trajectory) - ) - if not goal_response.accepted: - raise Exception("trajectory was not accepted") - - # Verify execution - result = self.get_result(action_client, goal_response) - return result.error_code == FollowJointTrajectory.Result.SUCCESSFUL - - def call_service(self, srv_name, request): - future = self.service_clients[srv_name].call_async(request) - rclpy.spin_until_future_complete(self.node, future) - if future.result() is not None: - return future.result() - else: - raise Exception(f"Exception while calling service: {future.exception()}") - - def call_action(self, ac_client, g): - future = ac_client.send_goal_async(g) - rclpy.spin_until_future_complete(self.node, future) - - if future.result() is not None: - return future.result() - else: - raise Exception(f"Exception while calling action: {future.exception()}") - - def get_result(self, ac_client, goal_response): - future_res = ac_client._get_result_async(goal_response) - rclpy.spin_until_future_complete(self.node, future_res) - if future_res.result() is not None: - return future_res.result().result - else: - raise Exception(f"Exception while calling action: {future_res.exception()}") - - -if __name__ == "__main__": - rclpy.init() - node = Node("robot_driver_test") - robot = Robot(node) - - # The following list are arbitrary joint positions, change according to your own needs - waypts = [ - [-1.6006, -1.7272, -2.2030, -0.8079, 1.5951, -0.0311], - [-1.2, -1.4, -1.9, -1.2, 1.5951, -0.0311], - [-1.6006, -1.7272, -2.2030, -0.8079, 1.5951, -0.0311], - ] - time_vec = [Duration(sec=4, nanosec=0), Duration(sec=8, nanosec=0), Duration(sec=12, nanosec=0)] - - # Execute trajectory on robot, make sure that the robot is booted and the control script is running - robot.send_trajectory(waypts, time_vec) - - # Set digital output 1 to true - robot.set_io(1, 1.0) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/examples/force_mode.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/examples/force_mode.py deleted file mode 100755 index 86ea05b..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/examples/force_mode.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2024, Universal Robots A/S -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import time - -import rclpy -from rclpy.node import Node -from controller_manager_msgs.srv import SwitchController -from builtin_interfaces.msg import Duration -from geometry_msgs.msg import Twist -from std_msgs.msg import Header -from std_srvs.srv import Trigger - -from geometry_msgs.msg import ( - Point, - Quaternion, - Pose, - PoseStamped, - Wrench, - Vector3, -) - -from ur_msgs.srv import SetForceMode - -from examples import Robot - -if __name__ == "__main__": - rclpy.init() - node = Node("robot_driver_test") - robot = Robot(node) - - # Add force mode service to service interfaces and re-init robot - robot.service_interfaces.update({"/force_mode_controller/start_force_mode": SetForceMode}) - robot.service_interfaces.update({"/force_mode_controller/stop_force_mode": Trigger}) - robot.init_robot() - time.sleep(0.5) - # Press play on the robot - robot.call_service("/dashboard_client/play", Trigger.Request()) - - time.sleep(0.5) - # Start controllers - robot.call_service( - "/controller_manager/switch_controller", - SwitchController.Request( - deactivate_controllers=["scaled_joint_trajectory_controller"], - activate_controllers=["passthrough_trajectory_controller", "force_mode_controller"], - strictness=SwitchController.Request.BEST_EFFORT, - ), - ) - - # Move robot in to position - robot.send_trajectory( - waypts=[[-1.5707, -1.5707, -1.5707, -1.5707, 1.5707, 0]], - time_vec=[Duration(sec=5, nanosec=0)], - action_client=robot.passthrough_trajectory_action_client, - ) - - # Finished moving - # Create task frame for force mode - point = Point(x=0.0, y=0.0, z=0.0) - orientation = Quaternion(x=0.0, y=0.0, z=0.0, w=1.0) - task_frame_pose = Pose() - task_frame_pose.position = point - task_frame_pose.orientation = orientation - header = Header(seq=1, frame_id="world") - header.stamp.sec = int(time.time()) + 1 - header.stamp.nanosec = 0 - frame_stamp = PoseStamped() - frame_stamp.header = header - frame_stamp.pose = task_frame_pose - - # Create compliance vector (which axes should be force controlled) - compliance = [False, False, True, False, False, False] - - # Create Wrench message for force mode - wrench_vec = Wrench(force=Vector3(x=0.0, y=0.0, z=-20.0), torque=Vector3(x=0.0, y=0.0, z=0.0)) - # Specify interpretation of task frame (no transform) - type_spec = SetForceMode.Request.NO_TRANSFORM - - # Specify max speeds and deviations of force mode - speed_limits = Twist() - speed_limits.linear = Vector3(x=0.0, y=0.0, z=1.0) - speed_limits.angular = Vector3(x=0.0, y=0.0, z=1.0) - deviation_limits = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0] - - # specify damping and gain scaling - damping_factor = 0.1 - gain_scale = 0.8 - - req = SetForceMode.Request() - req.task_frame = frame_stamp - req.selection_vector_x = compliance[0] - req.selection_vector_y = compliance[1] - req.selection_vector_z = compliance[2] - req.selection_vector_rx = compliance[3] - req.selection_vector_ry = compliance[4] - req.selection_vector_rz = compliance[5] - req.wrench = wrench_vec - req.type = type_spec - req.speed_limits = speed_limits - req.deviation_limits = deviation_limits - req.damping_factor = damping_factor - req.gain_scaling = gain_scale - - # Send request to controller - node.get_logger().info(f"Starting force mode with {req}") - robot.call_service("/force_mode_controller/start_force_mode", req) - robot.send_trajectory( - waypts=[[1.5707, -1.5707, -1.5707, -1.5707, 1.5707, 0]], - time_vec=[Duration(sec=5, nanosec=0)], - action_client=robot.passthrough_trajectory_action_client, - ) - - time.sleep(3) - node.get_logger().info("Deactivating force mode controller.") - robot.call_service("/force_mode_controller/stop_force_mode", Trigger.Request()) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/hardware_interface_plugin.xml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/hardware_interface_plugin.xml deleted file mode 100644 index b5030f5..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/hardware_interface_plugin.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - ROS2 Control System Driver for the Universal Robots series. - - - diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/controller_stopper.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/controller_stopper.hpp deleted file mode 100644 index 0345949..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/controller_stopper.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik, Created on behalf of Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-06-12 - * - * \author Mads Holm Peters - * \date 2022-02-25 - * - */ -//---------------------------------------------------------------------- -#ifndef UR_ROBOT_DRIVER__CONTROLLER_STOPPER_HPP_ -#define UR_ROBOT_DRIVER__CONTROLLER_STOPPER_HPP_ - -#include -#include -#include - -#include "rclcpp/rclcpp.hpp" -#include "controller_manager_msgs/srv/list_controllers.hpp" -#include "controller_manager_msgs/srv/switch_controller.hpp" -#include "std_msgs/msg/bool.hpp" - -class ControllerStopper -{ -public: - ControllerStopper() = delete; - ControllerStopper(const rclcpp::Node::SharedPtr& node, bool stop_controllers_on_startup); - virtual ~ControllerStopper() = default; - -private: - void robotRunningCallback(const std_msgs::msg::Bool::ConstSharedPtr msg); - - /*! - * \brief Queries running stoppable controllers and the controllers are stopped. - * - * Queries the controller manager for running controllers and compares the result with the - * consistent_controllers_. The remaining running controllers are stored in stopped_controllers_ - * and stopped afterwards. - */ - void findAndStopControllers(); - - /*! - * \brief Starts the controllers stored in stopped_controllers_. - * - */ - void startControllers(); - - std::shared_ptr node_; - rclcpp::Client::SharedPtr controller_manager_srv_; - rclcpp::Client::SharedPtr controller_list_srv_; - - rclcpp::Subscription::SharedPtr robot_running_sub_; - - std::vector consistent_controllers_; - std::vector stopped_controllers_; - - bool stop_controllers_on_startup_; - bool robot_running_; -}; -#endif // UR_ROBOT_DRIVER__CONTROLLER_STOPPER_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/dashboard_client_ros.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/dashboard_client_ros.hpp deleted file mode 100644 index 8349ec6..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/dashboard_client_ros.hpp +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-10-21 - * \author Marvin Große Besselmann grosse@fzi.de - * \date 2021-03-22 - * - */ -//---------------------------------------------------------------------- - -#ifndef UR_ROBOT_DRIVER__DASHBOARD_CLIENT_ROS_HPP_ -#define UR_ROBOT_DRIVER__DASHBOARD_CLIENT_ROS_HPP_ - -// System -#include -#include -#include - -// ROS -#include "rclcpp/rclcpp.hpp" -#include "std_srvs/srv/trigger.hpp" -// UR client library -#include "ur_client_library/ur/dashboard_client.h" -#include "ur_client_library/exceptions.h" -#include "ur_dashboard_msgs/msg/program_state.hpp" -#include "ur_dashboard_msgs/srv/add_to_log.hpp" -#include "ur_dashboard_msgs/srv/get_loaded_program.hpp" -#include "ur_dashboard_msgs/srv/get_program_state.hpp" -#include "ur_dashboard_msgs/srv/get_robot_mode.hpp" -#include "ur_dashboard_msgs/srv/get_safety_mode.hpp" -#include "ur_dashboard_msgs/srv/is_program_running.hpp" -#include "ur_dashboard_msgs/srv/is_program_saved.hpp" -#include "ur_dashboard_msgs/srv/load.hpp" -#include "ur_dashboard_msgs/srv/popup.hpp" -#include "ur_dashboard_msgs/srv/raw_request.hpp" - -namespace ur_robot_driver -{ -/*! - * \brief ROS wrapper for UR's dashboard server access. Many (not necessarily all) dashboard - * functionalities are wrapped into ROS services here. - */ -class DashboardClientROS -{ -public: - /*! - * \brief Constructor that shall be used by default - * - * \param nh Node handle pointing to the name space the dashboard-related functionalities are to - * be found - * \param robot_ip IP address of the robot - */ - DashboardClientROS(const rclcpp::Node::SharedPtr& node, const std::string& robot_ip); - DashboardClientROS() = delete; - virtual ~DashboardClientROS() = default; - -private: - inline rclcpp::Service::SharedPtr createDashboardTriggerSrv(const std::string& topic, - const std::string& command, - const std::string& expected) - { - rclcpp::Service::SharedPtr service = node_->create_service( - topic, [&, command, expected](const std::shared_ptr req, - const std::shared_ptr resp) { - try { - resp->message = this->client_.sendAndReceive(command); - resp->success = std::regex_match(resp->message, std::regex(expected)); - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->message = e.what(); - resp->success = false; - } - }); - return service; - } - - bool handleRunningQuery(ur_dashboard_msgs::srv::IsProgramRunning::Request::SharedPtr req, - ur_dashboard_msgs::srv::IsProgramRunning::Response::SharedPtr resp); - - bool handleSavedQuery(ur_dashboard_msgs::srv::IsProgramSaved::Request::SharedPtr req, - ur_dashboard_msgs::srv::IsProgramSaved::Response::SharedPtr resp); - - bool handleSafetyModeQuery(ur_dashboard_msgs::srv::GetSafetyMode::Request::SharedPtr req, - ur_dashboard_msgs::srv::GetSafetyMode::Response::SharedPtr resp); - - bool handleRobotModeQuery(ur_dashboard_msgs::srv::GetRobotMode::Request::SharedPtr req, - ur_dashboard_msgs::srv::GetRobotMode::Response::SharedPtr resp); - - bool connect(); - - std::shared_ptr node_; - urcl::DashboardClient client_; - - // Commanding services - rclcpp::Service::SharedPtr brake_release_service_; - rclcpp::Service::SharedPtr clear_operational_mode_service_; - rclcpp::Service::SharedPtr close_popup_service_; - rclcpp::Service::SharedPtr close_safety_popup_service_; - rclcpp::Service::SharedPtr pause_service_; - rclcpp::Service::SharedPtr play_service_; - rclcpp::Service::SharedPtr power_off_service_; - rclcpp::Service::SharedPtr power_on_service_; - rclcpp::Service::SharedPtr restart_safety_service_; - rclcpp::Service::SharedPtr shutdown_service_; - rclcpp::Service::SharedPtr stop_service_; - rclcpp::Service::SharedPtr unlock_protective_stop_service_; - rclcpp::Service::SharedPtr load_installation_service_; - rclcpp::Service::SharedPtr load_program_service_; - rclcpp::Service::SharedPtr quit_service_; - rclcpp::Service::SharedPtr add_to_log_service_; - rclcpp::Service::SharedPtr reconnect_service_; - rclcpp::Service::SharedPtr raw_request_service_; - - // Query services - rclcpp::Service::SharedPtr running_service_; - rclcpp::Service::SharedPtr get_loaded_program_service_; - rclcpp::Service::SharedPtr is_program_saved_service_; - rclcpp::Service::SharedPtr popup_service_; - rclcpp::Service::SharedPtr program_state_service_; - rclcpp::Service::SharedPtr safety_mode_service_; - rclcpp::Service::SharedPtr robot_mode_service_; -}; -} // namespace ur_robot_driver - -#endif // UR_ROBOT_DRIVER__DASHBOARD_CLIENT_ROS_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp deleted file mode 100644 index 14bdd29..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2019 FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \author Andy Zelenak zelenak@picknik.ai - * \author Marvin Große Besselmann grosse@fzi.de - * \date 2019-04-11 - * - */ -//---------------------------------------------------------------------- -#ifndef UR_ROBOT_DRIVER__HARDWARE_INTERFACE_HPP_ -#define UR_ROBOT_DRIVER__HARDWARE_INTERFACE_HPP_ - -// System -#include -#include -#include -#include - -// ros2_control hardware_interface -#include "hardware_interface/hardware_info.hpp" -#include "hardware_interface/system_interface.hpp" -#include "hardware_interface/types/hardware_interface_return_values.hpp" - -// UR stuff -#include "ur_client_library/ur/ur_driver.h" -#include "ur_client_library/ur/robot_receive_timeout.h" -#include "ur_robot_driver/dashboard_client_ros.hpp" -#include "ur_dashboard_msgs/msg/robot_mode.hpp" - -// ROS -#include "rclcpp/macros.hpp" -#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp" -#include "rclcpp_lifecycle/state.hpp" -#include "geometry_msgs/msg/transform_stamped.hpp" -#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp" - -namespace ur_robot_driver -{ -enum class PausingState -{ - PAUSED, - RUNNING, - RAMPUP -}; - -enum StoppingInterface -{ - NONE, - STOP_POSITION, - STOP_VELOCITY, - STOP_PASSTHROUGH, - STOP_FORCE_MODE, - STOP_FREEDRIVE, -}; - -// We define our own quaternion to use it as a buffer, since we need to pass pointers to the state -// interfaces. -struct Quaternion -{ - Quaternion() : x(0), y(0), z(0), w(0) - { - } - - void set(const tf2::Quaternion& q) - { - x = q.x(); - y = q.y(); - z = q.z(); - w = q.w(); - } - - double x; - double y; - double z; - double w; -}; - -/*! - * \brief The HardwareInterface class handles the interface between the ROS system and the main - * driver. It contains the read and write methods of the main control loop and registers various ROS - * topics and services. - */ -class URPositionHardwareInterface : public hardware_interface::SystemInterface -{ -public: - RCLCPP_SHARED_PTR_DEFINITIONS(URPositionHardwareInterface); - virtual ~URPositionHardwareInterface(); - - hardware_interface::CallbackReturn on_init(const hardware_interface::HardwareInfo& system_info) final; - - std::vector export_state_interfaces() final; - - std::vector export_command_interfaces() final; - - hardware_interface::CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) final; - hardware_interface::CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) final; - hardware_interface::CallbackReturn on_cleanup(const rclcpp_lifecycle::State& previous_state) final; - - hardware_interface::return_type read(const rclcpp::Time& time, const rclcpp::Duration& period) final; - hardware_interface::return_type write(const rclcpp::Time& time, const rclcpp::Duration& period) final; - - hardware_interface::return_type prepare_command_mode_switch(const std::vector& start_interfaces, - const std::vector& stop_interfaces) final; - - hardware_interface::return_type perform_command_mode_switch(const std::vector& start_interfaces, - const std::vector& stop_interfaces) final; - - /*! - * \brief Callback to handle a change in the current state of the URCaps program running on the - * robot. Executed only on the state change. - * - * \param program_running True when the URCap program is running on the robot. - */ - void handleRobotProgramState(bool program_running); - - static constexpr double NO_NEW_CMD_ = std::numeric_limits::quiet_NaN(); - - void asyncThread(); - -protected: - template - void readData(const std::unique_ptr& data_pkg, const std::string& var_name, - T& data); - template - void readBitsetData(const std::unique_ptr& data_pkg, const std::string& var_name, - std::bitset& data); - - void initAsyncIO(); - void checkAsyncIO(); - void updateNonDoubleValues(); - void extractToolPose(); - void transformForceTorque(); - void start_force_mode(); - void stop_force_mode(); - void check_passthrough_trajectory_controller(); - void trajectory_done_callback(urcl::control::TrajectoryResult result); - bool has_accelerations(std::vector> accelerations); - bool has_velocities(std::vector> velocities); - - urcl::vector6d_t urcl_position_commands_; - urcl::vector6d_t urcl_position_commands_old_; - urcl::vector6d_t urcl_velocity_commands_; - urcl::vector6d_t urcl_joint_positions_; - urcl::vector6d_t urcl_joint_velocities_; - urcl::vector6d_t urcl_joint_efforts_; - urcl::vector6d_t urcl_ft_sensor_measurements_; - urcl::vector6d_t urcl_tcp_pose_; - tf2::Quaternion tcp_rotation_quat_; - Quaternion tcp_rotation_buffer; - - bool packet_read_; - - uint32_t runtime_state_; - bool controllers_initialized_; - - std::bitset<18> actual_dig_out_bits_; - std::bitset<18> actual_dig_in_bits_; - std::array standard_analog_input_; - std::array standard_analog_output_; - std::bitset<4> analog_io_types_; - uint32_t tool_mode_; - std::bitset<2> tool_analog_input_types_; - std::array tool_analog_input_; - int32_t tool_output_voltage_; - double tool_output_current_; - double tool_temperature_; - double speed_scaling_; - double target_speed_fraction_; - double speed_scaling_combined_; - int32_t robot_mode_; - int32_t safety_mode_; - std::bitset<4> robot_status_bits_; - std::bitset<11> safety_status_bits_; - - // transform stuff - tf2::Vector3 tcp_force_; - tf2::Vector3 tcp_torque_; - - // asynchronous commands - std::array standard_dig_out_bits_cmd_; - std::array standard_analog_output_cmd_; - double analog_output_domain_cmd_; - double tool_voltage_cmd_; - double io_async_success_; - double target_speed_fraction_cmd_; - double scaling_async_success_; - double resend_robot_program_cmd_; - double resend_robot_program_async_success_; - double zero_ftsensor_cmd_; - double zero_ftsensor_async_success_; - double hand_back_control_cmd_; - double hand_back_control_async_success_; - bool first_pass_; - bool initialized_; - double system_interface_initialized_; - std::atomic_bool async_thread_shutdown_; - double get_robot_software_version_major_; - double get_robot_software_version_minor_; - double get_robot_software_version_bugfix_; - double get_robot_software_version_build_; - - // Freedrive mode controller interface values - bool freedrive_activated_; - bool freedrive_mode_controller_running_; - double freedrive_mode_async_success_; - double freedrive_mode_enable_; - double freedrive_mode_abort_; - - // Passthrough trajectory controller interface values - double passthrough_trajectory_transfer_state_; - double passthrough_trajectory_abort_; - bool passthrough_trajectory_controller_running_; - urcl::vector6d_t passthrough_trajectory_positions_; - urcl::vector6d_t passthrough_trajectory_velocities_; - urcl::vector6d_t passthrough_trajectory_accelerations_; - double passthrough_trajectory_time_from_start_; - - // payload stuff - urcl::vector3d_t payload_center_of_gravity_; - double payload_mass_; - double payload_async_success_; - - // force mode parameters - urcl::vector6d_t force_mode_task_frame_; - urcl::vector6d_t force_mode_selection_vector_; - urcl::vector6uint32_t force_mode_selection_vector_copy_; - urcl::vector6d_t force_mode_wrench_; - urcl::vector6d_t force_mode_limits_; - double force_mode_type_; - double force_mode_async_success_; - double force_mode_disable_cmd_; - double force_mode_damping_; - double force_mode_gain_scaling_; - - // copy of non double values - std::array actual_dig_out_bits_copy_; - std::array actual_dig_in_bits_copy_; - std::array analog_io_types_copy_; - double tool_mode_copy_; - std::array tool_analog_input_types_copy_; - double tool_output_voltage_copy_; - double robot_mode_copy_; - double safety_mode_copy_; - std::array robot_status_bits_copy_; - std::array safety_status_bits_copy_; - - bool robot_program_running_; - bool non_blocking_read_; - double robot_program_running_copy_; - - /* Vectors used to store the trajectory received from the passthrough trajectory controller. The whole trajectory is - * received before it is sent to the robot. */ - std::vector> trajectory_joint_positions_; - std::vector> trajectory_joint_velocities_; - std::vector> trajectory_joint_accelerations_; - std::vector trajectory_times_; - - PausingState pausing_state_; - double pausing_ramp_up_increment_; - - // resources switching aux vars - std::vector> stop_modes_; - std::vector> start_modes_; - bool position_controller_running_; - bool velocity_controller_running_; - bool force_mode_controller_running_ = false; - - std::unique_ptr ur_driver_; - std::shared_ptr async_thread_; - - std::atomic_bool rtde_comm_has_been_started_ = false; - - urcl::RobotReceiveTimeout receive_timeout_ = urcl::RobotReceiveTimeout::millisec(20); - - const std::string PASSTHROUGH_GPIO = "trajectory_passthrough"; - const std::string FORCE_MODE_GPIO = "force_mode"; - const std::string FREEDRIVE_MODE_GPIO = "freedrive_mode"; -}; -} // namespace ur_robot_driver - -#endif // UR_ROBOT_DRIVER__HARDWARE_INTERFACE_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/urcl_log_handler.hpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/urcl_log_handler.hpp deleted file mode 100644 index a0eb8e2..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/include/ur_robot_driver/urcl_log_handler.hpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2021 Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -// All source code contained in and/or linked to in this message (the “Source Code”) is subject to the copyright of -// Universal Robots A/S and/or its licensors. THE SOURCE CODE IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING – BUT NOT LIMITED TO – WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR -// NONINFRINGEMENT. USE OF THE SOURCE CODE IS AT YOUR OWN RISK AND UNIVERSAL ROBOTS A/S AND ITS LICENSORS SHALL, TO THE -// MAXIMUM EXTENT PERMITTED BY LAW, NOT BE LIABLE FOR ANY ERRORS OR MALICIOUS CODE IN THE SOURCE CODE, ANY THIRD-PARTY -// CLAIMS, OR ANY OTHER CLAIMS AND DAMAGES, INCLUDING INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES, -// OR ANY LOSS OF PROFITS, EXPECTED SAVINGS, OR REVENUES, WHETHER INCURRED DIRECTLY OR INDIRECTLY, OR ANY LOSS OF DATA, -// USE, GOODWILL, OR OTHER INTANGIBLE LOSSES, RESULTING FROM YOUR USE OF THE SOURCE CODE. You may make copies of the -// Source Code for use in connection with a Universal Robots or UR+ product, provided that you include (i) an -// appropriate copyright notice (“© [the year in which you received the Source Code or the Source Code was first -// published, e.g. “2021”] Universal Robots A/S and/or its licensors”) along with the capitalized section of this notice -// in all copies of the Source Code. By using the Source Code, you agree to the above terms. For more information, -// please contact legal@universal-robots.com. - -#ifndef UR_ROBOT_DRIVER__URCL_LOG_HANDLER_HPP_ -#define UR_ROBOT_DRIVER__URCL_LOG_HANDLER_HPP_ - -#include -#include "ur_client_library/log.h" -namespace ur_robot_driver -{ - -/*! - * \brief Register the UrclLoghHandler, this will start logging messages from the client library with ROS2 logging. - * This function has to be called inside your node, to enable the log handler. - */ -void registerUrclLogHandler(const std::string& tf_prefix = ""); - -/*! - * \brief Unregister the UrclLoghHandler, stop logging messages from the client library with ROS2 logging. - */ -void unregisterUrclLogHandler(); - -/*! - * \brief Loghandler for handling messages logged with the C++ client library. This loghandler will log the messages - * from the client library with ROS2s logging. - * Use registerLogHandler to register this LogHandler. This class shouldn't be instantiated directly. - */ -class UrclLogHandler : public urcl::LogHandler -{ -public: - /*! - * \brief Default constructor - */ - UrclLogHandler(); - - /*! - * \brief Function to log a message - * - * \param file The log message comes from this file - * \param line The log message comes from this line - * \param loglevel Indicates the severity of the log message - * \param log Log message - */ - void log(const char* file, int line, urcl::LogLevel loglevel, const char* message) override; - - /** - * @brief getTFPrefix - obtain the currently set tf_prefix - * @return - */ - const std::string& getTFPrefix() const - { - return tf_prefix_; - } - -private: - std::string tf_prefix_{}; - - /** - * @brief setTFPrefix - set the tf_prefix the logger will append to the node name - * @param tf_prefix - */ - void setTFPrefix(const std::string& tf_prefix) - { - tf_prefix_ = tf_prefix; - } - - // Declare the register method as a friend so that we can access setTFPrefix from it - friend void registerUrclLogHandler(const std::string& tf_prefix); -}; - -} // namespace ur_robot_driver - -#endif // UR_ROBOT_DRIVER__URCL_LOG_HANDLER_HPP_ diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_forward_velocity_controller.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_forward_velocity_controller.launch.py deleted file mode 100644 index 19a9439..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_forward_velocity_controller.launch.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Lovro Ivanov -# -# Description: After a robot has been loaded, this will execute a series of trajectories. - -from launch import LaunchDescription -from launch.substitutions import PathJoinSubstitution -from launch_ros.actions import Node -from launch_ros.substitutions import FindPackageShare - - -def generate_launch_description(): - - velocity_goals = PathJoinSubstitution( - [FindPackageShare("ur_robot_driver"), "config", "test_velocity_goal_publishers_config.yaml"] - ) - - return LaunchDescription( - [ - Node( - package="ros2_controllers_test_nodes", - executable="publisher_forward_position_controller", - name="publisher_forward_velocity_controller", - parameters=[velocity_goals], - output="screen", - ) - ] - ) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_joint_trajectory_controller.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_joint_trajectory_controller.launch.py deleted file mode 100644 index e0f6f4b..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_joint_trajectory_controller.launch.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl -# -# Description: After a robot has been loaded, this will execute a series of trajectories. - -from launch import LaunchDescription -from launch.substitutions import PathJoinSubstitution -from launch_ros.actions import Node -from launch_ros.substitutions import FindPackageShare - - -def generate_launch_description(): - - position_goals = PathJoinSubstitution( - [FindPackageShare("ur_robot_driver"), "config", "test_goal_publishers_config.yaml"] - ) - - return LaunchDescription( - [ - Node( - package="ros2_controllers_test_nodes", - executable="publisher_joint_trajectory_controller", - name="publisher_joint_trajectory_controller", - parameters=[position_goals], - output="screen", - ) - ] - ) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_scaled_joint_trajectory_controller.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_scaled_joint_trajectory_controller.launch.py deleted file mode 100644 index 4fc9f47..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/test_scaled_joint_trajectory_controller.launch.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl -# -# Description: After a robot has been loaded, this will execute a series of trajectories. - -from launch import LaunchDescription -from launch.substitutions import PathJoinSubstitution -from launch_ros.actions import Node -from launch_ros.substitutions import FindPackageShare - - -def generate_launch_description(): - - position_goals = PathJoinSubstitution( - [FindPackageShare("ur_robot_driver"), "config", "test_goal_publishers_config.yaml"] - ) - - return LaunchDescription( - [ - Node( - package="ros2_controllers_test_nodes", - executable="publisher_joint_trajectory_controller", - name="publisher_scaled_joint_trajectory_controller", - parameters=[position_goals], - output="screen", - ) - ] - ) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur10.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur10.launch.py deleted file mode 100644 index db4c7df..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur10.launch.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. " - "Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur10", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur10e.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur10e.launch.py deleted file mode 100644 index c2c4dd9..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur10e.launch.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. " - "Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur10e", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur16e.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur16e.launch.py deleted file mode 100644 index 1e90e03..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur16e.launch.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. " - "Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur16e", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur20.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur20.launch.py deleted file mode 100644 index aaf26af..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur20.launch.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. " - "Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur20", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur3.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur3.launch.py deleted file mode 100644 index ce2b1ae..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur3.launch.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. " - "Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur3", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur30.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur30.launch.py deleted file mode 100644 index e5d38e5..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur30.launch.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. " - "Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur30", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur3e.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur3e.launch.py deleted file mode 100644 index 8c23de1..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur3e.launch.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. " - "Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur3e", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur5.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur5.launch.py deleted file mode 100644 index bc157fa..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur5.launch.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. " - "Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur5", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur5e.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur5e.launch.py deleted file mode 100644 index 0fbdeb6..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur5e.launch.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. " - "Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - description="Initially loaded robot controller.", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - - base_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/ur_control.launch.py"]), - launch_arguments={ - "ur_type": "ur5e", - "robot_ip": robot_ip, - "use_fake_hardware": use_fake_hardware, - "fake_sensor_commands": fake_sensor_commands, - "initial_joint_controller": initial_joint_controller, - "activate_joint_controller": activate_joint_controller, - }.items(), - ) - - return LaunchDescription(declared_arguments + [base_launch]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur_control.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur_control.launch.py deleted file mode 100644 index b54608b..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur_control.launch.py +++ /dev/null @@ -1,636 +0,0 @@ -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Denis Stogl - -from launch_ros.actions import Node -from launch_ros.parameter_descriptions import ParameterFile, ParameterValue -from launch_ros.substitutions import FindPackageShare - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, OpaqueFunction -from launch.conditions import IfCondition, UnlessCondition -from launch.substitutions import ( - AndSubstitution, - Command, - FindExecutable, - LaunchConfiguration, - NotSubstitution, - PathJoinSubstitution, -) - - -def launch_setup(context, *args, **kwargs): - # Initialize Arguments - ur_type = LaunchConfiguration("ur_type") - robot_ip = LaunchConfiguration("robot_ip") - safety_limits = LaunchConfiguration("safety_limits") - safety_pos_margin = LaunchConfiguration("safety_pos_margin") - safety_k_position = LaunchConfiguration("safety_k_position") - # General arguments - runtime_config_package = LaunchConfiguration("runtime_config_package") - controllers_file = LaunchConfiguration("controllers_file") - description_package = LaunchConfiguration("description_package") - description_file = LaunchConfiguration("description_file") - kinematics_params_file = LaunchConfiguration("kinematics_params_file") - tf_prefix = LaunchConfiguration("tf_prefix") - use_fake_hardware = LaunchConfiguration("use_fake_hardware") - fake_sensor_commands = LaunchConfiguration("fake_sensor_commands") - controller_spawner_timeout = LaunchConfiguration("controller_spawner_timeout") - initial_joint_controller = LaunchConfiguration("initial_joint_controller") - activate_joint_controller = LaunchConfiguration("activate_joint_controller") - launch_rviz = LaunchConfiguration("launch_rviz") - headless_mode = LaunchConfiguration("headless_mode") - launch_dashboard_client = LaunchConfiguration("launch_dashboard_client") - use_tool_communication = LaunchConfiguration("use_tool_communication") - tool_parity = LaunchConfiguration("tool_parity") - tool_baud_rate = LaunchConfiguration("tool_baud_rate") - tool_stop_bits = LaunchConfiguration("tool_stop_bits") - tool_rx_idle_chars = LaunchConfiguration("tool_rx_idle_chars") - tool_tx_idle_chars = LaunchConfiguration("tool_tx_idle_chars") - tool_device_name = LaunchConfiguration("tool_device_name") - tool_tcp_port = LaunchConfiguration("tool_tcp_port") - tool_voltage = LaunchConfiguration("tool_voltage") - reverse_ip = LaunchConfiguration("reverse_ip") - script_command_port = LaunchConfiguration("script_command_port") - reverse_port = LaunchConfiguration("reverse_port") - script_sender_port = LaunchConfiguration("script_sender_port") - trajectory_port = LaunchConfiguration("trajectory_port") - - joint_limit_params = PathJoinSubstitution( - [FindPackageShare(description_package), "config", ur_type, "joint_limits.yaml"] - ) - physical_params = PathJoinSubstitution( - [FindPackageShare(description_package), "config", ur_type, "physical_parameters.yaml"] - ) - visual_params = PathJoinSubstitution( - [FindPackageShare(description_package), "config", ur_type, "visual_parameters.yaml"] - ) - script_filename = PathJoinSubstitution( - [FindPackageShare("ur_client_library"), "resources", "external_control.urscript"] - ) - input_recipe_filename = PathJoinSubstitution( - [FindPackageShare("ur_robot_driver"), "resources", "rtde_input_recipe.txt"] - ) - output_recipe_filename = PathJoinSubstitution( - [FindPackageShare("ur_robot_driver"), "resources", "rtde_output_recipe.txt"] - ) - - robot_description_content = Command( - [ - PathJoinSubstitution([FindExecutable(name="xacro")]), - " ", - PathJoinSubstitution([FindPackageShare(description_package), "urdf", description_file]), - " ", - "robot_ip:=", - robot_ip, - " ", - "joint_limit_params:=", - joint_limit_params, - " ", - "kinematics_params:=", - kinematics_params_file, - " ", - "physical_params:=", - physical_params, - " ", - "visual_params:=", - visual_params, - " ", - "safety_limits:=", - safety_limits, - " ", - "safety_pos_margin:=", - safety_pos_margin, - " ", - "safety_k_position:=", - safety_k_position, - " ", - "name:=", - ur_type, - " ", - "script_filename:=", - script_filename, - " ", - "input_recipe_filename:=", - input_recipe_filename, - " ", - "output_recipe_filename:=", - output_recipe_filename, - " ", - "tf_prefix:=", - tf_prefix, - " ", - "use_fake_hardware:=", - use_fake_hardware, - " ", - "fake_sensor_commands:=", - fake_sensor_commands, - " ", - "headless_mode:=", - headless_mode, - " ", - "use_tool_communication:=", - use_tool_communication, - " ", - "tool_parity:=", - tool_parity, - " ", - "tool_baud_rate:=", - tool_baud_rate, - " ", - "tool_stop_bits:=", - tool_stop_bits, - " ", - "tool_rx_idle_chars:=", - tool_rx_idle_chars, - " ", - "tool_tx_idle_chars:=", - tool_tx_idle_chars, - " ", - "tool_device_name:=", - tool_device_name, - " ", - "tool_tcp_port:=", - tool_tcp_port, - " ", - "tool_voltage:=", - tool_voltage, - " ", - "reverse_ip:=", - reverse_ip, - " ", - "script_command_port:=", - script_command_port, - " ", - "reverse_port:=", - reverse_port, - " ", - "script_sender_port:=", - script_sender_port, - " ", - "trajectory_port:=", - trajectory_port, - " ", - ] - ) - robot_description = { - "robot_description": ParameterValue(value=robot_description_content, value_type=str) - } - - initial_joint_controllers = PathJoinSubstitution( - [FindPackageShare(runtime_config_package), "config", controllers_file] - ) - - rviz_config_file = PathJoinSubstitution( - [FindPackageShare(description_package), "rviz", "view_robot.rviz"] - ) - - # define update rate - update_rate_config_file = PathJoinSubstitution( - [ - FindPackageShare(runtime_config_package), - "config", - ur_type.perform(context) + "_update_rate.yaml", - ] - ) - - control_node = Node( - package="controller_manager", - executable="ros2_control_node", - parameters=[ - robot_description, - update_rate_config_file, - ParameterFile(initial_joint_controllers, allow_substs=True), - ], - output="screen", - condition=IfCondition(use_fake_hardware), - ) - - ur_control_node = Node( - package="ur_robot_driver", - executable="ur_ros2_control_node", - parameters=[ - robot_description, - update_rate_config_file, - ParameterFile(initial_joint_controllers, allow_substs=True), - ], - output="screen", - condition=UnlessCondition(use_fake_hardware), - ) - - dashboard_client_node = Node( - package="ur_robot_driver", - condition=IfCondition( - AndSubstitution(launch_dashboard_client, NotSubstitution(use_fake_hardware)) - ), - executable="dashboard_client", - name="dashboard_client", - output="screen", - emulate_tty=True, - parameters=[{"robot_ip": robot_ip}], - ) - - tool_communication_node = Node( - package="ur_robot_driver", - condition=IfCondition(use_tool_communication), - executable="tool_communication.py", - name="ur_tool_comm", - output="screen", - parameters=[ - { - "robot_ip": robot_ip, - "tcp_port": tool_tcp_port, - "device_name": tool_device_name, - } - ], - ) - - urscript_interface = Node( - package="ur_robot_driver", - executable="urscript_interface", - parameters=[{"robot_ip": robot_ip}], - output="screen", - ) - - controller_stopper_node = Node( - package="ur_robot_driver", - executable="controller_stopper_node", - name="controller_stopper", - output="screen", - emulate_tty=True, - condition=UnlessCondition(use_fake_hardware), - parameters=[ - {"headless_mode": headless_mode}, - {"joint_controller_active": activate_joint_controller}, - { - "consistent_controllers": [ - "io_and_status_controller", - "force_torque_sensor_broadcaster", - "joint_state_broadcaster", - "speed_scaling_state_broadcaster", - "tcp_pose_broadcaster", - "ur_configuration_controller", - ] - }, - ], - ) - - robot_state_publisher_node = Node( - package="robot_state_publisher", - executable="robot_state_publisher", - output="both", - parameters=[robot_description], - ) - - rviz_node = Node( - package="rviz2", - condition=IfCondition(launch_rviz), - executable="rviz2", - name="rviz2", - output="log", - arguments=["-d", rviz_config_file], - ) - - # Spawn controllers - def controller_spawner(controllers, active=True): - inactive_flags = ["--inactive"] if not active else [] - return Node( - package="controller_manager", - executable="spawner", - arguments=[ - "--controller-manager", - "/controller_manager", - "--controller-manager-timeout", - controller_spawner_timeout, - ] - + inactive_flags - + controllers, - ) - - controllers_active = [ - "joint_state_broadcaster", - "io_and_status_controller", - "speed_scaling_state_broadcaster", - "force_torque_sensor_broadcaster", - "tcp_pose_broadcaster", - "ur_configuration_controller", - "panda_hand_controller", - ] - controllers_inactive = [ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "force_mode_controller", - "passthrough_trajectory_controller", - "freedrive_mode_controller", - ] - if activate_joint_controller.perform(context) == "true": - controllers_active.append(initial_joint_controller.perform(context)) - controllers_inactive.remove(initial_joint_controller.perform(context)) - - if use_fake_hardware.perform(context) == "true": - controllers_active.remove("tcp_pose_broadcaster") - - controller_spawners = [ - controller_spawner(controllers_active), - controller_spawner(controllers_inactive, active=False), - ] - - nodes_to_start = [ - control_node, - ur_control_node, - dashboard_client_node, - tool_communication_node, - controller_stopper_node, - urscript_interface, - robot_state_publisher_node, - rviz_node, - ] + controller_spawners - - return nodes_to_start - - -def generate_launch_description(): - declared_arguments = [] - # UR specific arguments - declared_arguments.append( - DeclareLaunchArgument( - "ur_type", - description="Type/series of used UR robot.", - choices=["ur3", "ur3e", "ur5", "ur5e", "ur10", "ur10e", "ur16e", "ur20", "ur30"], - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", description="IP address by which the robot can be reached." - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "safety_limits", - default_value="true", - description="Enables the safety limits controller if true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "safety_pos_margin", - default_value="0.15", - description="The margin to lower and upper limits in the safety controller.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "safety_k_position", - default_value="20", - description="k-position factor in the safety controller.", - ) - ) - # General arguments - declared_arguments.append( - DeclareLaunchArgument( - "runtime_config_package", - default_value="ur_robot_driver", - description='Package with the controller\'s configuration in "config" folder. ' - "Usually the argument is not set, it enables use of a custom setup.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "controllers_file", - default_value="ur_controllers.yaml", - description="YAML file with the controllers configuration.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "description_package", - default_value="ur_description", - description="Description package with robot URDF/XACRO files. Usually the argument " - "is not set, it enables use of a custom description.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "description_file", - default_value="ur.urdf.xacro", - description="URDF/XACRO description file with the robot.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "kinematics_params_file", - default_value=PathJoinSubstitution( - [ - FindPackageShare(LaunchConfiguration("description_package")), - "config", - LaunchConfiguration("ur_type"), - "default_kinematics.yaml", - ] - ), - description="The calibration configuration of the actual robot used.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "tf_prefix", - default_value="", - description="tf_prefix of the joint names, useful for " - "multi-robot setup. If changed, also joint names in the controllers' configuration " - "have to be updated.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_fake_hardware", - default_value="false", - description="Start robot with fake hardware mirroring command to its states.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "fake_sensor_commands", - default_value="false", - description="Enable fake command interfaces for sensors used for simple simulations. " - "Used only if 'use_fake_hardware' parameter is true.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "headless_mode", - default_value="false", - description="Enable headless mode for robot control", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "controller_spawner_timeout", - default_value="10", - description="Timeout used when spawning controllers.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "initial_joint_controller", - default_value="scaled_joint_trajectory_controller", - choices=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_velocity_controller", - "forward_position_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - description="Initially loaded robot controller.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "activate_joint_controller", - default_value="true", - description="Activate loaded joint controller.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument("launch_rviz", default_value="true", description="Launch RViz?") - ) - declared_arguments.append( - DeclareLaunchArgument( - "launch_dashboard_client", default_value="true", description="Launch Dashboard Client?" - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "use_tool_communication", - default_value="false", - description="Only available for e series!", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "tool_parity", - default_value="0", - description="Parity configuration for serial communication. Only effective, if " - "use_tool_communication is set to True.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "tool_baud_rate", - default_value="115200", - description="Baud rate configuration for serial communication. Only effective, if " - "use_tool_communication is set to True.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "tool_stop_bits", - default_value="1", - description="Stop bits configuration for serial communication. Only effective, if " - "use_tool_communication is set to True.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "tool_rx_idle_chars", - default_value="1.5", - description="RX idle chars configuration for serial communication. Only effective, " - "if use_tool_communication is set to True.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "tool_tx_idle_chars", - default_value="3.5", - description="TX idle chars configuration for serial communication. Only effective, " - "if use_tool_communication is set to True.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "tool_device_name", - default_value="/tmp/ttyUR", - description="File descriptor that will be generated for the tool communication device. " - "The user has be be allowed to write to this location. " - "Only effective, if use_tool_communication is set to True.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "tool_tcp_port", - default_value="54321", - description="Remote port that will be used for bridging the tool's serial device. " - "Only effective, if use_tool_communication is set to True.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "tool_voltage", - default_value="0", # 0 being a conservative value that won't destroy anything - description="Tool voltage that will be setup.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "reverse_ip", - default_value="0.0.0.0", - description="IP that will be used for the robot controller to communicate back to the driver.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "script_command_port", - default_value="50004", - description="Port that will be opened to forward URScript commands to the robot.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "reverse_port", - default_value="50001", - description="Port that will be opened to send cyclic instructions from the driver to the robot controller.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "script_sender_port", - default_value="50002", - description="The driver will offer an interface to query the external_control URScript on this port.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "trajectory_port", - default_value="50003", - description="Port that will be opened for trajectory control.", - ) - ) - return LaunchDescription(declared_arguments + [OpaqueFunction(function=launch_setup)]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur_dashboard_client.launch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur_dashboard_client.launch.py deleted file mode 100755 index 4b63839..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/launch/ur_dashboard_client.launch.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2021, PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument -from launch.substitutions import LaunchConfiguration -from launch_ros.actions import Node - - -def generate_launch_description(): - # Declare arguments - declared_arguments = [] - declared_arguments.append( - DeclareLaunchArgument( - "robot_ip", - description="IP address by which the robot can be reached.", - ) - ) - - # Initialize Arguments - robot_ip = LaunchConfiguration("robot_ip") - - dashboard_client_node = Node( - package="ur_robot_driver", - executable="dashboard_client", - name="dashboard_client", - output="screen", - emulate_tty=True, - parameters=[{"robot_ip": robot_ip}], - ) - - return LaunchDescription(declared_arguments + [dashboard_client_node]) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/package.xml b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/package.xml deleted file mode 100644 index 1cb83c1..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/package.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - ur_robot_driver - 2.5.2 - The new driver for Universal Robots UR3, UR5 and UR10 robots with CB3 controllers and the e-series. - - Felix Exner - Rune Søe-Knudsen - Universal Robots A/S - - BSD-3-Clause - - https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver/issues - https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver - - Denis Stogl - Marvin Große Besselmann - Lovro Ivanov - Andy Zelenak - Vincenzo Di Pentima - Thomas Timm Andersen - Simon Rasmussen - Felix Exner - Lea Steffen - Tristan Schnell - - ament_cmake - ament_cmake_python - - backward_ros - controller_manager - controller_manager_msgs - geometry_msgs - hardware_interface - pluginlib - rclcpp - rclcpp_lifecycle - rclpy - std_msgs - std_srvs - tf2_geometry_msgs - ur_client_library - ur_controllers - ur_dashboard_msgs - ur_description - ur_msgs - - force_torque_sensor_broadcaster - joint_state_broadcaster - joint_state_publisher - joint_trajectory_controller - launch - launch_ros - pose_broadcaster - position_controllers - robot_state_publisher - ros2_controllers_test_nodes - rviz2 - socat - urdf - velocity_controllers - xacro - - launch_testing_ament_cmake - - - ament_cmake - - - diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/externalcontrol-1.0.5.urcap b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/externalcontrol-1.0.5.urcap deleted file mode 100644 index 31908f34d7f13d8f9b20454cfad85aa91015c11a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35287 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 SERVO_UNINITIALIZED: - cmd_servo_state = SERVO_IDLE - end - - if do_extrapolate: - extrapolate_count = extrapolate_count + 1 - if extrapolate_count > extrapolate_max_count: - extrapolate_max_count = extrapolate_count - end - - q = extrapolate() - servoj(q, t=steptime, {{SERVO_J_REPLACE}}) - - elif state == SERVO_RUNNING: - extrapolate_count = 0 - servoj(q, t=steptime, {{SERVO_J_REPLACE}}) - else: - extrapolate_count = 0 - sync() - end - exit_critical - end - textmsg("ExternalControl: servo thread ended") - stopj(4.0) -end - -# Helpers for speed control -def set_speed(qd): - cmd_servo_qd = qd - control_mode = MODE_SPEEDJ -end - -thread speedThread(): - textmsg("ExternalControl: Starting speed thread") - while control_mode == MODE_SPEEDJ: - qd = cmd_servo_qd - speedj(qd, 40.0, steptime) - end - textmsg("ExternalControl: speedj thread ended") - stopj(5.0) -end - -# HEADER_END - -# NODE_CONTROL_LOOP_BEGINS - -socket_open("{{SERVER_IP_REPLACE}}", {{SERVER_PORT_REPLACE}}, "reverse_socket") - -control_mode = MODE_UNINITIALIZED -thread_move = 0 -global keepalive = -2 -params_mult = socket_read_binary_integer(1+6+1, "reverse_socket", 0) -textmsg("ExternalControl: External control active") -keepalive = params_mult[1] -while keepalive > 0 and control_mode > MODE_STOPPED: - enter_critical - params_mult = socket_read_binary_integer(1+6+1, "reverse_socket", 0.02) # steptime could work as well, but does not work in simulation - if params_mult[0] > 0: - keepalive = params_mult[1] - if control_mode != params_mult[8]: - control_mode = params_mult[8] - join thread_move - if control_mode == MODE_SERVOJ: - thread_move = run servoThread() - elif control_mode == MODE_SPEEDJ: - thread_move = run speedThread() - end - end - if control_mode == MODE_SERVOJ: - q = [params_mult[2] / MULT_jointstate, params_mult[3] / MULT_jointstate, params_mult[4] / MULT_jointstate, params_mult[5] / MULT_jointstate, params_mult[6] / MULT_jointstate, params_mult[7] / MULT_jointstate] - set_servo_setpoint(q) - elif control_mode == MODE_SPEEDJ: - qd = [params_mult[2] / MULT_jointstate, params_mult[3] / MULT_jointstate, params_mult[4] / MULT_jointstate, params_mult[5] / MULT_jointstate, params_mult[6] / MULT_jointstate, params_mult[7] / MULT_jointstate] - set_speed(qd) - end - else: - keepalive = keepalive - 1 - end - exit_critical -end - -textmsg("ExternalControl: Stopping communication and control") -control_mode = MODE_STOPPED -join thread_move -textmsg("ExternalControl: All threads ended") -socket_close("reverse_socket") - -# NODE_CONTROL_LOOP_ENDS diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rs485-1.0.urcap b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rs485-1.0.urcap deleted file mode 100644 index 5a9f7c65ba07fe9d09d4e37ff04369ee5f189735..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 141984 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 BM&|$k diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rtde_input_recipe.txt b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rtde_input_recipe.txt deleted file mode 100644 index c00dc05..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rtde_input_recipe.txt +++ /dev/null @@ -1,12 +0,0 @@ -speed_slider_mask -speed_slider_fraction -standard_digital_output_mask -standard_digital_output -configurable_digital_output_mask -configurable_digital_output -tool_digital_output_mask -tool_digital_output -standard_analog_output_mask -standard_analog_output_type -standard_analog_output_0 -standard_analog_output_1 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rtde_output_recipe.txt b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rtde_output_recipe.txt deleted file mode 100644 index 8d10be8..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/resources/rtde_output_recipe.txt +++ /dev/null @@ -1,27 +0,0 @@ -timestamp -actual_q -actual_qd -speed_scaling -target_speed_fraction -runtime_state -actual_TCP_force -actual_TCP_pose -actual_digital_input_bits -actual_digital_output_bits -standard_analog_input0 -standard_analog_input1 -standard_analog_output0 -standard_analog_output1 -analog_io_types -tool_mode -tool_analog_input_types -tool_analog_input0 -tool_analog_input1 -tool_output_voltage -tool_output_current -tool_temperature -robot_mode -safety_mode -robot_status_bits -safety_status_bits -actual_current diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/example_move.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/example_move.py deleted file mode 100755 index c77836a..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/example_move.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2024 FZI Forschungszentrum Informatik -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# -# Author: Felix Exner - -# This is an example of how to interface the robot without any additional ROS components. For -# real-life applications, we do recommend to use something like MoveIt! - -import time - -import rclpy -from rclpy.action import ActionClient - -from builtin_interfaces.msg import Duration -from action_msgs.msg import GoalStatus -from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint -from control_msgs.action import FollowJointTrajectory -from control_msgs.msg import JointTolerance - -TRAJECTORIES = { - "traj0": [ - { - "positions": [0.043128, -1.28824, 1.37179, -1.82208, -1.63632, -0.18], - "velocities": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - "time_from_start": Duration(sec=4, nanosec=0), - }, - { - "positions": [-0.195016, -1.70093, 0.902027, -0.944217, -1.52982, -0.195171], - "velocities": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - "time_from_start": Duration(sec=8, nanosec=0), - }, - ], - "traj1": [ - { - "positions": [-0.195016, -1.70094, 0.902027, -0.944217, -1.52982, -0.195171], - "velocities": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - "time_from_start": Duration(sec=0, nanosec=0), - }, - { - "positions": [0.30493, -0.982258, 0.955637, -1.48215, -1.72737, 0.204445], - "velocities": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - "time_from_start": Duration(sec=8, nanosec=0), - }, - ], -} - - -class JTCClient(rclpy.node.Node): - """Small test client for the jtc.""" - - def __init__(self): - super().__init__("jtc_client") - self.declare_parameter("controller_name", "scaled_joint_trajectory_controller") - self.declare_parameter( - "joints", - [ - "shoulder_pan_joint", - "shoulder_lift_joint", - "elbow_joint", - "wrist_1_joint", - "wrist_2_joint", - "wrist_3_joint", - ], - ) - - controller_name = self.get_parameter("controller_name").value + "/follow_joint_trajectory" - self.joints = self.get_parameter("joints").value - - if self.joints is None or len(self.joints) == 0: - raise Exception('"joints" parameter is required') - - self._action_client = ActionClient(self, FollowJointTrajectory, controller_name) - self.get_logger().info(f"Waiting for action server on {controller_name}") - self._action_client.wait_for_server() - - self.parse_trajectories() - self.i = 0 - self._send_goal_future = None - self._get_result_future = None - self.execute_next_trajectory() - - def parse_trajectories(self): - self.goals = {} - - for traj_name in TRAJECTORIES: - goal = JointTrajectory() - goal.joint_names = self.joints - for pt in TRAJECTORIES[traj_name]: - point = JointTrajectoryPoint() - point.positions = pt["positions"] - point.velocities = pt["velocities"] - point.time_from_start = pt["time_from_start"] - goal.points.append(point) - - self.goals[traj_name] = goal - - def execute_next_trajectory(self): - if self.i >= len(self.goals): - self.get_logger().info("Done with all trajectories") - raise SystemExit - traj_name = list(self.goals)[self.i] - self.i = self.i + 1 - if traj_name: - self.execute_trajectory(traj_name) - - def execute_trajectory(self, traj_name): - self.get_logger().info(f"Executing trajectory {traj_name}") - goal = FollowJointTrajectory.Goal() - goal.trajectory = self.goals[traj_name] - - goal.goal_time_tolerance = Duration(sec=0, nanosec=500000000) - goal.goal_tolerance = [ - JointTolerance(position=0.01, velocity=0.01, name=self.joints[i]) for i in range(6) - ] - - self._send_goal_future = self._action_client.send_goal_async(goal) - self._send_goal_future.add_done_callback(self.goal_response_callback) - - def goal_response_callback(self, future): - goal_handle = future.result() - if not goal_handle.accepted: - self.get_logger().error("Goal rejected :(") - raise RuntimeError("Goal rejected :(") - - self.get_logger().debug("Goal accepted :)") - - self._get_result_future = goal_handle.get_result_async() - self._get_result_future.add_done_callback(self.get_result_callback) - - def get_result_callback(self, future): - result = future.result().result - status = future.result().status - self.get_logger().info(f"Done with result: {self.status_to_str(status)}") - if status == GoalStatus.STATUS_SUCCEEDED: - time.sleep(2) - self.execute_next_trajectory() - else: - if result.error_code != FollowJointTrajectory.Result.SUCCESSFUL: - self.get_logger().error( - f"Done with result: {self.error_code_to_str(result.error_code)}" - ) - raise RuntimeError("Executing trajectory failed. " + result.error_string) - - @staticmethod - def error_code_to_str(error_code): - if error_code == FollowJointTrajectory.Result.SUCCESSFUL: - return "SUCCESSFUL" - if error_code == FollowJointTrajectory.Result.INVALID_GOAL: - return "INVALID_GOAL" - if error_code == FollowJointTrajectory.Result.INVALID_JOINTS: - return "INVALID_JOINTS" - if error_code == FollowJointTrajectory.Result.OLD_HEADER_TIMESTAMP: - return "OLD_HEADER_TIMESTAMP" - if error_code == FollowJointTrajectory.Result.PATH_TOLERANCE_VIOLATED: - return "PATH_TOLERANCE_VIOLATED" - if error_code == FollowJointTrajectory.Result.GOAL_TOLERANCE_VIOLATED: - return "GOAL_TOLERANCE_VIOLATED" - - @staticmethod - def status_to_str(error_code): - if error_code == GoalStatus.STATUS_UNKNOWN: - return "UNKNOWN" - if error_code == GoalStatus.STATUS_ACCEPTED: - return "ACCEPTED" - if error_code == GoalStatus.STATUS_EXECUTING: - return "EXECUTING" - if error_code == GoalStatus.STATUS_CANCELING: - return "CANCELING" - if error_code == GoalStatus.STATUS_SUCCEEDED: - return "SUCCEEDED" - if error_code == GoalStatus.STATUS_CANCELED: - return "CANCELED" - if error_code == GoalStatus.STATUS_ABORTED: - return "ABORTED" - - -def main(args=None): - rclpy.init(args=args) - - jtc_client = JTCClient() - try: - rclpy.spin(jtc_client) - except RuntimeError as err: - jtc_client.get_logger().error(str(err)) - except SystemExit: - rclpy.logging.get_logger("jtc_client").info("Done") - - rclpy.shutdown() - - -if __name__ == "__main__": - main() diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/start_ursim.sh b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/start_ursim.sh deleted file mode 100755 index 94e9b1e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/start_ursim.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -# copyright 2022 Universal Robots A/S -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -ORANGE='\033[0;33m' -NC='\033[0m' # No Color - -URSIM_CMD="ros2 run ur_client_library start_ursim.sh" - -echo -e "${ORANGE} DEPRECATION WARNING: The script starting URSim was moved to the ur_client_library package. This script here does still work, but will be removed with ROS Jazzy. Please use `${URSIM_CMD}` to start URSim in future." -$URSIM_CMD diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/tool_communication.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/tool_communication.py deleted file mode 100755 index d39d327..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/tool_communication.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2021 PickNik, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - - -"""Small helper script to start the tool communication interface.""" - -import subprocess - -import rclpy.logging -from rclpy.node import Node - - -class UrToolCommunication(Node): - """Starts socat.""" - - def __init__(self): - super().__init__("ur_tool_communication") - # IP address of the robot - self.declare_parameter("robot_ip", "192.168.56.101") - robot_ip = self.get_parameter("robot_ip").get_parameter_value().string_value - self.get_logger().info(robot_ip) - # Port on which the remote pc (robot) publishes the interface - self.declare_parameter("tcp_port", 54321) - tcp_port = self.get_parameter_or("tcp_port", 54321).get_parameter_value().integer_value - # By default, socat will create a pty in /dev/pts/N with n being an increasing number. - # Additionally, a symlink at the given location will be created. Use an absolute path here. - self.declare_parameter("device_name", "/tmp/ttyUR") - local_device = self.get_parameter("device_name").get_parameter_value().string_value - - self.get_logger().info("Remote device is available at '" + local_device + "'") - - cfg_params = ["pty"] - cfg_params.append("link=" + local_device) - cfg_params.append("raw") - cfg_params.append("ignoreeof") - cfg_params.append("waitslave") - - cmd = ["socat"] - cmd.append(",".join(cfg_params)) - cmd.append(":".join(["tcp", robot_ip, str(tcp_port)])) - - self.get_logger().info("Starting socat with following command:\n" + " ".join(cmd)) - subprocess.call(cmd) - - -def main(): - rclpy.init() - node = UrToolCommunication() - rclpy.spin(node) - - -if __name__ == "__main__": - main() diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/wait_dashboard_server.sh b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/wait_dashboard_server.sh deleted file mode 100755 index 69e087b..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/scripts/wait_dashboard_server.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -netcat -z 192.168.56.101 29999 -while [ $? -eq 1 ] -do - echo "Dashboard server not accepting connections..." - sleep 3 - netcat -z 192.168.56.101 29999 -done -echo "Dashboard server connections are possible." -sleep 5 diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/controller_stopper.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/controller_stopper.cpp deleted file mode 100644 index 0fc3726..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/controller_stopper.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik, Created on behalf of Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-06-12 - * - * \author Mads Holm Peters - * \date 2022-02-25 - * - */ -//---------------------------------------------------------------------- - -#include -#include -#include - -#include - -#include "ur_robot_driver/controller_stopper.hpp" - -ControllerStopper::ControllerStopper(const rclcpp::Node::SharedPtr& node, bool stop_controllers_on_startup) - : node_(node), stop_controllers_on_startup_(stop_controllers_on_startup), robot_running_(true) -{ - // Subscribes to a robot's running state topic. Ideally this topic is latched and only publishes - // on changes. However, this node only reacts on state changes, so a state published each cycle - // would also be fine. - robot_running_sub_ = node->create_subscription( - "io_and_status_controller/robot_program_running", 1, - std::bind(&ControllerStopper::robotRunningCallback, this, std::placeholders::_1)); - - // Controller manager service to switch controllers - controller_manager_srv_ = node_->create_client("controller_manager/" - "switch_controller"); - // Controller manager service to list controllers - controller_list_srv_ = node_->create_client("controller_manager/" - "list_controllers"); - - RCLCPP_INFO(rclcpp::get_logger("Controller stopper"), "Waiting for switch controller service to come up on " - "controller_manager/switch_controller"); - controller_manager_srv_->wait_for_service(); - RCLCPP_INFO(rclcpp::get_logger("Controller stopper"), "Service available"); - RCLCPP_INFO(rclcpp::get_logger("Controller stopper"), "Waiting for list controllers service to come up on " - "controller_manager/list_controllers"); - controller_list_srv_->wait_for_service(); - RCLCPP_INFO(rclcpp::get_logger("Controller stopper"), "Service available"); - - consistent_controllers_ = node_->declare_parameter>("consistent_controllers"); - - if (stop_controllers_on_startup_ == true) { - while (stopped_controllers_.empty()) { - auto request = std::make_shared(); - auto future = controller_list_srv_->async_send_request(request); - rclcpp::spin_until_future_complete(node_, future); - auto result = future.get(); - for (auto& controller : result->controller) { - // Check if in consistent_controllers - // Else: - // Add to stopped_controllers - if (controller.state == "active") { - auto it = std::find(consistent_controllers_.begin(), consistent_controllers_.end(), controller.name); - if (it == consistent_controllers_.end()) { - stopped_controllers_.push_back(controller.name); - } - } - } - rclcpp::sleep_for(std::chrono::milliseconds(100)); - } - auto request_switch_controller = std::make_shared(); - request_switch_controller->strictness = request_switch_controller->STRICT; - request_switch_controller->deactivate_controllers = stopped_controllers_; - auto future = controller_manager_srv_->async_send_request(request_switch_controller); - rclcpp::spin_until_future_complete(node_, future); - if (future.get()->ok == false) { - RCLCPP_ERROR(rclcpp::get_logger("Controller stopper"), "Could not deactivate requested controllers"); - } - robot_running_ = false; - } -} - -void ControllerStopper::findAndStopControllers() -{ - stopped_controllers_.clear(); - auto request_switch_controller = std::make_shared(); - auto request_list_controllers = std::make_shared(); - - // Callback to switch controllers - auto callback_switch_controller = - [this](rclcpp::Client::SharedFuture future_response) { - auto result = future_response.get(); - if (result->ok == false) { - RCLCPP_ERROR(rclcpp::get_logger("Controller stopper"), "Could not deactivate requested controllers"); - } - }; - - // Callback to list controllers - auto callback_list_controller = - [this, request_switch_controller, callback_switch_controller]( - rclcpp::Client::SharedFuture future_response) { - auto result = future_response.get(); - for (auto& controller : result->controller) { - // Check if in consistent_controllers - // Else: - // Add to stopped_controllers - if (controller.state == "active") { - auto it = std::find(consistent_controllers_.begin(), consistent_controllers_.end(), controller.name); - if (it == consistent_controllers_.end()) { - stopped_controllers_.push_back(controller.name); - } - } - } - request_switch_controller->strictness = request_switch_controller->STRICT; - if (!stopped_controllers_.empty()) { - request_switch_controller->deactivate_controllers = stopped_controllers_; - auto future = - controller_manager_srv_->async_send_request(request_switch_controller, callback_switch_controller); - } - }; - - auto future = controller_list_srv_->async_send_request(request_list_controllers, callback_list_controller); -} - -void ControllerStopper::startControllers() -{ - // Callback to switch controllers - auto callback = [this](rclcpp::Client::SharedFuture future_response) { - auto result = future_response.get(); - if (result->ok == false) { - RCLCPP_ERROR(rclcpp::get_logger("Controller stopper"), "Could not activate requested controllers"); - } - }; - if (!stopped_controllers_.empty()) { - auto request = std::make_shared(); - request->strictness = request->STRICT; - request->activate_controllers = stopped_controllers_; - auto future = controller_manager_srv_->async_send_request(request, callback); - } -} - -void ControllerStopper::robotRunningCallback(const std_msgs::msg::Bool::ConstSharedPtr msg) -{ - RCLCPP_DEBUG(rclcpp::get_logger("Controller stopper"), "robotRunningCallback with data %d", msg->data); - - if (msg->data && !robot_running_) { - RCLCPP_DEBUG(rclcpp::get_logger("Controller stopper"), "Starting controllers"); - startControllers(); - } else if (!msg->data && robot_running_) { - RCLCPP_DEBUG(rclcpp::get_logger("Controller stopper"), "Stopping controllers"); - // stop all controllers except the once in consistent_controllers_ - findAndStopControllers(); - } - robot_running_ = msg->data; -} diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/controller_stopper_node.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/controller_stopper_node.cpp deleted file mode 100644 index 244ab9a..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/controller_stopper_node.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik, Created on behalf of Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-06-12 - * - * \author Mads Holm Peters - * \date 2022-02-25 - * - */ -//---------------------------------------------------------------------- - -#include "rclcpp/rclcpp.hpp" -#include "ur_robot_driver/controller_stopper.hpp" - -int main(int argc, char** argv) -{ - rclcpp::init(argc, argv); - rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("controller_stopper_node"); - - bool headless_mode = node->declare_parameter("headless_mode", false); - node->get_parameter("headless_mode", headless_mode); - bool joint_controller_active = node->declare_parameter("joint_controller_active", true); - node->get_parameter("joint_controller_active", joint_controller_active); - - // If headless mode is not active, but the joint controllers are we should stop the joint controllers during startup - // of the node - bool stop_controllers_on_startup = false; - if (joint_controller_active == true && headless_mode == false) { - stop_controllers_on_startup = true; - } - - ControllerStopper stopper(node, stop_controllers_on_startup); - - rclcpp::spin(node); - - return 0; -} diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/dashboard_client_node.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/dashboard_client_node.cpp deleted file mode 100644 index 91cdf9f..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/dashboard_client_node.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-10-21 - * - */ -//---------------------------------------------------------------------- - -#include "ur_robot_driver/dashboard_client_ros.hpp" - -#include - -#include "rclcpp/rclcpp.hpp" -#include "ur_robot_driver/urcl_log_handler.hpp" - -int main(int argc, char** argv) -{ - rclcpp::init(argc, argv); - rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("ur_dashboard_client"); - - // The IP address under which the robot is reachable. - std::string robot_ip = node->declare_parameter("robot_ip", "192.168.56.101"); - node->get_parameter("robot_ip", robot_ip); - - ur_robot_driver::registerUrclLogHandler(""); // Set empty tf_prefix at the moment - - ur_robot_driver::DashboardClientROS client(node, robot_ip); - - rclcpp::spin(node); - - return 0; -} diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/dashboard_client_ros.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/dashboard_client_ros.cpp deleted file mode 100644 index d4b866a..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/dashboard_client_ros.cpp +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2019-10-21 - * \author Marvin Große Besselmann grosse@fzi.de - * \date 2021-03-22 - * - */ -//---------------------------------------------------------------------- - -#include - -#include - -namespace ur_robot_driver -{ -DashboardClientROS::DashboardClientROS(const rclcpp::Node::SharedPtr& node, const std::string& robot_ip) - : node_(node), client_(robot_ip) -{ - node_->declare_parameter("receive_timeout", 1); - connect(); - - // Service to release the brakes. If the robot is currently powered off, it will get powered on on the fly. - brake_release_service_ = createDashboardTriggerSrv("~/brake_release", "brake release\n", "Brake releasing"); - - // If this service is called the operational mode can again be changed from PolyScope, and the user password is - // enabled. - clear_operational_mode_service_ = createDashboardTriggerSrv("~/clear_operational_mode", "clear operational mode\n", - "No longer controlling the operational mode\\. " - "Current " - "operational mode: " - "'(MANUAL|AUTOMATIC)'\\."); - - // Close a (non-safety) popup on the teach pendant. - close_popup_service_ = createDashboardTriggerSrv("~/close_popup", "close popup\n", "closing popup"); - - // Close a safety popup on the teach pendant. - close_safety_popup_service_ = - createDashboardTriggerSrv("~/close_safety_popup", "close safety popup\n", "closing safety popup"); - - // Pause a running program. - pause_service_ = createDashboardTriggerSrv("~/pause", "pause\n", "Pausing program"); - - // Start execution of a previously loaded program - play_service_ = createDashboardTriggerSrv("~/play", "play\n", "Starting program"); - - // Power off the robot motors - power_off_service_ = createDashboardTriggerSrv("~/power_off", "power off\n", "Powering off"); - - // Power on the robot motors. To fully start the robot, call 'brake_release' afterwards. - power_on_service_ = createDashboardTriggerSrv("~/power_on", "power on\n", "Powering on"); - - // Used when robot gets a safety fault or violation to restart the safety. After safety has been rebooted the robot - // will be in Power Off. NOTE: You should always ensure it is okay to restart the system. It is highly recommended to - // check the error log before using this command (either via PolyScope or e.g. ssh connection). - restart_safety_service_ = createDashboardTriggerSrv("~/restart_safety", "restart safety\n", "Restarting safety"); - - // Shutdown the robot controller - shutdown_service_ = createDashboardTriggerSrv("~/shutdown", "shutdown\n", "Shutting down"); - - // Stop program execution on the robot - stop_service_ = createDashboardTriggerSrv("~/stop", "stop\n", "Stopped"); - - // Dismiss a protective stop to continue robot movements. NOTE: It is the responsibility of the user to ensure the - // cause of the protective stop is resolved before calling this service. - unlock_protective_stop_service_ = - createDashboardTriggerSrv("~/unlock_protective_stop", "unlock protective stop\n", "Protective stop releasing"); - - // Query whether there is currently a program running - running_service_ = node_->create_service( - "~/program_running", - std::bind(&DashboardClientROS::handleRunningQuery, this, std::placeholders::_1, std::placeholders::_2)); - - // Load a robot installation from a file - get_loaded_program_service_ = node_->create_service( - "~/get_loaded_program", [&](const ur_dashboard_msgs::srv::GetLoadedProgram::Request::SharedPtr req, - ur_dashboard_msgs::srv::GetLoadedProgram::Response::SharedPtr resp) { - try { - resp->answer = this->client_.sendAndReceive("get loaded program\n"); - std::smatch match; - std::regex expected("Loaded program: (.+)"); - resp->success = std::regex_match(resp->answer, match, expected); - if (resp->success) { - resp->program_name = match[1]; - } - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - resp->success = false; - } - return true; - }); - - // Load a robot installation from a file - load_installation_service_ = node_->create_service( - "~/load_installation", [&](const ur_dashboard_msgs::srv::Load::Request::SharedPtr req, - ur_dashboard_msgs::srv::Load::Response::SharedPtr resp) { - try { - resp->answer = this->client_.sendAndReceive("load installation " + req->filename + "\n"); - resp->success = std::regex_match(resp->answer, std::regex("Loading installation: .+")); - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - resp->success = false; - } - return true; - }); - - // Load a robot program from a file - load_program_service_ = node->create_service( - "~/load_program", [&](const ur_dashboard_msgs::srv::Load::Request::SharedPtr req, - ur_dashboard_msgs::srv::Load::Response::SharedPtr resp) { - try { - resp->answer = this->client_.sendAndReceive("load " + req->filename + "\n"); - resp->success = std::regex_match(resp->answer, std::regex("Loading program: .+")); - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - resp->success = false; - } - return true; - }); - - // // Query whether the current program is saved - is_program_saved_service_ = node_->create_service( - "~/program_saved", - std::bind(&DashboardClientROS::handleSavedQuery, this, std::placeholders::_1, std::placeholders::_2)); - - // Service to show a popup on the UR Teach pendant. - popup_service_ = node_->create_service( - "~/popup", [&](ur_dashboard_msgs::srv::Popup::Request::SharedPtr req, - ur_dashboard_msgs::srv::Popup::Response::SharedPtr resp) { - try { - resp->answer = this->client_.sendAndReceive("popup " + req->message + "\n"); - resp->success = std::regex_match(resp->answer, std::regex("showing popup")); - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - resp->success = false; - } - return true; - }); - - // Service to query the current program state - program_state_service_ = node_->create_service( - "~/program_state", [&](const ur_dashboard_msgs::srv::GetProgramState::Request::SharedPtr /*unused*/, - ur_dashboard_msgs::srv::GetProgramState::Response::SharedPtr resp) { - try { - resp->answer = this->client_.sendAndReceive("programState\n"); - std::smatch match; - std::regex expected("(STOPPED|PLAYING|PAUSED) (.+)"); - resp->success = std::regex_match(resp->answer, match, expected); - if (resp->success) { - resp->state.state = match[1]; - resp->program_name = match[2]; - } - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - resp->success = false; - } - return true; - }); - - // Service to query the current safety mode - safety_mode_service_ = node_->create_service( - "~/get_safety_mode", - std::bind(&DashboardClientROS::handleSafetyModeQuery, this, std::placeholders::_1, std::placeholders::_2)); - - // Service to query the current robot mode - robot_mode_service_ = node_->create_service( - "~/get_robot_mode", - std::bind(&DashboardClientROS::handleRobotModeQuery, this, std::placeholders::_1, std::placeholders::_2)); - - // Service to add a message to the robot's log - add_to_log_service_ = node->create_service( - "~/add_to_log", [&](const ur_dashboard_msgs::srv::AddToLog::Request::SharedPtr req, - ur_dashboard_msgs::srv::AddToLog::Response::SharedPtr resp) { - try { - resp->answer = this->client_.sendAndReceive("addToLog " + req->message + "\n"); - resp->success = std::regex_match(resp->answer, std::regex("(Added log message|No log message to add)")); - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - resp->success = false; - } - return true; - }); - - // General purpose service to send arbitrary messages to the dashboard server - raw_request_service_ = node_->create_service( - "~/raw_request", [&](const ur_dashboard_msgs::srv::RawRequest::Request::SharedPtr req, - ur_dashboard_msgs::srv::RawRequest::Response::SharedPtr resp) { - try { - resp->answer = this->client_.sendAndReceive(req->query + "\n"); - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - } - return true; - }); - - // Service to reconnect to the dashboard server - reconnect_service_ = node_->create_service( - "~/connect", - [&](const std_srvs::srv::Trigger::Request::SharedPtr req, std_srvs::srv::Trigger::Response::SharedPtr resp) { - try { - resp->success = connect(); - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->message = e.what(); - resp->success = false; - } - return true; - }); - - // Disconnect from the dashboard service. - quit_service_ = - node_->create_service("~/quit", [&](const std_srvs::srv::Trigger::Request::SharedPtr req, - std_srvs::srv::Trigger::Response::SharedPtr resp) { - try { - resp->message = this->client_.sendAndReceive("quit\n"); - resp->success = std::regex_match(resp->message, std::regex("Disconnected")); - client_.disconnect(); - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->message = e.what(); - resp->success = false; - } - return true; - }); -} - -bool DashboardClientROS::connect() -{ - timeval tv; - // Timeout after which a call to the dashboard server will be considered failure if no answer has been received. - double time_buffer = 0; - node_->get_parameter("receive_timeout", time_buffer); - tv.tv_sec = time_buffer; - tv.tv_usec = 0; - client_.setReceiveTimeout(tv); - return client_.connect(); -} - -bool DashboardClientROS::handleRunningQuery(const ur_dashboard_msgs::srv::IsProgramRunning::Request::SharedPtr req, - ur_dashboard_msgs::srv::IsProgramRunning::Response::SharedPtr resp) -{ - try { - resp->answer = this->client_.sendAndReceive("running\n"); - std::regex expected("Program running: (true|false)"); - std::smatch match; - resp->success = std::regex_match(resp->answer, match, expected); - - if (resp->success) { - resp->program_running = (match[1] == "true"); - } - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - resp->success = false; - } - - return true; -} - -bool DashboardClientROS::handleSavedQuery(ur_dashboard_msgs::srv::IsProgramSaved::Request::SharedPtr req, - ur_dashboard_msgs::srv::IsProgramSaved::Response::SharedPtr resp) -{ - try { - resp->answer = this->client_.sendAndReceive("isProgramSaved\n"); - std::regex expected("(true|false) ([^\\s]+)"); - std::smatch match; - resp->success = std::regex_match(resp->answer, match, expected); - - if (resp->success) { - resp->program_saved = (match[1] == "true"); - resp->program_name = match[2]; - } - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - resp->success = false; - } - - return true; -} - -bool DashboardClientROS::handleSafetyModeQuery(const ur_dashboard_msgs::srv::GetSafetyMode::Request::SharedPtr req, - ur_dashboard_msgs::srv::GetSafetyMode::Response::SharedPtr resp) -{ - try { - resp->answer = this->client_.sendAndReceive("safetymode\n"); - std::smatch match; - std::regex expected("Safetymode: (.+)"); - resp->success = std::regex_match(resp->answer, match, expected); - if (resp->success) { - if (match[1] == "NORMAL") { - resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::NORMAL; - } else if (match[1] == "REDUCED") { - resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::REDUCED; - } else if (match[1] == "PROTECTIVE_STOP") { - resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::PROTECTIVE_STOP; - } else if (match[1] == "RECOVERY") { - resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::RECOVERY; - } else if (match[1] == "SAFEGUARD_STOP") { - resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::SAFEGUARD_STOP; - } else if (match[1] == "SYSTEM_EMERGENCY_STOP") { - resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::SYSTEM_EMERGENCY_STOP; - } else if (match[1] == "ROBOT_EMERGENCY_STOP") { - resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::ROBOT_EMERGENCY_STOP; - } else if (match[1] == "VIOLATION") { - resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::VIOLATION; - } else if (match[1] == "FAULT") { - resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::FAULT; - } - // The following are only available in SafetyStatus from 5.5 on - // else if (match[1] == "AUTOMATIC_MODE_SAFEGUARD_STOP") - //{ - // resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::AUTOMATIC_MODE_SAFEGUARD_STOP; - //} - // else if (match[1] == "SYSTEM_THREE_POSITION_ENABLING_STOP") - //{ - // resp->safety_mode.mode = ur_dashboard_msgs::msg::SafetyMode::SYSTEM_THREE_POSITION_ENABLING_STOP; - //} - } - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - resp->success = false; - } - return true; -} - -bool DashboardClientROS::handleRobotModeQuery(const ur_dashboard_msgs::srv::GetRobotMode::Request::SharedPtr req, - ur_dashboard_msgs::srv::GetRobotMode::Response::SharedPtr resp) -{ - try { - resp->answer = this->client_.sendAndReceive("robotmode\n"); - std::smatch match; - std::regex expected("Robotmode: (.+)"); - resp->success = std::regex_match(resp->answer, match, expected); - if (resp->success) { - if (match[1] == "NO_CONTROLLER") { - resp->robot_mode.mode = ur_dashboard_msgs::msg::RobotMode::NO_CONTROLLER; - } else if (match[1] == "DISCONNECTED") { - resp->robot_mode.mode = ur_dashboard_msgs::msg::RobotMode::DISCONNECTED; - } else if (match[1] == "CONFIRM_SAFETY") { - resp->robot_mode.mode = ur_dashboard_msgs::msg::RobotMode::CONFIRM_SAFETY; - } else if (match[1] == "BOOTING") { - resp->robot_mode.mode = ur_dashboard_msgs::msg::RobotMode::BOOTING; - } else if (match[1] == "POWER_OFF") { - resp->robot_mode.mode = ur_dashboard_msgs::msg::RobotMode::POWER_OFF; - } else if (match[1] == "POWER_ON") { - resp->robot_mode.mode = ur_dashboard_msgs::msg::RobotMode::POWER_ON; - } else if (match[1] == "IDLE") { - resp->robot_mode.mode = ur_dashboard_msgs::msg::RobotMode::IDLE; - } else if (match[1] == "BACKDRIVE") { - resp->robot_mode.mode = ur_dashboard_msgs::msg::RobotMode::BACKDRIVE; - } else if (match[1] == "RUNNING") { - resp->robot_mode.mode = ur_dashboard_msgs::msg::RobotMode::RUNNING; - } else if (match[1] == "UPDATING_FIRMWARE") { - resp->robot_mode.mode = ur_dashboard_msgs::msg::RobotMode::UPDATING_FIRMWARE; - } - } - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("Dashboard_Client"), "Service Call failed: '%s'", e.what()); - resp->answer = e.what(); - resp->success = false; - } - return true; -} -} // namespace ur_robot_driver diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/hardware_interface.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/hardware_interface.cpp deleted file mode 100644 index 157310e..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/hardware_interface.cpp +++ /dev/null @@ -1,1372 +0,0 @@ -// Copyright 2019, FZI Forschungszentrum Informatik -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \author Andy Zelenak zelenak@picknik.ai - * \author Marvin Große Besselmann grosse@fzi.de - * \date 2020-11-9 - * - */ -//---------------------------------------------------------------------- -#include -#include -#include -#include -#include - -#include "ur_client_library/exceptions.h" -#include "ur_client_library/ur/tool_communication.h" -#include "ur_client_library/ur/version_information.h" - -#include -#include "hardware_interface/types/hardware_interface_type_values.hpp" -#include "ur_robot_driver/hardware_interface.hpp" -#include "ur_robot_driver/urcl_log_handler.hpp" - -namespace rtde = urcl::rtde_interface; - -namespace ur_robot_driver -{ - -URPositionHardwareInterface::~URPositionHardwareInterface() -{ - // If the controller manager is shutdown via Ctrl + C the on_deactivate methods won't be called. - // We therefore need to make sure to actually deactivate the communication - on_cleanup(rclcpp_lifecycle::State()); -} - -hardware_interface::CallbackReturn -URPositionHardwareInterface::on_init(const hardware_interface::HardwareInfo& system_info) -{ - if (hardware_interface::SystemInterface::on_init(system_info) != hardware_interface::CallbackReturn::SUCCESS) { - return hardware_interface::CallbackReturn::ERROR; - } - - info_ = system_info; - - // initialize - urcl_joint_positions_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - urcl_joint_velocities_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - urcl_joint_efforts_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - urcl_ft_sensor_measurements_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - urcl_tcp_pose_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - urcl_position_commands_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - urcl_position_commands_old_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - urcl_velocity_commands_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - position_controller_running_ = false; - velocity_controller_running_ = false; - freedrive_mode_controller_running_ = false; - passthrough_trajectory_controller_running_ = false; - runtime_state_ = static_cast(rtde::RUNTIME_STATE::STOPPED); - pausing_state_ = PausingState::RUNNING; - pausing_ramp_up_increment_ = 0.01; - controllers_initialized_ = false; - first_pass_ = true; - initialized_ = false; - async_thread_shutdown_ = false; - system_interface_initialized_ = 0.0; - freedrive_mode_abort_ = 0.0; - passthrough_trajectory_transfer_state_ = 0.0; - passthrough_trajectory_abort_ = 0.0; - trajectory_joint_positions_.clear(); - trajectory_joint_velocities_.clear(); - trajectory_joint_accelerations_.clear(); - - for (const hardware_interface::ComponentInfo& joint : info_.joints) { - if (joint.command_interfaces.size() != 2) { - RCLCPP_FATAL(rclcpp::get_logger("URPositionHardwareInterface"), - "Joint '%s' has %zu command interfaces found. 2 expected.", joint.name.c_str(), - joint.command_interfaces.size()); - return hardware_interface::CallbackReturn::ERROR; - } - - if (joint.command_interfaces[0].name != hardware_interface::HW_IF_POSITION) { - RCLCPP_FATAL(rclcpp::get_logger("URPositionHardwareInterface"), - "Joint '%s' have %s command interfaces found as first command interface. '%s' expected.", - joint.name.c_str(), joint.command_interfaces[0].name.c_str(), hardware_interface::HW_IF_POSITION); - return hardware_interface::CallbackReturn::ERROR; - } - - if (joint.command_interfaces[1].name != hardware_interface::HW_IF_VELOCITY) { - RCLCPP_FATAL(rclcpp::get_logger("URPositionHardwareInterface"), - "Joint '%s' have %s command interfaces found as second command interface. '%s' expected.", - joint.name.c_str(), joint.command_interfaces[1].name.c_str(), hardware_interface::HW_IF_VELOCITY); - return hardware_interface::CallbackReturn::ERROR; - } - - if (joint.state_interfaces.size() != 3) { - RCLCPP_FATAL(rclcpp::get_logger("URPositionHardwareInterface"), "Joint '%s' has %zu state interface. 3 expected.", - joint.name.c_str(), joint.state_interfaces.size()); - return hardware_interface::CallbackReturn::ERROR; - } - - if (joint.state_interfaces[0].name != hardware_interface::HW_IF_POSITION) { - RCLCPP_FATAL(rclcpp::get_logger("URPositionHardwareInterface"), - "Joint '%s' have %s state interface as first state interface. '%s' expected.", joint.name.c_str(), - joint.state_interfaces[0].name.c_str(), hardware_interface::HW_IF_POSITION); - return hardware_interface::CallbackReturn::ERROR; - } - - if (joint.state_interfaces[1].name != hardware_interface::HW_IF_VELOCITY) { - RCLCPP_FATAL(rclcpp::get_logger("URPositionHardwareInterface"), - "Joint '%s' have %s state interface as second state interface. '%s' expected.", joint.name.c_str(), - joint.state_interfaces[1].name.c_str(), hardware_interface::HW_IF_VELOCITY); - return hardware_interface::CallbackReturn::ERROR; - } - - if (joint.state_interfaces[2].name != hardware_interface::HW_IF_EFFORT) { - RCLCPP_FATAL(rclcpp::get_logger("URPositionHardwareInterface"), - "Joint '%s' have %s state interface as third state interface. '%s' expected.", joint.name.c_str(), - joint.state_interfaces[2].name.c_str(), hardware_interface::HW_IF_EFFORT); - return hardware_interface::CallbackReturn::ERROR; - } - } - return hardware_interface::CallbackReturn::SUCCESS; -} - -std::vector URPositionHardwareInterface::export_state_interfaces() -{ - std::vector state_interfaces; - for (size_t i = 0; i < info_.joints.size(); ++i) { - state_interfaces.emplace_back(hardware_interface::StateInterface( - info_.joints[i].name, hardware_interface::HW_IF_POSITION, &urcl_joint_positions_[i])); - - state_interfaces.emplace_back(hardware_interface::StateInterface( - info_.joints[i].name, hardware_interface::HW_IF_VELOCITY, &urcl_joint_velocities_[i])); - - state_interfaces.emplace_back(hardware_interface::StateInterface( - info_.joints[i].name, hardware_interface::HW_IF_EFFORT, &urcl_joint_efforts_[i])); - } - - // Obtain the tf_prefix from the urdf so that we can have the general interface multiple times - // NOTE using the tf_prefix at this point is some kind of workaround. One should actually go through the list of gpio - // state interface in info_ and match them accordingly - const std::string tf_prefix = info_.hardware_parameters.at("tf_prefix"); - state_interfaces.emplace_back(hardware_interface::StateInterface(tf_prefix + "speed_scaling", "speed_scaling_factor", - &speed_scaling_combined_)); - - for (auto& sensor : info_.sensors) { - if (sensor.name == tf_prefix + "tcp_fts_sensor") { - const std::vector fts_names = { - "force.x", "force.y", "force.z", "torque.x", "torque.y", "torque.z" - }; - for (uint j = 0; j < 6; ++j) { - state_interfaces.emplace_back( - hardware_interface::StateInterface(sensor.name, fts_names[j], &urcl_ft_sensor_measurements_[j])); - } - } - } - - for (size_t i = 0; i < 18; ++i) { - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "gpio", "digital_output_" + std::to_string(i), &actual_dig_out_bits_copy_[i])); - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "gpio", "digital_input_" + std::to_string(i), &actual_dig_in_bits_copy_[i])); - } - - for (size_t i = 0; i < 11; ++i) { - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "gpio", "safety_status_bit_" + std::to_string(i), &safety_status_bits_copy_[i])); - } - - for (size_t i = 0; i < 4; ++i) { - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "gpio", "analog_io_type_" + std::to_string(i), &analog_io_types_copy_[i])); - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "gpio", "robot_status_bit_" + std::to_string(i), &robot_status_bits_copy_[i])); - } - - for (size_t i = 0; i < 2; ++i) { - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "gpio", "tool_analog_input_type_" + std::to_string(i), &tool_analog_input_types_copy_[i])); - - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "gpio", "tool_analog_input_" + std::to_string(i), &tool_analog_input_[i])); - - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "gpio", "standard_analog_input_" + std::to_string(i), &standard_analog_input_[i])); - - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "gpio", "standard_analog_output_" + std::to_string(i), &standard_analog_output_[i])); - } - - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "gpio", "tool_output_voltage", &tool_output_voltage_copy_)); - - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "gpio", "robot_mode", &robot_mode_copy_)); - - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "gpio", "safety_mode", &safety_mode_copy_)); - - state_interfaces.emplace_back(hardware_interface::StateInterface(tf_prefix + "gpio", "tool_mode", &tool_mode_copy_)); - - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "gpio", "tool_output_current", &tool_output_current_)); - - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "gpio", "tool_temperature", &tool_temperature_)); - - state_interfaces.emplace_back(hardware_interface::StateInterface(tf_prefix + "system_interface", "initialized", - &system_interface_initialized_)); - - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "gpio", "program_running", &robot_program_running_copy_)); - - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "tcp_pose", "position.x", &urcl_tcp_pose_[0])); - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "tcp_pose", "position.y", &urcl_tcp_pose_[1])); - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "tcp_pose", "position.z", &urcl_tcp_pose_[2])); - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "tcp_pose", "orientation.x", &tcp_rotation_buffer.x)); - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "tcp_pose", "orientation.y", &tcp_rotation_buffer.y)); - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "tcp_pose", "orientation.z", &tcp_rotation_buffer.z)); - state_interfaces.emplace_back( - hardware_interface::StateInterface(tf_prefix + "tcp_pose", "orientation.w", &tcp_rotation_buffer.w)); - - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "get_robot_software_version", "get_version_major", &get_robot_software_version_major_)); - - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "get_robot_software_version", "get_version_minor", &get_robot_software_version_minor_)); - - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "get_robot_software_version", "get_version_bugfix", &get_robot_software_version_bugfix_)); - - state_interfaces.emplace_back(hardware_interface::StateInterface( - tf_prefix + "get_robot_software_version", "get_version_build", &get_robot_software_version_build_)); - - return state_interfaces; -} - -std::vector URPositionHardwareInterface::export_command_interfaces() -{ - std::vector command_interfaces; - for (size_t i = 0; i < info_.joints.size(); ++i) { - command_interfaces.emplace_back(hardware_interface::CommandInterface( - info_.joints[i].name, hardware_interface::HW_IF_POSITION, &urcl_position_commands_[i])); - - command_interfaces.emplace_back(hardware_interface::CommandInterface( - info_.joints[i].name, hardware_interface::HW_IF_VELOCITY, &urcl_velocity_commands_[i])); - } - // Obtain the tf_prefix from the urdf so that we can have the general interface multiple times - // NOTE using the tf_prefix at this point is some kind of workaround. One should actually go through the list of gpio - // command interface in info_ and match them accordingly - const std::string tf_prefix = info_.hardware_parameters.at("tf_prefix"); - - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + "gpio", "io_async_success", &io_async_success_)); - - command_interfaces.emplace_back(hardware_interface::CommandInterface( - tf_prefix + "speed_scaling", "target_speed_fraction_cmd", &target_speed_fraction_cmd_)); - - command_interfaces.emplace_back(hardware_interface::CommandInterface( - tf_prefix + "speed_scaling", "target_speed_fraction_async_success", &scaling_async_success_)); - - command_interfaces.emplace_back(hardware_interface::CommandInterface( - tf_prefix + "resend_robot_program", "resend_robot_program_cmd", &resend_robot_program_cmd_)); - - command_interfaces.emplace_back(hardware_interface::CommandInterface( - tf_prefix + "resend_robot_program", "resend_robot_program_async_success", &resend_robot_program_async_success_)); - command_interfaces.emplace_back(hardware_interface::CommandInterface( - tf_prefix + "hand_back_control", "hand_back_control_cmd", &hand_back_control_cmd_)); - - command_interfaces.emplace_back(hardware_interface::CommandInterface( - tf_prefix + "hand_back_control", "hand_back_control_async_success", &hand_back_control_async_success_)); - - command_interfaces.emplace_back(hardware_interface::CommandInterface(tf_prefix + "payload", "mass", &payload_mass_)); - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + "payload", "cog.x", &payload_center_of_gravity_[0])); - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + "payload", "cog.y", &payload_center_of_gravity_[1])); - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + "payload", "cog.z", &payload_center_of_gravity_[2])); - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + "payload", "payload_async_success", &payload_async_success_)); - - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "task_frame_x", &force_mode_task_frame_[0]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "task_frame_y", &force_mode_task_frame_[1]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "task_frame_z", &force_mode_task_frame_[2]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "task_frame_rx", &force_mode_task_frame_[3]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "task_frame_ry", &force_mode_task_frame_[4]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "task_frame_rz", &force_mode_task_frame_[5]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "selection_vector_x", &force_mode_selection_vector_[0]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "selection_vector_y", &force_mode_selection_vector_[1]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "selection_vector_z", &force_mode_selection_vector_[2]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "selection_vector_rx", &force_mode_selection_vector_[3]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "selection_vector_ry", &force_mode_selection_vector_[4]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "selection_vector_rz", &force_mode_selection_vector_[5]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "wrench_x", &force_mode_wrench_[0]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "wrench_y", &force_mode_wrench_[1]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "wrench_z", &force_mode_wrench_[2]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "wrench_rx", &force_mode_wrench_[3]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "wrench_ry", &force_mode_wrench_[4]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "wrench_rz", &force_mode_wrench_[5]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "type", &force_mode_type_); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "limits_x", &force_mode_limits_[0]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "limits_y", &force_mode_limits_[1]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "limits_z", &force_mode_limits_[2]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "limits_rx", &force_mode_limits_[3]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "limits_ry", &force_mode_limits_[4]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "limits_rz", &force_mode_limits_[5]); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "force_mode_async_success", &force_mode_async_success_); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "disable_cmd", &force_mode_disable_cmd_); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "damping", &force_mode_damping_); - command_interfaces.emplace_back(tf_prefix + FORCE_MODE_GPIO, "gain_scaling", &force_mode_gain_scaling_); - - for (size_t i = 0; i < 18; ++i) { - command_interfaces.emplace_back(hardware_interface::CommandInterface( - tf_prefix + "gpio", "standard_digital_output_cmd_" + std::to_string(i), &standard_dig_out_bits_cmd_[i])); - } - - for (size_t i = 0; i < 2; ++i) { - command_interfaces.emplace_back(hardware_interface::CommandInterface( - tf_prefix + "gpio", "standard_analog_output_cmd_" + std::to_string(i), &standard_analog_output_cmd_[i])); - } - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + "gpio", "analog_output_domain_cmd", &analog_output_domain_cmd_)); - - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + "gpio", "tool_voltage_cmd", &tool_voltage_cmd_)); - - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + "zero_ftsensor", "zero_ftsensor_cmd", &zero_ftsensor_cmd_)); - - command_interfaces.emplace_back(hardware_interface::CommandInterface( - tf_prefix + "zero_ftsensor", "zero_ftsensor_async_success", &zero_ftsensor_async_success_)); - - command_interfaces.emplace_back(hardware_interface::CommandInterface(tf_prefix + FREEDRIVE_MODE_GPIO, "async_success", - &freedrive_mode_async_success_)); - - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + FREEDRIVE_MODE_GPIO, "enable", &freedrive_mode_enable_)); - - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + FREEDRIVE_MODE_GPIO, "abort", &freedrive_mode_abort_)); - - command_interfaces.emplace_back(hardware_interface::CommandInterface(tf_prefix + PASSTHROUGH_GPIO, "transfer_state", - &passthrough_trajectory_transfer_state_)); - - command_interfaces.emplace_back(hardware_interface::CommandInterface(tf_prefix + PASSTHROUGH_GPIO, "time_from_start", - &passthrough_trajectory_time_from_start_)); - command_interfaces.emplace_back( - hardware_interface::CommandInterface(tf_prefix + PASSTHROUGH_GPIO, "abort", &passthrough_trajectory_abort_)); - - for (size_t i = 0; i < 6; ++i) { - command_interfaces.emplace_back(hardware_interface::CommandInterface(tf_prefix + PASSTHROUGH_GPIO, - "setpoint_positions_" + std::to_string(i), - &passthrough_trajectory_positions_[i])); - } - - for (size_t i = 0; i < 6; ++i) { - command_interfaces.emplace_back(hardware_interface::CommandInterface(tf_prefix + PASSTHROUGH_GPIO, - "setpoint_velocities_" + std::to_string(i), - &passthrough_trajectory_velocities_[i])); - } - - for (size_t i = 0; i < 6; ++i) { - command_interfaces.emplace_back(hardware_interface::CommandInterface(tf_prefix + PASSTHROUGH_GPIO, - "setpoint_accelerations_" + std::to_string(i), - &passthrough_trajectory_accelerations_[i])); - } - - return command_interfaces; -} - -hardware_interface::CallbackReturn -URPositionHardwareInterface::on_configure(const rclcpp_lifecycle::State& previous_state) -{ - RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "Starting ...please wait..."); - - // The robot's IP address. - const std::string robot_ip = info_.hardware_parameters["robot_ip"]; - // Path to the urscript code that will be sent to the robot - const std::string script_filename = info_.hardware_parameters["script_filename"]; - // Path to the file containing the recipe used for requesting RTDE outputs. - const std::string output_recipe_filename = info_.hardware_parameters["output_recipe_filename"]; - // Path to the file containing the recipe used for requesting RTDE inputs. - const std::string input_recipe_filename = info_.hardware_parameters["input_recipe_filename"]; - // Start robot in headless mode. This does not require the 'External Control' URCap to be running - // on the robot, but this will send the URScript to the robot directly. On e-Series robots this - // requires the robot to run in 'remote-control' mode. - const bool headless_mode = - (info_.hardware_parameters["headless_mode"] == "true") || (info_.hardware_parameters["headless_mode"] == "True"); - // Port that will be opened to communicate between the driver and the robot controller. - const int reverse_port = stoi(info_.hardware_parameters["reverse_port"]); - // The driver will offer an interface to receive the program's URScript on this port. - const int script_sender_port = stoi(info_.hardware_parameters["script_sender_port"]); - - // The ip address of the host the driver runs on - std::string reverse_ip = info_.hardware_parameters["reverse_ip"]; - if (reverse_ip == "0.0.0.0") { - reverse_ip = ""; - } - - // Port (on the host pc) of the trajectory interface - const int trajectory_port = stoi(info_.hardware_parameters["trajectory_port"]); - - // Port (on the host PC) that will be used to forward script commands from the driver to the robot - const int script_command_port = stoi(info_.hardware_parameters["script_command_port"]); - - // Enables non_blocking_read mode. Should only be used with combined_robot_hw. Disables error generated when read - // returns without any data, sets the read timeout to zero, and synchronises read/write operations. Enabling this when - // not used with combined_robot_hw can suppress important errors and affect real-time performance. - non_blocking_read_ = (info_.hardware_parameters["non_blocking_read"] == "true") || - (info_.hardware_parameters["non_blocking_read"] == "True"); - - // Specify gain for servoing to position in joint space. - // A higher gain can sharpen the trajectory. - const int servoj_gain = stoi(info_.hardware_parameters["servoj_gain"]); - // Specify lookahead time for servoing to position in joint space. - // A longer lookahead time can smooth the trajectory. - const double servoj_lookahead_time = stod(info_.hardware_parameters["servoj_lookahead_time"]); - - const bool use_tool_communication = (info_.hardware_parameters["use_tool_communication"] == "true") || - (info_.hardware_parameters["use_tool_communication"] == "True"); - - // Hash of the calibration reported by the robot. This is used for validating the robot - // description is using the correct calibration. If the robot's calibration doesn't match this - // hash, an error will be printed. You can use the robot as usual, however Cartesian poses of the - // endeffector might be inaccurate. See the "ur_calibration" package on help how to generate your - // own hash matching your actual robot. - const std::string calibration_checksum = info_.hardware_parameters["kinematics/hash"]; - - std::unique_ptr tool_comm_setup; - if (use_tool_communication) { - tool_comm_setup = std::make_unique(); - - using ToolVoltageT = std::underlying_type::type; - - // Tool voltage that will be set as soon as the UR-Program on the robot is started. Note: This - // parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. - // Then, this parameter is required.} - const ToolVoltageT tool_voltage = std::stoi(info_.hardware_parameters["tool_voltage"]); - tool_comm_setup->setToolVoltage(static_cast(tool_voltage)); - - using ParityT = std::underlying_type::type; - - // Parity used for tool communication. Will be set as soon as the UR-Program on the robot is - // started. Can be 0 (None), 1 (odd) and 2 (even). - // - // Note: This parameter is only evaluated, when the parameter "use_tool_communication" - // is set to TRUE. Then, this parameter is required. - const ParityT parity = std::stoi(info_.hardware_parameters["tool_parity"]); - tool_comm_setup->setParity(static_cast(parity)); - - // Baud rate used for tool communication. Will be set as soon as the UR-Program on the robot is - // started. See UR documentation for valid baud rates. - // - // Note: This parameter is only evaluated, when the parameter "use_tool_communication" - // is set to TRUE. Then, this parameter is required. - const int baud_rate = std::stoi(info_.hardware_parameters["tool_baud_rate"]); - tool_comm_setup->setBaudRate(static_cast(baud_rate)); - - // Number of stop bits used for tool communication. Will be set as soon as the UR-Program on the robot is - // started. Can be 1 or 2. - // - // Note: This parameter is only evaluated, when the parameter "use_tool_communication" - // is set to TRUE. Then, this parameter is required. - const int stop_bits = std::stoi(info_.hardware_parameters["tool_stop_bits"]); - tool_comm_setup->setStopBits(static_cast(stop_bits)); - - // Number of idle chars for the RX unit used for tool communication. Will be set as soon as the UR-Program on the - // robot is started. Valid values: min=1.0, max=40.0 - // - // Note: This parameter is only evaluated, when the parameter "use_tool_communication" - // is set to TRUE. Then, this parameter is required. - const int rx_idle_chars = std::stoi(info_.hardware_parameters["tool_rx_idle_chars"]); - tool_comm_setup->setRxIdleChars(rx_idle_chars); - - // Number of idle chars for the TX unit used for tool communication. Will be set as soon as the UR-Program on the - // robot is started. Valid values: min=0.0, max=40.0 - // - // Note: This parameter is only evaluated, when the parameter "use_tool_communication" - // is set to TRUE. Then, this parameter is required. - const int tx_idle_chars = std::stoi(info_.hardware_parameters["tool_tx_idle_chars"]); - tool_comm_setup->setTxIdleChars(tx_idle_chars); - } - - // Obtain the tf_prefix which is needed for the logging handler so that log messages from different arms are - // distiguishable in the log - const std::string tf_prefix = info_.hardware_parameters.at("tf_prefix"); - RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "Initializing driver..."); - registerUrclLogHandler(tf_prefix); - try { - rtde_comm_has_been_started_ = false; - ur_driver_ = std::make_unique( - robot_ip, script_filename, output_recipe_filename, input_recipe_filename, - std::bind(&URPositionHardwareInterface::handleRobotProgramState, this, std::placeholders::_1), headless_mode, - std::move(tool_comm_setup), (uint32_t)reverse_port, (uint32_t)script_sender_port, servoj_gain, - servoj_lookahead_time, non_blocking_read_, reverse_ip, trajectory_port, script_command_port); - } catch (urcl::ToolCommNotAvailable& e) { - RCLCPP_FATAL_STREAM(rclcpp::get_logger("URPositionHardwareInterface"), "See parameter use_tool_communication"); - - return hardware_interface::CallbackReturn::ERROR; - } catch (urcl::UrException& e) { - RCLCPP_FATAL_STREAM(rclcpp::get_logger("URPositionHardwareInterface"), e.what()); - return hardware_interface::CallbackReturn::ERROR; - } - // Timeout before the reverse interface will be dropped by the robot - receive_timeout_ = urcl::RobotReceiveTimeout::millisec(std::stoi(info_.hardware_parameters["keep_alive_count"]) * 20); - - RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "Calibration checksum: '%s'.", - calibration_checksum.c_str()); - // check calibration - // https://github.com/UniversalRobots/Universal_Robots_ROS_Driver/blob/c3378599d5fa73a261328b326392e847f312ab6b/ur_robot_driver/src/hardware_interface.cpp#L296-L309 - if (ur_driver_->checkCalibration(calibration_checksum)) { - RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "Calibration checked successfully."); - } else { - RCLCPP_ERROR_STREAM(rclcpp::get_logger("URPositionHardwareInterface"), - - "The calibration parameters of the connected robot don't match the ones from the given " - "kinematics config file. Please be aware that this can lead to critical inaccuracies of tcp " - "positions. Use the ur_calibration tool to extract the correct calibration from the robot and " - "pass that into the description. See " - "[https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver/blob/main/ur_calibration/" - "README.md] for details."); - } - - // Export version information to state interfaces - urcl::VersionInformation version_info = ur_driver_->getVersion(); - get_robot_software_version_major_ = version_info.major; - get_robot_software_version_minor_ = version_info.minor; - get_robot_software_version_build_ = version_info.build; - get_robot_software_version_bugfix_ = version_info.bugfix; - - async_thread_ = std::make_shared(&URPositionHardwareInterface::asyncThread, this); - - RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "System successfully started!"); - - ur_driver_->registerTrajectoryDoneCallback( - std::bind(&URPositionHardwareInterface::trajectory_done_callback, this, std::placeholders::_1)); - - return hardware_interface::CallbackReturn::SUCCESS; -} - -hardware_interface::CallbackReturn -URPositionHardwareInterface::on_activate(const rclcpp_lifecycle::State& previous_state) -{ - RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "Activating HW interface"); - - for (size_t i = 0; i < 6; i++) { - force_mode_task_frame_[i] = NO_NEW_CMD_; - force_mode_selection_vector_[i] = static_cast(NO_NEW_CMD_); - force_mode_wrench_[i] = NO_NEW_CMD_; - force_mode_limits_[i] = NO_NEW_CMD_; - } - force_mode_type_ = static_cast(NO_NEW_CMD_); - return hardware_interface::CallbackReturn::SUCCESS; -} - -hardware_interface::CallbackReturn -URPositionHardwareInterface::on_cleanup(const rclcpp_lifecycle::State& previous_state) -{ - RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "Stopping ...please wait..."); - - if (async_thread_) { - async_thread_shutdown_ = true; - async_thread_->join(); - async_thread_.reset(); - } - - ur_driver_.reset(); - - unregisterUrclLogHandler(); - - RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "System successfully stopped!"); - - return hardware_interface::CallbackReturn::SUCCESS; -} - -template -void URPositionHardwareInterface::readData(const std::unique_ptr& data_pkg, - const std::string& var_name, T& data) -{ - if (!data_pkg->getData(var_name, data)) { - // This throwing should never happen unless misconfigured - std::string error_msg = "Did not find '" + var_name + "' in data sent from robot. This should not happen!"; - throw std::runtime_error(error_msg); - } -} - -template -void URPositionHardwareInterface::readBitsetData(const std::unique_ptr& data_pkg, - const std::string& var_name, std::bitset& data) -{ - if (!data_pkg->getData(var_name, data)) { - // This throwing should never happen unless misconfigured - std::string error_msg = "Did not find '" + var_name + "' in data sent from robot. This should not happen!"; - throw std::runtime_error(error_msg); - } -} - -void URPositionHardwareInterface::asyncThread() -{ - async_thread_shutdown_ = false; - while (!async_thread_shutdown_) { - if (initialized_) { - // RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "Initialized in async thread"); - checkAsyncIO(); - } - std::this_thread::sleep_for(std::chrono::nanoseconds(20000000)); - } -} - -hardware_interface::return_type URPositionHardwareInterface::read(const rclcpp::Time& time, - const rclcpp::Duration& period) -{ - // We want to start the rtde comm the latest point possible due to the delay times arising from setting up the - // communication with multiple arms - if (!rtde_comm_has_been_started_) { - ur_driver_->startRTDECommunication(); - rtde_comm_has_been_started_ = true; - } - std::unique_ptr data_pkg = ur_driver_->getDataPackage(); - - if (data_pkg) { - packet_read_ = true; - readData(data_pkg, "actual_q", urcl_joint_positions_); - readData(data_pkg, "actual_qd", urcl_joint_velocities_); - readData(data_pkg, "actual_current", urcl_joint_efforts_); - - readData(data_pkg, "target_speed_fraction", target_speed_fraction_); - readData(data_pkg, "speed_scaling", speed_scaling_); - readData(data_pkg, "runtime_state", runtime_state_); - readData(data_pkg, "actual_TCP_force", urcl_ft_sensor_measurements_); - readData(data_pkg, "actual_TCP_pose", urcl_tcp_pose_); - readData(data_pkg, "standard_analog_input0", standard_analog_input_[0]); - readData(data_pkg, "standard_analog_input1", standard_analog_input_[1]); - readData(data_pkg, "standard_analog_output0", standard_analog_output_[0]); - readData(data_pkg, "standard_analog_output1", standard_analog_output_[1]); - readData(data_pkg, "tool_mode", tool_mode_); - readData(data_pkg, "tool_analog_input0", tool_analog_input_[0]); - readData(data_pkg, "tool_analog_input1", tool_analog_input_[1]); - readData(data_pkg, "tool_output_voltage", tool_output_voltage_); - readData(data_pkg, "tool_output_current", tool_output_current_); - readData(data_pkg, "tool_temperature", tool_temperature_); - readData(data_pkg, "robot_mode", robot_mode_); - readData(data_pkg, "safety_mode", safety_mode_); - readBitsetData(data_pkg, "robot_status_bits", robot_status_bits_); - readBitsetData(data_pkg, "safety_status_bits", safety_status_bits_); - readBitsetData(data_pkg, "actual_digital_input_bits", actual_dig_in_bits_); - readBitsetData(data_pkg, "actual_digital_output_bits", actual_dig_out_bits_); - readBitsetData(data_pkg, "analog_io_types", analog_io_types_); - readBitsetData(data_pkg, "tool_analog_input_types", tool_analog_input_types_); - - // required transforms - extractToolPose(); - transformForceTorque(); - - // TODO(anyone): logic for sending other stuff to higher level interface - - // pausing state follows runtime state when pausing - if (runtime_state_ == static_cast(rtde::RUNTIME_STATE::PAUSED)) { - pausing_state_ = PausingState::PAUSED; - } else if (runtime_state_ == static_cast(rtde::RUNTIME_STATE::PLAYING) && - pausing_state_ == PausingState::PAUSED) { - // When the robot resumed program execution and pausing state was PAUSED, we enter RAMPUP - speed_scaling_combined_ = 0.0; - pausing_state_ = PausingState::RAMPUP; - } - - if (pausing_state_ == PausingState::RAMPUP) { - double speed_scaling_ramp = speed_scaling_combined_ + pausing_ramp_up_increment_; - speed_scaling_combined_ = std::min(speed_scaling_ramp, speed_scaling_ * target_speed_fraction_); - - if (speed_scaling_ramp > speed_scaling_ * target_speed_fraction_) { - pausing_state_ = PausingState::RUNNING; - } - } else if (runtime_state_ == static_cast(rtde::RUNTIME_STATE::RESUMING)) { - // We have to keep speed scaling on ROS side at 0 during RESUMING to prevent controllers from - // continuing to interpolate - speed_scaling_combined_ = 0.0; - } else { - // Normal case - speed_scaling_combined_ = speed_scaling_ * target_speed_fraction_; - } - - if (first_pass_ && !initialized_) { - initAsyncIO(); - // initialize commands - urcl_position_commands_ = urcl_position_commands_old_ = urcl_joint_positions_; - urcl_velocity_commands_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - target_speed_fraction_cmd_ = NO_NEW_CMD_; - resend_robot_program_cmd_ = NO_NEW_CMD_; - zero_ftsensor_cmd_ = NO_NEW_CMD_; - hand_back_control_cmd_ = NO_NEW_CMD_; - force_mode_disable_cmd_ = NO_NEW_CMD_; - freedrive_mode_abort_ = NO_NEW_CMD_; - freedrive_mode_enable_ = NO_NEW_CMD_; - initialized_ = true; - } - - updateNonDoubleValues(); - - return hardware_interface::return_type::OK; - } - if (!non_blocking_read_) - RCLCPP_ERROR(rclcpp::get_logger("URPositionHardwareInterface"), "Unable to read from hardware..."); - // TODO(anyone): could not read from the driver --> return ERROR --> on error will be called - return hardware_interface::return_type::OK; -} - -hardware_interface::return_type URPositionHardwareInterface::write(const rclcpp::Time& time, - const rclcpp::Duration& period) -{ - // If there is no interpreting program running on the robot, we do not want to send anything. - // TODO(anyone): We would still like to disable the controllers requiring a writable interface. In ROS1 - // this was done externally using the controller_stopper. - if ((runtime_state_ == static_cast(rtde::RUNTIME_STATE::PLAYING) || - runtime_state_ == static_cast(rtde::RUNTIME_STATE::PAUSING)) && - robot_program_running_ && (!non_blocking_read_ || packet_read_)) { - if (position_controller_running_) { - ur_driver_->writeJointCommand(urcl_position_commands_, urcl::comm::ControlMode::MODE_SERVOJ, receive_timeout_); - - } else if (velocity_controller_running_) { - ur_driver_->writeJointCommand(urcl_velocity_commands_, urcl::comm::ControlMode::MODE_SPEEDJ, receive_timeout_); - - } else if (freedrive_mode_controller_running_ && freedrive_activated_) { - ur_driver_->writeFreedriveControlMessage(urcl::control::FreedriveControlMessage::FREEDRIVE_NOOP); - - } else if (passthrough_trajectory_controller_running_) { - ur_driver_->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_NOOP); - check_passthrough_trajectory_controller(); - } else { - ur_driver_->writeKeepalive(); - } - - if (!std::isnan(force_mode_task_frame_[0]) && !std::isnan(force_mode_selection_vector_[0]) && - !std::isnan(force_mode_wrench_[0]) && !std::isnan(force_mode_type_) && !std::isnan(force_mode_limits_[0]) && - !std::isnan(force_mode_damping_) && !std::isnan(force_mode_gain_scaling_) && ur_driver_ != nullptr) { - start_force_mode(); - } else if (!std::isnan(force_mode_disable_cmd_) && ur_driver_ != nullptr && force_mode_async_success_ == 2.0) { - stop_force_mode(); - } - - packet_read_ = false; - } - - return hardware_interface::return_type::OK; -} - -void URPositionHardwareInterface::handleRobotProgramState(bool program_running) -{ - robot_program_running_ = program_running; -} - -void URPositionHardwareInterface::initAsyncIO() -{ - for (size_t i = 0; i < 18; ++i) { - standard_dig_out_bits_cmd_[i] = NO_NEW_CMD_; - } - - for (size_t i = 0; i < 2; ++i) { - standard_analog_output_cmd_[i] = NO_NEW_CMD_; - } - - analog_output_domain_cmd_ = NO_NEW_CMD_; - - tool_voltage_cmd_ = NO_NEW_CMD_; - - payload_mass_ = NO_NEW_CMD_; - payload_center_of_gravity_ = { NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_ }; -} - -void URPositionHardwareInterface::checkAsyncIO() -{ - if (!rtde_comm_has_been_started_) { - return; - } - for (size_t i = 0; i < 18; ++i) { - if (!std::isnan(standard_dig_out_bits_cmd_[i]) && ur_driver_ != nullptr) { - if (i <= 7) { - io_async_success_ = - ur_driver_->getRTDEWriter().sendStandardDigitalOutput(i, static_cast(standard_dig_out_bits_cmd_[i])); - } else if (i <= 15) { - io_async_success_ = ur_driver_->getRTDEWriter().sendConfigurableDigitalOutput( - static_cast(i - 8), static_cast(standard_dig_out_bits_cmd_[i])); - } else { - io_async_success_ = ur_driver_->getRTDEWriter().sendToolDigitalOutput( - static_cast(i - 16), static_cast(standard_dig_out_bits_cmd_[i])); - } - standard_dig_out_bits_cmd_[i] = NO_NEW_CMD_; - } - } - - for (size_t i = 0; i < 2; ++i) { - if (!std::isnan(standard_analog_output_cmd_[i]) && ur_driver_ != nullptr) { - urcl::AnalogOutputType domain = urcl::AnalogOutputType::SET_ON_TEACH_PENDANT; - if (!std::isnan(analog_output_domain_cmd_) && ur_driver_ != nullptr) { - domain = static_cast(analog_output_domain_cmd_); - analog_output_domain_cmd_ = NO_NEW_CMD_; - } - io_async_success_ = - ur_driver_->getRTDEWriter().sendStandardAnalogOutput(i, standard_analog_output_cmd_[i], domain); - standard_analog_output_cmd_[i] = NO_NEW_CMD_; - } - } - - if (!std::isnan(tool_voltage_cmd_) && ur_driver_ != nullptr) { - io_async_success_ = ur_driver_->setToolVoltage(static_cast(tool_voltage_cmd_)); - tool_voltage_cmd_ = NO_NEW_CMD_; - } - - if (!std::isnan(target_speed_fraction_cmd_) && ur_driver_ != nullptr) { - scaling_async_success_ = ur_driver_->getRTDEWriter().sendSpeedSlider(target_speed_fraction_cmd_); - target_speed_fraction_cmd_ = NO_NEW_CMD_; - } - - if (!std::isnan(resend_robot_program_cmd_) && ur_driver_ != nullptr) { - try { - resend_robot_program_async_success_ = ur_driver_->sendRobotProgram(); - } catch (const urcl::UrException& e) { - RCLCPP_ERROR(rclcpp::get_logger("URPositionHardwareInterface"), "Service Call failed: '%s'", e.what()); - } - resend_robot_program_cmd_ = NO_NEW_CMD_; - } - - if (!std::isnan(hand_back_control_cmd_) && ur_driver_ != nullptr) { - robot_program_running_ = false; - hand_back_control_async_success_ = true; - hand_back_control_cmd_ = NO_NEW_CMD_; - } - - if (!std::isnan(payload_mass_) && !std::isnan(payload_center_of_gravity_[0]) && - !std::isnan(payload_center_of_gravity_[1]) && !std::isnan(payload_center_of_gravity_[2]) && - ur_driver_ != nullptr) { - payload_async_success_ = ur_driver_->setPayload(payload_mass_, payload_center_of_gravity_); - payload_mass_ = NO_NEW_CMD_; - payload_center_of_gravity_ = { NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_ }; - } - - if (!std::isnan(zero_ftsensor_cmd_) && ur_driver_ != nullptr) { - zero_ftsensor_async_success_ = ur_driver_->zeroFTSensor(); - zero_ftsensor_cmd_ = NO_NEW_CMD_; - } - - if (!std::isnan(freedrive_mode_enable_) && ur_driver_ != nullptr) { - RCLCPP_INFO(rclcpp::get_logger("URPosistionHardwareInterface"), "Starting freedrive mode."); - freedrive_mode_async_success_ = - ur_driver_->writeFreedriveControlMessage(urcl::control::FreedriveControlMessage::FREEDRIVE_START); - freedrive_mode_enable_ = NO_NEW_CMD_; - freedrive_activated_ = true; - } - - if (!std::isnan(freedrive_mode_abort_) && freedrive_mode_abort_ == 1.0 && freedrive_activated_ && - ur_driver_ != nullptr) { - RCLCPP_INFO(rclcpp::get_logger("URPosistionHardwareInterface"), "Stopping freedrive mode."); - freedrive_mode_async_success_ = - ur_driver_->writeFreedriveControlMessage(urcl::control::FreedriveControlMessage::FREEDRIVE_STOP); - freedrive_activated_ = false; - freedrive_mode_abort_ = NO_NEW_CMD_; - } -} - -void URPositionHardwareInterface::updateNonDoubleValues() -{ - for (size_t i = 0; i < 18; ++i) { - actual_dig_out_bits_copy_[i] = static_cast(actual_dig_out_bits_[i]); - actual_dig_in_bits_copy_[i] = static_cast(actual_dig_in_bits_[i]); - } - - for (size_t i = 0; i < 11; ++i) { - safety_status_bits_copy_[i] = static_cast(safety_status_bits_[i]); - } - - for (size_t i = 0; i < 4; ++i) { - analog_io_types_copy_[i] = static_cast(analog_io_types_[i]); - robot_status_bits_copy_[i] = static_cast(robot_status_bits_[i]); - } - - for (size_t i = 0; i < 2; ++i) { - tool_analog_input_types_copy_[i] = static_cast(tool_analog_input_types_[i]); - } - - tool_output_voltage_copy_ = static_cast(tool_output_voltage_); - robot_mode_copy_ = static_cast(robot_mode_); - safety_mode_copy_ = static_cast(safety_mode_); - tool_mode_copy_ = static_cast(tool_mode_); - system_interface_initialized_ = initialized_ ? 1.0 : 0.0; - robot_program_running_copy_ = robot_program_running_ ? 1.0 : 0.0; -} - -void URPositionHardwareInterface::transformForceTorque() -{ - // imported from ROS1 driver - hardware_interface.cpp#L867-L876 - tcp_force_.setValue(urcl_ft_sensor_measurements_[0], urcl_ft_sensor_measurements_[1], - urcl_ft_sensor_measurements_[2]); - tcp_torque_.setValue(urcl_ft_sensor_measurements_[3], urcl_ft_sensor_measurements_[4], - urcl_ft_sensor_measurements_[5]); - - tcp_force_ = tf2::quatRotate(tcp_rotation_quat_.inverse(), tcp_force_); - tcp_torque_ = tf2::quatRotate(tcp_rotation_quat_.inverse(), tcp_torque_); - - urcl_ft_sensor_measurements_ = { tcp_force_.x(), tcp_force_.y(), tcp_force_.z(), - tcp_torque_.x(), tcp_torque_.y(), tcp_torque_.z() }; -} - -void URPositionHardwareInterface::extractToolPose() -{ - // imported from ROS1 driver hardware_interface.cpp#L911-L928 - double tcp_angle = - std::sqrt(std::pow(urcl_tcp_pose_[3], 2) + std::pow(urcl_tcp_pose_[4], 2) + std::pow(urcl_tcp_pose_[5], 2)); - - tf2::Vector3 rotation_vec(urcl_tcp_pose_[3], urcl_tcp_pose_[4], urcl_tcp_pose_[5]); - if (tcp_angle > 1e-16) { - tcp_rotation_quat_.setRotation(rotation_vec.normalized(), tcp_angle); - } else { - tcp_rotation_quat_.setValue(0.0, 0.0, 0.0, 1.0); // default Quaternion is 0,0,0,0 which is invalid - } - tcp_rotation_buffer.set(tcp_rotation_quat_); -} - -hardware_interface::return_type URPositionHardwareInterface::prepare_command_mode_switch( - const std::vector& start_interfaces, const std::vector& stop_interfaces) -{ - hardware_interface::return_type ret_val = hardware_interface::return_type::OK; - - start_modes_ = std::vector>(info_.joints.size()); - stop_modes_ = std::vector>(info_.joints.size()); - std::vector> control_modes(info_.joints.size()); - const std::string tf_prefix = info_.hardware_parameters.at("tf_prefix"); - - // Assess current state - for (auto i = 0u; i < info_.joints.size(); i++) { - if (position_controller_running_) { - control_modes[i] = { hardware_interface::HW_IF_POSITION }; - } - if (velocity_controller_running_) { - control_modes[i] = { hardware_interface::HW_IF_VELOCITY }; - } - if (force_mode_controller_running_) { - control_modes[i].push_back(FORCE_MODE_GPIO); - } - if (passthrough_trajectory_controller_running_) { - control_modes[i].push_back(PASSTHROUGH_GPIO); - } - if (freedrive_mode_controller_running_) { - control_modes[i].push_back(FREEDRIVE_MODE_GPIO); - } - } - - if (!std::all_of(start_modes_.begin() + 1, start_modes_.end(), - [&](const std::vector& other) { return other == start_modes_[0]; })) { - RCLCPP_ERROR(rclcpp::get_logger("URPositionHardwareInterface"), "Start modes of all joints have to be the same."); - return hardware_interface::return_type::ERROR; - } - - // Starting interfaces - // If a joint has been reserved already, raise an error. - // Modes that are not directly mapped to a single joint such as force_mode reserve all joints. - for (const auto& key : start_interfaces) { - for (auto i = 0u; i < info_.joints.size(); i++) { - if (key == info_.joints[i].name + "/" + hardware_interface::HW_IF_POSITION) { - if (!start_modes_[i].empty()) { - RCLCPP_ERROR(rclcpp::get_logger("URPositionHardwareInterface"), "Attempting to start position control while " - "there is another control mode already " - "requested."); - return hardware_interface::return_type::ERROR; - } - start_modes_[i] = { hardware_interface::HW_IF_POSITION }; - } else if (key == info_.joints[i].name + "/" + hardware_interface::HW_IF_VELOCITY) { - if (!start_modes_[i].empty()) { - RCLCPP_ERROR(rclcpp::get_logger("URPositionHardwareInterface"), "Attempting to start velocity control while " - "there is another control mode already " - "requested."); - return hardware_interface::return_type::ERROR; - } - start_modes_[i] = { hardware_interface::HW_IF_VELOCITY }; - } else if (key == tf_prefix + FORCE_MODE_GPIO + "/type") { - if (std::any_of(start_modes_[i].begin(), start_modes_[i].end(), [&](const std::string& item) { - return item == hardware_interface::HW_IF_POSITION || item == hardware_interface::HW_IF_VELOCITY; - })) { - RCLCPP_ERROR(rclcpp::get_logger("URPositionHardwareInterface"), "Attempting to start force_mode control " - "while there is either position or " - "velocity mode already requested by another " - "controller."); - return hardware_interface::return_type::ERROR; - } - start_modes_[i].push_back(FORCE_MODE_GPIO); - } else if (key == tf_prefix + PASSTHROUGH_GPIO + "/setpoint_positions_" + std::to_string(i)) { - if (std::any_of(start_modes_[i].begin(), start_modes_[i].end(), [&](const std::string& item) { - return item == hardware_interface::HW_IF_POSITION || item == hardware_interface::HW_IF_VELOCITY; - })) { - RCLCPP_ERROR(rclcpp::get_logger("URPositionHardwareInterface"), "Attempting to start trajectory passthrough " - "control while there is either " - "position or velocity mode already requested " - "by another controller."); - return hardware_interface::return_type::ERROR; - } - start_modes_[i].push_back(PASSTHROUGH_GPIO); - } else if (key == tf_prefix + FREEDRIVE_MODE_GPIO + "/async_success") { - if (std::any_of(start_modes_[i].begin(), start_modes_[i].end(), [&](const std::string& item) { - return item == hardware_interface::HW_IF_POSITION || item == hardware_interface::HW_IF_VELOCITY || - item == PASSTHROUGH_GPIO || item == FORCE_MODE_GPIO; - })) { - return hardware_interface::return_type::ERROR; - } - start_modes_[i].push_back(FREEDRIVE_MODE_GPIO); - } - } - } - - // Stopping interfaces - // add stop interface per joint in tmp var for later check - for (const auto& key : stop_interfaces) { - for (auto i = 0u; i < info_.joints.size(); i++) { - if (key == info_.joints[i].name + "/" + hardware_interface::HW_IF_POSITION) { - stop_modes_[i].push_back(StoppingInterface::STOP_POSITION); - control_modes[i].erase( - std::remove_if(control_modes[i].begin(), control_modes[i].end(), - [](const std::string& item) { return item == hardware_interface::HW_IF_POSITION; }), - control_modes[i].end()); - } - if (key == info_.joints[i].name + "/" + hardware_interface::HW_IF_VELOCITY) { - stop_modes_[i].push_back(StoppingInterface::STOP_VELOCITY); - control_modes[i].erase( - std::remove_if(control_modes[i].begin(), control_modes[i].end(), - [](const std::string& item) { return item == hardware_interface::HW_IF_VELOCITY; }), - control_modes[i].end()); - } - if (key == tf_prefix + FORCE_MODE_GPIO + "/disable_cmd") { - stop_modes_[i].push_back(StoppingInterface::STOP_FORCE_MODE); - control_modes[i].erase(std::remove_if(control_modes[i].begin(), control_modes[i].end(), - [&](const std::string& item) { return item == FORCE_MODE_GPIO; }), - control_modes[i].end()); - } - if (key == tf_prefix + PASSTHROUGH_GPIO + "/setpoint_positions_" + std::to_string(i)) { - stop_modes_[i].push_back(StoppingInterface::STOP_PASSTHROUGH); - control_modes[i].erase(std::remove_if(control_modes[i].begin(), control_modes[i].end(), - [&](const std::string& item) { return item == PASSTHROUGH_GPIO; }), - control_modes[i].end()); - } - if (key == tf_prefix + FREEDRIVE_MODE_GPIO + "/async_success") { - stop_modes_[i].push_back(StoppingInterface::STOP_FREEDRIVE); - control_modes[i].erase(std::remove_if(control_modes[i].begin(), control_modes[i].end(), - [&](const std::string& item) { return item == FREEDRIVE_MODE_GPIO; }), - control_modes[i].end()); - } - } - } - - // Do not start conflicting controllers - // Passthrough controller requested to start - if (std::any_of(start_modes_[0].begin(), start_modes_[0].end(), - [this](auto& item) { return (item == PASSTHROUGH_GPIO); }) && - (std::any_of(start_modes_[0].begin(), start_modes_[0].end(), - [this](auto& item) { - return (item == hardware_interface::HW_IF_VELOCITY || item == hardware_interface::HW_IF_POSITION || - item == FREEDRIVE_MODE_GPIO); - }) || - std::any_of(control_modes[0].begin(), control_modes[0].end(), [this](auto& item) { - return (item == hardware_interface::HW_IF_VELOCITY || item == hardware_interface::HW_IF_POSITION || - item == FREEDRIVE_MODE_GPIO); - }))) { - RCLCPP_ERROR(rclcpp::get_logger("URPosistionHardwareInterface"), "Attempting to start passthrough_trajectory " - "control while there is either position or " - "velocity or freedrive mode running."); - ret_val = hardware_interface::return_type::ERROR; - } - - // Force mode requested to start - if (std::any_of(start_modes_[0].begin(), start_modes_[0].end(), - [this](auto& item) { return (item == FORCE_MODE_GPIO); }) && - (std::any_of(start_modes_[0].begin(), start_modes_[0].end(), - [this](auto& item) { - return (item == hardware_interface::HW_IF_VELOCITY || item == hardware_interface::HW_IF_POSITION || - item == FREEDRIVE_MODE_GPIO); - }) || - std::any_of(control_modes[0].begin(), control_modes[0].end(), [this](auto& item) { - return (item == hardware_interface::HW_IF_VELOCITY || item == hardware_interface::HW_IF_POSITION || - item == FORCE_MODE_GPIO || item == FREEDRIVE_MODE_GPIO); - }))) { - RCLCPP_ERROR(rclcpp::get_logger("URPosistionHardwareInterface"), "Attempting to start force mode control while " - "there is either position or " - "velocity mode running."); - ret_val = hardware_interface::return_type::ERROR; - } - - // Freedrive mode requested to start - if (std::any_of(start_modes_[0].begin(), start_modes_[0].end(), - [this](auto& item) { return (item == FREEDRIVE_MODE_GPIO); }) && - (std::any_of(start_modes_[0].begin(), start_modes_[0].end(), - [this](auto& item) { - return (item == hardware_interface::HW_IF_VELOCITY || item == hardware_interface::HW_IF_POSITION || - item == PASSTHROUGH_GPIO || item == FORCE_MODE_GPIO); - }) || - std::any_of(control_modes[0].begin(), control_modes[0].end(), [this](auto& item) { - return (item == hardware_interface::HW_IF_VELOCITY || item == hardware_interface::HW_IF_POSITION || - item == PASSTHROUGH_GPIO || item == FORCE_MODE_GPIO); - }))) { - RCLCPP_ERROR(rclcpp::get_logger("URPosistionHardwareInterface"), "Attempting to start force mode control while " - "there is either position or " - "velocity mode running."); - ret_val = hardware_interface::return_type::ERROR; - } - - // Position mode requested to start - if (std::any_of(start_modes_[0].begin(), start_modes_[0].end(), - [](auto& item) { return (item == hardware_interface::HW_IF_POSITION); }) && - (std::any_of(start_modes_[0].begin(), start_modes_[0].end(), - [this](auto& item) { - return (item == hardware_interface::HW_IF_VELOCITY || item == PASSTHROUGH_GPIO || - item == FORCE_MODE_GPIO || item == FREEDRIVE_MODE_GPIO); - }) || - std::any_of(control_modes[0].begin(), control_modes[0].end(), [this](auto& item) { - return (item == hardware_interface::HW_IF_VELOCITY || item == hardware_interface::HW_IF_POSITION || - item == PASSTHROUGH_GPIO || item == FORCE_MODE_GPIO || item == FREEDRIVE_MODE_GPIO); - }))) { - RCLCPP_ERROR(rclcpp::get_logger("URPosistionHardwareInterface"), "Attempting to start position control while there " - "is either trajectory passthrough or " - "velocity mode or force_mode or freedrive mode " - "running."); - ret_val = hardware_interface::return_type::ERROR; - } - - // Velocity mode requested to start - if (std::any_of(start_modes_[0].begin(), start_modes_[0].end(), - [](auto& item) { return (item == hardware_interface::HW_IF_VELOCITY); }) && - (std::any_of(start_modes_[0].begin(), start_modes_[0].end(), - [this](auto& item) { - return (item == hardware_interface::HW_IF_POSITION || item == PASSTHROUGH_GPIO || - item == FORCE_MODE_GPIO || item == FREEDRIVE_MODE_GPIO); - }) || - std::any_of(control_modes[0].begin(), control_modes[0].end(), [this](auto& item) { - return (item == hardware_interface::HW_IF_VELOCITY || item == hardware_interface::HW_IF_POSITION || - item == PASSTHROUGH_GPIO || item == FORCE_MODE_GPIO || item == FREEDRIVE_MODE_GPIO); - }))) { - RCLCPP_ERROR(rclcpp::get_logger("URPosistionHardwareInterface"), "Attempting to start velocity control while there " - "is either trajectory passthrough or " - "position mode or force_mode or freedrive mode " - "running."); - ret_val = hardware_interface::return_type::ERROR; - } - - controllers_initialized_ = true; - return ret_val; -} - -hardware_interface::return_type URPositionHardwareInterface::perform_command_mode_switch( - const std::vector& start_interfaces, const std::vector& stop_interfaces) -{ - hardware_interface::return_type ret_val = hardware_interface::return_type::OK; - - if (stop_modes_[0].size() != 0 && std::find(stop_modes_[0].begin(), stop_modes_[0].end(), - StoppingInterface::STOP_POSITION) != stop_modes_[0].end()) { - position_controller_running_ = false; - urcl_position_commands_ = urcl_position_commands_old_ = urcl_joint_positions_; - } else if (stop_modes_[0].size() != 0 && std::find(stop_modes_[0].begin(), stop_modes_[0].end(), - StoppingInterface::STOP_VELOCITY) != stop_modes_[0].end()) { - velocity_controller_running_ = false; - urcl_velocity_commands_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - } else if (stop_modes_[0].size() != 0 && std::find(stop_modes_[0].begin(), stop_modes_[0].end(), - StoppingInterface::STOP_FORCE_MODE) != stop_modes_[0].end()) { - force_mode_controller_running_ = false; - stop_force_mode(); - } else if (stop_modes_[0].size() != 0 && std::find(stop_modes_[0].begin(), stop_modes_[0].end(), - StoppingInterface::STOP_PASSTHROUGH) != stop_modes_[0].end()) { - passthrough_trajectory_controller_running_ = false; - passthrough_trajectory_abort_ = 1.0; - trajectory_joint_positions_.clear(); - trajectory_joint_accelerations_.clear(); - trajectory_joint_velocities_.clear(); - } else if (stop_modes_.size() != 0 && std::find(stop_modes_[0].begin(), stop_modes_[0].end(), - StoppingInterface::STOP_FREEDRIVE) != stop_modes_[0].end()) { - freedrive_mode_controller_running_ = false; - freedrive_activated_ = false; - freedrive_mode_abort_ = 1.0; - } - - if (start_modes_.size() != 0 && std::find(start_modes_[0].begin(), start_modes_[0].end(), - hardware_interface::HW_IF_POSITION) != start_modes_[0].end()) { - velocity_controller_running_ = false; - passthrough_trajectory_controller_running_ = false; - urcl_position_commands_ = urcl_position_commands_old_ = urcl_joint_positions_; - position_controller_running_ = true; - - } else if (start_modes_[0].size() != 0 && std::find(start_modes_[0].begin(), start_modes_[0].end(), - hardware_interface::HW_IF_VELOCITY) != start_modes_[0].end()) { - position_controller_running_ = false; - passthrough_trajectory_controller_running_ = false; - urcl_velocity_commands_ = { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } }; - velocity_controller_running_ = true; - } else if (start_modes_[0].size() != 0 && - std::find(start_modes_[0].begin(), start_modes_[0].end(), FORCE_MODE_GPIO) != start_modes_[0].end()) { - force_mode_controller_running_ = true; - } else if (start_modes_[0].size() != 0 && - std::find(start_modes_[0].begin(), start_modes_[0].end(), PASSTHROUGH_GPIO) != start_modes_[0].end()) { - velocity_controller_running_ = false; - position_controller_running_ = false; - passthrough_trajectory_controller_running_ = true; - passthrough_trajectory_abort_ = 0.0; - } else if (start_modes_[0].size() != 0 && - std::find(start_modes_[0].begin(), start_modes_[0].end(), FREEDRIVE_MODE_GPIO) != start_modes_[0].end()) { - velocity_controller_running_ = false; - position_controller_running_ = false; - freedrive_mode_controller_running_ = true; - freedrive_activated_ = false; - } - - start_modes_.clear(); - stop_modes_.clear(); - - return ret_val; -} - -void URPositionHardwareInterface::start_force_mode() -{ - for (size_t i = 0; i < force_mode_selection_vector_.size(); i++) { - force_mode_selection_vector_copy_[i] = force_mode_selection_vector_[i]; - } - /* Check version of robot to ensure that the correct startForceMode is called. */ - if (ur_driver_->getVersion().major < 5) { - force_mode_async_success_ = - ur_driver_->startForceMode(force_mode_task_frame_, force_mode_selection_vector_copy_, force_mode_wrench_, - force_mode_type_, force_mode_limits_, force_mode_damping_); - if (force_mode_gain_scaling_ != 0.5) { - RCLCPP_WARN(rclcpp::get_logger("URPositionHardwareInterface"), "Force mode gain scaling cannot be used on " - "CB3 " - "robots. Starting force mode, but " - "disregarding " - "gain scaling."); - } - } else { - force_mode_async_success_ = - ur_driver_->startForceMode(force_mode_task_frame_, force_mode_selection_vector_copy_, force_mode_wrench_, - force_mode_type_, force_mode_limits_, force_mode_damping_, force_mode_gain_scaling_); - } - - for (size_t i = 0; i < 6; i++) { - force_mode_task_frame_[i] = NO_NEW_CMD_; - force_mode_selection_vector_[i] = static_cast(NO_NEW_CMD_); - force_mode_wrench_[i] = NO_NEW_CMD_; - force_mode_limits_[i] = NO_NEW_CMD_; - } - force_mode_type_ = static_cast(NO_NEW_CMD_); - force_mode_damping_ = NO_NEW_CMD_; - force_mode_gain_scaling_ = NO_NEW_CMD_; -} - -void URPositionHardwareInterface::stop_force_mode() -{ - force_mode_async_success_ = ur_driver_->endForceMode(); - force_mode_disable_cmd_ = NO_NEW_CMD_; -} - -void URPositionHardwareInterface::check_passthrough_trajectory_controller() -{ - static double last_time = 0.0; - // See passthrough_trajectory_controller.hpp for an explanation of the passthrough_trajectory_transfer_state_ values. - - // We should abort and are not in state IDLE - if (passthrough_trajectory_abort_ == 1.0 && passthrough_trajectory_transfer_state_ != 0.0) { - ur_driver_->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_CANCEL); - } else if (passthrough_trajectory_transfer_state_ == 2.0) { - passthrough_trajectory_abort_ = 0.0; - trajectory_joint_positions_.push_back(passthrough_trajectory_positions_); - - trajectory_times_.push_back(passthrough_trajectory_time_from_start_ - last_time); - last_time = passthrough_trajectory_time_from_start_; - - if (!std::isnan(passthrough_trajectory_velocities_[0])) { - trajectory_joint_velocities_.push_back(passthrough_trajectory_velocities_); - } - if (!std::isnan(passthrough_trajectory_accelerations_[0])) { - trajectory_joint_accelerations_.push_back(passthrough_trajectory_accelerations_); - } - passthrough_trajectory_transfer_state_ = 1.0; - /* When all points have been read, write them to the physical robot controller.*/ - } else if (passthrough_trajectory_transfer_state_ == 3.0) { - /* Tell robot controller how many points are in the trajectory. */ - ur_driver_->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_START, - trajectory_joint_positions_.size()); - /* Write the appropriate type of point depending on the combination of positions, velocities and accelerations. */ - if (!has_velocities(trajectory_joint_velocities_) && !has_accelerations(trajectory_joint_accelerations_)) { - for (size_t i = 0; i < trajectory_joint_positions_.size(); i++) { - ur_driver_->writeTrajectorySplinePoint(trajectory_joint_positions_[i], urcl::vector6d_t{ 0, 0, 0, 0, 0, 0 }, - trajectory_times_[i]); - } - } else if (has_velocities(trajectory_joint_velocities_) && !has_accelerations(trajectory_joint_accelerations_)) { - for (size_t i = 0; i < trajectory_joint_positions_.size(); i++) { - ur_driver_->writeTrajectorySplinePoint(trajectory_joint_positions_[i], trajectory_joint_velocities_[i], - trajectory_times_[i]); - } - } else if (!has_velocities(trajectory_joint_velocities_) && has_accelerations(trajectory_joint_accelerations_)) { - for (size_t i = 0; i < trajectory_joint_positions_.size(); i++) { - ur_driver_->writeTrajectorySplinePoint(trajectory_joint_positions_[i], trajectory_joint_accelerations_[i], - trajectory_times_[i]); - } - } else if (has_velocities(trajectory_joint_velocities_) && has_accelerations(trajectory_joint_accelerations_)) { - for (size_t i = 0; i < trajectory_joint_positions_.size(); i++) { - ur_driver_->writeTrajectorySplinePoint(trajectory_joint_positions_[i], trajectory_joint_velocities_[i], - trajectory_joint_accelerations_[i], trajectory_times_[i]); - } - } - trajectory_joint_positions_.clear(); - trajectory_joint_accelerations_.clear(); - trajectory_joint_velocities_.clear(); - trajectory_times_.clear(); - last_time = 0.0; - passthrough_trajectory_abort_ = 0.0; - passthrough_trajectory_transfer_state_ = 4.0; - } -} - -void URPositionHardwareInterface::trajectory_done_callback(urcl::control::TrajectoryResult result) -{ - if (result == urcl::control::TrajectoryResult::TRAJECTORY_RESULT_FAILURE) { - passthrough_trajectory_abort_ = 1.0; - } else { - passthrough_trajectory_abort_ = 0.0; - } - passthrough_trajectory_transfer_state_ = 5.0; - return; -} - -bool URPositionHardwareInterface::has_velocities(std::vector> velocities) -{ - return (velocities.size() > 0); -} - -bool URPositionHardwareInterface::has_accelerations(std::vector> accelerations) -{ - return (accelerations.size() > 0); -} - -} // namespace ur_robot_driver - -#include "pluginlib/class_list_macros.hpp" - -PLUGINLIB_EXPORT_CLASS(ur_robot_driver::URPositionHardwareInterface, hardware_interface::SystemInterface) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/ur_ros2_control_node.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/ur_ros2_control_node.cpp deleted file mode 100644 index 5cce2c0..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/ur_ros2_control_node.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2022 Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the Universal Robots A/S nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \date 2022-1-7 - * - */ -//---------------------------------------------------------------------- - -#include -#include - -// ROS includes -#include "controller_manager/controller_manager.hpp" -#include "rclcpp/rclcpp.hpp" -#include "realtime_tools/thread_priority.hpp" - -// code is inspired by -// https://github.com/ros-controls/ros2_control/blob/master/controller_manager/src/ros2_control_node.cpp - -int main(int argc, char** argv) -{ - rclcpp::init(argc, argv); - - // create executor - std::shared_ptr e = std::make_shared(); - // create controller manager instance - auto controller_manager = std::make_shared(e, "controller_manager"); - - // control loop thread - std::thread control_loop([controller_manager]() { - if (!realtime_tools::configure_sched_fifo(50)) { - RCLCPP_WARN(controller_manager->get_logger(), "Could not enable FIFO RT scheduling policy"); - } - - // for calculating sleep time - auto const period = std::chrono::nanoseconds(1'000'000'000 / controller_manager->get_update_rate()); - auto const cm_now = std::chrono::nanoseconds(controller_manager->now().nanoseconds()); - std::chrono::time_point next_iteration_time{ cm_now }; - - // for calculating the measured period of the loop - rclcpp::Time previous_time = controller_manager->now(); - - while (rclcpp::ok()) { - // calculate measured period - auto const current_time = controller_manager->now(); - auto const measured_period = current_time - previous_time; - previous_time = current_time; - - // execute update loop - controller_manager->read(controller_manager->now(), measured_period); - controller_manager->update(controller_manager->now(), measured_period); - controller_manager->write(controller_manager->now(), measured_period); - - // wait until we hit the end of the period - next_iteration_time += period; - std::this_thread::sleep_until(next_iteration_time); - } - }); - - // spin the executor with controller manager node - e->add_node(controller_manager); - e->spin(); - - // wait for control loop to finish - control_loop.join(); - - // shutdown - rclcpp::shutdown(); - - return 0; -} diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/urcl_log_handler.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/urcl_log_handler.cpp deleted file mode 100644 index e951fab..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/urcl_log_handler.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2021 Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -// All source code contained in and/or linked to in this message (the “Source Code”) is subject to the copyright of -// Universal Robots A/S and/or its licensors. THE SOURCE CODE IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING – BUT NOT LIMITED TO – WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR -// NONINFRINGEMENT. USE OF THE SOURCE CODE IS AT YOUR OWN RISK AND UNIVERSAL ROBOTS A/S AND ITS LICENSORS SHALL, TO THE -// MAXIMUM EXTENT PERMITTED BY LAW, NOT BE LIABLE FOR ANY ERRORS OR MALICIOUS CODE IN THE SOURCE CODE, ANY THIRD-PARTY -// CLAIMS, OR ANY OTHER CLAIMS AND DAMAGES, INCLUDING INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES, -// OR ANY LOSS OF PROFITS, EXPECTED SAVINGS, OR REVENUES, WHETHER INCURRED DIRECTLY OR INDIRECTLY, OR ANY LOSS OF DATA, -// USE, GOODWILL, OR OTHER INTANGIBLE LOSSES, RESULTING FROM YOUR USE OF THE SOURCE CODE. You may make copies of the -// Source Code for use in connection with a Universal Robots or UR+ product, provided that you include (i) an -// appropriate copyright notice (“© [the year in which you received the Source Code or the Source Code was first -// published, e.g. “2021”] Universal Robots A/S and/or its licensors”) along with the capitalized section of this notice -// in all copies of the Source Code. By using the Source Code, you agree to the above terms. For more information, -// please contact legal@universal-robots.com. - -#include -#include - -#include "ur_robot_driver/urcl_log_handler.hpp" -#include "rclcpp/logging.hpp" - -namespace ur_robot_driver -{ -bool g_registered = false; - -UrclLogHandler::UrclLogHandler() = default; - -void UrclLogHandler::log(const char* file, int line, urcl::LogLevel loglevel, const char* message) -{ - rcutils_log_location_t location = { "", file, static_cast(line) }; - - const auto logger_name = "UR_Client_Library:" + tf_prefix_; - switch (loglevel) { - case urcl::LogLevel::DEBUG: - rcutils_log(&location, RCUTILS_LOG_SEVERITY::RCUTILS_LOG_SEVERITY_DEBUG, logger_name.c_str(), "%s", message); - break; - case urcl::LogLevel::INFO: - rcutils_log(&location, RCUTILS_LOG_SEVERITY::RCUTILS_LOG_SEVERITY_INFO, logger_name.c_str(), "%s", message); - break; - case urcl::LogLevel::WARN: - rcutils_log(&location, RCUTILS_LOG_SEVERITY::RCUTILS_LOG_SEVERITY_WARN, logger_name.c_str(), "%s", message); - break; - case urcl::LogLevel::ERROR: - rcutils_log(&location, RCUTILS_LOG_SEVERITY::RCUTILS_LOG_SEVERITY_ERROR, logger_name.c_str(), "%s", message); - break; - case urcl::LogLevel::FATAL: - rcutils_log(&location, RCUTILS_LOG_SEVERITY::RCUTILS_LOG_SEVERITY_FATAL, logger_name.c_str(), "%s", message); - break; - default: - break; - } -} - -void registerUrclLogHandler(const std::string& tf_prefix) -{ - if (g_registered == false) { - std::unique_ptr log_handler(new UrclLogHandler); - log_handler->setTFPrefix(tf_prefix); - // Log level is decided by ROS2 log level - urcl::setLogLevel(urcl::LogLevel::DEBUG); - urcl::registerLogHandler(std::move(log_handler)); - g_registered = true; - } -} - -void unregisterUrclLogHandler() -{ - if (g_registered == true) { - urcl::unregisterLogHandler(); - g_registered = false; - } -} - -} // namespace ur_robot_driver diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/urscript_interface.cpp b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/urscript_interface.cpp deleted file mode 100644 index 757e76f..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/src/urscript_interface.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2023, FZI Forschungszentrum Informatik, Created on behalf of Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the {copyright_holder} nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Felix Exner exner@fzi.de - * \date 2023-06-20 - * - */ -//---------------------------------------------------------------------- - -#include -#include - -#include - -#include -#include - -class URScriptInterface : public rclcpp::Node -{ -public: - URScriptInterface() - : Node("urscript_interface") - , m_script_sub(this->create_subscription( - "~/script_command", 1, [this](const std_msgs::msg::String::SharedPtr msg) { - auto program_with_newline = msg->data + '\n'; - - RCLCPP_INFO_STREAM(this->get_logger(), program_with_newline); - - size_t len = program_with_newline.size(); - const auto* data = reinterpret_cast(program_with_newline.c_str()); - size_t written; - - if (m_secondary_stream->write(data, len, written)) { - URCL_LOG_INFO("Sent program to robot:\n%s", program_with_newline.c_str()); - return true; - } - URCL_LOG_ERROR("Could not send program to robot"); - return false; - })) - { - this->declare_parameter("robot_ip", rclcpp::PARAMETER_STRING); - m_secondary_stream = std::make_unique>( - this->get_parameter("robot_ip").as_string(), urcl::primary_interface::UR_SECONDARY_PORT); - m_secondary_stream->connect(); - - auto program_with_newline = std::string("textmsg(\"urscript_interface connected\")\n"); - size_t len = program_with_newline.size(); - const auto* data = reinterpret_cast(program_with_newline.c_str()); - size_t written; - m_secondary_stream->write(data, len, written); - } - -private: - rclcpp::Subscription::SharedPtr m_script_sub; - std::unique_ptr> m_secondary_stream; -}; - -int main(int argc, char** argv) -{ - rclcpp::init(argc, argv); - rclcpp::spin(std::make_unique()); - rclcpp::shutdown(); - return 0; -} diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/dashboard_client.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/dashboard_client.py deleted file mode 100755 index d55d278..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/dashboard_client.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python -# Copyright 2019, FZI Forschungszentrum Informatik -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -import os -import sys -import time -import unittest - -import pytest -import rclpy -from rclpy.node import Node -from ur_dashboard_msgs.msg import RobotMode - -sys.path.append(os.path.dirname(__file__)) -from test_common import DashboardInterface, generate_dashboard_test_description # noqa: E402 - - -@pytest.mark.launch_test -def generate_test_description(): - return generate_dashboard_test_description() - - -class DashboardClientTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - # Initialize the ROS context - rclpy.init() - cls.node = Node("dashboard_client_test") - cls.init_robot(cls) - - @classmethod - def tearDownClass(cls): - # Shutdown the ROS context - cls.node.destroy_node() - rclpy.shutdown() - - def init_robot(self): - self._dashboard_interface = DashboardInterface(self.node) - - def test_switch_on(self): - """Test power on a robot.""" - # Wait until the robot is booted completely - end_time = time.time() + 10 - mode = RobotMode.DISCONNECTED - while mode != RobotMode.POWER_OFF and time.time() < end_time: - time.sleep(0.1) - result = self._dashboard_interface.get_robot_mode() - self.assertTrue(result.success) - mode = result.robot_mode.mode - - # Power on robot - self.assertTrue(self._dashboard_interface.power_on().success) - - # Wait until robot mode changes - end_time = time.time() + 10 - mode = RobotMode.DISCONNECTED - while mode not in (RobotMode.IDLE, RobotMode.RUNNING) and time.time() < end_time: - time.sleep(0.1) - result = self._dashboard_interface.get_robot_mode() - self.assertTrue(result.success) - mode = result.robot_mode.mode - - self.assertIn(mode, (RobotMode.IDLE, RobotMode.RUNNING)) - - # Release robot brakes - self.assertTrue(self._dashboard_interface.brake_release().success) - - # Wait until robot mode is RUNNING - end_time = time.time() + 10 - mode = RobotMode.DISCONNECTED - while mode != RobotMode.RUNNING and time.time() < end_time: - time.sleep(0.1) - result = self._dashboard_interface.get_robot_mode() - self.assertTrue(result.success) - mode = result.robot_mode.mode - - self.assertEqual(mode, RobotMode.RUNNING) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/integration_test_controller_switch.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/integration_test_controller_switch.py deleted file mode 100644 index 1d2d657..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/integration_test_controller_switch.py +++ /dev/null @@ -1,407 +0,0 @@ -#!/usr/bin/env python -# Copyright 2019, Universal Robots A/S -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import os -import sys -import time -import unittest - -import pytest - -import launch_testing -import rclpy -from rclpy.node import Node - -from controller_manager_msgs.srv import SwitchController - -sys.path.append(os.path.dirname(__file__)) -from test_common import ( # noqa: E402 - ControllerManagerInterface, - DashboardInterface, - IoStatusInterface, - generate_driver_test_description, -) - - -@pytest.mark.launch_test -@launch_testing.parametrize( - "tf_prefix", - [""], - # [(""), ("my_ur_")], -) -def generate_test_description(tf_prefix): - return generate_driver_test_description(tf_prefix=tf_prefix) - - -class RobotDriverTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - # Initialize the ROS context - rclpy.init() - cls.node = Node("robot_driver_test") - time.sleep(1) - cls.init_robot(cls) - - @classmethod - def tearDownClass(cls): - # Shutdown the ROS context - cls.node.destroy_node() - rclpy.shutdown() - - def init_robot(self): - self._dashboard_interface = DashboardInterface(self.node) - self._controller_manager_interface = ControllerManagerInterface(self.node) - self._io_status_controller_interface = IoStatusInterface(self.node) - - def setUp(self): - self._dashboard_interface.start_robot() - time.sleep(1) - self.assertTrue(self._io_status_controller_interface.resend_robot_program().success) - - def test_activating_multiple_controllers_same_interface_fails(self): - # Deactivate all writing controllers - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_position_controller", - "forward_velocity_controller", - "passthrough_trajectory_controller", - "force_mode_controller", - "freedrive_mode_controller", - ], - ).ok - ) - - # Activating different motion controllers should not be possible - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "scaled_joint_trajectory_controller", - "forward_position_controller", - ], - ).ok - ) - - def test_activating_multiple_controllers_different_interface_fails(self): - # Deactivate all writing controllers - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_position_controller", - "forward_velocity_controller", - "force_mode_controller", - "passthrough_trajectory_controller", - "freedrive_mode_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "scaled_joint_trajectory_controller", - "forward_velocity_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "scaled_joint_trajectory_controller", - "passthrough_trajectory_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "forward_velocity_controller", - "passthrough_trajectory_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "forward_position_controller", - "forward_velocity_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "scaled_joint_trajectory_controller", - "force_mode_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "scaled_joint_trajectory_controller", - "freedrive_mode_controller", - ], - ).ok - ) - - def test_activating_controller_with_running_position_controller_fails(self): - # Having a position-based controller active, no other controller should be able to - # activate. - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=[ - "scaled_joint_trajectory_controller", - ], - deactivate_controllers=[ - "joint_trajectory_controller", - "forward_position_controller", - "forward_velocity_controller", - "force_mode_controller", - "freedrive_mode_controller", - "passthrough_trajectory_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "forward_position_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "forward_velocity_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "freedrive_mode_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "passthrough_trajectory_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "force_mode_controller", - ], - ).ok - ) - # Stop controller again - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - ], - ).ok - ) - - def test_activating_controller_with_running_passthrough_trajectory_controller_fails(self): - # Having a position-based controller active, no other controller should be able to - # activate. - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=["passthrough_trajectory_controller"], - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_position_controller", - "forward_velocity_controller", - "force_mode_controller", # tested in separate test - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "scaled_joint_trajectory_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "forward_position_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "forward_velocity_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "joint_trajectory_controller", - ], - ).ok - ) - self.assertFalse( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "freedrive_mode_controller", - ], - ).ok - ) - # Stop the controller again - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - deactivate_controllers=[ - "passthrough_trajectory_controller", - ], - ).ok - ) - - def test_force_mode_and_trajectory_passthrough_controller_are_compatible(self): - # Deactivate all writing controllers - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_position_controller", - "forward_velocity_controller", - "passthrough_trajectory_controller", - "force_mode_controller", - ], - ).ok - ) - - time.sleep(3) - - # Start both together - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "passthrough_trajectory_controller", - "force_mode_controller", - ], - ).ok - ) - - # With passthrough traj controller running, start force mode controller - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=[ - "passthrough_trajectory_controller", - ], - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_position_controller", - "forward_velocity_controller", - "force_mode_controller", - ], - ).ok - ) - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "force_mode_controller", - ], - ).ok - ) - - # With start force mode controller running, passthrough traj controller - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=[ - "force_mode_controller", - ], - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - "forward_position_controller", - "forward_velocity_controller", - "passthrough_trajectory_controller", - ], - ).ok - ) - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - activate_controllers=[ - "passthrough_trajectory_controller", - ], - ).ok - ) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/integration_test_force_mode.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/integration_test_force_mode.py deleted file mode 100644 index a1dc89f..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/integration_test_force_mode.py +++ /dev/null @@ -1,514 +0,0 @@ -#!/usr/bin/env python -# Copyright 2019, Universal Robots A/S -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import os -import sys -import time -import unittest - -import pytest - -import launch_testing -import rclpy -from rclpy.node import Node - -from tf2_ros import TransformException -from tf2_ros.buffer import Buffer -from tf2_ros.transform_listener import TransformListener - -import std_msgs -from controller_manager_msgs.srv import SwitchController -from geometry_msgs.msg import ( - Pose, - PoseStamped, - Quaternion, - Point, - Twist, - Wrench, - Vector3, -) - -sys.path.append(os.path.dirname(__file__)) -from test_common import ( # noqa: E402 - ControllerManagerInterface, - DashboardInterface, - ForceModeInterface, - IoStatusInterface, - ConfigurationInterface, - generate_driver_test_description, -) - -TIMEOUT_EXECUTE_TRAJECTORY = 30 - - -def are_quaternions_same(q1, q2, tolerance): - dot_product = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w - return (abs(dot_product) - 1.0) < tolerance - - -@pytest.mark.launch_test -@launch_testing.parametrize( - "tf_prefix", - [(""), ("my_ur_")], -) -def generate_test_description(tf_prefix): - return generate_driver_test_description(tf_prefix=tf_prefix) - - -class RobotDriverTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - # Initialize the ROS context - rclpy.init() - cls.node = Node("robot_driver_test") - time.sleep(1) - cls.init_robot(cls) - - @classmethod - def tearDownClass(cls): - # Shutdown the ROS context - cls.node.destroy_node() - rclpy.shutdown() - - def init_robot(self): - self._dashboard_interface = DashboardInterface(self.node) - self._controller_manager_interface = ControllerManagerInterface(self.node) - self._io_status_controller_interface = IoStatusInterface(self.node) - self._configuration_controller_interface = ConfigurationInterface(self.node) - - def setUp(self): - self._dashboard_interface.start_robot() - time.sleep(1) - self.assertTrue(self._io_status_controller_interface.resend_robot_program().success) - self.tf_buffer = Buffer() - self.tf_listener = TransformListener(self.tf_buffer, self.node) - - def lookup_tcp_in_base(self, tf_prefix, timepoint): - trans = None - while not trans: - rclpy.spin_once(self.node) - try: - trans = self.tf_buffer.lookup_transform( - tf_prefix + "base", tf_prefix + "tool0", timepoint - ) - except TransformException: - pass - return trans - - def test_force_mode_controller(self, tf_prefix): - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=[ - "force_mode_controller", - ], - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - ], - ).ok - ) - self._force_mode_controller_interface = ForceModeInterface(self.node) - - # Create task frame for force mode - point = Point(x=0.8, y=0.8, z=0.8) - orientation = Quaternion(x=0.7071, y=0.0, z=0.0, w=0.7071) - task_frame_pose = Pose() - task_frame_pose.position = point - task_frame_pose.orientation = orientation - header = std_msgs.msg.Header(frame_id=tf_prefix + "base") - header.stamp.sec = int(time.time()) + 1 - header.stamp.nanosec = 0 - frame_stamp = PoseStamped() - frame_stamp.header = header - frame_stamp.pose = task_frame_pose - - # Create compliance vector (which axes should be force controlled) - compliance = [False, False, True, False, False, False] - - # Create Wrench message for force mode - wrench = Wrench() - wrench.force = Vector3(x=0.0, y=0.0, z=5.0) - wrench.torque = Vector3(x=0.0, y=0.0, z=0.0) - - # Specify interpretation of task frame (no transform) - type_spec = 2 - - # Specify max speeds and deviations of force mode - speed_limits = Twist() - speed_limits.linear = Vector3(x=0.0, y=0.0, z=1.0) - speed_limits.angular = Vector3(x=0.0, y=0.0, z=1.0) - deviation_limits = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0] - - # specify damping and gain scaling - damping_factor = 0.1 - gain_scale = 0.8 - - trans_before = self.lookup_tcp_in_base(tf_prefix, rclpy.time.Time()) - - # Send request to controller - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, - selection_vector_x=compliance[0], - selection_vector_y=compliance[1], - selection_vector_z=compliance[2], - selection_vector_rx=compliance[3], - selection_vector_ry=compliance[4], - selection_vector_rz=compliance[5], - wrench=wrench, - type=type_spec, - speed_limits=speed_limits, - deviation_limits=deviation_limits, - damping_factor=damping_factor, - gain_scaling=gain_scale, - ) - self.assertTrue(res.success) - - time.sleep(5.0) - - trans_after = self.lookup_tcp_in_base(tf_prefix, self.node.get_clock().now()) - - # task frame and wrench determines the expected motion - # In the example we used - # - a task frame rotated pi/2 deg around the base frame's x axis - # - a wrench with a positive z component for the force - # => we should expect a motion in negative y of the base frame - self.assertTrue(trans_after.transform.translation.y < trans_before.transform.translation.y) - self.assertAlmostEqual( - trans_after.transform.translation.x, - trans_before.transform.translation.x, - delta=0.001, - ) - self.assertAlmostEqual( - trans_after.transform.translation.z, - trans_before.transform.translation.z, - delta=0.001, - ) - self.assertTrue( - are_quaternions_same( - trans_after.transform.rotation, trans_before.transform.rotation, 0.001 - ) - ) - - res = self._force_mode_controller_interface.stop_force_mode() - self.assertTrue(res.success) - - # Deactivate controller - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.STRICT, - deactivate_controllers=["force_mode_controller"], - ).ok - ) - - def test_illegal_force_mode_types(self, tf_prefix): - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=[ - "force_mode_controller", - ], - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - ], - ).ok - ) - self._force_mode_controller_interface = ForceModeInterface(self.node) - - # Create task frame for force mode - point = Point(x=0.0, y=0.0, z=0.0) - orientation = Quaternion(x=0.0, y=0.0, z=0.0, w=1.0) - task_frame_pose = Pose() - task_frame_pose.position = point - task_frame_pose.orientation = orientation - header = std_msgs.msg.Header(frame_id=tf_prefix + "base") - header.stamp.sec = int(time.time()) + 1 - header.stamp.nanosec = 0 - frame_stamp = PoseStamped() - frame_stamp.header = header - frame_stamp.pose = task_frame_pose - - res = self._force_mode_controller_interface.start_force_mode(task_frame=frame_stamp, type=0) - self.assertFalse(res.success) - res = self._force_mode_controller_interface.start_force_mode(task_frame=frame_stamp, type=4) - self.assertFalse(res.success) - res = self._force_mode_controller_interface.start_force_mode(task_frame=frame_stamp, type=1) - self.assertTrue(res.success) - res = self._force_mode_controller_interface.stop_force_mode() - res = self._force_mode_controller_interface.start_force_mode(task_frame=frame_stamp, type=2) - self.assertTrue(res.success) - res = self._force_mode_controller_interface.stop_force_mode() - res = self._force_mode_controller_interface.start_force_mode(task_frame=frame_stamp, type=3) - self.assertTrue(res.success) - res = self._force_mode_controller_interface.stop_force_mode() - - def test_illegal_task_frame(self, tf_prefix): - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=[ - "force_mode_controller", - ], - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - ], - ).ok - ) - self._force_mode_controller_interface = ForceModeInterface(self.node) - - # Create task frame for force mode - point = Point(x=0.0, y=0.0, z=0.0) - orientation = Quaternion(x=0.0, y=0.0, z=0.0, w=1.0) - task_frame_pose = Pose() - task_frame_pose.position = point - task_frame_pose.orientation = orientation - header = std_msgs.msg.Header(frame_id=tf_prefix + "base") - header.stamp.sec = int(time.time()) + 1 - header.stamp.nanosec = 0 - frame_stamp = PoseStamped() - frame_stamp.header = header - frame_stamp.pose = task_frame_pose - - # Illegal frame name produces error - header.frame_id = "nonexisting6t54" - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, - ) - self.assertFalse(res.success) - header.frame_id = "base" - - # Illegal quaternion produces error - task_frame_pose.orientation = Quaternion(x=0.0, y=0.0, z=0.0, w=0.0) - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, - ) - self.assertFalse(res.success) - - def test_start_force_mode_on_inactive_controller_fails(self, tf_prefix): - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=[], - deactivate_controllers=[ - "force_mode_controller", - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - ], - ).ok - ) - self._force_mode_controller_interface = ForceModeInterface(self.node) - - # Create task frame for force mode - point = Point(x=0.0, y=0.0, z=0.0) - orientation = Quaternion(x=0.0, y=0.0, z=0.0, w=1.0) - task_frame_pose = Pose() - task_frame_pose.position = point - task_frame_pose.orientation = orientation - header = std_msgs.msg.Header(frame_id=tf_prefix + "base") - header.stamp.sec = int(time.time()) + 1 - header.stamp.nanosec = 0 - frame_stamp = PoseStamped() - frame_stamp.header = header - frame_stamp.pose = task_frame_pose - - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, - ) - self.assertFalse(res.success) - - def test_deactivating_controller_stops_force_mode(self, tf_prefix): - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=[ - "force_mode_controller", - ], - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - ], - ).ok - ) - self._force_mode_controller_interface = ForceModeInterface(self.node) - - # Create task frame for force mode - point = Point(x=0.0, y=0.0, z=0.0) - orientation = Quaternion(x=0.0, y=0.0, z=0.0, w=1.0) - task_frame_pose = Pose() - task_frame_pose.position = point - task_frame_pose.orientation = orientation - header = std_msgs.msg.Header(frame_id=tf_prefix + "base") - header.stamp.sec = int(time.time()) + 1 - header.stamp.nanosec = 0 - frame_stamp = PoseStamped() - frame_stamp.header = header - frame_stamp.pose = task_frame_pose - - # Create compliance vector (which axes should be force controlled) - compliance = [False, False, True, False, False, False] - - # Create Wrench message for force mode - wrench = Wrench() - wrench.force = Vector3(x=0.0, y=0.0, z=5.0) - wrench.torque = Vector3(x=0.0, y=0.0, z=0.0) - - # Specify interpretation of task frame (no transform) - type_spec = 2 - - # Specify max speeds and deviations of force mode - speed_limits = Twist() - speed_limits.linear = Vector3(x=0.0, y=0.0, z=1.0) - speed_limits.angular = Vector3(x=0.0, y=0.0, z=1.0) - deviation_limits = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0] - - # specify damping and gain scaling - damping_factor = 0.1 - gain_scale = 0.8 - - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, - selection_vector_x=compliance[0], - selection_vector_y=compliance[1], - selection_vector_z=compliance[2], - selection_vector_rx=compliance[3], - selection_vector_ry=compliance[4], - selection_vector_rz=compliance[5], - wrench=wrench, - type=type_spec, - speed_limits=speed_limits, - deviation_limits=deviation_limits, - damping_factor=damping_factor, - gain_scaling=gain_scale, - ) - self.assertTrue(res.success) - - time.sleep(0.5) - - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=[], - deactivate_controllers=[ - "force_mode_controller", - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - ], - ).ok - ) - self._force_mode_controller_interface = ForceModeInterface(self.node) - - time.sleep(0.5) - trans_before_wait = self.lookup_tcp_in_base(tf_prefix, self.node.get_clock().now()) - - # Make sure the robot didn't move anymore - time.sleep(0.5) - trans_after_wait = self.lookup_tcp_in_base(tf_prefix, self.node.get_clock().now()) - - self.assertAlmostEqual( - trans_before_wait.transform.translation.z, trans_after_wait.transform.translation.z - ) - - def test_params_out_of_range_fails(self, tf_prefix): - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=[ - "force_mode_controller", - ], - deactivate_controllers=[ - "scaled_joint_trajectory_controller", - "joint_trajectory_controller", - ], - ).ok - ) - self._force_mode_controller_interface = ForceModeInterface(self.node) - - # Create task frame for force mode - point = Point(x=0.0, y=0.0, z=0.0) - orientation = Quaternion(x=0.0, y=0.0, z=0.0, w=1.0) - task_frame_pose = Pose() - task_frame_pose.position = point - task_frame_pose.orientation = orientation - header = std_msgs.msg.Header(frame_id=tf_prefix + "base") - header.stamp.sec = int(time.time()) + 1 - header.stamp.nanosec = 0 - frame_stamp = PoseStamped() - frame_stamp.header = header - frame_stamp.pose = task_frame_pose - - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, gain_scaling=-0.1 - ) - self.assertFalse(res.success) - - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, gain_scaling=0.0 - ) - self.assertTrue(res.success) - res = self._force_mode_controller_interface.stop_force_mode() - self.assertTrue(res.success) - - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, gain_scaling=2.0 - ) - self.assertTrue(res.success) - res = self._force_mode_controller_interface.stop_force_mode() - self.assertTrue(res.success) - - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, gain_scaling=2.1 - ) - self.assertFalse(res.success) - - # damping factor - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, damping_factor=-0.1 - ) - self.assertFalse(res.success) - - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, damping_factor=0.0 - ) - self.assertTrue(res.success) - res = self._force_mode_controller_interface.stop_force_mode() - self.assertTrue(res.success) - - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, damping_factor=1.0 - ) - self.assertTrue(res.success) - res = self._force_mode_controller_interface.stop_force_mode() - self.assertTrue(res.success) - - res = self._force_mode_controller_interface.start_force_mode( - task_frame=frame_stamp, damping_factor=1.1 - ) - self.assertFalse(res.success) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/launch_args.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/launch_args.py deleted file mode 100644 index eba542f..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/launch_args.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env python -# Copyright 2024, FZI Forschungszentrum Informatik -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import logging -import os -import pytest -import sys -import time -import unittest - -import rclpy -from rclpy.node import Node - - -from launch import LaunchDescription -from launch.actions import ( - ExecuteProcess, - IncludeLaunchDescription, - RegisterEventHandler, -) -from launch.event_handlers import OnProcessExit -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, PathJoinSubstitution -from launch_ros.substitutions import FindPackagePrefix, FindPackageShare - -import launch_testing -from launch_testing.actions import ReadyToTest - -sys.path.append(os.path.dirname(__file__)) -from test_common import ( # noqa: E402 - ControllerManagerInterface, - _declare_launch_arguments, - _ursim_action, -) - - -@pytest.mark.launch_test -@launch_testing.parametrize( - "launch_dashboard_client", - [("true"), ("false")], -) -def generate_test_description(launch_dashboard_client): - ur_type = LaunchConfiguration("ur_type") - launch_arguments = { - "robot_ip": "192.168.56.101", - "ur_type": ur_type, - "launch_rviz": "false", - "controller_spawner_timeout": str(120), - "initial_joint_controller": "scaled_joint_trajectory_controller", - "headless_mode": "true", - "launch_dashboard_client": launch_dashboard_client, - "start_joint_controller": "false", - } - - robot_driver = IncludeLaunchDescription( - PythonLaunchDescriptionSource( - PathJoinSubstitution( - [FindPackageShare("ur_robot_driver"), "launch", "ur_control.launch.py"] - ) - ), - launch_arguments=launch_arguments.items(), - ) - wait_dashboard_server = ExecuteProcess( - cmd=[ - PathJoinSubstitution( - [ - FindPackagePrefix("ur_robot_driver"), - "bin", - "wait_dashboard_server.sh", - ] - ) - ], - name="wait_dashboard_server", - output="screen", - ) - driver_starter = RegisterEventHandler( - OnProcessExit(target_action=wait_dashboard_server, on_exit=robot_driver) - ) - - return LaunchDescription( - _declare_launch_arguments() - + [ReadyToTest(), wait_dashboard_server, _ursim_action(), driver_starter] - ) - - -class DashboardClientTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - # Initialize the ROS context - rclpy.init() - cls.node = Node("robot_driver_launch_test") - time.sleep(1) - cls.init_robot(cls) - - @classmethod - def tearDownClass(cls): - # Shutdown the ROS context - cls.node.destroy_node() - rclpy.shutdown() - - def init_robot(self): - # This waits for the controller_manager to be available - self._controller_manager_interface = ControllerManagerInterface(self.node) - - def setUp(self): - pass - - def test_dashboard_client_exists(self, launch_dashboard_client): - # Use the get_node_names_and_namespaces() method to get the list of nodes and their namespaces - nodes_with_ns = self.node.get_node_names_and_namespaces() - - nodes = [namespace + name for (name, namespace) in nodes_with_ns] - - for node in nodes: - print(node) - - # Print out the nodes and their namespaces - logging.info("Discovered ROS nodes:") - if launch_dashboard_client == "true": - self.assertIn("/dashboard_client", nodes) - else: - self.assertNotIn("/dashboard_client", nodes) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/robot_driver.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/robot_driver.py deleted file mode 100644 index b0b2a4c..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/robot_driver.py +++ /dev/null @@ -1,439 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2019, FZI Forschungszentrum Informatik -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -import logging -import os -import sys -import time -import unittest - -import launch_testing -import pytest -import rclpy -from builtin_interfaces.msg import Duration -from control_msgs.action import FollowJointTrajectory -from control_msgs.msg import JointTolerance -from controller_manager_msgs.srv import SwitchController -from rclpy.node import Node -from sensor_msgs.msg import JointState -from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint -from ur_msgs.msg import IOStates - -sys.path.append(os.path.dirname(__file__)) -from test_common import ( # noqa: E402 - ActionInterface, - ControllerManagerInterface, - DashboardInterface, - IoStatusInterface, - ConfigurationInterface, - generate_driver_test_description, - ROBOT_JOINTS, -) - -TIMEOUT_EXECUTE_TRAJECTORY = 30 - - -@pytest.mark.launch_test -@launch_testing.parametrize( - "tf_prefix", - [(""), ("my_ur_")], -) -def generate_test_description(tf_prefix): - return generate_driver_test_description(tf_prefix=tf_prefix) - - -class RobotDriverTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - # Initialize the ROS context - rclpy.init() - cls.node = Node("robot_driver_test") - time.sleep(1) - cls.init_robot(cls) - - @classmethod - def tearDownClass(cls): - # Shutdown the ROS context - cls.node.destroy_node() - rclpy.shutdown() - - def init_robot(self): - self._dashboard_interface = DashboardInterface(self.node) - self._controller_manager_interface = ControllerManagerInterface(self.node) - self._io_status_controller_interface = IoStatusInterface(self.node) - self._configuration_controller_interface = ConfigurationInterface(self.node) - - self._scaled_follow_joint_trajectory = ActionInterface( - self.node, - "/scaled_joint_trajectory_controller/follow_joint_trajectory", - FollowJointTrajectory, - ) - self._passthrough_forward_joint_trajectory = ActionInterface( - self.node, - "/passthrough_trajectory_controller/follow_joint_trajectory", - FollowJointTrajectory, - ) - - def setUp(self): - self._dashboard_interface.start_robot() - time.sleep(1) - self.assertTrue(self._io_status_controller_interface.resend_robot_program().success) - - # - # Test functions - # - - def test_get_robot_software_version(self): - self.assertNotEqual( - self._configuration_controller_interface.get_robot_software_version().major, 0 - ) - - def test_start_scaled_jtc_controller(self): - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=["scaled_joint_trajectory_controller"], - ).ok - ) - - def test_start_passthrough_controller(self): - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=["passthrough_trajectory_controller"], - deactivate_controllers=["scaled_joint_trajectory_controller"], - ).ok - ) - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - deactivate_controllers=["passthrough_trajectory_controller"], - activate_controllers=["scaled_joint_trajectory_controller"], - ).ok - ) - - def test_set_io(self): - """Test to set an IO and check whether it has been set.""" - # Create io callback to verify result - io_msg = None - - def io_msg_cb(msg): - nonlocal io_msg - io_msg = msg - - io_states_sub = self.node.create_subscription( - IOStates, - "/io_and_status_controller/io_states", - io_msg_cb, - rclpy.qos.qos_profile_system_default, - ) - - # Set pin 0 to 1.0 - test_pin = 0 - - logging.info("Setting pin %d to 1.0", test_pin) - self._io_status_controller_interface.set_io(fun=1, pin=test_pin, state=1.0) - - # Wait until the pin state has changed - pin_state = False - end_time = time.time() + 5 - while not pin_state and time.time() < end_time: - rclpy.spin_once(self.node, timeout_sec=0.1) - if io_msg is not None: - pin_state = io_msg.digital_out_states[test_pin].state - - self.assertEqual(pin_state, 1.0) - - # Set pin 0 to 0.0 - logging.info("Setting pin %d to 0.0", test_pin) - self._io_status_controller_interface.set_io(fun=1, pin=test_pin, state=0.0) - - # Wait until the pin state has changed back - end_time = time.time() + 5 - while pin_state and time.time() < end_time: - rclpy.spin_once(self.node, timeout_sec=0.1) - if io_msg is not None: - pin_state = io_msg.digital_out_states[test_pin].state - - self.assertEqual(pin_state, 0.0) - - # Clean up io subscription - self.node.destroy_subscription(io_states_sub) - - def test_trajectory(self, tf_prefix): - """Test robot movement.""" - # Construct test trajectory - test_trajectory = [ - (Duration(sec=6, nanosec=0), [0.0 for j in ROBOT_JOINTS]), - (Duration(sec=9, nanosec=0), [-0.5 for j in ROBOT_JOINTS]), - (Duration(sec=12, nanosec=0), [-1.0 for j in ROBOT_JOINTS]), - ] - - trajectory = JointTrajectory( - joint_names=[tf_prefix + joint for joint in ROBOT_JOINTS], - points=[ - JointTrajectoryPoint(positions=test_pos, time_from_start=test_time) - for (test_time, test_pos) in test_trajectory - ], - ) - - # Sending trajectory goal - logging.info("Sending simple goal") - goal_handle = self._scaled_follow_joint_trajectory.send_goal(trajectory=trajectory) - self.assertTrue(goal_handle.accepted) - - # Verify execution - result = self._scaled_follow_joint_trajectory.get_result( - goal_handle, TIMEOUT_EXECUTE_TRAJECTORY - ) - self.assertEqual(result.error_code, FollowJointTrajectory.Result.SUCCESSFUL) - - def test_illegal_trajectory(self, tf_prefix): - """ - Test trajectory server. - - This is more of a validation test that the testing suite does the right thing - """ - # Construct test trajectory, the second point wrongly starts before the first - test_trajectory = [ - (Duration(sec=6, nanosec=0), [0.0 for j in ROBOT_JOINTS]), - (Duration(sec=3, nanosec=0), [-0.5 for j in ROBOT_JOINTS]), - ] - - trajectory = JointTrajectory( - joint_names=[tf_prefix + joint for joint in ROBOT_JOINTS], - points=[ - JointTrajectoryPoint(positions=test_pos, time_from_start=test_time) - for (test_time, test_pos) in test_trajectory - ], - ) - - # Send illegal goal - logging.info("Sending illegal goal") - goal_handle = self._scaled_follow_joint_trajectory.send_goal( - trajectory=trajectory, - ) - - # Verify the failure is correctly detected - self.assertFalse(goal_handle.accepted) - - def test_trajectory_scaled(self, tf_prefix): - """Test robot movement.""" - # Construct test trajectory - test_trajectory = [ - (Duration(sec=6, nanosec=0), [0.0 for j in ROBOT_JOINTS]), - (Duration(sec=6, nanosec=500000000), [-1.0 for j in ROBOT_JOINTS]), - ] - - trajectory = JointTrajectory( - joint_names=[tf_prefix + joint for joint in ROBOT_JOINTS], - points=[ - JointTrajectoryPoint(positions=test_pos, time_from_start=test_time) - for (test_time, test_pos) in test_trajectory - ], - ) - - # Execute trajectory - logging.info("Sending goal for robot to follow") - goal_handle = self._scaled_follow_joint_trajectory.send_goal(trajectory=trajectory) - self.assertTrue(goal_handle.accepted) - - # Verify execution - result = self._scaled_follow_joint_trajectory.get_result( - goal_handle, - TIMEOUT_EXECUTE_TRAJECTORY, - ) - self.assertEqual(result.error_code, FollowJointTrajectory.Result.SUCCESSFUL) - - def test_trajectory_scaled_aborts_on_violation(self, tf_prefix): - """Test that the robot correctly aborts the trajectory when the constraints are violated.""" - # Construct test trajectory - test_trajectory = [ - (Duration(sec=6, nanosec=0), [0.0 for j in ROBOT_JOINTS]), - ( - Duration(sec=6, nanosec=50000000), - [-1.0 for j in ROBOT_JOINTS], - ), # physically unfeasible - ( - Duration(sec=8, nanosec=0), - [-1.5 for j in ROBOT_JOINTS], - ), # physically unfeasible - ] - - trajectory = JointTrajectory( - joint_names=[tf_prefix + joint for joint in ROBOT_JOINTS], - points=[ - JointTrajectoryPoint(positions=test_pos, time_from_start=test_time) - for (test_time, test_pos) in test_trajectory - ], - ) - - last_joint_state = None - - def js_cb(msg): - nonlocal last_joint_state - last_joint_state = msg - - joint_state_sub = self.node.create_subscription(JointState, "/joint_states", js_cb, 1) - joint_state_sub # prevent warning about unused variable - - # Send goal - logging.info("Sending goal for robot to follow") - goal_handle = self._scaled_follow_joint_trajectory.send_goal(trajectory=trajectory) - self.assertTrue(goal_handle.accepted) - - # Get result - result = self._scaled_follow_joint_trajectory.get_result( - goal_handle, - TIMEOUT_EXECUTE_TRAJECTORY, - ) - self.assertEqual(result.error_code, FollowJointTrajectory.Result.PATH_TOLERANCE_VIOLATED) - - state_when_aborted = last_joint_state - - # This section is to make sure the robot stopped moving once the trajectory was aborted - time.sleep(2.0) - # Ugly workaround since we want to wait for a joint state in the same thread - while last_joint_state == state_when_aborted: - rclpy.spin_once(self.node) - state_after_sleep = last_joint_state - - logging.info("Joint states before sleep:\t %s", state_when_aborted.position.tolist()) - logging.info("Joint states after sleep:\t %s", state_after_sleep.position.tolist()) - - self.assertTrue( - all( - [ - abs(a - b) < 0.01 - for a, b in zip(state_after_sleep.position, state_when_aborted.position) - ] - ) - ) - - # TODO: uncomment when JTC starts taking into account goal_time_tolerance from goal message - # see https://github.com/ros-controls/ros2_controllers/issues/249 - # Now do the same again, but with a goal time constraint - # self.node.get_logger().info("Sending scaled goal with time restrictions") - # - # goal.goal_time_tolerance = Duration(nanosec=10000000) - # goal_response = self.call_action("/scaled_joint_trajectory_controller/follow_joint_trajectory", goal) - # - # self.assertEqual(goal_response.accepted, True) - # - # if goal_response.accepted: - # result = self.get_result("/scaled_joint_trajectory_controller/follow_joint_trajectory", goal_response, TIMEOUT_EXECUTE_TRAJECTORY) - # self.assertEqual(result.error_code, FollowJointTrajectory.Result.GOAL_TOLERANCE_VIOLATED) - # self.node.get_logger().info("Received result GOAL_TOLERANCE_VIOLATED") - - def test_passthrough_trajectory(self, tf_prefix): - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - activate_controllers=["passthrough_trajectory_controller"], - deactivate_controllers=["scaled_joint_trajectory_controller"], - ).ok - ) - waypts = [ - [-1.58, -1.692, -1.4311, -0.0174, 1.5882, 0.0349], - [-3, -1.692, -1.4311, -0.0174, 1.5882, 0.0349], - [-1.58, -1.692, -1.4311, -0.0174, 1.5882, 0.0349], - ] - time_vec = [ - Duration(sec=4, nanosec=0), - Duration(sec=8, nanosec=0), - Duration(sec=12, nanosec=0), - ] - goal_tolerance = [ - JointTolerance(position=0.01, name=tf_prefix + ROBOT_JOINTS[i]) - for i in range(len(ROBOT_JOINTS)) - ] - goal_time_tolerance = Duration(sec=1, nanosec=0) - test_trajectory = zip(time_vec, waypts) - trajectory = JointTrajectory( - points=[ - JointTrajectoryPoint(positions=pos, time_from_start=times) - for (times, pos) in test_trajectory - ], - joint_names=[tf_prefix + ROBOT_JOINTS[i] for i in range(len(ROBOT_JOINTS))], - ) - goal_handle = self._passthrough_forward_joint_trajectory.send_goal( - trajectory=trajectory, - goal_time_tolerance=goal_time_tolerance, - goal_tolerance=goal_tolerance, - ) - self.assertTrue(goal_handle.accepted) - if goal_handle.accepted: - result = self._passthrough_forward_joint_trajectory.get_result( - goal_handle, TIMEOUT_EXECUTE_TRAJECTORY - ) - self.assertEqual(result.error_code, FollowJointTrajectory.Result.SUCCESSFUL) - # Test impossible goal tolerance, should fail. - goal_tolerance = [ - JointTolerance(position=0.000000001, name=tf_prefix + ROBOT_JOINTS[i]) - for i in range(len(ROBOT_JOINTS)) - ] - goal_handle = self._passthrough_forward_joint_trajectory.send_goal( - trajectory=trajectory, - goal_time_tolerance=goal_time_tolerance, - goal_tolerance=goal_tolerance, - ) - self.assertTrue(goal_handle.accepted) - if goal_handle.accepted: - result = self._passthrough_forward_joint_trajectory.get_result( - goal_handle, TIMEOUT_EXECUTE_TRAJECTORY - ) - self.assertEqual( - result.error_code, FollowJointTrajectory.Result.GOAL_TOLERANCE_VIOLATED - ) - - # Test impossible goal time - goal_tolerance = [ - JointTolerance(position=0.01, name=tf_prefix + ROBOT_JOINTS[i]) for i in range(6) - ] - goal_time_tolerance.sec = 0 - goal_time_tolerance.nanosec = 10 - goal_handle = self._passthrough_forward_joint_trajectory.send_goal( - trajectory=trajectory, - goal_time_tolerance=goal_time_tolerance, - goal_tolerance=goal_tolerance, - ) - self.assertTrue(goal_handle.accepted) - if goal_handle.accepted: - result = self._passthrough_forward_joint_trajectory.get_result( - goal_handle, TIMEOUT_EXECUTE_TRAJECTORY - ) - self.assertEqual( - result.error_code, FollowJointTrajectory.Result.GOAL_TOLERANCE_VIOLATED - ) - self.assertTrue( - self._controller_manager_interface.switch_controller( - strictness=SwitchController.Request.BEST_EFFORT, - deactivate_controllers=["passthrough_trajectory_controller"], - activate_controllers=["scaled_joint_trajectory_controller"], - ).ok - ) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/test_common.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/test_common.py deleted file mode 100644 index 9275426..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/test_common.py +++ /dev/null @@ -1,385 +0,0 @@ -# Copyright 2023, FZI Forschungszentrum Informatik -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -import logging -import time - -import rclpy -from controller_manager_msgs.srv import ( - ListControllers, - SwitchController, - LoadController, - UnloadController, -) -from launch import LaunchDescription -from launch.actions import ( - DeclareLaunchArgument, - ExecuteProcess, - IncludeLaunchDescription, - RegisterEventHandler, -) -from launch.event_handlers import OnProcessExit -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, PathJoinSubstitution -from launch_ros.substitutions import FindPackagePrefix, FindPackageShare -from launch_testing.actions import ReadyToTest -from rclpy.action import ActionClient -from std_srvs.srv import Trigger -from ur_dashboard_msgs.msg import RobotMode -from ur_dashboard_msgs.srv import ( - GetLoadedProgram, - GetProgramState, - GetRobotMode, - IsProgramRunning, - Load, -) -from ur_msgs.srv import SetIO, GetRobotSoftwareVersion, SetForceMode - -TIMEOUT_WAIT_SERVICE = 10 -TIMEOUT_WAIT_SERVICE_INITIAL = 120 # If we download the docker image simultaneously to the tests, it can take quite some time until the dashboard server is reachable and usable. -TIMEOUT_WAIT_ACTION = 10 - -ROBOT_JOINTS = [ - "elbow_joint", - "shoulder_lift_joint", - "shoulder_pan_joint", - "wrist_1_joint", - "wrist_2_joint", - "wrist_3_joint", -] - - -def _wait_for_service(node, srv_name, srv_type, timeout): - client = node.create_client(srv_type, srv_name) - - logging.info("Waiting for service '%s' with timeout %fs...", srv_name, timeout) - if client.wait_for_service(timeout) is False: - raise Exception(f"Could not reach service '{srv_name}' within timeout of {timeout}") - logging.info(" Successfully connected to service '%s'", srv_name) - - return client - - -def _wait_for_action(node, action_name, action_type, timeout): - client = ActionClient(node, action_type, action_name) - - logging.info("Waiting for action server '%s' with timeout %fs...", action_name, timeout) - if client.wait_for_server(timeout) is False: - raise Exception( - f"Could not reach action server '{action_name}' within timeout of {timeout}" - ) - - logging.info(" Successfully connected to action server '%s'", action_name) - return client - - -def _call_service(node, client, request): - logging.info("Calling service client '%s' with request '%s'", client.srv_name, request) - future = client.call_async(request) - - rclpy.spin_until_future_complete(node, future) - - if future.result() is not None: - logging.info(" Received result: %s", future.result()) - return future.result() - - raise Exception(f"Error while calling service '{client.srv_name}': {future.exception()}") - - -class _ServiceInterface: - def __init__( - self, node, initial_timeout=TIMEOUT_WAIT_SERVICE_INITIAL, timeout=TIMEOUT_WAIT_SERVICE - ): - self.__node = node - - self.__service_clients = { - srv_name: ( - _wait_for_service(self.__node, srv_name, srv_type, initial_timeout), - srv_type, - ) - for srv_name, srv_type in self.__initial_services.items() - } - self.__service_clients.update( - { - srv_name: (_wait_for_service(self.__node, srv_name, srv_type, timeout), srv_type) - for srv_name, srv_type in self.__services.items() - } - ) - - def __init_subclass__(mcs, namespace="", initial_services={}, services={}, **kwargs): - super().__init_subclass__(**kwargs) - - mcs.__initial_services = { - namespace + "/" + srv_name: srv_type for srv_name, srv_type in initial_services.items() - } - mcs.__services = { - namespace + "/" + srv_name: srv_type for srv_name, srv_type in services.items() - } - - for srv_name, srv_type in list(initial_services.items()) + list(services.items()): - full_srv_name = namespace + "/" + srv_name - - setattr( - mcs, - srv_name, - lambda s, full_srv_name=full_srv_name, *args, **kwargs: _call_service( - s.__node, - s.__service_clients[full_srv_name][0], - s.__service_clients[full_srv_name][1].Request(*args, **kwargs), - ), - ) - - -class ActionInterface: - def __init__(self, node, action_name, action_type, timeout=TIMEOUT_WAIT_ACTION): - self.__node = node - - self.__action_name = action_name - self.__action_type = action_type - self.__action_client = _wait_for_action(node, action_name, action_type, timeout) - - def send_goal(self, *args, **kwargs): - goal = self.__action_type.Goal(*args, **kwargs) - - logging.info("Sending goal to action server '%s': %s", self.__action_name, goal) - future = self.__action_client.send_goal_async(goal) - - rclpy.spin_until_future_complete(self.__node, future) - - if future.result() is not None: - logging.info(" Received result: %s", future.result()) - return future.result() - pass - - def get_result(self, goal_handle, timeout): - future_res = goal_handle.get_result_async() - - logging.info( - "Waiting for action result from '%s' with timeout %fs", self.__action_name, timeout - ) - rclpy.spin_until_future_complete(self.__node, future_res, timeout_sec=timeout) - - if future_res.result() is not None: - logging.info(" Received result: %s", future_res.result().result) - return future_res.result().result - else: - raise Exception( - f"Exception while calling action '{self.__action_name}': {future_res.exception()}" - ) - - -class DashboardInterface( - _ServiceInterface, - namespace="/dashboard_client", - initial_services={ - "power_on": Trigger, - }, - services={ - "power_off": Trigger, - "brake_release": Trigger, - "unlock_protective_stop": Trigger, - "restart_safety": Trigger, - "get_robot_mode": GetRobotMode, - "load_installation": Load, - "load_program": Load, - "close_popup": Trigger, - "get_loaded_program": GetLoadedProgram, - "program_state": GetProgramState, - "program_running": IsProgramRunning, - "play": Trigger, - "stop": Trigger, - }, -): - def start_robot(self): - self._check_call(self.power_on()) - self._check_call(self.brake_release()) - - time.sleep(1) - - robot_mode = self.get_robot_mode() - self._check_call(robot_mode) - if robot_mode.robot_mode.mode != RobotMode.RUNNING: - raise Exception( - f"Incorrect robot mode: Expected {RobotMode.RUNNING}, got {robot_mode.robot_mode.mode}" - ) - - self._check_call(self.stop()) - - def _check_call(self, result): - if not result.success: - raise Exception("Service call not successful") - - -class ControllerManagerInterface( - _ServiceInterface, - namespace="/controller_manager", - initial_services={ - "switch_controller": SwitchController, - "load_controller": LoadController, - "unload_controller": UnloadController, - }, - services={"list_controllers": ListControllers}, -): - def wait_for_controller(self, controller_name, target_state="active"): - while True: - controllers = self.list_controllers().controller - for controller in controllers: - if (controller.name == controller_name) and (controller.state == target_state): - return - - time.sleep(1) - - -class IoStatusInterface( - _ServiceInterface, - namespace="/io_and_status_controller", - initial_services={"set_io": SetIO}, - services={ - "resend_robot_program": Trigger, - }, -): - pass - - -class ConfigurationInterface( - _ServiceInterface, - namespace="/ur_configuration_controller", - initial_services={"get_robot_software_version": GetRobotSoftwareVersion}, - services={}, -): - pass - - -class ForceModeInterface( - _ServiceInterface, - namespace="/force_mode_controller", - initial_services={}, - services={"start_force_mode": SetForceMode, "stop_force_mode": Trigger}, -): - pass - - -def _declare_launch_arguments(): - declared_arguments = [] - - declared_arguments.append( - DeclareLaunchArgument( - "ur_type", - default_value="ur5e", - description="Type/series of used UR robot.", - choices=["ur3", "ur3e", "ur5", "ur5e", "ur10", "ur10e", "ur16e", "ur20", "ur30"], - ) - ) - - return declared_arguments - - -def _ursim_action(): - ur_type = LaunchConfiguration("ur_type") - - return ExecuteProcess( - cmd=[ - PathJoinSubstitution( - [ - FindPackagePrefix("ur_client_library"), - "lib", - "ur_client_library", - "start_ursim.sh", - ] - ), - "-m", - ur_type, - ], - name="start_ursim", - output="screen", - ) - - -def generate_dashboard_test_description(): - dashboard_client = IncludeLaunchDescription( - PythonLaunchDescriptionSource( - PathJoinSubstitution( - [ - FindPackageShare("ur_robot_driver"), - "launch", - "ur_dashboard_client.launch.py", - ] - ) - ), - launch_arguments={ - "robot_ip": "192.168.56.101", - }.items(), - ) - - return LaunchDescription( - _declare_launch_arguments() + [ReadyToTest(), dashboard_client, _ursim_action()] - ) - - -def generate_driver_test_description( - tf_prefix="", controller_spawner_timeout=TIMEOUT_WAIT_SERVICE_INITIAL -): - ur_type = LaunchConfiguration("ur_type") - - launch_arguments = { - "robot_ip": "192.168.56.101", - "ur_type": ur_type, - "launch_rviz": "false", - "controller_spawner_timeout": str(controller_spawner_timeout), - "initial_joint_controller": "scaled_joint_trajectory_controller", - "headless_mode": "true", - "launch_dashboard_client": "true", - "start_joint_controller": "false", - } - if tf_prefix: - launch_arguments["tf_prefix"] = tf_prefix - - robot_driver = IncludeLaunchDescription( - PythonLaunchDescriptionSource( - PathJoinSubstitution( - [FindPackageShare("ur_robot_driver"), "launch", "ur_control.launch.py"] - ) - ), - launch_arguments=launch_arguments.items(), - ) - wait_dashboard_server = ExecuteProcess( - cmd=[ - PathJoinSubstitution( - [FindPackagePrefix("ur_robot_driver"), "bin", "wait_dashboard_server.sh"] - ) - ], - name="wait_dashboard_server", - output="screen", - ) - driver_starter = RegisterEventHandler( - OnProcessExit(target_action=wait_dashboard_server, on_exit=robot_driver) - ) - - return LaunchDescription( - _declare_launch_arguments() - + [ReadyToTest(), wait_dashboard_server, _ursim_action(), driver_starter] - ) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/urscript_interface.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/urscript_interface.py deleted file mode 100755 index 724ad3a..0000000 --- a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/test/urscript_interface.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python -# Copyright 2023, FZI Forschungszentrum Informatik -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the name of the {copyright_holder} nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -import os -import sys -import time -import unittest - -import pytest -import rclpy -import rclpy.node -from std_msgs.msg import String as StringMsg -from ur_msgs.msg import IOStates - -sys.path.append(os.path.dirname(__file__)) -from test_common import ( # noqa: E402 - ControllerManagerInterface, - DashboardInterface, - IoStatusInterface, - generate_driver_test_description, -) - -ROBOT_IP = "192.168.56.101" - - -@pytest.mark.launch_test -def generate_test_description(): - return generate_driver_test_description() - - -class URScriptInterfaceTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - # Initialize the ROS context - rclpy.init() - cls.node = rclpy.node.Node("urscript_interface_test") - cls.init_robot(cls) - - @classmethod - def tearDownClass(cls): - # Shutdown the ROS context - cls.node.destroy_node() - rclpy.shutdown() - - def init_robot(self): - self._dashboard_interface = DashboardInterface(self.node) - self._controller_manager_interface = ControllerManagerInterface(self.node) - self._io_status_controller_interface = IoStatusInterface(self.node) - - self.urscript_pub = self.node.create_publisher( - StringMsg, "/urscript_interface/script_command", 1 - ) - - def setUp(self): - self._dashboard_interface.start_robot() - time.sleep(1) - self.assertTrue(self._io_status_controller_interface.resend_robot_program().success) - - self._controller_manager_interface.wait_for_controller("io_and_status_controller") - - def test_set_io(self): - """Test setting an IO using a direct program call.""" - self.io_states_sub = self.node.create_subscription( - IOStates, - "/io_and_status_controller/io_states", - self.io_msg_cb, - rclpy.qos.qos_profile_system_default, - ) - - self.set_digout_checked(0, True) - time.sleep(1) - self.set_digout_checked(0, False) - - self.io_msg = None - self.io_states_sub = self.node.create_subscription( - IOStates, - "/io_and_status_controller/io_states", - self.io_msg_cb, - rclpy.qos.qos_profile_system_default, - ) - - script_msg = StringMsg( - data="sec my_program():\n set_digital_out(0, False)\n set_digital_out(1,True)\nend" - ) - self.urscript_pub.publish(script_msg) - self.check_pin_states([0, 1], [False, True]) - - time.sleep(1) - - script_msg = StringMsg( - data="sec my_program():\n set_digital_out(0, True)\n set_digital_out(1,False)\nend" - ) - self.urscript_pub.publish(script_msg) - self.check_pin_states([0, 1], [True, False]) - - def io_msg_cb(self, msg): - self.io_msg = msg - - def set_digout_checked(self, pin, state): - self.io_msg = None - - script_msg = StringMsg(data=f"set_digital_out({pin}, {state})") - self.urscript_pub.publish(script_msg) - - self.check_pin_states([pin], [state]) - - def check_pin_states(self, pins, states): - pin_states = [not x for x in states] - end_time = time.time() + 50 - while pin_states != states and time.time() < end_time: - rclpy.spin_once(self.node, timeout_sec=0.1) - if self.io_msg is not None: - for i, pin_id in enumerate(pins): - pin_states[i] = self.io_msg.digital_out_states[pin_id].state - self.assertIsNotNone(self.io_msg, "Did not receive an IO state in requested time.") - self.assertEqual(pin_states, states) diff --git a/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/ur_robot_driver/__init__.py b/ros2_ws/src/Universal_Robots_ROS2_Driver/ur_robot_driver/ur_robot_driver/__init__.py deleted file mode 100644 index e69de29..0000000